Skip to content

Commit 4ebc748

Browse files
Guard dev pushes against missing version bumps
1 parent 307e291 commit 4ebc748

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

.githooks/pre-push

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ cd "${ROOT_DIR}"
77
echo "[pre-push] Running main branch history guard..."
88
bash scripts/main_branch_history_guard.sh
99

10+
echo "[pre-push] Running dev version bump guard..."
11+
bash scripts/dev_version_bump_guard.sh
12+
1013
echo "[pre-push] Running release guard checks..."
1114
bash scripts/release_guard.sh
1215

scripts/dev_version_bump_guard.sh

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
# shellcheck source=scripts/lib.sh
6+
source "${ROOT_DIR}/scripts/lib.sh"
7+
cd "${ROOT_DIR}"
8+
9+
fvplus::require_commands git sed
10+
11+
detect_branch() {
12+
local branch=""
13+
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
14+
if [[ -z "${branch}" || "${branch}" == "HEAD" ]]; then
15+
branch="${GITHUB_REF_NAME:-}"
16+
branch="${branch#refs/heads/}"
17+
fi
18+
printf '%s' "${branch}"
19+
}
20+
21+
resolve_base_ref() {
22+
local explicit_base="${FVPLUS_DEV_VERSION_BASE_REF:-}"
23+
if [[ -n "${explicit_base}" ]]; then
24+
if git rev-parse --verify "${explicit_base}^{commit}" >/dev/null 2>&1; then
25+
printf '%s' "${explicit_base}"
26+
return
27+
fi
28+
fvplus::fail "FVPLUS_DEV_VERSION_BASE_REF does not resolve to a commit: ${explicit_base}"
29+
fi
30+
31+
if git rev-parse --verify '@{upstream}' >/dev/null 2>&1; then
32+
printf '%s' '@{upstream}'
33+
return
34+
fi
35+
36+
if git rev-parse --verify 'refs/remotes/origin/dev^{commit}' >/dev/null 2>&1; then
37+
printf '%s' 'refs/remotes/origin/dev'
38+
return
39+
fi
40+
41+
if git rev-parse --verify 'HEAD~1' >/dev/null 2>&1; then
42+
printf '%s' 'HEAD~1'
43+
return
44+
fi
45+
46+
printf '%s' ''
47+
}
48+
49+
read_version_from_ref() {
50+
local ref_name="${1:-}"
51+
local payload=""
52+
payload="$(git show "${ref_name}:folderview.plus.plg" 2>/dev/null || true)"
53+
if [[ -z "${payload}" ]]; then
54+
printf '%s' ''
55+
return
56+
fi
57+
printf '%s\n' "${payload}" | sed -n 's/^<!ENTITY version "\([^"]*\)".*/\1/p' | head -n 1
58+
}
59+
60+
is_release_relevant_path() {
61+
local file_path="${1:-}"
62+
case "${file_path}" in
63+
src/folderview.plus/*|folderview.plus.plg|folderview.plus.xml)
64+
return 0
65+
;;
66+
*)
67+
return 1
68+
;;
69+
esac
70+
}
71+
72+
TARGET_BRANCH="$(detect_branch)"
73+
if [[ "${TARGET_BRANCH}" != "dev" ]]; then
74+
echo "Dev version bump guard skipped: branch=${TARGET_BRANCH:-unknown}"
75+
exit 0
76+
fi
77+
78+
BASE_REF="$(resolve_base_ref)"
79+
if [[ -z "${BASE_REF}" ]]; then
80+
echo "Dev version bump guard passed: no comparison base available."
81+
exit 0
82+
fi
83+
84+
CURRENT_VERSION="$(fvplus::read_plg_version "${ROOT_DIR}/folderview.plus.plg")"
85+
BASE_VERSION="$(read_version_from_ref "${BASE_REF}")"
86+
if [[ -z "${BASE_VERSION}" ]]; then
87+
echo "Dev version bump guard passed: base manifest unavailable at ${BASE_REF}."
88+
exit 0
89+
fi
90+
91+
mapfile -t CHANGED_FILES < <(git diff --name-only "${BASE_REF}..HEAD" || true)
92+
RELEASE_RELEVANT_FILES=()
93+
for file_path in "${CHANGED_FILES[@]}"; do
94+
[[ -z "${file_path}" ]] && continue
95+
if is_release_relevant_path "${file_path}"; then
96+
RELEASE_RELEVANT_FILES+=("${file_path}")
97+
fi
98+
done
99+
100+
if [[ "${#RELEASE_RELEVANT_FILES[@]}" -eq 0 ]]; then
101+
echo "Dev version bump guard passed: no shipped plugin files changed."
102+
exit 0
103+
fi
104+
105+
if [[ "${CURRENT_VERSION}" == "${BASE_VERSION}" ]]; then
106+
echo "ERROR: dev pushes that change shipped plugin files must bump folderview.plus.plg version." >&2
107+
echo "base=${BASE_REF} version=${BASE_VERSION} current=${CURRENT_VERSION}" >&2
108+
echo "Changed shipped files:" >&2
109+
printf ' %s\n' "${RELEASE_RELEVANT_FILES[@]}" >&2
110+
echo "HINT: Run 'bash scripts/release_prepare.sh' or 'bash pkg_build.sh', commit the updated manifest/archive artifacts, then push dev again." >&2
111+
exit 1
112+
fi
113+
114+
echo "Dev version bump guard passed: ${BASE_VERSION} -> ${CURRENT_VERSION}"

tests/versioning-guard.test.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ const themeRuntimeGuardPath = path.join(repoRoot, 'scripts/theme_runtime_guard.s
3333
const perfBudgetGuardPath = path.join(repoRoot, 'scripts/perf_budget_guard.sh');
3434
const reproBuildGuardPath = path.join(repoRoot, 'scripts/repro_build_guard.sh');
3535
const mainBranchHistoryGuardPath = path.join(repoRoot, 'scripts/main_branch_history_guard.sh');
36+
const devVersionBumpGuardPath = path.join(repoRoot, 'scripts/dev_version_bump_guard.sh');
3637
const pruneArchivesPath = path.join(repoRoot, 'scripts/prune_archives.sh');
3738
const unraidMatrixSmokePath = path.join(repoRoot, 'scripts/unraid_matrix_smoke.sh');
3839
const ensureChangesPath = path.join(repoRoot, 'scripts/ensure_plg_changes_entry.sh');
3940
const doctorPath = path.join(repoRoot, 'scripts/doctor.sh');
4041
const sharedLibPath = path.join(repoRoot, 'scripts/lib.sh');
42+
const prePushHookPath = path.join(repoRoot, '.githooks/pre-push');
4143
const perfBaselinePath = path.join(repoRoot, 'scripts/perf_baseline.json');
4244
const pkgBuild = fs.readFileSync(pkgBuildPath, 'utf8');
4345
const stableTemplate = fs.readFileSync(stableTemplatePath, 'utf8');
@@ -69,11 +71,13 @@ const themeRuntimeGuard = fs.readFileSync(themeRuntimeGuardPath, 'utf8');
6971
const perfBudgetGuard = fs.readFileSync(perfBudgetGuardPath, 'utf8');
7072
const reproBuildGuard = fs.readFileSync(reproBuildGuardPath, 'utf8');
7173
const mainBranchHistoryGuard = fs.readFileSync(mainBranchHistoryGuardPath, 'utf8');
74+
const devVersionBumpGuard = fs.readFileSync(devVersionBumpGuardPath, 'utf8');
7275
const pruneArchives = fs.readFileSync(pruneArchivesPath, 'utf8');
7376
const unraidMatrixSmoke = fs.readFileSync(unraidMatrixSmokePath, 'utf8');
7477
const ensureChanges = fs.readFileSync(ensureChangesPath, 'utf8');
7578
const doctorScript = fs.readFileSync(doctorPath, 'utf8');
7679
const sharedLib = fs.readFileSync(sharedLibPath, 'utf8');
80+
const prePushHook = fs.readFileSync(prePushHookPath, 'utf8');
7781
const perfBaseline = JSON.parse(fs.readFileSync(perfBaselinePath, 'utf8'));
7882

7983
test('pkg_build computes stable versions per current date only', () => {
@@ -208,6 +212,19 @@ test('remote publish guard validates raw manifest, archive, and checksum after p
208212
assert.match(remotePublishGuard, /remote raw manifest, archive, and checksum match/);
209213
});
210214

215+
test('dev pushes that change shipped plugin files must bump the manifest version', () => {
216+
assert.match(devVersionBumpGuard, /TARGET_BRANCH="\$\(detect_branch\)"/);
217+
assert.match(devVersionBumpGuard, /if \[\[ "\$\{TARGET_BRANCH\}" != "dev" \]\]/);
218+
assert.match(devVersionBumpGuard, /FVPLUS_DEV_VERSION_BASE_REF/);
219+
assert.match(devVersionBumpGuard, /fvplus::read_plg_version "\$\{ROOT_DIR\}\/folderview\.plus\.plg"/);
220+
assert.match(devVersionBumpGuard, /git show "\$\{ref_name\}:folderview\.plus\.plg"/);
221+
assert.match(devVersionBumpGuard, /src\/folderview\.plus\/\*|folderview\.plus\.plg|folderview\.plus\.xml/);
222+
assert.match(devVersionBumpGuard, /change shipped plugin files must bump folderview\.plus\.plg version/);
223+
assert.match(devVersionBumpGuard, /bash scripts\/release_prepare\.sh' or 'bash pkg_build\.sh/);
224+
assert.match(prePushHook, /echo "\[pre-push\] Running dev version bump guard\.\.\."/);
225+
assert.match(prePushHook, /bash scripts\/dev_version_bump_guard\.sh/);
226+
});
227+
211228
test('browser smoke scripts require folder editor coverage and include real editor interaction smoke', () => {
212229
assert.match(browserSmokeShell, /FVPLUS_BROWSER_SMOKE_URL/);
213230
assert.match(browserSmokeShell, /FVPLUS_BROWSER_SMOKE_REQUIRED/);

0 commit comments

Comments
 (0)