[ZEPPELIN-6404] Add Go CLI tool for merging pull requests#5183
[ZEPPELIN-6404] Add Go CLI tool for merging pull requests#5183jongyoul wants to merge 18 commits intoapache:masterfrom
Conversation
… library Replace multi-file cobra-based CLI with a single dev/merge-pr.go using only Go standard library (flag package). No go.mod needed - runs directly with `go run dev/merge-pr.go --pr <number> [flags]`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… issues - Extract 6 regexp patterns to package-level vars (avoid recompilation) - Add shortSHA helper to unify hash truncation with length guard - Remove unused flagPRRemote flag - Use local variable for target branch instead of mutating global - Deduplicate cherry-pick cleanup with closure - Fix jiraTransitions error handling for consistency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nches Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…fix-version inference - standardizeTitle now extracts [COMPONENT] brackets (e.g. [SPARK], [FLINK]) and assembles them after JIRA refs, matching Python's standardize_jira_ref - Add inferFixVersions to auto-map release branches to JIRA versions (e.g. branch-0.12 → smallest 0.12.x unreleased version) - Remove redundant X.Y.0 when X.(Y-1).0 is also present Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ase branch versions - --fix-versions and release branch inference now work together - e.g. --fix-versions 0.13.0 --release-branches branch-0.12 → 0.13.0, 0.12.1 - Master auto-inference only runs when --fix-versions is not specified Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ept header - dry-run now shows the effective command with all inferred values resolved - Fix HTTP Accept header: use application/json instead of GitHub-specific header that caused JIRA API 406 errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allows merging PRs with natural language input: /merge-pr 5167 fix-versions 0.13.0, cherry-pick into branch-0.12 Always dry-runs first and asks for confirmation before actual merge. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The skill now checks for Go availability and downloads it to .go/ directory if not found, so the merge tool works without pre-installed Go. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After dry-run, ask user if effective command looks correct and allow adjusting fix-versions, release-branches before actual merge. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Posts a comment on the GitHub PR listing which branches were merged into, e.g. "Merged into master (394e245). Cherry-picked into branch-0.12." Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@pan3793 @Reamer @tbonelee @ParkGyeongTae I just want to know your opinion about this kind of way. I added CLI application and Claude command for merging PRs. I believe that we already use AI, and which reduces our efforts for regular jobs like this. Moreover, it's the starting point for the discussion to adopt AI into the project so feel free to leave your opinion. You can use email or slack for discussion as well. 🙏 Ref. |
There was a problem hiding this comment.
Pull request overview
Adds a new single-file Go-based CLI in dev/ to automate merging Apache Zeppelin GitHub pull requests (optionally cherry-picking to release branches and resolving JIRA), targeting non-interactive / CI-friendly usage.
Changes:
- Introduce
dev/merge-pr.goGo CLI that talks to GitHub + JIRA APIs and runs local git commands for cherry-picks. - Add
.claude/commands/merge-pr.mdinstructions for invoking the tool (including an optional local Go bootstrap flow). - Update
.gitignoreto exclude a local.go/Go toolchain directory.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
dev/merge-pr.go |
New Go CLI for merging PRs, cherry-picking to release branches, and resolving JIRA issues. |
.gitignore |
Ignores .go/ directory intended for a locally downloaded Go toolchain. |
.claude/commands/merge-pr.md |
Adds an AI-agent oriented command doc for running the merge CLI (dry-run first, then merge). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
dev/merge-pr.go
Outdated
| type pullRequest struct { | ||
| URL string `json:"url"` | ||
| Title string `json:"title"` | ||
| Body string `json:"body"` | ||
| Mergeable bool `json:"mergeable"` | ||
| Base struct{ Ref string `json:"ref"` } `json:"base"` |
dev/merge-pr.go
Outdated
| func httpDo(method, url string, body interface{}, auth string) ([]byte, int, error) { | ||
| var r io.Reader | ||
| if body != nil { | ||
| b, err := json.Marshal(body) | ||
| if err != nil { | ||
| return nil, 0, err | ||
| } | ||
| r = bytes.NewReader(b) | ||
| } | ||
| req, err := http.NewRequest(method, url, r) | ||
| if err != nil { | ||
| return nil, 0, err | ||
| } | ||
| if auth != "" { | ||
| req.Header.Set("Authorization", auth) | ||
| } | ||
| req.Header.Set("Content-Type", "application/json") | ||
| req.Header.Set("Accept", "application/json") | ||
|
|
||
| resp, err := http.DefaultClient.Do(req) | ||
| if err != nil { | ||
| return nil, 0, err | ||
| } | ||
| defer resp.Body.Close() | ||
| data, err := io.ReadAll(resp.Body) | ||
| return data, resp.StatusCode, err |
dev/merge-pr.go
Outdated
| } | ||
| fmt.Printf("\nPR #%d merged! (hash: %s)\n", flagPR, shortSHA(resp.SHA)) | ||
|
|
||
| gitRun("fetch", flagPushRemote, target) |
.claude/commands/merge-pr.md
Outdated
|
|
||
| 1. Check if `go` is available by running `go version`. If not found: | ||
| - Detect OS and arch (`uname -s`, `uname -m`) | ||
| - Download Go to `.go/` directory: `curl -fsSL https://go.dev/dl/go1.23.6.<os>-<arch>.tar.gz | tar -xz -C .go --strip-components=1` |
.claude/commands/merge-pr.md
Outdated
| - Detect OS and arch (`uname -s`, `uname -m`) | ||
| - Download Go to `.go/` directory: `curl -fsSL https://go.dev/dl/go1.23.6.<os>-<arch>.tar.gz | tar -xz -C .go --strip-components=1` |
dev/merge-pr.go
Outdated
| fmt.Printf("title: %s\n", title) | ||
| fmt.Printf("source: %s\n", src) | ||
| fmt.Printf("target: %s\n", target) | ||
| fmt.Printf("url: %s\n", pr.URL) |
|
Go toolchain installation is another burden ... JEP 330 (Java 11) supports run Java source file directly, if we don't want to introduce additional toolchains, maybe we can write it in Java |
|
@pan3793 Yes, that can also be possible. By the way, how about adding some AI related instructions into the repository? |
|
That’s a fair point about Go toolchain installation being an extra burden. Still, avoiding that does not necessarily remove all setup tradeoffs. If we want to keep conveniences like .env parsing, a small external dependency may still be worth it regardless of the language. |
|
@tbonelee Yes, your point is also good. But we can try to re-write it using java - which is a mandatory for the project. It wasn't unavoidable, but I didn't want to cause confusion to AI. |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single-file Java 11 CLI (java dev/merge-pr.java) that mirrors the Go version. No external dependencies - uses java.net.http.HttpClient and manual JSON parsing. Supports GitHub squash merge, cherry-pick into release branches, JIRA resolution, fix-version inference, PR commenting, and effective command display in dry-run mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delete dev/merge-pr.go - Remove .go/ from .gitignore - Update merge-pr.md skill to reference java dev/merge-pr.java Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert all static fields/methods to instance-based: constructor parses args into instance fields, run() and helpers are instance methods. Only constants, JSON/utility helpers, and main() remain static. Also extract resolveFixVersionNames() and commentMergeSummary() for readability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@jongyoul, for the AI part, I saw some ASF projects start to add BTW, the Apache Iceberg dev mailing list is discussing the AI policy seriously, this should be a good reference for us. |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dev/merge-pr.java
Outdated
| return http.send(builder.build(), HttpResponse.BodyHandlers.ofString()); | ||
| } | ||
|
|
||
| // ── Simple JSON parser (no external deps) ────────────────────────────── |
There was a problem hiding this comment.
It's a pity that Java does not have built-in JSON support. I re-read your motivation, it seems the concerns are about venv and 3rd deps installation, can we modify the Python script to remove that? e.g.,
- replace
jira.clientusage with HTTP call - process
.envonly whenpython-dotenvis installed.
There was a problem hiding this comment.
Oh, yes, it looks reasonable. My point were actually two parts.
- 3rd party dependencies as you mentioned
- Interactive wasn't helpful to AI
Moreover, concerning about JSON, I totally agree with you. Python is a better choice.
@pan3793 I read the generative-tooling part on the ASF legal, and there's no limitation but I also don't think it's good to stick on some specific AI. Let me remove .claude including commands, and I will try to write some draft AGENTS.md in another PR. |
Replace dev/merge-pr.java with dev/merge_pr.py using only Python 3 built-in libraries (urllib, json, subprocess, argparse, re). Remove .claude/commands/merge-pr.md from git tracking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… constants - Resolve fix versions once and pass to both dry-run and JIRA resolution - Unify _resolve_fix_version_names and _do_resolve_jira fix-version logic into single _resolve_fix_versions method - Extract constants: DEFAULT_BRANCH, DEFAULT_REMOTE, JIRA_RESOLVE_TRANSITION, JIRA_CLOSED_STATUSES - Use try/finally in cherry-pick loop to guarantee HEAD restoration - Extract _pick_branch_name helper - Rename _http param body→payload to avoid shadowing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
dev/merge_zeppelin_pr.pyas a single-file Go CLI (dev/merge-pr.go)flagpackage)go run dev/merge-pr.go --pr <number> [flags](nogo.modneeded)Motivation
The existing Python merge script (
dev/merge_zeppelin_pr.py) requires a Python virtual environment with dependencies (jira,python-dotenv), and is interactive (prompts for user input). This makes it difficult to use from CI/CD or AI agent workflows.The Go version:
Usage
Flags
--pr--target--fix-versions--release-branches--resolve-jira--dry-run--push-remoteapache)--github-tokenGITHUB_OAUTH_KEY)--jira-tokenJIRA_ACCESS_TOKEN)Test Plan
go run dev/merge-pr.go --help— shows usagego run dev/merge-pr.go --pr 5167 --dry-run— fetches PR info correctly