diff --git a/.golangci.yml b/.golangci.yml index 54eca81..a34a77f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,57 +31,57 @@ linters: # Enable specific linters enable: # Enabled by default - - errcheck # Check for unchecked errors - - gosimple # Simplify code - - govet # Examine Go source code and reports suspicious constructs - - ineffassign # Detect ineffectual assignments - - staticcheck # Advanced Go linter - - unused # Check for unused constants, variables, functions and types - + - errcheck # Check for unchecked errors + - gosimple # Simplify code + - govet # Examine Go source code and reports suspicious constructs + - ineffassign # Detect ineffectual assignments + - staticcheck # Advanced Go linter + - unused # Check for unused constants, variables, functions and types + # Additional linters - - asciicheck # Check for non-ASCII characters - - bodyclose # Check HTTP response body is closed - - contextcheck # Check context.Context is propagated - - cyclop # Check cyclomatic complexity - - dupl # Check for duplicate code - - durationcheck # Check for two durations multiplied together - - errorlint # Check for error wrapping - - exhaustive # Check exhaustiveness of enum switch statements - - copyloopvar # Check for pointers to enclosing loop variables + - asciicheck # Check for non-ASCII characters + - bodyclose # Check HTTP response body is closed + - contextcheck # Check context.Context is propagated + - cyclop # Check cyclomatic complexity + - dupl # Check for duplicate code + - durationcheck # Check for two durations multiplied together + - errorlint # Check for error wrapping + - exhaustive # Check exhaustiveness of enum switch statements + - copyloopvar # Check for pointers to enclosing loop variables - forcetypeassert # Find forced type assertions - - funlen # Check function length - - gci # Control Go package import order - - gocognit # Check cognitive complexity - - goconst # Find repeated strings that could be constants - - gocritic # Various checks - - gocyclo # Check cyclomatic complexity - - godot # Check if comments end in a period - - gofmt # Check if the code was gofmt-ed - - goimports # Check if imports are sorted - - mnd # Check for magic numbers + - funlen # Check function length + - gci # Control Go package import order + - gocognit # Check cognitive complexity + - goconst # Find repeated strings that could be constants + - gocritic # Various checks + - gocyclo # Check cyclomatic complexity + - godot # Check if comments end in a period + - gofmt # Check if the code was gofmt-ed + - goimports # Check if imports are sorted + - mnd # Check for magic numbers - gomoddirectives # Check for //go:build directives - - gomodguard # Check for blocked module dependencies + - gomodguard # Check for blocked module dependencies - goprintffuncname # Check printf-like function names - - gosec # Security checker - - lll # Check line length - - makezero # Find slice declarations that are not initialized with zero length - - misspell # Find commonly misspelled English words - - nakedret # Check for naked returns - - nilerr # Check for nil errors - - nlreturn # Check for new line before return - - noctx # Check for HTTP requests without context - - prealloc # Find slice declarations that could be preallocated - - predeclared # Check for predeclared identifiers - - revive # Fast, configurable, extensible linter - - rowserrcheck # Check SQL rows.Err - - sqlclosecheck # Check SQL Close() calls - - stylecheck # Stylecheck is a replacement for golint - - thelper # Check test helpers - - tparallel # Check test parallelization - - unconvert # Check for unnecessary type conversions - - unparam # Check for unused function parameters - - wastedassign # Check for wasted assignment statements - - whitespace # Check for unnecessary whitespace + - gosec # Security checker + - lll # Check line length + - makezero # Find slice declarations that are not initialized with zero length + - misspell # Find commonly misspelled English words + - nakedret # Check for naked returns + - nilerr # Check for nil errors + - nlreturn # Check for new line before return + - noctx # Check for HTTP requests without context + - prealloc # Find slice declarations that could be preallocated + - predeclared # Check for predeclared identifiers + - revive # Fast, configurable, extensible linter + - rowserrcheck # Check SQL rows.Err + - sqlclosecheck # Check SQL Close() calls + - stylecheck # Stylecheck is a replacement for golint + - thelper # Check test helpers + - tparallel # Check test parallelization + - unconvert # Check for unnecessary type conversions + - unparam # Check for unused function parameters + - wastedassign # Check for wasted assignment statements + - whitespace # Check for unnecessary whitespace # Linters settings linters-settings: @@ -181,7 +181,7 @@ linters-settings: # Settings for lll lll: - line-length: 140 + line-length: 120 # Settings for misspell misspell: @@ -203,7 +203,7 @@ linters-settings: rules: - name: atomic - name: line-length-limit - arguments: [140] + arguments: [120] - name: argument-limit arguments: [4] - name: cyclomatic @@ -219,50 +219,10 @@ linters-settings: # Settings for stylecheck stylecheck: - checks: - ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"] + checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"] dot-import-whitelist: - fmt - initialisms: - [ - "ACL", - "API", - "ASCII", - "CPU", - "CSS", - "DNS", - "EOF", - "GUID", - "HTML", - "HTTP", - "HTTPS", - "ID", - "IP", - "JSON", - "QPS", - "RAM", - "RPC", - "SLA", - "SMTP", - "SQL", - "SSH", - "TCP", - "TLS", - "TTL", - "UDP", - "UI", - "GID", - "UID", - "UUID", - "URI", - "URL", - "UTF8", - "VM", - "XML", - "XMPP", - "XSRF", - "XSS", - ] + initialisms: ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS"] http-status-code-whitelist: ["200", "400", "404", "500"] # Settings for unparam @@ -274,24 +234,24 @@ issues: # List of regexps of issue texts to exclude exclude: # Exclude some linters from running on tests files - - "G104:" # Errors unhandled (gosec) - - "G204:" # Subprocess launched with variable (gosec) - - "G304:" # File path provided as taint input (gosec) - + - "G104:" # Errors unhandled (gosec) + - "G204:" # Subprocess launched with variable (gosec) + - "G304:" # File path provided as taint input (gosec) + # Skip files exclude-files: - ".*_test.go" - ".*\\.pb\\.go" - + # Skip directories exclude-dirs: - vendor - node_modules - .git - + # Make issues output unique by line uniq-by-line: true - + # Excluding configuration per-path, per-linter, per-text and per-source exclude-rules: # Exclude some linters from running on tests files @@ -410,4 +370,4 @@ issues: new-from-patch: "" # Fix found issues (if it's supported by the linter) - fix: false + fix: false \ No newline at end of file diff --git a/assets/.golangci.yml b/assets/.golangci.yml index 83fd28a..766b152 100644 --- a/assets/.golangci.yml +++ b/assets/.golangci.yml @@ -1,87 +1,48 @@ -version: "2" +run: + timeout: 5m + modules-download-mode: readonly + +linters-settings: + gofmt: + simplify: true + goimports: + local-prefixes: {{.GoModule.Name}} + govet: + check-shadowing: true + enable-all: true + disable: + - fieldalignment + misspell: + locale: US linters: + disable-all: true enable: - bodyclose - errcheck - gocritic + - gofmt + - goimports - gosec + - gosimple + - govet - ineffassign - misspell - nakedret - revive - - staticcheck # Now includes gosimple and stylecheck + - staticcheck + - stylecheck + - typecheck - unconvert - unused - whitespace - - govet # Ensure this is included - - settings: - errcheck: - # Add any errcheck settings here - exclude-functions: - - io.Copy(*bytes.Buffer) - - gocritic: - enabled-tags: - - diagnostic - - experimental - - opinionated - - performance - - style - - gosec: - # Add gosec settings - excludes: - - G104 # Errors unhandled - - staticcheck: - # Configure staticcheck (includes gosimple/stylecheck checks) - checks: ["all"] - - revive: - # Add revive rules - rules: - - name: exported - disabled: false - - exclusions: - presets: - - comments - - std-error-handling - - common-false-positives - - rules: - - path: '_test\.go' - linters: - - errcheck - - gosec - -formatters: - enable: - - gofmt - - goimports - - settings: - gofmt: - simplify: true - - goimports: - local-prefixes: - - {{.GoModule.Name}} - -output: - formats: - text: - path: stdout - colors: true - print-linter-name: true - -run: - timeout: 5m - tests: true issues: - max-issues-per-linter: 0 - max-same-issues: 0 - fix: false + exclude-rules: + - path: server/configuration.go + linters: + - unused + - path: _test\.go + linters: + - bodyclose + - scopelint # https://github.com/kyoh86/scopelint/issues/4 diff --git a/assets/Makefile b/assets/Makefile index ac014a2..c3a899c 100644 --- a/assets/Makefile +++ b/assets/Makefile @@ -23,6 +23,9 @@ ASSETS_DIR ?= assets .PHONY: default default: all +# Verify environment, and define PLUGIN_ID, PLUGIN_VERSION, HAS_SERVER and HAS_WEBAPP as needed. +include build/setup.mk + ifneq ($(MM_DEBUG),) GO_BUILD_GCFLAGS = -gcflags "all=-N -l" else diff --git a/assets/build/_setup.mk b/assets/build/_setup.mk index 9ff3a24..91bcc00 100644 --- a/assets/build/_setup.mk +++ b/assets/build/_setup.mk @@ -5,9 +5,9 @@ ifeq ($(GO),) endif # Gather build variables to inject into the manifest tool -BUILD_HASH_SHORT = $(shell git rev-parse --short HEAD 2>/dev/null) +BUILD_HASH_SHORT = $(shell git rev-parse --short HEAD) BUILD_TAG_LATEST = $(shell git describe --tags --match 'v*' --abbrev=0 2>/dev/null) -BUILD_TAG_CURRENT = $(shell git tag --points-at HEAD 2>/dev/null) +BUILD_TAG_CURRENT = $(shell git tag --points-at HEAD) # Extract the plugin id from the manifest. PLUGIN_ID ?= $(shell pluginctl manifest get '{{"{{"}}.Id{{"}}"}}') diff --git a/assets/build/build.mk b/assets/build/build.mk index 22cec48..19b83ec 100644 --- a/assets/build/build.mk +++ b/assets/build/build.mk @@ -11,16 +11,9 @@ all: check-style test dist manifest-check: pluginctl manifest check -## Cleans the server build artifacts. -.PHONY: clean-server -clean-server: -ifneq ($(HAS_SERVER),) - rm -rf server/dist -endif ## Builds the server, if it exists, for all supported architectures, unless MM_SERVICESETTINGS_ENABLEDEVELOPER is set. .PHONY: server -server: clean-server server: ifneq ($(HAS_SERVER),) ifneq ($(MM_DEBUG),) diff --git a/assets/build/test.mk b/assets/build/test.mk index 16261da..e7691a2 100644 --- a/assets/build/test.mk +++ b/assets/build/test.mk @@ -2,13 +2,11 @@ # Testing and Quality Assurance # ==================================================================================== -GOLANGCI_LINT_BINARY = ./build/bin/golangci-lint -GOTESTSUM_BINARY = ./build/bin/gotestsum - ## Install go tools install-go-tools: - @echo "Installing development tools..." - @pluginctl tools install --bin-dir ./build/bin + @echo Installing go tools + $(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61.0 + $(GO) install gotest.tools/gotestsum@v1.7.0 ## Runs eslint and golangci-lint .PHONY: check-style @@ -26,14 +24,14 @@ endif ifneq ($(HAS_SERVER),) @echo Running golangci-lint $(GO) vet ./... - $(GOLANGCI_LINT_BINARY) run ./... + $(GOBIN)/golangci-lint run ./... endif ## Runs any lints and unit tests defined for the server and webapp, if they exist. .PHONY: test test: apply webapp/node_modules install-go-tools ifneq ($(HAS_SERVER),) - $(GOTESTSUM_BINARY) -- -v ./... + $(GOBIN)/gotestsum -- -v ./... endif ifneq ($(HAS_WEBAPP),) cd webapp && $(NPM) run test; @@ -44,7 +42,7 @@ endif .PHONY: test-ci test-ci: apply webapp/node_modules install-go-tools ifneq ($(HAS_SERVER),) - $(GOTESTSUM_BINARY) --format standard-verbose --junitfile report.xml -- ./... + $(GOBIN)/gotestsum --format standard-verbose --junitfile report.xml -- ./... endif ifneq ($(HAS_WEBAPP),) cd webapp && $(NPM) run test; diff --git a/cmd/pluginctl/main.go b/cmd/pluginctl/main.go index f65e021..1446b72 100644 --- a/cmd/pluginctl/main.go +++ b/cmd/pluginctl/main.go @@ -81,8 +81,6 @@ func runCommand(command string, args []string, pluginPath string) error { return runVersionCommand(args) case "create-plugin": return runCreatePluginCommand(args, pluginPath) - case "tools": - return runToolsCommand(args, pluginPath) default: return fmt.Errorf("unknown command: %s", command) } @@ -130,10 +128,6 @@ func runCreatePluginCommand(args []string, pluginPath string) error { return pluginctl.RunCreatePluginCommand(args, pluginPath) } -func runToolsCommand(args []string, pluginPath string) error { - return pluginctl.RunToolsCommand(args, pluginPath) -} - func showUsage() { usageText := `pluginctl - Mattermost Plugin Development CLI @@ -154,7 +148,6 @@ Commands: manifest Manage plugin manifest files logs View plugin logs create-plugin Create a new plugin from template - tools Manage development tools (install golangci-lint, gotestsum) version Show version information Environment Variables: diff --git a/go.mod b/go.mod index 420fef7..736b43a 100644 --- a/go.mod +++ b/go.mod @@ -156,7 +156,6 @@ require ( github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/dnephin/pflag v1.0.7 // indirect github.com/docker/cli v28.1.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker v28.1.1+incompatible // indirect @@ -240,7 +239,6 @@ require ( github.com/google/rpmpack v0.7.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/safetext v0.0.0-20240722112252-5a72de7e7962 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/google/wire v0.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect @@ -290,7 +288,6 @@ require ( github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jjti/go-spancheck v0.6.2 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect - github.com/jonboulle/clockwork v0.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/julz/importas v0.1.0 // indirect github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect @@ -501,7 +498,6 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/gotestsum v1.7.0 // indirect honnef.co/go/tools v0.5.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect @@ -516,5 +512,4 @@ tool ( github.com/golangci/golangci-lint/cmd/golangci-lint github.com/goreleaser/goreleaser/v2 github.com/securego/gosec/v2/cmd/gosec - gotest.tools/gotestsum ) diff --git a/go.sum b/go.sum index 55addb7..86a9a75 100644 --- a/go.sum +++ b/go.sum @@ -394,8 +394,6 @@ github.com/distribution/distribution/v3 v3.0.0 h1:q4R8wemdRQDClzoNNStftB2ZAfqOiN github.com/distribution/distribution/v3 v3.0.0/go.mod h1:tRNuFoZsUdyRVegq8xGNeds4KLjwLCRin/tTo6i1DhU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= -github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k= github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= @@ -439,7 +437,6 @@ github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= @@ -457,7 +454,6 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= @@ -619,7 +615,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -823,9 +818,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= -github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -906,7 +898,6 @@ github.com/mattermost/logr/v2 v2.0.22 h1:npFkXlkAWR9J8payh8ftPcCZvLbHSI125mAM5/r github.com/mattermost/logr/v2 v2.0.22/go.mod h1:0sUKpO+XNMZApeumaid7PYaUZPBIydfuWZ0dqixXo+s= github.com/mattermost/mattermost/server/public v0.1.15 h1:8Kn5KzJJtrw1VaBlEH8ijhF20z0rGMge2ejpuJROfKc= github.com/mattermost/mattermost/server/public v0.1.15/go.mod h1:EwEPMkzc87/mZYkpi46K0R4fe08HXniPDcpYTB3gv5s= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -1199,7 +1190,6 @@ github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -1527,7 +1517,6 @@ golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1601,7 +1590,6 @@ golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1703,8 +1691,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/gotestsum v1.7.0 h1:RwpqwwFKBAa2h+F6pMEGpE707Edld0etUD3GhqqhDNc= -gotest.tools/gotestsum v1.7.0/go.mod h1:V1m4Jw3eBerhI/A6qCxUE07RnCg7ACkKj9BYcAm09V8= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= diff --git a/manifest.go b/manifest.go index 945c94d..1191be9 100644 --- a/manifest.go +++ b/manifest.go @@ -47,6 +47,12 @@ const manifest = JSON.parse(` + "`%s`" + `); export default manifest; ` +// PluginCtlConfig represents the configuration for pluginctl stored in the manifest props. +type PluginCtlConfig struct { + Version string `json:"version,omitempty"` + IgnoreAssets []string `json:"ignore_assets,omitempty"` +} + // LoadPluginManifest loads and parses the plugin.json file from the current directory. func LoadPluginManifest() (*model.Manifest, error) { return LoadPluginManifestFromPath(".") @@ -188,15 +194,7 @@ Examples: } var buf bytes.Buffer - if err := tmpl.Execute(&buf, struct { - model.Manifest - HasWebapp bool - HasServer bool - }{ - Manifest: *manifest, - HasWebapp: HasWebappCode(manifest), - HasServer: HasServerCode(manifest), - }); err != nil { + if err := tmpl.Execute(&buf, *manifest); err != nil { return fmt.Errorf("failed to execute template: %w", err) } diff --git a/pluginctl.go b/pluginctl.go index 13cfd29..59d0de2 100644 --- a/pluginctl.go +++ b/pluginctl.go @@ -17,12 +17,6 @@ const ( UnknownVersion = "unknown" ) -// PluginCtlConfig represents the configuration for pluginctl stored in the manifest props. -type PluginCtlConfig struct { - Version string `json:"version,omitempty"` - IgnoreAssets []string `json:"ignore_assets,omitempty"` -} - // IsValidPluginDirectory checks if the current directory contains a valid plugin. func IsValidPluginDirectory() error { _, err := LoadPluginManifest() @@ -114,7 +108,7 @@ func SavePluginCtlConfig(manifest *model.Manifest, config *PluginCtlConfig) { manifest.Props["pluginctl"] = config } -// ValidateAndUpdateVersion checks the plugin version for compatibility. +// ValidateAndUpdateVersion checks the plugin version and updates it if necessary. func ValidateAndUpdateVersion(pluginPath string) error { // Load the manifest manifest, err := LoadPluginManifestFromPath(pluginPath) @@ -141,33 +135,15 @@ func ValidateAndUpdateVersion(pluginPath string) error { config.Version, currentVersion) } - return nil -} + // Update version if different + if config.Version != currentVersion { + config.Version = currentVersion + SavePluginCtlConfig(manifest, config) -// UpdatePluginCtlVersion updates the pluginctl version in the manifest. -func UpdatePluginCtlVersion(pluginPath string) error { - // Load the manifest - manifest, err := LoadPluginManifestFromPath(pluginPath) - if err != nil { - return fmt.Errorf("failed to load plugin manifest: %w", err) - } - - // Get current pluginctl version - currentVersion := GetVersion() - - // Parse existing pluginctl config - config, err := ParsePluginCtlConfig(manifest) - if err != nil { - return fmt.Errorf("failed to parse pluginctl config: %w", err) - } - - // Update version - config.Version = currentVersion - SavePluginCtlConfig(manifest, config) - - // Save the updated manifest - if err := WritePluginManifest(manifest, pluginPath); err != nil { - return fmt.Errorf("failed to update version in manifest: %w", err) + // Save the updated manifest + if err := WritePluginManifest(manifest, pluginPath); err != nil { + return fmt.Errorf("failed to update version in manifest: %w", err) + } } return nil diff --git a/tools.go b/tools.go deleted file mode 100644 index 06e6a31..0000000 --- a/tools.go +++ /dev/null @@ -1,386 +0,0 @@ -// NOTE: We download tools directly from tarball/binary releases instead of using -// `go get -tool` to prevent modifications to plugin go.mod files on plugins. - -package pluginctl - -import ( - "archive/tar" - "compress/gzip" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "runtime" - "strings" -) - -const ( - defaultGolangciLintVersion = "v2.3.1" - defaultGotestsumVersion = "v1.7.0" - defaultBinDir = "./build/bin" - helpFlagLong = "--help" - helpFlagShort = "-h" - exeExtension = ".exe" - tempSuffix = "-temp" - // Platform constants. - platformDarwin = "darwin" - platformWindows = "windows" - platformLinux = "linux" - // Architecture constants. - archARM64 = "arm64" - archAMD64 = "amd64" - arch386 = "386" - // File permission constants. - dirPerm = 0750 - filePerm = 0600 -) - -// ToolConfig represents configuration for downloading and installing a tool. -type ToolConfig struct { - Name string - Version string - GitHubRepo string - URLTemplate string - FilenameTemplate string - BinaryPath string // Path within archive (e.g., "bin/tool" or "tool") -} - -var toolConfigs = map[string]ToolConfig{ - "golangci-lint": { - Name: "golangci-lint", - Version: defaultGolangciLintVersion, - GitHubRepo: "golangci/golangci-lint", - URLTemplate: "https://github.com/{repo}/releases/download/{version}/" + - "golangci-lint-{version_no_v}-{os}-{arch}.tar.gz", - FilenameTemplate: "golangci-lint-{version_no_v}-{os}-{arch}.tar.gz", - BinaryPath: "golangci-lint-{version_no_v}-{os}-{arch}/golangci-lint", - }, - "gotestsum": { - Name: "gotestsum", - Version: defaultGotestsumVersion, - GitHubRepo: "gotestyourself/gotestsum", - URLTemplate: "https://github.com/{repo}/releases/download/{version}/" + - "gotestsum_{version_no_v}_{os}_{arch}.tar.gz", - FilenameTemplate: "gotestsum_{version_no_v}_{os}_{arch}.tar.gz", - BinaryPath: "gotestsum", - }, -} - -func RunToolsCommand(args []string, pluginPath string) error { - if len(args) == 0 { - return showToolsUsage() - } - - subcommand := args[0] - subcommandArgs := args[1:] - - switch subcommand { - case "install": - return runToolsInstallCommand(subcommandArgs) - case "help", helpFlagLong, helpFlagShort: - return showToolsUsage() - default: - return fmt.Errorf("unknown tools subcommand: %s", subcommand) - } -} - -func runToolsInstallCommand(args []string) error { - binDir := defaultBinDir - - for i, arg := range args { - if arg == helpFlagLong || arg == helpFlagShort { - return showToolsInstallUsage() - } - if arg == "--bin-dir" && i+1 < len(args) { - binDir = args[i+1] - } - } - - Logger.Info("Installing development tools...", "bin-dir", binDir) - - if err := os.MkdirAll(binDir, dirPerm); err != nil { - return fmt.Errorf("failed to create bin directory: %w", err) - } - - for toolName := range toolConfigs { - if err := installTool(toolName, binDir); err != nil { - return fmt.Errorf("failed to install %s: %w", toolName, err) - } - } - - Logger.Info("All development tools installed successfully") - - return nil -} - -// getPlatform returns the platform string for tool downloads. -func getPlatform() string { - switch runtime.GOOS { - case platformDarwin: - return platformDarwin - case platformWindows: - return platformWindows - default: - return platformLinux - } -} - -// getArchitecture returns the architecture string for tool downloads. -func getArchitecture() string { - switch runtime.GOARCH { - case archARM64: - return archARM64 - case arch386: - return arch386 - default: - return archAMD64 - } -} - -// expandTemplate replaces placeholders in template strings. -func expandTemplate(template string, config *ToolConfig, platform, arch string) string { - versionNoV := strings.TrimPrefix(config.Version, "v") - - replacements := map[string]string{ - "{repo}": config.GitHubRepo, - "{version}": config.Version, - "{version_no_v}": versionNoV, - "{os}": platform, - "{arch}": arch, - } - - result := template - for placeholder, value := range replacements { - result = strings.ReplaceAll(result, placeholder, value) - } - - return result -} - -// downloadAndExtractTool downloads and extracts a tool from GitHub releases. -func downloadAndExtractTool(config *ToolConfig, binDir string) error { - platform := getPlatform() - arch := getArchitecture() - - downloadURL := expandTemplate(config.URLTemplate, config, platform, arch) - binaryPathInArchive := expandTemplate(config.BinaryPath, config, platform, arch) - - Logger.Info("Downloading tool", "tool", config.Name, "url", downloadURL) - - resp, err := downloadToolFromURL(downloadURL, config.Name) //nolint:gosec // Trusted source - if err != nil { - return err - } - defer func() { - if closeErr := resp.Body.Close(); closeErr != nil { - Logger.Error("Failed to close response body", "error", closeErr) - } - }() - - return extractToolFromArchive(resp.Body, config, binDir, binaryPathInArchive) -} - -// downloadToolFromURL downloads a tool from the specified URL. -func downloadToolFromURL(downloadURL, toolName string) (*http.Response, error) { - resp, err := http.Get(downloadURL) //nolint:gosec,noctx // URL from trusted configuration - if err != nil { - return nil, fmt.Errorf("failed to download %s: %w", toolName, err) - } - - if resp.StatusCode != http.StatusOK { - if closeErr := resp.Body.Close(); closeErr != nil { - Logger.Error("Failed to close response body", "error", closeErr) - } - - return nil, fmt.Errorf("failed to download %s: HTTP %d", toolName, resp.StatusCode) - } - - return resp, nil -} - -// extractToolFromArchive extracts the tool binary from a tar.gz archive. -func extractToolFromArchive(reader io.Reader, config *ToolConfig, binDir, binaryPathInArchive string) error { - gzr, err := gzip.NewReader(reader) - if err != nil { - return fmt.Errorf("failed to create gzip reader for %s: %w", config.Name, err) - } - defer func() { - if closeErr := gzr.Close(); closeErr != nil { - Logger.Error("Failed to close gzip reader", "error", closeErr) - } - }() - - tr := tar.NewReader(gzr) - - // Create final binary path - binaryName := config.Name - finalBinaryPath := filepath.Join(binDir, fmt.Sprintf("%s-%s", config.Name, config.Version)) - if runtime.GOOS == platformWindows { - binaryName += exeExtension - finalBinaryPath += exeExtension - } - - paths := binaryPaths{ - pathInArchive: binaryPathInArchive, - binaryName: binaryName, - finalPath: finalBinaryPath, - } - - return extractBinaryFromTar(tr, config, binDir, paths) -} - -// binaryPaths holds path information for binary extraction. -type binaryPaths struct { - pathInArchive string - binaryName string - finalPath string -} - -// extractBinaryFromTar searches and extracts the binary from a tar archive. -func extractBinaryFromTar(tr *tar.Reader, config *ToolConfig, binDir string, paths binaryPaths) error { - for { - header, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return fmt.Errorf("failed to read tar archive for %s: %w", config.Name, err) - } - - if isBinaryFile(header.Name, paths.pathInArchive, paths.binaryName, config.Name) { - return saveBinaryFile(tr, config, binDir, paths.finalPath) - } - } - - return fmt.Errorf("%s binary not found in archive", config.Name) -} - -// isBinaryFile checks if the file matches the binary we're looking for. -func isBinaryFile(fileName, binaryPathInArchive, binaryName, configName string) bool { - return fileName == binaryPathInArchive || - strings.HasSuffix(fileName, "/"+binaryName) || - strings.HasSuffix(fileName, configName) -} - -// saveBinaryFile saves the binary from tar reader to disk. -func saveBinaryFile(tr *tar.Reader, config *ToolConfig, binDir, finalBinaryPath string) error { - tempPath := filepath.Join(binDir, config.Name+tempSuffix) - if runtime.GOOS == platformWindows { - tempPath += exeExtension - } - - file, err := os.OpenFile(tempPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, filePerm) - if err != nil { - return fmt.Errorf("failed to create temporary binary file for %s: %w", config.Name, err) - } - - _, err = io.Copy(file, tr) //nolint:gosec // Archive from trusted source - if closeErr := file.Close(); closeErr != nil { - Logger.Error("Failed to close temp file", "error", closeErr) - } - if err != nil { - return fmt.Errorf("failed to write binary file for %s: %w", config.Name, err) - } - - if err := os.Rename(tempPath, finalBinaryPath); err != nil { - return fmt.Errorf("failed to rename binary to final path for %s: %w", config.Name, err) - } - - Logger.Info("Tool installed successfully", "tool", config.Name, "path", finalBinaryPath) - - return nil -} - -// installTool installs a single tool by name using its configuration. -func installTool(toolName, binDir string) error { - config, exists := toolConfigs[toolName] - if !exists { - return fmt.Errorf("unknown tool: %s", toolName) - } - - binaryPath := filepath.Join(binDir, fmt.Sprintf("%s-%s", config.Name, config.Version)) - symlinkPath := filepath.Join(binDir, config.Name) - - if runtime.GOOS == platformWindows { - binaryPath += exeExtension - symlinkPath += exeExtension - } - - if fileExists(binaryPath) { - return createSymlink(binaryPath, symlinkPath) - } - - Logger.Info("Installing tool", "tool", config.Name, "version", config.Version) - - if err := downloadAndExtractTool(&config, binDir); err != nil { - return err - } - - return createSymlink(binaryPath, symlinkPath) -} - -func fileExists(path string) bool { - _, err := os.Stat(path) - - return err == nil -} - -func createSymlink(target, link string) error { - if fileExists(link) { - if err := os.Remove(link); err != nil { - return fmt.Errorf("failed to remove existing symlink: %w", err) - } - } - - targetRel, err := filepath.Rel(filepath.Dir(link), target) - if err != nil { - return fmt.Errorf("failed to calculate relative path: %w", err) - } - - if err := os.Symlink(targetRel, link); err != nil { - return fmt.Errorf("failed to create symlink: %w", err) - } - - Logger.Info("Created symlink", "target", target, "link", link) - - return nil -} - -func showToolsUsage() error { - usageText := `Tools command - Manage development tools - -Usage: - pluginctl tools [options] - -Subcommands: - install Install development tools (golangci-lint, gotestsum) - -Use 'pluginctl tools --help' for detailed information about a subcommand. -` - Logger.Info(usageText) - - return nil -} - -func showToolsInstallUsage() error { - usageText := `Install development tools - -Usage: - pluginctl tools install - -Description: - Installs the following development tools to ./bin/ directory: - - golangci-lint ` + defaultGolangciLintVersion + ` - - gotestsum ` + defaultGotestsumVersion + ` - - Tools are downloaded with version-specific names (e.g., golangci-lint-v2.3.1) - to allow version tracking and prevent unnecessary re-downloads. - -Options: - --help, -h Show this help message -` - Logger.Info(usageText) - - return nil -} diff --git a/updateassets.go b/updateassets.go index 8ee1fce..472b691 100644 --- a/updateassets.go +++ b/updateassets.go @@ -87,11 +87,6 @@ Examples: Logger.Info("Assets updated successfully!", "files_updated", updatedCount) - // Store the current pluginctl version in the manifest after successful update - if err := UpdatePluginCtlVersion(pluginPath); err != nil { - return fmt.Errorf("failed to save version to manifest: %w", err) - } - return nil }