From 138e0dff84d88f62cf8cad4fb54d2324933aa24e Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 20 Sep 2026 13:06:37 +0200 Subject: [PATCH] Move the release signing baseline to 3.1.6 and catch lightweight tags in CI The 3.1.5 tag was created server-side by publishing the GitHub release, which can only produce a lightweight ref, so it carries no signature and git verify-tag fails on it. Rather than force-move a published tag, state that signing starts at 3.1.6 and cut that tag locally with git tag -s before the release is published. vtag.yml now fails the run when the pushed tag is not a tag object, so the same mistake shows up as a red check on the release instead of arriving as a user report. The mirror step runs first, so v is still created; its early "already exists" return became an else branch so the check is never skipped. https://github.com/acmesh-official/acme.sh/issues/7273 --- .github/workflows/vtag.yml | 37 ++++++++++++++++++++++++++++++------- README.md | 8 ++++---- acme.sh | 2 +- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/.github/workflows/vtag.yml b/.github/workflows/vtag.yml index 6b5de15a..d971d057 100644 --- a/.github/workflows/vtag.yml +++ b/.github/workflows/vtag.yml @@ -6,6 +6,9 @@ name: Mirror version tag # pointing to the same object, so both forms exist. # No retrigger loop: the tag filter never matches a "v"-prefixed tag, and # refs created with GITHUB_TOKEN do not fire workflows anyway. +# The job mirrors first and then fails when the pushed tag is lightweight, +# which is what the release form produces: that tag can never carry a +# signature, so the failure has to be loud. on: push: @@ -26,19 +29,39 @@ jobs: REPO: ${{ github.repository }} TAG: ${{ github.ref_name }} run: | - if gh api "repos/$REPO/git/ref/tags/v$TAG" >/dev/null 2>&1; then - echo "Tag v$TAG already exists, nothing to do." - exit 0 - fi # Mirror the object the pushed tag actually points at: the commit # for a lightweight tag, the tag object itself for an annotated or # signed one. Pointing the mirror at the commit would strip the # signature, so "git verify-tag v3.1.3" would fail while # "git verify-tag 3.1.3" succeeds. - sha="$(gh api "repos/$REPO/git/ref/tags/$TAG" --jq .object.sha)" + _ref="$(gh api "repos/$REPO/git/ref/tags/$TAG" --jq '.object.sha + " " + .object.type')" + sha="${_ref%% *}" + objtype="${_ref##* }" if [ -z "$sha" ] || [ "$sha" = "null" ]; then echo "Could not resolve refs/tags/$TAG" exit 1 fi - gh api "repos/$REPO/git/refs" -f ref="refs/tags/v$TAG" -f sha="$sha" - echo "Created tag v$TAG -> $sha" + if gh api "repos/$REPO/git/ref/tags/v$TAG" >/dev/null 2>&1; then + echo "Tag v$TAG already exists, nothing to do." + else + gh api "repos/$REPO/git/refs" -f ref="refs/tags/v$TAG" -f sha="$sha" + echo "Created tag v$TAG -> $sha" + fi + + - name: Check that the tag is signable + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + TAG: ${{ github.ref_name }} + run: | + # A release published from the GitHub UI creates the tag server-side + # as a lightweight ref, which points straight at a commit and can + # never carry a signature (3.1.5 shipped that way, see issue 7273). + # The tag has to be created locally with "git tag -s" and pushed + # BEFORE the release is published, then selected on the release form. + objtype="$(gh api "repos/$REPO/git/ref/tags/$TAG" --jq .object.type)" + if [ "$objtype" != "tag" ]; then + echo "::error::refs/tags/$TAG points at a $objtype, not a tag object. It is a lightweight tag and carries no signature. Recreate it locally with 'git tag -s $TAG' and push it before publishing the release." + exit 1 + fi + echo "refs/tags/$TAG is a tag object." diff --git a/README.md b/README.md index 8ffb54df..19bbc94b 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ acme.sh -h #### 🔏 Verify a Release -Release tags from `3.1.5` on are signed with the maintainer's SSH key. The +Release tags from `3.1.6` on are signed with the maintainer's SSH key. The signing happens on the maintainer's machine, so the private key is never available to CI. The public half is [`allowed_signers`](allowed_signers) in this repository. From a clone: @@ -243,7 +243,7 @@ git config gpg.ssh.allowedSignersFile allowed_signers ``` ```bash -git verify-tag 3.1.5 +git verify-tag 3.1.6 ``` The signature covers the tag object, which pins the commit and therefore the @@ -251,10 +251,10 @@ whole tree, so a good signature verifies every file at that release and no separate tarball checksum is needed. Build a tarball from the verified tag: ```bash -git archive --format=tar.gz --prefix=acme.sh-3.1.5/ 3.1.5 > acme.sh-3.1.5.tar.gz +git archive --format=tar.gz --prefix=acme.sh-3.1.6/ 3.1.6 > acme.sh-3.1.6.tar.gz ``` -> ⚠️ Tags up to `3.1.4` predate the signing key and are unsigned. +> ⚠️ Tags up to `3.1.5` are unsigned. --- diff --git a/acme.sh b/acme.sh index 4ed49c41..9561ff76 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=3.1.5 +VER=3.1.6 PROJECT_NAME="acme.sh"