package cmd import ( "context" "flag" "fmt" "regexp" "strings" "github.com/AlecAivazis/survey/v2" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" "github.com/peterbourgon/ff/v3/ffcli" "github.com/skratchdot/open-golang/open" ) var indexRe = regexp.MustCompile(`\(#(\d+)\)`) func (h *Handler) Backport() *ffcli.Command { fs := flag.NewFlagSet("backport", flag.ContinueOnError) fromFlag := fs.String("from", "", "Release to backport from (ex: `main`, default: main)") fs.StringVar(fromFlag, "f", *fromFlag, "--from") toFlag := fs.String("to", "", "Release to backport to (ex: `17`, default: `latest`)") fs.StringVar(toFlag, "t", *toFlag, "--to") listFlag := fs.Bool("list", false, "Open repository to see needed backports") fs.BoolVar(listFlag, "l", *listFlag, "--list") return &ffcli.Command{ Name: "backport", FlagSet: fs, ShortUsage: "backport --from [release=main] --to [release=latest]", ShortHelp: "backport cherry-picks a commit and applies it to a clean branch based on `release`", Exec: func(ctx context.Context, _ []string) error { if err := h.checkInit(); err != nil { return err } to := *toFlag if to == "" { to = h.latestRelease() } if *listFlag { if err := open.Run(fmt.Sprintf("https://github.com/go-gitea/gitea/pulls?q=is%%3Apr+-label%%3Abackport%%2Fdone+label%%3Abackport%%2Fv1.%s+is%%3Amerged", to)); err != nil { return err } return nil } h.fetch(ctx) from := *fromFlag if from == "" { from = "main" } commits, err := h.repo().Log(&git.LogOptions{ From: h.head(from), }) if err != nil { return err } optMap := make(map[string]string) var opts []string if err := commits.ForEach(func(c *object.Commit) error { title := strings.Split(c.Message, "\n")[0] opts = append(opts, title) optMap[title] = c.Hash.String() return nil }); err != nil { return err } var resp string if err := survey.AskOne(&survey.Select{ Message: "Commit to cherry-pick", Options: opts, }, &resp, survey.WithValidator(survey.Required)); err != nil { return err } index := optMap[resp] m := indexRe.FindStringSubmatch(resp) if m != nil { index = m[1] } branch := fmt.Sprintf("backport-%s-%s", to, index) base := fmt.Sprintf("upstream/release/v1.%s", to) if err := h.Branch().ParseAndRun(ctx, []string{"--base", base, branch}); err != nil { return err } return run(ctx, h.Config.WorkspaceBranch(branch), "git", "cherry-pick", optMap[resp]) }, } }