Skip to content

Commit b7a7950

Browse files
Merge pull request #316 from wphillipmoore/feature/sync-tooling
feat: sync shared tooling from standard-tooling v1.0.0
2 parents e4c5566 + 41161e0 commit b7a7950

7 files changed

Lines changed: 422 additions & 19 deletions

File tree

scripts/dev/finalize_repo.sh

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Finalize a repository after a PR merge: switch to the target branch,
5+
# fast-forward pull, delete merged local branches, and prune remotes.
6+
7+
# -- defaults ----------------------------------------------------------------
8+
9+
target_branch="develop"
10+
dry_run=false
11+
12+
# -- argument parsing --------------------------------------------------------
13+
14+
usage() {
15+
cat <<EOF
16+
Usage: $(basename "$0") [OPTIONS]
17+
18+
Finalize a repository after a PR merge.
19+
20+
Options:
21+
--target-branch BRANCH Target branch to switch to (default: develop)
22+
--dry-run Show what would be done without making changes
23+
-h, --help Show this help message
24+
EOF
25+
}
26+
27+
while [[ $# -gt 0 ]]; do
28+
case "$1" in
29+
--target-branch)
30+
target_branch="$2"
31+
shift 2
32+
;;
33+
--dry-run)
34+
dry_run=true
35+
shift
36+
;;
37+
-h|--help)
38+
usage
39+
exit 0
40+
;;
41+
*)
42+
echo "ERROR: unknown option '$1'" >&2
43+
usage >&2
44+
exit 1
45+
;;
46+
esac
47+
done
48+
49+
# -- eternal branch detection -----------------------------------------------
50+
51+
repo_root="$(git rev-parse --show-toplevel)"
52+
profile_file="$repo_root/docs/repository-standards.md"
53+
branching_model=""
54+
55+
if [[ -f "$profile_file" ]]; then
56+
while IFS= read -r line; do
57+
if [[ "$line" =~ ^[[:space:]-]*branching_model:[[:space:]]*(.+)$ ]]; then
58+
branching_model="${BASH_REMATCH[1]}"
59+
break
60+
fi
61+
done < "$profile_file"
62+
fi
63+
64+
# Build the list of eternal branches to protect from deletion.
65+
eternal_branches=("gh-pages")
66+
67+
case "$branching_model" in
68+
docs-single-branch)
69+
eternal_branches+=("develop")
70+
;;
71+
library-release)
72+
eternal_branches+=("develop" "main")
73+
;;
74+
application-promotion)
75+
eternal_branches+=("develop" "release" "main")
76+
;;
77+
"")
78+
echo "WARNING: branching_model not found; protecting develop and main." >&2
79+
eternal_branches+=("develop" "main")
80+
;;
81+
*)
82+
echo "ERROR: unrecognized branching_model '$branching_model'." >&2
83+
exit 1
84+
;;
85+
esac
86+
87+
is_eternal() {
88+
local branch="$1"
89+
for eternal in "${eternal_branches[@]}"; do
90+
if [[ "$branch" == "$eternal" ]]; then
91+
return 0
92+
fi
93+
done
94+
return 1
95+
}
96+
97+
# -- helpers -----------------------------------------------------------------
98+
99+
run() {
100+
if [[ "$dry_run" == true ]]; then
101+
echo " [dry-run] $*"
102+
else
103+
"$@"
104+
fi
105+
}
106+
107+
# -- step 1: switch to target branch ----------------------------------------
108+
109+
current_branch="$(git rev-parse --abbrev-ref HEAD)"
110+
111+
if [[ "$current_branch" != "$target_branch" ]]; then
112+
echo "Switching to $target_branch..."
113+
run git checkout "$target_branch"
114+
else
115+
echo "Already on $target_branch."
116+
fi
117+
118+
# -- step 2: fetch and fast-forward pull ------------------------------------
119+
120+
echo "Pulling latest from origin/$target_branch..."
121+
run git fetch origin "$target_branch"
122+
if [[ "$dry_run" != true ]]; then
123+
git pull --ff-only origin "$target_branch"
124+
else
125+
echo " [dry-run] git pull --ff-only origin $target_branch"
126+
fi
127+
128+
# -- step 3: delete merged local branches -----------------------------------
129+
130+
echo "Checking for merged local branches..."
131+
deleted_branches=()
132+
133+
for branch in $(git branch --merged "$target_branch" --format='%(refname:short)'); do
134+
if is_eternal "$branch"; then
135+
continue
136+
fi
137+
echo " Deleting merged branch: $branch"
138+
run git branch -d "$branch"
139+
deleted_branches+=("$branch")
140+
done
141+
142+
# -- step 4: prune stale remote-tracking references -------------------------
143+
144+
echo "Pruning stale remote-tracking references..."
145+
run git remote prune origin
146+
147+
# -- summary -----------------------------------------------------------------
148+
149+
echo ""
150+
echo "Finalization complete."
151+
echo " Branch: $target_branch"
152+
if [[ ${#deleted_branches[@]} -gt 0 ]]; then
153+
echo " Deleted: ${deleted_branches[*]}"
154+
else
155+
echo " Deleted: (none)"
156+
fi
157+
echo " Remotes: pruned"

scripts/dev/prepare_release.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
"""
1515

1616
from __future__ import annotations
17+
1718
import argparse
1819
import re
1920
import shutil
2021
import subprocess
2122
import sys
2223
from pathlib import Path
2324

24-
2525
# -- helpers -----------------------------------------------------------------
2626

2727

@@ -126,6 +126,20 @@ def ensure_clean_tree() -> None:
126126
raise SystemExit(message)
127127

128128

129+
def ensure_develop_up_to_date() -> None:
130+
"""Fail if local develop is behind origin/develop."""
131+
run_command(("git", "fetch", "origin", "develop"))
132+
local_sha = read_command_output(("git", "rev-parse", "HEAD"))
133+
remote_sha = read_command_output(("git", "rev-parse", "origin/develop"))
134+
if local_sha != remote_sha:
135+
message = (
136+
f"Local develop ({local_sha[:8]}) does not match "
137+
f"origin/develop ({remote_sha[:8]}). "
138+
f"Pull latest changes before preparing a release."
139+
)
140+
raise SystemExit(message)
141+
142+
129143
def branch_exists(name: str) -> bool:
130144
"""Return True if a branch exists locally or on origin."""
131145
for ref in (name, f"origin/{name}"):
@@ -258,6 +272,7 @@ def main() -> int:
258272

259273
ensure_on_develop()
260274
ensure_clean_tree()
275+
ensure_develop_up_to_date()
261276
ensure_tool_available("gh")
262277

263278
ecosystem, version = detect_ecosystem()

0 commit comments

Comments
 (0)