git-ea/cmd/backport.go

108 lines
2.8 KiB
Go

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")
pushFlag := fs.Bool("push", false, "Push immediately")
fs.BoolVar(pushFlag, "p", *pushFlag, "--push")
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
}
if err := run(ctx, h.Config.WorkspaceBranch(branch), "git", "cherry-pick", optMap[resp]); err != nil {
return err
}
if *pushFlag {
return run(ctx, h.Config.WorkspaceBranch(branch), "git", "push", "origin", "HEAD")
}
return nil
},
}
}