diff --git a/scripts/changelog-section.sh b/scripts/changelog-section.sh new file mode 100755 index 0000000..37360dd --- /dev/null +++ b/scripts/changelog-section.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Vendored from heavy-duty/ceremony 0.6.3: +# lib/changelog.sh (changelog_section + changelog_section_problem) +# bin/changelog-section + +set -euo pipefail + +changelog_section() { + awk -v ver="$2" ' + /^## / { if (found) exit; found = ($2 == ver); next } + found && !body && /^[[:space:]]*$/ { next } + found { body = 1; print } + ' "$1" +} + +changelog_section_problem() { + local file="$1" ver="$2" notes problem + + if ! awk -v ver="$ver" '/^## / && $2 == ver { found = 1; exit } END { exit !found }' "$file"; then + printf "no section for '%s'\n" "$ver" + return 1 + fi + + [ "$ver" = "Unreleased" ] && return 0 + + notes="$(changelog_section "$file" "$ver")" + if ! printf '%s\n' "$notes" | awk '/^[[:space:]]*[-*][[:space:]]/ { found = 1; exit } END { exit !found }'; then + printf "section '%s' has no entries — a heading is not an entry\n" "$ver" + return 1 + fi + + problem="$( + printf '%s\n' "$notes" | awk ' + /^### / { + if (heading != "" && !entry) { + reported = 1 + print heading + exit + } + heading = $0 + entry = 0 + next + } + heading != "" && /^[[:space:]]*[-*][[:space:]]/ { entry = 1 } + END { + if (!reported && heading != "" && !entry) print heading + } + ' + )" + if [ -n "$problem" ]; then + printf "section '%s' has an empty heading: '%s'\n" "$ver" "$problem" + return 1 + fi +} + +ver="${1:-}" +changelog="${2:-CHANGELOG.md}" +if [ -z "$ver" ]; then + echo "usage: changelog-section.sh []" >&2 + exit 2 +fi +[ -f "$changelog" ] || { + echo "changelog-section: no such file: $changelog" >&2 + exit 1 +} + +if ! diagnosis="$(changelog_section_problem "$changelog" "$ver")"; then + echo "changelog-section: $changelog has no publishable section for '$ver'" >&2 + printf 'changelog-section: %s\n' "$diagnosis" >&2 + exit 1 +fi +notes="$(changelog_section "$changelog" "$ver")" +printf '%s\n' "$notes" diff --git a/test/changelog-section.test.js b/test/changelog-section.test.js index 18b64bb..e28da16 100644 --- a/test/changelog-section.test.js +++ b/test/changelog-section.test.js @@ -48,7 +48,7 @@ test('heading without a list entry is rejected as empty', () => { assert.equal(result.status, 1); assert.equal(result.stdout, ''); - assert.match(result.stderr, /section for '2\.0\.0' has no changelog entries/); + assert.match(result.stderr, /section '2\.0\.0' has no entries/); }); }); @@ -70,7 +70,7 @@ test('extraction stops before the next version heading', () => { const result = extract('2.0.0', changelog); assert.equal(result.status, 0, result.stderr); - assert.equal(result.stdout, '### Added\n\n- Current change.\n\n'); + assert.equal(result.stdout, '### Added\n\n- Current change.\n'); assert.doesNotMatch(result.stdout, /Older change/); }); });