loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Simplify `contrib/backport` (#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 08efeb5cdc22d21b5ef12cc540727594a22062d1
* Amending commit to prepend `Backport #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>

authored by

silverwind
delvh
and committed by
GitHub
79e8865a e94a4ad2

+12 -225
+12 -225
contrib/backport/backport.go
··· 7 7 import ( 8 8 "context" 9 9 "fmt" 10 - "log" 11 10 "net/http" 12 11 "os" 13 12 "os/exec" ··· 19 18 20 19 "github.com/google/go-github/v53/github" 21 20 "github.com/urfave/cli/v2" 22 - "gopkg.in/yaml.v3" 23 21 ) 24 - 25 - const defaultVersion = "v1.18" // to backport to 26 22 27 23 func main() { 28 24 app := cli.NewApp() ··· 54 50 Name: "backport-branch", 55 51 Usage: "Backport branch to backport on to (default: backport-<pr>-<version>", 56 52 }, 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 - }, 67 53 &cli.BoolFlag{ 68 54 Name: "no-fetch", 69 55 Usage: "Set this flag to prevent fetch of remote branches", ··· 72 58 Name: "no-amend-message", 73 59 Usage: "Set this flag to prevent automatic amendment of the commit message", 74 60 }, 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 - }, 87 61 } 88 62 cli.AppHelpTemplate = `NAME: 89 63 {{.Name}} - {{.Usage}} ··· 101 75 app.Action = runBackport 102 76 103 77 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) 105 79 } 106 80 } 107 81 ··· 109 83 ctx, cancel := installSignals() 110 84 defer cancel() 111 85 112 - continuing := c.Bool("continue") 113 - 114 - var pr string 115 - 116 86 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 - } 125 87 if version == "" { 126 - version = readVersion() 127 - } 128 - if version == "" { 129 - version = defaultVersion 88 + return fmt.Errorf("Provide a version to backport to") 130 89 } 131 90 132 91 upstream := c.String("upstream") ··· 134 93 upstream = "origin" 135 94 } 136 95 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 - 147 96 upstreamReleaseBranch := c.String("release-branch") 148 97 if upstreamReleaseBranch == "" { 149 98 upstreamReleaseBranch = path.Join("release", version) ··· 152 101 localReleaseBranch := path.Join(upstream, upstreamReleaseBranch) 153 102 154 103 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) 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") 159 108 } 160 - if pr == "" { 161 - pr = args[0] 162 - } 109 + pr := args[0] 163 110 164 111 backportBranch := c.String("backport-branch") 165 112 if backportBranch == "" { ··· 186 133 } 187 134 } 188 135 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 193 138 } 194 139 195 140 if err := cherrypick(ctx, sha); err != nil { ··· 202 147 } 203 148 } 204 149 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 - } 150 + fmt.Printf("Backport done! You can now push it with `git push <your remote> %s`\n", backportBranch) 211 151 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 - } 232 - 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 - } 240 152 return nil 241 153 } 242 154 ··· 267 179 } 268 180 269 181 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 - 282 182 fmt.Printf("* Attempting git cherry-pick %s\n", sha) 283 183 out, err := exec.CommandContext(ctx, "git", "cherry-pick", sha).Output() 284 184 if err != nil { ··· 289 189 } 290 190 291 191 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() 308 194 309 195 fmt.Printf("* `git checkout -b %s %s`\n", backportBranch, releaseBranch) 310 196 return exec.CommandContext(ctx, "git", "checkout", "-b", backportBranch, releaseBranch).Run() ··· 317 203 fmt.Println(string(out)) 318 204 return fmt.Errorf("unable to fetch %s from %s: %w", "main", remote, err) 319 205 } 320 - fmt.Println(string(out)) 321 206 322 207 fmt.Printf("* `git fetch %s %s`\n", remote, releaseBranch) 323 208 out, err = exec.CommandContext(ctx, "git", "fetch", remote, releaseBranch).Output() ··· 325 210 fmt.Println(string(out)) 326 211 return fmt.Errorf("unable to fetch %s from %s: %w", releaseBranch, remote, err) 327 212 } 328 - fmt.Println(string(out)) 329 213 330 214 return nil 331 - } 332 - 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://www.github.com/") { 363 - forkUser = strings.TrimPrefix(remote, "https://www.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 215 } 429 216 430 217 func determineSHAforPR(ctx context.Context, prStr string) (string, error) {