From e9d6aadd588a31d8fb16ef01c52c0ecdcb4ea46a Mon Sep 17 00:00:00 2001 From: beardthelion <56458543+beardthelion@users.noreply.github.com> Date: Wed, 22 Jul 2026 12:04:34 -0500 Subject: [PATCH 1/2] ci(core): add dependency-purity allowlist gate for gitlawb-core gitlawb-core is embedded by gl, git-remote-gitlawb, and the node daemon, so it must stay lean. Add a CI gate that fails if its normal (non-dev, non-build) transitive dependency tree gains any crate not on an explicit allowlist. The allowlist is exhaustive-by-construction: a new heavy dependency reds CI whether or not anyone thought to ban it, which a denylist cannot do. The checker resolves like the build/test jobs (no --locked, since the committed lockfile can lag the manifests) and snapshots/restores Cargo.lock so a local run leaves no working-tree side effects. - ci/gitlawb-core-allowed-deps.txt: the 83 current normal deps, plus the regeneration command and rationale in the header - scripts/check-gitlawb-core-deps.sh: recompute and diff; hard-fail on a non-allowlisted crate, note-only on a stale (removed) allowlist entry - pr-checks.yml: new hard-fail core-deps-purity job, matching the pinned action SHAs and style of the sibling jobs --- .github/workflows/pr-checks.yml | 29 ++++++++ ci/gitlawb-core-allowed-deps.txt | 111 +++++++++++++++++++++++++++++ scripts/check-gitlawb-core-deps.sh | 71 ++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 ci/gitlawb-core-allowed-deps.txt create mode 100755 scripts/check-gitlawb-core-deps.sh diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 5033983a..d08c163e 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -247,3 +247,32 @@ jobs: - name: cargo test (shipped Windows crates) run: cargo test -p gl -p git-remote-gitlawb + + # gitlawb-core is embedded by every consumer (gl, git-remote-gitlawb, the node + # daemon), so it must stay lean. This gate fails if gitlawb-core's normal + # (non-dev, non-build) dependency tree gains any crate not on the allowlist in + # ci/gitlawb-core-allowed-deps.txt. Exhaustive-by-construction: a new heavy + # dependency reds CI whether or not anyone thought to ban it, which a denylist + # cannot do. Regen instructions live in the allowlist header. + core-deps-purity: + name: gitlawb-core dependency purity + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Check out repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable + with: + toolchain: stable + + - name: Cache cargo + uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0 + with: + key: core-deps-purity + + - name: Check gitlawb-core dependency allowlist + run: bash scripts/check-gitlawb-core-deps.sh diff --git a/ci/gitlawb-core-allowed-deps.txt b/ci/gitlawb-core-allowed-deps.txt new file mode 100644 index 00000000..90cbbc83 --- /dev/null +++ b/ci/gitlawb-core-allowed-deps.txt @@ -0,0 +1,111 @@ +# Dependency allowlist for the `gitlawb-core` crate. +# +# gitlawb-core is the shared-primitives crate every consumer embeds (the `gl` +# CLI, git-remote-gitlawb, the node daemon). It must stay lean so those +# consumers do not inherit daemon-weight dependencies. This file is the source +# of truth for the crates gitlawb-core's NORMAL (non-dev, non-build) transitive +# dependency closure is allowed to contain. CI (`scripts/check-gitlawb-core-deps.sh`, +# wired into the `core-deps-purity` job in .github/workflows/pr-checks.yml) +# fails if gitlawb-core's tree contains any crate not listed here. +# +# The allowlist is exhaustive-by-construction: a NEW dependency reds CI whether +# or not anyone thought to ban it, which a denylist cannot do. +# +# Measured in-workspace, so feature unification across the workspace can, in +# rare cases, surface a crate here that gitlawb-core would not pull standalone. +# An unexpected new entry is a prompt to check what pulled it in, not noise to +# rubber-stamp. +# +# Regenerate (from repo root, after an INTENTIONAL dependency change): +# cargo tree -p gitlawb-core --edges normal --prefix none \ +# | sed -E 's/ v[0-9].*$//' \ +# | grep -v '^gitlawb-core$' \ +# | sort -u +# then paste the result below this header block. (The checker snapshots and +# restores Cargo.lock; a manual regen may refresh it — `git checkout Cargo.lock` +# afterward if you did not intend that.) +# +# Lines starting with `#` and blank lines are ignored by the checker. +aead +anyhow +base-x +base256emoji +base64 +base64ct +block-buffer +cfg-if +chacha20 +chacha20poly1305 +chrono +cid +cipher +const-oid +const-str +core2 +cpufeatures +crypto-common +crypto_box +crypto_secretbox +curve25519-dalek +data-encoding +data-encoding-macro +data-encoding-macro-internal +der +digest +ed25519 +ed25519-dalek +equivalent +generic-array +getrandom +hashbrown +hex +iana-time-zone +indexmap +inout +itoa +libc +match-lookup +memchr +multibase +multihash +multihash-codetable +multihash-derive +multihash-derive-impl +num-traits +opaque-debug +pem-rfc7468 +pkcs8 +poly1305 +ppv-lite86 +proc-macro-crate +proc-macro2 +quote +rand +rand_chacha +rand_core +salsa20 +serde +serde_core +serde_derive +serde_json +sha2 +signature +spki +subtle +syn +synstructure +thiserror +thiserror-impl +toml_datetime +toml_edit +toml_parser +typenum +unicode-ident +universal-hash +unsigned-varint +uuid +winnow +zerocopy +zeroize +zeroize_derive +zmij diff --git a/scripts/check-gitlawb-core-deps.sh b/scripts/check-gitlawb-core-deps.sh new file mode 100755 index 00000000..e9c114ca --- /dev/null +++ b/scripts/check-gitlawb-core-deps.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# +# Dependency-purity gate for the `gitlawb-core` crate. +# +# gitlawb-core is embedded by every consumer (the gl CLI, git-remote-gitlawb, +# the node daemon), so it must stay lean. This script recomputes gitlawb-core's +# NORMAL (non-dev, non-build) transitive dependency set and fails if it contains +# any crate not present in ci/gitlawb-core-allowed-deps.txt. +# +# Hard-fail direction: a crate present now but NOT allowlisted. That is the case +# the gate exists to catch (core silently gaining a heavy dependency). +# Informational only: an allowlisted crate no longer present (stale entry) — a +# legitimate dependency removal should not red CI, so it is reported, not failed. +# +# Runnable from anywhere in the repo. +set -euo pipefail + +ROOT="$(git rev-parse --show-toplevel)" +ALLOW="$ROOT/ci/gitlawb-core-allowed-deps.txt" + +if [ ! -f "$ALLOW" ]; then + echo "ERROR: allowlist not found at $ALLOW" >&2 + exit 1 +fi + +# `cargo tree` resolves like the build/test jobs (no --locked): the committed +# Cargo.lock can lag the manifests, and --locked would red this gate for +# lock-staleness reasons unrelated to gitlawb-core's dependencies. Resolving can +# refresh Cargo.lock as a side effect, so snapshot and restore it — this check +# must never leave the working tree dirty when run locally. +lock_backup="$(mktemp)" +cp "$ROOT/Cargo.lock" "$lock_backup" +restore_lock() { cp "$lock_backup" "$ROOT/Cargo.lock"; rm -f "$lock_backup"; } +trap restore_lock EXIT + +# Current normal-dependency closure of gitlawb-core, one crate name per line. +# Must match the regen command documented in the allowlist header exactly. +current="$( + cargo tree -p gitlawb-core --edges normal --prefix none --manifest-path "$ROOT/Cargo.toml" \ + | sed -E 's/ v[0-9].*$//' \ + | grep -v '^gitlawb-core$' \ + | sort -u +)" + +# Allowlist with comments and blank lines stripped. +allowed="$(grep -vE '^[[:space:]]*(#|$)' "$ALLOW" | sort -u)" + +# comm needs sorted input; both sides are sorted above. +offenders="$(comm -23 <(printf '%s\n' "$current") <(printf '%s\n' "$allowed"))" +stale="$(comm -13 <(printf '%s\n' "$current") <(printf '%s\n' "$allowed"))" + +if [ -n "$stale" ]; then + echo "NOTE: allowlisted crates no longer in gitlawb-core's dependency tree" + echo " (safe to prune from ci/gitlawb-core-allowed-deps.txt):" + printf ' %s\n' $stale + echo +fi + +if [ -n "$offenders" ]; then + { + echo "ERROR: gitlawb-core gained dependencies not on the allowlist:" + printf ' %s\n' $offenders + echo + echo "gitlawb-core must stay embeddable and lean. If a new dependency is" + echo "intentional, add it to ci/gitlawb-core-allowed-deps.txt (regen command" + echo "is in that file's header). Otherwise, drop the dependency." + } >&2 + exit 1 +fi + +echo "gitlawb-core dependency purity: OK ($(printf '%s\n' "$current" | wc -l | tr -d ' ') normal deps, all allowlisted)." From c0ad500b552f34c54e15751c1904cd9b07f1b6a1 Mon Sep 17 00:00:00 2001 From: beardthelion <56458543+beardthelion@users.noreply.github.com> Date: Wed, 22 Jul 2026 12:36:25 -0500 Subject: [PATCH 2/2] ci(core): allowlist curve25519-dalek-derive Proc-macro helper for curve25519-dalek (already allowlisted). Newer cargo versions list it under `cargo tree --edges normal` while older ones do not; the allowlist is a superset so it passes on both. Caught by core-deps-purity on the first CI run of this branch. --- ci/gitlawb-core-allowed-deps.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/gitlawb-core-allowed-deps.txt b/ci/gitlawb-core-allowed-deps.txt index 90cbbc83..952be601 100644 --- a/ci/gitlawb-core-allowed-deps.txt +++ b/ci/gitlawb-core-allowed-deps.txt @@ -47,6 +47,7 @@ crypto-common crypto_box crypto_secretbox curve25519-dalek +curve25519-dalek-derive data-encoding data-encoding-macro data-encoding-macro-internal