From d48d6345f0fc615291da224b5790f6116382ce5d Mon Sep 17 00:00:00 2001 From: Ben Hoyt Date: Fri, 17 Mar 2023 17:12:30 +1300 Subject: [PATCH] Update to use GitHub Actions, go.mod, and fix for Go 1.20 (#1) --- .github/workflows/tests.yml | 36 ++++++++++++++++++++++++++++ .travis.yml | 13 ---------- README.md | 5 ++-- appveyor.yml | 13 ---------- go.mod | 10 ++++++++ go.sum | 6 +++++ gosnip.go | 47 ++++++++++++++++++------------------- sniplib/sniplib.go | 33 +++++++++----------------- sniplib/sniplib_test.go | 23 ++++++++++++++---- 9 files changed, 106 insertions(+), 80 deletions(-) create mode 100644 .github/workflows/tests.yml delete mode 100644 .travis.yml delete mode 100644 appveyor.yml create mode 100644 go.mod create mode 100644 go.sum diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..59feb1c --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,36 @@ +name: Tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build-linux: + strategy: + matrix: + go: ['1.20', '1.19'] + os: [ubuntu-latest, macos-latest, windows-latest] + + name: Go ${{ matrix.go }} on ${{ matrix.os }} + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + + - name: Run tests + run: | + go version + go test -race ./... + + - name: Test that binary builds + run: | + go build + ./gosnip "fmt.Println(time.Now())" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b0fb298..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go - -go: - - 1.11.x - -# Setting sudo access to false will let Travis CI use containers -# rather than VMs to run the tests. For more details see: -# https://docs.travis-ci.com/user/reference/overview/ -sudo: false - -script: - - go version - - go test ./... diff --git a/README.md b/README.md index 425efed..ff172e3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # gosnip: run small snippets of Go code from the command line -[![GoDoc](https://godoc.org/github.com/benhoyt/gosnip?status.png)](https://godoc.org/github.com/benhoyt/gosnip) -[![TravisCI Build](https://travis-ci.org/benhoyt/gosnip.svg)](https://travis-ci.org/benhoyt/gosnip) -[![AppVeyor Build](https://ci.appveyor.com/api/projects/status/github/benhoyt/gosnip?branch=master&svg=true)](https://ci.appveyor.com/project/benhoyt/gosnip) +[![Documentation](https://pkg.go.dev/badge/github.com/benhoyt/gosnip)](https://pkg.go.dev/github.com/benhoyt/gosnip) +[![GitHub Actions Build](https://github.com/benhoyt/gosnip/workflows/Tests/badge.svg)](https://github.com/benhoyt/gosnip/actions/workflows/tests.yml) Package gosnip is a tool that allows you to run small snippets of Go code from the command line. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 7c39276..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,13 +0,0 @@ -build: off - -clone_folder: c:\gopath\src\github.com\benhoyt\gosnip - -environment: - GOPATH: c:\gopath - -install: - - go version - - go get -t -v ./... - -test_script: - - go test ./... diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dc582fe --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module github.com/benhoyt/gosnip + +go 1.19 + +require golang.org/x/tools v0.7.0 + +require ( + golang.org/x/mod v0.9.0 // indirect + golang.org/x/sys v0.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dbaed27 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= diff --git a/gosnip.go b/gosnip.go index 92e27d4..0cd4169 100644 --- a/gosnip.go +++ b/gosnip.go @@ -1,7 +1,7 @@ // Package gosnip is a tool that allows you to run small snippets of // Go code from the command line. // -// usage: gosnip [-d] [-i import ...] statements... +// usage: gosnip [-d] [-i import ...] statements... // // For simple uses, just specify one or more Go statements on the // command line, and gosnip will roll them into a full Go program and @@ -9,45 +9,44 @@ // imports needed for packages in GOPATH are added automatically // (using the same logic as the "goimports" tool). Some examples: // -// $ gosnip 'fmt.Println("Hello world")' -// Hello world +// $ gosnip 'fmt.Println("Hello world")' +// Hello world // -// $ gosnip 'fmt.Println("Current time:")' 'fmt.Println(time.Now())' -// Current time: -// 2018-11-24 16:18:47.101951 -0500 EST m=+0.000419239 +// $ gosnip 'fmt.Println("Current time:")' 'fmt.Println(time.Now())' +// Current time: +// 2018-11-24 16:18:47.101951 -0500 EST m=+0.000419239 // // The -i flag allows you to specify an import explicitly, which may be // needed to select between ambiguous stdlib imports such as // "text/template" and "html/template" (multiple -i flags are // allowed). For example: // -// $ ./gosnip -i text/template 't, _ := template.New("w").Parse("{{ . }}\n")' \ -// 't.Execute(os.Stdout, "")' -// -// $ ./gosnip -i html/template 't, _ := template.New("w").Parse("{{ . }}\n")' \ -// 't.Execute(os.Stdout, "")' -// <b> +// $ ./gosnip -i text/template 't, _ := template.New("w").Parse("{{ . }}\n")' \ +// 't.Execute(os.Stdout, "")' +// +// $ ./gosnip -i html/template 't, _ := template.New("w").Parse("{{ . }}\n")' \ +// 't.Execute(os.Stdout, "")' +// <b> // // The -d flag turns on debug mode, which prints the full program on // stderr before running it. For example: // -// $ gosnip -d 'fmt.Println(time.Now())' -// package main +// $ gosnip -d 'fmt.Println(time.Now())' +// package main // -// import ( -// "fmt" -// "time" -// ) +// import ( +// "fmt" +// "time" +// ) // -// func main() { -// fmt.Println(time.Now()) -// } -// 2018-11-24 16:33:56.681024 -0500 EST m=+0.000383308 +// func main() { +// fmt.Println(time.Now()) +// } +// 2018-11-24 16:33:56.681024 -0500 EST m=+0.000383308 // // The gosnip command-line tool is a thin wrapper around the // "sniplib" package. To run Go snippets in your Go programs, see the // sniplib docs. -// package main import ( @@ -59,7 +58,7 @@ import ( ) const ( - version = "v1.1.1" + version = "v1.2.0" ) func main() { diff --git a/sniplib/sniplib.go b/sniplib/sniplib.go index 33029d2..7b38197 100644 --- a/sniplib/sniplib.go +++ b/sniplib/sniplib.go @@ -10,7 +10,6 @@ import ( "os" "os/exec" "strings" - "syscall" importspkg "golang.org/x/tools/imports" ) @@ -69,9 +68,10 @@ func Run(source string, stdin io.Reader, stdout, stderr io.Writer) error { errBuf := &bytes.Buffer{} cmd.Stderr = errBuf err = cmd.Run() - if exitStatus(err) == 2 { - // "go run" exit status 2 means compile error, so filter the - // funky temp filename and extraneous "go run" comments + if err != nil { + // Ideally we'd only do this filtering on compile error, not program + // error, but it's hard to tell the difference ("go run" used to + // return exit code 2 for this, but since Go 1.20, it doesn't). filterStderr(errBuf.Bytes(), stderr) return err } @@ -79,31 +79,20 @@ func Run(source string, stdin io.Reader, stdout, stderr io.Writer) error { return err } -// Return exit status from given exec error (there'll be a better way -// to do this in Go 1.12!). -func exitStatus(err error) int { - if err == nil { - return 0 - } - if exitErr, ok := err.(*exec.ExitError); ok { - if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { - return status.ExitStatus() - } - } - return 1 -} - // Filter out extraneous output and temp file name from "go run" // output in case of compile error. Example "go run" output: // -// # command-line-arguments -// /var/folders/sz/thh6m7316b3gvvvmjp8qpdrm0000gp/T/gosnip_615300750.go:8:2: undefined: fmt.X +// # command-line-arguments +// /var/folders/sz/thh6m7316b3gvvvmjp8qpdrm0000gp/T/gosnip_615300750.go:8:2: undefined: fmt.X // // Output after filtering: // -// 8:2: undefined: fmt.X -// +// 8:2: undefined: fmt.X func filterStderr(data []byte, writer io.Writer) { + if !bytes.Contains(data, []byte("# command-line-arguments")) { + writer.Write(data) + return + } lines := bytes.Split(data, []byte("\n")) for _, line := range lines { if bytes.HasPrefix(line, []byte("# ")) { diff --git a/sniplib/sniplib_test.go b/sniplib/sniplib_test.go index 2a636a7..4d282b7 100644 --- a/sniplib/sniplib_test.go +++ b/sniplib/sniplib_test.go @@ -6,6 +6,7 @@ import ( "bytes" "fmt" "os" + "regexp" "strings" "testing" @@ -149,8 +150,8 @@ func main() { } `, "", - "8:2: undefined: fmt.X\n", - "exit status 2", + "8:6: undefined: fmt.X\n", + "exit status [12]", }, { `package main @@ -178,7 +179,7 @@ func main() { `, "", "4:2: undefined: foo\n", - "exit status 2", + "exit status [12]", }, } for _, test := range tests { @@ -194,14 +195,26 @@ func main() { t.Errorf("expected stderr %q, got %q", test.stderr, errBuf.String()) } if err != nil { - if err.Error() != test.err { - t.Errorf("expected error %q, got %q", test.err, err.Error()) + if !mustMatch(test.err, err.Error()) { + t.Errorf("expected error to match %q, got %q", test.err, err.Error()) + } + } else { + if test.err != "" { + t.Errorf("expected error to match %q, got no error", test.err) } } }) } } +func mustMatch(pattern, s string) bool { + matched, err := regexp.MatchString(pattern, s) + if err != nil { + panic(err) + } + return matched +} + func ExampleToProgram() { statements := []string{`fmt.Println("Hello world")`} source, err := sniplib.ToProgram(statements, nil)