forked from heavy-duty/ceremony
fix: reject entry-less release notes
This commit is contained in:
parent
0e489438de
commit
69410723bd
3 changed files with 57 additions and 10 deletions
24
.github/workflows/release-exercise.yml
vendored
24
.github/workflows/release-exercise.yml
vendored
|
|
@ -171,12 +171,32 @@ jobs:
|
|||
run: |
|
||||
# shellcheck source=/dev/null
|
||||
. "$CEREMONY_DIR/lib/changelog.sh"
|
||||
changelog_section CHANGELOG.md "$VER" > "$RUNNER_TEMP/notes.md"
|
||||
if [ ! -s "$RUNNER_TEMP/notes.md" ]; then
|
||||
if ! diagnosis="$(changelog_section_problem CHANGELOG.md "$VER")"; then
|
||||
echo "CHANGELOG.md has no '## $VER' section at the merge commit — the ceremony PR must stamp it; refusing to publish an empty release" >&2
|
||||
printf '%s\n' "$diagnosis" >&2
|
||||
exit 1
|
||||
fi
|
||||
changelog_section CHANGELOG.md "$VER" > "$RUNNER_TEMP/notes.md"
|
||||
cat "$RUNNER_TEMP/notes.md"
|
||||
- name: an entry-less stamped fixture is refused by the notes predicate
|
||||
working-directory: ${{ runner.temp }}/fixture
|
||||
env:
|
||||
VER: ${{ steps.facts.outputs.ver }}
|
||||
run: |
|
||||
cp CHANGELOG.md "$RUNNER_TEMP/CHANGELOG.good.md"
|
||||
awk -v ver="$VER" '
|
||||
/^## / { in_section = ($2 == ver) }
|
||||
in_section && /^[[:space:]]*[-*][[:space:]]/ { next }
|
||||
{ print }
|
||||
' "$RUNNER_TEMP/CHANGELOG.good.md" > CHANGELOG.md
|
||||
# shellcheck source=/dev/null
|
||||
. "$CEREMONY_DIR/lib/changelog.sh"
|
||||
if diagnosis="$(changelog_section_problem CHANGELOG.md "$VER")"; then
|
||||
echo "entry-less stamped section unexpectedly passed" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' "$diagnosis" | grep -F "section '$VER' has no entries"
|
||||
cp "$RUNNER_TEMP/CHANGELOG.good.md" CHANGELOG.md
|
||||
- name: the chain must land where the fixture says it lands
|
||||
env:
|
||||
CEREMONY: ${{ steps.decide.outputs.ceremony }}
|
||||
|
|
|
|||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
|
|
@ -198,11 +198,12 @@ jobs:
|
|||
run: |
|
||||
# shellcheck source=/dev/null
|
||||
. "$CEREMONY_DIR/lib/changelog.sh"
|
||||
changelog_section CHANGELOG.md "$VER" > "$RUNNER_TEMP/notes.md"
|
||||
if [ ! -s "$RUNNER_TEMP/notes.md" ]; then
|
||||
if ! diagnosis="$(changelog_section_problem CHANGELOG.md "$VER")"; then
|
||||
echo "CHANGELOG.md has no '## $VER' section at the merge commit — the ceremony PR must stamp it; refusing to publish an empty release" >&2
|
||||
printf '%s\n' "$diagnosis" >&2
|
||||
exit 1
|
||||
fi
|
||||
changelog_section CHANGELOG.md "$VER" > "$RUNNER_TEMP/notes.md"
|
||||
cat "$RUNNER_TEMP/notes.md"
|
||||
- name: nothing may exist yet — no tag, no release (re-runs refuse loudly)
|
||||
if: steps.decide.outputs.ceremony == 'yes'
|
||||
|
|
@ -342,11 +343,12 @@ jobs:
|
|||
run: |
|
||||
# shellcheck source=/dev/null
|
||||
. "$CEREMONY_DIR/lib/changelog.sh"
|
||||
changelog_section CHANGELOG.md "$VER" > "$RUNNER_TEMP/notes.md"
|
||||
if [ ! -s "$RUNNER_TEMP/notes.md" ]; then
|
||||
if ! diagnosis="$(changelog_section_problem CHANGELOG.md "$VER")"; then
|
||||
echo "CHANGELOG.md has no '## $VER' section — stamp the Unreleased section in the release PR before tagging; refusing to publish an empty release" >&2
|
||||
printf '%s\n' "$diagnosis" >&2
|
||||
exit 1
|
||||
fi
|
||||
changelog_section CHANGELOG.md "$VER" > "$RUNNER_TEMP/notes.md"
|
||||
cat "$RUNNER_TEMP/notes.md"
|
||||
- name: artifact hook — the consumer's own release-artifact action
|
||||
# After the tag exists (it fired this door), before the publish —
|
||||
|
|
|
|||
|
|
@ -89,11 +89,11 @@ chain() {
|
|||
*ceremony=yes*)
|
||||
# shellcheck source=lib/changelog.sh
|
||||
. "$ROOT/lib/changelog.sh"
|
||||
notes="$(changelog_section CHANGELOG.md "$ver")"
|
||||
if [ -z "$notes" ]; then
|
||||
echo "chain: the changelog section for $ver is empty" >&2
|
||||
diagnosis="$(changelog_section_problem CHANGELOG.md "$ver")" || {
|
||||
printf 'chain: %s\n' "$diagnosis" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
notes="$(changelog_section CHANGELOG.md "$ver")"
|
||||
printf 'notes: %s\n' "$notes"
|
||||
;;
|
||||
esac
|
||||
|
|
@ -105,6 +105,31 @@ check "the ceremony merge decides ceremony=yes" 0 "ceremony=yes" \
|
|||
check "the notes are the stamped section's prose" 0 \
|
||||
"notes: - The entry this release ships." chain "$MERGE_SHA" "$BASE_SHA"
|
||||
|
||||
# The same ceremony facts with an entry-less stamped section must stop at the
|
||||
# notes door, before any tag or release mutation could run.
|
||||
git -C "$TMP/repo" reset -q --hard "$BASE_SHA"
|
||||
printf '0.7.0\n' >"$TMP/repo/VERSION"
|
||||
cat >"$TMP/repo/CHANGELOG.md" <<'EOF'
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 0.7.0 — 2026-07-21
|
||||
|
||||
### Added
|
||||
|
||||
## 0.6.8 — 2026-07-01
|
||||
|
||||
- An older entry.
|
||||
EOF
|
||||
git -C "$TMP/repo" add VERSION CHANGELOG.md
|
||||
git -C "$TMP/repo" commit -qm "release: entry-less 0.7.0"
|
||||
EMPTY_MERGE_SHA="$(git -C "$TMP/repo" rev-parse HEAD)"
|
||||
check "the notes door refuses an entry-less stamped section" 1 \
|
||||
"section '0.7.0' has no entries" chain "$EMPTY_MERGE_SHA" "$BASE_SHA"
|
||||
|
||||
git -C "$TMP/repo" reset -q --hard "$MERGE_SHA"
|
||||
|
||||
# The same chain on an ordinary merge: -dev, unchanged — a green NOTICE
|
||||
# no-op that never consults the API (the stub would refuse a release view).
|
||||
printf 'ordinary work\n' >"$TMP/repo/notes.txt"
|
||||
|
|
|
|||
Loading…
Reference in a new issue