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<tag> 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
This commit is contained in:
neil
2026-09-20 13:06:37 +02:00 Unverified
parent ccb376dde3
commit 138e0dff84
3 changed files with 35 additions and 12 deletions
+28 -5
View File
@@ -6,6 +6,9 @@ name: Mirror version tag
# pointing to the same object, so both forms exist. # pointing to the same object, so both forms exist.
# No retrigger loop: the tag filter never matches a "v"-prefixed tag, and # No retrigger loop: the tag filter never matches a "v"-prefixed tag, and
# refs created with GITHUB_TOKEN do not fire workflows anyway. # 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: on:
push: push:
@@ -26,19 +29,39 @@ jobs:
REPO: ${{ github.repository }} REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }} TAG: ${{ github.ref_name }}
run: | 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 # Mirror the object the pushed tag actually points at: the commit
# for a lightweight tag, the tag object itself for an annotated or # for a lightweight tag, the tag object itself for an annotated or
# signed one. Pointing the mirror at the commit would strip the # signed one. Pointing the mirror at the commit would strip the
# signature, so "git verify-tag v3.1.3" would fail while # signature, so "git verify-tag v3.1.3" would fail while
# "git verify-tag 3.1.3" succeeds. # "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 if [ -z "$sha" ] || [ "$sha" = "null" ]; then
echo "Could not resolve refs/tags/$TAG" echo "Could not resolve refs/tags/$TAG"
exit 1 exit 1
fi fi
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" gh api "repos/$REPO/git/refs" -f ref="refs/tags/v$TAG" -f sha="$sha"
echo "Created tag v$TAG -> $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."
+4 -4
View File
@@ -233,7 +233,7 @@ acme.sh -h
#### 🔏 Verify a Release #### 🔏 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 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 available to CI. The public half is [`allowed_signers`](allowed_signers) in
this repository. From a clone: this repository. From a clone:
@@ -243,7 +243,7 @@ git config gpg.ssh.allowedSignersFile allowed_signers
``` ```
```bash ```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 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: separate tarball checksum is needed. Build a tarball from the verified tag:
```bash ```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.
--- ---
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
VER=3.1.5 VER=3.1.6
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"