Skip to content

Commit

Permalink
Add -pr for inspecting GitHub pull requests (#11)
Browse files Browse the repository at this point in the history
* Checkpoint.

* Reorganize main package. Add -pr and -token. Update main package doc comment. Wire up doPR.

* Adjust whitespace in comment template.

* Upgrade go-git dependency.

* Improve (?) isModverComment.

* Oops.

* Take a stab at adding "modver -pr" to the GitHub Actions workflow.

* Wrong version of actions/checkout.

* Disable TestGit for now.

* Some decoupling courtesy of github.com/bobg/decouple.

* Add a test with mocks.

* More test coverage.

* Unskip TestGit.

* Fix (?) broken test.

* If it ain't broke?

* ???

* Grr

* revert revert revert

* Flail flail flail

* Update go-git.

* Skip go-git in GitHub Actions.

* Oops.

* Oh come on.

* Downgrade go-git.

* go mod tidy

* Formatting.

* Add a fetch-master step.

* checkout v3, no -v for go test

* Add a workaround.

* Add a different workaround.

* More test coverage.

* Oops, pass os.Args[1:] to fs.Parse.

* More test coverage.

* More test coverage.

* More test coverage.
  • Loading branch information
bobg committed Mar 29, 2023
1 parent 7e1cd03 commit e849b90
Show file tree
Hide file tree
Showing 17 changed files with 1,066 additions and 367 deletions.
32 changes: 17 additions & 15 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,24 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.18
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.18

- name: Unit tests
run: go test -v -coverprofile=cover.out ./...
- name: Unit tests
run: go test -coverprofile=cover.out ./...

- name: Send coverage
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: cover.out
- name: Modver
if: ${{ github.event_name == 'pull_request' }}
run: go run ./cmd/modver -pr https://github.com/${{ github.repository }}/pull/${{ github.event.number }} -token ${{ github.token }} -pretty

- name: Version string check
run: go run ./cmd/modver -git `pwd`/.git -versions HEAD~1 HEAD
- name: Send coverage
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: cover.out
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
*~
cover.out
modver
/cover.out
/modver
35 changes: 35 additions & 0 deletions cmd/modver/comment.md.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Modver result

This report was generated by [Modver](https://pkg.go.dev/github.com/bobg/modver/v2),
a Go package and command that helps you obey [semantic versioning rules](https://semver.org/) in your Go module.

{{ if eq .Code "Major" }}

This PR requires an increase in your module’s major version number.
If the new major version number is 2 or greater,
you must also add or update the version suffix
on the module path defined in your `go.mod` file.
See [the Go Modules Reference](https://go.dev/ref/mod#major-version-suffixes) for more info.

{{ else if eq .Code "Minor" }}

This PR requires (at least) an increase in your module's minor version number.

{{ else if eq .Code "Patchlevel" }}

This PR requires (at least) an increase in your module's patchlevel.

{{ else }}

This PR does not require a change in your module’s version number.
(You might still consider bumping the patchlevel anyway.)

{{ end }}

{{ if ne .Code "None" }}

```
{{ .Report -}}
```

{{ end }}
43 changes: 43 additions & 0 deletions cmd/modver/compare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"context"
"fmt"
"os"

"github.com/google/go-github/v50/github"
"github.com/pkg/errors"

"github.com/bobg/modver/v2"
)

func doCompare(ctx context.Context, opts options) (modver.Result, error) {
if opts.pr != "" {
owner, reponame, prnum, err := parsePR(opts.pr)
if err != nil {
return modver.None, errors.Wrap(err, "parsing pull-request URL")
}
if opts.ghtoken == "" {
return modver.None, fmt.Errorf("usage: %s -pr URL -token TOKEN [-q | -pretty]", os.Args[0])
}
gh := github.NewTokenClient(ctx, opts.ghtoken)
return doPR(ctx, gh, owner, reponame, prnum)
}

if opts.gitRepo != "" {
if len(opts.args) != 2 {
return nil, fmt.Errorf("usage: %s -git REPO [-gitcmd GIT_COMMAND] [-q | -pretty] [-v1 OLDERVERSION -v2 NEWERVERSION | -versions] OLDERREV NEWERREV", os.Args[0])
}

callback := modver.CompareDirs
if opts.versions {
callback = getTags(&opts.v1, &opts.v2, opts.args[0], opts.args[1])
}

return modver.CompareGitWith(ctx, opts.gitRepo, opts.args[0], opts.args[1], callback)
}
if len(opts.args) != 2 {
return nil, fmt.Errorf("usage: %s [-q | -pretty] [-v1 OLDERVERSION -v2 NEWERVERSION] OLDERDIR NEWERDIR", os.Args[0])
}
return modver.CompareDirs(opts.args[0], opts.args[1])
}
140 changes: 140 additions & 0 deletions cmd/modver/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Command modver compares two versions of the same Go packages
// and tells whether a Major, Minor, or Patchlevel version bump
// (or None)
// is needed to go from one to the other.
//
// Usage:
//
// modver -git REPO [-gitcmd GIT_COMMAND] [-q | -pretty] [-v1 OLDERVERSION -v2 NEWERVERSION | -versions] OLDERREV NEWERREV
// modver [-q | -pretty] [-v1 OLDERVERSION -v2 NEWERVERSION] OLDERDIR NEWERDIR
// modver -pr URL [-q | -pretty]
//
// With `-git REPO`,
// where REPO is the path to a Git repository,
// OLDER and NEWER are two revisions in the repository
// (e.g. hexadecimal SHA strings or "HEAD", etc)
// containing the older and newer versions of a Go module.
// Without the -git flag,
// OLDER and NEWER are two directories containing the older and newer versions of a Go module.
//
// With `-gitcmd GIT_COMMAND`,
// modver uses the given command for Git operations.
// This is "git" by default.
// If the command does not exist or is not found in your PATH,
// modver falls back to using the go-git library.
//
// With -v1 and -v2,
// modver checks whether the change from OLDERVERSION to NEWERVERSION
// (two version strings)
// is adequate for the differences detected between OLDER and NEWER.
// Output is either "OK" or "ERR"
// (followed by a description)
// and the exit code is 0 for OK and 1 for ERR.
// In quiet mode (-q),
// there is no output.
// With -git REPO and -versions instead of -v1 and -v2,
// the values for -v1 and -v2 are determined by querying the repo at the given revisions.
//
// Without -v1 and -v2
// (or -versions),
// output is a string describing the minimum version-number change required.
// In quiet mode (-q),
// there is no output,
// and the exit status is 0, 1, 2, 3, or 4
// for None, Patchlevel, Minor, Major, and error.
package main

import (
"context"
"fmt"
"io"
"os"

"golang.org/x/mod/semver"

"github.com/bobg/modver/v2"
)

const errorStatus = 4

func main() {
opts, err := parseArgs()
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing args: %s\n", err)
os.Exit(errorStatus)
}

ctx := context.Background()
if opts.gitCmd != "" {
ctx = modver.WithGit(ctx, opts.gitCmd)
}

res, err := doCompare(ctx, opts)
if err != nil {
fmt.Fprintf(os.Stderr, "Error in comparing: %s\n", err)
os.Exit(errorStatus)
}

exitCode := doShowResult(os.Stdout, res, opts)
os.Exit(exitCode)
}

func doShowResult(out io.Writer, res modver.Result, opts options) int {
if opts.v1 != "" && opts.v2 != "" {
var ok bool

cmp := semver.Compare(opts.v1, opts.v2)
switch res.Code() {
case modver.None:
ok = cmp <= 0 // v1 <= v2

case modver.Patchlevel:
ok = cmp < 0 // v1 < v2

case modver.Minor:
var (
min1 = semver.MajorMinor(opts.v1)
min2 = semver.MajorMinor(opts.v2)
)
ok = semver.Compare(min1, min2) < 0 // min1 < min2

case modver.Major:
var (
maj1 = semver.Major(opts.v1)
maj2 = semver.Major(opts.v2)
)
ok = semver.Compare(maj1, maj2) < 0 // maj1 < maj2
}

if ok {
if !opts.quiet {
if opts.versions {
fmt.Fprintf(out, "OK using versions %s and %s: %s\n", opts.v1, opts.v2, res)
} else {
fmt.Fprintf(out, "OK %s\n", res)
}
}
return 0
}
if !opts.quiet {
if opts.versions {
fmt.Fprintf(out, "ERR using versions %s and %s: %s\n", opts.v1, opts.v2, res)
} else {
fmt.Fprintf(out, "ERR %s\n", res)
}
}
return 1
}

if opts.quiet {
return int(res.Code())
}

if opts.pretty {
modver.Pretty(out, res)
} else {
fmt.Fprintln(out, res)
}

return 0
}
74 changes: 74 additions & 0 deletions cmd/modver/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

import (
"bytes"
"fmt"
"testing"

"github.com/bobg/modver/v2"
)

func TestDoShowResult(t *testing.T) {
cases := []struct {
res modver.Result
opts options
wantExitCode int
want string
}{{
res: modver.Patchlevel,
opts: options{quiet: true},
wantExitCode: int(modver.Patchlevel),
}, {
res: modver.Patchlevel,
want: modver.Patchlevel.String() + "\n",
}, {
res: modver.None,
opts: options{v1: "v1.0.0", v2: "v1.0.1"},
want: "OK None\n",
}, {
res: modver.None,
opts: options{v1: "v1.0.1", v2: "v1.0.0"},
want: "ERR None\n",
wantExitCode: 1,
}, {
res: modver.Patchlevel,
opts: options{v1: "v1.0.0", v2: "v1.0.1"},
want: "OK Patchlevel\n",
}, {
res: modver.Patchlevel,
opts: options{v1: "v1.0.0", v2: "v1.0.0"},
want: "ERR Patchlevel\n",
wantExitCode: 1,
}, {
res: modver.Minor,
opts: options{v1: "v1.0.0", v2: "v1.1.0"},
want: "OK Minor\n",
}, {
res: modver.Minor,
opts: options{v1: "v1.0.0", v2: "v1.0.1"},
want: "ERR Minor\n",
wantExitCode: 1,
}, {
res: modver.Major,
opts: options{v1: "v1.0.0", v2: "v2.0.0"},
want: "OK Major\n",
}, {
res: modver.Major,
opts: options{v1: "v1.0.0", v2: "v1.1.0"},
want: "ERR Major\n",
wantExitCode: 1,
}}

for i, tc := range cases {
t.Run(fmt.Sprintf("case_%02d", i+1), func(t *testing.T) {
buf := new(bytes.Buffer)
exitCode := doShowResult(buf, tc.res, tc.opts)
if exitCode != tc.wantExitCode {
t.Errorf("got exit code %d, want %d", exitCode, tc.wantExitCode)
}
if buf.String() != tc.want {
t.Errorf("got %s, want %s", buf, tc.want)
}
})
}
}

0 comments on commit e849b90

Please sign in to comment.