From f548bf807e4cb0de3bbe626cd81ae9a689318515 Mon Sep 17 00:00:00 2001 From: meh Date: Thu, 24 Sep 2026 12:21:59 +0700 Subject: [PATCH 1/4] Publish the way nago-wss does cargo read-manifest for the version and cargo info for whether crates.io has it, instead of a hand-rolled crates.io API query. Same house scheme: bumping the version in a PR is the release; a merge without a bump publishes nothing. --- .github/workflows/publish.yml | 92 +++++++++++++---------------------- 1 file changed, 35 insertions(+), 57 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 95cbd72..ad5a163 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,76 +1,54 @@ name: Publish -# Bumping `version` in Cargo.toml in a normal PR is the whole release: when it -# merges to master, this publishes whatever version crates.io does not have -# yet. endpoint-gen-macros goes first, since endpoint-gen depends on it by -# version. A merge that changes code but no version finds both published and -# exits green. +# House scheme, as nago-wss: a version bump on the default branch is the +# release. No tags, no manual step. Publishing is irreversible, so the trigger +# has to be something you cannot do by accident, and editing the version field +# is that. # -# This exists because 1.14.0 merged and sat unpublished until someone ran -# `cargo publish` by hand, while four repos already declared it in -# config/version.toml. +# Two crates: endpoint-gen-macros is published first, because endpoint-gen +# depends on it by version. Each is checked and published on its own. on: push: branches: [master] - paths: - - Cargo.toml - - build.rs - - src/** - - endpoint-gen-macros/** + paths: ['Cargo.toml', 'endpoint-gen-macros/Cargo.toml'] + # A release that was merged before this gate was fixed has no Cargo.toml + # change left to make, so it cannot be triggered by the push rule alone. workflow_dispatch: - inputs: - dry_run: - description: 'Package and verify, but do not upload' - required: false - type: boolean - default: false - -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: false jobs: publish: - name: crates.io runs-on: ubicloud-standard-2 - timeout-minutes: 30 steps: - - uses: actions/checkout@v6 - + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - - name: Test - run: cargo test --release - - - name: Publish what is new + # Checked before the upload rather than during it: an absent token + # surfaces as an authentication error from `cargo publish` after the + # package is built, which reads like a registry problem. + - run: | + if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then + echo "CARGO_REGISTRY_TOKEN is empty. Set it on pathscale/EndpointGen:" + echo " gh secret set CARGO_REGISTRY_TOKEN --repo pathscale/EndpointGen" + exit 1 + fi env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - DRY_RUN: ${{ inputs.dry_run }} + + - run: cargo test --release + + # The registry is the only authority on what is already released. + - name: Publish each crate whose version is not on crates.io run: | - published() { - curl -sS -H 'User-Agent: pathscale-ci' "https://crates.io/api/v1/crates/$1/versions" \ - | jq -r --arg v "$2" '.versions[]? | select(.num == $v) | .num' - } - for dir in endpoint-gen-macros .; do - name=$(cargo metadata --no-deps --format-version 1 --manifest-path "$dir/Cargo.toml" \ - | jq -r --arg m "$(cd "$dir" && pwd)/Cargo.toml" '.packages[] | select(.manifest_path == $m) | .name') - version=$(cargo metadata --no-deps --format-version 1 --manifest-path "$dir/Cargo.toml" \ - | jq -r --arg n "$name" '.packages[] | select(.name == $n) | .version') - if [ -n "$(published "$name" "$version")" ]; then - echo "$name $version is already on crates.io" | tee -a "$GITHUB_STEP_SUMMARY" - continue - fi - if [ "$DRY_RUN" = "true" ]; then - cargo package --manifest-path "$dir/Cargo.toml" - echo "Dry run: packaged $name $version" | tee -a "$GITHUB_STEP_SUMMARY" - continue - fi - if [ -z "$CARGO_REGISTRY_TOKEN" ]; then - echo "CARGO_REGISTRY_TOKEN is not set on this repository (Settings -> Secrets -> Actions)" >&2 - exit 1 + set -eu + for manifest in endpoint-gen-macros/Cargo.toml Cargo.toml; do + name=$(cargo read-manifest --manifest-path "$manifest" | jq -er '.name') + version=$(cargo read-manifest --manifest-path "$manifest" | jq -er '.version') + if cargo info --registry crates-io "$name@$version" >/dev/null 2>&1; then + echo "$name $version is already on crates.io, nothing to publish" + else + echo "$name $version is not on crates.io, publishing" + cargo publish --manifest-path "$manifest" --token "$CARGO_REGISTRY_TOKEN" fi - cargo publish --manifest-path "$dir/Cargo.toml" - echo "Published $name $version" | tee -a "$GITHUB_STEP_SUMMARY" done + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} From cc07040ad17ee62969d76970c226f5332bce0063 Mon Sep 17 00:00:00 2001 From: meh Date: Thu, 24 Sep 2026 12:26:38 +0700 Subject: [PATCH 2/4] Generate the lockfile before the audit, as honey_id-types does The audit reads Cargo.lock, which is no longer tracked, so it failed on master since that change. --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c06f951..62401f4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -104,6 +104,8 @@ jobs: runs-on: ubicloud-standard-2 steps: - uses: actions/checkout@v4 + - name: Resolve dependencies for audit + run: cargo generate-lockfile - uses: rustsec/audit-check@v2 with: token: ${{ secrets.GITHUB_TOKEN }} From 96e47958b03597823c4929bea6a8bdd07d0e1a63 Mon Sep 17 00:00:00 2001 From: meh Date: Thu, 24 Sep 2026 12:27:01 +0700 Subject: [PATCH 3/4] Move the security audit to a workflow run by hand It spent three minutes installing cargo-audit on every push and PR. Tests, clippy and fmt stay automatic. --- .github/workflows/audit.yml | 17 +++++++++++++++++ .github/workflows/rust.yml | 10 ---------- 2 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/audit.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..5015bad --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,17 @@ +name: Security audit + +# Run by hand: it takes minutes to install cargo-audit and rarely says anything +# that changes a merge, so it is not in the CI every push waits on. +on: + workflow_dispatch: + +jobs: + security_audit: + runs-on: ubicloud-standard-2 + steps: + - uses: actions/checkout@v4 + - name: Resolve dependencies for audit + run: cargo generate-lockfile + - uses: rustsec/audit-check@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 62401f4..109de39 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -99,13 +99,3 @@ jobs: - name: Check formatting run: cargo fmt --all -- --check - - security_audit: - runs-on: ubicloud-standard-2 - steps: - - uses: actions/checkout@v4 - - name: Resolve dependencies for audit - run: cargo generate-lockfile - - uses: rustsec/audit-check@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} From b96242bf66cd4f23618489ce632462b21c57ed52 Mon Sep 17 00:00:00 2001 From: meh Date: Thu, 24 Sep 2026 12:32:09 +0700 Subject: [PATCH 4/4] Remove the security audit workflow --- .github/workflows/audit.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/audit.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml deleted file mode 100644 index 5015bad..0000000 --- a/.github/workflows/audit.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Security audit - -# Run by hand: it takes minutes to install cargo-audit and rarely says anything -# that changes a merge, so it is not in the CI every push waits on. -on: - workflow_dispatch: - -jobs: - security_audit: - runs-on: ubicloud-standard-2 - steps: - - uses: actions/checkout@v4 - - name: Resolve dependencies for audit - run: cargo generate-lockfile - - uses: rustsec/audit-check@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }}