From 2aab0c4aabd8e45bbdaffd4eed484d818e6dc425 Mon Sep 17 00:00:00 2001 From: Metal Agent Date: Tue, 1 Sep 2026 14:02:59 -0700 Subject: [PATCH] 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) Claude-Session: https://claude.ai/code/session_01SPYebLiN2w4TqnHUYGdECq --- Cargo.lock | 82 ------------ Cargo.toml | 26 ++++ crates/lumbridge-ztracing-shim/Cargo.toml | 34 +++++ crates/lumbridge-ztracing-shim/src/lib.rs | 103 ++++++++++++++ deny.toml | 28 +++- ...023-gpui-moves-to-a-pinned-zed-revision.md | 38 +++++- ...025-gpuis-gpl-dependency-is-patched-out.md | 126 ++++++++++++++++++ scripts/workspace-guard.sh | 50 ++++++- 8 files changed, 395 insertions(+), 92 deletions(-) create mode 100644 crates/lumbridge-ztracing-shim/Cargo.toml create mode 100644 crates/lumbridge-ztracing-shim/src/lib.rs create mode 100644 docs/decisions/0025-gpuis-gpl-dependency-is-patched-out.md diff --git a/Cargo.lock b/Cargo.lock index 8302ddc..d4a7025 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3786,15 +3786,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "num" version = "0.4.3" @@ -5484,15 +5475,6 @@ dependencies = [ "digest 0.11.3", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shared_library" version = "0.1.9" @@ -5993,15 +5975,6 @@ dependencies = [ "syn 3.0.4", ] -[[package]] -name = "thread_local" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" -dependencies = [ - "cfg-if", -] - [[package]] name = "tiff" version = "0.11.3" @@ -6253,32 +6226,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", ] [[package]] @@ -6521,12 +6468,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - [[package]] name = "value-bag" version = "1.13.2" @@ -7910,17 +7851,6 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" -[[package]] -name = "zlog" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git?rev=ce48461eaadd16c65c31f835511ab96bd3b6e746#ce48461eaadd16c65c31f835511ab96bd3b6e746" -dependencies = [ - "anyhow", - "chrono", - "collections", - "log", -] - [[package]] name = "zmij" version = "1.0.23" @@ -7930,18 +7860,6 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git?rev=ce48461eaadd16c65c31f835511ab96bd3b6e746#ce48461eaadd16c65c31f835511ab96bd3b6e746" -dependencies = [ - "tracing", - "tracing-subscriber", - "zlog", - "ztracing_macro", -] - -[[package]] -name = "ztracing_macro" -version = "0.1.0" -source = "git+https://github.com/zed-industries/zed.git?rev=ce48461eaadd16c65c31f835511ab96bd3b6e746#ce48461eaadd16c65c31f835511ab96bd3b6e746" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index 7aa3faa..f3af6af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/lumbridge-terminal", "crates/lumbridge-theme", "crates/lumbridge-ui-fixture", + "crates/lumbridge-ztracing-shim", "tools/theme-gen", ] exclude = ["spikes"] @@ -30,3 +31,28 @@ unsafe_code = "forbid" [workspace.lints.clippy] all = "warn" pedantic = "warn" + +# GPUI's transitive GPL dependency, replaced by a first-party no-op. +# +# `gpui` at the pinned revision carries `ztracing.workspace = true` -- +# unconditional and not behind a feature -- and `ztracing` is GPL-3.0-or-later, +# as are the `zlog` and `ztracing_macro` it drags with it. All three land in the +# ordinary Linux build, not merely under `--all-features`, which makes them a +# licence problem for an Apache-2.0 product rather than a lint to silence. +# `cargo deny check licenses` failed on exactly this. +# +# The graph uses one symbol from the crate: an `#[instrument(skip_all)]` +# attribute, at nine sites across `gpui` and `sum_tree`. So `ztracing` is +# redirected at a workspace crate that provides that attribute and nothing else. +# `zlog` and `ztracing_macro` were reachable only through `ztracing` and leave +# the resolved graph with it. +# +# The URL is the one Cargo.lock records for the source being replaced -- +# `git+https://github.com/zed-industries/zed.git?rev=...` -- and it must stay +# spelled that way, `.git` included, or the patch silently applies to nothing. +# `scripts/workspace-guard.sh` asserts the result rather than the intent: that +# no `ztracing`, `zlog` or `ztracing_macro` resolves to a Zed source. +# +# Read crates/lumbridge-ztracing-shim/src/lib.rs before bumping the revision. +[patch."https://github.com/zed-industries/zed.git"] +ztracing = { path = "crates/lumbridge-ztracing-shim" } diff --git a/crates/lumbridge-ztracing-shim/Cargo.toml b/crates/lumbridge-ztracing-shim/Cargo.toml new file mode 100644 index 0000000..a4348e3 --- /dev/null +++ b/crates/lumbridge-ztracing-shim/Cargo.toml @@ -0,0 +1,34 @@ +# This package is deliberately named `ztracing` and not `lumbridge-ztracing-shim`. +# +# A `[patch]` entry can only replace a package with one of the SAME NAME, so the +# name is load-bearing: the root manifest's +# `[patch."https://github.com/zed-industries/zed.git"]` table redirects the +# `ztracing` that `gpui` and `sum_tree` ask for at this directory, and Cargo +# matches the two by package name. The directory keeps the `lumbridge-` prefix +# every other crate here has so that a reader browsing `crates/` can see whose +# code it is; `src/lib.rs` explains why the package underneath answers to a +# different name. +[package] +name = "ztracing" +description = "A no-op stand-in for Zed's GPL-licensed ztracing, so Lumbridge's build graph stays Apache-2.0" +version = "0.1.0" +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true +publish = false + +# A proc-macro crate, because the only symbol the graph reaches for is an +# attribute macro. Nothing else can live in a crate declared this way, which is +# the point: an upstream revision that starts using `ztracing::Span` or +# `ztracing::info_span!` fails to compile here rather than quietly resolving. +[lib] +proc-macro = true + +# No dependencies. `syn` and `quote` would only be needed to parse an item this +# macro then reassembles unchanged; returning the input `TokenStream` untouched +# is both correct and three crates cheaper. +[dependencies] + +[lints] +workspace = true diff --git a/crates/lumbridge-ztracing-shim/src/lib.rs b/crates/lumbridge-ztracing-shim/src/lib.rs new file mode 100644 index 0000000..79545c1 --- /dev/null +++ b/crates/lumbridge-ztracing-shim/src/lib.rs @@ -0,0 +1,103 @@ +//! A first-party `ztracing` that does nothing, so that Lumbridge stays Apache-2.0. +//! +//! Lumbridge is Apache-2.0, and `DISTRIBUTION.md` treats the licence closure of +//! everything it links as a hard gate rather than a preference. Decision 0023 +//! moved the UI framework from published crates.io releases to a pinned +//! revision of Zed, and that move brought more than `gpui` with it: it brought +//! `gpui`'s own workspace-internal dependencies, which are not published +//! anywhere and were never chosen one by one. +//! +//! One of them is **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` in turn pulls `zlog` and +//! `ztracing_macro`, both also GPL-3.0-or-later. `sum_tree` asks for `ztracing` +//! too. The path is short and entirely inside the build we ship: +//! +//! ```text +//! lumbridge -> gpui_platform -> gpui_linux -> gpui -> ztracing -> zlog +//! -> ztracing_macro +//! ``` +//! +//! This is not an `--all-features` artefact and not a dev-dependency. It is the +//! ordinary Linux build, and `cargo deny check licenses` failed on it with +//! `rejected: license is not explicitly allowed` for GPL-3.0-or-later. There is +//! no feature flag to turn off, so there were three ways out: relax the licence +//! policy, drop the framework, or stop compiling those crates. The first +//! trades the product's licence for a lint's convenience and the second throws +//! away decision 0023. This crate is the third. +//! +//! # What it replaces +//! +//! The root manifest's `[patch."https://github.com/zed-industries/zed.git"]` +//! table redirects the `ztracing` package -- and only that package -- at this +//! directory. Nothing else about the pinned revision changes: `gpui` and +//! `gpui_platform` still resolve to +//! `ce48461eaadd16c65c31f835511ab96bd3b6e746`, and `scripts/workspace-guard.sh` +//! still asserts it. Because `zlog` and `ztracing_macro` were reachable ONLY +//! through `ztracing`, patching the one crate removes all three from the +//! resolved graph; they no longer appear in `Cargo.lock` at all. +//! +//! # The entire API surface the graph uses +//! +//! Every reference to `ztracing` or `zlog` across the eighteen Zed packages in +//! Lumbridge's resolved graph, found by grepping the vendored checkout rather +//! than by reading manifests: +//! +//! - `gpui/src/svg_renderer.rs`, two sites: `#[ztracing::instrument(skip_all)]` +//! - `sum_tree/src/sum_tree.rs` and `sum_tree/src/cursor.rs`: +//! `use ztracing::instrument;`, then seven `#[instrument(skip_all)]` sites +//! - `sum_tree/src/sum_tree.rs:1401`: `zlog::init_test()` -- inside `#[cfg(test)]`, +//! reached through a dev-dependency that Cargo never resolves for a +//! non-workspace package, which is why no `zlog` shim is needed +//! +//! That is the whole of it: one attribute macro, always with `skip_all`. So +//! [`instrument`] is an attribute proc-macro that returns the item it was given, +//! byte for byte, and discards its arguments. Upstream's own crate does almost +//! exactly this when the `ztracing` cfg is off -- which is to say, in every +//! build that is not a Tracy profiling build -- so the shim is not a +//! behavioural downgrade of a shipping feature. It is the same nothing, +//! reimplemented under a licence we can distribute. +//! +//! # What a maintainer must check when the gpui revision is bumped +//! +//! **Whether upstream started using a `ztracing` symbol this crate does not +//! provide.** That is the one way this arrangement can go wrong, and the design +//! makes it go wrong loudly: a proc-macro crate can export nothing except proc +//! macros, so `ztracing::Span`, `ztracing::info_span!`, `ztracing::init()`, +//! `ztracing::Level` or `ztracing::field` all fail to resolve at compile time +//! with `E0432`/`E0433` naming this crate. There is no silent fallback and no +//! runtime surprise; the build stops. +//! +//! When it does, the fix is not to widen this crate by reflex. Read the new +//! upstream use first: if it is another zero-cost annotation, add the +//! corresponding no-op here (which will mean splitting this into a normal +//! library crate plus a proc-macro crate, since the two cannot share one +//! package). If instead upstream has started routing real telemetry through +//! `ztracing`, that is a decision record, because Lumbridge ships no telemetry +//! by default and `AGENTS.md` says so. +//! +//! Also re-run `cargo deny check licenses sources` and confirm the bump has not +//! introduced a *second* GPL crate on a different path. The lesson of decision +//! 0023 -- recorded there, in the correction about there being five Zed Git +//! sources rather than two -- is that reading the manifests of the crates you +//! added never finds a transitive dependency. Only resolving the graph does. + +#![forbid(unsafe_code)] + +use proc_macro::TokenStream; + +/// Zed's `#[instrument]`, with the tracing removed. +/// +/// Expands to the annotated item unchanged and drops the attribute arguments. +/// Every call site in Lumbridge's graph passes `skip_all`, which asks for no +/// fields to be recorded, so there is nothing here to preserve even in +/// principle. +/// +/// Deliberately accepts any argument tokens without parsing them: a future +/// upstream `#[instrument(name = "...")]` should keep compiling, because the +/// failure mode worth having is a missing *symbol*, which is loud, and not a +/// spuriously rejected argument list, which would only be noise. +#[proc_macro_attribute] +pub fn instrument(_arguments: TokenStream, item: TokenStream) -> TokenStream { + item +} diff --git a/deny.toml b/deny.toml index 899f025..d3a4bea 100644 --- a/deny.toml +++ b/deny.toml @@ -34,6 +34,15 @@ allow = [ # attribution requirement on downstream distribution. It arrives through # ureq, which the Claude usage endpoint needs (decision 0016). "CDLA-Permissive-2.0", + # bzip2-1.0.6 is the bzip2/libbzip2 licence carried by libbz2-rs-sys. It is a + # BSD-style permissive licence -- retain the notice, do not misrepresent the + # origin, no warranty -- with no copyleft and no source-disclosure term. It + # reaches the graph as libbz2-rs-sys -> bzip2 -> compression-codecs -> + # async-compression -> http_client -> gpui, so it is in the ordinary Linux + # build and not an `--all-features` artefact. It was already failing this + # gate alongside the GPL crates; removing those made it the only rejection + # left, which is the first time anyone could see it. + "bzip2-1.0.6", # NCSA is libfuzzer-sys, a permissive BSD/MIT-style licence. It reaches the # graph only as rav1e -> ravif -> image -> gpui and only under # `all-features`; no shipped build links it. Allowed rather than excluded so @@ -73,7 +82,22 @@ allow-git = [ # backends now live in crates Zed does not publish at all. "https://github.com/zed-industries/zed", # Not optional and not obvious: gpui_wgpu reaches font-kit through a Git - # source of Zed's own, so adopting the framework brings two Git sources - # rather than one. Naming it here is what stops that arriving unnoticed. + # source of Zed's own. Decision 0023 predicted that this made two Git + # sources rather than one. It was wrong, and the three below are why -- the + # resolved graph carries five Zed sources, not two. Each is reached through + # gpui_platform, which is to say through the Linux backend the move was made + # to obtain, and each is listed rather than waved through with a wildcard. "https://github.com/zed-industries/font-kit", + # X11 input methods, supplying zed-xim/xim-ctext/xim-parser to gpui_linux. + # This is the IME path, so it is load-bearing for a hard gate rather than + # incidental. + "https://github.com/zed-industries/xim-rs", + # Reached twice: through gpui_web, and through scheduler <- gpui. + "https://github.com/zed-industries/wasm_thread", + # Resolved in Cargo.lock but NOT reachable in this target's graph, so + # cargo deny does not currently object to it. Named anyway: it is screen + # capture, it arrives on another platform's build, and discovering it for + # the first time in a macOS CI failure would teach us nothing we cannot + # write down now. + "https://github.com/zed-industries/scap", ] diff --git a/docs/decisions/0023-gpui-moves-to-a-pinned-zed-revision.md b/docs/decisions/0023-gpui-moves-to-a-pinned-zed-revision.md index 4026c64..4682194 100644 --- a/docs/decisions/0023-gpui-moves-to-a-pinned-zed-revision.md +++ b/docs/decisions/0023-gpui-moves-to-a-pinned-zed-revision.md @@ -89,9 +89,22 @@ failure is unattributable: versions of new `clippy::pedantic` lints arrive as hard errors under `-D warnings`. Isolating this stage is what stops a lint avalanche being mistaken for a GPUI API break. -3. **`deny.toml`.** Add `zed-industries/zed` and `zed-industries/font-kit` to - `allow-git`. The second is not optional: `gpui_wgpu` reaches `font-kit` - through a Git source, so the move brings two Git sources, not one. +3. **`deny.toml`.** Add the Zed Git sources to `allow-git`. This record + originally said there were two, `zed-industries/zed` and + `zed-industries/font-kit`. **That was wrong: there are five.** The graph also + carries `xim-rs` (X11 input methods, feeding `gpui_linux` -- the IME path, + so load-bearing for a hard gate rather than incidental), `wasm_thread` + (reached twice, through `gpui_web` and through `scheduler` <- `gpui`), and + `scap`, which resolves in the lockfile without being reachable in this + target's graph and so is named before some later platform's build finds it + first. + + The error is worth keeping visible rather than quietly corrected. It was made + by reading the manifests of the two crates being added, which is exactly the + reasoning that misses a transitive Git source; only resolving the graph + answers the question. `cargo deny check sources` is what caught it, on the + commit after the allowance was written -- which is the argument for running + that check at all. 4. **The dependency swap.** `gpui` and `gpui_platform` at the pinned rev; `wayland`/`x11` requested on `gpui_platform`, where they still mean something; and `Application::new()` — which does not exist at that revision — @@ -101,10 +114,21 @@ failure is unattributable: 6. **The probe changes job**: it stops proving AccessKit exists and starts proving the next revision still compiles. -The renderer swap from blade to wgpu in stage 4 is the least characterised risk. -`scripts/native-libs.sh` addresses one missing `libxkbcommon-x11` symlink and -says nothing about a Vulkan or GL loader, and the Gitea job installs no wgpu -dependency. Expect to learn something here. +The renderer swap from blade to wgpu in stage 4 was called the least +characterised risk here, and at build time it cost nothing: the whole closure +compiled on the existing `libxkbcommon-x11` shim, with no Vulkan or GL loader +problem and no new native dependency. That is not the same as the risk being +disproven. Nothing in `cargo check`, `clippy`, or the test suite opens a window, +so the renderer has still never run. The risk moved from build time to first +launch; it did not go away. + +The rest of stage 4 was smaller than expected. The entire API delta across the +ten-month jump was three signatures over nineteen sites: `Window::focus` taking +the `App` it used to reach through the window, `flex_shrink` taking the factor +it used to assume, and `Application::new` becoming +`gpui_platform::application()`. No crate outside `apps/lumbridge` needed any +change, because none of them imports a gpui type -- the narrow boundary +`ARCHITECTURE.md` insisted on is the reason this was an afternoon. ## What is knowingly still unmet diff --git a/docs/decisions/0025-gpuis-gpl-dependency-is-patched-out.md b/docs/decisions/0025-gpuis-gpl-dependency-is-patched-out.md new file mode 100644 index 0000000..5ce7240 --- /dev/null +++ b/docs/decisions/0025-gpuis-gpl-dependency-is-patched-out.md @@ -0,0 +1,126 @@ +# 0025: GPUI's GPL dependency is patched out, not tolerated + +Status: accepted. Amends decision 0023, which chose the pinned Zed revision +without noticing what came with it. Does not change that revision. + +## The decision + +`ztracing` is redirected, by a `[patch]` table in the root manifest, at +`crates/lumbridge-ztracing-shim` — a first-party, zero-dependency, no-op crate +under Apache-2.0. Nothing else about decision 0023's pin changes. + +## The problem + +Lumbridge is Apache-2.0, and `DISTRIBUTION.md` treats the licence closure of +everything the product links as a hard gate. Decision 0023 replaced published +`gpui` releases with a pinned revision of Zed's monorepo, and a monorepo +revision does not bring only the crates you named. `crates/gpui/Cargo.toml` at +`ce48461` carries: + +```toml +ztracing.workspace = true +``` + +Unconditional. Not optional. Not behind a feature. `ztracing` is +**GPL-3.0-or-later**, and it pulls `zlog` and `ztracing_macro`, both also +GPL-3.0-or-later. `sum_tree` asks for `ztracing` as well. The path is entirely +inside the build that ships: + +```text +lumbridge -> gpui_platform -> gpui_linux -> gpui -> ztracing -> zlog + -> ztracing_macro +``` + +This is not an `--all-features` artefact and not a dev-dependency. It is the +ordinary Linux build. `cargo deny check licenses` failed on it, exit 4, three +rejections, each `license is not explicitly allowed`. + +That is the second thing decision 0023 got wrong by reading manifests instead of +resolving a graph. The first was believing there were two Zed Git sources when +there are five. Both were caught by a gate that had never been run, and both +argue the same point: the transitive closure is the only thing that answers a +licensing question. + +## What was rejected + +**Relax `deny.toml`.** Adding GPL-3.0-or-later to the allow list would trade the +product's licence for a green build. The gate exists precisely to stop that +being done quietly. + +**Turn the feature off.** There is no feature. `ztracing.workspace = true` has +no `optional = true` beside it, and no `cfg` guards its use sites. + +**Drop GPUI.** That throws away decision 0023 and, until `lumbridge-toolkit` +exists, the product's ability to draw a window. + +**Vendor and relicense.** Not available: GPL is GPL. + +## What was chosen, and why it is small + +Every use of `ztracing` and `zlog` across the eighteen Zed packages in the +resolved graph, found by grepping the vendored checkout rather than the +manifests: + +- `gpui/src/svg_renderer.rs`, two sites: `#[ztracing::instrument(skip_all)]` +- `sum_tree/src/sum_tree.rs` and `sum_tree/src/cursor.rs`: + `use ztracing::instrument;`, then seven `#[instrument(skip_all)]` sites +- `sum_tree/src/sum_tree.rs:1401`: `zlog::init_test()`, inside `#[cfg(test)]`, + behind a dev-dependency Cargo never resolves for a non-workspace package — + which is why no `zlog` shim is needed and why `zlog` leaves the lockfile + entirely + +Nine attribute sites, all `skip_all`, and nothing else. So the shim is one +attribute proc-macro that returns its input unchanged. Upstream's own crate +compiles to almost exactly that whenever the `ztracing` cfg is off — which is +every build that is not a Tracy profiling build — so no shipping behaviour is +lost. Lumbridge ships no telemetry by default in any case. + +Because `zlog` and `ztracing_macro` were reachable only through `ztracing`, +patching the one package removes all three: they no longer appear in +`Cargo.lock`. + +## How it is held + +`scripts/workspace-guard.sh` gained a third gate. It asserts, 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 +in `Cargo.toml`. + +`cargo deny` already checks this, so the duplication is deliberate. +`scripts/ci.sh` downgrades a missing `cargo-deny` to a warning unless +`LUMBRIDGE_CI_STRICT=1`, and that arrangement is how the licence closure went +ungated once already — green on a runner that had never installed the tool. The +guard needs no tool and runs in milliseconds. It is what makes deleting the +patch a build failure rather than a silent relicensing of the product. + +A `[patch]` is an unusually quiet thing to lose: delete the table and everything +still compiles, still passes every test, and is GPL again. + +## The unrelated failure this exposed + +Removing the three GPL rejections left one behind that had been sitting beside +them: `libbz2-rs-sys` under the `bzip2-1.0.6` licence, reached through +`bzip2 -> compression-codecs -> async-compression -> http_client -> gpui`. It is +BSD-style and permissive — retain the notice, do not misrepresent the origin, no +warranty — with no copyleft and no source-disclosure term, and it is allowed in +`deny.toml` with that reasoning written down. It is recorded here because it was +never reported as a separate finding; it was simply the fourth line of a failure +everyone read as being about GPL. + +## What the next revision bump must check + +Whether upstream started using a `ztracing` symbol the shim does not provide. +The shim is a proc-macro crate, which can export nothing except proc macros, so +`ztracing::Span`, `ztracing::info_span!`, `ztracing::init()`, `ztracing::Level` +and `ztracing::field` all fail to resolve at compile time and name this crate in +the error. There is no silent fallback. + +The fix then is not to widen the shim by reflex. If the new use is another +zero-cost annotation, add the matching no-op — which means splitting the shim +into a library crate and a proc-macro crate, since one package cannot be both. +If upstream has started routing real telemetry through `ztracing`, that is a +decision record, because no telemetry is enabled by default here. + +And run `cargo deny check licenses sources` at the new revision before anything +else. A monorepo pin can acquire a *different* GPL crate on a different path, +and reading the manifests of the crates you added will not find it. diff --git a/scripts/workspace-guard.sh b/scripts/workspace-guard.sh index 63d6dec..e556650 100755 --- a/scripts/workspace-guard.sh +++ b/scripts/workspace-guard.sh @@ -86,7 +86,55 @@ for pkg in gpui gpui_platform; do 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 and the pinned UI dependency are as recorded." + echo "workspace-guard: crate membership, the pinned UI dependency, and the GPL patch are as recorded." fi exit "$status"