Files
lumbridge-code/scripts/workspace-guard.sh
T
Metal AgentandClaude Opus 5 2aab0c4aab Stop compiling the GPL crate GPUI drags in behind the framework
Lumbridge is Apache-2.0, and decision 0023 quietly made it link
GPL-3.0-or-later. `crates/gpui/Cargo.toml` at the pinned revision carries
`ztracing.workspace = true` -- unconditional, not optional, not behind a
feature -- and `ztracing` is GPL-3.0-or-later, as are the `zlog` and
`ztracing_macro` it pulls. `sum_tree` asks for it too. The path is the ordinary
Linux build, not an `--all-features` artefact and not a dev-dependency:

    lumbridge -> gpui_platform -> gpui_linux -> gpui -> ztracing -> zlog
                                                                -> ztracing_macro

`cargo deny check licenses` failed on it, exit 4.

This is the second thing decision 0023 got wrong by reading the manifests of the
crates it added instead of resolving the graph; the first was believing there
were two Zed Git sources when there are five. Both were found by a gate that had
never been run.

There was no feature to turn off, so the choice was to relax the licence policy,
drop the framework, or stop compiling the crate. `ztracing` is now redirected by
a `[patch]` table at `crates/lumbridge-ztracing-shim`, a first-party
zero-dependency no-op under Apache-2.0. `zlog` and `ztracing_macro` were
reachable only through it and leave the lockfile with it.

The shim is small because the usage is: nine `#[instrument(skip_all)]` sites
across `gpui/src/svg_renderer.rs`, `sum_tree/src/sum_tree.rs` and
`sum_tree/src/cursor.rs`, and nothing else. Upstream's own crate compiles to
almost exactly this whenever the `ztracing` cfg is off, which is every build
that is not a Tracy profiling build, so no shipping behaviour is lost. It is a
proc-macro crate deliberately: such a crate can export nothing but proc macros,
so an upstream revision that starts using `ztracing::Span` or
`ztracing::info_span!` fails to compile and names the shim, rather than
resolving to something plausible.

`scripts/workspace-guard.sh` gained a third gate asserting, against Cargo.lock
rather than the manifest, that no `ztracing`, `zlog` or `ztracing_macro`
resolves to a Zed source and that the patch table is still present. `cargo deny`
already checks this, and the duplication is the point: `scripts/ci.sh`
downgrades a missing cargo-deny to a warning unless `LUMBRIDGE_CI_STRICT=1`, and
that is how the licence closure went ungated once already. A `[patch]` is an
unusually quiet thing to lose -- delete the table and everything still compiles,
still passes, and is GPL again.

Removing the three GPL rejections exposed a fourth that had been sitting beside
them and was never reported separately: `libbz2-rs-sys` under `bzip2-1.0.6`,
reached through async-compression <- http_client <- gpui. It is BSD-style and
permissive with no copyleft, and is allowed in `deny.toml` with that reasoning
written down. `cargo deny check licenses sources` is exit 0 for the first time.

The Git-source allowances in `deny.toml` are all still needed; the patched
crate's own source was `zed.git`, which `gpui` still requires.

Decision 0025 records the whole of it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPYebLiN2w4TqnHUYGdECq
2026-09-01 14:02:59 -07:00

141 lines
6.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Structural gates that need no compiler, so they run first and finish instantly.
#
# Both of these exist because the repository has already been wrong in exactly
# these two ways, and in both cases the mistake was invisible: the build stayed
# green while the code was not being built.
#
# scripts/workspace-guard.sh
set -euo pipefail
root="$(cd "$(dirname "$0")/.." && pwd)"
cd "$root"
status=0
# ---------------------------------------------------------------------------
# 1. Every crate is a member or is explicitly excluded.
#
# A crate directory named in neither list is not a build error. Cargo simply
# never looks at it: no lints, no tests, no compilation. lumbridge-devices sat
# that way with 1,127 lines, 19 tests that had never run, and a `mod` statement
# pointing at a file that did not exist. Decision 0017 records the same failure
# a month earlier with the GPUI shell in `spikes/`. Neither was caught by a
# human reading a diff, so it is caught here instead.
members="$(sed -n '/^members = \[/,/^]/p' Cargo.toml)"
excludes="$(sed -n '/^exclude = \[/,/^]/p' Cargo.toml)"
# A single-line `exclude = [...]` does not match the range above; catch it too.
excludes="$excludes$(grep -E '^exclude = \[.*\]' Cargo.toml || true)"
while IFS= read -r manifest; do
dir="$(dirname "$manifest")"
dir="${dir#./}"
# A crate carrying its own [workspace] table is deliberately independent.
if grep -qE '^\[workspace\]' "$manifest"; then
continue
fi
if printf '%s' "$members" | grep -qF "\"$dir\""; then
continue
fi
if printf '%s' "$excludes" | grep -qF "\"$dir\""; then
continue
fi
echo "workspace-guard: $dir/Cargo.toml is in neither workspace.members nor workspace.exclude." >&2
echo " Cargo will never build it, lint it, or run its tests. See AGENTS.md." >&2
status=1
done < <(find apps crates tools -mindepth 2 -maxdepth 2 -name Cargo.toml 2>/dev/null | sort)
# ---------------------------------------------------------------------------
# 2. The GPUI dependency is the one the decision record chose.
#
# Checked against Cargo.lock rather than the manifest, because the manifest
# states an intent and the lockfile states what would actually be compiled --
# and a manifest `rev` can drift from the revision that was actually resolved,
# just as the caret requirement it replaced silently accepted a version nobody
# decided on. Asserted in the fast headless job so it gates every push, and not
# in the UI job, which is `continue-on-error` and therefore cannot fail one.
#
# Decision 0023 moved this from published crates.io 0.2.2 to a full-SHA
# revision of Zed. Two packages arrive from it, and they must arrive from the
# SAME revision: `gpui` is the framework, `gpui_platform` is the entry point and
# the Linux backends that the publishable crate no longer contains. A pair that
# disagreed would compile against two different framework snapshots. Their
# `version` fields differ (0.2.2 and 0.1.0) and mean nothing here -- neither has
# been bumped upstream in ten months -- so the revision is what is asserted.
#
# Changing these lines means writing a decision record. See AGENTS.md,
# "Dependencies that are not an agent's decision".
expected_gpui_git='https://github.com/zed-industries/zed.git'
expected_gpui_rev='ce48461eaadd16c65c31f835511ab96bd3b6e746'
expected_gpui_source="git+$expected_gpui_git?rev=$expected_gpui_rev#$expected_gpui_rev"
for pkg in gpui gpui_platform; do
block="$(awk -v pkg="$pkg" '$0 == "name = \"" pkg "\""{found=1} found{print} found&&/^$/{exit}' Cargo.lock)"
if [ -z "$block" ]; then
echo "workspace-guard: no $pkg package in Cargo.lock; the shell cannot build." >&2
status=1
continue
fi
actual_source="$(printf '%s' "$block" | sed -n 's/^source = "\(.*\)"$/\1/p')"
if [ "$actual_source" != "$expected_gpui_source" ]; then
echo "workspace-guard: the $pkg dependency is not the one the decision record chose." >&2
echo " expected $expected_gpui_source" >&2
echo " found ${actual_source:-<no source: a path or workspace override>}" >&2
echo " Both gpui crates must carry rev $expected_gpui_rev." >&2
echo " A bump is a decision record, not a commit. See AGENTS.md." >&2
status=1
fi
done
# ---------------------------------------------------------------------------
# 3. No GPL crate has come back into the graph.
#
# `gpui` depends on `ztracing` unconditionally, and `ztracing` -- with the
# `zlog` and `ztracing_macro` it pulls -- is GPL-3.0-or-later. That is a
# licence problem for an Apache-2.0 product, and it is in the ordinary Linux
# build, not behind a feature. The root manifest's
# `[patch."https://github.com/zed-industries/zed.git"]` table redirects
# `ztracing` at crates/lumbridge-ztracing-shim, a first-party no-op, and the
# other two leave the graph with it.
#
# `cargo deny check licenses` already catches this, so why assert it here as
# well? Because deny is a gate a runner can skip. scripts/ci.sh downgrades a
# missing cargo-deny to a warning unless LUMBRIDGE_CI_STRICT=1, and that exact
# arrangement is how the licence closure went ungated once already -- reported
# green on a runner that had never installed the tool. This check needs no
# tool, runs in milliseconds, and is what makes deleting the patch table a
# failure rather than a quiet relicensing of the product.
#
# Asserted against Cargo.lock for the same reason as the gate above: the
# manifest states an intent, and a `[patch]` whose URL does not match the
# source character for character applies to nothing while still looking
# correct in a diff. The lockfile states what would actually be compiled.
for pkg in ztracing zlog ztracing_macro; do
block="$(awk -v pkg="$pkg" '$0 == "name = \"" pkg "\""{found=1} found{print} found&&/^$/{exit}' Cargo.lock)"
[ -n "$block" ] || continue
actual_source="$(printf '%s' "$block" | sed -n 's/^source = "\(.*\)"$/\1/p')"
case "$actual_source" in
"")
# No source line: a path package, which is the patched shim. Correct.
;;
*)
echo "workspace-guard: $pkg resolves to $actual_source, not the local shim." >&2
echo " That crate is GPL-3.0-or-later and Lumbridge is Apache-2.0." >&2
echo " The [patch] table in Cargo.toml is missing, misspelled, or" >&2
echo " no longer matches the source it is meant to replace." >&2
echo " See crates/lumbridge-ztracing-shim/src/lib.rs." >&2
status=1
;;
esac
done
if ! grep -qF '[patch."https://github.com/zed-industries/zed.git"]' Cargo.toml; then
echo "workspace-guard: the Zed [patch] table is gone from Cargo.toml." >&2
echo " Without it gpui compiles GPL-3.0-or-later ztracing." >&2
status=1
fi
if [ "$status" = 0 ]; then
echo "workspace-guard: crate membership, the pinned UI dependency, and the GPL patch are as recorded."
fi
exit "$status"