diff options
| -rw-r--r-- | go.mod | 9 | ||||
| -rw-r--r-- | go.sum | 2 | ||||
| -rw-r--r-- | main.go | 90 | ||||
| -rw-r--r-- | main_test.go | 203 |
4 files changed, 234 insertions, 70 deletions
@@ -3,19 +3,20 @@ module git.ofmax.li/go-bumpver go 1.20 require ( - github.com/coreos/go-semver v0.3.1 + github.com/Masterminds/semver/v3 v3.2.1 + github.com/go-git/go-billy/v5 v5.4.1 github.com/go-git/go-git/v5 v5.7.0 + github.com/spf13/cobra v1.7.0 + github.com/spf13/pflag v1.0.5 ) require ( - github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -24,8 +25,6 @@ require ( github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/skeema/knownhosts v1.1.1 // indirect - github.com/spf13/cobra v1.7.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect golang.org/x/crypto v0.9.0 // indirect golang.org/x/net v0.10.0 // indirect @@ -12,8 +12,6 @@ github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= -github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -1,15 +1,14 @@ package main import ( + "errors" "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" @@ -18,10 +17,12 @@ import ( ) var ( - clonedRepo = &git.Repository{} - localRepo = &git.Repository{} - repoDir string - rootCmd = &cobra.Command{ + errNoRemoteFound = errors.New("No remotes found") + clonedRepo = &git.Repository{} + localRepo = &git.Repository{} + repoDir string + remoteName string + rootCmd = &cobra.Command{ Use: "semverbump part [major|minor|patch]", Short: "A tool for bumping semver git tags.", ValidArgs: []string{"major", "minor", "patch"}, @@ -33,74 +34,47 @@ var ( if err != nil { return err } - - remoteURL := getRemoteURL(localRepo) - fs := memfs.New() - storer := memory.NewStorage() - remoteRepo, err := git.Clone(storer, fs, &git.CloneOptions{ - URL: remoteURL, - }) + // finding tags and last version + tags, err := findTags(localRepo) if err != nil { - log.Fatal(err) + return err } - - tags := findTags(remoteRepo) - latestVersion := latestTag(tags) - if latestVersion == nil { - fmt.Println("No tags found. Not doing anything") - return nil + lastVersion := latestTag(tags) + if (lastVersion == &semver.Version{}) { + return errors.New("No tags found. Not doing anything") } + + // actual bump nextVersion := semver.Version{} switch part { case "major": - nextVersion = latestVersion.IncMajor() + nextVersion = lastVersion.IncMajor() case "minor": - nextVersion = latestVersion.IncMinor() + nextVersion = lastVersion.IncMinor() case "patch": - nextVersion = latestVersion.IncPatch() + nextVersion = lastVersion.IncPatch() } - fmt.Println(nextVersion) + fmt.Fprint(cmd.OutOrStdout(), 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 &semver.Version{} } return tags[numTags-1] } -func findTags(repo *git.Repository) []*semver.Version { +func findTags(repo *git.Repository) ([]*semver.Version, error) { repoTagsIter, err := repo.Tags() if err != nil { - log.Fatal(err) + return []*semver.Version{}, 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 { @@ -110,23 +84,13 @@ func findTags(repo *git.Repository) []*semver.Version { repoTags = append(repoTags, version) return nil }); err != nil { - // log.error - fmt.Printf("Iteration Error %s", err) + return []*semver.Version{}, err } sort.Sort(semver.Collection(repoTags)) - return repoTags + return repoTags, nil } 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) @@ -137,8 +101,8 @@ func init() { // global flags cwd, err := os.Getwd() if err != nil { - log.Fatalf("couldn't get cwd %s", err) + log.Fatal(err) } - repoDir = *flags.String("repoDir", cwd, "repo to examine") - //pflag + repoDir = *flags.String("repo-dir", cwd, "repo to examine") + remoteName = *flags.String("remote-name", "origin", "remote to search and push to") } diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..51285fc --- /dev/null +++ b/main_test.go @@ -0,0 +1,203 @@ +package main + +import ( + "bytes" + "log" + "testing" + + "github.com/Masterminds/semver/v3" + "github.com/go-git/go-billy/v5/osfs" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/storage/filesystem" +) + +var ( + tags = []string{"0.1.1", "1.2.3", "3.1.1", "0.3.0", "4.1.1", "0.8.1", "1.1.1-PR.44.4"} +) + +func newRepo(tempDir string) *git.Repository { + fs := osfs.New(tempDir) + strg := filesystem.NewStorage(fs, nil) + r, err := git.Init(strg, fs) + if err != nil { + log.Fatal(err) + } + return r +} + +func makeCommit(wt *git.Worktree) (plumbing.Hash, error) { + file, err := wt.Filesystem.Create("thisismyfile") + if err != nil { + return plumbing.Hash{}, err + } + file.Write([]byte("this is my content")) + wt.Add("thisismyfile") + hash, err := wt.Commit("this is a commit", &git.CommitOptions{}) + if err != nil { + return plumbing.Hash{}, err + } + return hash, nil + +} +func TestCmd(t *testing.T) { + + tests := []struct { + name string + input string + prior string + output string + isError error + }{ + { + name: "patch bump", + input: "patch", + prior: "1.1.1", + output: "1.1.2", + isError: nil, + }, + { + name: "minor bump", + input: "minor", + prior: "1.1.0", + output: "1.2.0", + isError: nil, + }, + { + name: "major bump", + input: "major", + prior: "1.2.1", + output: "2.0.0", + isError: nil, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + logOut := &bytes.Buffer{} + repoDir = t.TempDir() + testRepo := newRepo(repoDir) + wt, _ := testRepo.Worktree() + hash, err := makeCommit(wt) + if err != nil { + t.Fatal(err) + } + + _, err = testRepo.CreateTag(test.prior, hash, nil) + if err != nil { + t.Fatal(err) + } + + rootCmd.SetArgs([]string{test.input}) + rootCmd.SetOut(logOut) + rootCmd.SetErr(logOut) + result := rootCmd.Execute() + if result != test.isError { + t.Fatal("didn't expect this") + } + out := logOut.String() + t.Logf("stuff %s", out) + if out != test.output { + t.Fatalf("expected %s found %s", test.output, out) + } + }) + } +} + +func TestLatestTags(t *testing.T) { + semverTags := []*semver.Version{} + for _, v := range tags { + version, err := semver.NewVersion(v) + if err != nil { + log.Fatal(err) + } + semverTags = append(semverTags, version) + } + tests := []struct { + name string + inputs []*semver.Version + // * for nil cases + output *semver.Version + }{ + { + name: "returns the last item in slice", + inputs: semverTags, + output: semver.New(1, 1, 1, "PR.44.4", ""), + }, + { + name: "returns nil if no tags are present", + inputs: []*semver.Version{}, + output: &semver.Version{}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := latestTag(test.inputs) + if !test.output.Equal(result) { + t.Fatalf("Expect %s found %s", test.output.String(), result.String()) + } + }) + } +} + +func TestFindingTags(t *testing.T) { + tmpDir := t.TempDir() + r := newRepo(tmpDir) + wt, err := r.Worktree() + if err != nil { + t.Fatal(err) + } + hash, err := makeCommit(wt) + if err != nil { + t.Fatal(err) + } + + results, err := findTags(r) + if err != nil { + t.Fatal(err) + } + + if len(results) != 0 { + t.Fatalf("Expected no tags, found %d", len(results)) + } + + // at least a single tag is found + _, err = r.CreateTag("0.0.1", hash, nil) + if err != nil { + t.Fatal(err) + } + + results1, err := findTags(r) + if err != nil { + t.Fatal(err) + } + + if len(results1) != 1 { + t.Fatalf("Expected no tags, found %d", len(results1)) + } + + // a random tag + _, _ = r.CreateTag("this-is-not-me", hash, nil) + results2, err := findTags(r) + if err != nil { + t.Fatal(err) + } + + if len(results2) != 1 { + t.Fatalf("Expected no tags, found %d", len(results2)) + } + + // all semver tags + for _, tag := range tags { + _, _ = r.CreateTag(tag, hash, nil) + } + results3, err := findTags(r) + if err != nil { + t.Fatal(err) + } + + if len(results3) != 8 { + t.Fatalf("found %d", len(results3)) + } +} |