|
1 | 1 | # SPDX-License-Identifier: Apache-2.0 |
2 | 2 | # Copyright 2026 Firelock, LLC |
3 | 3 |
|
4 | | -# Registry safety net + idempotent auto-publish. |
| 4 | +# Registry safety net + dependency-aware, idempotent auto-publish (FIR-1021). |
5 | 5 | # |
6 | 6 | # registry_smoke: builds the crate against the PUBLISHED Kin registry only — |
7 | 7 | # every local `[patch.kin]` redirect in .cargo/config.toml is stripped first. |
8 | 8 | # Local CI normally builds WITH those sibling-checkout patches, which hides a |
9 | 9 | # crate that cannot resolve from the registry (the exact version-skew failure |
10 | 10 | # this workflow exists to prevent). Runs on PRs and on main. |
11 | 11 | # |
12 | | -# registry_publish: on a push to main, packages the crate and POSTs it to the |
13 | | -# registry. The version is read from Cargo.toml (cargo metadata), so a |
14 | | -# version-bump merge auto-publishes without a git tag. A 409 (already |
15 | | -# published) is treated as success, so re-running on an unchanged version is a |
16 | | -# no-op. It only runs after registry_smoke passes. |
| 12 | +# registry_publish: on a push to main, runs a dependency-ordered preflight |
| 13 | +# (kin-db's required registry deps — notably the pinned kin-model version — |
| 14 | +# must already be published), packages the crate, and POSTs it to the registry. |
| 15 | +# The version is read from Cargo.toml (cargo metadata), so a version-bump merge |
| 16 | +# auto-publishes without a git tag. An existing version (HTTP 409) is accepted |
| 17 | +# only when its index checksum is byte-identical to the freshly packaged crate; |
| 18 | +# a mismatch fails loudly. After publish it asserts download == index == |
| 19 | +# packaged checksum. It only runs after registry_smoke passes. |
| 20 | +# |
| 21 | +# TROY-GATED: registry_publish is pinned to the protected `registry-publish` |
| 22 | +# GitHub Environment (configure required reviewer = Troy + scope the |
| 23 | +# KINLAB_CARGO_TOKEN secret to it). The kin daemon also fails closed without the |
| 24 | +# token, so a publish cannot proceed unattended. |
| 25 | +# |
| 26 | +# registry_consumer_smoke: after publish, builds an out-of-tree throwaway crate |
| 27 | +# that depends on kin-db ONLY via `registry = "kin"` (no patches) with a fresh |
| 28 | +# CARGO_HOME — proving the published crate is self-consistent and downloadable. |
17 | 29 |
|
18 | 30 | name: Registry Publish |
19 | 31 |
|
@@ -62,14 +74,71 @@ jobs: |
62 | 74 | needs: registry_smoke |
63 | 75 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' |
64 | 76 | runs-on: ubuntu-latest |
| 77 | + # TROY-GATED: protected environment holds the publish token + required |
| 78 | + # reviewer. Without it the kin daemon fails closed on publish anyway. |
| 79 | + environment: registry-publish |
65 | 80 | steps: |
66 | 81 | - uses: actions/checkout@v6 |
67 | 82 |
|
68 | 83 | - name: Install Rust toolchain |
69 | 84 | uses: dtolnay/rust-toolchain@1.96.0 |
70 | 85 |
|
71 | | - - name: Publish kin-db |
| 86 | + - name: Publish kin-db (dependency-ordered + checksum-verified) |
72 | 87 | run: bash ./scripts/publish-kinlab-crates.sh |
73 | 88 | env: |
74 | 89 | KINLAB_CARGO_REGISTRY_URL: ${{ vars.KINLAB_CARGO_REGISTRY_URL }} |
75 | 90 | KINLAB_CARGO_TOKEN: ${{ secrets.KINLAB_CARGO_TOKEN }} |
| 91 | + |
| 92 | + registry_consumer_smoke: |
| 93 | + name: Registry-only consumer smoke (post-publish) |
| 94 | + needs: registry_publish |
| 95 | + if: github.event_name == 'push' && github.ref == 'refs/heads/main' |
| 96 | + runs-on: ubuntu-latest |
| 97 | + steps: |
| 98 | + - uses: actions/checkout@v6 |
| 99 | + |
| 100 | + - name: Install Rust toolchain |
| 101 | + uses: dtolnay/rust-toolchain@1.96.0 |
| 102 | + |
| 103 | + - name: Resolve published kin-db version |
| 104 | + id: ver |
| 105 | + run: | |
| 106 | + V="$(cargo metadata --no-deps --format-version 1 \ |
| 107 | + | python3 -c 'import json,sys; print(next(p["version"] for p in json.load(sys.stdin)["packages"] if p["name"]=="kin-db"))')" |
| 108 | + echo "version=$V" >> "$GITHUB_OUTPUT" |
| 109 | +
|
| 110 | + - name: Stage consumer crate out-of-tree (no repo .cargo patches inherited) |
| 111 | + env: |
| 112 | + VERSION: ${{ steps.ver.outputs.version }} |
| 113 | + run: | |
| 114 | + set -euo pipefail |
| 115 | + dest="$RUNNER_TEMP/consumer" |
| 116 | + rm -rf "$dest" |
| 117 | + cp -r ci/registry-smoke "$dest" |
| 118 | + mkdir -p "$dest/.cargo" |
| 119 | + # Registry alias ONLY — no [patch.*] redirects, so kin-db and its deps |
| 120 | + # must resolve from the published registry. |
| 121 | + cat > "$dest/.cargo/config.toml" <<'EOF' |
| 122 | + [registries.kin] |
| 123 | + index = "sparse+https://kinlab.ai/registry/cargo/" |
| 124 | + EOF |
| 125 | + # Pin the consumer to the exact just-published kin-db version. |
| 126 | + python3 - "$dest/Cargo.toml" "$VERSION" <<'PY' |
| 127 | + import re, sys |
| 128 | + path, version = sys.argv[1], sys.argv[2] |
| 129 | + with open(path, "r", encoding="utf-8") as fh: |
| 130 | + text = fh.read() |
| 131 | + text = re.sub(r'(kin-db\s*=\s*\{\s*version\s*=\s*")[^"]*(")', |
| 132 | + lambda m: m.group(1) + version + m.group(2), text, count=1) |
| 133 | + with open(path, "w", encoding="utf-8") as fh: |
| 134 | + fh.write(text) |
| 135 | + print("pinned consumer to kin-db", version) |
| 136 | + PY |
| 137 | +
|
| 138 | + - name: Build consumer from the kin registry only (fresh cache) |
| 139 | + working-directory: ${{ runner.temp }}/consumer |
| 140 | + env: |
| 141 | + # Fresh CARGO_HOME forces a clean download from the registry rather |
| 142 | + # than reusing any cached sibling artifacts. |
| 143 | + CARGO_HOME: ${{ runner.temp }}/fresh-cargo-home |
| 144 | + run: cargo build --locked || cargo build |
0 commit comments