test(docs): check the evidence anchors against source #1993
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: RegistryStack CI | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| - release/1.0 | |
| merge_group: | |
| types: | |
| - checks_requested | |
| push: | |
| branches: | |
| - main | |
| concurrency: | |
| group: ci-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('run-{0}', github.run_id) }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| permissions: | |
| contents: read | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CARGO_INCREMENTAL: "0" | |
| CARGO_PROFILE_DEV_DEBUG: "0" | |
| CARGO_PROFILE_TEST_DEBUG: "0" | |
| CARGO_FUZZ_VERSION: "0.13.2" | |
| CARGO_LLVM_COV_VERSION: "0.8.7" | |
| GITLEAKS_VERSION: "8.30.1" | |
| GITLEAKS_LINUX_X64_SHA256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb" | |
| ACTIONLINT_VERSION: "1.7.7" | |
| ACTIONLINT_LINUX_X64_SHA256: "023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757" | |
| OASDIFF_VERSION: "1.23.0" | |
| OASDIFF_LINUX_X64_SHA256: "972b10535c3db4366b9dc3ebc11ca021279af3095267c3cffdca854a3a3c4f89" | |
| jobs: | |
| changes: | |
| name: Changed paths | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| rust: ${{ steps.filter.outputs.rust }} | |
| rust_matrix: ${{ steps.filter.outputs.rust_matrix }} | |
| rust_packages: ${{ steps.filter.outputs.rust_packages }} | |
| platform: ${{ steps.filter.outputs.platform }} | |
| platform_hygiene: ${{ steps.filter.outputs.platform_hygiene }} | |
| discovery_contracts: ${{ steps.filter.outputs.discovery_contracts }} | |
| relay_v2_contracts: ${{ steps.filter.outputs.relay_v2_contracts }} | |
| relay_client_contracts: ${{ steps.filter.outputs.relay_client_contracts }} | |
| evidence_contracts: ${{ steps.filter.outputs.evidence_contracts }} | |
| release_tool: ${{ steps.filter.outputs.release_tool }} | |
| release_source_proof: ${{ steps.filter.outputs.release_source_proof }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| docs_archives: ${{ steps.filter.outputs.docs_archives }} | |
| editors: ${{ steps.filter.outputs.editors }} | |
| client_bindings: ${{ steps.filter.outputs.client_bindings }} | |
| release_linux_node_clients: ${{ steps.filter.outputs.release_linux_node_clients }} | |
| evidence_tutorial: ${{ steps.filter.outputs.evidence_tutorial }} | |
| identifiers: ${{ steps.filter.outputs.identifiers }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Classify changed paths | |
| id: filter | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| metadata="${RUNNER_TEMP}/registry-stack-cargo-metadata.json" | |
| changed_files="${RUNNER_TEMP}/registry-stack-changed-files" | |
| cargo metadata --locked --format-version 1 > "${metadata}" | |
| classifier=( | |
| python3 .github/scripts/ci_changes.py | |
| --metadata "${metadata}" | |
| --github-output "${GITHUB_OUTPUT}" | |
| ) | |
| if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then | |
| if git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null && | |
| git cat-file -e "${HEAD_SHA}^{commit}" 2>/dev/null; then | |
| git diff --name-only "${BASE_SHA}" "${HEAD_SHA}" > "${changed_files}" | |
| classifier+=(--changed-files "${changed_files}") | |
| else | |
| classifier+=(--all) | |
| fi | |
| elif git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null && | |
| git cat-file -e "${HEAD_SHA}^{commit}" 2>/dev/null; then | |
| # Keep push and merge-queue complete matrices while preserving | |
| # paths for gates that intentionally do not run on every change. | |
| git diff --name-only "${BASE_SHA}" "${HEAD_SHA}" > "${changed_files}" | |
| classifier+=(--all --changed-files "${changed_files}") | |
| else | |
| # Runs without resolvable comparison commits use the complete | |
| # matrix and leave opt-in immutable archive verification skipped. | |
| classifier+=(--all) | |
| fi | |
| "${classifier[@]}" | |
| - name: Check repository identifier reference closure | |
| run: products/identifiers/scripts/generate.py --check-references | |
| - name: Test CI classifier | |
| run: python3 .github/scripts/test_ci_changes.py | |
| - name: Install pinned actionlint | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "${RUNNER_TEMP}/bin" | |
| curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ | |
| --output "${RUNNER_TEMP}/actionlint.tar.gz" \ | |
| "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | |
| echo "${ACTIONLINT_LINUX_X64_SHA256} ${RUNNER_TEMP}/actionlint.tar.gz" | \ | |
| sha256sum --check --strict | |
| tar -xzf "${RUNNER_TEMP}/actionlint.tar.gz" -C "${RUNNER_TEMP}/bin" actionlint | |
| - name: Lint GitHub Actions workflows | |
| run: | | |
| set -euo pipefail | |
| "${RUNNER_TEMP}/bin/actionlint" | |
| - name: Check Debian 13 image contract | |
| run: python3 release/scripts/check-debian13-images.py | |
| - name: Test Debian 13 image contract | |
| run: python3 -m unittest release/scripts/test_check_debian13_images.py | |
| - name: Test container runtime preflight | |
| run: python3 -m unittest docker/test_runtime_preflight.py | |
| - name: Test advisory baseline exposure policy | |
| run: python3 -m unittest release/scripts/test_check_advisory_baselines.py | |
| - name: Test Mint demonstration support scripts | |
| run: | | |
| python3 -m unittest discover \ | |
| --start-directory crates/registry-mint/demo/support | |
| secrets: | |
| name: Secret scan | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| submodules: false | |
| - name: Install pinned Gitleaks | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "${RUNNER_TEMP}/bin" | |
| curl --fail --silent --show-error --location \ | |
| --output "${RUNNER_TEMP}/gitleaks.tar.gz" \ | |
| "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" | |
| echo "${GITLEAKS_LINUX_X64_SHA256} ${RUNNER_TEMP}/gitleaks.tar.gz" | sha256sum -c - | |
| tar -xzf "${RUNNER_TEMP}/gitleaks.tar.gz" -C "${RUNNER_TEMP}/bin" gitleaks | |
| chmod +x "${RUNNER_TEMP}/bin/gitleaks" | |
| - name: Scan tracked source for secrets | |
| run: >- | |
| "${RUNNER_TEMP}/bin/gitleaks" dir | |
| --config .gitleaks.toml | |
| --no-banner | |
| --redact | |
| --timeout 120 | |
| . | |
| platform-quality: | |
| name: Platform all-features | |
| needs: changes | |
| if: needs.changes.outputs.platform == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry and build artifacts | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| key: platform-all-features | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Lint platform crates with all features | |
| run: cargo clippy --locked -p 'registry-platform-*' --all-targets --all-features -- -D warnings | |
| platform-coverage: | |
| name: Platform line coverage | |
| needs: changes | |
| if: needs.changes.outputs.platform == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry and build artifacts | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| key: platform-coverage | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install pinned coverage tool | |
| uses: taiki-e/install-action@288e746965032cfcc232e09af2daf5f23c14d780 # v2.86.1 | |
| with: | |
| tool: cargo-llvm-cov@${{ env.CARGO_LLVM_COV_VERSION }} | |
| - name: Enforce platform line coverage | |
| run: >- | |
| cargo llvm-cov --locked | |
| -p 'registry-platform-*' | |
| --all-features | |
| --fail-under-lines 80 | |
| - name: Export platform coverage | |
| run: | | |
| set -euo pipefail | |
| coverage_dir="target/platform-coverage" | |
| mkdir -p "${coverage_dir}" | |
| cargo llvm-cov report --locked \ | |
| -p 'registry-platform-*' \ | |
| --lcov \ | |
| --output-path "${coverage_dir}/platform.lcov" | |
| - name: Stage platform coverage for trusted upload | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: platform-coverage | |
| path: target/platform-coverage/platform.lcov | |
| if-no-files-found: error | |
| retention-days: 1 | |
| platform-coverage-upload: | |
| name: Upload platform coverage | |
| needs: | |
| - changes | |
| - platform-coverage | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.changes.outputs.platform == 'true' | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| actions: read | |
| contents: read | |
| id-token: write | |
| steps: | |
| - name: Download platform coverage | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: platform-coverage | |
| path: target/platform-coverage | |
| - name: Upload platform coverage to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| disable_search: true | |
| fail_ci_if_error: false | |
| files: target/platform-coverage/platform.lcov | |
| flags: platform | |
| name: platform | |
| use_oidc: true | |
| version: v11.3.1 | |
| platform-hygiene: | |
| name: Platform hygiene | |
| needs: changes | |
| if: needs.changes.outputs.platform_hygiene == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Check shared Rust hygiene files | |
| run: products/platform/scripts/check-hygiene-alignment.sh | |
| platform-fuzz: | |
| name: Platform fuzz smoke (${{ matrix.target }}) | |
| needs: changes | |
| if: needs.changes.outputs.platform == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - authcommon_parsers | |
| - sdjwt_holder_proof | |
| - sdjwt_issuance | |
| - sqlite_statement | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # nightly | |
| with: | |
| toolchain: nightly | |
| - name: Cache Cargo registry and build artifacts | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| key: platform-fuzz-${{ matrix.target }} | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install pinned cargo-fuzz | |
| uses: taiki-e/install-action@288e746965032cfcc232e09af2daf5f23c14d780 # v2.86.1 | |
| with: | |
| tool: cargo-fuzz@${{ env.CARGO_FUZZ_VERSION }} | |
| - name: Smoke fuzz target | |
| working-directory: products/platform | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "fuzz/artifacts/${{ matrix.target }}" | |
| cargo +nightly fuzz run --fuzz-dir fuzz --target x86_64-unknown-linux-gnu "${{ matrix.target }}" -- \ | |
| -max_total_time=60 \ | |
| -rss_limit_mb=1024 \ | |
| -artifact_prefix="fuzz/artifacts/${{ matrix.target }}/" \ | |
| -print_final_stats=1 | |
| - name: Upload fuzz failures | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: platform-fuzz-${{ matrix.target }} | |
| path: products/platform/fuzz/artifacts/${{ matrix.target }}/ | |
| if-no-files-found: ignore | |
| rust-policy: | |
| name: Rust policy | |
| needs: changes | |
| if: needs.changes.outputs.rust == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install cargo-deny | |
| uses: taiki-e/install-action@288e746965032cfcc232e09af2daf5f23c14d780 # v2.86.1 | |
| with: | |
| tool: cargo-deny@0.19.8 | |
| - name: Gate inventory | |
| if: needs.changes.outputs.release_tool != 'true' | |
| run: python3 release/scripts/check-gates-inventory.py | |
| - name: Test gate inventory | |
| if: needs.changes.outputs.release_tool != 'true' | |
| run: python3 -m unittest release/scripts/test_check_gates_inventory.py | |
| - name: Cargo deny | |
| run: cargo deny check | |
| rust-quality: | |
| name: Rust format and clippy | |
| needs: changes | |
| if: needs.changes.outputs.rust == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| env: | |
| CI_RUST_PACKAGES: ${{ needs.changes.outputs.rust_packages }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Format | |
| run: cargo fmt --check | |
| - name: Clippy affected packages | |
| run: python3 .github/scripts/run_cargo_packages.py clippy | |
| - name: Report disk usage | |
| if: always() | |
| run: | | |
| df -h / | |
| du -sh target 2>/dev/null || true | |
| rust-tests: | |
| name: Rust tests (${{ matrix.name }}) | |
| needs: changes | |
| if: needs.changes.outputs.rust == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJSON(needs.changes.outputs.rust_matrix) }} | |
| env: | |
| CI_RUST_ALL_FEATURES: ${{ matrix.all_features }} | |
| CI_RUST_PACKAGES: ${{ toJSON(matrix.packages) }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Test affected packages | |
| run: python3 .github/scripts/run_cargo_packages.py test | |
| - name: Validate Manifest profiles | |
| if: matrix.name == 'manifest' | |
| working-directory: products/manifest | |
| run: cargo run --locked --profile ci -p registry-manifest-cli -- validate-profiles profiles | |
| - name: Report disk usage | |
| if: always() | |
| run: | | |
| df -h / | |
| du -sh target 2>/dev/null || true | |
| evidence-contracts: | |
| name: Evidence contracts and source neutrality | |
| needs: changes | |
| if: needs.changes.outputs.evidence_contracts == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Reproduce Evidence generated contracts | |
| run: products/evidence/scripts/check-contracts.sh | |
| - name: Reproduce the Evidence authoring schemas | |
| run: products/evidence/scripts/check-authoring-schema.sh | |
| - name: Enforce the authoring library's no-input-output invariant | |
| run: products/evidence/scripts/check-authoring-no-io.sh | |
| - name: Self-test the Evidence neutrality gate | |
| run: products/evidence/scripts/test-check-source-neutrality.sh | |
| - name: Enforce Evidence source-product neutrality | |
| run: products/evidence/scripts/check-source-neutrality.sh | |
| - name: Enforce Evidence verifier portability | |
| run: products/evidence/scripts/check-verifier-portability.sh | |
| - name: Self-test the Evidence configuration key-path gate | |
| run: python3 products/evidence/scripts/test_evidence_config_key_paths.py | |
| - name: Enforce Evidence configuration reference parity | |
| run: products/evidence/scripts/check-config-key-paths.sh | |
| discovery-contracts: | |
| name: Registry Discovery product contracts | |
| needs: changes | |
| if: needs.changes.outputs.discovery_contracts == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 40 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| with: | |
| version: "0.11.16" | |
| - name: Registry Discovery contract consistency | |
| run: products/discovery/scripts/check-contracts.sh | |
| - name: Registry Discovery HTTP journeys | |
| run: products/discovery/scripts/test-http.sh | |
| - name: Registry Discovery clean-checkout adopter tutorial | |
| run: products/discovery/scripts/test-adopter-tutorial.sh | |
| relay-v2-contracts: | |
| name: Relay V2 product contracts | |
| needs: changes | |
| if: needs.changes.outputs.relay_v2_contracts == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Relay V2 contract consistency | |
| run: products/relay-v2/scripts/check-contracts.sh | |
| - name: Reproduce Relay V2 authoring schemas | |
| run: products/relay-v2/scripts/check-authoring-schema.sh | |
| - name: Relay V2 coequal HTTP journeys | |
| run: products/relay-v2/scripts/test-http.sh | |
| relay-client-contracts: | |
| name: Relay client contract and source neutrality | |
| needs: changes | |
| if: needs.changes.outputs.relay_client_contracts == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Relay client contract consistency | |
| run: products/relay-v2/scripts/check-client-contract.sh | |
| - name: Relay client source neutrality | |
| run: products/relay-v2/scripts/check-source-neutrality.sh | |
| identifiers: | |
| name: Public identifier catalog | |
| needs: changes | |
| if: needs.changes.outputs.identifiers == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Check public identifier catalog | |
| run: products/identifiers/scripts/check.sh | |
| rust-result: | |
| name: Rust workspace | |
| if: always() | |
| needs: | |
| - changes | |
| - rust-policy | |
| - rust-quality | |
| - rust-tests | |
| - discovery-contracts | |
| - evidence-contracts | |
| - relay-v2-contracts | |
| - relay-client-contracts | |
| - identifiers | |
| runs-on: ubuntu-24.04 | |
| env: | |
| RUST_JOB_RESULTS: ${{ toJSON(needs) }} | |
| steps: | |
| - name: Require every selected Rust job | |
| run: | | |
| python3 - <<'PY' | |
| import json | |
| import os | |
| import sys | |
| jobs = json.loads(os.environ["RUST_JOB_RESULTS"]) | |
| failed = { | |
| name: details["result"] | |
| for name, details in jobs.items() | |
| if details["result"] not in {"success", "skipped"} | |
| } | |
| if failed: | |
| print(f"Rust jobs did not pass: {failed}", file=sys.stderr) | |
| raise SystemExit(1) | |
| print("Every selected Rust job passed; unselected jobs were skipped.") | |
| PY | |
| release-tool: | |
| name: Release tooling checks | |
| needs: changes | |
| if: needs.changes.outputs.release_tool == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Test release helper | |
| run: python3 -m unittest release/scripts/test_registry_release.py | |
| - name: Test release planning commands | |
| run: python3 -m unittest release/scripts/test_registry_release_plans.py | |
| - name: Test release candidate manifest and promotion verifier | |
| run: python3 -m unittest release/scripts/test_release_candidate.py | |
| - name: Test public release verifier | |
| run: python3 -m unittest release/scripts/test_verify_public_release.py | |
| - name: Test client registry reconciliation | |
| run: python3 -m unittest release/scripts/test_client_registry.py | |
| - name: Test release storage preflight | |
| run: python3 -m unittest release/scripts/test_check_release_storage.py | |
| - name: Test release candidate cleanup | |
| run: python3 -m unittest release/scripts/test_cleanup_release_candidates.py | |
| - name: Test release repeatability workflow | |
| run: python3 -m unittest release/scripts/test_release_repeatability_workflow.py | |
| - name: Test release rehearsal workflow | |
| run: python3 -m unittest release/scripts/test_release_rehearsal.py | |
| - name: Test Linux Node client release build helper | |
| run: python3 -m unittest release/scripts/test_build_linux_node_client.py | |
| - name: Test Zig glibc compiler wrapper | |
| run: python3 -m unittest release/scripts/test_zig_glibc_compiler.py | |
| - name: Test release workflow structure | |
| run: python3 -m unittest release/scripts/test_release_workflow_structure.py | |
| - name: Test release workflow guard | |
| run: python3 -m unittest release/scripts/test_release_workflow_guard.py | |
| - name: Test release image OCI label checker | |
| run: python3 -m unittest release/scripts/test_check_release_image_oci_labels.py | |
| - name: Test release image layout comparator | |
| run: python3 -m unittest release/scripts/test_compare_release_image_layouts.py | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c | |
| with: | |
| version: v0.33.0 | |
| driver-opts: image=moby/buildkit:v0.31.2@sha256:2f5adac4ecd194d9f8c10b7b5d7bceb5186853db1b26e5abd3a657af0b7e26ec | |
| - name: Smoke release image OCI labels | |
| run: release/scripts/smoke-release-image-oci-labels.sh | |
| - name: Gate inventory | |
| run: python3 release/scripts/check-gates-inventory.py | |
| - name: Test gate inventory | |
| run: python3 -m unittest release/scripts/test_check_gates_inventory.py | |
| - name: Validate release manifest | |
| run: release/scripts/registry-release validate-current | |
| - name: Validate release docsets | |
| run: release/scripts/registry-release validate-docsets | |
| - name: Audit import map | |
| run: release/scripts/registry-release audit release/manifests/import-map-2026-06-24.yaml | |
| release-tool-required: | |
| # Branch protection requires this exact context. Keep it successful for | |
| # irrelevant changes without running the release-tooling suite. | |
| name: Release tooling | |
| needs: | |
| - changes | |
| - release-tool | |
| if: ${{ always() }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Report path-gated result | |
| env: | |
| CHANGES_RESULT: ${{ needs.changes.result }} | |
| REQUIRED: ${{ needs.changes.outputs.release_tool }} | |
| RESULT: ${{ needs.release-tool.result }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| case "${CHANGES_RESULT}:${REQUIRED}:${RESULT}" in | |
| success:true:success|success:false:skipped) | |
| ;; | |
| *) | |
| echo "Release tooling path gate did not complete safely: changes=${CHANGES_RESULT} required=${REQUIRED} result=${RESULT}" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| release-source-proof: | |
| name: Release source proof checks | |
| needs: changes | |
| if: needs.changes.outputs.release_source_proof == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Check monorepo source model | |
| run: REGISTRY_RELEASE_SOURCE_MODE=monorepo release/scripts/check-release-source-model.sh | |
| - name: Test source model checker | |
| run: python3 -m unittest release/scripts/test_check_release_source_model.py | |
| release-source-proof-required: | |
| # Branch protection requires this exact context. Keep it successful for | |
| # irrelevant changes without running source proof. | |
| name: Release source proof | |
| needs: | |
| - changes | |
| - release-source-proof | |
| if: ${{ always() }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Report path-gated result | |
| env: | |
| CHANGES_RESULT: ${{ needs.changes.result }} | |
| REQUIRED: ${{ needs.changes.outputs.release_source_proof }} | |
| RESULT: ${{ needs.release-source-proof.result }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| case "${CHANGES_RESULT}:${REQUIRED}:${RESULT}" in | |
| success:true:success|success:false:skipped) | |
| ;; | |
| *) | |
| echo "Release source proof path gate did not complete safely: changes=${CHANGES_RESULT} required=${REQUIRED} result=${RESULT}" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| evidence-tutorials: | |
| name: Evidence tutorials from source | |
| needs: changes | |
| if: needs.changes.outputs.evidence_tutorial == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 40 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Assert amd64 runner | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| [[ "$(uname -m)" == "x86_64" ]] | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 | |
| with: | |
| node-version: 22.12.0 | |
| cache: npm | |
| cache-dependency-path: docs/site/package-lock.json | |
| - name: Cache Cargo registry | |
| uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2 | |
| with: | |
| shared-key: workspace-registry | |
| cache-targets: false | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Test the tutorial gate helpers | |
| working-directory: docs/site | |
| run: npm run test:tutorial:evidence | |
| - name: Check tutorial command drift | |
| working-directory: docs/site | |
| run: npm run check:tutorial:evidence:dry-run | |
| - name: Build the Evidence toolset under test | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| CARGO_TARGET_DIR="target/evidence-tutorial-source" \ | |
| cargo build --locked --profile ci \ | |
| -p registry-evidence -p registry-evidencectl \ | |
| -p registry-evidence-oid4vci -p registry-mint | |
| CARGO_TARGET_DIR="target/evidence-tutorial-source" \ | |
| cargo test --locked --profile ci \ | |
| -p registry-evidence-oid4vci --test inji_interoperability --no-run | |
| test_binary=$(find target/evidence-tutorial-source/ci/deps \ | |
| -maxdepth 1 -type f -perm -111 -name 'inji_interoperability-*' \ | |
| -printf '%T@ %p\n' | sort -nr | sed -n '1s/^[^ ]* //p') | |
| [[ -n "$test_binary" ]] | |
| cp "$test_binary" \ | |
| target/evidence-tutorial-source/ci/inji-oid4vci-interop-test | |
| # The application tutorial imports the Python client. It is built for | |
| # the stable ABI, so the container step below imports this same file | |
| # rather than building inside a read-only mount. | |
| CARGO_TARGET_DIR="target/evidence-tutorial-source" \ | |
| cargo build --locked --profile ci \ | |
| -p registry-evidence-client-py --lib \ | |
| --features registry-evidence-client-py/extension-module | |
| - name: Test the exact local Evidence lifecycle | |
| shell: bash | |
| env: | |
| EVIDENCE_BIN: ${{ github.workspace }}/target/evidence-tutorial-source/ci/evidence | |
| MINT_BIN: ${{ github.workspace }}/target/evidence-tutorial-source/ci/mint | |
| run: | | |
| set -euo pipefail | |
| CARGO_TARGET_DIR="target/evidence-tutorial-source" \ | |
| cargo test --locked --profile ci \ | |
| -p registry-evidencectl --test dev_lifecycle -- \ | |
| --ignored --test-threads=1 | |
| - name: Execute the Evidence tutorials in a clean container | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # The image is the repository's pinned release-builder digest, used | |
| # here only as a clean Debian userland: the gate exercises a shell | |
| # and coreutils, and the toolset binaries are mounted in prebuilt. | |
| docker run --rm \ | |
| --mount "type=bind,src=${PWD},dst=/work,readonly" \ | |
| --env EVIDENCE_BIN=/work/target/evidence-tutorial-source/ci/evidence \ | |
| --env EVIDENCECTL_BIN=/work/target/evidence-tutorial-source/ci/evidencectl \ | |
| --env EVIDENCE_OID4VCI_BIN=/work/target/evidence-tutorial-source/ci/evidence-oid4vci \ | |
| --env EVIDENCE_OID4VCI_INTEROP_TEST_BIN=/work/target/evidence-tutorial-source/ci/inji-oid4vci-interop-test \ | |
| --env MINT_BIN=/work/target/evidence-tutorial-source/ci/mint \ | |
| --env EVIDENCE_CLIENT_PY_LIB=/work/target/evidence-tutorial-source/ci/libregistry_evidence_client.so \ | |
| rust:1.95-trixie@sha256:f49565f188ee00bc2a18dd418183f2c5f23ef7d6e691890517ed341a598f67c3 \ | |
| bash /work/docs/site/scripts/check-evidence-tutorials.sh | |
| evidence-anchors: | |
| # The documentation anchors cite source across the whole workspace, so this check | |
| # runs on every pull request rather than behind the changed-path classifier: a | |
| # rename outside the docs job's allow-list is exactly the drift it exists to catch. | |
| # The checker imports only node:fs, node:path, and node:url, so a checkout and a | |
| # Node runtime are all it needs; the docs job runs it again inside check:source. | |
| name: Evidence anchors | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| persist-credentials: false | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 | |
| with: | |
| node-version: 22.12.0 | |
| - name: Check documentation anchors against the source tree | |
| run: node docs/site/scripts/check-evidence-anchors.mjs | |
| docs: | |
| name: Docs checks | |
| needs: changes | |
| if: needs.changes.outputs.docs == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 | |
| with: | |
| node-version: 22.12.0 | |
| cache: npm | |
| cache-dependency-path: docs/site/package-lock.json | |
| - name: Install docs shell dependency | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install --yes zsh | |
| - name: Assert docs shell dependencies | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| command -v sh | |
| command -v bash | |
| command -v zsh | |
| - name: Install docs dependencies | |
| working-directory: docs/site | |
| run: npm ci | |
| - name: Fetch pinned OpenAPI inputs | |
| working-directory: docs/site | |
| run: node scripts/fetch-openapi.mjs | |
| - name: Test docs scripts | |
| working-directory: docs/site | |
| run: npm test | |
| - name: Check production-shaped docs build | |
| working-directory: docs/site | |
| run: npm run check:production | |
| docs-required: | |
| # Branch protection requires this exact context. Keep it successful for | |
| # irrelevant changes without installing or building the docs site. | |
| name: Docs | |
| needs: | |
| - changes | |
| - docs | |
| - docs-archives | |
| if: ${{ always() }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Report path-gated result | |
| env: | |
| CHANGES_RESULT: ${{ needs.changes.result }} | |
| REQUIRED: ${{ needs.changes.outputs.docs }} | |
| RESULT: ${{ needs.docs.result }} | |
| ARCHIVES_REQUIRED: ${{ needs.changes.outputs.docs_archives }} | |
| ARCHIVES_RESULT: ${{ needs.docs-archives.result }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| case "${CHANGES_RESULT}:${REQUIRED}:${RESULT}" in | |
| success:true:success|success:false:skipped) | |
| ;; | |
| *) | |
| echo "Docs path gate did not complete safely: changes=${CHANGES_RESULT} required=${REQUIRED} result=${RESULT}" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| case "${CHANGES_RESULT}:${ARCHIVES_REQUIRED}:${ARCHIVES_RESULT}" in | |
| success:true:success|success:false:skipped) | |
| ;; | |
| *) | |
| echo "Docs archive path gate did not complete safely: changes=${CHANGES_RESULT} required=${ARCHIVES_REQUIRED} result=${ARCHIVES_RESULT}" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| docs-archives: | |
| name: Immutable docs archives | |
| needs: changes | |
| if: needs.changes.outputs.docs_archives == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 | |
| with: | |
| node-version: 22.12.0 | |
| cache: npm | |
| cache-dependency-path: docs/site/package-lock.json | |
| - name: Install docs dependencies | |
| working-directory: docs/site | |
| run: npm ci | |
| - name: Reject changes to locked archive digests | |
| working-directory: docs/site | |
| env: | |
| ARCHIVE_LOCK_BASE_REF: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before || 'origin/main' }} | |
| run: npm run check:archive-lock -- --base-ref "${ARCHIVE_LOCK_BASE_REF}" | |
| - name: Assemble and verify the published archive window | |
| working-directory: docs/site | |
| run: npm run check:archives | |
| editor-extensions: | |
| name: Editor extensions | |
| needs: changes | |
| if: needs.changes.outputs.editors == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 | |
| with: | |
| node-version: 22.12.0 | |
| cache: npm | |
| cache-dependency-path: editors/vscode/package-lock.json | |
| - name: Test editor installer | |
| run: bash editors/tests/install_test.sh | |
| - name: Package and test VS Code integration | |
| working-directory: editors/vscode | |
| run: | | |
| npm ci | |
| npm run package:dev | |
| cargo build --locked -p registry-language-server | |
| xvfb-run -a npm test | |
| cmp ../../LICENSE LICENSE | |
| - name: Install Zed extension target | |
| run: rustup target add wasm32-wasip2 | |
| - name: Check Zed extension | |
| run: | | |
| cargo check --locked --target wasm32-wasip2 --manifest-path editors/zed/Cargo.toml | |
| cmp LICENSE editors/zed/LICENSE | |
| client-bindings: | |
| name: Native client bindings | |
| needs: changes | |
| if: needs.changes.outputs.client_bindings == 'true' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 | |
| with: | |
| node-version: 22.12.0 | |
| cache: npm | |
| cache-dependency-path: | | |
| crates/registry-discovery-client-node/package-lock.json | |
| crates/registry-evidence-client-node/package-lock.json | |
| crates/registry-relay-client-node/package-lock.json | |
| - name: Build and test Node bindings | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| for client in registry-discovery-client-node registry-evidence-client-node registry-relay-client-node; do | |
| ( | |
| cd "crates/${client}" | |
| npm ci | |
| npm run build:debug | |
| npm test | |
| npm run check:types | |
| cmp ../../LICENSE LICENSE | |
| ) | |
| done | |
| - name: Build and test Python bindings | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| for client in registry-discovery-client-py registry-evidence-client-py registry-relay-client-py; do | |
| cargo build --locked -p "${client}" --lib --features "${client}/extension-module" | |
| ( | |
| cd "crates/${client}" | |
| python3 -m unittest discover -s tests/python -v | |
| cmp ../../LICENSE LICENSE | |
| ) | |
| done | |
| release-linux-node-clients: | |
| name: Release Linux Node clients (${{ matrix.asset }}) | |
| needs: changes | |
| if: needs.changes.outputs.release_linux_node_clients == 'true' | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 40 | |
| permissions: | |
| contents: read | |
| env: | |
| RUSTUP_TOOLCHAIN: "1.95.0" | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: ubuntu-24.04 | |
| asset: linux-amd64-glibc | |
| target: x86_64-unknown-linux-gnu | |
| napi_platform: linux-x64-gnu | |
| - runner: ubuntu-24.04-arm | |
| asset: linux-arm64-glibc | |
| target: aarch64-unknown-linux-gnu | |
| napi_platform: linux-arm64-gnu | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 | |
| with: | |
| node-version: 22.20.0 | |
| cache: npm | |
| cache-dependency-path: | | |
| crates/registry-discovery-client-node/package-lock.json | |
| crates/registry-evidence-client-node/package-lock.json | |
| crates/registry-relay-client-node/package-lock.json | |
| - name: Install pinned Linux client build tools | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| rustup toolchain install 1.95.0 --profile minimal | |
| python3 -m venv "${RUNNER_TEMP}/maturin" | |
| "${RUNNER_TEMP}/maturin/bin/pip" install --quiet \ | |
| --require-hashes --only-binary=:all: \ | |
| --requirement "${GITHUB_WORKSPACE}/release/requirements/maturin-1.9.6.txt" | |
| - name: Prove production Linux Node client recipe | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| for client in discovery evidence relay; do | |
| client_dir="${GITHUB_WORKSPACE}/crates/registry-${client}-client-node" | |
| (cd "${client_dir}" && npm ci) | |
| release/scripts/build-linux-node-client \ | |
| --client "${client}" \ | |
| --target "${{ matrix.target }}" \ | |
| --napi-platform "${{ matrix.napi_platform }}" \ | |
| --zig-python "${RUNNER_TEMP}/maturin/bin/python" | |
| smoke="${RUNNER_TEMP}/node-smoke-${client}" | |
| mkdir -p "${smoke}/node_modules/@registrystack" | |
| ln -s "${client_dir}" \ | |
| "${smoke}/node_modules/@registrystack/${client}-client" | |
| cp "${GITHUB_WORKSPACE}/release/scripts/smoke-${client}-client-package.js" \ | |
| "${smoke}/" | |
| (cd "${smoke}" && node "smoke-${client}-client-package.js") | |
| done | |
| ci-result: | |
| name: CI result | |
| if: always() | |
| needs: | |
| - changes | |
| - secrets | |
| - platform-quality | |
| - platform-coverage | |
| - platform-coverage-upload | |
| - platform-hygiene | |
| - platform-fuzz | |
| - rust-result | |
| - release-tool | |
| - release-source-proof | |
| - evidence-tutorials | |
| - evidence-anchors | |
| - docs | |
| - editor-extensions | |
| - client-bindings | |
| - release-linux-node-clients | |
| runs-on: ubuntu-24.04 | |
| env: | |
| CI_JOB_RESULTS: ${{ toJSON(needs) }} | |
| steps: | |
| - name: Require every selected CI job | |
| run: | | |
| python3 - <<'PY' | |
| import json | |
| import os | |
| import sys | |
| jobs = json.loads(os.environ["CI_JOB_RESULTS"]) | |
| failed = { | |
| name: details["result"] | |
| for name, details in jobs.items() | |
| if details["result"] not in {"success", "skipped"} | |
| } | |
| if failed: | |
| print(f"CI jobs did not pass: {failed}", file=sys.stderr) | |
| raise SystemExit(1) | |
| print("Every selected CI job passed; unselected jobs were skipped.") | |
| PY |