ci: upgraded continuous integration workflows and migrated bazel dependency locking

- Updated CI workflows and GitHub actions to enhance Bazel cache keying, credential masking, and validation checks.
- Migrated dependency locking from Cargo.Bazel.lock to MODULE.bazel.lock using rules_rust crate_universe.
- Updated build configuration, documentation, and tooling scripts to reflect the lockfile and cache changes.
This commit is contained in:
can1357
2026-07-28 12:53:23 +02:00
parent 0820085890
commit b550858265
11 changed files with 11654 additions and 61727 deletions
+24 -9
View File
@@ -7,9 +7,16 @@ description: >
omp-kata jobs use the cluster remote cache. GitHub-hosted jobs use an omp-kata jobs use the cluster remote cache. GitHub-hosted jobs use an
actions/cache-backed disk cache seeded by the bazel-cache-warm workflow actions/cache-backed disk cache seeded by the bazel-cache-warm workflow
on the same runner image — cross-host action keys never hit, so the disk on the same runner image — cross-host action keys never hit, so the disk
cache is hosted-only and its key carries a schema version (v2; v1 was cache is hosted-only and its key carries a schema version (v1 was
poisoned by a kata-produced export that silently missed every action). poisoned by a kata-produced export that silently missed every action).
Consumers save a new exact-key archive after a miss.
The v3 key is <config hash>-<source hash>: config covers toolchain and
build settings, source covers crates/** + BUILD.bazel. Restores fall
back to the config-scoped prefix, so a source generation the exact key
has never seen still seeds from the previous generation's archive, and
the refreshed archive is saved after the build (an exact hit suppresses
the save — without the source component the first archive for a config
generation would permanently shadow newer source states).
inputs: inputs:
scope: scope:
@@ -37,7 +44,8 @@ runs:
id: backend id: backend
shell: bash shell: bash
env: env:
CACHE_KEY: bazel-disk-v2-${{ inputs.scope }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.Bazel.lock', 'MODULE.bazel', 'MODULE.bazel.lock', 'rust-toolchain.toml', '.bazelrc', '.bazelversion', 'bazel/**') }} CONFIG_HASH: ${{ hashFiles('Cargo.toml', 'Cargo.lock', 'MODULE.bazel', 'MODULE.bazel.lock', 'rust-toolchain.toml', 'rustfmt.toml', '.bazelrc', '.bazelignore', '.bazelversion', 'bazel/**') }}
SOURCE_HASH: ${{ hashFiles('crates/**', 'BUILD.bazel') }}
run: | run: |
set -euo pipefail set -euo pipefail
remote=false remote=false
@@ -48,9 +56,12 @@ runs:
fi fi
remote=true remote=true
fi fi
key="bazel-disk-v3-${{ inputs.scope }}-${{ runner.os }}-${{ runner.arch }}-$CONFIG_HASH-$SOURCE_HASH"
prefix="bazel-disk-v3-${{ inputs.scope }}-${{ runner.os }}-${{ runner.arch }}-$CONFIG_HASH-"
{ {
echo "remote=$remote" echo "remote=$remote"
echo "cache-key=$CACHE_KEY" echo "cache-key=$key"
echo "cache-prefix=$prefix"
} >> "$GITHUB_OUTPUT" } >> "$GITHUB_OUTPUT"
- name: Restore bazel disk cache - name: Restore bazel disk cache
@@ -61,7 +72,7 @@ runs:
path: ~/.cache/omp-bazel-disk path: ~/.cache/omp-bazel-disk
key: ${{ steps.backend.outputs.cache-key }} key: ${{ steps.backend.outputs.cache-key }}
restore-keys: | restore-keys: |
bazel-disk-v2-${{ inputs.scope }}-${{ runner.os }}-${{ runner.arch }}- ${{ steps.backend.outputs.cache-prefix }}
- name: Compose cache config - name: Compose cache config
id: compose id: compose
@@ -78,6 +89,14 @@ runs:
rc="$RUNNER_TEMP/bazel-cache.rc" rc="$RUNNER_TEMP/bazel-cache.rc"
if [ "$REMOTE" = "true" ]; then if [ "$REMOTE" = "true" ]; then
# Mask the derived credential BEFORE writing the rc: config=ci
# enables --announce_rc, which prints --remote_header verbatim.
# Mask lines inside the redirected block below would land in the
# rc file itself and mask nothing.
raw_auth="${BAZEL_REMOTE_USER}:${BAZEL_REMOTE_PASSWORD}"
auth="$(printf %s "$raw_auth" | base64 | tr -d '\n')"
echo "::add-mask::$raw_auth"
echo "::add-mask::$auth"
{ {
# The PVC mount lives outside $HOME. Pods are single-job and # The PVC mount lives outside $HOME. Pods are single-job and
# use RUNNER_TEMP for Bazel's output root. # use RUNNER_TEMP for Bazel's output root.
@@ -85,10 +104,6 @@ runs:
echo "common --config=ci" echo "common --config=ci"
echo "common --repository_cache=/opt/bazel-repo-cache" echo "common --repository_cache=/opt/bazel-repo-cache"
echo "common --repo_env=OMP_XWIN_CACHE_DIR=/opt/bazel-repo-cache/xwin" echo "common --repo_env=OMP_XWIN_CACHE_DIR=/opt/bazel-repo-cache/xwin"
raw_auth="${BAZEL_REMOTE_USER}:${BAZEL_REMOTE_PASSWORD}"
auth="$(printf %s "$raw_auth" | base64 | tr -d '\n')"
echo "::add-mask::$raw_auth"
echo "::add-mask::$auth"
echo "common --config=cache-rw" echo "common --config=cache-rw"
echo "common --remote_cache=grpcs://bazel-remote.bazel-cache.svc.cluster.local:9092" echo "common --remote_cache=grpcs://bazel-remote.bazel-cache.svc.cluster.local:9092"
echo "common --tls_certificate=infra/bazel-remote/ca.crt" echo "common --tls_certificate=infra/bazel-remote/ca.crt"
+14 -8
View File
@@ -35,25 +35,32 @@ runs:
echo "rust=true" >> "$GITHUB_OUTPUT" echo "rust=true" >> "$GITHUB_OUTPUT"
exit 0 exit 0
fi fi
if gh pr diff ${{ github.event.pull_request.number }} --name-only \ # Write the diff to a file before matching: in a pipeline, an API
| grep -qE '^(crates/|bazel/|Cargo\.(toml|lock)|Cargo\.Bazel\.lock|MODULE\.bazel(\.lock)?|BUILD\.bazel|\.bazelrc|\.bazelignore|\.bazelversion|rust-toolchain\.toml|rustfmt\.toml|scripts/bazel-natives\.ts|\.github/actions/(bazel-cache|bazel-natives|native-artifacts|native-inputs)/|\.github/workflows/ci\.yml)'; then # failure is indistinguishable from "no native changes" (skips
# validation — fail-open), and grep -q can close the pipe early so
# gh dies on SIGPIPE and pipefail flips a MATCH to rust=false.
changed_files="$RUNNER_TEMP/native-changed-files"
gh pr diff ${{ github.event.pull_request.number }} --name-only > "$changed_files"
if grep -qE '^(crates/|bazel/|Cargo\.(toml|lock)|MODULE\.bazel(\.lock)?|BUILD\.bazel|\.bazelrc|\.bazelignore|\.bazelversion|rust-toolchain\.toml|rustfmt\.toml|scripts/bazel-natives\.ts|\.github/actions/(bazel-cache|bazel-natives|native-artifacts|native-inputs)/|\.github/workflows/ci\.yml)' "$changed_files"; then
echo "rust=true" >> "$GITHUB_OUTPUT" echo "rust=true" >> "$GITHUB_OUTPUT"
else else
echo "No native-affecting changes; skipping Rust validation." echo "No native-affecting changes; skipping Rust validation."
echo "rust=false" >> "$GITHUB_OUTPUT" echo "rust=false" >> "$GITHUB_OUTPUT"
fi fi
# Content-addresses the addon bytes. `git ls-files` covers path + # Content-addresses the addon bytes from git index entries (mode,
# content + mode of every tracked input; a listed path that disappears # blob identity, path) — `git ls-files -s` covers the executable bit
# fails the step loudly instead of silently narrowing the key. # and never follows symlinks into worktree bytes. `--error-unmatch`
# fails the step loudly when a listed path disappears instead of
# silently narrowing the key.
- name: Compute native source hash - name: Compute native source hash
id: hash id: hash
shell: bash shell: bash
run: | run: |
set -euo pipefail set -euo pipefail
source_hash=$(git ls-files -z -- \ source_hash=$(git ls-files -s -z --error-unmatch -- \
crates bazel \ crates bazel \
Cargo.toml Cargo.lock Cargo.Bazel.lock \ Cargo.toml Cargo.lock \
MODULE.bazel MODULE.bazel.lock BUILD.bazel \ MODULE.bazel MODULE.bazel.lock BUILD.bazel \
.bazelrc .bazelignore .bazelversion \ .bazelrc .bazelignore .bazelversion \
rust-toolchain.toml rustfmt.toml \ rust-toolchain.toml rustfmt.toml \
@@ -62,7 +69,6 @@ runs:
.github/actions/native-artifacts .github/actions/native-inputs \ .github/actions/native-artifacts .github/actions/native-inputs \
.github/workflows/ci.yml \ .github/workflows/ci.yml \
| sort -z \ | sort -z \
| xargs -0 sha256sum \
| sha256sum \ | sha256sum \
| cut -c1-16) | cut -c1-16)
{ {
+42 -6
View File
@@ -4,13 +4,33 @@ name: Warm bazel disk cache
# actions/cache entries created on the default branch. Bazel action keys do # actions/cache entries created on the default branch. Bazel action keys do
# not transfer across runner environments (a kata-produced disk cache misses # not transfer across runner environments (a kata-produced disk cache misses
# every action on ubuntu-22.04), so seed the disk cache from the same image # every action on ubuntu-22.04), so seed the disk cache from the same image
# PR jobs run on. A warm run restores the exact-key archive, builds # PR jobs run on. Runs the full hosted action set — Rust validation (test,
# incrementally, and saves nothing; a lockfile/config change misses, rebuilds, # clippy, rustfmt) plus the native addons — so one exact-key archive covers
# and saves the new key. # both rust_validate and native_addons for this source generation. The v3
# key embeds a crates/** source fingerprint, so a native change on main
# means an exact miss: the build seeds from the previous generation via the
# config-scoped prefix and the refreshed archive is saved. An exact hit
# makes every invocation a cache replay and saves nothing.
on: on:
schedule: push:
- cron: "23 */6 * * *" branches: [main]
paths:
- "packages/**"
- "crates/**"
- "scripts/**"
- "bazel/**"
- "MODULE.bazel"
- "MODULE.bazel.lock"
- "BUILD.bazel"
- ".bazelrc"
- ".bazelignore"
- ".bazelversion"
- "Cargo.toml"
- "Cargo.lock"
- "rust-toolchain.toml"
- "rustfmt.toml"
- ".github/**"
workflow_dispatch: workflow_dispatch:
permissions: permissions:
@@ -30,10 +50,26 @@ jobs:
uses: ./.github/actions/bazel-cache uses: ./.github/actions/bazel-cache
with: with:
scope: linux scope: linux
- name: Build native addons # Invocation set mirrors the hosted CI jobs (rust_validate +
# native_addons) so the saved archive serves both.
- name: Rust tests
run: | run: |
set -euo pipefail set -euo pipefail
if [ "$(ulimit -Sn)" != unlimited ] && [ "$(ulimit -Sn)" -lt 65536 ]; then ulimit -Sn 65536 || true; fi if [ "$(ulimit -Sn)" != unlimited ] && [ "$(ulimit -Sn)" -lt 65536 ]; then ulimit -Sn 65536 || true; fi
bazelisk --bazelrc="${{ steps.cache.outputs.rc }}" test //crates/...
- name: Clippy (workspace lint policy on opted-in crates)
run: |
bazelisk query "kind('rust_library|rust_shared_library', //crates/pi-ast/... + //crates/pi-iso/... + //crates/pi-natives/... + //crates/pi-shell/... + //crates/pi-walker/...)" \
| xargs bazelisk --bazelrc="${{ steps.cache.outputs.rc }}" build --config=clippy-strict --
- name: Clippy (default lints elsewhere)
run: |
bazelisk query "kind('rust_library|rust_shared_library', //crates/... - (//crates/pi-ast/... + //crates/pi-iso/... + //crates/pi-natives/... + //crates/pi-shell/... + //crates/pi-walker/...) - //crates/vendor/brush-core/... - //crates/vendor/brush-builtins/...)" \
| xargs bazelisk --bazelrc="${{ steps.cache.outputs.rc }}" build --config=clippy --
- name: Rustfmt
run: bazelisk --bazelrc="${{ steps.cache.outputs.rc }}" build --config=rustfmt //crates/...
- name: Build native addons
run: |
set -euo pipefail
bazelisk --bazelrc="${{ steps.cache.outputs.rc }}" build //:natives-linux-x64-baseline //:natives-linux-x64-modern bazelisk --bazelrc="${{ steps.cache.outputs.rc }}" build //:natives-linux-x64-baseline //:natives-linux-x64-modern
- name: Save bazel disk cache - name: Save bazel disk cache
if: steps.cache.outputs.save-needed == 'true' if: steps.cache.outputs.save-needed == 'true'
+17 -3
View File
@@ -9,12 +9,15 @@ on:
- "scripts/**" - "scripts/**"
- "bazel/**" - "bazel/**"
- "MODULE.bazel" - "MODULE.bazel"
- "MODULE.bazel.lock"
- "BUILD.bazel" - "BUILD.bazel"
- ".bazelrc" - ".bazelrc"
- ".bazelignore"
- ".bazelversion" - ".bazelversion"
- "Cargo.toml" - "Cargo.toml"
- "Cargo.lock" - "Cargo.lock"
- "Cargo.Bazel.lock" - "rust-toolchain.toml"
- "rustfmt.toml"
- ".github/**" - ".github/**"
pull_request: pull_request:
branches: [main] branches: [main]
@@ -24,12 +27,15 @@ on:
- "scripts/**" - "scripts/**"
- "bazel/**" - "bazel/**"
- "MODULE.bazel" - "MODULE.bazel"
- "MODULE.bazel.lock"
- "BUILD.bazel" - "BUILD.bazel"
- ".bazelrc" - ".bazelrc"
- ".bazelignore"
- ".bazelversion" - ".bazelversion"
- "Cargo.toml" - "Cargo.toml"
- "Cargo.lock" - "Cargo.lock"
- "Cargo.Bazel.lock" - "rust-toolchain.toml"
- "rustfmt.toml"
- ".github/**" - ".github/**"
workflow_dispatch: workflow_dispatch:
inputs: inputs:
@@ -160,6 +166,11 @@ jobs:
- name: Rustfmt - name: Rustfmt
if: steps.inputs.outputs.rust == 'true' if: steps.inputs.outputs.rust == 'true'
run: bazelisk --bazelrc="${{ steps.cache.outputs.rc }}" build --config=rustfmt //crates/... run: bazelisk --bazelrc="${{ steps.cache.outputs.rc }}" build --config=rustfmt //crates/...
# No disk-cache save here: native_addons saves this same key
# concurrently, and an immutable archive can have only one producer
# per key — a validation-only archive winning the race would
# suppress the addon half on every later exact hit. Validation
# actions are seeded by the main cache warmer's combined archive.
# Builds the native addons every downstream job installs. TS-only PRs # Builds the native addons every downstream job installs. TS-only PRs
# restore the prebuilt Linux x64 pair published by trusted main builds # restore the prebuilt Linux x64 pair published by trusted main builds
@@ -264,7 +275,10 @@ jobs:
if: steps.decide.outputs.needed == 'true' if: steps.decide.outputs.needed == 'true'
shell: bash shell: bash
run: | run: |
summary=$(grep -E "INFO: [0-9]+ processes:" "$RUNNER_TEMP/bazel-build.log" | tail -1 || true) # Bazel runs with --color=yes (config=ci); strip ANSI before
# matching — escapes split "INFO:" from the process count in
# the raw log.
summary=$(sed -E 's/\x1b\[[0-9;]*m//g' "$RUNNER_TEMP/bazel-build.log" | grep -E '[0-9]+ processes:' | tail -1 || true)
echo "::notice title=Bazel build summary::${summary:-no process summary found}" echo "::notice title=Bazel build summary::${summary:-no process summary found}"
- name: Save Bazel disk cache - name: Save Bazel disk cache
if: steps.cache.outputs.save-needed == 'true' if: steps.cache.outputs.save-needed == 'true'
-1
View File
@@ -11,7 +11,6 @@ package(default_visibility = ["//visibility:public"])
exports_files([ exports_files([
"Cargo.toml", "Cargo.toml",
"Cargo.lock", "Cargo.lock",
"Cargo.Bazel.lock",
"rustfmt.toml", "rustfmt.toml",
]) ])
_ADDONS = { _ADDONS = {
-61665
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -12,8 +12,8 @@ Layout:
The cargo workspace stays authoritative for local iteration (rust-analyzer, The cargo workspace stays authoritative for local iteration (rust-analyzer,
`cargo nextest`, napi typedef regeneration); Bazel is the artifact and CI `cargo nextest`, napi typedef regeneration); Bazel is the artifact and CI
pipeline. Keep Cargo.toml/Cargo.lock and this module in sync: after editing pipeline. `crate_universe` derives the Bazel dependency graph directly from
either, run `bun run bazel:repin` (CARGO_BAZEL_REPIN=1) to refresh Cargo.Bazel.lock. Cargo.toml/Cargo.lock and the module annotations; no separate repin step is required.
""" """
module(name = "oh-my-pi") module(name = "oh-my-pi")
@@ -92,7 +92,6 @@ crate.render_config(
crate.from_cargo( crate.from_cargo(
name = "crates", name = "crates",
cargo_lockfile = "//:Cargo.lock", cargo_lockfile = "//:Cargo.lock",
lockfile = "//:Cargo.Bazel.lock",
# Exactly the shipped addon triples: crate BUILD files get # Exactly the shipped addon triples: crate BUILD files get
# target_compatible_with selects over this set (defaults omit darwin-x64 # target_compatible_with selects over this set (defaults omit darwin-x64
# and musl), and features/deps resolve per-triple from Cargo.lock. # and musl), and features/deps resolve per-triple from Cargo.lock.
+11522
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -65,8 +65,8 @@ exec hosts. Replaces cargo-xwin.
- `bazel build --nobuild //:natives-win32-x64-baseline` analyzes clean; - `bazel build --nobuild //:natives-win32-x64-baseline` analyzes clean;
`cquery deps(...)` confirms `@msvc_cc//:cc_toolchain` (not the host Xcode `cquery deps(...)` confirms `@msvc_cc//:cc_toolchain` (not the host Xcode
toolchain) resolved for the windows target. Full fetch + splat took ~2.5 min toolchain) resolved for the windows target. Full fetch + splat took ~2.5 min
on a fast link. Repin (`bun run bazel:repin`) already run for the on a fast link; crate-universe generation included the
`CMAKE_GENERATOR_x86_64_pc_windows_msvc` annotation key. `CMAKE_GENERATOR_x86_64_pc_windows_msvc` annotation.
- Wrapper smoke test outside Bazel: `clang-cl /MD` compiled a windows.h + - Wrapper smoke test outside Bazel: `clang-cl /MD` compiled a windows.h +
smmintrin.h SSE4.1 program and driver-linked it via `-fuse-ld=lld-link` + smmintrin.h SSE4.1 program and driver-linked it via `-fuse-ld=lld-link` +
`LIB` into a valid PE32+ exe; the standalone `lld-link` wrapper (rustc's `LIB` into a valid PE32+ exe; the standalone `lld-link` wrapper (rustc's
@@ -84,7 +84,7 @@ exec hosts. Replaces cargo-xwin.
rules_rust runner then substitutes `${pwd}` → exec root). The wrapper dir rules_rust runner then substitutes `${pwd}` → exec root). The wrapper dir
reaches the sandbox via the cc toolchain's `all_files`. NOTE: the PATH entry reaches the sandbox via the cc toolchain's `all_files`. NOTE: the PATH entry
hardcodes @msvc_cc's canonical repo name — keep in sync if the repo rule or hardcodes @msvc_cc's canonical repo name — keep in sync if the repo rule or
repo name changes. Repin for this annotation already run. repo name changes. The generated crate graph includes this annotation.
- audiopus_sys/opus cmake exe links (can.internal finding #2): cmake's - audiopus_sys/opus cmake exe links (can.internal finding #2): cmake's
`vs_link_exe` demands rc/mt tools that `find_program` can't locate on a `vs_link_exe` demands rc/mt tools that `find_program` can't locate on a
linux/mac PATH. @msvc_cc now generates `toolchain.cmake` (self-locating via linux/mac PATH. @msvc_cc now generates `toolchain.cmake` (self-locating via
@@ -98,7 +98,7 @@ exec hosts. Replaces cargo-xwin.
and `CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL` (/MD everywhere). and `CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL` (/MD everywhere).
Verified on darwin: scratch `project(C)` + `add_executable` configures with Verified on darwin: scratch `project(C)` + `add_executable` configures with
"Clang 20.1.7 with MSVC-like command-line" and links a valid PE32+ exe "Clang 20.1.7 with MSVC-like command-line" and links a valid PE32+ exe
through vs_link_exe with the wrapper rc/mt/linker. Repin already run. through vs_link_exe with the wrapper rc/mt/linker.
## What to verify on can.internal (linux-x64) ## What to verify on can.internal (linux-x64)
+29 -27
View File
@@ -20,7 +20,7 @@ Build side:
- `bazel/variants/BUILD.bazel` — `baseline`/`modern` ISA constraint values - `bazel/variants/BUILD.bazel` — `baseline`/`modern` ISA constraint values
- `bazel/toolchains/` — musl rustc disambiguation + the msvc cross cc toolchain (`msvc/NOTES.md`) - `bazel/toolchains/` — musl rustc disambiguation + the msvc cross cc toolchain (`msvc/NOTES.md`)
- `bazel/clippy.bazelrc` — generated from `[workspace.lints]` in `Cargo.toml` - `bazel/clippy.bazelrc` — generated from `[workspace.lints]` in `Cargo.toml`
- `MODULE.bazel`, `.bazelrc`, `.bazelversion` (Bazel 9.2.0), `Cargo.Bazel.lock` - `MODULE.bazel`, `MODULE.bazel.lock`, `.bazelrc`, `.bazelversion` (Bazel 9.2.0)
- `scripts/bazel-natives.ts` — the canonical driver (build + locate + install) - `scripts/bazel-natives.ts` — the canonical driver (build + locate + install)
- `crates/pi-natives/BUILD.bazel`, `crates/pi-natives/Cargo.toml` - `crates/pi-natives/BUILD.bazel`, `crates/pi-natives/Cargo.toml`
@@ -79,15 +79,9 @@ Rust toolchains are nightly (pinned in `MODULE.bazel`), with repo-local musl re-
### 4) Third-party crates (`crate_universe`) ### 4) Third-party crates (`crate_universe`)
`@crates//...` is generated from the workspace `Cargo.toml`/`Cargo.lock` (lockfile: `Cargo.Bazel.lock`), restricted to exactly the seven shipped triples. Crate-specific build fixes live as `crate.annotation`s in `MODULE.bazel` (see the debugging playbook below). `@crates//...` is generated from the workspace `Cargo.toml`/`Cargo.lock`, restricted to exactly the seven shipped triples. Crate-specific build fixes live as `crate.annotation`s in `MODULE.bazel` (see the debugging playbook below).
**Repin flow:** after any `Cargo.toml`/`Cargo.lock` change (or annotation edit), run The root module intentionally omits `crate_universe`'s optional rendering lock. The first evaluation after crate inputs change splices the workspace and generates external repository specs from the pinned `Cargo.lock`; Bazel records that extension result in `MODULE.bazel.lock`, so later clean output bases reuse it. Cargo manifest, lock, and annotation edits therefore require no separate repin step.
```bash
bun run bazel:repin # = CARGO_BAZEL_REPIN=1 bazelisk fetch @crates//...
```
and commit the updated `Cargo.Bazel.lock`. A stale lockfile fails analysis with a "lockfile out of date" style error.
## Local development ## Local development
@@ -140,9 +134,11 @@ build --tls_certificate=infra/bazel-remote/ca.crt
## CI ## CI
### `rust` job (validate + cache warm) ### Split Rust validation and addon production
`.github/workflows/ci.yml` `rust` runs on `omp-kata` pods for pushes and `ubuntu-22.04` for PRs, composes cache wiring via the `bazel-cache` action, then: `.github/workflows/ci.yml` separates `rust_validate` from `native_addons`. Both run on `omp-kata` pods for pushes and `ubuntu-22.04` for pull requests, but TypeScript jobs depend only on `native_addons`.
`rust_validate` uses `.github/actions/native-inputs` to inspect the complete pull-request file list. TypeScript-only pull requests skip every Rust step; native-affecting changes and all non-PR events run:
```bash ```bash
bazelisk --bazelrc="$rc" test //crates/... # full Rust suite bazelisk --bazelrc="$rc" test //crates/... # full Rust suite
@@ -155,30 +151,40 @@ bazelisk query "kind('rust_library|rust_shared_library', //crates/... - (…stri
bazelisk --bazelrc="$rc" build --config=rustfmt //crates/... bazelisk --bazelrc="$rc" build --config=rustfmt //crates/...
``` ```
- `--config=clippy` = rules_rust clippy aspect + `-Dwarnings`; `--config=clippy-strict` layers the generated `bazel/clippy.bazelrc` (rendered from `[workspace.lints]` in `Cargo.toml` — regenerate it when workspace lints change) for the crates with `[lints] workspace = true`. - `--config=clippy` = rules_rust clippy aspect + `-Dwarnings`; `--config=clippy-strict` layers the generated `bazel/clippy.bazelrc` for crates with `[lints] workspace = true`.
- `--config=rustfmt` = rustfmt aspect against the workspace `rustfmt.toml`. - `--config=rustfmt` = rustfmt aspect against the workspace `rustfmt.toml`.
- On main pushes (read-write cache) the job additionally runs `bazelisk build //:natives-linux-all` to warm the shared cache for every downstream job. - `rust_validate` never saves a hosted disk-cache archive: `native_addons` may concurrently own the same immutable key, while the main-branch warmer publishes a combined validation/addon archive.
No toolchain setup steps: bazelisk is on the GitHub images and baked into the kata runner image; Bazel fetches Rust/zig/LLVM/xwin hermetically. `native_addons` is the artifact producer for every downstream TypeScript and release job:
- Pull requests first restore the exact `native-addons-v1-linux-x64-baseline+modern-opt-<source-hash>` cache entry published by trusted main builds. Both addons are loaded before use; a miss or failed smoke check falls back to building the Linux x64 pair.
- Main and other non-PR runs build `//:natives-linux-all`, smoke the x64 pair before publishing its exact addon cache, and upload every `.node` output as the `native-addons` workflow artifact.
- Downstream jobs use `.github/actions/native-artifacts` to download that workflow artifact and install the requested target set without invoking Bazel.
No toolchain setup steps are required for native jobs: bazelisk is on the GitHub images and baked into the kata runner image; Bazel fetches Rust/zig/LLVM/xwin hermetically.
### Hosted cache warmer
`.github/workflows/bazel-cache-warm.yml` runs the full hosted validation and Linux x64 addon invocation set on `ubuntu-22.04`. Main-branch input changes restore the previous config-compatible generation, rebuild incrementally, and publish one combined exact-key disk-cache archive visible to pull requests.
### `bazel-cache` action (`.github/actions/bazel-cache`) ### `bazel-cache` action (`.github/actions/bazel-cache`)
Single source of truth for cache wiring, emitted as a bazelrc fragment (its `rc` output) that consumers pass via `bazelisk --bazelrc=...` (or `OMP_BAZEL_RC` for the driver). Two modes, detected via `BAZEL_REMOTE_USER`/`BAZEL_REMOTE_PASSWORD` (injected from the `bazel-remote-ci` secret on kata pods only): Single source of truth for cache wiring, emitted as a bazelrc fragment (its `rc` output) that consumers pass via `bazelisk --bazelrc=...` or `OMP_BAZEL_RC`. Two modes are selected via `BAZEL_REMOTE_USER`/`BAZEL_REMOTE_PASSWORD`:
| Runner | Fragment contents | | Runner | Fragment contents |
| --- | --- | | --- | --- |
| omp-kata pod | `--config=ci --config=cache-rw --remote_cache=grpcs://bazel-remote.bazel-cache.svc.cluster.local:9092 --tls_certificate=infra/bazel-remote/ca.crt --remote_header='authorization=Basic <b64 ci creds>'` | | omp-kata pod | `--config=ci --config=cache-rw --remote_cache=grpcs://bazel-remote.bazel-cache.svc.cluster.local:9092 --tls_certificate=infra/bazel-remote/ca.crt --remote_header='authorization=Basic <b64 ci creds>'` |
| GitHub-hosted | `--config=ci --disk_cache=~/.cache/omp-bazel-disk --repository_cache=~/.cache/omp-bazel-repo`, persisted by `actions/cache` keyed on `bazel-disk-<scope>-<os>-<arch>-<hash(Cargo.Bazel.lock, MODULE.bazel, rust-toolchain.toml)>` | | GitHub-hosted | `--config=ci --disk_cache=~/.cache/omp-bazel-disk --repository_cache=~/.cache/omp-bazel-repo` |
The remote endpoint resolves **only inside the cluster** (see `infra/bazel-remote/` and `infra/docs/04-arc-and-caching.md` §5); GitHub-hosted runners never talk to it. The `scope` input separates disk-cache keys per target set (`linux-x64-pair`, `release-<target_id>`, …) so jobs don't evict each other's entries. Hosted disk caches use `bazel-disk-v3-<scope>-<os>-<arch>-<config-hash>-<source-hash>`. The config hash covers Cargo/Bazel/toolchain settings; the source hash covers `crates/**` and root `BUILD.bazel`. An exact miss restores the newest config-compatible generation and permits one refreshed exact-key save. The remote endpoint resolves only inside the cluster.
### `bazel-natives` action (`.github/actions/bazel-natives`) ### Native artifact actions
Thin composite: `bazel-cache` (with `cache-scope`) → `OMP_BAZEL_RC=<rc> bun scripts/bazel-natives.ts <targets> --dest <dest>`. Every TS test job uses it with `targets: linux-x64-baseline linux-x64-modern`, `cache-scope: linux-x64-pair`. `.github/actions/bazel-natives` is the direct builder: `bazel-cache` → `OMP_BAZEL_RC=<rc> bun scripts/bazel-natives.ts <targets> --dest <dest>`, followed by a disk-cache save after a hosted miss. `.github/actions/native-artifacts` is the no-build consumer: download `native-addons` → run the same driver with `--source`.
### `release_binary` ### `release_binary`
Release runners are GitHub-hosted and build addons **inline** (disk-cache mode — repeat releases with unchanged Rust are mostly local cache hits): the `bazel-natives` action runs with the matrix's `native_targets` (`linux-x64-baseline linux-x64-modern`, `linux-musl-x64-baseline`, `linux-arm64`, `linux-musl-arm64`, `darwin-all` on both mac runners, `win32-x64-baseline` cross-built from `ubuntu-22.04`) and `cache-scope: release-<target_id>`, then `bun run ci:release:build-binaries` embeds and compiles. Linux and Windows release matrices install addons from the `native_addons` workflow artifact. Darwin artifacts cannot be cross-built on Linux, so each macOS matrix builds only its own architecture through `bazel-natives` with scope `release-<target_id>`, then `bun run ci:release:build-binaries` embeds and compiles the executable.
## Debugging playbook ## Debugging playbook
@@ -221,8 +227,8 @@ bazelisk build --nobuild //:natives-win32-x64-baseline
### Cache behavior ### Cache behavior
- **omp-kata (push/main, release_binary is not here):** read-write gRPC to in-cluster bazel-remote (`grpcs://bazel-remote.bazel-cache.svc.cluster.local:9092`, TLS via the committed `infra/bazel-remote/ca.crt`, htpasswd user `ci`). Expect `remote cache hit` counts in the build summary; the `rust` job's `//:natives-linux-all` warm build on main pushes is what seeds it. `--remote_local_fallback` + retries mean a cache outage degrades to a local build, never a failure. - **omp-kata:** read-write gRPC to the in-cluster bazel-remote (`grpcs://bazel-remote.bazel-cache.svc.cluster.local:9092`, TLS via the committed `infra/bazel-remote/ca.crt`, htpasswd user `ci`). `--remote_local_fallback` plus retries make an outage degrade to local execution rather than fail the build.
- **GitHub-hosted (PRs, macOS, releases):** no remote cache at all — `--disk_cache`/`--repository_cache` persisted by `actions/cache`, keyed on `(scope, os, arch, hash(Cargo.Bazel.lock, MODULE.bazel, rust-toolchain.toml))` with a prefix restore key. A lockfile/module change starts from the nearest previous entry. - **GitHub-hosted:** no cluster access. The v3 `actions/cache` disk key separates config and source generations; `.github/workflows/bazel-cache-warm.yml` publishes the combined default-branch archive from the same `ubuntu-22.04` image as pull-request consumers. The smaller final-addon cache is independent and is trusted only after both addons load successfully.
- **msvc repos:** the ~2 GiB LLVM download is sha256-pinned and repository-cache backed; the ~1 GiB xwin CRT/SDK splat is fetched from the Microsoft CDN inside the repo rule and is **not** repo-cache backed — a cold output base re-downloads it. Microsoft advances the VS channel payload over time, so remote-cache hit rates for win32 actions degrade gracefully after an MS bump (same property the previous cross toolchain had). Win32 link actions also don't share cache entries across host OSes (linux vs mac clang binaries). - **msvc repos:** the ~2 GiB LLVM download is sha256-pinned and repository-cache backed; the ~1 GiB xwin CRT/SDK splat is fetched from the Microsoft CDN inside the repo rule and is **not** repo-cache backed — a cold output base re-downloads it. Microsoft advances the VS channel payload over time, so remote-cache hit rates for win32 actions degrade gracefully after an MS bump (same property the previous cross toolchain had). Win32 link actions also don't share cache entries across host OSes (linux vs mac clang binaries).
- Server-side operations (deploy, TLS/auth, egress, poisoning boundary): `infra/docs/04-arc-and-caching.md` §5. - Server-side operations (deploy, TLS/auth, egress, poisoning boundary): `infra/docs/04-arc-and-caching.md` §5.
@@ -312,7 +318,6 @@ Generated declarations currently include exports from these Rust modules:
- Unknown target name: the driver errors with the full known-target list (`//:natives-*` names + `host`/`linux-all`/`darwin-all`). - Unknown target name: the driver errors with the full known-target list (`//:natives-*` names + `host`/`linux-all`/`darwin-all`).
- No `.node` outputs located after a successful build: driver exits 1 (check `bazel cquery --output=files` manually). - No `.node` outputs located after a successful build: driver exits 1 (check `bazel cquery --output=files` manually).
- Basename collision (gnu + musl in one invocation): driver refuses to install and names both sources — split into separate `--dest` dirs. - Basename collision (gnu + musl in one invocation): driver refuses to install and names both sources — split into separate `--dest` dirs.
- Stale `Cargo.Bazel.lock` after a `Cargo.{toml,lock}` change: run `bun run bazel:repin`.
- `build:bindings` (napi) failure: script surfaces non-zero exit and stderr; artifact builds are unaffected (Bazel never runs the napi CLI). - `build:bindings` (napi) failure: script surfaces non-zero exit and stderr; artifact builds are unaffected (Bazel never runs the napi CLI).
## Runtime loader failures (`native/loader-state.js`) ## Runtime loader failures (`native/loader-state.js`)
@@ -345,9 +350,6 @@ bun scripts/bazel-natives.ts linux-x64-modern linux-x64-baseline --dest packages
# Raw bazel (output: bazel-bin/natives-<t>/pi_natives.<...>.node) # Raw bazel (output: bazel-bin/natives-<t>/pi_natives.<...>.node)
bazelisk build //:natives-darwin-arm64 bazelisk build //:natives-darwin-arm64
# Refresh Cargo.Bazel.lock after Cargo.{toml,lock} or annotation changes
bun run bazel:repin
# Regenerate TS typedefs + enum exports (napi CLI, only on Rust API changes) # Regenerate TS typedefs + enum exports (napi CLI, only on Rust API changes)
bun --cwd=packages/natives run build:bindings bun --cwd=packages/natives run build:bindings
@@ -387,7 +389,7 @@ The key is `sha256` over `(path \t git-tree-hash \n)` pairs for the following in
Tree hashes come from one `git cat-file --batch-check` invocation against `HEAD`; paths missing from `HEAD` fold in as a fixed null hash so the key stays deterministic across repos that don't ship every input. The target-triple suffix matches the addon basename convention (`<platform>-<arch>` for non-x64, `<platform>-<arch>-<variant>` for x64). Tree hashes come from one `git cat-file --batch-check` invocation against `HEAD`; paths missing from `HEAD` fold in as a fixed null hash so the key stays deterministic across repos that don't ship every input. The target-triple suffix matches the addon basename convention (`<platform>-<arch>` for non-x64, `<platform>-<arch>-<variant>` for x64).
Anything outside this input set (Bazel definition files like `MODULE.bazel`/`BUILD.bazel`/`Cargo.Bazel.lock`, host glibc, env vars) is **not** in the key. If you need to invalidate after such a change, delete the cache directory by hand or bump one of the input files. Anything outside this input set (Bazel definition files such as `MODULE.bazel`/`BUILD.bazel`, host glibc, env vars) is **not** in the key. If you need to invalidate after such a change, delete the cache directory by hand or bump one of the input files.
### Layout and ownership ### Layout and ownership
-1
View File
@@ -119,7 +119,6 @@
"test:ts": "bun scripts/ci-test-ts.ts local-ts", "test:ts": "bun scripts/ci-test-ts.ts local-ts",
"test:scripts": "bun test scripts/bazel-natives.test.ts scripts/ci-concurrency.test.ts scripts/ci-release-build-binaries.test.ts scripts/ci-release-notes.test.ts scripts/ci-release-publish.test.ts scripts/fix-dts-extensions.test.ts scripts/link-omp.test.ts scripts/musl-release.test.ts", "test:scripts": "bun test scripts/bazel-natives.test.ts scripts/ci-concurrency.test.ts scripts/ci-release-build-binaries.test.ts scripts/ci-release-notes.test.ts scripts/ci-release-publish.test.ts scripts/fix-dts-extensions.test.ts scripts/link-omp.test.ts scripts/musl-release.test.ts",
"test:rs": "bun scripts/run-rs-task.ts test:rs", "test:rs": "bun scripts/run-rs-task.ts test:rs",
"bazel:repin": "CARGO_BAZEL_REPIN=1 bazelisk fetch @crates//...",
"check": "bun run --parallel check:ts check:rs", "check": "bun run --parallel check:ts check:rs",
"check:ts": "bun run check:tools && bun run --workspaces --if-present check", "check:ts": "bun run check:tools && bun run --workspaces --if-present check",
"check:tools": "biome check . --no-errors-on-unmatched", "check:tools": "biome check . --no-errors-on-unmatched",