From 88c0c3cbe479a0abb0a65ce4d50ea761dc80fac1 Mon Sep 17 00:00:00 2001 From: Mark Kim Date: Thu, 15 Oct 2026 22:42:34 -0700 Subject: [PATCH] test(api): generate the pinned OpenAPI digest instead of hand-editing it The bundled-contract spec hardcoded the SHA-256 of openapi/openapi.yaml, so every automated contract sync failed CI until someone bumped it by hand, and the sync bot's force-rebuilt branch dropped any such bump. Have bin/generate-openapi write the digest to openapi/openapi.yaml.sha256, which the sync already runs and commits, and read it in the spec. check-openapi now also verifies the file, so editing the contract without regenerating still fails. Co-Authored-By: Claude Sonnet 5.5 --- CONTRIBUTING.md | 3 ++- bin/check-openapi | 6 ++++++ bin/generate-openapi | 9 +++++++++ openapi/openapi.yaml.sha256 | 1 + spec/volcano_generation_spec.rb | 2 +- 5 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 openapi/openapi.yaml.sha256 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4bba444c..98a5c49d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,8 @@ See [runtime coverage](maintainers/coverage.md) for coverage reports and focused After updating `openapi/openapi.yaml` from Hosting's public bundle, regenerate the internal client with `bin/generate-openapi` and check it with -`bin/check-openapi`. Keep generated code inside `lib/volcano/generated` and the realtime protocol +`bin/check-openapi`. The generator also refreshes `openapi/openapi.yaml.sha256`, +the digest the bundled-contract spec pins; commit it with the contract. Keep generated code inside `lib/volcano/generated` and the realtime protocol adapter behind the public facade. ## Coordinate SDK changes diff --git a/bin/check-openapi b/bin/check-openapi index 7d2ad7e4..fb5d41ba 100755 --- a/bin/check-openapi +++ b/bin/check-openapi @@ -14,4 +14,10 @@ if ! diff -qr "$expected" "$temporary/generated"; then exit 1 fi +actual_digest=$(ruby -rdigest -e 'puts Digest::SHA256.file(ARGV.fetch(0)).hexdigest' "$repo_root/openapi/openapi.yaml") +if [[ "$(tr -d '[:space:]' < "$repo_root/openapi/openapi.yaml.sha256")" != "$actual_digest" ]]; then + echo "openapi/openapi.yaml.sha256 is out of date; run bin/generate-openapi" >&2 + exit 1 +fi + echo "generated OpenAPI boundary is up to date" diff --git a/bin/generate-openapi b/bin/generate-openapi index 7fb41f68..59cec7b2 100755 --- a/bin/generate-openapi +++ b/bin/generate-openapi @@ -55,3 +55,12 @@ npx --no-install openapi-generator-cli generate \ -t openapi/templates \ -o "$output" \ --global-property apiTests=false,modelTests=false,apiDocs=false,modelDocs=false + +# Record the digest of the vendored contract next to it. The bundled-contract +# spec compares against this file, so a contract sync updates it here instead +# of by hand. Only the committed output refreshes it; freshness checks write to +# a temporary directory and leave it alone. +if [[ "$output" == "$committed_output" ]]; then + ruby -rdigest -e 'puts Digest::SHA256.file(ARGV.fetch(0)).hexdigest' \ + openapi/openapi.yaml > openapi/openapi.yaml.sha256 +fi diff --git a/openapi/openapi.yaml.sha256 b/openapi/openapi.yaml.sha256 new file mode 100644 index 00000000..bb7b29ca --- /dev/null +++ b/openapi/openapi.yaml.sha256 @@ -0,0 +1 @@ +7fec2090c1f4f4455b490a50ce2c92ef7ba82a1a71cfdb021ccc14adf5e1def8 diff --git a/spec/volcano_generation_spec.rb b/spec/volcano_generation_spec.rb index 98ececaa..e27d560a 100644 --- a/spec/volcano_generation_spec.rb +++ b/spec/volcano_generation_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Volcano do let(:root) { File.expand_path('..', __dir__) } - let(:openapi_sha256) { '7fec2090c1f4f4455b490a50ce2c92ef7ba82a1a71cfdb021ccc14adf5e1def8' } + let(:openapi_sha256) { File.read(File.join(root, 'openapi/openapi.yaml.sha256')).strip } it 'accepts current and legacy plan names in project responses' do project = described_class.const_get(:Generated).const_get(:Project)