Update version_branch.yml
This commit is contained in:
211
.github/workflows/version_branch.yml
vendored
211
.github/workflows/version_branch.yml
vendored
@@ -1,10 +1,10 @@
|
|||||||
name: Create version branch and bump versions
|
name: Create version branch and bump versions (src and docs only)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
new_version:
|
new_version:
|
||||||
description: "New version in format NN.NN.NN (example 01.03.00)"
|
description: "New version in format NN.NN.NN (example 03.01.00)"
|
||||||
required: true
|
required: true
|
||||||
base_branch:
|
base_branch:
|
||||||
description: "Base branch to branch from"
|
description: "Base branch to branch from"
|
||||||
@@ -47,22 +47,21 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
trap 'echo "[FATAL] Validation error at line $LINENO" >&2' ERR
|
trap 'echo "[FATAL] Validation error at line $LINENO" >&2; echo "[FATAL] Last command: $BASH_COMMAND" >&2' ERR
|
||||||
|
|
||||||
echo "[INFO] Inputs received:"
|
echo "[INFO] Inputs received:"
|
||||||
echo " NEW_VERSION=${NEW_VERSION}"
|
echo " NEW_VERSION=${NEW_VERSION}"
|
||||||
echo " BASE_BRANCH=${BASE_BRANCH}"
|
echo " BASE_BRANCH=${BASE_BRANCH}"
|
||||||
echo " BRANCH_PREFIX=${BRANCH_PREFIX}"
|
echo " BRANCH_PREFIX=${BRANCH_PREFIX}"
|
||||||
|
echo " COMMIT_CHANGES=${COMMIT_CHANGES}"
|
||||||
|
|
||||||
[[ -n "${NEW_VERSION}" ]] || { echo "[ERROR] new_version missing"; exit 2; }
|
[[ -n "${NEW_VERSION}" ]] || { echo "[ERROR] new_version missing" >&2; exit 2; }
|
||||||
[[ "${NEW_VERSION}" =~ ^[0-9]{2}\.[0-9]{2}\.[0-9]{2}$ ]] || {
|
[[ "${NEW_VERSION}" =~ ^[0-9]{2}\.[0-9]{2}\.[0-9]{2}$ ]] || { echo "[ERROR] Invalid version format: ${NEW_VERSION}" >&2; exit 2; }
|
||||||
echo "[ERROR] Invalid version format: ${NEW_VERSION}"
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
|
|
||||||
git show-ref --verify --quiet "refs/remotes/origin/${BASE_BRANCH}" || {
|
git ls-remote --exit-code --heads origin "${BASE_BRANCH}" >/dev/null 2>&1 || {
|
||||||
echo "[ERROR] Base branch does not exist on origin: ${BASE_BRANCH}"
|
echo "[ERROR] Base branch does not exist on origin: ${BASE_BRANCH}" >&2
|
||||||
git branch -a
|
echo "[INFO] Remote branches:"
|
||||||
|
git ls-remote --heads origin | awk '{sub("refs/heads/","",$2); print $2}'
|
||||||
exit 2
|
exit 2
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +71,8 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
trap 'echo "[FATAL] Git identity step failed at line $LINENO" >&2; echo "[FATAL] Last command: $BASH_COMMAND" >&2' ERR
|
||||||
|
|
||||||
git config user.name "github-actions[bot]"
|
git config user.name "github-actions[bot]"
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
echo "[INFO] Git identity configured"
|
echo "[INFO] Git identity configured"
|
||||||
@@ -80,78 +81,198 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
trap 'echo "[FATAL] Branch creation failed at line $LINENO" >&2' ERR
|
trap 'echo "[FATAL] Branch creation failed at line $LINENO" >&2; echo "[FATAL] Last command: $BASH_COMMAND" >&2' ERR
|
||||||
|
|
||||||
BRANCH_NAME="${BRANCH_PREFIX}${NEW_VERSION}"
|
BRANCH_NAME="${BRANCH_PREFIX}${NEW_VERSION}"
|
||||||
echo "[INFO] Creating branch ${BRANCH_NAME}"
|
echo "[INFO] Creating branch: ${BRANCH_NAME} from origin/${BASE_BRANCH}"
|
||||||
|
|
||||||
git fetch --all --tags --prune
|
git fetch --all --tags --prune
|
||||||
|
|
||||||
if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" >/dev/null 2>&1; then
|
if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" >/dev/null 2>&1; then
|
||||||
echo "[ERROR] Branch already exists on origin: ${BRANCH_NAME}"
|
echo "[ERROR] Branch already exists on origin: ${BRANCH_NAME}" >&2
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git checkout -B "${BRANCH_NAME}" "origin/${BASE_BRANCH}"
|
git checkout -B "${BRANCH_NAME}" "origin/${BASE_BRANCH}"
|
||||||
|
echo "BRANCH_NAME=${BRANCH_NAME}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Bump versions in headers and XML
|
- name: Preflight discovery (src and docs only)
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
trap 'echo "[FATAL] Version bump failed at line $LINENO" >&2' ERR
|
trap 'echo "[FATAL] Preflight failed at line $LINENO" >&2; echo "[FATAL] Last command: $BASH_COMMAND" >&2' ERR
|
||||||
|
|
||||||
|
TARGET_DIRS=("src" "docs")
|
||||||
|
|
||||||
|
echo "[INFO] Confirming target directories exist"
|
||||||
|
FOUND_ANY=0
|
||||||
|
for d in "${TARGET_DIRS[@]}"; do
|
||||||
|
if [[ -d "${d}" ]]; then
|
||||||
|
echo "[INFO] Found directory: ${d}"
|
||||||
|
FOUND_ANY=1
|
||||||
|
else
|
||||||
|
echo "[WARN] Missing directory: ${d}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "${FOUND_ANY}" -ne 1 ]]; then
|
||||||
|
echo "[ERROR] Neither ./src nor ./docs exists in this checkout" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[INFO] Searching for VERSION: lines under src and docs"
|
||||||
|
HIT_VERSION=0
|
||||||
|
for d in "${TARGET_DIRS[@]}"; do
|
||||||
|
[[ -d "${d}" ]] || continue
|
||||||
|
COUNT=$(grep -RIn --exclude-dir=.git "^[[:space:]]*VERSION[[:space:]]*:" "${d}" | wc -l || true)
|
||||||
|
echo "[INFO] VERSION: hits in ${d}: ${COUNT}"
|
||||||
|
HIT_VERSION=$((HIT_VERSION + COUNT))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "[INFO] Searching for XML <version> tags under src and docs"
|
||||||
|
HIT_XML=0
|
||||||
|
for d in "${TARGET_DIRS[@]}"; do
|
||||||
|
[[ -d "${d}" ]] || continue
|
||||||
|
COUNT=$(grep -RIn --exclude-dir=.git "<version" "${d}" | wc -l || true)
|
||||||
|
echo "[INFO] <version> hits in ${d}: ${COUNT}"
|
||||||
|
HIT_XML=$((HIT_XML + COUNT))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "[INFO] Total VERSION: hits: ${HIT_VERSION}"
|
||||||
|
echo "[INFO] Total <version> hits: ${HIT_XML}"
|
||||||
|
|
||||||
|
- name: Bump versions in headers and XML (src and docs only)
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
trap 'echo "[FATAL] Version bump failed at line $LINENO" >&2; echo "[FATAL] Last command: $BASH_COMMAND" >&2' ERR
|
||||||
|
|
||||||
python3 - <<'PY'
|
python3 - <<'PY'
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import os
|
from collections import defaultdict
|
||||||
|
|
||||||
|
new_version = os.environ.get("NEW_VERSION", "").strip()
|
||||||
|
if not new_version:
|
||||||
|
raise SystemExit("[FATAL] NEW_VERSION env var missing")
|
||||||
|
|
||||||
new_version = os.environ["NEW_VERSION"]
|
|
||||||
root = Path(".").resolve()
|
root = Path(".").resolve()
|
||||||
|
targets = [root / "src", root / "docs"]
|
||||||
|
|
||||||
header_re = re.compile(r"(?m)^(\\s*VERSION\\s*:\\s*)(\\d{2}\\.\\d{2}\\.\\d{2})(\\s*)$")
|
header_re = re.compile(r"(?m)^(\s*VERSION\s*:\s*)(\S+)(\s*)$")
|
||||||
xml_re = re.compile(r"(?is)(<version\\s*>)(\\s*\\d{2}\\.\\d{2}\\.\\d{2}\\s*)(</version\\s*>)")
|
xml_re = re.compile(r"(?is)(<version\s*>)([^<]*?)(</version\s*>)")
|
||||||
|
|
||||||
|
skip_ext = {".json", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".pdf", ".zip", ".7z", ".tar", ".gz", ".woff", ".woff2", ".ttf", ".otf", ".mp3", ".mp4"}
|
||||||
|
skip_dirs = {".git", "node_modules", "vendor", ".venv", "dist", "build"}
|
||||||
|
|
||||||
|
counters = defaultdict(int)
|
||||||
updated = []
|
updated = []
|
||||||
|
|
||||||
for p in root.rglob("*"):
|
def should_skip(p: Path) -> bool:
|
||||||
if not p.is_file():
|
if p.suffix.lower() in skip_ext:
|
||||||
continue
|
counters["skipped_by_ext"] += 1
|
||||||
if p.suffix.lower() == ".json":
|
return True
|
||||||
continue
|
parts = {x.lower() for x in p.parts}
|
||||||
try:
|
if any(d in parts for d in skip_dirs):
|
||||||
text = p.read_text(encoding="utf-8")
|
counters["skipped_by_dir"] += 1
|
||||||
except Exception:
|
return True
|
||||||
continue
|
return False
|
||||||
|
|
||||||
original = text
|
existing_targets = [t for t in targets if t.exists() and t.is_dir()]
|
||||||
text = header_re.sub(r"\\1" + new_version + r"\\3", text)
|
if not existing_targets:
|
||||||
if p.suffix.lower() == ".xml":
|
raise SystemExit("[ERROR] Neither ./src nor ./docs exists in this checkout")
|
||||||
text = xml_re.sub(r"\\1" + new_version + r"\\3", text)
|
|
||||||
|
|
||||||
if text != original:
|
print("[INFO] Scanning directories:")
|
||||||
p.write_text(text, encoding="utf-8")
|
for t in existing_targets:
|
||||||
updated.append(str(p))
|
print(f" - {t}")
|
||||||
|
|
||||||
|
for base in existing_targets:
|
||||||
|
for p in base.rglob("*"):
|
||||||
|
if not p.is_file():
|
||||||
|
continue
|
||||||
|
if should_skip(p):
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = p.read_text(encoding="utf-8")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
counters["skipped_non_utf8"] += 1
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
counters["skipped_read_error"] += 1
|
||||||
|
print(f"[WARN] Read error: {p} :: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
original = text
|
||||||
|
|
||||||
|
text, n1 = header_re.subn(r"\\1" + new_version + r"\\3", text)
|
||||||
|
if n1:
|
||||||
|
counters["header_replacements"] += n1
|
||||||
|
|
||||||
|
if p.suffix.lower() == ".xml":
|
||||||
|
text2, n2 = xml_re.subn(r"\\1" + new_version + r"\\3", text)
|
||||||
|
text = text2
|
||||||
|
if n2:
|
||||||
|
counters["xml_replacements"] += n2
|
||||||
|
|
||||||
|
if text != original:
|
||||||
|
try:
|
||||||
|
p.write_text(text, encoding="utf-8")
|
||||||
|
updated.append(str(p))
|
||||||
|
except Exception as e:
|
||||||
|
raise SystemExit(f"[FATAL] Write failed: {p} :: {e}")
|
||||||
|
|
||||||
|
print("[INFO] Scan summary")
|
||||||
|
for k in sorted(counters.keys()):
|
||||||
|
print(f" {k}: {counters[k]}")
|
||||||
|
|
||||||
|
print(f"[INFO] Updated files: {len(updated)}")
|
||||||
|
for f in updated[:200]:
|
||||||
|
print(f" [UPDATED] {f}")
|
||||||
|
if len(updated) > 200:
|
||||||
|
print(f" [INFO] (truncated) +{len(updated) - 200} more")
|
||||||
|
|
||||||
if not updated:
|
if not updated:
|
||||||
raise SystemExit("[ERROR] No files updated. Check headers and XML manifests.")
|
print("[ERROR] No files updated within src and docs")
|
||||||
|
print("[DIAG] Confirm these exist in src or docs:")
|
||||||
print(f"[INFO] Updated {len(updated)} files")
|
print(" - A line containing: VERSION: <value>")
|
||||||
for f in updated:
|
print(" - An XML tag: <version>...</version>")
|
||||||
print(f" - {f}")
|
raise SystemExit(1)
|
||||||
PY
|
PY
|
||||||
|
|
||||||
|
- name: Show git status
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
trap 'echo "[FATAL] git status failed at line $LINENO" >&2; echo "[FATAL] Last command: $BASH_COMMAND" >&2' ERR
|
||||||
|
git status --porcelain=v1
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
if: ${{ env.COMMIT_CHANGES == 'true' }}
|
if: ${{ env.COMMIT_CHANGES == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
git status --porcelain
|
trap 'echo "[FATAL] Commit failed at line $LINENO" >&2; echo "[FATAL] Last command: $BASH_COMMAND" >&2' ERR
|
||||||
git add -A
|
|
||||||
|
if [[ -z "$(git status --porcelain=v1)" ]]; then
|
||||||
|
echo "[INFO] No changes detected. Skipping commit and push."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git add src docs
|
||||||
git commit -m "chore(release): bump version to ${NEW_VERSION}"
|
git commit -m "chore(release): bump version to ${NEW_VERSION}"
|
||||||
|
|
||||||
- name: Push branch
|
- name: Push branch
|
||||||
if: ${{ env.COMMIT_CHANGES == 'true' }}
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
git push --set-upstream origin "${BRANCH_PREFIX}${NEW_VERSION}"
|
trap 'echo "[FATAL] Push failed at line $LINENO" >&2; echo "[FATAL] Last command: $BASH_COMMAND" >&2' ERR
|
||||||
|
|
||||||
|
git push --set-upstream origin "${BRANCH_NAME}"
|
||||||
|
|
||||||
|
- name: Output branch name
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
echo "[INFO] Created branch: ${BRANCH_NAME}"
|
||||||
|
|||||||
Reference in New Issue
Block a user