153 lines
3.2 KiB
Go
153 lines
3.2 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"go.jolheiser.com/git-ea/config"
|
|
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/go-git/go-git/v5/plumbing"
|
|
"github.com/peterbourgon/ff/v3/ffcli"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var Version = "x.y.z"
|
|
|
|
type Handler struct {
|
|
Config *config.Config
|
|
}
|
|
|
|
func New() (*ffcli.Command, error) {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
handler := Handler{
|
|
Config: cfg,
|
|
}
|
|
|
|
fs := flag.NewFlagSet("git-ea", flag.ContinueOnError)
|
|
versionFlag := fs.Bool("version", false, "Print git-ea version")
|
|
fs.BoolVar(versionFlag, "v", *versionFlag, "--version")
|
|
|
|
return &ffcli.Command{
|
|
Name: "git-ea",
|
|
FlagSet: fs,
|
|
ShortUsage: "git-ea <cmd>",
|
|
ShortHelp: "git-ea is the base command",
|
|
Subcommands: []*ffcli.Command{
|
|
handler.Cleanup(),
|
|
handler.Backport(),
|
|
handler.Branch(),
|
|
handler.Frontport(),
|
|
handler.Init(),
|
|
},
|
|
Exec: func(_ context.Context, _ []string) error {
|
|
if *versionFlag {
|
|
log.Info().Msgf("git-ea v%s", Version)
|
|
return nil
|
|
}
|
|
|
|
dir := cfg.Base
|
|
if fs.NArg() > 0 {
|
|
dir = cfg.WorkspaceBranch(fs.Arg(0))
|
|
}
|
|
|
|
fmt.Println(dir)
|
|
return nil
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (h *Handler) checkInit() error {
|
|
if !h.Config.IsInit() {
|
|
return errors.New("git-ea must be initialized first with `git ea init` in an appropriate directory")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *Handler) run(ctx context.Context, cmd string, args ...string) error {
|
|
return run(ctx, h.Config.Base, cmd, args...)
|
|
}
|
|
|
|
func run(ctx context.Context, dir, cmd string, args ...string) error {
|
|
c := exec.CommandContext(ctx, cmd, args...)
|
|
c.Dir = dir
|
|
c.Stdout = os.Stdout
|
|
c.Stderr = os.Stderr
|
|
c.Stdin = os.Stdin
|
|
return c.Run()
|
|
}
|
|
|
|
func (h *Handler) fetch(ctx context.Context) {
|
|
if err := run(ctx, h.Config.Base, "git", "fetch", "upstream"); err != nil {
|
|
log.Err(err).Msg("")
|
|
}
|
|
}
|
|
|
|
func isClean() bool {
|
|
c := exec.Command("git", "status", "--porcelain")
|
|
o, err := c.Output()
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("could not get git status")
|
|
}
|
|
return len(o) == 0
|
|
}
|
|
|
|
func (h *Handler) repo() *git.Repository {
|
|
repo, err := git.PlainOpen(h.Config.Base)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("cannot open git repository")
|
|
}
|
|
return repo
|
|
}
|
|
|
|
func (h *Handler) worktree() *git.Worktree {
|
|
tree, err := h.repo().Worktree()
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("cannot get git worktree")
|
|
}
|
|
return tree
|
|
}
|
|
|
|
func (h *Handler) head(name string) plumbing.Hash {
|
|
ref, err := h.repo().Reference(plumbing.NewRemoteReferenceName("upstream", name), false)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msgf("cannot get remote upstream %s HEAD", name)
|
|
}
|
|
return ref.Hash()
|
|
}
|
|
|
|
var releaseRe = regexp.MustCompile(`release/v1\.(\d+)`)
|
|
|
|
func (h *Handler) latestRelease() string {
|
|
cmd := exec.Command("git", "ls-remote", "upstream", "release/*")
|
|
cmd.Dir = h.Config.Base
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("could not get latest release")
|
|
}
|
|
matches := releaseRe.FindAllStringSubmatch(string(out), -1)
|
|
|
|
var latest string
|
|
for _, match := range matches {
|
|
m := match[1]
|
|
if len(m) < 3 {
|
|
m = strings.Repeat("0", 3-len(m)) + m
|
|
}
|
|
if m > latest {
|
|
latest = m
|
|
}
|
|
}
|
|
|
|
return strings.TrimLeft(latest, "0")
|
|
}
|