Skip to content

scripts: harden tag-release.sh ref resolution - #11047

Open
ziggie1984 wants to merge 1 commit into
lightningnetwork:masterfrom
ziggie1984:tag-release-upstream-branch
Open

scripts: harden tag-release.sh ref resolution#11047
ziggie1984 wants to merge 1 commit into
lightningnetwork:masterfrom
ziggie1984:tag-release-upstream-branch

Conversation

@ziggie1984

@ziggie1984 ziggie1984 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Hardens ref resolution in scripts/tag-release.sh. The helper's behavior and
CLI surface are unchanged: it still verifies that HEAD matches the upstream
branch tip and tags HEAD, and --branch still selects the upstream branch to
verify against.

  • fetch a fully qualified refs/heads/<branch> refspec
  • peel FETCH_HEAD to a commit before comparing it against HEAD
  • pass --no-tags
  • reject an empty --branch= value instead of silently falling back to the
    current branch

Motivation

git fetch <remote> <name> resolves a tag named <name> ahead of a branch of
the same name. A tag shadowing a release branch would therefore leave
FETCH_HEAD on unrelated history. Fetching refs/heads/<name> removes the
ambiguity.

The refspec and the peel belong in the same change. Today the SHA comparison
happens to fail closed under such a collision, because git rev-parse FETCH_HEAD yields the annotated tag object and that can never equal a commit
id. Peeling on its own would convert that into a silent success on the tag's
target commit, so peeling is only safe once the refspec pins refs/heads.

--no-tags is hygiene rather than a fix. With an explicit refspec, tag
auto-following does not happen by default; it happens only when
remote.<name>.tagOpt=--tags is configured, in which case a verification step
that should be read-only silently mutates the local tag namespace. It cannot
affect any decision the script makes, since the duplicate-tag check is a
git ls-remote query against the remote.

Note that no such collision exists today — lnd release tags look like
v0.21.2-beta and would not shadow v0.21.x-branch. This closes the shape
rather than a live bug.

Note on the previous revision of this PR

An earlier version of this branch changed --branch to fetch and tag the
upstream branch tip directly, so that a release could be cut without checking
the branch out. That has been dropped.

The motivating problem was that scripts/tag-release.sh did not exist on
v0.20.x-branch, which has since been addressed by backporting the script.
Retargeting also gave up a property worth keeping: because the existing check
hard-fails on a HEAD/upstream mismatch rather than merely reporting it, both
designs guarantee equally that the signed commit is the upstream branch tip, but
only the original guarantees that the maintainer had that tree checked out when
signing it.

Validation

  • bash -n scripts/tag-release.sh passes; no added line exceeds 80 characters

  • --branch= and --branch with no value both exit 1 with usage

  • live run against origin master with a deliberately mismatched tag:
    FETCH_HEAD resolved to branch 'master' rather than a tag, peeled to a
    commit object, local tag count unchanged (412), and no tag was created
    Exercised end to end on git 2.50.1 against a throwaway upstream carrying the
    collision this change is about: a branch v9.9.x-branch at commit Y
    (build/version.go = 9.9.1) and an annotated tag of the same name at an
    unrelated commit X (9.9.0).

  • this branch, HEAD at Y: verification passes and targets Y, the branch tip,
    rather than X (the run then stops at gpg for want of a key in the sandbox,
    which is past every check)

  • master's script, same situation: aborts, reporting an Upstream: SHA that
    is neither X nor Y but the annotated tag object, since that can never equal a
    commit id

  • peel applied without the qualified refspec, HEAD at X: passes verification and
    would tag X rather than the branch tip, which is why the two changes are in
    one commit

  • under remote.origin.tagOpt=--tags, this branch leaves the local tag list
    empty where master's script pulls in the remote tag

@ziggie1984 ziggie1984 self-assigned this Aug 7, 2026
@github-actions github-actions Bot added the severity-low Best-effort review label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

🟢 PR Severity: LOW

file classification | 1 files | 85 lines changed

🟢 Low (1 files)
  • scripts/tag-release.sh - build/release tooling script, not application code

Analysis

This PR only modifies scripts/tag-release.sh, a release tooling script. It does not touch any wallet, channel, HTLC, peer, or other consensus/fund-handling code paths, so it qualifies as best-effort review under the LOW severity tier. No severity bump conditions apply (single file, no critical packages touched).


To override, add a severity-override-{critical,high,medium,low} label.

@ziggie1984 ziggie1984 changed the title scripts: allow tagging upstream release branches scripts: harden tag-release.sh ref resolution Aug 10, 2026
@ziggie1984
ziggie1984 force-pushed the tag-release-upstream-branch branch from 6210575 to f5f542a Compare August 10, 2026 01:07
@ziggie1984
ziggie1984 marked this pull request as ready for review August 10, 2026 02:11
Fully qualifies the fetch refspec and peels FETCH_HEAD to a commit before
comparing it against HEAD.

`git fetch <remote> <name>` resolves a tag named <name> ahead of a branch
of the same name, so a tag shadowing a release branch would leave
FETCH_HEAD pointing at unrelated history. Fetching `refs/heads/<name>`
removes the ambiguity.

The two changes belong together. Today the SHA comparison happens to
fail closed under such a collision, because `git rev-parse FETCH_HEAD`
yields the annotated tag object and that can never equal a commit id.
Peeling on its own would turn that into a silent success on the tag's
target commit, so it is only safe once the refspec pins refs/heads.

Also passes --no-tags, which keeps a remote.<name>.tagOpt=--tags setting
from pulling remote tags into the local repo as a side effect of what
should be a read-only verification step, and rejects an empty --branch=
value rather than silently falling back to the current branch.
@ziggie1984
ziggie1984 force-pushed the tag-release-upstream-branch branch from f5f542a to fc06ecc Compare August 10, 2026 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant