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.

Revert "Simplify `contrib/backport` (#27520)" (#27566)

This reverts #27520 commit 79e8865aaed43de81816390ee616263bb2bee67f
which breaks `--continue` functionality.

authored by

zeripath and committed by
GitHub
4378f9df 248b7ee8

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