Skip to content

Commit 39bd4d8

Browse files
author
t
committed
Merge branch 'main' into fix/served-git-concurrency-cap
Brings the branch onto post-#243 main so cargo metadata --locked passes against the committed tree.
2 parents 4b69c45 + c83cbc5 commit 39bd4d8

13 files changed

Lines changed: 2418 additions & 38 deletions

File tree

‎.github/workflows/pr-checks.yml‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ on:
66
# result before it lands. Guards against semantic merge races where two
77
# individually-green PRs break main in combination (#113 + #145 → #229).
88
merge_group:
9+
# Release-branch heads are created and pushed by GITHUB_TOKEN (release-please
10+
# and the lock-sync job), and GitHub's recursion guard means those pushes
11+
# trigger no workflow runs. workflow_dispatch is the documented carve-out that
12+
# GITHUB_TOKEN CAN trigger, so the release flow dispatches this workflow at
13+
# the release branch to attach check runs to its head. Read-only workflow
14+
# (contents: read), so the dispatch surface adds no privilege.
15+
workflow_dispatch:
916
push:
1017
branches:
1118
- main
@@ -46,7 +53,7 @@ jobs:
4653
run: cargo fmt --all -- --check
4754

4855
- name: cargo clippy
49-
run: cargo clippy --workspace --all-targets -- -D warnings
56+
run: cargo clippy --locked --workspace --all-targets -- -D warnings
5057

5158
test:
5259
name: test (${{ matrix.toolchain }})
@@ -94,7 +101,7 @@ jobs:
94101
- name: cargo test
95102
env:
96103
DATABASE_URL: postgres://postgres:postgres@localhost:5432/gitlawb_test
97-
run: cargo test --workspace
104+
run: cargo test --locked --workspace
98105

99106
build-release:
100107
name: build --release
@@ -117,7 +124,7 @@ jobs:
117124
key: release
118125

119126
- name: cargo build --release
120-
run: cargo build --release --workspace
127+
run: cargo build --locked --release --workspace
121128

122129
audit:
123130
name: cargo audit
@@ -199,7 +206,7 @@ jobs:
199206
key: msrv
200207

201208
- name: cargo check on MSRV
202-
run: cargo check --workspace --all-targets
209+
run: cargo check --locked --workspace --all-targets
203210

204211
docker-build:
205212
name: Docker build smoke test
@@ -246,7 +253,7 @@ jobs:
246253
key: windows
247254

248255
- name: cargo test (shipped Windows crates)
249-
run: cargo test -p gl -p git-remote-gitlawb
256+
run: cargo test --locked -p gl -p git-remote-gitlawb
250257

251258
# gitlawb-core is embedded by every consumer (gl, git-remote-gitlawb, the node
252259
# daemon), so it must stay lean. This gate fails if gitlawb-core's normal

‎.github/workflows/release.yml‎

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,151 @@ jobs:
4141
release_created: ${{ steps.release.outputs.release_created }}
4242
tag_name: ${{ steps.release.outputs.tag_name }}
4343
version: ${{ steps.release.outputs.version }}
44+
pr: ${{ steps.release.outputs.pr }}
4445
steps:
4546
- name: Run release-please
4647
id: release
4748
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
4849
with:
4950
token: ${{ secrets.GITHUB_TOKEN }}
5051

52+
# The release PR bumps the workspace crate versions in the Cargo.tomls but
53+
# release-please cannot update Cargo.lock, so without this job every release
54+
# merge ships a tag whose lockfile disagrees with its manifests (a --locked
55+
# build from the tag fails, and pr-checks builds --locked). Sync the lock on
56+
# the release branch so the tagged tree is internally consistent.
57+
#
58+
# Runs on every push-triggered release run, not only when release-please
59+
# reports the PR: the `pr` output is set only on runs that CREATE or UPDATE
60+
# the release PR, so gating on it would leave an already-open release PR
61+
# (opened before this job existed, or untouched by a non-releasable push)
62+
# permanently without the sync and without check runs. The resolve step
63+
# discovers any open release-please PR itself and no-ops when there is none.
64+
sync-release-lock:
65+
name: Sync Cargo.lock on the release PR
66+
needs: release-please
67+
if: ${{ !cancelled() && github.repository == 'Gitlawb/node' && github.event_name != 'workflow_dispatch' }}
68+
runs-on: ubuntu-latest
69+
permissions:
70+
contents: write
71+
pull-requests: read
72+
# actions: write lets the final step dispatch pr-checks on the release
73+
# branch. Note this grant is repo-wide dispatch rights (GitHub cannot
74+
# scope it to one workflow); acceptable here because reaching this job's
75+
# steps already requires write to main. Needed because every push to the
76+
# release branch (release-please's and the lock sync below) is made with
77+
# GITHUB_TOKEN, which triggers no workflow runs, so without an explicit
78+
# dispatch the release PR's head carries no check runs and the --locked
79+
# gate never validates it.
80+
actions: write
81+
steps:
82+
# Prefer the fresh `pr` output when this run just created/updated the
83+
# release PR; otherwise fall back to querying for an open release-please
84+
# branch (prefix match: the branch carries `--components--` suffixes).
85+
- name: Resolve the open release PR branch
86+
id: resolve
87+
env:
88+
GH_TOKEN: ${{ github.token }}
89+
OWNER: ${{ github.repository_owner }}
90+
BRANCH_FROM_OUTPUT: ${{ needs.release-please.outputs.pr && fromJSON(needs.release-please.outputs.pr).headBranchName || '' }}
91+
run: |
92+
branch="$BRANCH_FROM_OUTPUT"
93+
if [ -z "$branch" ]; then
94+
# The branch name alone is not a trust signal. `gh pr list` includes
95+
# fork PRs, and a fork's headRefName is bare (no owner prefix), so a
96+
# prefix match by itself lets any fork nominate the branch this job
97+
# checks out, commits to, and dispatches checks on. Require the head
98+
# repo to be THIS repository and the author to be the release-please
99+
# app, so neither a fork nor a same-repo human branch named
100+
# `release-please--*` can be selected. Then take the highest PR
101+
# number: `[0]` depended on gh's default ordering, which is not a
102+
# documented guarantee, and picking a stable one keeps a second
103+
# (component) release PR from making the choice flap between runs.
104+
branch=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open \
105+
--json headRefName,headRepositoryOwner,number,author \
106+
--jq '[ .[]
107+
| select(.headRepositoryOwner.login == env.OWNER)
108+
| select(.author.login == "app/github-actions")
109+
| select(.headRefName | startswith("release-please--"))
110+
] | max_by(.number).headRefName // empty')
111+
fi
112+
if [ -z "$branch" ]; then
113+
echo "no open release PR; nothing to sync"
114+
fi
115+
echo "branch=$branch" >> "$GITHUB_OUTPUT"
116+
117+
- name: Checkout release branch
118+
if: ${{ steps.resolve.outputs.branch != '' }}
119+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
120+
with:
121+
ref: ${{ steps.resolve.outputs.branch }}
122+
123+
# Resolve on a declared toolchain instead of whatever Rust the runner
124+
# image happens to ship, so the lock this job writes comes off the same
125+
# stable channel the other cargo jobs use. Not a claim of identical
126+
# resolution: `stable` still moves between runs, and the MSRV gate
127+
# checks this same lock on 1.91 (pr-checks.yml). It only removes the
128+
# runner image as an undeclared input.
129+
- name: Install Rust toolchain
130+
if: ${{ steps.resolve.outputs.branch != '' }}
131+
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable
132+
with:
133+
toolchain: stable
134+
135+
- name: Sync workspace crate versions into Cargo.lock
136+
if: ${{ steps.resolve.outputs.branch != '' }}
137+
run: cargo update --workspace
138+
139+
- name: Commit and push when the lock changed
140+
if: ${{ steps.resolve.outputs.branch != '' }}
141+
run: |
142+
if git diff --quiet Cargo.lock; then
143+
echo "Cargo.lock already in sync"
144+
exit 0
145+
fi
146+
git config user.name "github-actions[bot]"
147+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
148+
git add Cargo.lock
149+
git commit -m "chore: sync Cargo.lock with the release version bump"
150+
git push
151+
152+
# Every push to the release branch is made with GITHUB_TOKEN (release-
153+
# please's own pushes and the lock sync above), and events created by
154+
# GITHUB_TOKEN start no workflow runs. workflow_dispatch is the documented
155+
# exception GITHUB_TOKEN may trigger, so dispatch pr-checks at the release
156+
# branch to attach check runs to its current head. Unconditional (also on
157+
# the already-in-sync path) because the branch's ORIGINAL head has the
158+
# same no-runs problem. Loud on failure by design: a silent fallback
159+
# would recreate the checkless-head problem, and a job rerun is safe
160+
# (sync no-ops, dispatch retries). Loop-safe: pr-checks is read-only and
161+
# dispatches nothing. Runs after the push ack, so the dispatched ref tip
162+
# is the sync commit. Merge-queue note: this validates the branch tip;
163+
# the enforced merge queue's merge_group run still re-validates the true
164+
# merged result before landing.
165+
- name: Dispatch PR checks on the release branch
166+
if: ${{ steps.resolve.outputs.branch != '' }}
167+
env:
168+
GH_TOKEN: ${{ github.token }}
169+
BRANCH: ${{ steps.resolve.outputs.branch }}
170+
run: |
171+
# Every dispatch failure fails the job, including the 422 a release
172+
# branch cut before pr-checks gained its workflow_dispatch trigger
173+
# returns ("does not have a workflow_dispatch trigger", reported
174+
# against the dispatched ref's copy of the file). An earlier revision
175+
# tolerated exactly that 422 on the theory that release-please would
176+
# rewrite the branch and heal it, but a rewrite only happens when a
177+
# later releasable commit lands: an open release PR nobody touches is
178+
# never rewritten. Tolerating it therefore left the lock-sync commit
179+
# this job had just pushed sitting on the release head with no
180+
# --locked validation, which is the precise state this workflow exists
181+
# to prevent. The tolerance is also no longer needed: once this lands,
182+
# every release branch is cut from a main that already carries the
183+
# trigger, so the only branch that could 422 is one open at merge time
184+
# (there is none), and a job rerun after a manual rebase is safe (the
185+
# sync no-ops, the dispatch retries).
186+
gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/pr-checks.yml/dispatches" \
187+
-f "ref=$BRANCH"
188+
51189
# Each architecture builds NATIVELY (amd64 on ubuntu-latest, arm64 on
52190
# ubuntu-24.04-arm) and pushes by digest; docker-manifest below stitches the
53191
# digests into the tagged multi-arch image. No QEMU: emulating the arm64
@@ -308,7 +446,7 @@ jobs:
308446
set -euo pipefail
309447
args=""
310448
for b in $BINS; do args="$args -p $b"; done
311-
cargo build --release --target ${{ matrix.target }} $args
449+
cargo build --release --locked --target ${{ matrix.target }} $args
312450
313451
# Run each packaged binary's --version so a broken release artifact fails the
314452
# build instead of shipping. Every target builds on a matching-arch runner,

‎Cargo.lock‎

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Dockerfile‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,33 @@ FROM rust:1.91-bookworm AS builder
88

99
WORKDIR /build
1010

11-
# Cache dependencies first for faster rebuilds
11+
# Cache dependencies first for faster rebuilds.
12+
# EVERY workspace member's manifest must be copied here, not just the ones this
13+
# image ships: cargo loads the whole workspace before it resolves anything, so a
14+
# missing member's Cargo.toml (or a path dependency pointing at one) aborts the
15+
# layer before a single crate is fetched.
1216
COPY Cargo.toml Cargo.lock ./
1317
COPY crates/gitlawb-core/Cargo.toml crates/gitlawb-core/
1418
COPY crates/gitlawb-node/Cargo.toml crates/gitlawb-node/
1519
COPY crates/gl/Cargo.toml crates/gl/
1620
COPY crates/git-remote-gitlawb/Cargo.toml crates/git-remote-gitlawb/
21+
COPY crates/gitlawb-attest/Cargo.toml crates/gitlawb-attest/
22+
COPY crates/icaptcha-client/Cargo.toml crates/icaptcha-client/
1723

18-
# Fetch deps (this layer caches until Cargo.{toml,lock} change)
19-
RUN mkdir -p crates/gitlawb-core/src crates/gitlawb-node/src crates/gl/src crates/git-remote-gitlawb/src && \
24+
# Fetch deps (this layer caches until Cargo.{toml,lock} change).
25+
# No `|| true`: this layer is expected to succeed, and swallowing its exit code
26+
# is what let it sit dead from the moment gl gained a path dependency on
27+
# icaptcha-client. A failure here should stop the build, not silently turn every
28+
# image build into a cold one.
29+
RUN mkdir -p crates/gitlawb-core/src crates/gitlawb-node/src crates/gl/src \
30+
crates/git-remote-gitlawb/src crates/gitlawb-attest/src crates/icaptcha-client/src && \
2031
echo 'fn main() {}' > crates/gitlawb-node/src/main.rs && \
2132
echo 'fn main() {}' > crates/gl/src/main.rs && \
2233
echo 'fn main() {}' > crates/git-remote-gitlawb/src/main.rs && \
2334
echo '' > crates/gitlawb-core/src/lib.rs && \
24-
cargo build --release -p gitlawb-node -p gl -p git-remote-gitlawb || true
35+
echo '' > crates/gitlawb-attest/src/lib.rs && \
36+
echo '' > crates/icaptcha-client/src/lib.rs && \
37+
cargo build --release --locked -p gitlawb-node -p gl -p git-remote-gitlawb
2538

2639
# Now copy real sources and build for real.
2740
# Force-bump mtimes so cargo's fingerprint check rebuilds — without this,
@@ -34,7 +47,7 @@ RUN find crates -name "*.rs" -exec touch {} + && \
3447
rm -rf target/release/.fingerprint/gitlawb-node-* \
3548
target/release/.fingerprint/gl-* \
3649
target/release/.fingerprint/git-remote-gitlawb-* && \
37-
cargo build --release -p gitlawb-node -p gl -p git-remote-gitlawb && \
50+
cargo build --release --locked -p gitlawb-node -p gl -p git-remote-gitlawb && \
3851
strip target/release/gitlawb-node target/release/gl target/release/git-remote-gitlawb
3952

4053
# ── Runtime stage ───────────────────────────────────────────────────────────

‎Dockerfile.bins‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
# Used by scripts/build-bins.sh — not used for node deployment.
44
ARG TARGET=x86_64-unknown-linux-musl
55

6-
FROM rust:1.85-bookworm AS builder
6+
# Must stay at or above the workspace `rust-version` in Cargo.toml. An unmet
7+
# MSRV is a hard cargo error, and nothing in CI builds this file, so a stale
8+
# base image here fails only when someone runs scripts/build-bins.sh.
9+
FROM rust:1.91-bookworm AS builder
710
ARG TARGET
811

912
WORKDIR /build
@@ -23,7 +26,7 @@ COPY crates/ crates/
2326
# Configure cross-linker for aarch64
2427
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc
2528

26-
RUN cargo build --release --target "$TARGET" -p gl -p git-remote-gitlawb
29+
RUN cargo build --release --locked --target "$TARGET" -p gl -p git-remote-gitlawb
2730

2831
FROM debian:bookworm-slim
2932
ARG TARGET

0 commit comments

Comments
 (0)