Skip to content

Commit fac98e1

Browse files
Harden main release and back-merge workflow
1 parent 0eaeeee commit fac98e1

14 files changed

Lines changed: 343 additions & 77 deletions

.github/workflows/release-main.yml

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,7 @@ jobs:
2828
git config user.name "github-actions[bot]"
2929
git config user.email "github-actions[bot]@users.noreply.github.com"
3030
31-
# Remove today's packages to avoid collision detection in pkg_build.sh
32-
- name: Clean existing packages for today
33-
run: |
34-
TODAY=$(date +"%Y.%m.%d")
35-
rm -f archive/folderview.plus-${TODAY}*.txz
36-
echo "Cleaned packages for $TODAY"
37-
38-
- name: Build stable package
39-
run: |
40-
chmod +x pkg_build.sh
41-
bash pkg_build.sh
42-
43-
- name: Ensure CHANGES entry for release version
44-
run: |
45-
chmod +x scripts/ensure_plg_changes_entry.sh
46-
bash scripts/ensure_plg_changes_entry.sh
47-
48-
- name: Run release validation suite
31+
- name: Prepare and push stable release
4932
env:
5033
FVPLUS_UNRAID_MATRIX: ${{ secrets.FVPLUS_UNRAID_MATRIX }}
5134
FVPLUS_UNRAID_MATRIX_REQUIRED: ${{ secrets.FVPLUS_UNRAID_MATRIX != '' && '1' || '0' }}
@@ -54,6 +37,7 @@ jobs:
5437
FVPLUS_I18N_STRICT: '1'
5538
FVPLUS_DEAD_CODE_STRICT: '1'
5639
FVPLUS_REQUIRE_PERF_BASELINE: '1'
40+
FVPLUS_REQUIRE_EXPLICIT_RELEASE_NOTES: '1'
5741
FVPLUS_MAIN_HISTORY_BASE_REF: ${{ github.event.before }}
5842
FVPLUS_BROWSER_SMOKE_URL: ${{ secrets.FVPLUS_BROWSER_SMOKE_URL }}
5943
FVPLUS_BROWSER_SMOKE_REQUIRED: ${{ secrets.FVPLUS_BROWSER_SMOKE_URL != '' && '1' || '0' }}
@@ -73,29 +57,8 @@ jobs:
7357
FVPLUS_THEME_SMOKE_ARTIFACT_DIR: ${{ github.workspace }}/tmp/browser-smoke-artifacts/theme-matrix
7458
FVPLUS_PLAYWRIGHT_INSTALL_WITH_DEPS: '1'
7559
run: |
76-
chmod +x scripts/run_ci_suite.sh
77-
bash scripts/run_ci_suite.sh --release
78-
79-
- name: Extract stable version
80-
id: version
81-
run: |
82-
VERSION=$(grep -oP '<!ENTITY version "\K[^"]+' folderview.plus.plg)
83-
echo "version=$VERSION" >> $GITHUB_OUTPUT
84-
FILENAME="folderview.plus-${VERSION}.txz"
85-
echo "filename=$FILENAME" >> $GITHUB_OUTPUT
86-
echo "Stable version: $VERSION"
87-
88-
- name: Commit stable release to main
89-
run: |
90-
git add -A
91-
if git diff --cached --quiet; then
92-
echo "No release file changes to commit."
93-
else
94-
git commit -m "Stable release ${{ steps.version.outputs.version }}"
95-
fi
96-
97-
- name: Push main
98-
run: git push origin main
60+
chmod +x scripts/release_prepare.sh
61+
bash scripts/release_prepare.sh --push-main
9962
10063
- name: Upload debug artifacts on failure
10164
if: failure()

.github/workflows/release-on-main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ jobs:
102102
FVPLUS_I18N_STRICT: '1'
103103
FVPLUS_DEAD_CODE_STRICT: '1'
104104
FVPLUS_REQUIRE_PERF_BASELINE: '1'
105+
FVPLUS_REQUIRE_EXPLICIT_RELEASE_NOTES: '1'
105106
FVPLUS_MAIN_HISTORY_BASE_REF: ${{ github.event.before }}
106107
FVPLUS_BROWSER_SMOKE_URL: ${{ secrets.FVPLUS_BROWSER_SMOKE_URL }}
107108
FVPLUS_BROWSER_SMOKE_REQUIRED: ${{ secrets.FVPLUS_BROWSER_SMOKE_URL != '' && '1' || '0' }}

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,11 @@ Recommended migration path:
363363
- `bash scripts/dev_finalize.sh --open-fixture --skip-build`
364364
- Build a package locally:
365365
- `bash pkg_build.sh`
366-
- Prepare a release build with checks:
366+
- Curate stable release notes first:
367+
- `docs/releases/<version>.md`
368+
- Simulate the full `main` release flow safely in a temporary worktree:
369+
- `bash scripts/simulate_main_release.sh`
370+
- Prepare a stable release from `main` with checks:
367371
- `bash scripts/release_prepare.sh`
368372
- Run the automated test suite:
369373
- `node --test tests/*.mjs`

archive/folderview.plus-2026.04.04.13.txz.sha256

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ba7a4634943ff0307143e0a1e0aead153db3721745e7aae2ef25e5c2d2d96b82 folderview.plus-2026.04.05.09.txz

folderview.plus.plg

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
<!ENTITY launch "Settings/FolderViewPlus">
77
<!ENTITY plugdir "/usr/local/emhttp/plugins/&name;">
88
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/dev/folderview.plus.plg">
9-
<!ENTITY version "2026.04.05.08">
10-
<!ENTITY md5 "0aa6a00b5a9ebfcfc7ccbbe6a3c172a9">
9+
<!ENTITY version "2026.04.05.09">
10+
<!ENTITY md5 "9ec5159372e5b36652993d96c01ea953">
1111
]>
1212

1313
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;" pluginURL="&pluginURL;" icon="folder-icon.png" support="https://forums.unraid.net/topic/197631-plugin-folderview-plus/" min="7.0.0">
1414
<CHANGES>
1515

16+
###2026.04.05.09
17+
- Quality: Release automation, CI smoke coverage, and packaging guards.
18+
- Docs: Project documentation and support guidance.
19+
20+
1621
###2026.04.05.08
1722
- UX: Folder editor flows, previews, and bootstrap behavior.
1823

scripts/ensure_plg_changes_entry.sh

Lines changed: 142 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,154 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
55
PLG_FILE="${ROOT_DIR}/folderview.plus.plg"
66
MAX_AUTO_LINES="${FVPLUS_AUTO_CHANGE_LINES:-6}"
77
AUTO_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
912
source "${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+
1147
if [[ ! -f "${PLG_FILE}" ]]; then
1248
fvplus::fail "Missing plugin manifest: ${PLG_FILE}"
1349
fi
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
20135
fi
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+
22156
normalize_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
514643
fi
515644

516645
TMP_FILE="$(mktemp)"

scripts/release_notes_consistency_guard.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,30 @@ cd "${ROOT_DIR}"
88

99
fvplus::require_commands bash node awk sed mktemp grep
1010

11+
env_truthy() {
12+
case "$(printf '%s' "${1:-0}" | tr '[:upper:]' '[:lower:]')" in
13+
1|true|yes|on)
14+
return 0
15+
;;
16+
*)
17+
return 1
18+
;;
19+
esac
20+
}
21+
1122
VERSION="$(fvplus::read_plg_version "${ROOT_DIR}/folderview.plus.plg")"
1223
mkdir -p "${ROOT_DIR}/tmp"
1324
TMP_DIR="$(mktemp -d "${ROOT_DIR}/tmp/release-notes-guard.XXXXXX")"
1425
trap 'rm -rf "${TMP_DIR}"' EXIT
1526
OUTPUT_FILE="${TMP_DIR}/release_notes.md"
1627

28+
if env_truthy "${FVPLUS_REQUIRE_EXPLICIT_RELEASE_NOTES:-0}"; then
29+
chmod +x scripts/ensure_plg_changes_entry.sh
30+
FVPLUS_TARGET_RELEASE_VERSION="${VERSION}" \
31+
FVPLUS_REQUIRE_EXPLICIT_RELEASE_NOTES=1 \
32+
bash scripts/ensure_plg_changes_entry.sh --check-only --require-explicit --version "${VERSION}"
33+
fi
34+
1735
chmod +x scripts/build_release_notes.sh
1836
bash scripts/build_release_notes.sh --version "${VERSION}" --output "${OUTPUT_FILE}"
1937

0 commit comments

Comments
 (0)