diff --git a/lib/changelog.sh b/lib/changelog.sh index c9c052c..4489898 100644 --- a/lib/changelog.sh +++ b/lib/changelog.sh @@ -20,3 +20,48 @@ changelog_section() { found { body = 1; print } ' "$1" } + +# changelog_section_problem +# +# Print the first reason a version section cannot be published. Unreleased is +# a work-in-progress template, so its headings may deliberately be empty. +# A printed problem returns 1; silence returns 0. +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 +} diff --git a/test/changelog.test.sh b/test/changelog.test.sh index 94cb0bc..0adfa8c 100755 --- a/test/changelog.test.sh +++ b/test/changelog.test.sh @@ -57,6 +57,77 @@ check "date-less version heading parses" 0 "" assert_section 0.4.0 '- A date-les check "empty stamped section returns empty output" 0 "" assert_section 0.5.0 "" check "missing section returns empty output" 0 "" assert_section 9.9.9 "" +PROBLEM_FIXTURE="$TMP/CHANGELOG.problems.md" +assert_problem() { + local version="$1" expected_status="$2" expected="$3" + check "predicate: $version / $expected" "$expected_status" "$expected" \ + changelog_section_problem "$PROBLEM_FIXTURE" "$version" +} + +cat >"$PROBLEM_FIXTURE" <<'EOF' +# Changelog + +## Unreleased + +### Added + +### Changed + +### Fixed + +## 1.0.0 + +- Flat dash entry. + +## 1.1.0 + +* Flat star entry. + +## 1.2.0 + +### Fixed + +- Fixed entry. + +## 1.3.0 + +### Added + +- Added entry. + +### Changed + +* Changed entry. + +### Fixed + +- Fixed entry. + +## 1.4.0 + +## 1.5.0 + +### Added + +## 1.6.0 + +### Added + +### Fixed + +- Fixed entry. +EOF + +assert_problem Unreleased 0 "" +assert_problem 1.0.0 0 "" +assert_problem 1.1.0 0 "" +assert_problem 1.2.0 0 "" +assert_problem 1.3.0 0 "" +assert_problem 1.4.0 1 "section '1.4.0' has no entries — a heading is not an entry" +assert_problem 1.5.0 1 "section '1.5.0' has no entries — a heading is not an entry" +assert_problem 1.6.0 1 "section '1.6.0' has an empty heading: '### Added'" +assert_problem 9.9.9 1 "no section for '9.9.9'" + WRAPPER="$ROOT/bin/changelog-section" check "wrapper publishes the requested body" 0 "The seven-oh entry" "$WRAPPER" 0.7.0 "$FIXTURE" check "wrapper refuses an empty section" 1 "no section for '0.5.0'" "$WRAPPER" 0.5.0 "$FIXTURE"