diff --git a/.golangci.yml b/.golangci.yml index 72d46eeca..98b473eb5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,6 +31,10 @@ linters-settings: errorlint: # these are still common in Go: for instance, exit errors. asserts: false + # Forcing %w in error wrapping forces authors to make errors part of their package APIs. The decision to make + # an error part of a package API should be a concious decision by the author. + # Also see Hyrums Law. + errorf: false exhaustive: default-signifies-exhaustive: true @@ -88,10 +92,10 @@ linters-settings: - name: waitgroup-by-value staticcheck: - go: "1.16" + go: "1.17" unused: - go: "1.16" + go: "1.17" output: sort-results: true @@ -107,8 +111,7 @@ linters: - dupl - durationcheck - errcheck - # errname is only available in golangci-lint v1.42.0+ - wait until v1.43 is available to settle - #- errname + - errname - errorlint - exhaustive - exportloopref @@ -137,7 +140,7 @@ linters: - nolintlint - predeclared # disabling for the initial iteration of the linting tool - #- promlinter + # - promlinter - revive - rowserrcheck - sqlclosecheck @@ -188,7 +191,7 @@ issues: linters: - noctx - # kubebuilder needs the stdlib invalid `inline` json struct tag + # local to tink: kubebuilder needs the stdlib invalid `inline` json struct tag - path: pkg/apis/.* text: "struct-tag" diff --git a/.yamllint b/.yamllint new file mode 100644 index 000000000..9a08ad176 --- /dev/null +++ b/.yamllint @@ -0,0 +1,16 @@ +--- +extends: default + +rules: + braces: + max-spaces-inside: 1 + brackets: + max-spaces-inside: 1 + comments: disable + comments-indentation: disable + document-start: disable + line-length: + level: warning + max: 160 + allow-non-breakable-inline-mappings: true + truthy: disable diff --git a/cmd/tink-cli/cmd/get/get.go b/cmd/tink-cli/cmd/get/get.go index 9d20570c2..0c4b8f7d5 100644 --- a/cmd/tink-cli/cmd/get/get.go +++ b/cmd/tink-cli/cmd/get/get.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" + "github.com/google/uuid" "github.com/jedib0t/go-pretty/table" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -19,6 +20,8 @@ type Options struct { RetrieveData func(context.Context, *client.FullClient) ([]interface{}, error) // RetrieveByID is used when a get command has a list of arguments RetrieveByID func(context.Context, *client.FullClient, string) (interface{}, error) + // RetrieveByName is used when a get command has a list of arguments + RetrieveByName func(context.Context, *client.FullClient, string) (interface{}, error) // PopulateTable populates a table with the data retrieved with the RetrieveData function. PopulateTable func([]interface{}, table.Writer) error @@ -62,17 +65,11 @@ func NewGetCommand(opt Options) *cobra.Command { client := clientctx.Get(cmd.Context()) if len(args) != 0 { - if opt.RetrieveByID == nil { - return errors.New("option RetrieveByID is not implemented for this resource yet. Please have a look at the issue in GitHub or open a new one") - } - for _, requestedID := range args { - s, err := opt.RetrieveByID(cmd.Context(), client, requestedID) - if err != nil { - continue - } - data = append(data, s) - } + data, err = retrieveMulti(cmd.Context(), opt, client, args) } else { + if opt.RetrieveData == nil { + return errors.New("get-all-data is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one") + } data, err = opt.RetrieveData(cmd.Context(), client) } if err != nil { @@ -117,3 +114,31 @@ func NewGetCommand(opt Options) *cobra.Command { cmd.PersistentFlags().BoolVar(&opt.NoHeaders, "no-headers", false, "Table contains an header with the columns' name. You can disable it from being printed out") return cmd } + +func retrieveMulti(ctx context.Context, opt Options, fc *client.FullClient, args []string) ([]interface{}, error) { + var data []interface{} + for _, arg := range args { + var retriever func(context.Context, *client.FullClient, string) (interface{}, error) + if _, err := uuid.Parse(arg); err != nil { + // arg is invalid UUID, search for arg in `name` field of db + if opt.RetrieveByName == nil { + return nil, errors.New("get by Name is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one") + } + retriever = opt.RetrieveByName + } else { + // arg is a valid UUID, search for arg in `id` field of db + if opt.RetrieveByID == nil { + return nil, errors.New("get by ID is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one") + } + retriever = opt.RetrieveByID + } + + s, err := retriever(ctx, fc, arg) + if err != nil { + continue + } + data = append(data, s) + } + + return data, nil +} diff --git a/cmd/tink-cli/cmd/get/get_test.go b/cmd/tink-cli/cmd/get/get_test.go index 38cf4c39a..d9aa3ecfc 100644 --- a/cmd/tink-cli/cmd/get/get_test.go +++ b/cmd/tink-cli/cmd/get/get_test.go @@ -3,12 +3,14 @@ package get import ( "bytes" "context" + "fmt" "io" "io/ioutil" "testing" "github.com/google/go-cmp/cmp" "github.com/jedib0t/go-pretty/table" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/tinkerbell/tink/client" "github.com/tinkerbell/tink/cmd/tink-cli/cmd/internal/clientctx" @@ -18,6 +20,7 @@ func TestNewGetCommand(t *testing.T) { tests := []struct { Name string ExpectStdout string + ExpectError error Args []string Opt Options Skip string @@ -51,20 +54,20 @@ func TestNewGetCommand(t *testing.T) { }, { Name: "get-by-id", - Args: []string{"30"}, - ExpectStdout: `+------+-------+ -| NAME | ID | -+------+-------+ -| 30 | hello | -+------+-------+ + Args: []string{"e0ffbf50-ae7c-4c92-bc7f-34e0de25a989"}, + ExpectStdout: `+-------+--------------------------------------+ +| NAME | ID | ++-------+--------------------------------------+ +| hello | e0ffbf50-ae7c-4c92-bc7f-34e0de25a989 | ++-------+--------------------------------------+ `, Opt: Options{ Headers: []string{"name", "id"}, RetrieveByID: func(ctx context.Context, cl *client.FullClient, arg string) (interface{}, error) { - if arg != "30" { - t.Errorf("expected 30 as arg got %s", arg) + if arg != "e0ffbf50-ae7c-4c92-bc7f-34e0de25a989" { + t.Errorf("expected e0ffbf50-ae7c-4c92-bc7f-34e0de25a989 as arg got %s", arg) } - return []string{"30", "hello"}, nil + return []string{"hello", "e0ffbf50-ae7c-4c92-bc7f-34e0de25a989"}, nil }, PopulateTable: func(data []interface{}, w table.Writer) error { for _, v := range data { @@ -76,6 +79,43 @@ func TestNewGetCommand(t *testing.T) { }, }, }, + { + Name: "get-by-id but no retriever", + Args: []string{"e0ffbf50-ae7c-4c92-bc7f-34e0de25a989"}, + ExpectError: errors.New("get by ID is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one"), + }, + { + Name: "get-by-name", + Args: []string{"hello"}, + ExpectStdout: `+-------+--------------------------------------+ +| NAME | ID | ++-------+--------------------------------------+ +| hello | e0ffbf50-ae7c-4c92-bc7f-34e0de25a989 | ++-------+--------------------------------------+ +`, + Opt: Options{ + Headers: []string{"name", "id"}, + RetrieveByName: func(ctx context.Context, cl *client.FullClient, arg string) (interface{}, error) { + if arg != "hello" { + t.Errorf("expected hello as arg got %s", arg) + } + return []string{"hello", "e0ffbf50-ae7c-4c92-bc7f-34e0de25a989"}, nil + }, + PopulateTable: func(data []interface{}, w table.Writer) error { + for _, v := range data { + if vv, ok := v.([]string); ok { + w.AppendRow(table.Row{vv[0], vv[1]}) + } + } + return nil + }, + }, + }, + { + Name: "get-by-name but no retriever", + Args: []string{"hello"}, + ExpectError: errors.New("get by Name is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one"), + }, { Name: "happy-path-no-headers", ExpectStdout: `+----+-------+ @@ -174,6 +214,10 @@ func TestNewGetCommand(t *testing.T) { }, }, }, + { + Name: "no opts", + ExpectError: errors.New("get-all-data is not implemented for this resource yet, please have a look at the issue in GitHub or open a new one"), + }, } for _, s := range tests { @@ -181,18 +225,22 @@ func TestNewGetCommand(t *testing.T) { if s.Skip != "" { t.Skip(s.Skip) } - stdout := bytes.NewBufferString("") + stdout := &bytes.Buffer{} cmd := NewGetCommand(s.Opt) + cmd.SilenceErrors = true cmd.SetOut(stdout) cmd.SetArgs(s.Args) err := cmd.ExecuteContext(clientctx.Set(context.Background(), &client.FullClient{})) - if err != nil { - t.Error(err) + if fmt.Sprint(err) != fmt.Sprint(s.ExpectError) { + t.Errorf("unexpected error: want=%v, got=%v", s.ExpectError, err) } out, err := ioutil.ReadAll(stdout) if err != nil { t.Error(err) } + if s.ExpectError != nil { + s.ExpectStdout = cmd.UsageString() + "\n" + } if diff := cmp.Diff(string(out), s.ExpectStdout); diff != "" { t.Fatal(diff) } diff --git a/cmd/tink-cli/cmd/template/get.go b/cmd/tink-cli/cmd/template/get.go index fdfb6b535..99f5da295 100644 --- a/cmd/tink-cli/cmd/template/get.go +++ b/cmd/tink-cli/cmd/template/get.go @@ -26,19 +26,18 @@ var GetCmd = &cobra.Command{ if len(args) == 0 { return fmt.Errorf("%v requires an argument", c.UseLine()) } - for _, arg := range args { - if _, err := uuid.Parse(arg); err != nil { - return fmt.Errorf("invalid uuid: %s", arg) - } - } return nil }, Run: func(c *cobra.Command, args []string) { for _, arg := range args { - req := template.GetRequest{ - GetBy: &template.GetRequest_Id{ - Id: arg, - }, + req := template.GetRequest{} + // Parse arg[0] to see if it is a UUID + if _, err := uuid.Parse(arg); err == nil { + // UUID + req.GetBy = &template.GetRequest_Id{Id: arg} + } else { + // String (Name) + req.GetBy = &template.GetRequest_Name{Name: arg} } t, err := client.TemplateClient.GetTemplate(context.Background(), &req) if err != nil { @@ -61,6 +60,14 @@ func (h *getTemplate) RetrieveByID(ctx context.Context, cl *client.FullClient, r }) } +func (h *getTemplate) RetrieveByName(_ context.Context, cl *client.FullClient, requestName string) (interface{}, error) { + return cl.TemplateClient.GetTemplate(context.Background(), &template.GetRequest{ + GetBy: &template.GetRequest_Name{ + Name: requestName, + }, + }) +} + func (h *getTemplate) RetrieveData(ctx context.Context, cl *client.FullClient) ([]interface{}, error) { list, err := cl.TemplateClient.ListTemplates(ctx, &template.ListRequest{ FilterBy: &template.ListRequest_Name{ @@ -98,9 +105,10 @@ func (h *getTemplate) PopulateTable(data []interface{}, t table.Writer) error { func NewGetOptions() get.Options { h := getTemplate{} return get.Options{ - Headers: []string{"ID", "Name", "Created At", "Updated At"}, - RetrieveByID: h.RetrieveByID, - RetrieveData: h.RetrieveData, - PopulateTable: h.PopulateTable, + Headers: []string{"ID", "Name", "Created At", "Updated At"}, + RetrieveByID: h.RetrieveByID, + RetrieveByName: h.RetrieveByName, + RetrieveData: h.RetrieveData, + PopulateTable: h.PopulateTable, } } diff --git a/db/hardware_test.go b/db/hardware_test.go index cf878e603..f85d155bf 100644 --- a/db/hardware_test.go +++ b/db/hardware_test.go @@ -320,7 +320,7 @@ func TestGetByID_WithNonExistingID(t *testing.T) { // TODO: use errors.Is here if !strings.Contains(err.Error(), want) { - t.Error(fmt.Errorf("unexpected output, looking for %q as a substring in %q", want, err.Error())) //nolint:errorlint // non-wrapping format verb for fmt.Errorf + t.Error(fmt.Errorf("unexpected output, looking for %q as a substring in %q", want, err.Error())) } } @@ -433,7 +433,7 @@ func TestGetByIP_WithNonExistingIP(t *testing.T) { want := "no rows in result set" if !strings.Contains(err.Error(), want) { - t.Error(fmt.Errorf("unexpected output, looking for %q as a substring in %q", want, err.Error())) //nolint:errorlint // non-wrapping format verb for fmt.Errorf + t.Error(fmt.Errorf("unexpected output, looking for %q as a substring in %q", want, err.Error())) } } @@ -544,7 +544,7 @@ func TestGetByMAC_WithNonExistingMAC(t *testing.T) { want := "no rows in result set" if !strings.Contains(err.Error(), want) { - t.Error(fmt.Errorf("unexpected output, looking for %q as a substring in %q", want, err.Error())) //nolint:errorlint // non-wrapping format verb for fmt.Errorf + t.Error(fmt.Errorf("unexpected output, looking for %q as a substring in %q", want, err.Error())) } } diff --git a/db/template_test.go b/db/template_test.go index 28184ba08..7e84195db 100644 --- a/db/template_test.go +++ b/db/template_test.go @@ -362,7 +362,7 @@ func TestGetTemplateWithInvalidID(t *testing.T) { want := "no rows in result set" // TODO: replace with errors.Is if !strings.Contains(err.Error(), want) { - t.Error(fmt.Errorf("unexpected output, looking for %q as a substring in %q", want, err.Error())) // nolint:errorlint // non-wrapping format verb for fmt.Errorf + t.Error(fmt.Errorf("unexpected output, looking for %q as a substring in %q", want, err.Error())) } } diff --git a/go.mod b/go.mod index 7cd658c70..0657e5439 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,8 @@ require ( github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.7.0 github.com/testcontainers/testcontainers-go v0.11.1 + github.com/tinkerbell/lint-install v0.0.0-20220502172532-2fdfa62596af + go.mongodb.org/mongo-driver v1.1.2 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.22.0 go.uber.org/multierr v1.7.0 google.golang.org/genproto v0.0.0-20211021150943-2b146023228c @@ -70,7 +72,7 @@ require ( github.com/fatih/color v1.12.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-errors/errors v1.0.1 // indirect - github.com/go-logr/logr v1.2.0 // indirect + github.com/go-logr/logr v1.2.3 // indirect github.com/go-openapi/errors v0.19.2 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect @@ -90,11 +92,13 @@ require ( github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hexops/gotextdiff v1.0.3 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jhump/protoreflect v1.9.1-0.20210817181203-db1a327a393e // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/karrick/godirwalk v1.17.0 // indirect github.com/klauspost/compress v1.13.6 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/ktr0731/grpc-web-go-client v0.2.7 // indirect @@ -129,7 +133,6 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect - go.mongodb.org/mongo-driver v1.1.2 // indirect go.opencensus.io v0.23.0 // indirect go.opentelemetry.io/contrib v0.22.0 // indirect go.opentelemetry.io/otel v1.0.0-RC2 // indirect @@ -160,7 +163,7 @@ require ( k8s.io/api v0.23.0 // indirect k8s.io/apiextensions-apiserver v0.23.0 // indirect k8s.io/component-base v0.23.0 // indirect - k8s.io/klog/v2 v2.30.0 // indirect + k8s.io/klog/v2 v2.60.1 // indirect k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect diff --git a/go.sum b/go.sum index 1a90016f8..66fe6b114 100644 --- a/go.sum +++ b/go.sum @@ -497,8 +497,9 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= @@ -805,6 +806,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= @@ -881,6 +884,9 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= +github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -1377,6 +1383,8 @@ github.com/tetafro/godot v1.4.7/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEo github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tinkerbell/lint-install v0.0.0-20220502172532-2fdfa62596af h1:KlPjxhYanKNUEd0TCjKsyTAqrtZ6HF0El2/+NKhpUHQ= +github.com/tinkerbell/lint-install v0.0.0-20220502172532-2fdfa62596af/go.mod h1:xW1I8Nqsc88qG0zmPLcbQqHE1Co8eo628OJMRerLHFM= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= @@ -2279,8 +2287,10 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= +k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= diff --git a/lint.mk b/lint.mk index b7a7ad112..e2a6b5a03 100644 --- a/lint.mk +++ b/lint.mk @@ -2,11 +2,12 @@ # BEGIN: lint-install --dockerfile=warn -makefile=lint.mk . # http://github.com/tinkerbell/lint-install -GOLINT_VERSION ?= v1.42.0 -HADOLINT_VERSION ?= v2.7.0 -SHELLCHECK_VERSION ?= v0.7.2 -LINT_OS := $(shell uname) +.PHONY: lint +lint: _lint + LINT_ARCH := $(shell uname -m) +LINT_OS := $(shell uname) +LINT_OS_LOWER := $(shell echo $(LINT_OS) | tr '[:upper:]' '[:lower:]') LINT_ROOT := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) # shellcheck and hadolint lack arm64 native binaries: rely on x86-64 emulation @@ -16,31 +17,76 @@ ifeq ($(LINT_OS),Darwin) endif endif -LINT_LOWER_OS = $(shell echo $(LINT_OS) | tr '[:upper:]' '[:lower:]') -GOLINT_CONFIG:=$(LINT_ROOT)/.golangci.yml +LINTERS := +FIXERS := -lint: out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH)/shellcheck out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH) - out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH) run - out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) --no-fail $(shell find . -name "*Dockerfile") - out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH)/shellcheck $(shell find . -name "*.sh") +SHELLCHECK_VERSION ?= v0.8.0 +SHELLCHECK_BIN := out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH) +$(SHELLCHECK_BIN): + mkdir -p out/linters + curl -sSfL -o $@.tar.xz https://github.com/koalaman/shellcheck/releases/download/$(SHELLCHECK_VERSION)/shellcheck-$(SHELLCHECK_VERSION).$(LINT_OS_LOWER).$(LINT_ARCH).tar.xz \ + || echo "Unable to fetch shellcheck for $(LINT_OS)/$(LINT_ARCH): falling back to locally install" + test -f $@.tar.xz \ + && tar -C out/linters -xJf $@.tar.xz \ + && mv out/linters/shellcheck-$(SHELLCHECK_VERSION)/shellcheck $@ \ + || printf "#!/usr/bin/env shellcheck\n" > $@ + chmod u+x $@ + +LINTERS += shellcheck-lint +shellcheck-lint: $(SHELLCHECK_BIN) + $(SHELLCHECK_BIN) $(shell find . -name "*.sh") -fix: out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH)/shellcheck out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH) - out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH) run --fix - out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH)/shellcheck $(shell find . -name "*.sh") -f diff | git apply -p2 - +FIXERS += shellcheck-fix +shellcheck-fix: $(SHELLCHECK_BIN) + $(SHELLCHECK_BIN) $(shell find . -name "*.sh") -f diff | { read -t 1 line || exit 0; { echo "$$line" && cat; } | git apply -p2; } -out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH)/shellcheck: +HADOLINT_VERSION ?= v2.8.0 +HADOLINT_BIN := out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) +$(HADOLINT_BIN): mkdir -p out/linters - curl -sSfL https://github.com/koalaman/shellcheck/releases/download/$(SHELLCHECK_VERSION)/shellcheck-$(SHELLCHECK_VERSION).$(LINT_LOWER_OS).$(LINT_ARCH).tar.xz | tar -C out/linters -xJf - - mv out/linters/shellcheck-$(SHELLCHECK_VERSION) out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH) + curl -sSfL -o $@.dl https://github.com/hadolint/hadolint/releases/download/$(HADOLINT_VERSION)/hadolint-$(LINT_OS)-$(LINT_ARCH) \ + || echo "Unable to fetch hadolint for $(LINT_OS)/$(LINT_ARCH), falling back to local install" + test -f $@.dl && mv $(HADOLINT_BIN).dl $@ || printf "#!/usr/bin/env hadolint\n" > $@ + chmod u+x $@ + +LINTERS += hadolint-lint +hadolint-lint: $(HADOLINT_BIN) + $(HADOLINT_BIN) --no-fail $(shell find . -name "*Dockerfile") -out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH): +GOLANGCI_LINT_CONFIG := $(LINT_ROOT)/.golangci.yml +GOLANGCI_LINT_VERSION ?= v1.43.0 +GOLANGCI_LINT_BIN := out/linters/golangci-lint-$(GOLANGCI_LINT_VERSION)-$(LINT_ARCH) +$(GOLANGCI_LINT_BIN): mkdir -p out/linters - curl -sfL https://github.com/hadolint/hadolint/releases/download/v2.6.1/hadolint-$(LINT_OS)-$(LINT_ARCH) > out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) - chmod u+x out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) + rm -rf out/linters/golangci-lint-* + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b out/linters $(GOLANGCI_LINT_VERSION) + mv out/linters/golangci-lint $@ -out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH): +LINTERS += golangci-lint-lint +golangci-lint-lint: $(GOLANGCI_LINT_BIN) + $(GOLANGCI_LINT_BIN) run + +FIXERS += golangci-lint-fix +golangci-lint-fix: $(GOLANGCI_LINT_BIN) + $(GOLANGCI_LINT_BIN) run --fix + +YAMLLINT_VERSION ?= 1.26.3 +YAMLLINT_ROOT := out/linters/yamllint-$(YAMLLINT_VERSION) +YAMLLINT_BIN := $(YAMLLINT_ROOT)/dist/bin/yamllint +$(YAMLLINT_BIN): mkdir -p out/linters - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b out/linters $(GOLINT_VERSION) - mv out/linters/golangci-lint out/linters/golangci-lint-$(GOLINT_VERSION)-$(LINT_ARCH) + rm -rf out/linters/yamllint-* + curl -sSfL https://github.com/adrienverge/yamllint/archive/refs/tags/v$(YAMLLINT_VERSION).tar.gz | tar -C out/linters -zxf - + cd $(YAMLLINT_ROOT) && pip3 install --target dist . || pip install --target dist . + +LINTERS += yamllint-lint +yamllint-lint: $(YAMLLINT_BIN) + PYTHONPATH=$(YAMLLINT_ROOT)/dist $(YAMLLINT_ROOT)/dist/bin/yamllint . + +.PHONY: _lint $(LINTERS) +_lint: $(LINTERS) + +.PHONY: fix $(FIXERS) +fix: $(FIXERS) # END: lint-install --dockerfile=warn -makefile=lint.mk . diff --git a/shell.nix b/shell.nix index 566986d6d..4ad4fdc8c 100644 --- a/shell.nix +++ b/shell.nix @@ -20,6 +20,8 @@ mkShell { nodePackages.prettier protobuf python3Packages.codespell + python3Packages.pip + python3Packages.setuptools shellcheck shfmt vagrant diff --git a/tools.go b/tools.go index ec5891f0a..c7fa89eae 100644 --- a/tools.go +++ b/tools.go @@ -6,6 +6,7 @@ package tools import ( _ "github.com/bufbuild/buf/cmd/buf" _ "github.com/matryer/moq" + _ "github.com/tinkerbell/lint-install" _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" _ "google.golang.org/protobuf/cmd/protoc-gen-go" _ "mvdan.cc/gofumpt"