From 609a1a0859eb5f7d9c87c5e88a40d6a5f22e857c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Wed, 13 May 2026 17:40:23 +0200 Subject: [PATCH 01/21] update .gitignore and CLAUDE.md --- .claude/.gitignore | 2 ++ .gitignore | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .claude/.gitignore diff --git a/.claude/.gitignore b/.claude/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/.claude/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/.gitignore b/.gitignore index 37d51964..77a41f92 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /.direnv/ /result .idea -.sandbox include/ .sandbox/ .tool-versions From 9f968f45b6ebcd6a153ed172dc0894d855c2b5c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 10:31:38 +0200 Subject: [PATCH 02/21] chore(lint): bump golangci-lint to v2.1.6 and migrate config v2 introduces a new config schema: 'version: "2"' is required, linters move under 'linters.default/enable', settings live in 'linters.settings', and formatters (gofmt, goimports) are split into a 'formatters' section. gosimple and typecheck are folded into staticcheck and removed. --- .golangci.yaml | 34 ++++++++++++++++++---------------- tools/install.sh | 2 +- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 526563e6..97fb9a55 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,3 +1,5 @@ +version: "2" + run: timeout: 10m concurrency: 16 @@ -6,17 +8,12 @@ run: output: formats: - - format: colored-line-number - -linters-settings: - godox: - keywords: - - FIXME - -issues: - max-same-issues: 0 + text: + path: stdout + colors: true linters: + default: none enable: - asasalint - asciicheck @@ -39,13 +36,10 @@ linters: - gochecksumtype - gocritic - godox - - gofmt - goheader - - goimports - gomodguard - goprintffuncname - gosec - - gosimple - gosmopolitan - govet - grouper @@ -72,19 +66,27 @@ linters: - staticcheck - tagalign - tagliatelle - - usetesting - testableexamples - testifylint - thelper - tparallel - - typecheck - unconvert - unparam - unused - usestdlibvars + - usetesting - wastedassign - whitespace - zerologlint + settings: + godox: + keywords: + - FIXME + +issues: + max-same-issues: 0 - disable-all: true - fast: false +formatters: + enable: + - gofmt + - goimports diff --git a/tools/install.sh b/tools/install.sh index 1a7f659b..eed09be1 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -20,4 +20,4 @@ echo "GOBIN=${GOBIN}" rm -rf tmp # https://github.com/golangci/golangci-lint#go Please, do not installDaemon golangci-lint by go get -curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$GOBIN" v1.64.7 +curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$GOBIN" v2.1.6 From 0c91ba786d8981525f0cd6bb89c5c52310abca2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 10:32:02 +0200 Subject: [PATCH 03/21] feat(release): introduce goreleaser for release pipeline .goreleaser.yaml replaces the hand-rolled per-platform build/upload steps. Asset names are preserved exactly (zcli-, the zcli--npm.tar.gz tarball with builds/ inside used by @zerops/zcli npm package, and zcli__.deb) so consumers are unaffected. Pinned to v2.5.0. Local invocation via 'gomodrun goreleaser' (installed by tools/install.sh). Makefile targets: - make goreleaser-check -> validate config - make goreleaser-snapshot -> dry-run full release build to ./dist --- .gitignore | 2 + .goreleaser.yaml | 105 +++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 10 +++++ tools/install.sh | 7 ++++ 4 files changed, 124 insertions(+) create mode 100644 .goreleaser.yaml diff --git a/.gitignore b/.gitignore index 77a41f92..903bab2f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ tools/npm/node_modules *.DS_Store # ignore built binary in root dir /zcli +# goreleaser output +/dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 00000000..7e728b36 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,105 @@ +version: 2 + +project_name: zcli + +builds: + - id: linux-amd64 + main: ./cmd/zcli/main.go + binary: zcli-linux-amd64 + goos: [linux] + goarch: [amd64] + env: + - CGO_ENABLED=0 + flags: [-trimpath] + ldflags: + - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} + + - id: linux-386 + main: ./cmd/zcli/main.go + binary: zcli-linux-i386 + goos: [linux] + goarch: ["386"] + env: + - CGO_ENABLED=0 + flags: [-trimpath] + ldflags: + - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} + + - id: darwin-amd64 + main: ./cmd/zcli/main.go + binary: zcli-darwin-amd64 + goos: [darwin] + goarch: [amd64] + env: + - CGO_ENABLED=0 + flags: [-trimpath] + ldflags: + - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} + + - id: darwin-arm64 + main: ./cmd/zcli/main.go + binary: zcli-darwin-arm64 + goos: [darwin] + goarch: [arm64] + env: + - CGO_ENABLED=0 + flags: [-trimpath] + ldflags: + - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} + + - id: windows-amd64 + main: ./cmd/zcli/main.go + binary: zcli-win-x64 + goos: [windows] + goarch: [amd64] + env: + - CGO_ENABLED=0 + flags: [-trimpath] + ldflags: + - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} + +upx: + - enabled: true + ids: [linux-amd64, linux-386] + compress: best + +archives: + - id: raw + builds: [linux-amd64, linux-386, darwin-amd64, darwin-arm64, windows-amd64] + format: binary + name_template: >- + {{- if eq .Os "windows" -}}zcli-win-x64 + {{- else if and (eq .Os "linux") (eq .Arch "386") -}}zcli-linux-i386 + {{- else -}}zcli-{{ .Os }}-{{ .Arch }} + {{- end -}} + + - id: npm + builds: [linux-amd64, linux-386, darwin-amd64, darwin-arm64, windows-amd64] + format: tar.gz + wrap_in_directory: builds + files: + - none* + name_template: >- + {{- if eq .Os "windows" -}}zcli-win-x64.exe-npm + {{- else if and (eq .Os "linux") (eq .Arch "386") -}}zcli-linux-i386-npm + {{- else -}}zcli-{{ .Os }}-{{ .Arch }}-npm + {{- end -}} + +nfpms: + - id: deb + package_name: zcli + builds: [linux-amd64, linux-386] + maintainer: "Zerops " + description: "Zerops CLI" + homepage: "https://github.com/zeropsio/zcli" + formats: [deb] + bindir: /usr/local/bin + file_name_template: >- + zcli_{{ .Tag }}_{{ if eq .Arch "386" }}i386{{ else }}{{ .Arch }}{{ end }} + +release: + mode: append + prerelease: auto + +changelog: + disable: true diff --git a/Makefile b/Makefile index 172f71b1..85ea6a39 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ possible targets: - dawrin-amd - darwin-arm - showcase +- goreleaser-check +- goreleaser-snapshot endef export helpMessage @@ -52,3 +54,11 @@ darwin-arm: # showcase of ui elements showcase: go run src/uxBlock/showcase/main.go + +# validate .goreleaser.yaml +goreleaser-check: + gomodrun goreleaser check + +# dry-run release build to ./dist (no upload, no publish) +goreleaser-snapshot: + gomodrun goreleaser release --snapshot --clean --skip=publish diff --git a/tools/install.sh b/tools/install.sh index eed09be1..54512828 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -21,3 +21,10 @@ rm -rf tmp # https://github.com/golangci/golangci-lint#go Please, do not installDaemon golangci-lint by go get curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$GOBIN" v2.1.6 + +GORELEASER_VERSION=v2.5.0 +GORELEASER_OS=$(uname -s) +GORELEASER_ARCH=$(uname -m) +curl -sSfL "https://github.com/goreleaser/goreleaser/releases/download/${GORELEASER_VERSION}/goreleaser_${GORELEASER_OS}_${GORELEASER_ARCH}.tar.gz" \ + | tar -xz -C "$GOBIN" goreleaser +chmod +x "$GOBIN/goreleaser" From fee19178e30d443f4ac1dd6e1869827f2ba19e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 10:32:14 +0200 Subject: [PATCH 04/21] ci: restructure workflows around split jobs and goreleaser main.yml: lint and test now run once each in dedicated jobs instead of 5x duplicated across the build matrix (previous setup referenced an undefined ${{ matrix.osEnv }}, so all lint/test invocations actually ran as linux/amd64). Build matrix is preserved as a compile-only check for each target platform. Adds concurrency cancellation, per-job timeouts, fail-fast: false, and a goreleaser check job. release.yml: full pipeline now driven by goreleaser, replacing ~140 lines of upload/packaging steps. Released and prereleased events share one workflow; npm publish and the Discord notification are gated to full releases via 'github.event.action == 'released''. Pre-release UPX compression and .deb packaging now happen for prereleases too, which is a deliberate simplification. pre-release.yml: deleted, folded into release.yml. --- .github/workflows/main.yml | 82 ++++++++++------ .github/workflows/pre-release.yml | 80 --------------- .github/workflows/release.yml | 158 ++++++------------------------ 3 files changed, 84 insertions(+), 236 deletions(-) delete mode 100644 .github/workflows/pre-release.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d5525f4d..949e7f6a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,69 +8,95 @@ on: branches: - '*' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + jobs: + test: + name: test + runs-on: ubuntu-22.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + + - name: install tooling + run: ./tools/install.sh + + - name: test + run: go test -v ./cmd/... ./src/... + + lint: + name: lint + runs-on: ubuntu-22.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + + - name: install tooling + run: ./tools/install.sh + + - name: lint + run: gomodrun golangci-lint run ./cmd/... ./src/... --verbose + build: - name: build && tests for ${{ matrix.name }} + name: build ${{ matrix.name }} runs-on: ubuntu-22.04 + timeout-minutes: 10 env: CGO_ENABLED: '0' strategy: + fail-fast: false matrix: include: - name: linux amd64 env: GOOS: linux GOARCH: amd64 - runTests: true - deb_arch: amd64 - - name: linux 386 env: GOOS: linux GOARCH: 386 - runTests: false - deb_arch: i386 - - name: darwin amd64 env: GOOS: darwin GOARCH: amd64 - runTests: true - - name: darwin arm64 env: GOOS: darwin GOARCH: arm64 - runTests: false - - name: windows amd64 env: GOOS: windows GOARCH: amd64 - runTests: true steps: - - name: check out code into the Go module directory - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: set up Go 1.x - uses: actions/setup-go@v5 + - uses: actions/setup-go@v5 with: go-version-file: "go.mod" - id: go - - - name: get dependencies - run: | - export GOPATH=$HOME/go - ./tools/install.sh - name: build env: ${{ matrix.env }} run: go build -v ./cmd/... ./src/... - - name: test - if: ${{ matrix.runTests }} - run: env ${{ matrix.osEnv }} go test -v ./cmd/... ./src/... - - - name: lint - run: env ${{ matrix.osEnv }} gomodrun golangci-lint run ./cmd/... ./src/... --verbose + goreleaser-check: + name: goreleaser check + runs-on: ubuntu-22.04 + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: v2.5.0 + args: check diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml deleted file mode 100644 index 5033ecc5..00000000 --- a/.github/workflows/pre-release.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: pre-release - -on: - release: - types: - - prereleased - -jobs: - build: - name: build & upload ${{ matrix.name }} - runs-on: ubuntu-22.04 - env: - CGO_ENABLED: '0' - strategy: - matrix: - include: - - name: linux amd64 - env: - GOOS: linux - GOARCH: amd64 - file: zcli-linux-amd64 - - - name: linux 386 - env: - GOOS: linux - GOARCH: 386 - file: zcli-linux-i386 - - - name: darwin amd64 - env: - GOOS: darwin - GOARCH: amd64 - buildCmd: >- - file: zcli-darwin-amd64 - - - name: darwin arm64 - env: - GOOS: darwin - GOARCH: arm64 - file: zcli-darwin-arm64 - - - name: windows amd64 - env: - GOOS: windows - GOARCH: amd64 - file: zcli-win-x64.exe - - steps: - - name: checkout code - uses: actions/checkout@v4 - - - name: set up Go 1.x - uses: actions/setup-go@v5 - with: - go-version-file: "go.mod" - id: go - - - name: get dependencies - run: | - export GOPATH=$HOME/go - ./tools/install.sh - - - name: build - env: ${{ matrix.env }} - run: >- - go build - -tags devel - -o builds/${{ matrix.file }} - -ldflags "-X github.com/zeropsio/zcli/src/version.version=${{ github.event.release.tag_name }}" - ./cmd/zcli/main.go - - - name: upload asset clean bin - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ./builds/${{ matrix.file }} - asset_name: ${{ matrix.file }} - asset_content_type: application/octet-stream diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53f5f708..e15fb905 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,148 +4,48 @@ on: release: types: - released + - prereleased + +concurrency: + group: ${{ github.workflow }}-${{ github.event.release.tag_name }} + cancel-in-progress: false jobs: - build: - name: build & upload ${{ matrix.name }} + goreleaser: + name: build & upload artifacts runs-on: ubuntu-22.04 - env: - CGO_ENABLED: '0' - strategy: - matrix: - include: - - name: linux amd64 - env: - GOOS: linux - GOARCH: amd64 - file: zcli-linux-amd64 - compress: true - strip: true - deb_arch: amd64 - - - name: linux 386 - env: - GOOS: linux - GOARCH: 386 - file: zcli-linux-i386 - compress: true - strip: true - deb_arch: i386 - - - name: darwin amd64 - env: - GOOS: darwin - GOARCH: amd64 - file: zcli-darwin-amd64 - compress: false - strip: false - - - name: darwin arm64 - env: - GOOS: darwin - GOARCH: arm64 - file: zcli-darwin-arm64 - compress: false - strip: false - - - name: windows amd64 - env: - GOOS: windows - GOARCH: amd64 - file: zcli-win-x64.exe - compress: false - strip: false - + timeout-minutes: 20 + permissions: + contents: write steps: - - name: checkout code - uses: actions/checkout@v4 - - - name: set up Go 1.x - uses: actions/setup-go@v5 - with: - go-version-file: "go.mod" - id: go - - - name: get dependencies - run: | - export GOPATH=$HOME/go - ./tools/install.sh - - - name: build - env: ${{ matrix.env }} - run: >- - go build - -o builds/${{ matrix.file }} - -ldflags "-s -w -X github.com/zeropsio/zcli/src/version.version=${{ github.event.release.tag_name }}" - ./cmd/zcli/main.go - - - name: compress binary - if: ${{ matrix.compress }} - uses: svenstaro/upx-action@v2 - with: - file: ./builds/${{ matrix.file }} - strip: ${{ matrix.strip }} - - - name: prepare .deb package tree - if: ${{ matrix.deb_arch }} - run: | - mkdir -p .debpkg/usr/local/bin - cp builds/${{ matrix.file }} .debpkg/usr/local/bin/zcli - chmod +x .debpkg/usr/local/bin/zcli - - - name: build .deb package - id: deb - if: ${{ matrix.deb_arch }} - uses: jiro4989/build-deb-action@v4 + - uses: actions/checkout@v4 with: - package: zcli - package_root: .debpkg - maintainer: 'Zerops ' - version: ${{ github.event.release.tag_name }} - arch: ${{ matrix.deb_arch }} - desc: 'Zerops CLI' - homepage: 'https://github.com/zeropsio/zcli' + fetch-depth: 0 + ref: ${{ github.event.release.tag_name }} - - name: upload .deb asset - if: ${{ matrix.deb_arch }} - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/setup-go@v5 with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ${{ steps.deb.outputs.file_name }} - asset_name: zcli_${{ github.event.release.tag_name }}_${{ matrix.deb_arch }}.deb - asset_content_type: application/vnd.debian.binary-package + go-version-file: "go.mod" - - name: package binaries for NPM - run: tar -czvf ${{ matrix.file }}-npm.tar.gz builds/${{ matrix.file }} + - name: install upx + run: sudo apt-get update && sudo apt-get install -y upx-ucl - - name: upload asset for NPM - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: goreleaser/goreleaser-action@v6 with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ./${{ matrix.file }}-npm.tar.gz - asset_name: ${{ matrix.file }}-npm.tar.gz - asset_content_type: application/octet-stream - - - name: upload asset clean bin - uses: actions/upload-release-asset@v1 + distribution: goreleaser + version: v2.5.0 + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ./builds/${{ matrix.file }} - asset_name: ${{ matrix.file }} - asset_content_type: application/octet-stream publish-npm: - needs: build + needs: goreleaser + if: github.event.action == 'released' runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 22 registry-url: https://registry.npmjs.org/ @@ -156,11 +56,13 @@ jobs: replace-in-files --string='v0.0.0-zerops' --replacement='${{ github.event.release.tag_name }}' package.json npm publish --access=public env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} report: - needs: build + needs: goreleaser + if: github.event.action == 'released' runs-on: ubuntu-latest + timeout-minutes: 5 steps: - name: Notify discord about new release uses: sarisia/actions-status-discord@v1.15.0 From 33e62c50e64c1f474406c4fe0c56ab4bc5aff49d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 10:47:08 +0200 Subject: [PATCH 05/21] chore(make): self-documenting help and grouped target sections Replace the hand-maintained help message with awk that reads '## desc' comments from the target lines themselves, so help can't drift from the real targets. Group targets under '##@ Section' headers, declare every phony target in .PHONY, and add inline comments for the non-obvious mechanics (build.sh ldflags, per-GOOS lint pass, gomodrun indirection). --- Makefile | 96 ++++++++++++++++++++++++++------------------------------ 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index 85ea6a39..8471118e 100644 --- a/Makefile +++ b/Makefile @@ -1,64 +1,56 @@ -.PHONY: help - -define helpMessage -possible targets: -- test -- test-integration -- lint -- all -- windows-amd -- linux-amd -- dawrin-amd -- darwin-arm -- showcase -- goreleaser-check -- goreleaser-snapshot -endef -export helpMessage - -help: - @echo "$$helpMessage" - -test: - go test -v ./cmd/... ./src/... - -test-integration: - go test -v -tags devel ./src/cmd/... - -lint: - GOOS=darwin GOARCH=arm64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose - GOOS=linux GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose - GOOS=windows GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose - -######### -# BUILD # -######### -all: windows-amd linux-amd darwin-amd darwin-arm - -windows-amd: +.DEFAULT_GOAL := help + +.PHONY: help test test-integration lint all windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot + +# Self-documenting help. Targets are listed in the order they appear here; +# their description is the text after the '##' on the recipe line. +help: ## Show this help. + @awk 'BEGIN { FS = ":.*## " } \ + /^##@ / { printf "\n%s\n", substr($$0, 5); next } \ + /^[a-zA-Z0-9_-]+:.*## / { printf " %-22s %s\n", $$1, $$2 }' $(MAKEFILE_LIST) + +##@ Development + +test: ## Run the full Go test suite. + go test -v ./cmd/... ./src/... + +test-integration: ## Run the integration test suite (devel build tag). + go test -v -tags devel ./src/cmd/... + +# Lint each target GOOS in turn so platform-specific build tags get covered. +lint: ## Run golangci-lint for darwin/arm64, linux/amd64, windows/amd64. + GOOS=darwin GOARCH=arm64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose + GOOS=linux GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose + GOOS=windows GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose + +##@ Build + +# tools/build.sh embeds version metadata via -ldflags and writes to ./bin/. +all: windows-amd linux-amd darwin-amd darwin-arm ## Cross-build all release targets. + +windows-amd: ## Build the windows/amd64 binary. GOOS=windows GOARCH=amd64 tools/build.sh zcli.win.exe -linux-amd: +linux-amd: ## Build the linux/amd64 binary. GOOS=linux GOARCH=amd64 tools/build.sh zcli.linux -darwin-amd: +darwin-amd: ## Build the darwin/amd64 binary. GOOS=darwin GOARCH=amd64 tools/build.sh zcli.darwin.amd64 -darwin-arm: +darwin-arm: ## Build the darwin/arm64 binary. GOOS=darwin GOARCH=arm64 tools/build.sh zcli.darwin.arm64 -######### -# OTHER # -######### - -# showcase of ui elements -showcase: - go run src/uxBlock/showcase/main.go +##@ Release tooling -# validate .goreleaser.yaml -goreleaser-check: +# Both targets shell out to the pinned goreleaser in ./bin via gomodrun; +# tools/install.sh provisions it alongside golangci-lint. +goreleaser-check: ## Validate .goreleaser.yaml without building. gomodrun goreleaser check -# dry-run release build to ./dist (no upload, no publish) -goreleaser-snapshot: +goreleaser-snapshot: ## Dry-run a full release build to ./dist (no upload, no publish). gomodrun goreleaser release --snapshot --clean --skip=publish + +##@ Other + +showcase: ## Preview uxBlock UI elements in the terminal. + go run src/uxBlock/showcase/main.go From 408aafcc35d570f389d34ec4d1e601f06bbdf65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 10:52:37 +0200 Subject: [PATCH 06/21] chore: bump go to 1.26 CI uses setup-go with go-version-file, so it auto-tracks go.mod. Updates the version mention in CLAUDE.md too. --- CLAUDE.md | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2e766508..04d08b7f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Overview -`zcli` is the Zerops CLI written in Go (module `github.com/zeropsio/zcli`, Go 1.24). The entrypoint is `cmd/zcli/main.go`, which delegates to `src/cmd.ExecuteCmd()`. All implementation lives under `src/`. +`zcli` is the Zerops CLI written in Go (module `github.com/zeropsio/zcli`, Go 1.26). The entrypoint is `cmd/zcli/main.go`, which delegates to `src/cmd.ExecuteCmd()`. All implementation lives under `src/`. ## Common commands diff --git a/go.mod b/go.mod index 0c6881fb..90790893 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zeropsio/zcli -go 1.24.0 +go 1.26 require github.com/zeropsio/zerops-go v1.0.18 From 595371d86ab45dba5dcd29e62004e39bf68bbf46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 11:02:11 +0200 Subject: [PATCH 07/21] ci: stop calling tools/install.sh in workflows install.sh assumes $GOPATH is set and writes binaries to $GOPATH/bin. In the runner $GOPATH is empty, so it expands to /bin and the gomodrun install fails with permission denied. The lint job now installs golangci-lint via golangci/golangci-lint-action, and the test job doesn't need the wrapper at all. install.sh stays as the local developer entrypoint. --- .github/workflows/main.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 949e7f6a..63844d37 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,9 +24,6 @@ jobs: with: go-version-file: "go.mod" - - name: install tooling - run: ./tools/install.sh - - name: test run: go test -v ./cmd/... ./src/... @@ -41,11 +38,10 @@ jobs: with: go-version-file: "go.mod" - - name: install tooling - run: ./tools/install.sh - - - name: lint - run: gomodrun golangci-lint run ./cmd/... ./src/... --verbose + - uses: golangci/golangci-lint-action@v7 + with: + version: v2.1.6 + args: --verbose ./cmd/... ./src/... build: name: build ${{ matrix.name }} From 05df7e7a0cb38a83bdc27ab63c0b3f31d23dbd97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 11:24:57 +0200 Subject: [PATCH 08/21] chore: port tools/install.sh into Makefile, drop gomodrun MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install.sh script assumed $GOPATH was set and used a wrapper binary (gomodrun) to find tools in ./bin from arbitrary CWDs. CI didn't set $GOPATH so install.sh expanded GOBIN to /bin and failed with permission denied. Replaced both with Makefile rules. Versions are pinned at the top of the Makefile and a stamp file (./bin/.-) tracks each install — bumping a version retargets the dependency, the old stamp is removed in the recipe, and the tool gets reinstalled. lint and the goreleaser-* targets depend on the tool stamps, so a first invocation installs automatically. gomodrun is gone because Make recipes always run from the repo root, so plain ./bin/ works without a path-discovery wrapper. --- CLAUDE.md | 5 ++-- Makefile | 51 +++++++++++++++++++++++++++++--------- tools/gomodrun.go | 62 ----------------------------------------------- tools/install.sh | 30 ----------------------- 4 files changed, 43 insertions(+), 105 deletions(-) delete mode 100644 tools/gomodrun.go delete mode 100755 tools/install.sh diff --git a/CLAUDE.md b/CLAUDE.md index 04d08b7f..59c2242f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,10 +9,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Common commands - `make test` — runs `go test -v ./cmd/... ./src/...`. Run a single test with `go test -v -run TestName ./src//...`. -- `make lint` — runs `golangci-lint` for darwin/arm64, linux/amd64, and windows/amd64 in turn via the `gomodrun` wrapper. Config: `.golangci.yaml`. +- `make lint` — runs the pinned `./bin/golangci-lint` for darwin/arm64, linux/amd64, and windows/amd64. Config: `.golangci.yaml`. - `make all` — cross-builds binaries for windows-amd, linux-amd, darwin-amd, darwin-arm. Single-target builds: `make linux-amd` etc. Builds go through `tools/build.sh`, which sets the version via `-ldflags` from `git rev-parse`/`git describe`, uses build tag `devel`, and writes to `bin/`. +- `make goreleaser-check` / `make goreleaser-snapshot` — validate `.goreleaser.yaml` and dry-run a release build to `./dist`. +- `make tools` — install pinned dev tooling (`golangci-lint`, `goreleaser`) into `./bin`. Versions are declared at the top of the Makefile; bumping them triggers reinstall via stamp files. `make lint` and the goreleaser targets depend on `tools`, so first invocation auto-installs. - `make showcase` — runs `src/uxBlock/showcase/main.go` to preview UI elements. -- `gomodrun` (`tools/gomodrun.go`) executes binaries from `./bin` relative to the nearest `go.mod`; the lint target relies on it to find a pinned `golangci-lint`. ## Architecture diff --git a/Makefile b/Makefile index 8471118e..9fb02802 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,12 @@ .DEFAULT_GOAL := help -.PHONY: help test test-integration lint all windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot +BIN := $(CURDIR)/bin +GOLANGCI_LINT_VERSION := v2.1.6 +GORELEASER_VERSION := v2.5.0 +UNAME_S := $(shell uname -s) +UNAME_M := $(shell uname -m) + +.PHONY: help test test-integration lint all windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot tools clean-tools # Self-documenting help. Targets are listed in the order they appear here; # their description is the text after the '##' on the recipe line. @@ -18,10 +24,10 @@ test-integration: ## Run the integration test suite (devel build tag). go test -v -tags devel ./src/cmd/... # Lint each target GOOS in turn so platform-specific build tags get covered. -lint: ## Run golangci-lint for darwin/arm64, linux/amd64, windows/amd64. - GOOS=darwin GOARCH=arm64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose - GOOS=linux GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose - GOOS=windows GOARCH=amd64 gomodrun golangci-lint run ./cmd/... ./src/... --verbose +lint: $(BIN)/.golangci-lint-$(GOLANGCI_LINT_VERSION) ## Run golangci-lint for darwin/arm64, linux/amd64, windows/amd64. + GOOS=darwin GOARCH=arm64 $(BIN)/golangci-lint run ./cmd/... ./src/... --verbose + GOOS=linux GOARCH=amd64 $(BIN)/golangci-lint run ./cmd/... ./src/... --verbose + GOOS=windows GOARCH=amd64 $(BIN)/golangci-lint run ./cmd/... ./src/... --verbose ##@ Build @@ -42,13 +48,36 @@ darwin-arm: ## Build the darwin/arm64 binary. ##@ Release tooling -# Both targets shell out to the pinned goreleaser in ./bin via gomodrun; -# tools/install.sh provisions it alongside golangci-lint. -goreleaser-check: ## Validate .goreleaser.yaml without building. - gomodrun goreleaser check +goreleaser-check: $(BIN)/.goreleaser-$(GORELEASER_VERSION) ## Validate .goreleaser.yaml without building. + $(BIN)/goreleaser check + +goreleaser-snapshot: $(BIN)/.goreleaser-$(GORELEASER_VERSION) ## Dry-run a full release build to ./dist (no upload, no publish). + $(BIN)/goreleaser release --snapshot --clean --skip=publish + +##@ Tooling + +tools: $(BIN)/.golangci-lint-$(GOLANGCI_LINT_VERSION) $(BIN)/.goreleaser-$(GORELEASER_VERSION) ## Install pinned dev tooling into ./bin. + +clean-tools: ## Remove installed tooling from ./bin. + rm -f $(BIN)/golangci-lint $(BIN)/goreleaser $(BIN)/.golangci-lint-* $(BIN)/.goreleaser-* + +# Stamp files encode the pinned version. Bumping a version above retargets +# the dependency, the old stamp is removed inside the recipe, and the tool +# gets reinstalled on the next `make lint` / `make goreleaser-*` / `make tools`. +$(BIN)/.golangci-lint-$(GOLANGCI_LINT_VERSION): + @mkdir -p $(BIN) + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \ + | sh -s -- -b $(BIN) $(GOLANGCI_LINT_VERSION) + @rm -f $(BIN)/.golangci-lint-* + @touch $@ -goreleaser-snapshot: ## Dry-run a full release build to ./dist (no upload, no publish). - gomodrun goreleaser release --snapshot --clean --skip=publish +$(BIN)/.goreleaser-$(GORELEASER_VERSION): + @mkdir -p $(BIN) + curl -sSfL "https://github.com/goreleaser/goreleaser/releases/download/$(GORELEASER_VERSION)/goreleaser_$(UNAME_S)_$(UNAME_M).tar.gz" \ + | tar -xz -C $(BIN) goreleaser + chmod +x $(BIN)/goreleaser + @rm -f $(BIN)/.goreleaser-* + @touch $@ ##@ Other diff --git a/tools/gomodrun.go b/tools/gomodrun.go deleted file mode 100644 index 437601d2..00000000 --- a/tools/gomodrun.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "log" - "os" - "os/exec" - "path" - "path/filepath" - - "github.com/pkg/errors" -) - -func getMod(dir string) (string, error) { - if _, err := os.Stat(path.Join(dir, "go.mod")); err == nil { - return dir, nil - } - newDir := filepath.Dir(dir) - if newDir != "" { - return getMod(newDir) - } - return "", errors.New("go.mod not found") -} - -func main() { - wd, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - modDir, err := getMod(wd) - if err != nil { - log.Fatal(err) - } - - if len(os.Args) < 2 { - log.Fatal("executable not defined") - return - } - - bin := os.Args[1] - if !path.IsAbs(bin) { - bin = path.Join(modDir, "bin", bin) - } - - if err := os.Setenv("PATH", path.Join(modDir, "bin")+":"+os.Getenv("PATH")); err != nil { - log.Fatal(err) - return - } - - cmd := &exec.Cmd{ - Path: bin, - Dir: wd, - Stdout: os.Stdout, - Stdin: os.Stdin, - Stderr: os.Stderr, - Args: os.Args[1:], - Env: os.Environ(), - } - if err := cmd.Run(); err != nil { - log.Fatal(err) - } - os.Exit(0) -} diff --git a/tools/install.sh b/tools/install.sh deleted file mode 100755 index 54512828..00000000 --- a/tools/install.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -cd `dirname $0` - -set -e - -cd .. -export GOBIN=$GOPATH/bin -export PATH=$GOBIN:$PATH - -[[ ! -d "${GOBIN}" ]] && mkdir -p "${GOBIN}" - -go install tools/gomodrun.go - -export GOBIN=$PWD/bin -export PATH="${GOBIN}:${PATH}" - -echo "GOBIN=${GOBIN}" - -rm -rf tmp - -# https://github.com/golangci/golangci-lint#go Please, do not installDaemon golangci-lint by go get -curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$GOBIN" v2.1.6 - -GORELEASER_VERSION=v2.5.0 -GORELEASER_OS=$(uname -s) -GORELEASER_ARCH=$(uname -m) -curl -sSfL "https://github.com/goreleaser/goreleaser/releases/download/${GORELEASER_VERSION}/goreleaser_${GORELEASER_OS}_${GORELEASER_ARCH}.tar.gz" \ - | tar -xz -C "$GOBIN" goreleaser -chmod +x "$GOBIN/goreleaser" From 0166a7740062dc7b67ca7f063a0c2d4cb55589c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 11:39:58 +0200 Subject: [PATCH 09/21] chore(lint): bump golangci-lint to v2.12.0 golangci-lint v2.1.6 is built with Go 1.24 and refuses to lint projects targeting Go 1.26 ('language version used to build is lower than targeted'). v2.12.0 is built with Go 1.26. The master install.sh checksum verification kept failing for v2.12.0 and v2.12.2 (computed vs. expected mismatch), so the Makefile now fetches the release tarball directly via the github.com/.../releases URL, mirroring the goreleaser install pattern. Both the Makefile pin and the CI golangci-lint-action version are bumped in lockstep. --- .github/workflows/main.yml | 4 ++-- Makefile | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 63844d37..17574692 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Main +name: main on: push: @@ -40,7 +40,7 @@ jobs: - uses: golangci/golangci-lint-action@v7 with: - version: v2.1.6 + version: v2.12.0 args: --verbose ./cmd/... ./src/... build: diff --git a/Makefile b/Makefile index 9fb02802..223fdc60 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,14 @@ .DEFAULT_GOAL := help BIN := $(CURDIR)/bin -GOLANGCI_LINT_VERSION := v2.1.6 +GOLANGCI_LINT_VERSION := v2.12.0 GORELEASER_VERSION := v2.5.0 UNAME_S := $(shell uname -s) UNAME_M := $(shell uname -m) +# golangci-lint releases use lowercase OS and amd64/arm64 in tarball names. +GCI_OS := $(shell echo $(UNAME_S) | tr A-Z a-z) +GCI_ARCH := $(if $(filter $(UNAME_M),x86_64),amd64,$(if $(filter $(UNAME_M),aarch64),arm64,$(UNAME_M))) +GCI_DIR := golangci-lint-$(GOLANGCI_LINT_VERSION:v%=%)-$(GCI_OS)-$(GCI_ARCH) .PHONY: help test test-integration lint all windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot tools clean-tools @@ -66,8 +70,9 @@ clean-tools: ## Remove installed tooling from ./bin. # gets reinstalled on the next `make lint` / `make goreleaser-*` / `make tools`. $(BIN)/.golangci-lint-$(GOLANGCI_LINT_VERSION): @mkdir -p $(BIN) - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \ - | sh -s -- -b $(BIN) $(GOLANGCI_LINT_VERSION) + curl -sSfL "https://github.com/golangci/golangci-lint/releases/download/$(GOLANGCI_LINT_VERSION)/$(GCI_DIR).tar.gz" \ + | tar -xz -C $(BIN) --strip-components=1 $(GCI_DIR)/golangci-lint + chmod +x $(BIN)/golangci-lint @rm -f $(BIN)/.golangci-lint-* @touch $@ From b408ed0a620ea09294289296acb447f94aed7f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 11:40:11 +0200 Subject: [PATCH 10/21] chore(lint): silence v2-surfaced noise, fix small findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v1 -> v2 golangci-lint bump exposed ~84 findings. Triaged: Silenced in .golangci.yaml: - gosec dropped entirely — every meaningful check (G104) duplicated errcheck, and the rest (G204/G304/G301) are inherent to a CLI tool that runs subprocesses, opens user-supplied paths, and writes to a ~/.zcli config dir. - prealloc dropped — slice prealloc nudges are noise where the slice length is tiny or driven by control flow. - errcheck excludes: never-erroring Builder/Buffer writes, fmt.Fprint* to printer wrappers, deferred Close (matched by source regex since errcheck's (io.Closer).Close exclude doesn't match concrete types). - staticcheck: QF1008 (cosmetic embedded-field selector) and ST1001 (intentional dot import in showcase/main.go) excluded by text rule. ST1005 silenced on src/cmdRunner/run.go — those error strings match exact stderr output from wireguard/ip and can't be lowered without breaking string comparisons. Fixed in code: - 3x ST1006 in src/version/message.go: drop '_' receiver names. - 1x noctx in src/serviceLogs/handler_getLogs.go: use NewRequestWithContext. - 1x testifylint in src/storage/handler_test.go: require.Empty for empty-string check. make lint now passes for all three target GOOSes. --- .golangci.yaml | 35 ++++++++++++++++++++++++++++-- src/serviceLogs/handler_getLogs.go | 3 +-- src/storage/handler_test.go | 2 +- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 97fb9a55..9c709f46 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -39,7 +39,6 @@ linters: - goheader - gomodguard - goprintffuncname - - gosec - gosmopolitan - govet - grouper @@ -55,7 +54,6 @@ linters: - nilerr - noctx - nosprintfhostport - - prealloc - predeclared - promlinter - protogetter @@ -82,6 +80,39 @@ linters: godox: keywords: - FIXME + errcheck: + # Exclude write/print/close patterns where the error is conventionally + # ignored: strings.Builder / bytes.Buffer never error; fmt.Fprint* on + # printers/log writers is fire-and-forget; deferred Close on read-only + # files has nothing meaningful to do with the error. + exclude-functions: + - (*strings.Builder).WriteString + - (*strings.Builder).WriteByte + - (*strings.Builder).WriteRune + - (*bytes.Buffer).WriteString + - (*bytes.Buffer).WriteByte + - (*bytes.Buffer).WriteRune + - fmt.Fprint + - fmt.Fprintf + - fmt.Fprintln + - (io.Closer).Close + - (*os.File).Close + - os.Remove + exclusions: + rules: + # QF1008 "could remove embedded field from selector" is cosmetic. + # ST1001 "should not use dot imports" is intentional in showcase/main.go. + - linters: [staticcheck] + text: "(QF1008|ST1001):" + # defer x.Close() on gzip/tar writers, ws conns, etc. — concrete types + # implementing io.Closer that errcheck's interface exclude won't match. + - linters: [errcheck] + source: "^\\s*defer .+\\.Close\\(\\)" + # These sentinels match exact stderr output from wireguard/ip; lowering + # them would break string comparisons. + - linters: [staticcheck] + path: src/cmdRunner/run.go + text: "ST1005:" issues: max-same-issues: 0 diff --git a/src/serviceLogs/handler_getLogs.go b/src/serviceLogs/handler_getLogs.go index b78a5dfd..4689d89d 100644 --- a/src/serviceLogs/handler_getLogs.go +++ b/src/serviceLogs/handler_getLogs.go @@ -36,11 +36,10 @@ type Data struct { func (h *Handler) getLogs(ctx context.Context, method, url, format, formatTemplate, mode string) error { c := http.Client{Timeout: time.Duration(1) * time.Minute} - req, err := http.NewRequest(method, url, nil) + req, err := http.NewRequestWithContext(ctx, method, url, nil) if err != nil { return err } - req = req.WithContext(ctx) req.Header.Add("Content-Type", "application/json") resp, err := c.Do(req) diff --git a/src/storage/handler_test.go b/src/storage/handler_test.go index 5f7fe310..8dc8b5b2 100644 --- a/src/storage/handler_test.go +++ b/src/storage/handler_test.go @@ -21,7 +21,7 @@ func TestStorage(t *testing.T) { { d := storage.Data() - require.Equal(t, "", d.Param) + require.Empty(t, d.Param) } { From f7bd38e8932de2f08582fd3d1703431a6164ef2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 11:47:42 +0200 Subject: [PATCH 11/21] chore(lint): drop irrelevant linters, clean stale nolint directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disabled 18 linters that either target frameworks the project doesn't use or are purely stylistic: Framework/library mismatch (no-op for this codebase): - ginkgolinter, gochecksumtype, goheader, gomodguard, importas, loggercheck, promlinter, protogetter, rowserrcheck, sloglint, sqlclosecheck, zerologlint Low signal / stylistic: - asciicheck, bidichk, decorder, grouper, nosprintfhostport, tagliatelle (the last was already being papered over by a // nolint:tagliatelle on src/version/apiDto.go). Kept: gosmopolitan, godox, mirror, maintidx — small but real signal. Also removed nolint directives left dead by this PR's earlier drops (tagliatelle, gosec). --- .golangci.yaml | 18 ------------------ src/archiveClient/handler_archiveGitFiles.go | 1 - src/cmdBuilder/cmd.go | 1 - src/constants/zerops.go | 2 +- src/gn/generic_test.go | 1 - src/version/apiDto.go | 1 - 6 files changed, 1 insertion(+), 23 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 9c709f46..be005a1e 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -16,12 +16,9 @@ linters: default: none enable: - asasalint - - asciicheck - - bidichk - bodyclose - containedctx - contextcheck - - decorder - dogsled - dupword - durationcheck @@ -31,20 +28,13 @@ linters: - errorlint - exhaustive - forcetypeassert - - ginkgolinter - gocheckcompilerdirectives - - gochecksumtype - gocritic - godox - - goheader - - gomodguard - goprintffuncname - gosmopolitan - govet - - grouper - - importas - ineffassign - - loggercheck - maintidx - makezero - mirror @@ -53,17 +43,10 @@ linters: - nakedret - nilerr - noctx - - nosprintfhostport - predeclared - - promlinter - - protogetter - reassign - - rowserrcheck - - sloglint - - sqlclosecheck - staticcheck - tagalign - - tagliatelle - testableexamples - testifylint - thelper @@ -75,7 +58,6 @@ linters: - usetesting - wastedassign - whitespace - - zerologlint settings: godox: keywords: diff --git a/src/archiveClient/handler_archiveGitFiles.go b/src/archiveClient/handler_archiveGitFiles.go index 864a6e58..02a233ee 100644 --- a/src/archiveClient/handler_archiveGitFiles.go +++ b/src/archiveClient/handler_archiveGitFiles.go @@ -216,7 +216,6 @@ func (h *Handler) mergeGitArchive(gitArchiveReader io.Reader, tarWriter *tar.Wri if err := tarWriter.WriteHeader(header); err != nil { return err } - //nolint:gosec // disable G110 - this is a local CLI util... if _, err := io.Copy(tarWriter, tr); err != nil { return err } diff --git a/src/cmdBuilder/cmd.go b/src/cmdBuilder/cmd.go index 7619ff6d..20b45fa8 100644 --- a/src/cmdBuilder/cmd.go +++ b/src/cmdBuilder/cmd.go @@ -205,7 +205,6 @@ func warpString(s string, runeCutCount uint) string { scanner.Split(bufio.ScanWords) var runeCount uint for scanner.Scan() { - //nolint:gosec runeCount += uint(utf8.RuneCount(scanner.Bytes())) if runeCount >= runeCutCount { runeCount = 0 diff --git a/src/constants/zerops.go b/src/constants/zerops.go index 13d2b620..f263737a 100644 --- a/src/constants/zerops.go +++ b/src/constants/zerops.go @@ -26,7 +26,7 @@ const ( CliWgConfigPathEnvVar = "ZEROPS_WG_CONFIG_FILE_PATH" CliZcliYamlFilePathEnvVar = "ZEROPS_CLI_YAML_FILE_PATH" CliTerminalMode = "ZEROPS_CLI_TERMINAL_MODE" - CliTokenEnvVar = "ZEROPS_TOKEN" //nolint:gosec + CliTokenEnvVar = "ZEROPS_TOKEN" ) type pathReceiver func(fileMode os.FileMode) (path string, err error) diff --git a/src/gn/generic_test.go b/src/gn/generic_test.go index c3f37dd5..e9861f8f 100644 --- a/src/gn/generic_test.go +++ b/src/gn/generic_test.go @@ -1,4 +1,3 @@ -//nolint:gosec package gn import ( diff --git a/src/version/apiDto.go b/src/version/apiDto.go index f970143f..8a1ab42f 100644 --- a/src/version/apiDto.go +++ b/src/version/apiDto.go @@ -1,4 +1,3 @@ -// nolint:tagliatelle package version import "time" From aa43d752fa84ff2fc01456758b087dd1dddaf7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 11:54:43 +0200 Subject: [PATCH 12/21] chore(release): bump goreleaser to v2.15.4 Migrates .goreleaser.yaml off the deprecated 'builds:' / 'format:' keys (archives + nfpms) onto the modern 'ids:' / 'formats:' schema, which v2.15 requires. Locally verified with 'goreleaser check' and a full --snapshot build: all asset names, tarball structure (builds/), and .deb file names are identical to the v2.5.0 output. --- .github/workflows/main.yml | 2 +- .github/workflows/release.yml | 2 +- .goreleaser.yaml | 10 +++++----- Makefile | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 17574692..5fdb62cd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -94,5 +94,5 @@ jobs: - uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser - version: v2.5.0 + version: v2.15.4 args: check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e15fb905..63d9e74a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,7 @@ jobs: - uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser - version: v2.5.0 + version: v2.15.4 args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 7e728b36..2e80034b 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -65,8 +65,8 @@ upx: archives: - id: raw - builds: [linux-amd64, linux-386, darwin-amd64, darwin-arm64, windows-amd64] - format: binary + ids: [linux-amd64, linux-386, darwin-amd64, darwin-arm64, windows-amd64] + formats: [binary] name_template: >- {{- if eq .Os "windows" -}}zcli-win-x64 {{- else if and (eq .Os "linux") (eq .Arch "386") -}}zcli-linux-i386 @@ -74,8 +74,8 @@ archives: {{- end -}} - id: npm - builds: [linux-amd64, linux-386, darwin-amd64, darwin-arm64, windows-amd64] - format: tar.gz + ids: [linux-amd64, linux-386, darwin-amd64, darwin-arm64, windows-amd64] + formats: [tar.gz] wrap_in_directory: builds files: - none* @@ -88,7 +88,7 @@ archives: nfpms: - id: deb package_name: zcli - builds: [linux-amd64, linux-386] + ids: [linux-amd64, linux-386] maintainer: "Zerops " description: "Zerops CLI" homepage: "https://github.com/zeropsio/zcli" diff --git a/Makefile b/Makefile index 223fdc60..6d688044 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ BIN := $(CURDIR)/bin GOLANGCI_LINT_VERSION := v2.12.0 -GORELEASER_VERSION := v2.5.0 +GORELEASER_VERSION := v2.15.4 UNAME_S := $(shell uname -s) UNAME_M := $(shell uname -m) # golangci-lint releases use lowercase OS and amd64/arm64 in tarball names. From 8a783a37fd75493a86ea29415b2c4583dbe89948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 11:58:47 +0200 Subject: [PATCH 13/21] chore: port tools/build.sh into Makefile, add build-dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a 'make build-dev' target that builds a host-platform dev binary into ./bin/zcli with the devel build tag, -gcflags='all=-l -N' for dlv-friendly debugging, and version metadata composed from git (branch:tag-(name:)) — same shape build.sh produced. windows-amd/linux-amd/darwin-amd/darwin-arm now share the same DEV_BUILD recipe via the Makefile variable instead of shelling out. Also fixes a latent bug build.sh has been carrying: -X was targeting github.com/zeropsio/zcli/src/cmd.version which has no such symbol, so the dev binaries always reported 'local'. The correct symbol is src/version.version (the same one .goreleaser.yaml uses). --- CLAUDE.md | 3 ++- Makefile | 31 ++++++++++++++++++++----------- tools/build.sh | 8 -------- 3 files changed, 22 insertions(+), 20 deletions(-) delete mode 100755 tools/build.sh diff --git a/CLAUDE.md b/CLAUDE.md index 59c2242f..597dd4a4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `make test` — runs `go test -v ./cmd/... ./src/...`. Run a single test with `go test -v -run TestName ./src//...`. - `make lint` — runs the pinned `./bin/golangci-lint` for darwin/arm64, linux/amd64, and windows/amd64. Config: `.golangci.yaml`. -- `make all` — cross-builds binaries for windows-amd, linux-amd, darwin-amd, darwin-arm. Single-target builds: `make linux-amd` etc. Builds go through `tools/build.sh`, which sets the version via `-ldflags` from `git rev-parse`/`git describe`, uses build tag `devel`, and writes to `bin/`. +- `make build-dev` — build a dev binary for the host into `./bin/zcli` (build tag `devel`, debug-friendly via `-gcflags='all=-l -N'`, version injected from git). +- `make all` — cross-builds dev binaries for windows-amd, linux-amd, darwin-amd, darwin-arm. Single-target builds: `make linux-amd` etc. All build targets share the `DEV_BUILD` recipe in the Makefile (version metadata, devel tag, gcflags). - `make goreleaser-check` / `make goreleaser-snapshot` — validate `.goreleaser.yaml` and dry-run a release build to `./dist`. - `make tools` — install pinned dev tooling (`golangci-lint`, `goreleaser`) into `./bin`. Versions are declared at the top of the Makefile; bumping them triggers reinstall via stamp files. `make lint` and the goreleaser targets depend on `tools`, so first invocation auto-installs. - `make showcase` — runs `src/uxBlock/showcase/main.go` to preview UI elements. diff --git a/Makefile b/Makefile index 6d688044..0416bc65 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,14 @@ GCI_OS := $(shell echo $(UNAME_S) | tr A-Z a-z) GCI_ARCH := $(if $(filter $(UNAME_M),x86_64),amd64,$(if $(filter $(UNAME_M),aarch64),arm64,$(UNAME_M))) GCI_DIR := golangci-lint-$(GOLANGCI_LINT_VERSION:v%=%)-$(GCI_OS)-$(GCI_ARCH) -.PHONY: help test test-integration lint all windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot tools clean-tools +.PHONY: help test test-integration lint all build-dev windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot tools clean-tools + +# Dev-build version metadata (matches what tools/build.sh used to compose). +DEV_VERSION := $(shell git rev-parse --abbrev-ref HEAD):$(shell git describe --tags 2>/dev/null)-($(shell git config --get user.name):<$(shell git config --get user.email)>) +# -gcflags disables inlining and optimizations so the binary is dlv-friendly. +DEV_BUILD := go build -tags devel \ + -gcflags='all=-l -N' \ + -ldflags='-X "github.com/zeropsio/zcli/src/version.version=$(DEV_VERSION)"' # Self-documenting help. Targets are listed in the order they appear here; # their description is the text after the '##' on the recipe line. @@ -35,20 +42,22 @@ lint: $(BIN)/.golangci-lint-$(GOLANGCI_LINT_VERSION) ## Run golangci-lint for da ##@ Build -# tools/build.sh embeds version metadata via -ldflags and writes to ./bin/. -all: windows-amd linux-amd darwin-amd darwin-arm ## Cross-build all release targets. +build-dev: ## Build a dev binary for the host into ./bin/zcli (devel tag, no optimizations). + $(DEV_BUILD) -o $(BIN)/zcli ./cmd/zcli + +all: windows-amd linux-amd darwin-amd darwin-arm ## Cross-build all dev targets. -windows-amd: ## Build the windows/amd64 binary. - GOOS=windows GOARCH=amd64 tools/build.sh zcli.win.exe +windows-amd: ## Build the windows/amd64 dev binary. + GOOS=windows GOARCH=amd64 $(DEV_BUILD) -o $(BIN)/zcli.win.exe ./cmd/zcli -linux-amd: ## Build the linux/amd64 binary. - GOOS=linux GOARCH=amd64 tools/build.sh zcli.linux +linux-amd: ## Build the linux/amd64 dev binary. + GOOS=linux GOARCH=amd64 $(DEV_BUILD) -o $(BIN)/zcli.linux ./cmd/zcli -darwin-amd: ## Build the darwin/amd64 binary. - GOOS=darwin GOARCH=amd64 tools/build.sh zcli.darwin.amd64 +darwin-amd: ## Build the darwin/amd64 dev binary. + GOOS=darwin GOARCH=amd64 $(DEV_BUILD) -o $(BIN)/zcli.darwin.amd64 ./cmd/zcli -darwin-arm: ## Build the darwin/arm64 binary. - GOOS=darwin GOARCH=arm64 tools/build.sh zcli.darwin.arm64 +darwin-arm: ## Build the darwin/arm64 dev binary. + GOOS=darwin GOARCH=arm64 $(DEV_BUILD) -o $(BIN)/zcli.darwin.arm64 ./cmd/zcli ##@ Release tooling diff --git a/tools/build.sh b/tools/build.sh deleted file mode 100755 index a677bc7f..00000000 --- a/tools/build.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -export VERSION="$(git rev-parse --abbrev-ref HEAD):$(git describe --tags)-($(git config --get user.name):<$(git config --get user.email)>)" -go build -tags devel \ - -o bin/$1 \ - -gcflags="all=-l -N" \ - -ldflags="all=\"-X=github.com/zeropsio/zcli/src/cmd.version=${VERSION}\"" \ - cmd/zcli/main.go From bd39eaf649b8a67d5529f1dafd7b49c025b21853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 12:02:09 +0200 Subject: [PATCH 14/21] feat(make): add install and install-dev targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 'make install' builds an optimized, stripped zcli binary (using the same flag set goreleaser uses for releases — -trimpath, -s -w, version from git describe) and writes it to $GOBIN (falling back to $GOPATH/bin). - 'make install-dev' uses the existing DEV_BUILD recipe (devel tag, -gcflags='all=-l -N', verbose version metadata) and writes a zcli-dev binary, so devs can keep a production zcli alongside the dlv-friendly dev build on the same PATH. --- CLAUDE.md | 2 ++ Makefile | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 597dd4a4..e850386d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,6 +12,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `make lint` — runs the pinned `./bin/golangci-lint` for darwin/arm64, linux/amd64, and windows/amd64. Config: `.golangci.yaml`. - `make build-dev` — build a dev binary for the host into `./bin/zcli` (build tag `devel`, debug-friendly via `-gcflags='all=-l -N'`, version injected from git). - `make all` — cross-builds dev binaries for windows-amd, linux-amd, darwin-amd, darwin-arm. Single-target builds: `make linux-amd` etc. All build targets share the `DEV_BUILD` recipe in the Makefile (version metadata, devel tag, gcflags). +- `make install` — production install into `$GOBIN` (or `$GOPATH/bin`) as `zcli` (stripped, optimized, version from `git describe`). Same flag set as `.goreleaser.yaml`. +- `make install-dev` — same as `build-dev` but installs to `$GOBIN` as `zcli-dev` so it can coexist on PATH with a production `zcli`. - `make goreleaser-check` / `make goreleaser-snapshot` — validate `.goreleaser.yaml` and dry-run a release build to `./dist`. - `make tools` — install pinned dev tooling (`golangci-lint`, `goreleaser`) into `./bin`. Versions are declared at the top of the Makefile; bumping them triggers reinstall via stamp files. `make lint` and the goreleaser targets depend on `tools`, so first invocation auto-installs. - `make showcase` — runs `src/uxBlock/showcase/main.go` to preview UI elements. diff --git a/Makefile b/Makefile index 0416bc65..40b003a6 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,11 @@ GCI_OS := $(shell echo $(UNAME_S) | tr A-Z a-z) GCI_ARCH := $(if $(filter $(UNAME_M),x86_64),amd64,$(if $(filter $(UNAME_M),aarch64),arm64,$(UNAME_M))) GCI_DIR := golangci-lint-$(GOLANGCI_LINT_VERSION:v%=%)-$(GCI_OS)-$(GCI_ARCH) -.PHONY: help test test-integration lint all build-dev windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot tools clean-tools +.PHONY: help test test-integration lint all build-dev install install-dev windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot tools clean-tools + +# Where `make install` / `make install-dev` puts binaries: GOBIN if set, +# else GOPATH/bin. The user is expected to have that directory on PATH. +INSTALL_DIR := $(or $(shell go env GOBIN),$(shell go env GOPATH)/bin) # Dev-build version metadata (matches what tools/build.sh used to compose). DEV_VERSION := $(shell git rev-parse --abbrev-ref HEAD):$(shell git describe --tags 2>/dev/null)-($(shell git config --get user.name):<$(shell git config --get user.email)>) @@ -19,6 +23,12 @@ DEV_BUILD := go build -tags devel \ -gcflags='all=-l -N' \ -ldflags='-X "github.com/zeropsio/zcli/src/version.version=$(DEV_VERSION)"' +# Production build flags mirror what .goreleaser.yaml uses for release builds: +# optimized, stripped, version from `git describe`, paths trimmed. +PROD_VERSION := $(shell git describe --tags 2>/dev/null) +PROD_BUILD := go build -trimpath \ + -ldflags='-s -w -X github.com/zeropsio/zcli/src/version.version=$(PROD_VERSION)' + # Self-documenting help. Targets are listed in the order they appear here; # their description is the text after the '##' on the recipe line. help: ## Show this help. @@ -59,6 +69,16 @@ darwin-amd: ## Build the darwin/amd64 dev binary. darwin-arm: ## Build the darwin/arm64 dev binary. GOOS=darwin GOARCH=arm64 $(DEV_BUILD) -o $(BIN)/zcli.darwin.arm64 ./cmd/zcli +##@ Install + +install: ## Build a production zcli (stripped, optimized) and install it into $GOBIN. + $(PROD_BUILD) -o $(INSTALL_DIR)/zcli ./cmd/zcli + @echo "installed $(INSTALL_DIR)/zcli ($(PROD_VERSION))" + +install-dev: ## Build a dev zcli-dev (devel tag, debug-friendly) and install it into $GOBIN. + $(DEV_BUILD) -o $(INSTALL_DIR)/zcli-dev ./cmd/zcli + @echo "installed $(INSTALL_DIR)/zcli-dev" + ##@ Release tooling goreleaser-check: $(BIN)/.goreleaser-$(GORELEASER_VERSION) ## Validate .goreleaser.yaml without building. From d600891b6983c63071c38bfebc590d9c3da25310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 12:04:03 +0200 Subject: [PATCH 15/21] feat(make): install zcli to ~/.local/bin to match install.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The public install.sh script puts zcli at $HOME/.local/bin/zcli, so 'make install' should land on the same PATH entry — anyone who has the binary on their PATH from the public install script will get a local make-installed binary picking up the same way. 'make install-dev' stays on $GOBIN/$GOPATH/bin (Go's convention for dev tooling) so the dev build sits alongside other go-installed tools and doesn't shadow the user-facing production zcli. --- CLAUDE.md | 4 ++-- Makefile | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index e850386d..4a7d72e6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,8 +12,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `make lint` — runs the pinned `./bin/golangci-lint` for darwin/arm64, linux/amd64, and windows/amd64. Config: `.golangci.yaml`. - `make build-dev` — build a dev binary for the host into `./bin/zcli` (build tag `devel`, debug-friendly via `-gcflags='all=-l -N'`, version injected from git). - `make all` — cross-builds dev binaries for windows-amd, linux-amd, darwin-amd, darwin-arm. Single-target builds: `make linux-amd` etc. All build targets share the `DEV_BUILD` recipe in the Makefile (version metadata, devel tag, gcflags). -- `make install` — production install into `$GOBIN` (or `$GOPATH/bin`) as `zcli` (stripped, optimized, version from `git describe`). Same flag set as `.goreleaser.yaml`. -- `make install-dev` — same as `build-dev` but installs to `$GOBIN` as `zcli-dev` so it can coexist on PATH with a production `zcli`. +- `make install` — production install into `~/.local/bin/zcli` (same path as the public `install.sh` script). Stripped/optimized, version from `git describe`, same flag set as `.goreleaser.yaml`. +- `make install-dev` — installs to `$GOBIN` (or `$GOPATH/bin`) as `zcli-dev` so it sits with other Go dev tools and coexists on PATH with a production `zcli`. - `make goreleaser-check` / `make goreleaser-snapshot` — validate `.goreleaser.yaml` and dry-run a release build to `./dist`. - `make tools` — install pinned dev tooling (`golangci-lint`, `goreleaser`) into `./bin`. Versions are declared at the top of the Makefile; bumping them triggers reinstall via stamp files. `make lint` and the goreleaser targets depend on `tools`, so first invocation auto-installs. - `make showcase` — runs `src/uxBlock/showcase/main.go` to preview UI elements. diff --git a/Makefile b/Makefile index 40b003a6..ad74197f 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,12 @@ GCI_DIR := golangci-lint-$(GOLANGCI_LINT_VERSION:v%=%)-$(GCI_OS)-$(GCI_ARCH) .PHONY: help test test-integration lint all build-dev install install-dev windows-amd linux-amd darwin-amd darwin-arm showcase goreleaser-check goreleaser-snapshot tools clean-tools -# Where `make install` / `make install-dev` puts binaries: GOBIN if set, -# else GOPATH/bin. The user is expected to have that directory on PATH. -INSTALL_DIR := $(or $(shell go env GOBIN),$(shell go env GOPATH)/bin) +# `make install` matches install.sh — puts zcli into ~/.local/bin so it +# lands on the same PATH entry end users get from the install script. +# `make install-dev` uses Go's convention ($GOBIN, else $GOPATH/bin) so +# zcli-dev sits with the user's other Go dev tools. +PROD_INSTALL_DIR := $(HOME)/.local/bin +DEV_INSTALL_DIR := $(or $(shell go env GOBIN),$(shell go env GOPATH)/bin) # Dev-build version metadata (matches what tools/build.sh used to compose). DEV_VERSION := $(shell git rev-parse --abbrev-ref HEAD):$(shell git describe --tags 2>/dev/null)-($(shell git config --get user.name):<$(shell git config --get user.email)>) @@ -71,13 +74,14 @@ darwin-arm: ## Build the darwin/arm64 dev binary. ##@ Install -install: ## Build a production zcli (stripped, optimized) and install it into $GOBIN. - $(PROD_BUILD) -o $(INSTALL_DIR)/zcli ./cmd/zcli - @echo "installed $(INSTALL_DIR)/zcli ($(PROD_VERSION))" +install: ## Build a production zcli (stripped, optimized) and install it into ~/.local/bin (matches install.sh). + @mkdir -p $(PROD_INSTALL_DIR) + $(PROD_BUILD) -o $(PROD_INSTALL_DIR)/zcli ./cmd/zcli + @echo "installed $(PROD_INSTALL_DIR)/zcli ($(PROD_VERSION))" install-dev: ## Build a dev zcli-dev (devel tag, debug-friendly) and install it into $GOBIN. - $(DEV_BUILD) -o $(INSTALL_DIR)/zcli-dev ./cmd/zcli - @echo "installed $(INSTALL_DIR)/zcli-dev" + $(DEV_BUILD) -o $(DEV_INSTALL_DIR)/zcli-dev ./cmd/zcli + @echo "installed $(DEV_INSTALL_DIR)/zcli-dev" ##@ Release tooling From bd8ea191d8b0a49a8cd7a5d467cb0c3c23d4744b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 12:06:24 +0200 Subject: [PATCH 16/21] docs(readme): add Development setup section Documents the make-driven workflow (tools, build-dev, install, install-dev, test, lint, goreleaser-snapshot) plus the version-pinning contract for tooling, so a new contributor can go from clone to working build without spelunking through the Makefile. --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 04ca8c2d..e9460fe3 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,31 @@ For more information go through https://docs.zerops.io/references/cli.
+## Development setup + +Prerequisites: **Go 1.26+**, `git`, `make`, and `curl` (used to fetch pinned tooling). + +```shell +git clone https://github.com/zeropsio/zcli && cd zcli +make tools # download pinned golangci-lint + goreleaser into ./bin +make build-dev # build ./bin/zcli for the host platform +``` + +Common targets (run `make help` for the full list): + +| Target | What it does | +|---|---| +| `make build-dev` | Build `./bin/zcli` with the `devel` tag, no optimizations (dlv-friendly). | +| `make install` | Build a production `zcli` and install to `~/.local/bin` (same path as `install.sh`). | +| `make install-dev` | Install a `zcli-dev` binary into `$GOBIN` (or `$GOPATH/bin`). | +| `make test` | Run the full Go test suite. | +| `make lint` | Run `golangci-lint` across darwin/arm64, linux/amd64, windows/amd64. | +| `make all` | Cross-build dev binaries for all release platforms. | +| `make goreleaser-snapshot` | Dry-run a full release build into `./dist` (no upload). | + +Tool versions (`golangci-lint`, `goreleaser`) are pinned at the top of the `Makefile`. Bumping a version triggers an automatic reinstall on next use; run `make clean-tools` to wipe `./bin` and force a fresh install. + + ## Want to Contribute? Contributions to zCLI are welcome and highly appreciated. However, We would like you to go through [CONTRIBUTING.md](https://github.com/zeropsio/zcli/blob/main/CONTRIBUTING.md). From 25768a11fa649fa10fee27c7e606df5d189c5d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Fri, 15 May 2026 13:34:00 +0200 Subject: [PATCH 17/21] ci: bump GitHub Actions to latest majors - actions/checkout v4 -> v6 - actions/setup-go v5 -> v6 - actions/setup-node v4 -> v6 - golangci/golangci-lint-action v7 -> v9 (requires golangci-lint >= v2.1.0; we pin v2.12.0) - goreleaser/goreleaser-action v6 -> v7 - sarisia/actions-status-discord v1.15.0 -> v1.16.0 The major bumps are all Node runtime updates (Node 20 -> Node 24) plus the golangci-lint-action v8 'use absolute paths by default' change, which doesn't affect our run. No input/argument schemas changed for how we invoke them. --- .github/workflows/main.yml | 18 +++++++++--------- .github/workflows/release.yml | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5fdb62cd..ddc1ac27 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,9 +18,9 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version-file: "go.mod" @@ -32,13 +32,13 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version-file: "go.mod" - - uses: golangci/golangci-lint-action@v7 + - uses: golangci/golangci-lint-action@v9 with: version: v2.12.0 args: --verbose ./cmd/... ./src/... @@ -75,9 +75,9 @@ jobs: GOARCH: amd64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version-file: "go.mod" @@ -90,8 +90,8 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 5 steps: - - uses: actions/checkout@v4 - - uses: goreleaser/goreleaser-action@v6 + - uses: actions/checkout@v6 + - uses: goreleaser/goreleaser-action@v7 with: distribution: goreleaser version: v2.15.4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 63d9e74a..e989f7f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,19 +18,19 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ github.event.release.tag_name }} - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version-file: "go.mod" - name: install upx run: sudo apt-get update && sudo apt-get install -y upx-ucl - - uses: goreleaser/goreleaser-action@v6 + - uses: goreleaser/goreleaser-action@v7 with: distribution: goreleaser version: v2.15.4 @@ -44,8 +44,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 with: node-version: 22 registry-url: https://registry.npmjs.org/ @@ -65,7 +65,7 @@ jobs: timeout-minutes: 5 steps: - name: Notify discord about new release - uses: sarisia/actions-status-discord@v1.15.0 + uses: sarisia/actions-status-discord@v1.16.0 with: webhook: ${{ secrets.DISCORD_WEBHOOK }} title: "New version of `zcli` is ready!" From 1d0dcf5b433dffb46c150ea20dff907af3ce58e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Wed, 20 May 2026 20:48:31 +0200 Subject: [PATCH 18/21] ci: run integration tests in main pipeline Add an integration job that runs the devel-tagged integration tests (go test -tags devel ./src/cmd/...) alongside the existing test, lint, and build jobs. --- .github/workflows/main.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ddc1ac27..a5346d5c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,6 +27,20 @@ jobs: - name: test run: go test -v ./cmd/... ./src/... + integration: + name: integration + runs-on: ubuntu-22.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-go@v6 + with: + go-version-file: "go.mod" + + - name: test-integration + run: go test -v -tags devel ./src/cmd/... + lint: name: lint runs-on: ubuntu-22.04 From c5b8b193fa84da9846a9b99bfde489fa749773c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Wed, 20 May 2026 20:52:38 +0200 Subject: [PATCH 19/21] chore(lint): use assert.Empty for empty-string check Satisfies testifylint in src/uxBlock/models/input/model_test.go. --- src/uxBlock/models/input/model_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uxBlock/models/input/model_test.go b/src/uxBlock/models/input/model_test.go index 05e07951..51858e8e 100644 --- a/src/uxBlock/models/input/model_test.go +++ b/src/uxBlock/models/input/model_test.go @@ -46,7 +46,7 @@ func TestInput_EnterWithEmptyValue(t *testing.T) { tm.WaitFinished(t, teatest.WithFinalTimeout(2*time.Second)) val, err := GetValueFunc(tm.FinalModel(t)) require.NoError(t, err) - assert.Equal(t, "", val) + assert.Empty(t, val) } // Esc aborts the input and propagates the cancellation error. From 96e39eafe29660f8174adb8840697cd31c384eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Wed, 20 May 2026 21:15:16 +0200 Subject: [PATCH 20/21] ci: remove upx packer from release pipeline Drop the upx compression of the linux binaries and the upx-ucl install step it required. --- .github/workflows/release.yml | 3 --- .goreleaser.yaml | 5 ----- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e989f7f9..8e54b48e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,9 +27,6 @@ jobs: with: go-version-file: "go.mod" - - name: install upx - run: sudo apt-get update && sudo apt-get install -y upx-ucl - - uses: goreleaser/goreleaser-action@v7 with: distribution: goreleaser diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 2e80034b..bf5f67c2 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -58,11 +58,6 @@ builds: ldflags: - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} -upx: - - enabled: true - ids: [linux-amd64, linux-386] - compress: best - archives: - id: raw ids: [linux-amd64, linux-386, darwin-amd64, darwin-arm64, windows-amd64] From 58a19cd09ce03fc677ac37fc7ce5fe055936d6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hellmann?= Date: Wed, 20 May 2026 21:26:57 +0200 Subject: [PATCH 21/21] ci: install deb binary as /usr/local/bin/zcli Add deb-only linux builds whose binary is named zcli and point the deb package at them, so the installed command is /usr/local/bin/zcli instead of the arch-suffixed name. The arch-suffixed builds stay for the archives and npm wrapper, which depend on those exact names. --- .goreleaser.yaml | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index bf5f67c2..708ea859 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -58,6 +58,31 @@ builds: ldflags: - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} + # Deb-only builds: binary is named "zcli" so the package installs + # /usr/local/bin/zcli rather than the arch-suffixed name the archives + # and npm wrapper rely on. + - id: deb-amd64 + main: ./cmd/zcli/main.go + binary: zcli + goos: [linux] + goarch: [amd64] + env: + - CGO_ENABLED=0 + flags: [-trimpath] + ldflags: + - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} + + - id: deb-386 + main: ./cmd/zcli/main.go + binary: zcli + goos: [linux] + goarch: ["386"] + env: + - CGO_ENABLED=0 + flags: [-trimpath] + ldflags: + - -s -w -X github.com/zeropsio/zcli/src/version.version={{ .Tag }} + archives: - id: raw ids: [linux-amd64, linux-386, darwin-amd64, darwin-arm64, windows-amd64] @@ -83,7 +108,7 @@ archives: nfpms: - id: deb package_name: zcli - ids: [linux-amd64, linux-386] + ids: [deb-amd64, deb-386] maintainer: "Zerops " description: "Zerops CLI" homepage: "https://github.com/zeropsio/zcli"