Fetch the tag annotation explicitly; checkout was shadowing it
Build and release image / build (push) Successful in 11s
Build and release image / build (push) Successful in 11s
`actions/checkout` fetches the tag by commit sha into refs/tags/<tag>, which replaces the annotated tag with a lightweight one. `git tag -l` then reports the *commit* message as the tag message, which is why the last attempt published my commit message as the release notes. The annotation was never lost, it just was not what that ref named any more. The steps that read it are gone and the tag object is now fetched by its own sha into refs/release-notes/<tag>, a namespace checkout cannot collide with. Checked against a simulated runner checkout — fetch by sha into refs/tags, then the workflow's own commands — and the notes come out byte-identical to docs/releases/v0.1.3.md.
This commit is contained in:
1 parent
1b652a4ba2
commit
72b9cbcab8
1 file changed
+29
-15
@@ -187,19 +187,22 @@ jobs:
|
||||
|
||||
# Read the release notes from the tag object itself.
|
||||
#
|
||||
# This is not paranoia about the notes file. Across five runs of this
|
||||
# This is not paranoia about the notes file. Across six runs of this
|
||||
# workflow, act_runner produced worktrees where `git ls-files docs` and
|
||||
# `ls docs/releases` both showed docs/releases/v0.1.3.md while the file
|
||||
# itself was absent — intermittently, from the same commit — and then
|
||||
# `git show HEAD:...` came back empty too, because the shallow fetch had
|
||||
# not brought that blob across. Three separate ways of reading one file
|
||||
# failed for three different reasons, and v0.1.3 shipped a placeholder
|
||||
# body three times because of it.
|
||||
# not brought that blob across. v0.1.3 shipped a placeholder body four
|
||||
# times before this landed.
|
||||
#
|
||||
# The tag object is the one thing the runner provably receives: it is
|
||||
# named in the fetch refspec itself. So the notes live there, written at
|
||||
# tag time with `git tag -a v0.1.4 -F docs/releases/v0.1.4.md`. The docs
|
||||
# file stays as the thing a human edits and reads on the web.
|
||||
# There is a second trap, which cost two more runs: `actions/checkout`
|
||||
# fetches the tag *by commit sha* into refs/tags/<tag> (see the
|
||||
# `+<sha>:refs/tags/v0.1.3` in its fetch line). That overwrites the
|
||||
# annotated tag with a lightweight one pointing at the commit, so
|
||||
# `git tag -l` happily reports the commit message as the tag message. The
|
||||
# annotation is still in the object database; it just is not what
|
||||
# `refs/tags/v0.1.3` names any more. Hence the explicit fetch by tag
|
||||
# object sha below, into a namespace checkout cannot collide with.
|
||||
- name: Collect release notes
|
||||
if: github.ref_type == 'tag'
|
||||
run: |
|
||||
@@ -207,19 +210,30 @@ jobs:
|
||||
TAG="${{ github.ref_name }}"
|
||||
VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
|
||||
|
||||
# Everything after the tag's subject line, with the blank line that
|
||||
# separates them dropped. `%(contents:body)` is not used here: it
|
||||
# also treats any line starting with `#` as a comment, which silently
|
||||
# removed every heading from these notes.
|
||||
NOTES=$(git tag -l --format='%(contents)' "$TAG" 2>/dev/null | tail -n +2 | sed '/./,$!d' || true)
|
||||
NOTES=""
|
||||
TAG_SHA=$(git ls-remote --tags origin "refs/tags/${TAG}" 2>/dev/null | awk '{print $1}')
|
||||
if [ -n "$TAG_SHA" ]; then
|
||||
git fetch -q --depth=1 origin "+${TAG_SHA}:refs/release-notes/${TAG}" 2>/dev/null || true
|
||||
if [ "$(git cat-file -t "refs/release-notes/${TAG}" 2>/dev/null)" = "tag" ]; then
|
||||
# cat-file prints the object verbatim: three header lines, a
|
||||
# blank, then the message. `sed '1,/^$/d'` drops the headers,
|
||||
# `tail -n +2` the one-line subject, and the last sed any blank
|
||||
# line left between them. Format specifiers like %(contents:body)
|
||||
# are avoided because they also treat a `#` line as a comment,
|
||||
# which would delete every heading in these notes.
|
||||
NOTES=$(git cat-file -p "refs/release-notes/${TAG}" \
|
||||
| sed '1,/^$/d' | tail -n +2 | sed '/./,$!d' || true)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$NOTES" ]; then
|
||||
NOTES=$(git show "HEAD:docs/releases/${VERSION}.md" 2>/dev/null || true)
|
||||
fi
|
||||
if [ -n "$NOTES" ]; then
|
||||
echo "notes: ${#NOTES} bytes from tag ${TAG}"
|
||||
echo "notes: ${#NOTES} bytes from the ${TAG} annotation"
|
||||
else
|
||||
NOTES=$(printf 'Release %s.\n\nSee the repository README for deployment instructions.' "$VERSION")
|
||||
echo "::warning::tag ${TAG} has no notes body; using a placeholder"
|
||||
echo "::warning::tag ${TAG} carries no notes body; using a placeholder"
|
||||
fi
|
||||
printf '%s' "$NOTES" > /tmp/notes.md
|
||||
|
||||
|
||||
Reference in new issue
Block a user