@@ -90,6 +90,93 @@ previous_changes_version() {
9090 ' " ${PLG_FILE} "
9191}
9292
93+ version_greater_than () {
94+ local left=" ${1:- } "
95+ local right=" ${2:- } "
96+ local max_ver=" "
97+ [[ -n " ${left} " ]] || return 1
98+ [[ -n " ${right} " ]] || return 1
99+ [[ " ${left} " != " ${right} " ]] || return 1
100+ max_ver=" $( printf ' %s\n%s\n' " ${left} " " ${right} " | sort -V | tail -n 1) "
101+ [[ " ${max_ver} " == " ${left} " ]]
102+ }
103+
104+ head_manifest_version () {
105+ local version_line=" "
106+ if ! command -v git > /dev/null 2>&1 || ! git -C " ${ROOT_DIR} " rev-parse --is-inside-work-tree > /dev/null 2>&1 ; then
107+ return
108+ fi
109+ version_line=" $(
110+ git -C " ${ROOT_DIR} " show HEAD:folderview.plus.plg 2> /dev/null \
111+ | sed -n ' s/^<!ENTITY version "\([^"]*\)".*/\1/p' \
112+ | head -n 1 \
113+ || true
114+ ) "
115+ printf ' %s' " ${version_line} "
116+ }
117+
118+ list_changes_versions () {
119+ awk '
120+ /^###/ {
121+ candidate = $0
122+ sub(/^###/, "", candidate)
123+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", candidate)
124+ if (candidate != "") {
125+ print candidate
126+ }
127+ }
128+ ' " ${PLG_FILE} "
129+ }
130+
131+ remove_changes_block_for_version () {
132+ local target_version=" ${1:- } "
133+ local tmp_file=" "
134+ [[ -n " ${target_version} " ]] || return
135+ tmp_file=" $( mktemp) "
136+ awk -v version=" ${target_version} " '
137+ BEGIN { skip = 0 }
138+ /^###/ {
139+ if ($0 ~ "^###" version "[[:space:]]*$") {
140+ skip = 1
141+ next
142+ }
143+ if (skip) {
144+ skip = 0
145+ }
146+ }
147+ {
148+ if (!skip) {
149+ print
150+ }
151+ }
152+ ' " ${PLG_FILE} " > " ${tmp_file} "
153+ mv " ${tmp_file} " " ${PLG_FILE} "
154+ }
155+
156+ prune_unreleased_retry_blocks () {
157+ local target_version=" ${1:- } "
158+ local head_version=" "
159+ local stale_version=" "
160+ local removed=0
161+ [[ -n " ${target_version} " ]] || return
162+ head_version=" $( head_manifest_version) "
163+ [[ -n " ${head_version} " ]] || return
164+ while IFS= read -r stale_version; do
165+ [[ -n " ${stale_version} " ]] || continue
166+ if ! version_greater_than " ${stale_version} " " ${head_version} " ; then
167+ continue
168+ fi
169+ if [[ " ${stale_version} " == " ${target_version} " ]]; then
170+ continue
171+ fi
172+ remove_changes_block_for_version " ${stale_version} "
173+ removed=$(( removed + 1 ))
174+ done < <( list_changes_versions)
175+ if [[ " ${removed} " -gt 0 ]]; then
176+ echo " Pruned ${removed} unreleased local CHANGES block(s) newer than HEAD before inserting ${target_version} "
177+ fi
178+ }
179+
93180is_metadata_only_changes_line () {
94181 local line=" ${1:- } "
95182 local lowered=" "
@@ -116,6 +203,10 @@ block_is_metadata_only() {
116203 [[ " ${saw_content} " -eq 1 ]]
117204}
118205
206+ if [[ " ${CHECK_ONLY} " != " 1" ]]; then
207+ prune_unreleased_retry_blocks " ${VERSION} "
208+ fi
209+
119210current_block=" $( changes_block_for_version " ${VERSION} " ) "
120211if [[ -n " ${current_block} " ]]; then
121212 if [[ " ${REQUIRE_EXPLICIT} " == " 1" ]]; then
@@ -286,13 +377,31 @@ classify_changed_path_subsystems() {
286377 ;;
287378 esac
288379
380+ case " ${changed} " in
381+ scripts/ensure_plg_changes_entry.sh|scripts/build_release_notes.sh|scripts/release_prepare.sh|scripts/release_guard.sh|scripts/dev_finalize.sh|tests/versioning-guard.test.mjs)
382+ printf ' %s\n' " release-notes-tooling"
383+ return
384+ ;;
385+ esac
386+
289387 case " ${changed} " in
290388 .github/workflows/* |.github/actions/* |.githooks/* |pkg_build.sh|scripts/* )
291389 printf ' %s\n' " release-tooling"
292390 return
293391 ;;
294392 esac
295393
394+ case " ${changed} " in
395+ src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js|\
396+ src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.support-bundle-browser.js|\
397+ src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.support-bundle-telemetry.js|\
398+ src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.activity-diagnostics.js|\
399+ tests/browser-smoke-docker-diagnostics.test.mjs|tests/support-bundle-browser-telemetry.test.mjs|tests/docker-update-status-regression.test.mjs)
400+ printf ' %s\n' " docker-diagnostics"
401+ return
402+ ;;
403+ esac
404+
296405 case " ${changed} " in
297406 src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js|\
298407 src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.* |\
@@ -450,6 +559,10 @@ format_subsystem_note_line() {
450559 local subject=" "
451560
452561 case " ${subsystem} " in
562+ docker-diagnostics)
563+ category=" Fix"
564+ subject=" Docker support-bundle snapshots, trace storage caps, and rendered-state diagnostics"
565+ ;;
453566 docker-runtime)
454567 category=" Fix"
455568 subject=" Docker runtime rows, folder state, and container interactions"
@@ -474,6 +587,10 @@ format_subsystem_note_line() {
474587 category=" Fix"
475588 subject=" Diagnostics surfaces, issue reports, and support bundle coverage"
476589 ;;
590+ release-notes-tooling)
591+ category=" Quality"
592+ subject=" Release-note generation, retry cleanup, and packaging guards"
593+ ;;
477594 automation-rules)
478595 category=" Feature"
479596 subject=" Setup Assistant, rules, smart-detect, and starter-template workflows"
@@ -515,12 +632,14 @@ build_diff_based_notes() {
515632 local -a changed_files=()
516633 local -a notes=()
517634 local -a subsystem_order=(
635+ docker-diagnostics
518636 docker-runtime
519637 vm-runtime
520638 dashboard
521639 folder-editor
522640 settings-workspace
523641 settings-diagnostics
642+ release-notes-tooling
524643 automation-rules
525644 operations-recovery
526645 icon-workflows
0 commit comments