diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..56a5c59 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 + +[{*.go,go.mod,Makefile}] +indent_style = tab + +# Ignore paths +[LICENSE] +indent_size = unset diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..4decab1 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,33 @@ +name: Go + +on: + push: + branches: [ 'main', 'release-*' ] + pull_request: + types: [opened, synchronize, reopened] + +jobs: + + build: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + go-version: + - '1.18' + steps: + + - name: Set up Go ${{ matrix.go-version }} + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Test + run: go run gotest.tools/gotestsum@v1.8.0 --format testname -- + -race -count=1 -short ./... + env: + FORCE_COLOR: true diff --git a/.github/workflows/knative-style.yaml b/.github/workflows/knative-style.yaml new file mode 100644 index 0000000..f34e11c --- /dev/null +++ b/.github/workflows/knative-style.yaml @@ -0,0 +1,10 @@ +name: Code Style + +on: + pull_request: + branches: [ 'main', 'release-*' ] + +jobs: + + style: + uses: knative/actions/.github/workflows/style.yaml@main diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..4ad7c82 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,36 @@ +run: + timeout: 5m + build-tags: + - e2e + +linters: + disable-all: false + presets: + - bugs + - unused + - complexity + - format + - performance + - style + enable: + - gci + disable: + - paralleltest + - nlreturn + - exhaustivestruct + - wsl + - godox + - scopelint + - maligned + - interfacer + - golint + - ireturn + - varnamelen + - gochecknoglobals + +issues: + exclude-rules: + - path: _test\.go + linters: + - wrapcheck + diff --git a/README.md b/README.md index 899c750..119c2e4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ -# go-retcode -Deterministic process exit codes +# retcode package for Go + +Deterministic process exit codes based on Go errors. + +## Usage + +```go +err := fmt.Errorf("example error") +os.Exit(retcode.Calc(err)) +``` + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..aa527f2 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/wavesoftware/go-retcode + +go 1.18 diff --git a/retcode.go b/retcode.go new file mode 100644 index 0000000..0618282 --- /dev/null +++ b/retcode.go @@ -0,0 +1,21 @@ +package retcode + +import "hash/crc32" + +var ( + // LowerBound is the lower bound of the POSIX retcode range. Use this to + // configure the package. + LowerBound = 1 + // UpperBound is the upper bound of the POSIX retcode range. Use this to + // configure the package. + UpperBound = 255 +) + +// Calc will calculate an POSIX retcode from an error. +func Calc(err error) int { + if err == nil { + return 0 + } + upper := UpperBound - LowerBound + return int(crc32.ChecksumIEEE([]byte(err.Error())))%upper + LowerBound +} diff --git a/retcode_test.go b/retcode_test.go new file mode 100644 index 0000000..3fb5526 --- /dev/null +++ b/retcode_test.go @@ -0,0 +1,44 @@ +package retcode_test + +import ( + "fmt" + "testing" + + "github.com/wavesoftware/go-retcode" +) + +func TestCalc(t *testing.T) { + cases := testCases() + for i := range cases { + tt := cases[i] + t.Run(tt.name, func(t *testing.T) { + if got := retcode.Calc(tt.err); got != tt.want { + t.Errorf("Calc() = %v, want %v", got, tt.want) + } + }) + } +} + +func testCases() []testCase { + return []testCase{{ + name: "nil", + err: nil, + want: 0, + }, { + name: "errExample", + err: errExample, + want: 133, + }, { + name: "error of wrap caused by 12345", + err: fmt.Errorf("%w: 12345", errExample), + want: 249, + }} +} + +var errExample = fmt.Errorf("example error") + +type testCase struct { + name string + err error + want int +}