From e63b6e569a9f0433c8b8d3c7682c5bb96a534b2c Mon Sep 17 00:00:00 2001 From: 7h3-3mp7y-m4n Date: Fri, 11 Apr 2025 01:20:16 +0530 Subject: [PATCH 1/5] ci: add go-static-check Signed-off-by: 7h3-3mp7y-m4n --- .github/workflows/ci.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 42d46911..a20e7fc4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,15 +18,17 @@ jobs: - name: Fetch all tags run: git fetch --force --tags - name: Set up Go - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' - name: Run Go Vet - run: go vet ./... + run: go vet ./... + - name: Run Go Static Check + run: go install honnef.co/go/tools/cmd/staticcheck@latest && staticcheck ./... - name: Run Go Tests run: go test ./... -cover -race - name: Build binary - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 + uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 with: distribution: goreleaser version: '~> v2' From 5b6556a412b6c3d16ffdf90928955518d2bf3b73 Mon Sep 17 00:00:00 2001 From: 7h3-3mp7y-m4n Date: Thu, 22 May 2025 22:21:27 +0530 Subject: [PATCH 2/5] add golagci-lint Signed-off-by: 7h3-3mp7y-m4n --- .github/workflows/ci.yaml | 8 ++++---- .golangci.yml | 32 ++++++++++++++++++++++++++++++++ internal/command/resources.go | 2 +- internal/compose/convert_test.go | 5 +++-- internal/provisioners/core.go | 2 +- 5 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 .golangci.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a20e7fc4..720c24f8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -21,10 +21,10 @@ jobs: uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' - - name: Run Go Vet - run: go vet ./... - - name: Run Go Static Check - run: go install honnef.co/go/tools/cmd/staticcheck@latest && staticcheck ./... + - name: Install golangci-lint + run: + - name: Run golangci-lint + run: golangci-lint run - name: Run Go Tests run: go test ./... -cover -race - name: Build binary diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..4af4e748 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,32 @@ +version: "2" + +run: + tests: true + +linters: + default: standard + enable: + - bodyclose + - contextcheck + - goconst + - gosec + - mnd + - testifylint + - govet + - staticcheck + +issues: + exclude-rules: + - path: _test\.go + linters: + - gosec + max-issues-per-linter: 0 + max-same-issues: 0 + +output: + formats: + text: + path: stdout + print-linter-name: true + print-issued-lines: true + show-stats: false \ No newline at end of file diff --git a/internal/command/resources.go b/internal/command/resources.go index 78f38eb7..6d189141 100644 --- a/internal/command/resources.go +++ b/internal/command/resources.go @@ -113,7 +113,7 @@ func getResourceOutputsKeys(uid framework.ResourceUid, state *project.State) ([] return nil, err } keys := make([]string, 0, len(outputs)) - for key, _ := range outputs { + for key := range outputs { keys = append(keys, key) } slices.Sort(keys) diff --git a/internal/compose/convert_test.go b/internal/compose/convert_test.go index 3ebdb2e0..3e6dd5c4 100644 --- a/internal/compose/convert_test.go +++ b/internal/compose/convert_test.go @@ -15,6 +15,7 @@ package compose import ( + "context" "errors" "fmt" "os" @@ -449,9 +450,9 @@ func TestScoreConvert(t *testing.T) { evt := new(envprov.Provisioner) state.Resources["environment.default#test.env"] = framework.ScoreResourceState[framework.NoExtras]{OutputLookupFunc: evt.LookupOutput} - po, _ := evt.GenerateSubProvisioner("app-db", "").Provision(nil, nil) + po, _ := evt.GenerateSubProvisioner("app-db", "").Provision(context.TODO(), nil) state.Resources["mysql.default#test.app-db"] = framework.ScoreResourceState[framework.NoExtras]{OutputLookupFunc: po.OutputLookupFunc} - po, _ = evt.GenerateSubProvisioner("some-dns", "").Provision(nil, nil) + po, _ = evt.GenerateSubProvisioner("some-dns", "").Provision(context.TODO(), nil) state.Resources["dns.default#test.some-dns"] = framework.ScoreResourceState[framework.NoExtras]{OutputLookupFunc: po.OutputLookupFunc} state.Resources["volume.default#test.data"] = framework.ScoreResourceState[framework.NoExtras]{Outputs: map[string]interface{}{ "type": "volume", diff --git a/internal/provisioners/core.go b/internal/provisioners/core.go index e77a931b..2323f205 100644 --- a/internal/provisioners/core.go +++ b/internal/provisioners/core.go @@ -322,7 +322,7 @@ func ProvisionResources(ctx context.Context, state *project.State, provisioners } var params map[string]interface{} - if resState.Params != nil && len(resState.Params) > 0 { + if len(resState.Params) > 0 { resOutputs, err := out.GetResourceOutputForWorkload(resState.SourceWorkload) if err != nil { return nil, fmt.Errorf("failed to find resource params for resource '%s': %w", resUid, err) From 02459e2914c309d0fdd671a73dccaa53c60c1d56 Mon Sep 17 00:00:00 2001 From: 7h3-3mp7y-m4n Date: Wed, 11 Jun 2025 00:09:32 +0530 Subject: [PATCH 3/5] minor fix for lint Signed-off-by: 7h3-3mp7y-m4n --- .golangci.yml | 47 +++++++++++++++++++++++++---------------------- Makefile | 6 +++++- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 4af4e748..0cb54e42 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,32 +1,35 @@ version: "2" - +modules-download-mode: readonly run: - tests: true - + tests: false linters: - default: standard enable: - - bodyclose + - govet + - staticcheck + - unused - contextcheck - goconst - gosec - - mnd - testifylint - - govet - - staticcheck - -issues: - exclude-rules: - - path: _test\.go - linters: - - gosec - max-issues-per-linter: 0 - max-same-issues: 0 + - errcheck + - revive + - mnd + - bodyclose +linters-settings: + errcheck: + check-type-assertions: true + check-blank: true + revive: + severity: warning + confidence: 0.8 + rules: + - name: indent-error-flow + - name: var-naming + - name: import-shadowing output: - formats: - text: - path: stdout - print-linter-name: true - print-issued-lines: true - show-stats: false \ No newline at end of file + formats: colored-line-number + print-issued-lines: true + print-linter-name: true + uniq-by-line: false + sort-results: true \ No newline at end of file diff --git a/Makefile b/Makefile index 90c1647a..c5bdac83 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,14 @@ MAKEFLAGS += --no-builtin-rules .SUFFIXES: ## Display a list of the documented make targets -.PHONY: help +.PHONY: help lint help: @echo Documented Make targets: @perl -e 'undef $$/; while (<>) { while ($$_ =~ /## (.*?)(?:\n# .*)*\n.PHONY:\s+(\S+).*/mg) { printf "\033[36m%-30s\033[0m %s\n", $$2, $$1 } }' $(MAKEFILE_LIST) | sort + +lint: + @echo "Running golangci-lint..." + golangci-lint run ./... .PHONY: .FORCE .FORCE: From 116bbf2aaa9e46efb4eea9258e3c59240d8cdaef Mon Sep 17 00:00:00 2001 From: 7h3-3mp7y-m4n Date: Wed, 11 Jun 2025 00:30:58 +0530 Subject: [PATCH 4/5] fix ci Signed-off-by: 7h3-3mp7y-m4n --- .github/workflows/ci.yaml | 3 ++- .golangci.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f59f6b48..0678d20f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,8 @@ jobs: with: go-version-file: 'go.mod' - name: Install golangci-lint - run: + run: | + go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 - name: Run golangci-lint run: golangci-lint run - name: Run Go Tests diff --git a/.golangci.yml b/.golangci.yml index 0cb54e42..326e6960 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -28,7 +28,8 @@ linters-settings: - name: var-naming - name: import-shadowing output: - formats: colored-line-number + formats: + colored-line-number: true print-issued-lines: true print-linter-name: true uniq-by-line: false From 11aef8613a3059fb14959fbbdb348426479e98cc Mon Sep 17 00:00:00 2001 From: 7h3-3mp7y-m4n Date: Sat, 14 Jun 2025 21:44:18 +0530 Subject: [PATCH 5/5] minor changes and fix Signed-off-by: 7h3-3mp7y-m4n --- .golangci.yml | 24 ++++++++++++-------- internal/command/generate.go | 4 ++-- internal/command/init.go | 6 ++--- internal/command/run.go | 18 +++++++++------ internal/compose/convert.go | 13 +++++++---- internal/logging/logging.go | 2 +- internal/provisioners/cmdprov/commandprov.go | 2 +- internal/provisioners/core.go | 6 ++--- 8 files changed, 43 insertions(+), 32 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 326e6960..f530aa88 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,32 +1,36 @@ version: "2" modules-download-mode: readonly + run: tests: false + linters: enable: - govet - staticcheck - unused - contextcheck - - goconst - gosec - testifylint - errcheck - - revive - - mnd - bodyclose + - misspell linters-settings: errcheck: check-type-assertions: true check-blank: true - revive: - severity: warning - confidence: 0.8 - rules: - - name: indent-error-flow - - name: var-naming - - name: import-shadowing + exclude-functions: + - (os.File).Close + - (io.Closer).Close + misspell: + locale: US + gosec: + exclude: + - G304 + - G301 + - G306 + - G115 output: formats: colored-line-number: true diff --git a/internal/command/generate.go b/internal/command/generate.go index 7847e43b..8222c8c2 100644 --- a/internal/command/generate.go +++ b/internal/command/generate.go @@ -374,7 +374,7 @@ arguments. return fmt.Errorf("no output file specified") } else if v == "-" { _, _ = fmt.Fprint(cmd.OutOrStdout(), string(raw)) - } else if err := os.WriteFile(v+".temp", raw, 0644); err != nil { + } else if err := os.WriteFile(v+".temp", raw, 0600); err != nil { return fmt.Errorf("failed to write output file: %w", err) } else if err := os.Rename(v+".temp", v); err != nil { return fmt.Errorf("failed to complete writing output file: %w", err) @@ -388,7 +388,7 @@ arguments. _, _ = content.WriteRune('\n') } slog.Info(fmt.Sprintf("Writing env var file to '%s'", v)) - if err := os.WriteFile(v, []byte(content.String()), 0644); err != nil { + if err := os.WriteFile(v, []byte(content.String()), 0600); err != nil { return fmt.Errorf("failed to write env var file: %w", err) } } diff --git a/internal/command/init.go b/internal/command/init.go index 8b66d3f8..50df13da 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -222,11 +222,11 @@ URI Retrieval: return fmt.Errorf("failed to check for existing default provisioners file: %w", err) } - f, err := os.OpenFile(defaultProvisioners, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) + f, err := os.OpenFile(defaultProvisioners, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) if err != nil { return fmt.Errorf("failed to create default provisioners file: %w", err) } - defer f.Close() + defer f.Close() //nolint:errcheck slog.Info("Writing default provisioners file", "path", defaultProvisioners) if _, err := f.WriteString(defaultProvisionersContent); err != nil { @@ -248,7 +248,7 @@ URI Retrieval: return fmt.Errorf("failed to check existing Score file: %w", err) } slog.Info(fmt.Sprintf("Initial Score file '%s' does not exist - creating it", initCmdScoreFile)) - if err := os.WriteFile(initCmdScoreFile+".temp", []byte(DefaultScoreFileContent), 0755); err != nil { + if err := os.WriteFile(initCmdScoreFile+".temp", []byte(DefaultScoreFileContent), 0755); err != nil { //nolint:gosec return fmt.Errorf("failed to write initial score file: %w", err) } else if err := os.Rename(initCmdScoreFile+".temp", initCmdScoreFile); err != nil { return fmt.Errorf("failed to complete writing initial Score file: %w", err) diff --git a/internal/command/run.go b/internal/command/run.go index b4acdec5..c94bccfb 100644 --- a/internal/command/run.go +++ b/internal/command/run.go @@ -98,7 +98,7 @@ func run(cmd *cobra.Command, args []string) error { if src, err = os.Open(scoreFile); err != nil { return err } - defer src.Close() + defer src.Close() //nolint:errcheck // Parse SCORE spec // @@ -112,7 +112,7 @@ func run(cmd *cobra.Command, args []string) error { // if overridesFile != "" { if ovr, err := os.Open(overridesFile); err == nil { - defer ovr.Close() + defer ovr.Close() //nolint:errcheck slog.Info(fmt.Sprintf("Loading Score overrides file '%s'", overridesFile)) var ovrMap map[string]interface{} @@ -223,16 +223,20 @@ func run(cmd *cobra.Command, args []string) error { // Deprecated behavior of the run command which used to publish ports // Todo: remove this once score-compose run is removed if spec.Service != nil && len(spec.Service.Ports) > 0 { - ports := make([]types.ServicePortConfig, 0) + ports := make([]types.ServicePortConfig, 0, len(spec.Service.Ports)) for _, pSpec := range spec.Service.Ports { - var pubPort = fmt.Sprintf("%v", pSpec.Port) + port := util.DerefOr(pSpec.TargetPort, pSpec.Port) + if port < 0 || port > 65535 { + return fmt.Errorf("invalid port number: %d", port) + } + pubPort := fmt.Sprintf("%v", pSpec.Port) var protocol string if pSpec.Protocol != nil { protocol = strings.ToLower(string(*pSpec.Protocol)) } ports = append(ports, types.ServicePortConfig{ Published: pubPort, - Target: uint32(util.DerefOr(pSpec.TargetPort, pSpec.Port)), + Target: uint32(port), // #nosec G115 Protocol: protocol, }) } @@ -270,7 +274,7 @@ func run(cmd *cobra.Command, args []string) error { if err != nil { return err } - defer destFile.Close() + defer destFile.Close() //nolint:errcheck dest = io.MultiWriter(dest, destFile) } @@ -289,7 +293,7 @@ func run(cmd *cobra.Command, args []string) error { if err != nil { return err } - defer dest.Close() + defer dest.Close() //nolint:errcheck // Write .env file // diff --git a/internal/compose/convert.go b/internal/compose/convert.go index 0222c27c..e5cb45fb 100644 --- a/internal/compose/convert.go +++ b/internal/compose/convert.go @@ -21,10 +21,10 @@ import ( "errors" "fmt" "log/slog" - "os" - "slices" "maps" + "os" "path/filepath" + "slices" "sort" "strconv" "strings" @@ -107,7 +107,7 @@ func ConvertSpec(state *project.State, spec *score.Workload) (*compose.Project, if len(cSpec.Volumes) > 0 { volumes = make([]compose.ServiceVolumeConfig, 0, len(cSpec.Volumes)) for _, target := range slices.Sorted(maps.Keys(cSpec.Volumes)) { - vol := cSpec.Volumes[target] + vol := cSpec.Volumes[target] cfg, err := convertVolumeSourceIntoVolume(state, deferredSubstitutionFunction, workloadName, target, vol) if err != nil { return nil, fmt.Errorf("containers.%s.volumes[%s]: %w", containerName, target, err) @@ -237,11 +237,11 @@ func convertFilesIntoVolumes(state *project.State, workloadName string, containe var err error filesDir := filepath.Join(mountsDirectory, "files") - if err = os.MkdirAll(filesDir, 0755); err != nil && !errors.Is(err, os.ErrExist) { + if err = os.MkdirAll(filesDir, 0750); err != nil && !errors.Is(err, os.ErrExist) { return nil, fmt.Errorf("failed to ensure the files directory exists") } for _, target := range slices.Sorted(maps.Keys(input)) { - file := input[target] + file := input[target] var content []byte if file.Content != nil { content = []byte(*file.Content) @@ -250,6 +250,9 @@ func convertFilesIntoVolumes(state *project.State, workloadName string, containe if !filepath.IsAbs(sourcePath) && state.Workloads[workloadName].File != nil { sourcePath = filepath.Join(filepath.Dir(*state.Workloads[workloadName].File), sourcePath) } + if !filepath.IsAbs(sourcePath) { + return nil, fmt.Errorf("invalid source path: must be absolute") + } content, err = os.ReadFile(sourcePath) if err != nil { return nil, fmt.Errorf("containers.%s.files[%s].source: failed to read: %w", containerName, target, err) diff --git a/internal/logging/logging.go b/internal/logging/logging.go index fd9af9b6..d4f1eada 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -36,7 +36,7 @@ func (h *SimpleHandler) Enabled(ctx context.Context, level slog.Level) bool { func (h *SimpleHandler) Handle(ctx context.Context, record slog.Record) error { h.mu.Lock() defer h.mu.Unlock() - _, err := h.Writer.Write([]byte(fmt.Sprintf("%s: %s\n", record.Level.String(), record.Message))) + _, err := fmt.Fprintf(h.Writer, "%s: %s\n", record.Level.String(), record.Message) return err } diff --git a/internal/provisioners/cmdprov/commandprov.go b/internal/provisioners/cmdprov/commandprov.go index 9fa97b6e..00c7068d 100644 --- a/internal/provisioners/cmdprov/commandprov.go +++ b/internal/provisioners/cmdprov/commandprov.go @@ -130,7 +130,7 @@ func (p *Provisioner) Provision(ctx context.Context, input *provisioners.Input) return nil, fmt.Errorf("failed to encode json input: %w", err) } outputBuffer := new(bytes.Buffer) - + // #nosec G204 - bin and args are from trusted internal config cmd := exec.CommandContext(ctx, bin, p.Args...) slog.Debug(fmt.Sprintf("Executing '%s %v' for command provisioner", bin, p.Args)) cmd.Stdin = bytes.NewReader(rawInput) diff --git a/internal/provisioners/core.go b/internal/provisioners/core.go index 2323f205..f0911880 100644 --- a/internal/provisioners/core.go +++ b/internal/provisioners/core.go @@ -209,7 +209,7 @@ func (po *ProvisionOutput) ApplyToStateAndProject(state *project.State, resUid f dst := filepath.Join(state.Extras.MountsDirectory, relativePath) if b { slog.Debug(fmt.Sprintf("Ensuring mount directory '%s' exists", dst)) - if err := os.MkdirAll(dst, 0755); err != nil && !errors.Is(err, os.ErrExist) { + if err := os.MkdirAll(dst, 0750); err != nil && !errors.Is(err, os.ErrExist) { return nil, fmt.Errorf("failed to create volume directory '%s': %w", dst, err) } } else { @@ -228,10 +228,10 @@ func (po *ProvisionOutput) ApplyToStateAndProject(state *project.State, resUid f dst := filepath.Join(state.Extras.MountsDirectory, relativePath) if b != nil { slog.Debug(fmt.Sprintf("Ensuring mount file '%s' exists", dst)) - if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil && !errors.Is(err, os.ErrExist) { + if err := os.MkdirAll(filepath.Dir(dst), 0750); err != nil && !errors.Is(err, os.ErrExist) { return nil, fmt.Errorf("failed to create directories for file '%s': %w", dst, err) } - if err := os.WriteFile(dst, []byte(*b), 0644); err != nil { + if err := os.WriteFile(dst, []byte(*b), 0600); err != nil { return nil, fmt.Errorf("failed to write file '%s': %w", dst, err) } } else {