From 69d1bed295dea7ecab36a45993a8c6a10d60a1ad Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Mon, 23 Sep 2024 23:45:19 +0200 Subject: [PATCH 1/3] feat: Stub lint command Signed-off-by: Kim Christensen --- cmd/skeletor/lint.go | 17 +++++ cmd/skeletor/main.go | 1 + pkg/skeletor/build.go | 5 +- pkg/skeletor/lint.go | 75 +++++++++++++++++++ pkg/skeletor/lint_test.go | 55 ++++++++++++++ .../testdata/actions-input-fail-lint.yaml | 12 +++ pkg/skeletor/testdata/actions-input.yaml | 11 +++ 7 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 cmd/skeletor/lint.go create mode 100644 pkg/skeletor/lint.go create mode 100644 pkg/skeletor/lint_test.go create mode 100644 pkg/skeletor/testdata/actions-input-fail-lint.yaml create mode 100644 pkg/skeletor/testdata/actions-input.yaml diff --git a/cmd/skeletor/lint.go b/cmd/skeletor/lint.go new file mode 100644 index 0000000..7406174 --- /dev/null +++ b/cmd/skeletor/lint.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/getporter/skeletor/pkg/skeletor" + "github.com/spf13/cobra" +) + +func buildLintCommand(m *skeletor.Mixin) *cobra.Command { + cmd := &cobra.Command{ + Use: "lint", + Short: "Execute the lint functionality of this mixin", + RunE: func(cmd *cobra.Command, args []string) error { + return m.PrintLintResults(cmd.Context()) + }, + } + return cmd +} diff --git a/cmd/skeletor/main.go b/cmd/skeletor/main.go index 9073d65..07078fa 100644 --- a/cmd/skeletor/main.go +++ b/cmd/skeletor/main.go @@ -73,6 +73,7 @@ func buildRootCommand(m *skeletor.Mixin, in io.Reader) *cobra.Command { cmd.AddCommand(buildInvokeCommand(m)) cmd.AddCommand(buildUpgradeCommand(m)) cmd.AddCommand(buildUninstallCommand(m)) + cmd.AddCommand(buildLintCommand(m)) return cmd } diff --git a/pkg/skeletor/build.go b/pkg/skeletor/build.go index f1eacf5..2c306cc 100644 --- a/pkg/skeletor/build.go +++ b/pkg/skeletor/build.go @@ -7,9 +7,10 @@ import ( "gopkg.in/yaml.v2" ) -// BuildInput represents stdin passed to the mixin for the build command. +// BuildInput represents stdin passed to the mixin for the build and lint commands. type BuildInput struct { - Config MixinConfig + Config MixinConfig + Actions Actions `yaml:"actions"` } // MixinConfig represents configuration that can be set on the skeletor mixin in porter.yaml diff --git a/pkg/skeletor/lint.go b/pkg/skeletor/lint.go new file mode 100644 index 0000000..0324ccf --- /dev/null +++ b/pkg/skeletor/lint.go @@ -0,0 +1,75 @@ +package skeletor + +import ( + "context" + "fmt" + + "get.porter.sh/porter/pkg/encoding" + "get.porter.sh/porter/pkg/exec/builder" + "get.porter.sh/porter/pkg/linter" + + "gopkg.in/yaml.v2" +) + +const mixinName = "skeletor" + +const ( + // CodeInvalidName is the linter code for when the name of the step is invalid. + CodeInvalidName linter.Code = "skeletor-100" +) + +func (m *Mixin) Lint(ctx context.Context) (linter.Results, error) { + var input BuildInput + err := builder.LoadAction(ctx, m.RuntimeConfig, "", func(contents []byte) (interface{}, error) { + err := yaml.Unmarshal(contents, &input) + return &input, err + }) + if err != nil { + return nil, err + } + + results := make(linter.Results, 0) + + for _, action := range input.Actions { + for stepNumber, step := range action.Steps { + // TODO: Replace with your own linting logic + if step.Name != "invalid" { + continue + } + + result := linter.Result{ + Level: linter.LevelError, + Code: CodeInvalidName, + Location: linter.Location{ + Action: action.Name, + Mixin: mixinName, + StepNumber: stepNumber + 1, // We index from 1 for natural counting, 1st, 2nd, etc. + StepDescription: step.Description, + }, + Title: "Invalid name", + Message: "The name cannot be 'invalid'", + URL: "", + } + results = append(results, result) + } + } + return results, nil +} + +func (m *Mixin) PrintLintResults(ctx context.Context) error { + results, err := m.Lint(ctx) + if err != nil { + return err + } + + b, err := encoding.MarshalJson(results) + if err != nil { + return fmt.Errorf("could not marshal lint results %#v: %w", results, err) + } + + // Print the results as json to stdout for Porter to read + resultsJson := string(b) + fmt.Fprintln(m.Config.Out, resultsJson) + + return nil +} diff --git a/pkg/skeletor/lint_test.go b/pkg/skeletor/lint_test.go new file mode 100644 index 0000000..96e2568 --- /dev/null +++ b/pkg/skeletor/lint_test.go @@ -0,0 +1,55 @@ +package skeletor + +import ( + "bytes" + "context" + "os" + "testing" + + "get.porter.sh/porter/pkg/linter" + + "github.com/stretchr/testify/require" +) + +func TestMixin_Lint(t *testing.T) { + testcases := []struct { + name string // Test case name + file string // Path to the test input yaml + wantResults linter.Results // Indicates the wanted lint result + }{ + {"valid file", "testdata/actions-input.yaml", nil}, + {"invalid name", "testdata/actions-input-fail-lint.yaml", linter.Results{ + linter.Result{ + Level: linter.LevelError, + Location: linter.Location{ + Action: "install", + Mixin: "skeletor", + StepNumber: 1, + StepDescription: "Summon Minion", + }, + Code: CodeInvalidName, + Title: "Invalid name", + Message: "The name cannot be 'invalid'", + }, + }}, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + m := NewTestMixin(t) + mixinInputB, err := os.ReadFile(tc.file) + require.NoError(t, err) + + m.In = bytes.NewBuffer(mixinInputB) + + results, err := m.Lint(ctx) + require.NoError(t, err, "lint failed") + + require.Len(t, results, len(tc.wantResults)) + for _, wantResult := range tc.wantResults { + require.Contains(t, results, wantResult) + } + }) + } +} diff --git a/pkg/skeletor/testdata/actions-input-fail-lint.yaml b/pkg/skeletor/testdata/actions-input-fail-lint.yaml new file mode 100644 index 0000000..7454f83 --- /dev/null +++ b/pkg/skeletor/testdata/actions-input-fail-lint.yaml @@ -0,0 +1,12 @@ +actions: + install: + - skeletor: + name: "invalid" + description: "Summon Minion" + arguments: + - "man-e-faces" + flags: + species: "human" + outputs: + - name: "VICTORY" + jsonPath: "$Id" diff --git a/pkg/skeletor/testdata/actions-input.yaml b/pkg/skeletor/testdata/actions-input.yaml new file mode 100644 index 0000000..6e27827 --- /dev/null +++ b/pkg/skeletor/testdata/actions-input.yaml @@ -0,0 +1,11 @@ +actions: + install: + - skeletor: + description: "Summon Minion" + arguments: + - "man-e-faces" + flags: + species: "human" + outputs: + - name: "VICTORY" + jsonPath: "$Id" From 876f7dd9971711f88802d92107f40e442cdd579f Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Mon, 23 Sep 2024 23:49:00 +0200 Subject: [PATCH 2/3] chore: Stop using deprecated ioutil package Signed-off-by: Kim Christensen --- pkg/skeletor/action_test.go | 4 ++-- pkg/skeletor/execute_test.go | 3 +-- pkg/skeletor/schema_test.go | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/skeletor/action_test.go b/pkg/skeletor/action_test.go index 62f4eff..917cc35 100644 --- a/pkg/skeletor/action_test.go +++ b/pkg/skeletor/action_test.go @@ -1,7 +1,7 @@ package skeletor import ( - "io/ioutil" + "os" "testing" "get.porter.sh/porter/pkg/exec/builder" @@ -12,7 +12,7 @@ import ( ) func TestMixin_UnmarshalStep(t *testing.T) { - b, err := ioutil.ReadFile("testdata/step-input.yaml") + b, err := os.ReadFile("testdata/step-input.yaml") require.NoError(t, err) var action Action diff --git a/pkg/skeletor/execute_test.go b/pkg/skeletor/execute_test.go index 13da685..7fdd991 100644 --- a/pkg/skeletor/execute_test.go +++ b/pkg/skeletor/execute_test.go @@ -3,7 +3,6 @@ package skeletor import ( "bytes" "context" - "io/ioutil" "os" "path" "testing" @@ -36,7 +35,7 @@ func TestMixin_Execute(t *testing.T) { m := NewTestMixin(t) m.Setenv(test.ExpectedCommandEnv, tc.wantCommand) - mixinInputB, err := ioutil.ReadFile(tc.file) + mixinInputB, err := os.ReadFile(tc.file) require.NoError(t, err) m.In = bytes.NewBuffer(mixinInputB) diff --git a/pkg/skeletor/schema_test.go b/pkg/skeletor/schema_test.go index dfb194f..8a40675 100644 --- a/pkg/skeletor/schema_test.go +++ b/pkg/skeletor/schema_test.go @@ -2,7 +2,7 @@ package skeletor import ( "fmt" - "io/ioutil" + "os" "testing" "github.com/ghodss/yaml" // We are not using go-yaml because of serialization problems with jsonschema, don't use this library elsewhere @@ -17,7 +17,7 @@ func TestMixin_PrintSchema(t *testing.T) { m.PrintSchema() gotSchema := m.TestContext.GetOutput() - wantSchema, err := ioutil.ReadFile("schema/schema.json") + wantSchema, err := os.ReadFile("schema/schema.json") require.NoError(t, err) assert.Equal(t, string(wantSchema), gotSchema) @@ -41,7 +41,7 @@ func TestMixin_ValidateSchema(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { // Read the mixin input as a go dump - mixinInputB, err := ioutil.ReadFile(tc.file) + mixinInputB, err := os.ReadFile(tc.file) require.NoError(t, err) mixinInputMap := make(map[string]interface{}) err = yaml.Unmarshal(mixinInputB, &mixinInputMap) From a9024a0b3e98a2bd32b411b5626b664aae45df6b Mon Sep 17 00:00:00 2001 From: Kim Christensen Date: Tue, 24 Sep 2024 00:12:03 +0200 Subject: [PATCH 3/3] fix: Correctly install Go in Github Actions Signed-off-by: Kim Christensen --- .github/workflows/skeletor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/skeletor.yml b/.github/workflows/skeletor.yml index e8385fb..28784be 100644 --- a/.github/workflows/skeletor.yml +++ b/.github/workflows/skeletor.yml @@ -19,7 +19,7 @@ jobs: with: fetch-depth: 0 - name: Install Go - uses: actions/checkout@v4 + uses: actions/setup-go@v4 with: go-version-file: go.mod cache: true