Skip to content

docs(release): tag step must be signed+annotated (git tag -s -m) (#624) #459

docs(release): tag step must be signed+annotated (git tag -s -m) (#624)

docs(release): tag step must be signed+annotated (git tag -s -m) (#624) #459

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
env:
CARGO_TERM_COLOR: always
jobs:
# Default-feature builds + the canonical core test suite (AGENTS.md "Build & test").
build-default:
name: Build & test (default features)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Build workspace (default features)
run: cargo build --workspace --locked
# The release workflow is the only other caller of this script, and it runs on a tag — so
# until now the script itself was unverified code on every PR that changed what it checks,
# and a broken release verifier reached main invisibly (#278, #262). The binary argument is
# the load-bearing part: the artifact leg is the only one that inspects a built executable
# rather than the tree, and it is the leg that actually failed. A tree-only invocation here
# would be a green row blind to exactly the class of failure this guards.
- name: Verify the release version check agrees with the built binary
run: |
VERSION="$(cargo metadata --no-deps --format-version 1 \
| node -e 'const m=JSON.parse(require("fs").readFileSync(0,"utf8"));const p=m.packages.find(p=>p.name==="maxplayer");if(!p){process.stderr.write("no maxplayer package in cargo metadata\n");process.exit(1)}process.stdout.write(p.version)')"
./scripts/verify-release-version.sh "$VERSION" target/debug/maxplayer
# One half of a pair — this build has `acp` off, and the acp job below runs the other verifier
# on a build that has it on. Since #510 this is the ONLY place the racer verifier runs: a
# release ships one binary with `acp` in, so nothing else exercises the buyer-only feature set.
# It stays because that feature set is still supported from source and must keep compiling and
# keep being acp-free, and because a surface verifier that has never run is not a guard.
- name: Verify the racer surface (default features)
run: ./scripts/verify-racer-surface.sh target/debug/maxplayer
- name: Test maxplayer-core (default features)
run: cargo test -p maxplayer-core --locked
# The CLI crate's tests (cli unit tests, cli_e2e.rs, and the #126/#127/#131 daemon self-spawn
# acceptance in mcp_daemon.rs, which drives the real built binary) run nowhere else: every
# other test invocation in this file is -p maxplayer-core (#264). mcp_daemon.rs is gated on the
# wallet feature, which is a default feature, so the default-feature job is the one that
# satisfies its cfg gate.
- name: Test maxplayer (CLI crate, default features)
run: cargo test -p maxplayer --locked
# Same gap, other crate (#264): the relay write-policy plugin's tests (the #372 config-flip
# red-prove, #397 NIP-17 admission) are built by the workspace build but named by no test
# invocation. The crate has no feature gates, so it runs here too.
- name: Test maxplayer-relay-write-policy
run: cargo test -p maxplayer-relay-write-policy --locked
# The acp feature gates the seller-execute path and is off by default, so it rots silently unless
# CI compiles it explicitly (issue #108). Build BOTH crates with acp on — maxplayer-core with the full
# shipped feature combo — then run the acp-gated tests.
build-acp:
name: Build & test (acp)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Build maxplayer (acp + wallet, release)
run: cargo build -p maxplayer --release --features acp,wallet --locked
- name: Build maxplayer-core (full shipped feature combo)
run: cargo build -p maxplayer-core --features acp,gateway,git-delivery,wallet --locked
# The acp-gated tests (the ACP driver units + the #223 seller-concurrency regression:
# N jobs must OVERLAP on one LocalSet thread and the loop must stay live) only exist under
# the acp feature — the default-feature job above compiles them out, so run them here.
- name: Test maxplayer-core (acp)
run: cargo test -p maxplayer-core --features acp --locked
# The CLI crate's acp-gated tests run NOWHERE else: the default-feature job tests -p maxplayer
# WITHOUT acp (so `#[cfg(feature = "acp")]` tests compile out), and the acp steps here only
# BUILD -p maxplayer or TEST -p maxplayer-core (#264, same gap). Without this the seller-surface
# CLI locks are a false green — they compile but never execute (#549): the `seller --help`
# regression, the #542 no-alias foil, and the #533 doctor-wording pin. acp_smoke is `#[ignore]`d
# (env-gated real agent), so it is reported ignored here, not run.
- name: Test maxplayer (CLI crate, acp)
run: cargo test -p maxplayer --features acp,wallet --locked
# Paired with the racer check in the default-feature job: the two scripts must reach opposite
# verdicts on the two builds, which is what shows each one moves with the feature rather than
# with something incidental to the binary it happens to be handed.
- name: Verify the seller surface (acp)
run: ./scripts/verify-seller-surface.sh target/release/maxplayer
# The money path runs in its OWN job, on its own runner, with no `needs:` — the isolation IS the
# feature here, not an accident of layout (#271). Sharing a job means sharing fate: when the acp
# concurrency test dies, every step still queued behind it is marked `skipped` while the job as a
# whole reports `failure`, so the check list shows a single red row that cannot distinguish "the
# money suite failed" from "the money suite never ran" — the one ambiguity a money gate must not
# have. Job isolation is what empirically survives this fault: on run 30487009981 attempt 2 both
# sibling jobs passed while that job died. `if: always()` is the cheaper edit but a weaker
# guarantee — it keeps the suite on the same runner as the fault it is meant to outlive, and still
# buries its result inside another job's conclusion. A separate runner also gives the suite its own
# resource budget, which matters while the acp test's subprocess peak is the leading suspect.
# Co-locating them bought nothing to trade away, either: this suite compiles its own feature set
# (no acp, release) and so reuses none of the acp job's build output. The default-feature job
# carries zero money teeth (it compiles the gateway/git-delivery/wallet tests out entirely), which
# makes this the only job in CI that runs them.
money-path:
name: Money-path tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# Release build: the money-path suite is run in release (as the local money gate is), which
# also sidesteps a debug-only latent bug in the git-delivery HTTP transport
# (git_transport::client_short builds a nested tokio runtime inside the async collect path and
# panics on drop under debug). Interim: release-only until that transport-lifecycle bug is
# fixed (#152); the money assertions here (tip-match refusal, zero-burn, no
# journal/materialization) hold in release.
- name: Test maxplayer-core (money-path suite)
run: cargo test -p maxplayer-core --release --no-default-features --features gateway,git-delivery,wallet --locked
# The release workflow triggers only on a tag, so no pull request ever exercises it. What CI can
# check is that the gates keeping it from publishing are still in place — otherwise the property
# holds only until somebody edits the file, and nothing would say so.
release-gates:
name: Release workflow gates
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/verify-release-workflow.sh
# The tree-side platform surfaces — the npm payload packages, the launcher's pins and its
# runtime resolver — held to .github/release-platforms.json on every pull request.
# `--no-artifacts` skips only the built-artifact check (nothing is built in this job); the
# drift #249 is about is a tree edit, so this catches it here, before a release is ever
# dispatched, rather than only in the release workflow's own dry run.
- run: ./scripts/verify-release-surface.sh --no-artifacts