diff --git a/.envrc b/.envrc deleted file mode 100644 index f310aea..0000000 --- a/.envrc +++ /dev/null @@ -1,4 +0,0 @@ -has nix && use nix -dotenv_if_exists -PATH_add bin -path_add GOBIN bin diff --git a/.github/mergify.yml b/.github/mergify.yml deleted file mode 100644 index 10834a9..0000000 --- a/.github/mergify.yml +++ /dev/null @@ -1,26 +0,0 @@ -queue_rules: - - name: default - conditions: - # Conditions to get out of the queue (= merged) - - check-success=DCO - - check-success=build - -pull_request_rules: - - name: Automatic merge on approval - conditions: - - base=main - - "#approved-reviews-by>=1" - - "#changes-requested-reviews-by=0" - - "#review-requested=0" - - check-success=DCO - - check-success=build - - label!=do-not-merge - - label=ready-to-merge - actions: - queue: - method: merge - name: default - commit_message_template: | - {{ title }} (#{{ number }}) - - {{ body }} diff --git a/.github/settings.yml b/.github/settings.yml deleted file mode 100644 index 3a6228c..0000000 --- a/.github/settings.yml +++ /dev/null @@ -1,28 +0,0 @@ -# Collaborators: give specific users access to this repository. -# See https://docs.github.com/en/rest/reference/repos#add-a-repository-collaborator for available options -collaborators: - # Maintainers, should also be added to the .github/CODEOWNERS file as owners of this settings.yml file. - - username: tstromberg - permission: maintain - - username: chrisdoherty4 - permission: maintain - # Approvers - - username: mmlb - permission: push - - username: stephen-fox - permission: push - # Reviewers - - username: displague - permission: triage - - username: jacobweinstock - permission: triage - - username: Raj-Dharwadkar - permission: triage - - # Note: `permission` is only valid on organization-owned repositories. - # The permission to grant the collaborator. Can be one of: - # * `pull` - can pull, but not push to or administer this repository. - # * `push` - can pull and push, but not administer this repository. - # * `admin` - can pull, push and administer this repository. - # * `maintain` - Recommended for project managers who need to manage the repository without access to sensitive or destructive actions. - # * `triage` - Recommended for contributors who need to proactively manage issues and pull requests without write access. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83a9a80..1dd38c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,6 @@ on: branches: [main] jobs: - build: runs-on: ubuntu-latest steps: @@ -16,7 +15,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.18 + go-version: 1.22 - name: Build run: go build -v ./... diff --git a/.golangci.yml b/.golangci.yml index d3416a8..95eff1a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,208 +1,244 @@ -run: - # The default runtime timeout is 1m, which doesn't work well on Github Actions. - timeout: 4m - -# NOTE: This file is populated by the lint-install tool. Local adjustments may be overwritten. -linters-settings: - cyclop: - # NOTE: This is a very high transitional threshold - max-complexity: 37 - package-average: 34.0 - skip-tests: true - - gocognit: - # NOTE: This is a very high transitional threshold - min-complexity: 98 - - dupl: - threshold: 200 - - goconst: - min-len: 4 - min-occurrences: 5 - ignore-tests: true - - gosec: - excludes: - - G107 # Potential HTTP request made with variable url - - G204 # Subprocess launched with function call as argument or cmd arguments - - G404 # Use of weak random number generator (math/rand instead of crypto/rand - - 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 - - nestif: - min-complexity: 8 - - nolintlint: - require-explanation: true - allow-unused: false - require-specific: true - - revive: - ignore-generated-header: true - severity: warning - rules: - - name: atomic - - name: blank-imports - - name: bool-literal-in-expr - - name: confusing-naming - - name: constant-logical-expr - - name: context-as-argument - - name: context-keys-type - - name: deep-exit - - name: defer - - name: range-val-in-closure - - name: range-val-address - - name: dot-imports - - name: error-naming - - name: error-return - - name: error-strings - - name: errorf - - name: exported - - name: identical-branches - - name: if-return - - name: import-shadowing - - name: increment-decrement - - name: indent-error-flow - - name: indent-error-flow - - name: package-comments - - name: range - - name: receiver-naming - - name: redefines-builtin-id - - name: superfluous-else - - name: struct-tag - - name: time-naming - - name: unexported-naming - - name: unexported-return - - name: unnecessary-stmt - - name: unreachable-code - - name: unused-parameter - - name: var-declaration - - name: var-naming - - name: unconditional-recursion - - name: waitgroup-by-value - - staticcheck: - go: "1.18" - - unused: - go: "1.18" - -output: - sort-results: true +## Golden config for golangci-lint - strict, but within the realm of what Go authors might use. +# +# This is tied to the version of golangci-lint listed in the Makefile, usage with other +# versions of golangci-lint will yield errors and/or false positives. +# +# Docs: https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.reference.yml +# Based heavily on https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322 -linters: - disable-all: true - enable: - - asciicheck - - bodyclose - - cyclop - - dogsled - - dupl - - durationcheck - - errcheck - - errname - - errorlint - - exhaustive - - exportloopref - - forcetypeassert - - gocognit - - goconst - - gocritic - - godot - - gofmt - - gofumpt - - gosec - - goheader - - goimports - - goprintffuncname - - gosimple - - govet - - importas - - ineffassign - - makezero - - misspell - - nakedret - - nestif - - nilerr - - noctx - - nolintlint - - predeclared - # disabling for the initial iteration of the linting tool - # - promlinter - - revive - # - rowserrcheck - disabled because of generics, https://github.com/golangci/golangci-lint/issues/2649 - # - sqlclosecheck - disabled because of generics, https://github.com/golangci/golangci-lint/issues/2649 - - staticcheck - # - structcheck - disabled because of generics, https://github.com/golangci/golangci-lint/issues/2649 - - stylecheck - - thelper - - tparallel - - typecheck - - unconvert - - unparam - - unused - # - wastedassign - disabled because of generics, https://github.com/golangci/golangci-lint/issues/2649 - - whitespace - - # Disabled linters, due to being misaligned with Go practices - # - exhaustivestruct - # - gochecknoglobals - # - gochecknoinits - # - goconst - # - godox - # - goerr113 - # - gomnd - # - lll - # - nlreturn - # - testpackage - # - wsl - # Disabled linters, due to not being relevant to our code base: - # - maligned - # - prealloc "For most programs usage of prealloc will be a premature optimization." - # Disabled linters due to bad error messages or bugs - # - tagliatelle +version: "2" issues: - # Excluding configuration per-path, per-linter, per-text and per-source - exclude-rules: - - path: _test\.go - linters: - - dupl - - errcheck - - forcetypeassert - - gocyclo - - gosec - - noctx - - - path: .*cmd.* - linters: - - noctx - - - path: main\.go - linters: - - noctx - - - path: .*cmd.* - text: "deep-exit" - - - path: main\.go - text: "deep-exit" - - # This check is of questionable value - - linters: - - tparallel - text: "call t.Parallel on the top level as well as its subtests" - - # Don't hide lint issues just because there are many of them - max-same-issues: 0 + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. max-issues-per-linter: 0 + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + +formatters: + enable: + # - gci + # - gofmt + - gofumpt + # - goimports + # - golines + - swaggo + + settings: + golines: + # Default: 100 + max-len: 120 + +linters: + default: all + disable: + # linters that give advice contrary to what the Go authors advise + - decorder # checks declaration order and count of types, constants, variables and functions + - dupword # [useless without config] checks for duplicate words in the source code + - exhaustruct # [highly recommend to enable] checks if all structure fields are initialized + - forcetypeassert # [replaced by errcheck] finds forced type assertions + - ginkgolinter # [if you use ginkgo/gomega] enforces standards of using ginkgo and gomega + - gochecknoglobals # checks that no global variables exist + - cyclop # replaced by revive + - gocyclo # replaced by revive + - forbidigo # needs configuration to be useful + - funlen # replaced by revive + - godox # TODO's are OK + - ireturn # It's OK + - musttag + - nonamedreturns + - goconst # finds repeated strings that could be replaced by a constant + - goheader # checks is file header matches to pattern + - gomodguard # [use more powerful depguard] allow and block lists linter for direct Go module dependencies + - gomoddirectives + - err113 # bad advice about dynamic errors + - lll # [replaced by golines] reports long lines + - mnd # detects magic numbers, duplicated by revive + - nlreturn # [too strict and mostly code is not more readable] checks for a new line before return and branch statements to increase code clarity + - noinlineerr # disallows inline error handling `if err := ...; err != nil {` + - prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated + - tagliatelle # needs configuration + - testableexamples # checks if examples are testable (have an expected output) + - testpackage # makes you use a separate _test package + - paralleltest # not every test should be in parallel + - wrapcheck # not required + - wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines + - wsl_v5 # [too strict and mostly code is not more readable] add or remove empty lines + - zerologlint # detects the wrong usage of zerolog that a user forgets to dispatch zerolog.Event + + # All settings can be found here https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.reference.yml + settings: + depguard: + rules: + "deprecated": + files: + - "$all" + deny: + - pkg: github.com/golang/protobuf + desc: Use google.golang.org/protobuf instead, see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules + - pkg: github.com/satori/go.uuid + desc: Use github.com/google/uuid instead, satori's package is not maintained + - pkg: github.com/gofrs/uuid$ + desc: Use github.com/gofrs/uuid/v5 or later, it was not a go module before v5 + "non-test files": + files: + - "!$test" + deny: + - pkg: math/rand$ + desc: Use math/rand/v2 instead, see https://go.dev/blog/randv2 + - pkg: "github.com/sirupsen/logrus" + desc: not allowed + - pkg: "github.com/pkg/errors" + desc: Should be replaced by standard lib errors package + + dupl: + # token count (default: 150) + threshold: 300 + + embeddedstructfieldcheck: + # Checks that sync.Mutex and sync.RWMutex are not used as embedded fields. + forbid-mutex: true + + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + check-type-assertions: true + check-blank: true + + exhaustive: + # Program elements to check for exhaustiveness. + # Default: [ switch ] + check: + - switch + - map + default-signifies-exhaustive: true + + fatcontext: + # Check for potential fat contexts in struct pointers. + # May generate false positives. + # Default: false + check-struct-pointers: true + + funcorder: + # Checks if the exported methods of a structure are placed before the non-exported ones. + struct-method: false + + gocognit: + min-complexity: 55 + + gocritic: + enable-all: true + disabled-checks: + - paramTypeCombine + # The list of supported checkers can be found at https://go-critic.com/overview. + settings: + captLocal: + # Whether to restrict checker to params only. + paramsOnly: false + underef: + # Whether to skip (*x).method() calls where x is a pointer receiver. + skipRecvDeref: false + hugeParam: + # Default: 80 + sizeThreshold: 200 + + govet: + enable-all: true + + godot: + scope: toplevel + + inamedparam: + # Skips check for interface methods with only a single parameter. + skip-single-param: true + + nakedret: + # Default: 30 + max-func-lines: 7 + + nestif: + min-complexity: 15 + + nolintlint: + # Exclude following linters from requiring an explanation. + # Default: [] + allow-no-explanation: [funlen, gocognit, golines] + # Enable to require an explanation of nonzero length after each nolint directive. + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. + require-specific: true + + revive: + enable-all-rules: true + rules: + - name: add-constant + severity: warning + disabled: true + - name: cognitive-complexity + disabled: true # prefer maintidx + - name: cyclomatic + disabled: true # prefer maintidx + - name: function-length + arguments: [150, 225] + - name: line-length-limit + arguments: [150] + - name: nested-structs + disabled: true + - name: max-public-structs + arguments: [10] + - name: flag-parameter # fixes are difficult + disabled: true + - name: bare-return + disabled: true + - name: confusing-naming # many false positives + disabled: true + + rowserrcheck: + # database/sql is always checked. + # Default: [] + packages: + - github.com/jmoiron/sqlx + + perfsprint: + # optimize fmt.Sprintf("x: %s", y) into "x: " + y + strconcat: false + + staticcheck: + checks: + - all + + usetesting: + # Enable/disable `os.TempDir()` detections. + # Default: false + os-temp-dir: true + + varnamelen: + max-distance: 75 + min-name-length: 2 + check-receivers: false + ignore-names: + - r + - w + - f + - err + + exclusions: + # Default: [] + presets: + - common-false-positives + rules: + # Allow "err" and "ok" vars to shadow existing declarations, otherwise we get too many false positives. + - text: '^shadow: declaration of "(err|ok)" shadows declaration' + linters: + - govet + - text: "parameter 'ctx' seems to be unused, consider removing or renaming it as _" + linters: + - revive + - path: _test\.go + linters: + - dupl + - gosec + - godot + - govet # alignment + - noctx + - perfsprint + - revive + - varnamelen diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 137edc2..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,30 +0,0 @@ -## Hello Contributors! - -Thanks for your interest! -We're so glad you're here. - -### Important Resources - -#### bugs: [https://github.com/tinkerbell/proposals/issues](https://github.com/tinkerbell/proposals/issues) - -### Code of Conduct - -Please read and understand the code of conduct found [here](https://github.com/tinkerbell/.github/blob/main/CODE_OF_CONDUCT.md). - -### DCO Sign Off - -Please read and understand the DCO found [here](docs/DCO.md). - -### Environment Details - -### How to Submit Change Requests - -Please submit change requests and / or features via [Issues](https://github.com/tinkerbell/proposals/issues). -There's no guarantee it'll be changed, but you never know until you try. -We'll try to add comments as soon as possible, though. - -### How to Report a Bug - -Bugs are problems in code, in the functionality of an application or in its UI design; you can submit them through [Issues](https://github.com/tinkerbell/proposals/issues). - -## Code Style Guides diff --git a/Makefile b/Makefile index 16649e6..7380cd0 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,11 @@ +.PHONY: test +test: + @echo "Running integration tests..." + @chmod +x $(LINT_ROOT)/test/run_tests.sh + @$(LINT_ROOT)/test/run_tests.sh # BEGIN: lint-install . -# http://github.com/tinkerbell/lint-install +# http://github.com/codeGROOVE-dev/lint-install .PHONY: lint lint: _lint @@ -20,7 +25,7 @@ endif LINTERS := FIXERS := -SHELLCHECK_VERSION ?= v0.8.0 +SHELLCHECK_VERSION ?= v0.11.0 SHELLCHECK_BIN := $(LINT_ROOT)/out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH) $(SHELLCHECK_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -40,7 +45,7 @@ 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; } -HADOLINT_VERSION ?= v2.10.0 +HADOLINT_VERSION ?= v2.14.0 HADOLINT_BIN := $(LINT_ROOT)/out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) $(HADOLINT_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -54,7 +59,7 @@ hadolint-lint: $(HADOLINT_BIN) $(HADOLINT_BIN) $(shell find . -name "*Dockerfile") GOLANGCI_LINT_CONFIG := $(LINT_ROOT)/.golangci.yml -GOLANGCI_LINT_VERSION ?= v1.50.0 +GOLANGCI_LINT_VERSION ?= v2.10.1 GOLANGCI_LINT_BIN := $(LINT_ROOT)/out/linters/golangci-lint-$(GOLANGCI_LINT_VERSION)-$(LINT_ARCH) $(GOLANGCI_LINT_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -70,7 +75,7 @@ FIXERS += golangci-lint-fix golangci-lint-fix: $(GOLANGCI_LINT_BIN) find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLANGCI_LINT_CONFIG)" --fix \; -YAMLLINT_VERSION ?= 1.27.1 +YAMLLINT_VERSION ?= 1.37.1 YAMLLINT_ROOT := $(LINT_ROOT)/out/linters/yamllint-$(YAMLLINT_VERSION) YAMLLINT_BIN := $(YAMLLINT_ROOT)/dist/bin/yamllint $(YAMLLINT_BIN): @@ -83,10 +88,46 @@ LINTERS += yamllint-lint yamllint-lint: $(YAMLLINT_BIN) PYTHONPATH=$(YAMLLINT_ROOT)/dist $(YAMLLINT_ROOT)/dist/bin/yamllint . +BIOME_VERSION ?= 2.3.8 +BIOME_BIN := $(LINT_ROOT)/out/linters/biome-$(BIOME_VERSION)-$(LINT_ARCH) +BIOME_CONFIG := $(LINT_ROOT)/biome.json + +# Map architecture names for Biome downloads +BIOME_ARCH := $(LINT_ARCH) +ifeq ($(LINT_ARCH),x86_64) + BIOME_ARCH := x64 +endif + +$(BIOME_BIN): + mkdir -p $(LINT_ROOT)/out/linters + rm -rf $(LINT_ROOT)/out/linters/biome-* + curl -sSfL -o $@ https://github.com/biomejs/biome/releases/download/%40biomejs%2Fbiome%40$(BIOME_VERSION)/biome-$(LINT_OS_LOWER)-$(BIOME_ARCH) \ + || echo "Unable to fetch biome for $(LINT_OS_LOWER)/$(BIOME_ARCH), falling back to local install" + test -f $@ || printf "#!/usr/bin/env biome\n" > $@ + chmod u+x $@ + +LINTERS += biome-lint +biome-lint: $(BIOME_BIN) + $(BIOME_BIN) check --config-path=$(BIOME_CONFIG) . + +FIXERS += biome-fix +biome-fix: $(BIOME_BIN) + $(BIOME_BIN) check --write --config-path=$(BIOME_CONFIG) . + .PHONY: _lint $(LINTERS) -_lint: $(LINTERS) +_lint: + @exit_code=0; \ + for target in $(LINTERS); do \ + $(MAKE) $$target || exit_code=1; \ + done; \ + exit $$exit_code .PHONY: fix $(FIXERS) -fix: $(FIXERS) +fix: + @exit_code=0; \ + for target in $(FIXERS); do \ + $(MAKE) $$target || exit_code=1; \ + done; \ + exit $$exit_code # END: lint-install . diff --git a/Makefile.tmpl b/Makefile.tmpl index 417c4d1..c06e8e8 100644 --- a/Makefile.tmpl +++ b/Makefile.tmpl @@ -1,6 +1,6 @@ # BEGIN: lint-install {{.Args}} -# http://github.com/tinkerbell/lint-install +# http://github.com/codeGROOVE-dev/lint-install .PHONY: lint lint: _lint @@ -21,7 +21,7 @@ LINTERS := FIXERS := {{ if .Shell -}} -SHELLCHECK_VERSION ?= v0.8.0 +SHELLCHECK_VERSION ?= v0.11.0 SHELLCHECK_BIN := $(LINT_ROOT)/out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH) $(SHELLCHECK_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -44,7 +44,7 @@ shellcheck-fix: $(SHELLCHECK_BIN) {{ end -}} {{ if .Dockerfile -}} -HADOLINT_VERSION ?= v2.10.0 +HADOLINT_VERSION ?= v2.14.0 HADOLINT_BIN := $(LINT_ROOT)/out/linters/hadolint-$(HADOLINT_VERSION)-$(LINT_ARCH) $(HADOLINT_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -61,7 +61,7 @@ hadolint-lint: $(HADOLINT_BIN) {{ if .Go -}} GOLANGCI_LINT_CONFIG := $(LINT_ROOT)/.golangci.yml -GOLANGCI_LINT_VERSION ?= v1.50.0 +GOLANGCI_LINT_VERSION ?= v2.10.1 GOLANGCI_LINT_BIN := $(LINT_ROOT)/out/linters/golangci-lint-$(GOLANGCI_LINT_VERSION)-$(LINT_ARCH) $(GOLANGCI_LINT_BIN): mkdir -p $(LINT_ROOT)/out/linters @@ -80,7 +80,7 @@ golangci-lint-fix: $(GOLANGCI_LINT_BIN) {{ end -}} {{ if .YAML -}} -YAMLLINT_VERSION ?= 1.27.1 +YAMLLINT_VERSION ?= 1.37.1 YAMLLINT_ROOT := $(LINT_ROOT)/out/linters/yamllint-$(YAMLLINT_VERSION) YAMLLINT_BIN := $(YAMLLINT_ROOT)/dist/bin/yamllint $(YAMLLINT_BIN): @@ -95,10 +95,49 @@ yamllint-lint: $(YAMLLINT_BIN) {{ end -}} +{{ if .Web -}} +BIOME_VERSION ?= 2.3.8 +BIOME_BIN := $(LINT_ROOT)/out/linters/biome-$(BIOME_VERSION)-$(LINT_ARCH) +BIOME_CONFIG := $(LINT_ROOT)/biome.json + +# Map architecture names for Biome downloads +BIOME_ARCH := $(LINT_ARCH) +ifeq ($(LINT_ARCH),x86_64) + BIOME_ARCH := x64 +endif + +$(BIOME_BIN): + mkdir -p $(LINT_ROOT)/out/linters + rm -rf $(LINT_ROOT)/out/linters/biome-* + curl -sSfL -o $@ https://github.com/biomejs/biome/releases/download/%40biomejs%2Fbiome%40$(BIOME_VERSION)/biome-$(LINT_OS_LOWER)-$(BIOME_ARCH) \ + || echo "Unable to fetch biome for $(LINT_OS_LOWER)/$(BIOME_ARCH), falling back to local install" + test -f $@ || printf "#!/usr/bin/env biome\n" > $@ + chmod u+x $@ + +LINTERS += biome-lint +biome-lint: $(BIOME_BIN) + {{ .LintCommands.biome }} + +FIXERS += biome-fix +biome-fix: $(BIOME_BIN) + {{ .FixCommands.biome }} + +{{ end -}} + .PHONY: _lint $(LINTERS) -_lint: $(LINTERS) +_lint: + @exit_code=0; \ + for target in $(LINTERS); do \ + $(MAKE) $$target || exit_code=1; \ + done; \ + exit $$exit_code .PHONY: fix $(FIXERS) -fix: $(FIXERS) +fix: + @exit_code=0; \ + for target in $(FIXERS); do \ + $(MAKE) $$target || exit_code=1; \ + done; \ + exit $$exit_code # END: lint-install {{.Args}} diff --git a/README.md b/README.md index 4cb12d1..0873413 100644 --- a/README.md +++ b/README.md @@ -3,60 +3,70 @@ [![GoReport Widget]][GoReport Status] [![stability-stable](https://img.shields.io/badge/stability-stable-green.svg)](https://github.com/emersion/stability-badges#stable) -[GoReport Status]: https://goreportcard.com/report/github.com/tinkerbell/lint-install -[GoReport Widget]: https://goreportcard.com/badge/github.com/tinkerbell/lint-install +[GoReport Status]: https://goreportcard.com/report/github.com/codeGROOVE-dev/lint-install +[GoReport Widget]: https://goreportcard.com/badge/github.com/codeGROOVE-dev/lint-install -Idiomatic linters for opinionated projects. +Automated linter installation and configuration for consistent code quality across teams and environments. -This tool installs well-configured linters to any project, open-source or -otherwise. The linters can be used in a repeatable and consistent way across CI, -local tests, and IDE's. +## Quick Start -lint-install adds linter configuration to the root of your project, and Makefile -rules to install a consistently versioned set of linters to be used in any -environment. These Makefile rules can also be upgrading by lint-install, updating -all environments simultaneously. +```bash +# Install +go install github.com/codeGROOVE-dev/lint-install@latest -Currently supported languages: +# Add linters to your project +lint-install . -- Go -- Shell -- Dockerfile -- YAML +# Run linters +make lint -## Philosophy +# Auto-fix issues +make fix +``` -- Catch all the bugs! -- Improve readability as much as possible. -- Be idiomatic: only raise issues that the language authors would flag +## Why Use It? -## Usage +- **One command setup** - Adds industry-standard linters instantly +- **Version pinning** - Same linter versions for everyone +- **Battle-tested configs** - Opinionated rules that catch real bugs +- **Multi-environment** - Works locally, in CI/CD, and IDEs -Installation: +## Supported Languages -`go get github.com/tinkerbell/lint-install` +- **Go** - golangci-lint +- **Shell** - shellcheck +- **Dockerfile** - hadolint +- **YAML** - yamllint +- **Web** (JS/TS/HTML/CSS/JSON) - biome -Add Makefile rules for a git repository: +## Usage Examples -`$HOME/go/bin/lint-install ` +```bash +# Basic usage +lint-install . +make lint -Users can then lint the project using: +# Auto-fix all fixable issues +make fix -`make lint` +# CI/CD (GitHub Actions) +- run: make lint-install +- run: make lint +``` -Other options: +## What Gets Added -``` - -dockerfile string - Level to lint Dockerfile with: [ignore, warn, error] (default "error") - -dry-run - Display changes to make - -go string - Level to lint Go with: [ignore, warn, error] (default "error") - -makefile string - name of Makefile to update (default "Makefile") - -shell string - Level to lint Shell with: [ignore, warn, error] (default "error") - -yaml string - Level to lint YAML with: [ignore, warn, error] (default "error") -``` +- **Makefile targets**: `lint`, `fix`, `lint-`, `lint-install` +- **Config files**: Only for languages detected in your project + - `.golangci.yml` (Go files) + - `.yamllint` (YAML files) + - `biome.json` (JS/TS/HTML/CSS/JSON files) +- **Linter binaries**: `./out/linters/` (git-ignored) + +## Contributing + +Contributions welcome! Submit issues or pull requests. + +## License + +See [LICENSE](LICENSE) for details. \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..2d404c2 --- /dev/null +++ b/biome.json @@ -0,0 +1,128 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.6/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": [ + "**", + "!**/node_modules", + "!**/dist", + "!**/build", + "!**/out", + "!**/coverage", + "!**/.next", + "!**/.nuxt" + ] + }, + "formatter": { + "enabled": true, + "formatWithErrors": false, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "complexity": { + "noExtraBooleanCast": "error", + "noUselessCatch": "error", + "noUselessTypeConstraint": "error", + "noAdjacentSpacesInRegex": "error", + "noArguments": "error" + }, + "correctness": { + "noConstAssign": "error", + "noConstantCondition": "error", + "noEmptyCharacterClassInRegex": "error", + "noEmptyPattern": "error", + "noGlobalObjectCalls": "error", + "noInnerDeclarations": "error", + "noInvalidConstructorSuper": "error", + "noNonoctalDecimalEscape": "error", + "noPrecisionLoss": "error", + "noSelfAssign": "error", + "noSetterReturn": "error", + "noSwitchDeclarations": "error", + "noUndeclaredVariables": "error", + "noUnreachable": "error", + "noUnreachableSuper": "error", + "noUnsafeFinally": "error", + "noUnsafeOptionalChaining": "error", + "noUnusedLabels": "error", + "noUnusedVariables": "error", + "useIsNan": "error", + "useValidForDirection": "error", + "useYield": "error", + "noInvalidBuiltinInstantiation": "error", + "useValidTypeof": "error" + }, + "security": { + "noDangerouslySetInnerHtml": "warn", + "noDangerouslySetInnerHtmlWithChildren": "error" + }, + "style": { + "useConst": "error" + }, + "suspicious": { + "noAssignInExpressions": "error", + "noAsyncPromiseExecutor": "error", + "noCatchAssign": "error", + "noClassAssign": "error", + "noCompareNegZero": "error", + "noControlCharactersInRegex": "error", + "noDebugger": "error", + "noDoubleEquals": "warn", + "noDuplicateCase": "error", + "noDuplicateClassMembers": "error", + "noDuplicateObjectKeys": "error", + "noDuplicateParameters": "error", + "noEmptyBlockStatements": "warn", + "noExplicitAny": "warn", + "noExtraNonNullAssertion": "error", + "noFallthroughSwitchClause": "error", + "noFunctionAssign": "error", + "noGlobalAssign": "error", + "noImportAssign": "error", + "noMisleadingCharacterClass": "error", + "noPrototypeBuiltins": "error", + "noRedeclare": "error", + "noShadowRestrictedNames": "error", + "noUnsafeDeclarationMerging": "error", + "noUnsafeNegation": "error", + "useGetterReturn": "error", + "noWith": "error", + "noVar": "error" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "trailingCommas": "es5", + "semicolons": "always", + "arrowParentheses": "always", + "bracketSpacing": true, + "bracketSameLine": false + } + }, + "json": { + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "parser": { + "allowComments": true, + "allowTrailingCommas": false + } + } +} diff --git a/docs/DCO.md b/docs/DCO.md deleted file mode 100644 index 4a28c20..0000000 --- a/docs/DCO.md +++ /dev/null @@ -1,62 +0,0 @@ -# DCO Sign Off - -All authors to the project retain copyright to their work. However, to ensure -that they are only submitting work that they have rights to, we are requiring -everyone to acknowledge this by signing their work. - -Since this signature indicates your rights to the contribution and -certifies the statements below, it must contain your real name and -email address. Various forms of noreply email address must not be used. - -Any copyright notices in this repository should specify the authors as "The -project authors". - -To sign your work, just add a line like this at the end of your commit message: - -```text -Signed-off-by: Jess Owens -``` - -This can easily be done with the `--signoff` option to `git commit`. - -By doing this you state that you can certify the following (from [https://developercertificate.org/][1]): - -```text -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -1 Letterman Drive -Suite D4700 -San Francisco, CA, 94129 - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` diff --git a/go.mod b/go.mod index 1abee08..549e1e7 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ -module github.com/tinkerbell/lint-install +module github.com/codeGROOVE-dev/lint-install -go 1.18 +go 1.22 require ( github.com/hexops/gotextdiff v1.0.3 - github.com/karrick/godirwalk v1.16.1 - k8s.io/klog/v2 v2.10.0 + github.com/karrick/godirwalk v1.17.0 + k8s.io/klog/v2 v2.130.1 ) -require github.com/go-logr/logr v0.4.0 // indirect +require github.com/go-logr/logr v1.4.3 // indirect diff --git a/go.sum b/go.sum index 440b095..bbe0e76 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,14 @@ github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= 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= k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE= k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= diff --git a/lint-install.go b/lint-install.go index cbc1a3e..5a938a8 100644 --- a/lint-install.go +++ b/lint-install.go @@ -1,3 +1,4 @@ +// Package main provides a tool to automatically install and configure linters in Go projects. package main import ( @@ -23,6 +24,7 @@ var ( shellFlag = flag.String("shell", "error", "Level to lint Shell with: [ignore, warn, error]") dockerfileFlag = flag.String("dockerfile", "error", "Level to lint Dockerfile with: [ignore, warn, error]") yamlFlag = flag.String("yaml", "error", "Level to lint YAML with: [ignore, warn, error]") + webFlag = flag.String("web", "error", "Level to lint Web files (JS/TS/HTML/CSS/JSON) with: [ignore, warn, error]") makeFileName = flag.String("makefile", "Makefile", "name of Makefile to update") //go:embed .golangci.yml @@ -31,6 +33,9 @@ var ( //go:embed .yamllint yamlLintConfig []byte + //go:embed biome.json + biomeLintConfig []byte + //go:embed Makefile.tmpl makeTmpl string ) @@ -42,17 +47,19 @@ const ( Shell Dockerfile YAML + Web ) type Config struct { + LintCommands map[string]string + FixCommands map[string]string Makefile string Args string Go string Dockerfile string Shell string YAML string - LintCommands map[string]string - FixCommands map[string]string + Web string } // applicableLinters returns a list of languages with known linters within a given directory. @@ -61,7 +68,7 @@ func applicableLinters(root string) (map[Language]bool, error) { found := map[Language]bool{} err := godirwalk.Walk(root, &godirwalk.Options{ - Callback: func(path string, de *godirwalk.Dirent) error { + Callback: func(path string, _ *godirwalk.Dirent) error { switch { case strings.HasSuffix(path, ".go"): found[Go] = true @@ -71,14 +78,21 @@ func applicableLinters(root string) (map[Language]bool, error) { found[Shell] = true case strings.HasSuffix(path, ".yml"), strings.HasSuffix(path, ".yaml"): found[YAML] = true + case strings.HasSuffix(path, ".js"), strings.HasSuffix(path, ".jsx"), + strings.HasSuffix(path, ".ts"), strings.HasSuffix(path, ".tsx"), + strings.HasSuffix(path, ".html"), strings.HasSuffix(path, ".css"): + found[Web] = true + default: } return nil }, Unsorted: true, }) - - return found, err + if err != nil { + return found, fmt.Errorf("walk: %w", err) + } + return found, nil } // updateMakefile updates the Makefile within a project with lint rules. @@ -91,7 +105,7 @@ func updateMakefile(root string, cfg Config, dryRun bool) (string, error) { klog.Infof("Found existing %s", dest) existing, err = os.ReadFile(dest) if err != nil { - return "", err + return "", fmt.Errorf("read file: %w", err) } } @@ -142,7 +156,7 @@ func updateMakefile(root string, cfg Config, dryRun bool) (string, error) { change := gotextdiff.ToUnified(filepath.Base(dest), filepath.Base(dest), string(existing), edits) if !dryRun { if err := os.WriteFile(dest, proposed, 0o600); err != nil { - return "", err + return "", fmt.Errorf("write file: %w", err) } } return fmt.Sprint(change), nil @@ -159,7 +173,7 @@ func updateFile(root string, basename string, content []byte, dryRun bool) (stri klog.Infof("Found existing %s", dest) existing, err = os.ReadFile(dest) if err != nil { - return "", err + return "", fmt.Errorf("read file: %w", err) } } @@ -167,20 +181,21 @@ func updateFile(root string, basename string, content []byte, dryRun bool) (stri edits := myers.ComputeEdits(span.URI(basename), string(existing), proposed) change := gotextdiff.ToUnified(basename, basename, string(existing), edits) - if !dryRun { - var err error - if content == nil { - // must be broken up, can't be conten == nil && f != nil because then - // the else branch will be taken if the file does *not* exist and - // an empty file will be written - if f != nil { - err = os.Remove(dest) - } - } else { - err = os.WriteFile(dest, content, 0o600) + if dryRun { + return fmt.Sprint(change), nil + } + + // Handle file operations when not in dry-run mode + if content == nil && f != nil { + if err := os.Remove(dest); err != nil { + return "", fmt.Errorf("remove file: %w", err) } - if err != nil { - return "", err + return fmt.Sprint(change), nil + } + + if content != nil { + if err := os.WriteFile(dest, content, 0o600); err != nil { + return "", fmt.Errorf("write file: %w", err) } } @@ -197,7 +212,7 @@ func updateGitignore(root string, dryRun bool) (string, error) { klog.Infof("Found existing %s", dest) existing, err = os.ReadFile(dest) if err != nil { - return "", err + return "", fmt.Errorf("read file: %w", err) } } proposed := []byte{} @@ -222,7 +237,7 @@ func updateGitignore(root string, dryRun bool) (string, error) { change := gotextdiff.ToUnified(filepath.Base(dest), filepath.Base(dest), string(existing), edits) if !dryRun { if err := os.WriteFile(dest, proposed, 0o600); err != nil { - return "", err + return "", fmt.Errorf("write file: %w", err) } } return fmt.Sprint(change), nil @@ -234,7 +249,7 @@ func goLintCmd(root string, level string, fix bool) string { found := []string{} err := godirwalk.Walk(root, &godirwalk.Options{ - Callback: func(path string, de *godirwalk.Dirent) error { + Callback: func(path string, _ *godirwalk.Dirent) error { if strings.HasSuffix(path, "go.mod") { found = append(found, filepath.Dir(path)) } @@ -254,7 +269,10 @@ func goLintCmd(root string, level string, fix bool) string { } klog.Infof("found %d modules within %s: %s", len(found), root, found) - return fmt.Sprintf(`find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLANGCI_LINT_CONFIG)"%s \;`, suffix) + return fmt.Sprintf( + `find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLANGCI_LINT_CONFIG)"%s \;`, + suffix, + ) } // shellLintCmd returns the appropriate shell lint command for a project. @@ -294,6 +312,68 @@ func yamlLintCmd(_ string, level string) string { return fmt.Sprintf(`PYTHONPATH=$(YAMLLINT_ROOT)/dist $(YAMLLINT_ROOT)/dist/bin/yamllint .%s`, suffix) } +// biomeLintCmd returns the appropriate biome lint command for a project. +func biomeLintCmd(_ string, level string, fix bool) string { + cmd := "check" + if fix { + cmd = "check --write" + } + + suffix := "" + if level == "warn" { + suffix = " || true" + } + + return fmt.Sprintf(`$(BIOME_BIN) %s --config-path=$(BIOME_CONFIG) .%s`, cmd, suffix) +} + +// configureGoLinter sets up Go linting configuration and commands. +func configureGoLinter(root string, cfg *Config, dryRun bool) error { + cfg.Go = *goFlag + cfg.LintCommands["golangci-lint"] = goLintCmd(root, cfg.Go, false) + cfg.FixCommands["golangci-lint"] = goLintCmd(root, cfg.Go, true) + + diff, err := updateFile(root, ".golangci.yml", goLintConfig, dryRun) + if err != nil { + return fmt.Errorf("update go lint config: %w", err) + } + if diff != "" { + klog.Infof("go lint config changes:\n%s", diff) + } else { + klog.Infof("go lint config has no changes") + } + + // Remove non-standard config files + for _, name := range []string{".golangci.json", ".golangci.toml", ".golangci.yaml"} { + diff, err := updateFile(root, name, nil, dryRun) + if err != nil { + return fmt.Errorf("delete %s: %w", name, err) + } + if diff != "" { + klog.Infof("standardizing on golangci.yml, deleting %s", name) + } + } + return nil +} + +// configureBiomeLinter sets up Biome linting configuration and commands. +func configureBiomeLinter(root string, cfg *Config, dryRun bool) error { + cfg.Web = *webFlag + cfg.LintCommands["biome"] = biomeLintCmd(root, cfg.Web, false) + cfg.FixCommands["biome"] = biomeLintCmd(root, cfg.Web, true) + + diff, err := updateFile(root, "biome.json", biomeLintConfig, dryRun) + if err != nil { + return fmt.Errorf("update biome config: %w", err) + } + if diff != "" { + klog.Infof("biome config changes:\n%s", diff) + } else { + klog.Infof("biome config has no changes") + } + return nil +} + // main creates peanut butter & jelly sandwiches with utmost precision. func main() { klog.InitFlags(nil) @@ -320,28 +400,8 @@ func main() { } if needs[Go] { - cfg.Go = *goFlag - cfg.LintCommands["golangci-lint"] = goLintCmd(root, cfg.Go, false) - cfg.FixCommands["golangci-lint"] = goLintCmd(root, cfg.Go, true) - - diff, err := updateFile(root, ".golangci.yml", goLintConfig, *dryRunFlag) - if err != nil { - klog.Exitf("update go lint config failed: %v", err) - } - if diff != "" { - klog.Infof("go lint config changes:\n%s", diff) - } else { - klog.Infof("go lint config has no changes") - } - - for _, name := range []string{".golangci.json", ".golangci.toml", ".golangci.yaml"} { - diff, err := updateFile(root, name, nil, *dryRunFlag) - if err != nil { - klog.Exitf("deleting non-standardized go lint config failed: %v", err) - } - if diff != "" { - klog.Infof("standardizing on golangci.yml, deleting %s", name) - } + if err := configureGoLinter(root, &cfg, *dryRunFlag); err != nil { + klog.Exitf("configure go linter: %v", err) } } if needs[Dockerfile] { @@ -367,6 +427,11 @@ func main() { klog.Infof("yamllint config has no changes") } } + if needs[Web] { + if err := configureBiomeLinter(root, &cfg, *dryRunFlag); err != nil { + klog.Exitf("configure biome linter: %v", err) + } + } diff, err := updateMakefile(root, cfg, *dryRunFlag) if err != nil { diff --git a/shell.nix b/shell.nix deleted file mode 100644 index d663ba3..0000000 --- a/shell.nix +++ /dev/null @@ -1,23 +0,0 @@ -let _pkgs = import { }; -in { pkgs ? import (_pkgs.fetchFromGitHub { - owner = "NixOS"; - repo = "nixpkgs"; - #branch@date: nixpkgs-unstable@2022-08-17 - rev = "d61d4e71ba9a8f56e9f2092b7cfa9cffa4253971"; - sha256 = "0x76l64pchaqaw8v0b331g4jm3li1xcpkbwxpn9n6a769c4ynj58"; -}) { } }: - -with pkgs; - -mkShell { - buildInputs = [ - git - gnumake - go_1_18 - nixfmt - nodePackages.prettier - python3Packages.pip - python3Packages.setuptools - python3Packages.wheel - ]; -} diff --git a/test/go.mod b/test/go.mod new file mode 100644 index 0000000..2ad179c --- /dev/null +++ b/test/go.mod @@ -0,0 +1,3 @@ +module github.com/codeGROOVE-dev/lint-install/test + +go 1.23 diff --git a/test/run_tests.sh b/test/run_tests.sh new file mode 100755 index 0000000..dd56b30 --- /dev/null +++ b/test/run_tests.sh @@ -0,0 +1,95 @@ +#!/bin/bash +# Integration tests for lint-install +# This script verifies that each linter catches intentional issues in test files + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +echo "Running lint-install integration tests..." +echo "==========================================" +echo "" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +test_count=0 +pass_count=0 +fail_count=0 + +# Helper function to run a test +run_test() { + local linter_name=$1 + local command=$2 + local expected_failure=$3 # "should_fail" or "should_pass" + + test_count=$((test_count + 1)) + echo -n "Testing ${linter_name}... " + + if eval "$command" > /dev/null 2>&1; then + # Command succeeded + if [ "$expected_failure" = "should_fail" ]; then + echo -e "${RED}FAIL${NC} (expected to catch issues but passed)" + fail_count=$((fail_count + 1)) + return 1 + else + echo -e "${GREEN}PASS${NC}" + pass_count=$((pass_count + 1)) + return 0 + fi + else + # Command failed + if [ "$expected_failure" = "should_fail" ]; then + echo -e "${GREEN}PASS${NC} (correctly caught issues)" + pass_count=$((pass_count + 1)) + return 0 + else + echo -e "${RED}FAIL${NC} (unexpected failure)" + fail_count=$((fail_count + 1)) + return 1 + fi + fi +} + +# Build the lint-install binary if needed +cd "$ROOT_DIR" +if [ ! -f "./lint-install" ]; then + echo "Building lint-install..." + go build -o lint-install . + echo "" +fi + +# Test 1: Shellcheck should catch issues in test.sh +run_test "shellcheck" "make shellcheck-lint" "should_fail" + +# Test 2: Hadolint should catch issues in test.Dockerfile +run_test "hadolint" "make hadolint-lint" "should_fail" + +# Test 3: Biome should catch issues in test.js +run_test "biome" "make biome-lint" "should_fail" + +# Test 4: Yamllint should catch issues in test.yaml +run_test "yamllint" "make yamllint-lint" "should_fail" + +# Test 5: Golangci-lint should catch issues in test/test_main.go +run_test "golangci-lint" "cd test && make golangci-lint-lint" "should_fail" + +echo "" +echo "==========================================" +echo "Test Results:" +echo -e " ${GREEN}Passed: ${pass_count}${NC}" +echo -e " ${RED}Failed: ${fail_count}${NC}" +echo -e " Total: ${test_count}" +echo "" + +if [ $fail_count -eq 0 ]; then + echo -e "${GREEN}All tests passed!${NC}" + exit 0 +else + echo -e "${RED}Some tests failed.${NC}" + exit 1 +fi diff --git a/test/test.Dockerfile b/test/test.Dockerfile index bb895d6..ac3fe1d 100644 --- a/test/test.Dockerfile +++ b/test/test.Dockerfile @@ -1,2 +1,5 @@ FROM ubuntu:hirsute-20210723 +# DL3008: Pin versions in apt-get install +RUN apt-get update && apt-get install -y curl +# DL3059: Multiple RUN commands should be consolidated RUN echo hello world diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..1aa8e00 --- /dev/null +++ b/test/test.js @@ -0,0 +1,12 @@ +// Test file with intentional biome lint issues +var unused = "this variable is never used"; + +function badlyFormatted( x,y ){ +const z=x+y +return z +} + +// Missing semicolons, bad formatting +let a = 1 +let b = 2 +console.log(a,b) diff --git a/test/test.sh b/test/test.sh index 1a24852..2b2a01c 100644 --- a/test/test.sh +++ b/test/test.sh @@ -1 +1,14 @@ #!/bin/sh +# Test script with intentional shellcheck issues + +# SC2006: Use $() instead of backticks +result=`echo "hello"` + +# SC2086: Double quote to prevent globbing +echo $result + +# SC2164: Use cd ... || exit in case cd fails +cd /tmp + +# SC2034: unused variable +unused_var="never used" diff --git a/test/test.yaml b/test/test.yaml new file mode 100644 index 0000000..e757c73 --- /dev/null +++ b/test/test.yaml @@ -0,0 +1,10 @@ +--- +# Test YAML with intentional yamllint issues +key1: value1 + key2: value2 +key3: + - item1 + - item2 + - nested_badly +key4: "trailing whitespace here " +very_long_line_that_exceeds_the_normal_line_length_limit_and_should_be_flagged_by_yamllint_as_too_long: true diff --git a/test/test_main.go b/test/test_main.go new file mode 100644 index 0000000..dee6b57 --- /dev/null +++ b/test/test_main.go @@ -0,0 +1,17 @@ +package test + +import "fmt" + +// TestFunction has intentional golangci-lint issues +func TestFunction() { + // Unused variable + x := 5 + + // Inefficient string concatenation in loop + var result string + for i := 0; i < 10; i++ { + result = result + fmt.Sprintf("%d", i) + } + + fmt.Println(result) +}