package main import ( "fmt" "log" "os" "sort" "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" "github.com/spf13/cobra" flags "github.com/spf13/pflag" "github.com/Masterminds/semver/v3" ) var ( clonedRepo = &git.Repository{} localRepo = &git.Repository{} repoDir string rootCmd = &cobra.Command{ Use: "semverbump part [major|minor|patch]", Short: "A tool for bumping semver git tags.", ValidArgs: []string{"major", "minor", "patch"}, Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), Long: `Inspired by the python bumpversion tool.`, RunE: func(cmd *cobra.Command, args []string) error { part := args[0] localRepo, err := git.PlainOpen(repoDir) if err != nil { return err } remoteURL := getRemoteURL(localRepo) fs := memfs.New() storer := memory.NewStorage() remoteRepo, err := git.Clone(storer, fs, &git.CloneOptions{ URL: remoteURL, }) if err != nil { log.Fatal(err) } tags := findTags(remoteRepo) latestVersion := latestTag(tags) if latestVersion == nil { fmt.Println("No tags found. Not doing anything") return nil } nextVersion := semver.Version{} switch part { case "major": nextVersion = latestVersion.IncMajor() case "minor": nextVersion = latestVersion.IncMinor() case "patch": nextVersion = latestVersion.IncPatch() } fmt.Println(nextVersion) return err }, } ) // TODO this only works for a single remote func getRemoteURL(repo *git.Repository) string { _, err := repo.Config() if err != nil { log.Fatal(err) } remotes, err := repo.Remotes() if err != nil { log.Fatalf("error getting remotes %s", err) } if len(remotes) == 0 { return "" } primaryConfig := remotes[0].Config() return primaryConfig.URLs[0] } func latestTag(tags []*semver.Version) *semver.Version { numTags := len(tags) if numTags == 0 { return nil } return tags[numTags-1] } func findTags(repo *git.Repository) []*semver.Version { repoTagsIter, err := repo.Tags() if err != nil { log.Fatal(err) } repoTags := []*semver.Version{} if err := repoTagsIter.ForEach(func(ref *plumbing.Reference) error { if err != nil { log.Fatal(err) } rn := plumbing.ReferenceName(ref.Name()) version, err := semver.NewVersion(rn.Short()) if err != nil { // not a semver return nil } repoTags = append(repoTags, version) return nil }); err != nil { // log.error fmt.Printf("Iteration Error %s", err) } sort.Sort(semver.Collection(repoTags)) return repoTags } func main() { // Clone the given repository to the given directory //repo, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{ // URL: "https://github.com/grumps/selector", // Progress: os.Stdout, //}) //if err != nil { // log.Fatal(err) //} if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func init() { // global flags cwd, err := os.Getwd() if err != nil { log.Fatalf("couldn't get cwd %s", err) } repoDir = *flags.String("repoDir", cwd, "repo to examine") //pflag }