chore(release): reconcile platform manifest with flow 1.34.0, privacy 1.0.4 #229
Workflow file for this run
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
| name: Platform manifest and source boundary | |
| # Validates platform-manifest.json against the real published artifacts | |
| # (PyPI, status.json) and enforces the open-core source boundary. | |
| # See docs/platform-manifest.md and scripts/check_source_boundary.py. | |
| on: | |
| pull_request: | |
| branches: | |
| - '**' | |
| push: | |
| branches: | |
| - main | |
| schedule: | |
| # DAILY drift check. The manifest goes stale when a component releases in | |
| # ANOTHER repository, so no commit | |
| # lands here and only the schedule can notice. This was weekly, which | |
| # meant up to seven days of serving wrong sha256 digests from | |
| # raw.githubusercontent; flow 1.24.0 published on 2026-07-27 and the | |
| # manifest still advertised 1.23.0's digests. One small stdlib-only job | |
| # costs seconds a day. | |
| - cron: '17 6 * * *' | |
| workflow_dispatch: | |
| # Receiver for a component repository to announce its release immediately | |
| # rather than waiting for the daily sweep. | |
| # | |
| # NOT YET WIRED: as of 2026-08-18 no component repository sends this. No | |
| # component has a dispatch step in its release | |
| # workflow, so the daily cron above is in practice the ONLY thing that | |
| # notices a component release. That is how flow 1.25.0 and 1.25.1 published | |
| # on 2026-07-27 after the manifest was regenerated at 15:42 that day and the | |
| # manifest went stale again within hours, red on every PR until someone | |
| # regenerated by hand. | |
| # | |
| # The sending side would be one step in each component's release workflow: | |
| # gh api repos/OpenAdaptAI/OpenAdapt/dispatches -f event_type=component-released | |
| # It cannot use that repository's default GITHUB_TOKEN, which is scoped to | |
| # its own repository; cross-repository dispatch needs a token with `contents: | |
| # write` on OpenAdaptAI/OpenAdapt stored as a secret in each component repo. | |
| # Provisioning that secret is a credential decision, not a code change, so it | |
| # is tracked outside this file rather than half-implemented here. | |
| # | |
| # Note that even a wired sender only makes DETECTION immediate. Repair stays | |
| # manual: this workflow validates and files an issue, it does not regenerate. | |
| # Auto-opening a fix PR is currently impossible anyway -- Actions is not | |
| # permitted to create pull requests in this repository | |
| # (`can_approve_pull_request_reviews: false`), so such a job would be | |
| # permanently red rather than helpful. | |
| repository_dispatch: | |
| types: [component-released] | |
| concurrency: | |
| group: >- | |
| platform-manifest-${{ github.event_name }}-${{ | |
| github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| validate-platform-manifest: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| # Cheapest decisive check first: structure, signature honesty, and | |
| # repo agreement need no network and cannot be flaky. | |
| - name: Validate platform manifest structure (offline) | |
| run: python scripts/validate_platform_manifest.py --offline | |
| # Standard library only: no dependency install, no lockfile, no cache. | |
| - name: Validate platform manifest against published artifacts | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: python scripts/validate_platform_manifest.py | |
| - name: Prove the drift guard fails when it should | |
| # The daily/dispatched production check is intentionally stdlib-only. | |
| # This characterization suite belongs on source changes, not every | |
| # remote drift probe, so scheduled checks stay fast and network-light. | |
| if: github.event_name == 'pull_request' || github.event_name == 'push' | |
| run: | | |
| python -m pip install --quiet 'pytest>=8.0.0' | |
| python -m pytest \ | |
| tests/test_platform_manifest_drift.py \ | |
| tests/test_platform_version_display.py -q | |
| report-manifest-drift: | |
| # A red scheduled run in a repository nobody has open is not a signal. | |
| # The 2026-07-27 drift DID turn the cron red and still shipped, because | |
| # main was already red from the previous night's release. File an issue | |
| # so drift has an owner and a paper trail instead of a stale red dot. | |
| needs: validate-platform-manifest | |
| if: >- | |
| ${{ always() && needs.validate-platform-manifest.result == 'failure' && | |
| (github.event_name == 'schedule' || | |
| github.event_name == 'workflow_dispatch' || | |
| github.event_name == 'repository_dispatch') }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| steps: | |
| - name: File or update the manifest drift issue | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| TITLE="platform-manifest.json has drifted from the published releases" | |
| BODY="The scheduled platform manifest check failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| \`platform-manifest.json\` is served publicly from raw.githubusercontent and is OpenAdapt's only cryptographic claim about the current release. While it is stale, anyone verifying a downloaded wheel or sdist against it fails. | |
| Fix: \`python scripts/generate_platform_manifest.py && python scripts/validate_platform_manifest.py\`, then open a PR." | |
| EXISTING=$(gh issue list --repo "${{ github.repository }}" --state open \ | |
| --search "in:title \"$TITLE\"" --json number --jq '.[0].number // empty') | |
| if [ -n "$EXISTING" ]; then | |
| gh issue comment "$EXISTING" --repo "${{ github.repository }}" --body "$BODY" | |
| else | |
| gh issue create --repo "${{ github.repository }}" --title "$TITLE" --body "$BODY" | |
| fi | |
| check-source-boundary: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| # The guard reads its rules from source-policy.public.json (rendered from | |
| # the canonical private manifest) and fails closed when that file is | |
| # missing or incomplete. Stdlib only: no dependency install. | |
| - name: Check open-core source boundary | |
| run: python scripts/check_source_boundary.py | |
| # Prove the guard still blocks what it blocked before the rules moved out | |
| # of the script, and that it fails closed without them. | |
| - name: Prove the boundary guard fails when it should | |
| if: github.event_name == 'pull_request' || github.event_name == 'push' | |
| run: | | |
| python -m pip install --quiet 'pytest>=8.0.0' | |
| python -m pytest tests/test_source_boundary.py -q |