@@ -5,20 +5,154 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
55PLG_FILE=" ${ROOT_DIR} /folderview.plus.plg"
66MAX_AUTO_LINES=" ${FVPLUS_AUTO_CHANGE_LINES:- 6} "
77AUTO_FALLBACK_NOTE=' Maintenance: Release metadata and packaging sync.'
8+ CHECK_ONLY=0
9+ VERSION_OVERRIDE=" ${FVPLUS_TARGET_RELEASE_VERSION:- } "
10+ REQUIRE_EXPLICIT=" ${FVPLUS_REQUIRE_EXPLICIT_RELEASE_NOTES:- 0} "
811# shellcheck source=scripts/lib.sh
912source " ${ROOT_DIR} /scripts/lib.sh"
1013
14+ print_usage () {
15+ cat << 'EOF '
16+ Usage: ensure_plg_changes_entry.sh [options]
17+ --version VERSION Validate or insert notes for VERSION instead of the manifest version
18+ --check-only Validate note availability/content without modifying folderview.plus.plg
19+ --require-explicit Require curated or explicit non-generic release notes instead of auto-generated notes
20+ -h, --help Show this help
21+ EOF
22+ }
23+
24+ while [[ $# -gt 0 ]]; do
25+ case " ${1:- } " in
26+ --version)
27+ VERSION_OVERRIDE=" ${2:- } "
28+ shift
29+ ;;
30+ --check-only)
31+ CHECK_ONLY=1
32+ ;;
33+ --require-explicit)
34+ REQUIRE_EXPLICIT=1
35+ ;;
36+ -h|--help)
37+ print_usage
38+ exit 0
39+ ;;
40+ * )
41+ fvplus::fail " Unknown argument: ${1} "
42+ ;;
43+ esac
44+ shift
45+ done
46+
1147if [[ ! -f " ${PLG_FILE} " ]]; then
1248 fvplus::fail " Missing plugin manifest: ${PLG_FILE} "
1349fi
1450
15- VERSION=" $( fvplus::read_plg_version " ${PLG_FILE} " ) "
51+ VERSION=" ${VERSION_OVERRIDE:- $(fvplus:: read_plg_version " ${PLG_FILE} " )} "
52+ OVERRIDE_FILE=" ${ROOT_DIR} /docs/releases/${VERSION} .md"
53+
54+ normalize_changes_block () {
55+ local raw=" ${1:- } "
56+ printf ' %s' " ${raw} " | sed -E ' /^[[:space:]]*$/d; s/[[:space:]]+/ /g; s/^[[:space:]]+|[[:space:]]+$//g'
57+ }
58+
59+ changes_block_for_version () {
60+ local target_version=" ${1:- } "
61+ awk -v version=" ${target_version} " '
62+ BEGIN { capture = 0 }
63+ /^###/ {
64+ if (capture) {
65+ exit
66+ }
67+ if ($0 ~ "^###" version "[[:space:]]*$") {
68+ capture = 1
69+ next
70+ }
71+ }
72+ capture {
73+ print
74+ }
75+ ' " ${PLG_FILE} " | sed ' /^[[:space:]]*$/d'
76+ }
77+
78+ previous_changes_version () {
79+ local target_version=" ${1:- } "
80+ awk -v version=" ${target_version} " '
81+ /^###/ {
82+ candidate = $0
83+ sub(/^###/, "", candidate)
84+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", candidate)
85+ if (candidate != version) {
86+ print candidate
87+ exit
88+ }
89+ }
90+ ' " ${PLG_FILE} "
91+ }
92+
93+ is_metadata_only_changes_line () {
94+ local line=" ${1:- } "
95+ local lowered=" "
96+ lowered=" $( printf ' %s' " ${line} " | tr ' [:upper:]' ' [:lower:]' ) "
97+ [[ " ${lowered} " == * " release metadata and packaging sync" * ]] && return 0
98+ [[ " ${lowered} " == * " automated release metadata update" * ]] && return 0
99+ [[ " ${lowered} " == * " metadata update" * ]] && return 0
100+ [[ " ${lowered} " == * " packaging sync" * ]] && return 0
101+ [[ " ${lowered} " == * " folder editor flows, previews, and bootstrap behavior." * ]] && return 0
102+ return 1
103+ }
104+
105+ block_is_metadata_only () {
106+ local block=" ${1:- } "
107+ local line=" "
108+ local saw_content=0
109+ while IFS= read -r line; do
110+ [[ -z " ${line} " ]] && continue
111+ saw_content=1
112+ if ! is_metadata_only_changes_line " ${line} " ; then
113+ return 1
114+ fi
115+ done <<< " ${block}"
116+ [[ " ${saw_content} " -eq 1 ]]
117+ }
16118
17- if grep -q " ^###${VERSION} $" " ${PLG_FILE} " ; then
119+ current_block=" $( changes_block_for_version " ${VERSION} " ) "
120+ if [[ -n " ${current_block} " ]]; then
121+ if [[ " ${REQUIRE_EXPLICIT} " == " 1" ]]; then
122+ if block_is_metadata_only " ${current_block} " ; then
123+ fvplus::fail " CHANGES entry for ${VERSION} contains only generic release-metadata boilerplate. Add explicit release notes or docs/releases/${VERSION} .md."
124+ fi
125+ previous_version=" $( previous_changes_version " ${VERSION} " ) "
126+ if [[ -n " ${previous_version} " ]]; then
127+ previous_block=" $( changes_block_for_version " ${previous_version} " ) "
128+ if [[ -n " ${previous_block} " ]] && [[ " $( normalize_changes_block " ${current_block} " ) " == " $( normalize_changes_block " ${previous_block} " ) " ]]; then
129+ fvplus::fail " CHANGES entry for ${VERSION} duplicates the previous release notes block. Add explicit release deltas or docs/releases/${VERSION} .md."
130+ fi
131+ fi
132+ fi
18133 echo " CHANGES entry already present for ${VERSION} "
19134 exit 0
20135fi
21136
137+ if [[ -f " ${OVERRIDE_FILE} " ]]; then
138+ OVERRIDE_NOTES=" $( sed ' /^[[:space:]]*$/d' " ${OVERRIDE_FILE} " ) "
139+ if [[ -z " ${OVERRIDE_NOTES} " ]]; then
140+ fvplus::fail " Curated release note override is empty: docs/releases/${VERSION} .md"
141+ fi
142+ if [[ " ${CHECK_ONLY} " == " 1" ]]; then
143+ echo " Validated curated release notes for ${VERSION} from docs/releases/${VERSION} .md"
144+ exit 0
145+ fi
146+ AUTO_NOTES=" ${OVERRIDE_NOTES} "
147+ elif [[ " ${REQUIRE_EXPLICIT} " == " 1" ]]; then
148+ fvplus::fail " Explicit release notes are required for ${VERSION} . Add docs/releases/${VERSION} .md or a non-generic CHANGES block before packaging."
149+ fi
150+
151+ if [[ " ${CHECK_ONLY} " == " 1" ]]; then
152+ echo " Validated auto-generated CHANGES plan for ${VERSION} "
153+ exit 0
154+ fi
155+
22156normalize_subject () {
23157 local raw=" ${1:- } "
24158 local cleaned
@@ -499,18 +633,13 @@ build_auto_notes() {
499633 printf ' %s\n' " ${notes[@]} "
500634}
501635
502- PREVIOUS_VERSION=" $( awk '
503- /<CHANGES>/ { in_changes = 1; next }
504- in_changes && /^###/ {
505- gsub(/^###/, "", $0)
506- print
507- exit
508- }
509- ' " ${PLG_FILE} " ) "
636+ PREVIOUS_VERSION=" $( previous_changes_version " ${VERSION} " ) "
510637
511- AUTO_NOTES=" $( build_auto_notes " ${PREVIOUS_VERSION} " ) "
512- if [[ -z " ${AUTO_NOTES} " ]]; then
513- AUTO_NOTES=" - ${AUTO_FALLBACK_NOTE} "
638+ if [[ -z " ${AUTO_NOTES:- } " ]]; then
639+ AUTO_NOTES=" $( build_auto_notes " ${PREVIOUS_VERSION} " ) "
640+ if [[ -z " ${AUTO_NOTES} " ]]; then
641+ AUTO_NOTES=" - ${AUTO_FALLBACK_NOTE} "
642+ fi
514643fi
515644
516645TMP_FILE=" $( mktemp) "
0 commit comments