Skip to content

Commit 9cbe437

Browse files
authored
Extract shell scripts from commit-as-pull-request prompt (#102)
Move git operations into reusable scripts under .github/scripts/ci/: - parse-repo-info.sh: extract owner/repo from remote URL - commit-and-push.sh: verify, format, branch, commit, push - sync-after-merge.sh: sync main and delete branch Simplify the prompt from 10 steps to 6 by delegating to scripts.
1 parent a4ccc84 commit 9cbe437

File tree

4 files changed

+161
-45
lines changed

4 files changed

+161
-45
lines changed

.github/prompts/commit-as-pull-request.prompt.md

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,28 @@ You are an automated assistant that takes the current uncommitted changes in the
88
- There must be uncommitted changes (staged or unstaged) in the working tree.
99
- The GitHub MCP tools must be available for creating and merging pull requests.
1010

11-
## Workflow
11+
## Helper Scripts
1212

13-
Execute the following steps **in order**. Stop immediately if any step fails.
13+
The following scripts in `.github/scripts/ci/` automate the git operations for this workflow:
1414

15-
### Step 1: Verify there are changes to commit
15+
| Script | Purpose |
16+
|--------|---------|
17+
| `parse-repo-info.sh` | Extracts `REPO_OWNER` and `REPO_NAME` from the git remote URL |
18+
| `commit-and-push.sh` | Verifies changes, runs formatter, creates branch, commits, and pushes |
19+
| `sync-after-merge.sh` | Syncs local `main` and deletes the feature branch |
1620

17-
Run `git status --porcelain` to confirm there are uncommitted changes. If the output is empty, inform the user there is nothing to commit and stop.
21+
## Workflow
1822

19-
### Step 2: Determine the repository owner and name
23+
Execute the following steps **in order**. Stop immediately if any step fails.
2024

21-
Read the repository remote URL to extract the GitHub `owner` and `repo`:
25+
### Step 1: Determine the repository owner and name
2226

2327
```bash
24-
git remote get-url origin
28+
eval "$(.github/scripts/ci/parse-repo-info.sh)"
29+
# Sets: REPO_OWNER, REPO_NAME
2530
```
2631

27-
Parse the owner and repo from the URL (handles both HTTPS and SSH formats).
28-
29-
### Step 3: Auto-detect branch name and commit message
32+
### Step 2: Auto-detect branch name and commit message
3033

3134
Analyze the changed files using `git diff` (and `git diff --cached` for staged changes) to understand what was modified. Generate:
3235

@@ -37,69 +40,47 @@ Analyze the changed files using `git diff` (and `git diff --cached` for staged c
3740

3841
If the user has provided an explicit branch name or commit message, use those instead.
3942

40-
### Step 4: Run code formatter
41-
42-
If the project has a formatter configured, run it before committing:
43-
44-
```bash
45-
mvn spotless:apply
46-
```
47-
48-
Only run this if a `pom.xml` exists with Spotless configured. Skip for non-Maven projects.
49-
50-
### Step 5: Create branch, stage, and commit
51-
52-
```bash
53-
git checkout -b <branch-name>
54-
git add -A
55-
git commit -m "<commit-message>"
56-
```
43+
### Step 3: Commit and push
5744

58-
### Step 6: Push the branch
45+
Runs the formatter (if applicable), creates the branch, stages all changes, commits, and pushes:
5946

6047
```bash
61-
git push -u origin <branch-name>
48+
.github/scripts/ci/commit-and-push.sh "<branch-name>" "<commit-message>"
49+
# Outputs: BRANCH_NAME (may differ if suffix was appended)
6250
```
6351

64-
If the push reports "Everything up-to-date", verify with `git log --oneline -1` that the commit exists, then retry with `git push -u origin <branch-name> 2>&1`.
52+
Pass `--skip-format` as a third argument to skip `mvn spotless:apply` (e.g., when only non-Java files changed).
6553

66-
### Step 7: Create a pull request
54+
### Step 4: Create a pull request
6755

6856
Use the GitHub MCP `create_pull_request` tool with:
6957

70-
- **owner** and **repo**: from Step 2
58+
- **owner** and **repo**: from Step 1
7159
- **title**: the first line of the commit message
72-
- **head**: the branch name
60+
- **head**: the branch name from Step 3
7361
- **base**: `main` (or the repository's default branch)
7462
- **body**: A well-structured PR description including:
7563
- **Summary**: What the change does and why
7664
- **Changes**: Bullet list of files/areas modified
7765
- **Testing**: How the changes were verified
7866

79-
### Step 8: Merge the pull request
67+
### Step 5: Merge the pull request
8068

8169
Use the GitHub MCP `merge_pull_request` tool with:
8270

8371
- **merge_method**: `squash`
8472
- **commit_title**: `<PR title> (#<PR number>)`
8573

86-
### Step 9: Sync local main
87-
88-
```bash
89-
git checkout main
90-
git pull
91-
```
92-
93-
### Step 10: Clean up the local branch (optional)
74+
### Step 6: Sync and clean up
9475

9576
```bash
96-
git branch -d <branch-name>
77+
.github/scripts/ci/sync-after-merge.sh "<branch-name>"
9778
```
9879

9980
## Error Handling
10081

101-
- If the branch name already exists, append a numeric suffix (e.g., `fix/my-change-2`).
102-
- If the push fails due to authentication, inform the user and stop.
82+
- Branch name collisions are handled automatically by `commit-and-push.sh` (appends a numeric suffix).
83+
- If the push fails due to authentication, the script exits with code 2 — inform the user and stop.
10384
- If the PR creation fails, provide the error and stop.
10485
- If the merge fails (e.g., merge conflicts, required checks), inform the user and leave the PR open.
10586

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/usr/bin/env bash
2+
# Verify changes, optionally run the code formatter, create a branch,
3+
# stage all changes, commit, and push to origin.
4+
#
5+
# Usage:
6+
# ./commit-and-push.sh <branch-name> <commit-message> [--skip-format]
7+
#
8+
# Arguments:
9+
# branch-name The branch to create (e.g., fix/my-change)
10+
# commit-message The commit message (may include newlines via $'...' syntax)
11+
# --skip-format Skip running mvn spotless:apply
12+
#
13+
# Exit codes:
14+
# 0 Success
15+
# 1 No changes to commit / general error
16+
# 2 Push failed
17+
18+
set -euo pipefail
19+
20+
if [[ $# -lt 2 ]]; then
21+
echo "Usage: $0 <branch-name> <commit-message> [--skip-format]" >&2
22+
exit 1
23+
fi
24+
25+
branch_name="$1"
26+
commit_message="$2"
27+
skip_format="${3:-}"
28+
29+
# -- Step 1: Verify there are changes to commit ---------------------------------
30+
if [[ -z "$(git status --porcelain)" ]]; then
31+
echo "ERROR: No uncommitted changes found. Nothing to commit." >&2
32+
exit 1
33+
fi
34+
35+
echo "✓ Uncommitted changes detected."
36+
37+
# -- Step 4: Run code formatter (if applicable) ----------------------------------
38+
if [[ "$skip_format" != "--skip-format" ]] && [[ -f pom.xml ]]; then
39+
if grep -q 'spotless-maven-plugin' pom.xml 2>/dev/null; then
40+
echo "Running Spotless formatter..."
41+
mvn -q spotless:apply
42+
echo "✓ Spotless formatting applied."
43+
fi
44+
fi
45+
46+
# -- Step 5: Create branch, stage, and commit ------------------------------------
47+
# If the branch already exists locally, append a numeric suffix
48+
actual_branch="$branch_name"
49+
suffix=2
50+
while git show-ref --verify --quiet "refs/heads/$actual_branch" 2>/dev/null; do
51+
actual_branch="${branch_name}-${suffix}"
52+
suffix=$((suffix + 1))
53+
done
54+
55+
git checkout -b "$actual_branch"
56+
git add -A
57+
git commit -m "$commit_message"
58+
59+
echo "✓ Committed on branch '$actual_branch'."
60+
61+
# -- Step 6: Push the branch -----------------------------------------------------
62+
if ! git push -u origin "$actual_branch" 2>&1; then
63+
echo "ERROR: Push failed." >&2
64+
exit 2
65+
fi
66+
67+
# Verify the push actually transferred the commit
68+
remote_sha=$(git ls-remote origin "refs/heads/$actual_branch" | awk '{print $1}')
69+
local_sha=$(git rev-parse HEAD)
70+
71+
if [[ "$remote_sha" != "$local_sha" ]]; then
72+
echo "WARNING: Remote SHA does not match local. Retrying push..." >&2
73+
git push -u origin "$actual_branch" 2>&1 || { echo "ERROR: Retry push failed." >&2; exit 2; }
74+
fi
75+
76+
echo "✓ Pushed to origin/$actual_branch."
77+
echo "BRANCH_NAME=$actual_branch"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env bash
2+
# Parse the GitHub owner and repository name from the git remote URL.
3+
# Outputs two lines: owner on the first, repo on the second.
4+
# Handles both HTTPS and SSH remote URL formats.
5+
#
6+
# Usage:
7+
# eval "$(./parse-repo-info.sh)"
8+
# echo "Owner: $REPO_OWNER Repo: $REPO_NAME"
9+
10+
set -euo pipefail
11+
12+
remote_url=$(git remote get-url origin 2>/dev/null) || {
13+
echo "ERROR: No git remote named 'origin' found." >&2
14+
exit 1
15+
}
16+
17+
# Strip trailing .git if present
18+
remote_url="${remote_url%.git}"
19+
20+
if [[ "$remote_url" =~ github\.com[:/]([^/]+)/([^/]+)$ ]]; then
21+
owner="${BASH_REMATCH[1]}"
22+
repo="${BASH_REMATCH[2]}"
23+
else
24+
echo "ERROR: Could not parse owner/repo from remote URL: $remote_url" >&2
25+
exit 1
26+
fi
27+
28+
echo "REPO_OWNER=$owner"
29+
echo "REPO_NAME=$repo"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env bash
2+
# After a PR has been merged, sync the local main branch and
3+
# optionally delete the local feature branch.
4+
#
5+
# Usage:
6+
# ./sync-after-merge.sh [branch-name]
7+
#
8+
# Arguments:
9+
# branch-name The local branch to delete (optional). Skipped if omitted.
10+
11+
set -euo pipefail
12+
13+
branch_name="${1:-}"
14+
15+
# -- Step 9: Sync local main ----------------------------------------------------
16+
git checkout main
17+
git pull
18+
19+
echo "✓ Local main is up to date."
20+
21+
# -- Step 10: Clean up the local branch ------------------------------------------
22+
if [[ -n "$branch_name" ]]; then
23+
if git show-ref --verify --quiet "refs/heads/$branch_name" 2>/dev/null; then
24+
git branch -d "$branch_name" 2>/dev/null || git branch -D "$branch_name"
25+
echo "✓ Deleted local branch '$branch_name'."
26+
else
27+
echo "Branch '$branch_name' does not exist locally (already cleaned up)."
28+
fi
29+
fi

0 commit comments

Comments
 (0)