From f5637fc57484e5ed4123116ab0ecb1e9cfad9e9e Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Thu, 3 Sep 2026 22:07:01 +0200 Subject: [PATCH] ci: check for an already-published version before authenticating The v0.6.0 tag push failed on 'No Trusted Publishing config found' even though 0.6.0 was already on crates.io, published by hand minutes earlier. It should have been a no-op. The skip is what makes a re-pushed tag recoverable - the whole reason it exists - and it could not do that job from behind the auth step, because it never ran. A step that decides whether to act should not sit downstream of acquiring the means to act. The check now runs first and gates both the auth and the publish. A tag for a version already on crates.io is now a clean green no-op whether or not Trusted Publishing has ever been configured. Trusted Publishing is still unconfigured on crates.io, so the first real release through this workflow will still fail at the auth step - which is the correct failure, and the header says how to fix it. Signed-off-by: Glenn Gore --- .github/workflows/publish.yml | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5e591ae..6341c23 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -56,23 +56,37 @@ jobs: fi echo "publishing dtg-credentials ${crate}" - - name: Authenticate to crates.io - uses: rust-lang/crates-io-auth-action@v1 - id: auth - - - name: Publish - env: - CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + # Before authenticating, not after. The skip is what makes a re-pushed tag + # recoverable, and it cannot do that job from behind the auth step: the + # v0.6.0 tag push found 0.6.0 already on crates.io (published by hand + # minutes earlier) and still failed, because it never got past + # "No Trusted Publishing config found" to reach the check that would have + # said there was nothing to do. A step that decides whether to act should + # not sit downstream of acquiring the means to act. + - name: Skip if this version is already published + id: check run: | set -euo pipefail version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version') + echo "version=${version}" >> "$GITHUB_OUTPUT" # The sparse index is newline-delimited JSON, one object per version. # A 404 (name never published) is a clean "not there". if curl -sSf "https://index.crates.io/dt/g-/dtg-credentials" 2>/dev/null \ | jq -se --arg v "$version" 'any(.[]; .vers == $v)' >/dev/null; then echo "::notice::dtg-credentials ${version} is already on crates.io — nothing to do" - exit 0 + echo "published=true" >> "$GITHUB_OUTPUT" + else + echo "published=false" >> "$GITHUB_OUTPUT" fi - cargo publish --locked + - name: Authenticate to crates.io + if: steps.check.outputs.published == 'false' + uses: rust-lang/crates-io-auth-action@v1 + id: auth + + - name: Publish + if: steps.check.outputs.published == 'false' + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + run: cargo publish --locked