diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6c0070..cafa237 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,14 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v.5.5.0 with: - go-version: '^1.17.5' + go-version: '^1.24.3' + - name: golangci-lint + uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 + with: + version: latest + args: --timeout=5m - run: make test diff --git a/.go-version b/.go-version new file mode 100644 index 0000000..ae96cc7 --- /dev/null +++ b/.go-version @@ -0,0 +1 @@ +1.24.3 diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..15eadfd --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,11 @@ +# https://golangci-lint.run/usage/configuration/ +version: '2' +linters: + default: standard + disable: + - errcheck + - unused +formatters: + enable: + - gofmt + - goimports diff --git a/Brewfile b/Brewfile index c9491e0..46f83f3 100644 --- a/Brewfile +++ b/Brewfile @@ -1,2 +1,2 @@ brew 'go' -brew 'hub' +brew 'gh' diff --git a/Makefile b/Makefile index 4cf0474..5a43472 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ SHELL = /bin/sh -VERSION=1.6.1 +VERSION=1.7.0 BUILD=`git rev-parse HEAD` LDFLAGS=-ldflags "-w -s \ @@ -8,7 +8,6 @@ LDFLAGS=-ldflags "-w -s \ -X github.com/Betterment/testtrack-cli/cmds.build=${BUILD}" PACKAGES=$$(find . -maxdepth 1 -type d ! -path '.' ! -path './.*' ! -path './vendor' ! -path './dist' ! -path './script' ! -path './doc') -LINTEXCLUDES="123nothingyet123" all: test @@ -16,41 +15,30 @@ install: @go install ${LDFLAGS} github.com/Betterment/testtrack-cli/testtrack dist: - @mkdir dist &&\ - GOOS=linux GOARCH=amd64 go build -o "dist/testtrack.linux" ${LDFLAGS} github.com/Betterment/testtrack-cli/testtrack &&\ - GOOS=darwin GOARCH=amd64 go build -o "dist/testtrack.darwin-amd64" ${LDFLAGS} github.com/Betterment/testtrack-cli/testtrack &&\ + @mkdir dist && \ + GOOS=linux GOARCH=amd64 go build -o "dist/testtrack.linux" ${LDFLAGS} github.com/Betterment/testtrack-cli/testtrack && \ + GOOS=darwin GOARCH=amd64 go build -o "dist/testtrack.darwin-amd64" ${LDFLAGS} github.com/Betterment/testtrack-cli/testtrack && \ GOOS=darwin GOARCH=arm64 go build -o "dist/testtrack.darwin-arm64" ${LDFLAGS} github.com/Betterment/testtrack-cli/testtrack release: distclean dist - @hub release create\ - -a dist/testtrack.linux\ - -a dist/testtrack.darwin-amd64\ - -a dist/testtrack.darwin-arm64\ - -m "TestTrack CLI ${VERSION}"\ - -t "${BUILD}"\ - v${VERSION} + @(gh release view v${VERSION} > /dev/null 2>&1 \ + && echo "v${VERSION} has already been released.") \ + || gh release create v${VERSION} \ + dist/testtrack.linux \ + dist/testtrack.darwin-amd64 \ + dist/testtrack.darwin-arm64 \ + --title "TestTrack CLI ${VERSION}" \ + --target "${BUILD}" \ + --generate-notes test: - @go install golang.org/x/tools/cmd/goimports@latest - @go install golang.org/x/lint/golint@latest - @GOIMPORTS_RESULT=$$($$(go env GOPATH)/bin/goimports -l ${PACKAGES} | grep -v ${LINTEXCLUDES});\ - if [ $$(echo "$$GOIMPORTS_RESULT\c" | head -c1 | wc -c) -ne 0 ];\ - then\ - echo "Style violations found. Run the following command to fix:";\ - echo;\ - echo "$$(go env GOPATH)/bin/goimports -w" $$GOIMPORTS_RESULT;\ - echo;\ - exit 1;\ - fi - @go vet ${PACKAGES} - @GOLINT_RESULT=$$($$(go env GOPATH)/bin/golint ${PACKAGES} | grep -v ${LINTEXCLUDES});\ - if [ $$(echo "$$GOLINT_RESULT\c" | head -c1 | wc -c) -ne 0 ];\ - then\ - echo $$GOLINT_RESULT;\ - exit 1;\ - fi @go test ${PACKAGES} +lint: + golangci-lint version &>/dev/null || brew install golangci-lint + golangci-lint fmt --verbose + golangci-lint run --verbose --timeout 5m + cover: @echo "What package do you want a coverage report for? \c" @read PACKAGE &&\ @@ -68,4 +56,4 @@ clean: distclean: clean @rm -rf dist -.PHONY: all build install check clean distclean test +.PHONY: all build install clean distclean test diff --git a/cmds/generate_build_timestamp.go b/cmds/generate_build_timestamp.go index 54c7376..02def0f 100644 --- a/cmds/generate_build_timestamp.go +++ b/cmds/generate_build_timestamp.go @@ -1,7 +1,6 @@ package cmds import ( - "io/ioutil" "os" "time" @@ -36,5 +35,5 @@ func generateBuildTimestamp() error { return err } - return ioutil.WriteFile("testtrack/build_timestamp", timestamp, 0644) + return os.WriteFile("testtrack/build_timestamp", timestamp, 0644) } diff --git a/cmds/init_project.go b/cmds/init_project.go index 5dbdba6..2078b17 100644 --- a/cmds/init_project.go +++ b/cmds/init_project.go @@ -1,7 +1,6 @@ package cmds import ( - "io/ioutil" "log" "os" @@ -40,7 +39,7 @@ func initProject() error { } keepfile.Close() - if err := ioutil.WriteFile("testtrack/.gitignore", []byte("build_timestamp\n"), 0644); err != nil { + if err := os.WriteFile("testtrack/.gitignore", []byte("build_timestamp\n"), 0644); err != nil { log.Fatal(err) } diff --git a/fakeassignments/fakeassignments.go b/fakeassignments/fakeassignments.go index 2714a59..f87726b 100644 --- a/fakeassignments/fakeassignments.go +++ b/fakeassignments/fakeassignments.go @@ -1,11 +1,9 @@ package fakeassignments import ( - "io/ioutil" "os" "github.com/Betterment/testtrack-cli/paths" - "gopkg.in/yaml.v2" ) @@ -20,12 +18,12 @@ func Read() (*map[string]string, error) { if err != nil { return nil, err } - err = ioutil.WriteFile(*configDir+"/assignments.yml", []byte("{}"), 0644) + err = os.WriteFile(*configDir+"/assignments.yml", []byte("{}"), 0644) if err != nil { return nil, err } } - assignmentsBytes, err := ioutil.ReadFile(*configDir + "/assignments.yml") + assignmentsBytes, err := os.ReadFile(*configDir + "/assignments.yml") if err != nil { return nil, err } @@ -47,7 +45,7 @@ func Write(assignments *map[string]string) error { if err != nil { return err } - err = ioutil.WriteFile(*configDir+"/assignments.yml", bytes, 0644) + err = os.WriteFile(*configDir+"/assignments.yml", bytes, 0644) if err != nil { return err } diff --git a/fakeserver/routes.go b/fakeserver/routes.go index 77f83ba..4730be6 100644 --- a/fakeserver/routes.go +++ b/fakeserver/routes.go @@ -3,7 +3,7 @@ package fakeserver import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" @@ -341,7 +341,7 @@ func postV1AssignmentOverride(r *http.Request) error { Variant: r.PostForm.Get("variant"), } case strings.HasPrefix(contentType, "application/json"): - requestBytes, err := ioutil.ReadAll(r.Body) + requestBytes, err := io.ReadAll(r.Body) if err != nil { return err } @@ -353,6 +353,10 @@ func postV1AssignmentOverride(r *http.Request) error { return fmt.Errorf("got unexpected content type %s", contentType) } assignments, err := fakeassignments.Read() + if err != nil { + return err + } + (*assignments)[assignment.SplitName] = assignment.Variant err = fakeassignments.Write(assignments) if err != nil { @@ -366,7 +370,7 @@ func postV2AssignmentOverride(r *http.Request) error { contentType := r.Header.Get("content-type") switch { case strings.HasPrefix(contentType, "application/json"): - requestBytes, err := ioutil.ReadAll(r.Body) + requestBytes, err := io.ReadAll(r.Body) if err != nil { return err } @@ -380,6 +384,10 @@ func postV2AssignmentOverride(r *http.Request) error { return fmt.Errorf("got unexpected content type %s", contentType) } storedAssignments, err := fakeassignments.Read() + if err != nil { + return err + } + for _, assignment := range assignments { (*storedAssignments)[assignment.SplitName] = assignment.Variant } @@ -453,12 +461,12 @@ func getV1SplitDetail() (interface{}, error) { Location: "location", Platform: "platform", VariantDetails: []v1VariantDetail{ - v1VariantDetail{ + { Name: "variant_a", Description: "this is a fake description", ScreenshotURL: "https://example.org/a", }, - v1VariantDetail{ + { Name: "variant_b", Description: "this is another fake description", ScreenshotURL: "https://example.org/b", diff --git a/fakeserver/server_test.go b/fakeserver/server_test.go index 836646a..bca788a 100644 --- a/fakeserver/server_test.go +++ b/fakeserver/server_test.go @@ -2,7 +2,6 @@ package fakeserver import ( "bytes" - "io/ioutil" "log" "net/http" "net/http/httptest" @@ -41,7 +40,7 @@ something_something_enabled: "true" func TestMain(m *testing.M) { current, exists := os.LookupEnv("TESTTRACK_FAKE_SERVER_CONFIG_DIR") - dir, err := ioutil.TempDir("", "testtrack-cli") + dir, err := os.MkdirTemp("", "testtrack-cli") if err != nil { log.Fatal(err) } @@ -53,12 +52,12 @@ func TestMain(m *testing.M) { } schemaContent := []byte(testSchema) - if err := ioutil.WriteFile(filepath.Join(schemasDir, "test.yml"), schemaContent, 0644); err != nil { + if err := os.WriteFile(filepath.Join(schemasDir, "test.yml"), schemaContent, 0644); err != nil { log.Fatal(err) } assignmentsContent := []byte(testAssignments) - if err := ioutil.WriteFile(filepath.Join(dir, "assignments.yml"), assignmentsContent, 0644); err != nil { + if err := os.WriteFile(filepath.Join(dir, "assignments.yml"), assignmentsContent, 0644); err != nil { log.Fatal(err) } @@ -230,7 +229,7 @@ func TestCors(t *testing.T) { h.ServeHTTP(w, request) require.Equal(t, http.StatusOK, w.Code) - require.Equal(t, "", w.HeaderMap.Get("Access-Control-Allow-Origin")) + require.Equal(t, "", w.Result().Header.Get("Access-Control-Allow-Origin")) }) t.Run("it passes cors with an allowed origin", func(t *testing.T) { @@ -243,7 +242,7 @@ func TestCors(t *testing.T) { h.ServeHTTP(w, request) require.Equal(t, http.StatusOK, w.Code) - require.Equal(t, "http://www.allowed.com", w.HeaderMap.Get("Access-Control-Allow-Origin")) + require.Equal(t, "http://www.allowed.com", w.Result().Header.Get("Access-Control-Allow-Origin")) }) t.Run("it passes cors from localhost", func(t *testing.T) { @@ -256,7 +255,7 @@ func TestCors(t *testing.T) { h.ServeHTTP(w, request) require.Equal(t, http.StatusOK, w.Code) - require.Equal(t, "http://localhost:3000", w.HeaderMap.Get("Access-Control-Allow-Origin")) + require.Equal(t, "http://localhost:3000", w.Result().Header.Get("Access-Control-Allow-Origin")) }) t.Run("it passes cors from loopback ip", func(t *testing.T) { @@ -269,7 +268,7 @@ func TestCors(t *testing.T) { h.ServeHTTP(w, request) require.Equal(t, http.StatusOK, w.Code) - require.Equal(t, "http://127.0.0.1:3000", w.HeaderMap.Get("Access-Control-Allow-Origin")) + require.Equal(t, "http://127.0.0.1:3000", w.Result().Header.Get("Access-Control-Allow-Origin")) }) os.Unsetenv("TESTTRACK_ALLOWED_ORIGINS") @@ -305,11 +304,11 @@ func TestPersistAssignmentV2(t *testing.T) { overrides := v2AssignmentOverrideRequestBody{ Assignments: []v1Assignment{ - v1Assignment{ + { SplitName: "test.test_experiment", Variant: "control", }, - v1Assignment{ + { SplitName: "test.test2_experiment", Variant: "treatment", }, diff --git a/featurecompletions/featurecompletions.go b/featurecompletions/featurecompletions.go index b952661..6131059 100644 --- a/featurecompletions/featurecompletions.go +++ b/featurecompletions/featurecompletions.go @@ -114,7 +114,7 @@ func (f *FeatureCompletion) ApplyToSchema(schema *serializers.Schema, _ migratio if idempotently { return nil } - return fmt.Errorf("Couldn't locate feature_completion of %s in schema", *f.featureGate) + return fmt.Errorf("couldn't locate feature_completion of %s in schema", *f.featureGate) } for i, candidate := range schema.FeatureCompletions { // Replace if candidate.FeatureGate == *f.featureGate { diff --git a/go.mod b/go.mod index 3eee947..58fbf41 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,20 @@ module github.com/Betterment/testtrack-cli -go 1.17 +go 1.24 require ( - github.com/gorilla/mux v1.7.1 - github.com/joho/godotenv v1.3.0 - github.com/pkg/errors v0.8.1 - github.com/rs/cors v1.6.0 - github.com/spf13/cobra v0.0.3 - github.com/stretchr/testify v1.3.0 - gopkg.in/yaml.v2 v2.2.4 + github.com/gorilla/mux v1.8.1 + github.com/joho/godotenv v1.5.1 + github.com/rs/cors v1.11.1 + github.com/spf13/cobra v1.9.1 + github.com/stretchr/testify v1.10.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/spf13/pflag v1.0.3 // indirect + github.com/spf13/pflag v1.0.6 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 62256d4..b7ef1fc 100644 --- a/go.sum +++ b/go.sum @@ -1,26 +1,26 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU= -github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= -github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/migrationloaders/migrationloaders.go b/migrationloaders/migrationloaders.go index 931f7bd..8310ec5 100644 --- a/migrationloaders/migrationloaders.go +++ b/migrationloaders/migrationloaders.go @@ -2,7 +2,7 @@ package migrationloaders import ( "fmt" - "io/ioutil" + "os" "path" "strings" @@ -19,7 +19,7 @@ import ( // Load loads a set of migrations func Load() (migrations.Repository, error) { - files, err := ioutil.ReadDir("testtrack/migrate") + files, err := os.ReadDir("testtrack/migrate") if err != nil { return nil, err } @@ -35,7 +35,7 @@ func Load() (migrations.Repository, error) { return nil, err } - fileBytes, err := ioutil.ReadFile(path.Join("testtrack/migrate", file.Name())) + fileBytes, err := os.ReadFile(path.Join("testtrack/migrate", file.Name())) if err != nil { return nil, err } diff --git a/migrationmanagers/migrationmanagers.go b/migrationmanagers/migrationmanagers.go index b2edba5..2d43455 100644 --- a/migrationmanagers/migrationmanagers.go +++ b/migrationmanagers/migrationmanagers.go @@ -1,8 +1,8 @@ package migrationmanagers import ( + "errors" "fmt" - "io/ioutil" "os" "github.com/Betterment/testtrack-cli/migrationloaders" @@ -10,7 +10,6 @@ import ( "github.com/Betterment/testtrack-cli/schema" "github.com/Betterment/testtrack-cli/serializers" "github.com/Betterment/testtrack-cli/servers" - "github.com/pkg/errors" "gopkg.in/yaml.v2" ) @@ -117,7 +116,7 @@ func (m *MigrationManager) Sync() error { case 204: return nil case 422: - return errors.New("Migration unsuccessful on server. Does your split exist?") + return errors.New("migration unsuccessful on server. Does your split exist?") default: return fmt.Errorf("got %d status code", resp.StatusCode) } @@ -126,7 +125,7 @@ func (m *MigrationManager) Sync() error { func (m *MigrationManager) persistFile() error { stat, err := os.Stat("testtrack/migrate") if err != nil { - return errors.Wrap(err, "migration directory not found - run `testtrack init_project` to resolve") + return fmt.Errorf("migration directory not found - run `testtrack init_project` to resolve: %w", err) } if !stat.IsDir() { @@ -134,8 +133,11 @@ func (m *MigrationManager) persistFile() error { } out, err := yaml.Marshal(m.migration.File()) + if err != nil { + return fmt.Errorf("failed to marshal migration file: %w", err) + } - err = ioutil.WriteFile(fmt.Sprintf("testtrack/migrate/%s", *m.migration.Filename()), out, 0644) + err = os.WriteFile(fmt.Sprintf("testtrack/migrate/%s", *m.migration.Filename()), out, 0644) if err != nil { return err } diff --git a/migrations/migrations.go b/migrations/migrations.go index d5186ac..9f5497f 100644 --- a/migrations/migrations.go +++ b/migrations/migrations.go @@ -1,6 +1,7 @@ package migrations import ( + "errors" "fmt" "path/filepath" "regexp" @@ -9,7 +10,6 @@ import ( "time" "github.com/Betterment/testtrack-cli/serializers" - "github.com/pkg/errors" ) // IMigration represents a migration @@ -49,7 +49,7 @@ func GenerateMigrationVersion() (*string, error) { lastMatch := matches[len(matches)-1] matches = migrationFilenameRegex.FindStringSubmatch(filepath.Base(lastMatch)) if matches == nil { - return nil, fmt.Errorf("Failed to parse migration filename %s", lastMatch) + return nil, fmt.Errorf("failed to parse migration filename %s", lastMatch) } var i int @@ -58,7 +58,7 @@ func GenerateMigrationVersion() (*string, error) { } else if len(matches[1]) == 17 { i, err = strconv.Atoi(matches[1][14:17]) if err != nil { - return nil, errors.Wrap(err, "couldn't parse file version") + return nil, fmt.Errorf("couldn't parse file version: %w", err) } i++ } else { diff --git a/remotekills/remotekills.go b/remotekills/remotekills.go index ac3c5e8..d921b0e 100644 --- a/remotekills/remotekills.go +++ b/remotekills/remotekills.go @@ -142,7 +142,7 @@ func (r *RemoteKill) ApplyToSchema(schema *serializers.Schema, _ migrations.Repo if idempotently { return nil } - return fmt.Errorf("Couldn't locate remote_kill %s of %s in schema", *r.reason, *r.split) + return fmt.Errorf("couldn't locate remote_kill %s of %s in schema", *r.reason, *r.split) } for i, candidate := range schema.RemoteKills { // Replace if candidate.Split == *r.split && candidate.Reason == *r.reason { diff --git a/schema/schema.go b/schema/schema.go index ed4debd..cdc66e0 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -3,7 +3,6 @@ package schema import ( "errors" "fmt" - "io/ioutil" "os" "path" "path/filepath" @@ -21,7 +20,7 @@ func Read() (*serializers.Schema, error) { if _, err := os.Stat("testtrack/schema.yml"); os.IsNotExist(err) { return Generate() } - schemaBytes, err := ioutil.ReadFile("testtrack/schema.yml") + schemaBytes, err := os.ReadFile("testtrack/schema.yml") if err != nil { return nil, err } @@ -55,8 +54,11 @@ func Generate() (*serializers.Schema, error) { func Write(schema *serializers.Schema) error { SortAlphabetically(schema) out, err := yaml.Marshal(schema) + if err != nil { + return err + } - err = ioutil.WriteFile("testtrack/schema.yml", out, 0644) + err = os.WriteFile("testtrack/schema.yml", out, 0644) if err != nil { return err } @@ -113,7 +115,7 @@ func ReadMerged() (*serializers.Schema, error) { } } // Read file - schemaBytes, err := ioutil.ReadFile(path) + schemaBytes, err := os.ReadFile(path) if err != nil { return nil, err } @@ -123,18 +125,10 @@ func ReadMerged() (*serializers.Schema, error) { return nil, err } // Merge into master schema - for _, split := range schema.Splits { - mergedSchema.Splits = append(mergedSchema.Splits, split) - } - for _, featureCompletion := range schema.FeatureCompletions { - mergedSchema.FeatureCompletions = append(mergedSchema.FeatureCompletions, featureCompletion) - } - for _, remoteKill := range schema.RemoteKills { - mergedSchema.RemoteKills = append(mergedSchema.RemoteKills, remoteKill) - } - for _, identifierType := range schema.IdentifierTypes { - mergedSchema.IdentifierTypes = append(mergedSchema.IdentifierTypes, identifierType) - } + mergedSchema.Splits = append(mergedSchema.Splits, schema.Splits...) + mergedSchema.FeatureCompletions = append(mergedSchema.FeatureCompletions, schema.FeatureCompletions...) + mergedSchema.RemoteKills = append(mergedSchema.RemoteKills, schema.RemoteKills...) + mergedSchema.IdentifierTypes = append(mergedSchema.IdentifierTypes, schema.IdentifierTypes...) } return &mergedSchema, nil } @@ -143,7 +137,7 @@ func mergeLegacySchema(schema *serializers.Schema) error { if _, err := os.Stat("db/test_track_schema.yml"); os.IsNotExist(err) { return nil } - legacySchemaBytes, err := ioutil.ReadFile("db/test_track_schema.yml") + legacySchemaBytes, err := os.ReadFile("db/test_track_schema.yml") if err != nil { return err } diff --git a/schemaloaders/schemaloaders.go b/schemaloaders/schemaloaders.go index a7665e8..222ce08 100644 --- a/schemaloaders/schemaloaders.go +++ b/schemaloaders/schemaloaders.go @@ -14,7 +14,6 @@ import ( "github.com/Betterment/testtrack-cli/servers" "github.com/Betterment/testtrack-cli/splitdecisions" "github.com/Betterment/testtrack-cli/splits" - "github.com/pkg/errors" ) // SchemaLoader loads schemas into TestTrack @@ -101,7 +100,7 @@ func schemaSplitMigrations(schemaSplit serializers.SchemaSplit) ([]migrations.IM var decision *string weights, err := splits.WeightsFromYAML(schemaSplit.Weights) if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("schema split %s invalid", schemaSplit.Name)) + return nil, fmt.Errorf("schema split %s invalid: %w", schemaSplit.Name, err) } for variant, weight := range *weights { if weight == 100 { diff --git a/servers/servers.go b/servers/servers.go index b0c3019..1887312 100644 --- a/servers/servers.go +++ b/servers/servers.go @@ -5,7 +5,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net/http" "net/url" "os" @@ -57,7 +57,7 @@ func (s *Server) Get(path string, v interface{}) error { return fmt.Errorf("got %d status code", resp.StatusCode) } - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } diff --git a/splitdecisions/splitdecisions.go b/splitdecisions/splitdecisions.go index e5533c0..cf8dfe1 100644 --- a/splitdecisions/splitdecisions.go +++ b/splitdecisions/splitdecisions.go @@ -7,7 +7,6 @@ import ( "github.com/Betterment/testtrack-cli/serializers" "github.com/Betterment/testtrack-cli/splits" "github.com/Betterment/testtrack-cli/validations" - "github.com/pkg/errors" ) // SplitDecision represents a feature we're marking (un)completed @@ -104,7 +103,7 @@ func (s *SplitDecision) ApplyToSchema(schema *serializers.Schema, migrationRepo } err = weights.ReweightToDecision(*s.variant) if err != nil { - return errors.Wrap(err, fmt.Sprintf("in split %s in schema", *s.split)) + return fmt.Errorf("in split %s in schema: %w", *s.split, err) } schema.Splits[i].Weights = weights.ToYAML() return nil @@ -116,7 +115,7 @@ func (s *SplitDecision) ApplyToSchema(schema *serializers.Schema, migrationRepo weights := split.Weights() err := weights.ReweightToDecision(*s.variant) if err != nil { - return errors.Wrap(err, fmt.Sprintf("in most recent split migration %s", *s.split)) + return fmt.Errorf("in most recent split migration %s: %w", *s.split, err) } schema.Splits = append(schema.Splits, serializers.SchemaSplit{ Name: *s.split, @@ -129,5 +128,5 @@ func (s *SplitDecision) ApplyToSchema(schema *serializers.Schema, migrationRepo if idempotently { return nil } - return fmt.Errorf("Couldn't locate split %s in schema to decide", *s.split) + return fmt.Errorf("couldn't locate split %s in schema to decide", *s.split) } diff --git a/splitretirements/splitretirements.go b/splitretirements/splitretirements.go index 14b24a3..a0f18fc 100644 --- a/splitretirements/splitretirements.go +++ b/splitretirements/splitretirements.go @@ -7,7 +7,6 @@ import ( "github.com/Betterment/testtrack-cli/serializers" "github.com/Betterment/testtrack-cli/splits" "github.com/Betterment/testtrack-cli/validations" - "github.com/pkg/errors" ) // SplitRetirement represents a feature we're marking (un)completed @@ -103,7 +102,7 @@ func (s *SplitRetirement) ApplyToSchema(schema *serializers.Schema, _ migrations } err = weights.ReweightToDecision(*s.decision) if err != nil { - return errors.Wrap(err, fmt.Sprintf("in split %s in schema", *s.split)) + return fmt.Errorf("in split %s in schema: %w", *s.split, err) } schema.Splits = append(schema.Splits[:i], schema.Splits[i+1:]...) return nil diff --git a/validations/validations.go b/validations/validations.go index 27f5272..79e4185 100644 --- a/validations/validations.go +++ b/validations/validations.go @@ -2,7 +2,6 @@ package validations import ( "fmt" - "io/ioutil" "os" "regexp" "strings" @@ -94,7 +93,7 @@ func ValidateOwnerName(owner string) error { return fmt.Errorf("owner must be specified when ownership file (%s) exists", ownershipFilePath) } - fileBytes, err := ioutil.ReadFile(ownershipFilePath) + fileBytes, err := os.ReadFile(ownershipFilePath) if err != nil { return err } diff --git a/validations/validations_test.go b/validations/validations_test.go index 91bb1bb..7c0cd13 100644 --- a/validations/validations_test.go +++ b/validations/validations_test.go @@ -65,7 +65,7 @@ func TestAutoPrefixAndValidateSplit(t *testing.T) { currentAppName := "my_app" schema := &serializers.Schema{ Splits: []serializers.SchemaSplit{ - serializers.SchemaSplit{ + { Name: "my_app.foo_enabled", }, }, @@ -83,10 +83,10 @@ func TestAutoPrefixAndValidateSplit(t *testing.T) { currentAppName := "my_app" schema := &serializers.Schema{ Splits: []serializers.SchemaSplit{ - serializers.SchemaSplit{ + { Name: "foo_enabled", }, - serializers.SchemaSplit{ + { Name: "my_app.foo_enabled", }, }, @@ -116,10 +116,10 @@ func TestAutoPrefixAndValidateSplit(t *testing.T) { currentAppName := "my_app" schema := &serializers.Schema{ Splits: []serializers.SchemaSplit{ - serializers.SchemaSplit{ + { Name: "my_app.foo_enabled", }, - serializers.SchemaSplit{ + { Name: "foo_enabled", }, },