-
Notifications
You must be signed in to change notification settings - Fork 9
BC5 readiness: pairwise BC4↔BC5 + check-bc5-compat orchestrator + scheduled CI #308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
dbbdaac
conformance: pairwise BC4↔BC5 schema + canonical memories rule
jeremy 0e9b7c9
scripts: pairwise canary comparison
jeremy 9efd568
make: conformance-live + check-bc5-compat orchestrators
jeremy 629841f
ci+docs: live-canary scheduled workflow + CONTRIBUTING pairwise section
jeremy 0f6eab0
docs: drop stale PR-numbered references to canary infra
jeremy 6b2ee04
conformance: address PR #308 review threads + reframe memories canary…
jeremy 5bd5075
conformance: clarify BC4 branch reference in memories waiver reason
jeremy cef05f0
ci(live-canary): align action pins with main's bumped SHAs
jeremy 830010f
conformance: strict pairwiseSupersetKeys types + decouple harness fro…
jeremy 3da1622
conformance: fail loud when the pairwise tests file can't be parsed
jeremy 47ccdd1
conformance: reject non-array tests files, fast env preflight, 3.81-s…
jeremy f8e2dbf
ci(live-canary): update BASECAMP_HOST default rationale comment
jeremy 5819de0
conformance: pages[*] superset arrays compare item totals; bash>=4 guard
jeremy 91edd99
conformance: materialize remaining jq reads; keep make -n non-destruc…
jeremy 1b583ca
conformance: refuse rm -rf on '.'; explicit error for non-array paths
jeremy f54c8f3
conformance: preserve trailing body-root paths, validate snapshot sha…
jeremy 4573d38
conformance: absolutize LIVE_RECORD_DIR, validate waiver types + page…
jeremy 86d5892
docs: align pages[*] path semantics with the compare implementation
jeremy df08353
conformance: waivers require a non-empty reason at runtime
jeremy 6f136eb
conformance: rm -rf -- for option-injection robustness
jeremy 6b33135
docs(make): note the bash >= 4.4 requirement on check-compare-canary
jeremy 2eebe52
conformance: snapshot/test operation integrity; schema forbids empty …
jeremy 02354f5
conformance: waiver paths must be non-empty at runtime
jeremy bc49a29
conformance: segment-aware .. guard, jq preflight, broader read-error…
jeremy 4e52977
docs: pairwiseSupersetKeys does not take pages[*] aggregates
jeremy e757595
conformance: '[*]' only as the leading pages[*] segment
jeremy ee5c039
docs: bash/jq prerequisites; pin double-star rejection
jeremy 904a563
conformance: refuse checkout-ancestor LIVE_RECORD_DIR; mktemp template
jeremy 45b5e5c
conformance: reframe memories waiver as permanent documented contract…
jeremy 880d07f
conformance: validate rule paths before applying the waiver skip
jeremy 2e887b7
conformance: reject unknown rule types before applying path waivers
jeremy 41802ca
conformance: reject all undocumented bracket paths, not just '[*]'
jeremy c9f588a
conformance: harden pairwise path + snapshot validation (review round 4)
jeremy 794970d
conformance: reject pages[N] rules whose page is absent from a capture
jeremy 03dc858
conformance: whitelist rule-path grammar instead of blacklisting brac…
jeremy 2182ea1
conformance: drop hyphens from path grammar; stop uploading raw wire …
jeremy e4b12a2
make: rm the original LIVE_RECORD_DIR path, guard on the canonical one
jeremy 1e56951
conformance: redact live values from pairwiseEqual violation output
jeremy cf38f9b
conformance: redact live values from superset invalid-target violations
jeremy a63a1d0
conformance: require explicit page syntax once a capture paginates
jeremy b45d014
conformance: use explicit ERE in scenario X's grep
jeremy 881138e
make: refuse a LIVE_RECORD_DIR that canonicalizes to filesystem root
jeremy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| name: Live Canary | ||
|
|
||
| # Runs the BC4↔BC5 live compatibility canary against real Basecamp backends. | ||
| # | ||
| # Triggers | ||
| # -------- | ||
| # - workflow_dispatch: manual run, useful for ad-hoc verification. | ||
| # - schedule: nightly cron at 09:00 UTC. Catches server-side drift between | ||
| # the BC4 (master) baseline and the BC5 (five) branch over time. | ||
| # | ||
| # Required secrets (configured at repo level) | ||
| # ------------------------------------------- | ||
| # BASECAMP_TOKEN OAuth token for both backends. Must have read scope | ||
| # on the canary's read-only fixture set. | ||
| # BASECAMP_ACCOUNT_ID The numeric account ID used to compose the per-backend | ||
| # base URL. Same on both backends — the canary requires | ||
| # identical account state across BC4 and BC5 for | ||
| # pairwise rules to be meaningful. | ||
| # BC5_HOST Origin of the BC5 backend (e.g. https://5.basecampapi.com). | ||
| # | ||
| # Optional secret (defaults) | ||
| # -------------------------- | ||
| # BASECAMP_HOST Origin of the BC4 backend. Defaults to | ||
| # https://3.basecampapi.com when unset. | ||
| # | ||
| # The job no-ops with a clear log message if BASECAMP_TOKEN is unset (e.g. | ||
| # when running from a fork without secrets). It does not fail the run — | ||
| # missing secrets are operator-controllable state. | ||
| on: | ||
| workflow_dispatch: | ||
| schedule: | ||
| - cron: '0 9 * * *' | ||
|
|
||
| permissions: {} | ||
|
|
||
| concurrency: | ||
| group: live-canary | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| canary: | ||
| name: BC4↔BC5 canary | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| timeout-minutes: 30 | ||
|
jeremy marked this conversation as resolved.
|
||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Check for canary secrets | ||
| id: secrets | ||
| env: | ||
| BASECAMP_TOKEN: ${{ secrets.BASECAMP_TOKEN }} | ||
| BASECAMP_ACCOUNT_ID: ${{ secrets.BASECAMP_ACCOUNT_ID }} | ||
| BC5_HOST: ${{ secrets.BC5_HOST }} | ||
| run: | | ||
| if [ -z "$BASECAMP_TOKEN" ] || [ -z "$BASECAMP_ACCOUNT_ID" ] || [ -z "$BC5_HOST" ]; then | ||
| echo "Canary secrets not configured; skipping live run." | ||
| echo "Required: BASECAMP_TOKEN, BASECAMP_ACCOUNT_ID, BC5_HOST." | ||
| echo "configured=false" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "configured=true" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Set up Go | ||
| if: steps.secrets.outputs.configured == 'true' | ||
| uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | ||
| with: | ||
| go-version-file: 'go/go.mod' | ||
| cache-dependency-path: 'go/go.sum' | ||
|
|
||
|
jeremy marked this conversation as resolved.
|
||
| - name: Set up Node.js | ||
| if: steps.secrets.outputs.configured == 'true' | ||
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | ||
| with: | ||
| node-version: '22' | ||
| cache: 'npm' | ||
| cache-dependency-path: | | ||
| typescript/package-lock.json | ||
| conformance/runner/typescript/package-lock.json | ||
|
|
||
| - name: Set up Java | ||
| if: steps.secrets.outputs.configured == 'true' | ||
| uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 | ||
| with: | ||
| distribution: 'temurin' | ||
| java-version: '17' | ||
|
|
||
| - name: Setup Gradle | ||
| if: steps.secrets.outputs.configured == 'true' | ||
| uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 | ||
| with: | ||
| cache-provider: basic | ||
|
|
||
| - name: Set up Ruby | ||
| if: steps.secrets.outputs.configured == 'true' | ||
| uses: ruby/setup-ruby@d45b1a4e94b71acab930e56e79c6aa188764e7f9 # v1.316.0 | ||
| with: | ||
| ruby-version: '3.3' | ||
| bundler-cache: true | ||
| working-directory: conformance/runner/ruby | ||
|
|
||
| - name: Install uv (Python) | ||
| if: steps.secrets.outputs.configured == 'true' | ||
| uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 | ||
|
|
||
| - name: Build TypeScript SDK | ||
| if: steps.secrets.outputs.configured == 'true' | ||
| working-directory: typescript | ||
| run: | | ||
| npm ci | ||
| npm run build | ||
|
|
||
| - name: Run live canary against BC4 + BC5 with pairwise comparison | ||
| if: steps.secrets.outputs.configured == 'true' | ||
| env: | ||
| BASECAMP_TOKEN: ${{ secrets.BASECAMP_TOKEN }} | ||
| BASECAMP_ACCOUNT_ID: ${{ secrets.BASECAMP_ACCOUNT_ID }} | ||
| # Default the BC4 origin here: an unset optional secret expands to ''. | ||
| # The Makefile's recipe-shell ${BASECAMP_HOST:-...} default would also | ||
| # catch the empty string, but defaulting explicitly keeps the workflow | ||
| # self-describing about which BC4 origin a run used. | ||
| BASECAMP_HOST: ${{ secrets.BASECAMP_HOST || 'https://3.basecampapi.com' }} | ||
| BC5_HOST: ${{ secrets.BC5_HOST }} | ||
| LIVE_RECORD_DIR: tmp/live-canary | ||
| run: make check-bc5-compat | ||
|
|
||
| # Deliberately EXCLUDES the wire/ trees: wire snapshots carry raw | ||
| # response bodies and captured headers from a live account, which must | ||
| # not persist as downloadable workflow artifacts. Decode results carry | ||
| # only booleans, error strings, and dotted field paths. | ||
| - name: Upload canary decode results | ||
| if: always() && steps.secrets.outputs.configured == 'true' | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: live-canary-${{ github.run_id }} | ||
| path: | | ||
| tmp/live-canary/*/decode/ | ||
|
jeremy marked this conversation as resolved.
|
||
| retention-days: 14 | ||
| if-no-files-found: warn | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.