Skip to content

Commit 79e8865

Browse files
silverwinddelvh
andauthored
Simplify contrib/backport (go-gitea#27520)
This script was trying to be too smart, make it more straightforward and less error-prone so that i could be used by the backport bot too ideally. - Always delete the backport branch so that script is idempotent in this regard - Remove the push functionality, it's best done by the user because only they know the remote name - Remove reading docs/config.yaml, it no longer exists - Remove version detection, version is now a required argument - Remove opening the pull request with xdg-open, xdg-open is not portable - Remove continue from failed cherry pick. It's best to reset manually in this case - Clean up the console logging Example run: ``` $ go run ./contrib/backport --version v1.21 27503 * Backporting 27503 to origin/release/v1.21 as backport-27503-v1.21 * `git fetch origin main` * `git fetch origin release/v1.21` * `git branch -D backport-27503-v1.21` * `git checkout -b backport-27503-v1.21 origin/release/v1.21` * Attempting git cherry-pick 08efeb5 * Amending commit to prepend `Backport go-gitea#27503` to body Backport done! You can now push it with `git push yourremote backport-27503-v1.21` ``` --------- Co-authored-by: delvh <dev.lh@web.de>
1 parent e94a4ad commit 79e8865

File tree

1 file changed

+12
-225
lines changed

1 file changed

+12
-225
lines changed

contrib/backport/backport.go

+12-225
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package main
77
import (
88
"context"
99
"fmt"
10-
"log"
1110
"net/http"
1211
"os"
1312
"os/exec"
@@ -19,11 +18,8 @@ import (
1918

2019
"github.com/google/go-github/v53/github"
2120
"github.com/urfave/cli/v2"
22-
"gopkg.in/yaml.v3"
2321
)
2422

25-
const defaultVersion = "v1.18" // to backport to
26-
2723
func main() {
2824
app := cli.NewApp()
2925
app.Name = "backport"
@@ -54,16 +50,6 @@ func main() {
5450
Name: "backport-branch",
5551
Usage: "Backport branch to backport on to (default: backport-<pr>-<version>",
5652
},
57-
&cli.StringFlag{
58-
Name: "remote",
59-
Value: "",
60-
Usage: "Remote for your fork of the Gitea upstream",
61-
},
62-
&cli.StringFlag{
63-
Name: "fork-user",
64-
Value: "",
65-
Usage: "Forked user name on Github",
66-
},
6753
&cli.BoolFlag{
6854
Name: "no-fetch",
6955
Usage: "Set this flag to prevent fetch of remote branches",
@@ -72,18 +58,6 @@ func main() {
7258
Name: "no-amend-message",
7359
Usage: "Set this flag to prevent automatic amendment of the commit message",
7460
},
75-
&cli.BoolFlag{
76-
Name: "no-push",
77-
Usage: "Set this flag to prevent pushing the backport up to your fork",
78-
},
79-
&cli.BoolFlag{
80-
Name: "no-xdg-open",
81-
Usage: "Set this flag to not use xdg-open to open the PR URL",
82-
},
83-
&cli.BoolFlag{
84-
Name: "continue",
85-
Usage: "Set this flag to continue from a git cherry-pick that has broken",
86-
},
8761
}
8862
cli.AppHelpTemplate = `NAME:
8963
{{.Name}} - {{.Usage}}
@@ -101,49 +75,24 @@ OPTIONS:
10175
app.Action = runBackport
10276

10377
if err := app.Run(os.Args); err != nil {
104-
fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err)
78+
fmt.Fprintf(os.Stderr, "%v\n", err)
10579
}
10680
}
10781

10882
func runBackport(c *cli.Context) error {
10983
ctx, cancel := installSignals()
11084
defer cancel()
11185

112-
continuing := c.Bool("continue")
113-
114-
var pr string
115-
11686
version := c.String("version")
117-
if version == "" && continuing {
118-
// determine version from current branch name
119-
var err error
120-
pr, version, err = readCurrentBranch(ctx)
121-
if err != nil {
122-
return err
123-
}
124-
}
12587
if version == "" {
126-
version = readVersion()
127-
}
128-
if version == "" {
129-
version = defaultVersion
88+
return fmt.Errorf("Provide a version to backport to")
13089
}
13190

13291
upstream := c.String("upstream")
13392
if upstream == "" {
13493
upstream = "origin"
13594
}
13695

137-
forkUser := c.String("fork-user")
138-
remote := c.String("remote")
139-
if remote == "" && !c.Bool("--no-push") {
140-
var err error
141-
remote, forkUser, err = determineRemote(ctx, forkUser)
142-
if err != nil {
143-
return err
144-
}
145-
}
146-
14796
upstreamReleaseBranch := c.String("release-branch")
14897
if upstreamReleaseBranch == "" {
14998
upstreamReleaseBranch = path.Join("release", version)
@@ -152,14 +101,12 @@ func runBackport(c *cli.Context) error {
152101
localReleaseBranch := path.Join(upstream, upstreamReleaseBranch)
153102

154103
args := c.Args().Slice()
155-
if len(args) == 0 && pr == "" {
156-
return fmt.Errorf("no PR number provided\nProvide a PR number to backport")
157-
} else if len(args) != 1 && pr == "" {
158-
return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args)
159-
}
160-
if pr == "" {
161-
pr = args[0]
104+
if len(args) == 0 {
105+
return fmt.Errorf("Provide a PR number to backport")
106+
} else if len(args) != 1 {
107+
return fmt.Errorf("Only a single PR can be backported at a time")
162108
}
109+
pr := args[0]
163110

164111
backportBranch := c.String("backport-branch")
165112
if backportBranch == "" {
@@ -186,10 +133,8 @@ func runBackport(c *cli.Context) error {
186133
}
187134
}
188135

189-
if !continuing {
190-
if err := checkoutBackportBranch(ctx, backportBranch, localReleaseBranch); err != nil {
191-
return err
192-
}
136+
if err := checkoutBackportBranch(ctx, backportBranch, localReleaseBranch); err != nil {
137+
return err
193138
}
194139

195140
if err := cherrypick(ctx, sha); err != nil {
@@ -202,41 +147,8 @@ func runBackport(c *cli.Context) error {
202147
}
203148
}
204149

205-
if !c.Bool("no-push") {
206-
url := "https://github.com/go-gitea/gitea/compare/" + upstreamReleaseBranch + "..." + forkUser + ":" + backportBranch
207-
208-
if err := gitPushUp(ctx, remote, backportBranch); err != nil {
209-
return err
210-
}
211-
212-
if !c.Bool("no-xdg-open") {
213-
if err := xdgOpen(ctx, url); err != nil {
214-
return err
215-
}
216-
} else {
217-
fmt.Printf("* Navigate to %s to open PR\n", url)
218-
}
219-
}
220-
return nil
221-
}
222-
223-
func xdgOpen(ctx context.Context, url string) error {
224-
fmt.Printf("* `xdg-open %s`\n", url)
225-
out, err := exec.CommandContext(ctx, "xdg-open", url).Output()
226-
if err != nil {
227-
fmt.Fprintf(os.Stderr, "%s", string(out))
228-
return fmt.Errorf("unable to xdg-open to %s: %w", url, err)
229-
}
230-
return nil
231-
}
150+
fmt.Printf("Backport done! You can now push it with `git push <your remote> %s`\n", backportBranch)
232151

233-
func gitPushUp(ctx context.Context, remote, backportBranch string) error {
234-
fmt.Printf("* `git push -u %s %s`\n", remote, backportBranch)
235-
out, err := exec.CommandContext(ctx, "git", "push", "-u", remote, backportBranch).Output()
236-
if err != nil {
237-
fmt.Fprintf(os.Stderr, "%s", string(out))
238-
return fmt.Errorf("unable to push up to %s: %w", remote, err)
239-
}
240152
return nil
241153
}
242154

@@ -267,18 +179,6 @@ func amendCommit(ctx context.Context, pr string) error {
267179
}
268180

269181
func cherrypick(ctx context.Context, sha string) error {
270-
// Check if a CHERRY_PICK_HEAD exists
271-
if _, err := os.Stat(".git/CHERRY_PICK_HEAD"); err == nil {
272-
// Assume that we are in the middle of cherry-pick - continue it
273-
fmt.Println("* Attempting git cherry-pick --continue")
274-
out, err := exec.CommandContext(ctx, "git", "cherry-pick", "--continue").Output()
275-
if err != nil {
276-
fmt.Fprintf(os.Stderr, "git cherry-pick --continue failed:\n%s\n", string(out))
277-
return fmt.Errorf("unable to continue cherry-pick: %w", err)
278-
}
279-
return nil
280-
}
281-
282182
fmt.Printf("* Attempting git cherry-pick %s\n", sha)
283183
out, err := exec.CommandContext(ctx, "git", "cherry-pick", sha).Output()
284184
if err != nil {
@@ -289,22 +189,8 @@ func cherrypick(ctx context.Context, sha string) error {
289189
}
290190

291191
func checkoutBackportBranch(ctx context.Context, backportBranch, releaseBranch string) error {
292-
out, err := exec.CommandContext(ctx, "git", "branch", "--show-current").Output()
293-
if err != nil {
294-
return fmt.Errorf("unable to check current branch %w", err)
295-
}
296-
297-
currentBranch := strings.TrimSpace(string(out))
298-
fmt.Printf("* Current branch is %s\n", currentBranch)
299-
if currentBranch == backportBranch {
300-
fmt.Printf("* Current branch is %s - not checking out\n", currentBranch)
301-
return nil
302-
}
303-
304-
if _, err := exec.CommandContext(ctx, "git", "rev-list", "-1", backportBranch).Output(); err == nil {
305-
fmt.Printf("* Branch %s already exists. Checking it out...\n", backportBranch)
306-
return exec.CommandContext(ctx, "git", "checkout", "-f", backportBranch).Run()
307-
}
192+
fmt.Printf("* `git branch -D %s`\n", backportBranch)
193+
_ = exec.CommandContext(ctx, "git", "branch", "-D", backportBranch).Run()
308194

309195
fmt.Printf("* `git checkout -b %s %s`\n", backportBranch, releaseBranch)
310196
return exec.CommandContext(ctx, "git", "checkout", "-b", backportBranch, releaseBranch).Run()
@@ -317,116 +203,17 @@ func fetchRemoteAndMain(ctx context.Context, remote, releaseBranch string) error
317203
fmt.Println(string(out))
318204
return fmt.Errorf("unable to fetch %s from %s: %w", "main", remote, err)
319205
}
320-
fmt.Println(string(out))
321206

322207
fmt.Printf("* `git fetch %s %s`\n", remote, releaseBranch)
323208
out, err = exec.CommandContext(ctx, "git", "fetch", remote, releaseBranch).Output()
324209
if err != nil {
325210
fmt.Println(string(out))
326211
return fmt.Errorf("unable to fetch %s from %s: %w", releaseBranch, remote, err)
327212
}
328-
fmt.Println(string(out))
329213

330214
return nil
331215
}
332216

333-
func determineRemote(ctx context.Context, forkUser string) (string, string, error) {
334-
out, err := exec.CommandContext(ctx, "git", "remote", "-v").Output()
335-
if err != nil {
336-
fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out))
337-
return "", "", fmt.Errorf("unable to determine forked remote: %w", err)
338-
}
339-
lines := strings.Split(string(out), "\n")
340-
for _, line := range lines {
341-
fields := strings.Split(line, "\t")
342-
name, remote := fields[0], fields[1]
343-
// only look at pushers
344-
if !strings.HasSuffix(remote, " (push)") {
345-
continue
346-
}
347-
// only look at github.com pushes
348-
if !strings.Contains(remote, "github.com") {
349-
continue
350-
}
351-
// ignore go-gitea/gitea
352-
if strings.Contains(remote, "go-gitea/gitea") {
353-
continue
354-
}
355-
if !strings.Contains(remote, forkUser) {
356-
continue
357-
}
358-
if strings.HasPrefix(remote, "git@github.com:") {
359-
forkUser = strings.TrimPrefix(remote, "git@github.com:")
360-
} else if strings.HasPrefix(remote, "https://github.com/") {
361-
forkUser = strings.TrimPrefix(remote, "https://github.com/")
362-
} else if strings.HasPrefix(remote, "https://github.com/") {
363-
forkUser = strings.TrimPrefix(remote, "https://github.com/")
364-
} else if forkUser == "" {
365-
return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote)
366-
}
367-
idx := strings.Index(forkUser, "/")
368-
if idx >= 0 {
369-
forkUser = forkUser[:idx]
370-
}
371-
return name, forkUser, nil
372-
}
373-
return "", "", fmt.Errorf("unable to find appropriate remote in:\n%s", string(out))
374-
}
375-
376-
func readCurrentBranch(ctx context.Context) (pr, version string, err error) {
377-
out, err := exec.CommandContext(ctx, "git", "branch", "--show-current").Output()
378-
if err != nil {
379-
fmt.Fprintf(os.Stderr, "Unable to read current git branch:\n%s\n", string(out))
380-
return "", "", fmt.Errorf("unable to read current git branch: %w", err)
381-
}
382-
parts := strings.Split(strings.TrimSpace(string(out)), "-")
383-
384-
if len(parts) != 3 || parts[0] != "backport" {
385-
fmt.Fprintf(os.Stderr, "Unable to continue from git branch:\n%s\n", string(out))
386-
return "", "", fmt.Errorf("unable to continue from git branch:\n%s", string(out))
387-
}
388-
389-
return parts[1], parts[2], nil
390-
}
391-
392-
func readVersion() string {
393-
bs, err := os.ReadFile("docs/config.yaml")
394-
if err != nil {
395-
if err == os.ErrNotExist {
396-
log.Println("`docs/config.yaml` not present")
397-
return ""
398-
}
399-
fmt.Fprintf(os.Stderr, "Unable to read `docs/config.yaml`: %v\n", err)
400-
return ""
401-
}
402-
403-
type params struct {
404-
Version string
405-
}
406-
type docConfig struct {
407-
Params params
408-
}
409-
dc := &docConfig{}
410-
if err := yaml.Unmarshal(bs, dc); err != nil {
411-
fmt.Fprintf(os.Stderr, "Unable to read `docs/config.yaml`: %v\n", err)
412-
return ""
413-
}
414-
415-
if dc.Params.Version == "" {
416-
fmt.Fprintf(os.Stderr, "No version in `docs/config.yaml`")
417-
return ""
418-
}
419-
420-
version := dc.Params.Version
421-
if version[0] != 'v' {
422-
version = "v" + version
423-
}
424-
425-
split := strings.SplitN(version, ".", 3)
426-
427-
return strings.Join(split[:2], ".")
428-
}
429-
430217
func determineSHAforPR(ctx context.Context, prStr string) (string, error) {
431218
prNum, err := strconv.Atoi(prStr)
432219
if err != nil {

0 commit comments

Comments
 (0)