Skip to content

Move the patch series onto fork branches, and pin them by rev - #12

Merged
jonathanKingston merged 1 commit into
mainfrom
claude/servo-patches-rebase-ci-ba28af
Aug 25, 2026
Merged

jonathanKingston merged 1 commit into
mainfrom
claude/servo-patches-rebase-ci-ba28af

Conversation

@jonathanKingston

@jonathanKingston jonathanKingston commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

What

The servo-patches/*.patch files are gone. The patches now live as tauri-runtime-patches branches on the org's forks, and [patch.crates-io] names them by rev:

crate fork based on commits
servo copse-dev/servo servo 77fccacc (servo 0.5.0) 24
stylo + 7 siblings copse-dev/stylo stylo 67faaab3 (stylo 0.20.0) 5
content-security-policy copse-dev/rust-content-security-policy upstream 05528760 2 (unchanged)

Why

The series existed twice — as files here, and as whatever a consumer's checkout held after applying them — and the recipe pushed the reconciliation onto the consumer: clone two repos at exact revisions, git am 24 patches into one, git apply --3way 5 into the other, wire eleven path overrides. The CSP pair had already moved to a branch for this reason. The rest have followed, and the four steps collapse to two: paste a block, enable a feature.

Verified

cargo generate-lockfile against the block the README now publishes resolves servo 0.5.0 and stylo 0.20.0 from the forks — 816 packages, no path anywhere in the graph. The new check-engine-pins.sh passes against all ten overrides locally.

CI

check-patch-series.shcheck-engine-pins.sh. There is no series to apply, so it instead reads each overridden crate's manifest at its pinned rev and compares the version against Cargo.lock — cargo accepts a [patch] entry only when the replacement satisfies the requirement it replaces, and reports the mismatch at build time rather than in the manifest. It reads ten files over the GitHub API rather than cloning (a shallow servo clone is over a gigabyte), so the job drops from minutes to seconds. A Dependabot bump that outruns the forks still arrives red.

It also found something the old check could not have: the graph carries two selectors, 0.36.1 for an unrelated dependent and 0.40.0 for stylo. The old script's locked_version() would have failed outright on that, but it was only ever called for servo, stylo and CSP, so it never saw it. The new one accepts a pin matching any locked version and fails only when it matches none — the case where the override is silently inert.

prepare-patched-servo.sh loses its cloning and patching and keeps the part that mattered: lifting the block out of README.md rather than restating it.

Reviewer note

The fork branches are the artefact now, so a force-push to either invalidates the pins here — check-engine-pins.sh is what catches that.

The series lived twice: as .patch files here, and as whatever a consumer's
checkout held after applying them. A second copy can only drift from the
first, and the recipe made the drift a consumer's problem — clone two
repositories at exact revisions, `git am` twenty-four patches into one and
`git apply --3way` five into the other, then wire eleven path overrides.
Four steps, every one of which could be got subtly wrong on a machine nobody
else could see.

The CSP pair had already left for a fork branch, for exactly this reason.
The rest have now followed. `tauri-runtime-patches` on copse-dev/servo
carries the twenty-four servo commits on `77fccacc`, the revision `servo
0.5.0` was cut from; the same branch on copse-dev/stylo carries the five
stylo commits on `67faaab3`. The overrides name those branches by rev, so
cargo fetches them like any other git dependency and the four steps collapse
into two: paste a block, enable a feature. Verified end to end — `cargo
generate-lockfile` against the published block resolves servo 0.5.0 and
stylo 0.20.0 from the forks, 816 packages, no path anywhere in the graph.

The commits were swept on the way across. Twenty-two of the twenty-four were
authored as Claude with Co-Authored-By and session trailers, which is not how
work destined for upstream should arrive; all twenty-nine are now authored as
the person who has to stand behind them, with the trailers dropped. Nine
Americanisms went with them (honor, behavior), and the stylo messages lost
their "To try it" postscripts, which described a workflow that no longer
exists.

CI follows the change in kind, not just in name. There is no series to apply,
so `check-patch-series.sh` becomes `check-engine-pins.sh`: for every override
the README publishes it reads that crate's manifest at the pinned rev and
compares the version against Cargo.lock, because cargo accepts a `[patch]`
entry only when the replacement satisfies the requirement it replaces — and
reports the mismatch at build time rather than in the manifest. It reads ten
files over the API instead of cloning, so the job drops from a couple of
minutes to seconds, and a Dependabot bump that outruns the forks still
arrives red, which was always the point.

That check found something the old one could not have: the graph carries two
`selectors`, 0.36.1 for an unrelated dependent and 0.40.0 for stylo. Cargo
patches whichever entries the replacement satisfies and leaves the rest, so
the check accepts a pin matching any locked version and fails only when it
matches none — the case where the override is silently inert.

prepare-patched-servo.sh loses its cloning and patching and keeps only the
part that mattered: lifting the block out of README.md rather than restating
it, so what CI builds is what a consumer is handed.
@jonathanKingston
jonathanKingston merged commit 4a63fa6 into main Aug 25, 2026
6 checks passed
jonathanKingston added a commit to copse-dev/agent-pane that referenced this pull request Aug 25, 2026
…#1939)

Pairs with
[copse-dev/tauri-runtime-servo#12](copse-dev/tauri-runtime-servo#12),
which moves the patch series onto `tauri-runtime-patches` branches of
the org's engine forks. This is the consumer side.

## The bug this fixes

Servo mode had two defaults that disagreed. `build:servo` wrote a
`tauri.html` with its CSP **enforced**, which only a patched engine can
satisfy — while the shell's `[patch]` block was commented out and
`patched-servo` was off, so cargo built **stock** libservo. Stock Servo
gives `tauri://localhost` an opaque origin that CSP `'self'` can never
match, so following the README exactly gave you a blank window with
every same-origin subresource blocked. Both working configurations were
one manual step away; neither was the one you got for free.

## What changes

The engine is no longer something you assemble:

```toml
[patch.crates-io]
servo = { git = "https://github.com/copse-dev/servo", rev = "3cb68676..." }
# ... the CSP fork and all eight stylo crates
```

Live, pinned by rev, fetched by cargo. No clone, no `git am`, no path
overrides, nothing to uncomment. `patched-servo` becomes a default
feature to match, which is safe in the way that matters: enabling it
against stock libservo is a **compile error**, not a silent no-op,
because the pref it sets is a struct field only the patched tree has.
The feature and the overrides cannot drift apart unnoticed, and the
enforced CSP is now correct rather than aspirational.

## Engine iteration still works — and is now the cheap path, not the
only one

Put a checkout at `../servo` or `../stylo` and `build:servo` writes a
gitignored `tauri-shell/.cargo/config.toml` redirecting those crates at
it. A `[patch]` in a cargo config takes precedence over the manifest's
**per crate**, so a checkout of one repository wins for what it provides
while the pins keep covering the other. Delete it and the next run
removes the file — the case that would otherwise bite, since a config
pointing at a directory that has gone leaves cargo reporting the error
instead of us.

Every run reports which of the three modes it took:

```
servo engine: pinned (no sibling checkout — using the forks pinned in Cargo.toml)
servo engine: checkout (../servo and ../stylo — wrote tauri-shell/.cargo/config.toml)
servo engine: artifacts-only (COPSE_SERVO_ARTIFACTS_ONLY=1 — engine untouched)
```

## CI

The `servo mode` job sets `COPSE_SERVO_ARTIFACTS_ONLY=1`. It exists to
catch the sidecar bundle rotting, never invokes cargo, and resolving an
engine there would be pure cost.

## Verified locally

- All three modes exercised, including that removing a checkout removes
the generated config.
- `cargo generate-lockfile` resolves `servo 0.5.0` and `stylo 0.20.0`
from the forks; the lockfile here is regenerated against the pins.
- `cargo metadata --no-deps` confirms `default = ["patched-servo"] →
tauri-runtime-servo/patched-servo`.
- typecheck, lint, format, dead-code (801 modules), plain `pnpm build`
still servo-free, `build:servo` + sidecar smoke test.
- Unit suite 7654/7656. The two failures are `hooks/session-start` and
`hooks/after-file-edit`, which pass 6/6 and 10/10 in isolation —
timing-sensitive under parallel load, and nowhere near this change.

## Stock-engine builds

Still supported, but now both halves are explicit, because getting one
right and not the other is exactly what caused the original bug:

```bash
COPSE_TAURI_STRIP_CSP=1 pnpm build:servo
cd tauri-shell && cargo run --no-default-features
```

## Merge order

Land
[tauri-runtime-servo#12](copse-dev/tauri-runtime-servo#12)
first if you want the two repos' recipes consistent, though nothing here
depends on it: the fork branches already exist and the revs are pinned,
so this builds either way.

---------

Co-authored-by: Jonathan Kingston <KingstonMailBox@gmail.com>
jonathanKingston added a commit to copse-dev/agent-pane that referenced this pull request Aug 31, 2026
…#1939)

Pairs with
[copse-dev/tauri-runtime-servo#12](copse-dev/tauri-runtime-servo#12),
which moves the patch series onto `tauri-runtime-patches` branches of
the org's engine forks. This is the consumer side.

## The bug this fixes

Servo mode had two defaults that disagreed. `build:servo` wrote a
`tauri.html` with its CSP **enforced**, which only a patched engine can
satisfy — while the shell's `[patch]` block was commented out and
`patched-servo` was off, so cargo built **stock** libservo. Stock Servo
gives `tauri://localhost` an opaque origin that CSP `'self'` can never
match, so following the README exactly gave you a blank window with
every same-origin subresource blocked. Both working configurations were
one manual step away; neither was the one you got for free.

## What changes

The engine is no longer something you assemble:

```toml
[patch.crates-io]
servo = { git = "https://github.com/copse-dev/servo", rev = "3cb68676..." }
# ... the CSP fork and all eight stylo crates
```

Live, pinned by rev, fetched by cargo. No clone, no `git am`, no path
overrides, nothing to uncomment. `patched-servo` becomes a default
feature to match, which is safe in the way that matters: enabling it
against stock libservo is a **compile error**, not a silent no-op,
because the pref it sets is a struct field only the patched tree has.
The feature and the overrides cannot drift apart unnoticed, and the
enforced CSP is now correct rather than aspirational.

## Engine iteration still works — and is now the cheap path, not the
only one

Put a checkout at `../servo` or `../stylo` and `build:servo` writes a
gitignored `tauri-shell/.cargo/config.toml` redirecting those crates at
it. A `[patch]` in a cargo config takes precedence over the manifest's
**per crate**, so a checkout of one repository wins for what it provides
while the pins keep covering the other. Delete it and the next run
removes the file — the case that would otherwise bite, since a config
pointing at a directory that has gone leaves cargo reporting the error
instead of us.

Every run reports which of the three modes it took:

```
servo engine: pinned (no sibling checkout — using the forks pinned in Cargo.toml)
servo engine: checkout (../servo and ../stylo — wrote tauri-shell/.cargo/config.toml)
servo engine: artifacts-only (COPSE_SERVO_ARTIFACTS_ONLY=1 — engine untouched)
```

## CI

The `servo mode` job sets `COPSE_SERVO_ARTIFACTS_ONLY=1`. It exists to
catch the sidecar bundle rotting, never invokes cargo, and resolving an
engine there would be pure cost.

## Verified locally

- All three modes exercised, including that removing a checkout removes
the generated config.
- `cargo generate-lockfile` resolves `servo 0.5.0` and `stylo 0.20.0`
from the forks; the lockfile here is regenerated against the pins.
- `cargo metadata --no-deps` confirms `default = ["patched-servo"] →
tauri-runtime-servo/patched-servo`.
- typecheck, lint, format, dead-code (801 modules), plain `pnpm build`
still servo-free, `build:servo` + sidecar smoke test.
- Unit suite 7654/7656. The two failures are `hooks/session-start` and
`hooks/after-file-edit`, which pass 6/6 and 10/10 in isolation —
timing-sensitive under parallel load, and nowhere near this change.

## Stock-engine builds

Still supported, but now both halves are explicit, because getting one
right and not the other is exactly what caused the original bug:

```bash
COPSE_TAURI_STRIP_CSP=1 pnpm build:servo
cd tauri-shell && cargo run --no-default-features
```

## Merge order

Land
[tauri-runtime-servo#12](copse-dev/tauri-runtime-servo#12)
first if you want the two repos' recipes consistent, though nothing here
depends on it: the fork branches already exist and the revs are pinned,
so this builds either way.

---------

Co-authored-by: Jonathan Kingston <jonathan@copse.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant