Skip to content

Commit ed459e5

Browse files
committed
refactor(shell): extract post-push PR template
1 parent 8a62c12 commit ed459e5

5 files changed

Lines changed: 272 additions & 252 deletions

File tree

packages/app/src/lib/core/templates-entrypoint/git-hooks.ts

Lines changed: 2 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { renderEntrypointGitPostPushWrapperInstall } from "./git-post-push-wrapper.js"
2+
import { renderPostPushPrEnsure } from "./post-push-pr.js"
23

34
const entrypointGitHooksTemplate = String
45
.raw`# 3) Install global git hooks to protect main/master + managed AGENTS context
@@ -145,132 +146,7 @@ if [[ -z "$REPO_ROOT" || ! -d "$REPO_ROOT" ]]; then
145146
fi
146147
cd "$REPO_ROOT"
147148
148-
# CHANGE: ensure an open GitHub PR exists for the pushed branch before PR-bound post-push tools run.
149-
# WHY: issue #375 requires every successful git push to leave the branch with an open PR; plan sync and session backup both target PR discussion.
150-
# REF: issue-375
151-
docker_git_github_repo_from_remote_url() {
152-
local remote_url="$1"
153-
local repo_path=""
154-
local owner=""
155-
local repo=""
156-
157-
case "$remote_url" in
158-
https://github.com/*)
159-
repo_path="${"${"}remote_url#https://github.com/}"
160-
;;
161-
http://github.com/*)
162-
repo_path="${"${"}remote_url#http://github.com/}"
163-
;;
164-
https://*@github.com/*)
165-
repo_path="${"${"}remote_url#https://*@github.com/}"
166-
;;
167-
http://*@github.com/*)
168-
repo_path="${"${"}remote_url#http://*@github.com/}"
169-
;;
170-
ssh://git@github.com/*)
171-
repo_path="${"${"}remote_url#ssh://git@github.com/}"
172-
;;
173-
git@github.com:*)
174-
repo_path="${"${"}remote_url#git@github.com:}"
175-
;;
176-
*)
177-
return 1
178-
;;
179-
esac
180-
181-
repo_path="${"${"}repo_path%%\?*}"
182-
repo_path="${"${"}repo_path%%#*}"
183-
repo_path="${"${"}repo_path%/}"
184-
repo_path="${"${"}repo_path%.git}"
185-
owner="${"${"}repo_path%%/*}"
186-
repo="${"${"}repo_path#*/}"
187-
repo="${"${"}repo%%/*}"
188-
repo="${"${"}repo%.git}"
189-
190-
if [[ -z "$owner" || -z "$repo" || "$owner" == "$repo_path" ]]; then
191-
return 1
192-
fi
193-
194-
printf "%s/%s\n" "$owner" "$repo"
195-
}
196-
197-
docker_git_github_repo_from_remote() {
198-
local remote="$1"
199-
local remote_url=""
200-
201-
remote_url="$(git remote get-url "$remote" 2>/dev/null || true)"
202-
if [[ -z "$remote_url" ]]; then
203-
return 1
204-
fi
205-
206-
docker_git_github_repo_from_remote_url "$remote_url"
207-
}
208-
209-
docker_git_ensure_open_pr() {
210-
local branch=""
211-
local base_repo=""
212-
local head_repo=""
213-
local head_owner=""
214-
local head_arg=""
215-
local base_branch=""
216-
local pr_url=""
217-
218-
if ! command -v gh >/dev/null 2>&1; then
219-
echo "[post-push-pr] Error: gh CLI not found" >&2
220-
return 1
221-
fi
222-
223-
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
224-
if [[ -z "$branch" || "$branch" == "HEAD" ]]; then
225-
echo "[post-push-pr] Error: cannot create PR from detached HEAD" >&2
226-
return 1
227-
fi
228-
229-
if ! base_repo="$(docker_git_github_repo_from_remote upstream)"; then
230-
if ! base_repo="$(docker_git_github_repo_from_remote origin)"; then
231-
echo "[post-push-pr] Skipped: no GitHub remote found"
232-
return 0
233-
fi
234-
fi
235-
236-
if ! head_repo="$(docker_git_github_repo_from_remote origin)"; then
237-
head_repo="$base_repo"
238-
fi
239-
240-
base_branch="$(gh repo view "$base_repo" --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null || true)"
241-
if [[ -z "$base_branch" ]]; then
242-
echo "[post-push-pr] Error: failed to resolve default branch for $base_repo" >&2
243-
return 1
244-
fi
245-
246-
if [[ "$head_repo" == "$base_repo" ]]; then
247-
head_arg="$branch"
248-
else
249-
head_owner="${"${"}head_repo%%/*}"
250-
head_arg="${"${"}head_owner}:${"${"}branch}"
251-
fi
252-
253-
if ! pr_url="$(gh pr list --repo "$base_repo" --state open --head "$head_arg" --json url --jq '.[0].url // ""' 2>/dev/null)"; then
254-
echo "[post-push-pr] Error: failed to list open PRs for $head_arg in $base_repo" >&2
255-
return 1
256-
fi
257-
if [[ -z "$pr_url" && "$head_arg" != "$branch" ]]; then
258-
if ! pr_url="$(gh pr list --repo "$base_repo" --state open --head "$branch" --json url --jq '.[0].url // ""' 2>/dev/null)"; then
259-
echo "[post-push-pr] Error: failed to list open PRs for $branch in $base_repo" >&2
260-
return 1
261-
fi
262-
fi
263-
264-
if [[ -n "$pr_url" ]]; then
265-
echo "[post-push-pr] Open PR: $pr_url"
266-
return 0
267-
fi
268-
269-
echo "[post-push-pr] Creating PR for $head_arg into $base_repo:$base_branch"
270-
gh pr create --repo "$base_repo" --base "$base_branch" --head "$head_arg" --fill
271-
}
272-
273-
docker_git_ensure_open_pr
149+
${renderPostPushPrEnsure()}
274150
275151
# CHANGE: sync captured Codex plans to the current branch PR after push.
276152
# WHY: issue #369 requires the agent plan to be uploaded to PR discussion.
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/* jscpd:ignore-start */
2+
// Mirror of packages/lib/src/core/templates-entrypoint/post-push-pr.ts.
3+
// The lib template test asserts rendered output equality to prevent drift.
4+
const postPushPrEnsureTemplate = String
5+
.raw`# CHANGE: ensure an open GitHub PR exists for the pushed branch before PR-bound post-push tools run.
6+
# WHY: issue #375 requires every successful git push to leave the branch with an open PR; plan sync and session backup both target PR discussion.
7+
# REF: issue-375
8+
docker_git_github_repo_from_remote_url() {
9+
local remote_url="$1"
10+
local repo_path=""
11+
local owner=""
12+
local repo=""
13+
14+
case "$remote_url" in
15+
https://github.com/*)
16+
repo_path="${"${"}remote_url#https://github.com/}"
17+
;;
18+
http://github.com/*)
19+
repo_path="${"${"}remote_url#http://github.com/}"
20+
;;
21+
https://*@github.com/*)
22+
repo_path="${"${"}remote_url#https://*@github.com/}"
23+
;;
24+
http://*@github.com/*)
25+
repo_path="${"${"}remote_url#http://*@github.com/}"
26+
;;
27+
ssh://git@github.com/*)
28+
repo_path="${"${"}remote_url#ssh://git@github.com/}"
29+
;;
30+
git@github.com:*)
31+
repo_path="${"${"}remote_url#git@github.com:}"
32+
;;
33+
*)
34+
return 1
35+
;;
36+
esac
37+
38+
repo_path="${"${"}repo_path%%\?*}"
39+
repo_path="${"${"}repo_path%%#*}"
40+
repo_path="${"${"}repo_path%/}"
41+
repo_path="${"${"}repo_path%.git}"
42+
owner="${"${"}repo_path%%/*}"
43+
repo="${"${"}repo_path#*/}"
44+
repo="${"${"}repo%%/*}"
45+
repo="${"${"}repo%.git}"
46+
47+
if [[ -z "$owner" || -z "$repo" || "$owner" == "$repo_path" ]]; then
48+
return 1
49+
fi
50+
51+
printf "%s/%s\n" "$owner" "$repo"
52+
}
53+
54+
docker_git_github_repo_from_remote() {
55+
local remote="$1"
56+
local remote_url=""
57+
58+
remote_url="$(git remote get-url "$remote" 2>/dev/null || true)"
59+
if [[ -z "$remote_url" ]]; then
60+
return 1
61+
fi
62+
63+
docker_git_github_repo_from_remote_url "$remote_url"
64+
}
65+
66+
docker_git_ensure_open_pr() {
67+
local branch=""
68+
local base_repo=""
69+
local head_repo=""
70+
local head_owner=""
71+
local head_arg=""
72+
local base_branch=""
73+
local pr_url=""
74+
75+
if ! command -v gh >/dev/null 2>&1; then
76+
echo "[post-push-pr] Error: gh CLI not found" >&2
77+
return 1
78+
fi
79+
80+
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
81+
if [[ -z "$branch" || "$branch" == "HEAD" ]]; then
82+
echo "[post-push-pr] Error: cannot create PR from detached HEAD" >&2
83+
return 1
84+
fi
85+
86+
if ! base_repo="$(docker_git_github_repo_from_remote upstream)"; then
87+
if ! base_repo="$(docker_git_github_repo_from_remote origin)"; then
88+
echo "[post-push-pr] Skipped: no GitHub remote found"
89+
return 0
90+
fi
91+
fi
92+
93+
if ! head_repo="$(docker_git_github_repo_from_remote origin)"; then
94+
head_repo="$base_repo"
95+
fi
96+
97+
base_branch="$(gh repo view "$base_repo" --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null || true)"
98+
if [[ -z "$base_branch" ]]; then
99+
echo "[post-push-pr] Error: failed to resolve default branch for $base_repo" >&2
100+
return 1
101+
fi
102+
103+
if [[ "$head_repo" == "$base_repo" ]]; then
104+
head_arg="$branch"
105+
else
106+
head_owner="${"${"}head_repo%%/*}"
107+
head_arg="${"${"}head_owner}:${"${"}branch}"
108+
fi
109+
110+
if ! pr_url="$(gh pr list --repo "$base_repo" --state open --head "$head_arg" --json url --jq '.[0].url // ""' 2>/dev/null)"; then
111+
echo "[post-push-pr] Error: failed to list open PRs for $head_arg in $base_repo" >&2
112+
return 1
113+
fi
114+
if [[ -z "$pr_url" && "$head_arg" != "$branch" ]]; then
115+
if ! pr_url="$(gh pr list --repo "$base_repo" --state open --head "$branch" --json url --jq '.[0].url // ""' 2>/dev/null)"; then
116+
echo "[post-push-pr] Error: failed to list open PRs for $branch in $base_repo" >&2
117+
return 1
118+
fi
119+
fi
120+
121+
if [[ -n "$pr_url" ]]; then
122+
echo "[post-push-pr] Open PR: $pr_url"
123+
return 0
124+
fi
125+
126+
echo "[post-push-pr] Creating PR for $head_arg into $base_repo:$base_branch"
127+
gh pr create --repo "$base_repo" --base "$base_branch" --head "$head_arg" --fill
128+
}
129+
130+
docker_git_ensure_open_pr`
131+
132+
export const renderPostPushPrEnsure = (): string => postPushPrEnsureTemplate
133+
/* jscpd:ignore-end */

0 commit comments

Comments
 (0)