Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.git
cli/bin
cli/dist
cli/coverage.out
.github
.claude
.goreleaser.yml
cli/Makefile
*.md
LICENSE
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: cli
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: cli/go.mod
cache-dependency-path: cli/go.sum
- run: make test
Comment on lines +22 to +26
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Go version is duplicated across jobs (go-version: '1.26.1'). To avoid future drift from cli/go.mod, consider switching to go-version-file: cli/go.mod (and drop the hard-coded version) in each actions/setup-go step.

Copilot uses AI. Check for mistakes.

lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: cli
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: cli/go.mod
cache-dependency-path: cli/go.sum
- run: make tools
- run: make lint

build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 15
needs: [test, lint]
defaults:
run:
working-directory: cli
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: cli/go.mod
cache-dependency-path: cli/go.sum
- run: make build-all
36 changes: 36 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Release

on:
push:
tags: ['v*']

permissions:
contents: write

jobs:
release:
name: Release
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version-file: cli/go.mod
cache-dependency-path: cli/go.sum
- name: Install staticcheck
working-directory: cli
run: make tools
- name: Run tests and lint
working-directory: cli
run: make test && make lint
- name: Run goreleaser
uses: goreleaser/goreleaser-action@v6
with:
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The release workflow pins the action major version (goreleaser/goreleaser-action@v6) but not the GoReleaser binary version itself, so a new GoReleaser release could change behavior or break releases unexpectedly. Consider setting the action input to pin a specific GoReleaser version (or at least a constrained major/minor range) for reproducible releases.

Suggested change
with:
with:
version: v2.7.0

Copilot uses AI. Check for mistakes.
version: v2.7.0
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
Comment on lines +29 to +36
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description’s “Key Decisions” section appears corrupted (repeated/truncated text around goreleaser/Homebrew token and build targets). Please clean up the PR description so reviewers/users can understand the intent and required setup.

Copilot uses AI. Check for mistakes.
65 changes: 65 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
version: 2

project_name: stackctl

dist: cli/dist

builds:
- dir: cli
main: .
binary: stackctl
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ignore:
- goos: windows
goarch: arm64
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}

archives:
- formats:
- tar.gz
format_overrides:
- goos: windows
formats:
- zip
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"

checksum:
name_template: "checksums.txt"
algorithm: sha256

changelog:
sort: asc
filters:
exclude:
- "^Merge"

brews:
- repository:
owner: omattsson
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
homepage: https://github.com/omattsson/stackctl
description: CLI for managing Kubernetes stack deployments via k8s-stack-manager
license: MIT
install: |
bin.install "stackctl"
test: |
system "#{bin}/stackctl", "version"

release:
github:
owner: omattsson
name: stackctl
draft: false
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Stage 1: Build
FROM golang:1.26.1-alpine@sha256:2389ebfa5b7f43eeafbd6be0c3700cc46690ef842ad962f6c5bd6be49ed82039 AS builder

Comment on lines +1 to +3
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For supply-chain/reproducibility, consider pinning the base images by digest (e.g., golang:...@sha256:..., alpine:...@sha256:...) instead of mutable tags. This reduces the risk of builds changing unexpectedly over time.

Copilot uses AI. Check for mistakes.
ARG VERSION=dev
ARG COMMIT=none
ARG DATE=unknown

WORKDIR /build

# Cache dependencies
COPY cli/go.mod cli/go.sum ./cli/
RUN cd cli && go mod download

# Copy source and build
COPY cli/ ./cli/
RUN cd cli && CGO_ENABLED=0 go build \
-ldflags "-s -w -X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${DATE}" \
-o /build/stackctl .

# Stage 2: Runtime
FROM alpine:3.20@sha256:a4f4213abb84c497377b8544c81b3564f313746700372ec4fe84653e4fb03805

RUN apk add --no-cache ca-certificates && \
adduser -D -h /home/stackctl stackctl

COPY --from=builder /build/stackctl /usr/local/bin/stackctl

USER stackctl
Comment on lines +23 to +28
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The runtime image creates a non-root user but leaves the default login shell enabled. For a CLI container image, it’s safer to create the user with a nologin shell (or otherwise disable interactive login) to reduce the attack surface.

Copilot uses AI. Check for mistakes.

ENTRYPOINT ["stackctl"]
1 change: 1 addition & 0 deletions cli/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
bin/
dist/
coverage.out
64 changes: 64 additions & 0 deletions cli/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# stackctl CLI Makefile

BINARY_NAME = stackctl
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "none")
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS = -s -w -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)

STATICCHECK_VERSION ?= 2026.1

# Detect Windows for .exe extension
ifdef GOOS
ifeq ($(GOOS),windows)
EXT = .exe
endif
else
ifeq ($(OS),Windows_NT)
EXT = .exe
endif
endif

# windows/arm64 excluded: not a common deployment target and avoids cross-compilation issues
PLATFORMS = linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64

.PHONY: build build-all test lint coverage install clean tools

build:
@mkdir -p bin
CGO_ENABLED=0 go build -ldflags "$(LDFLAGS)" -o bin/$(BINARY_NAME)$(EXT) .

build-all:
@mkdir -p bin
@for platform in $(PLATFORMS); do \
os=$${platform%/*}; \
arch=$${platform#*/}; \
output=bin/$(BINARY_NAME)-$${os}-$${arch}; \
if [ "$${os}" = "windows" ]; then output=$${output}.exe; fi; \
echo "Building $${output}..."; \
CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} go build -ldflags "$(LDFLAGS)" -o $${output} . || exit 1; \
done
Comment on lines +27 to +40
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make build / make build-all write outputs under bin/, but the Makefile never creates that directory. On a clean checkout (including in CI), go build -o bin/... will fail with “no such file or directory”. Create bin/ (e.g., mkdir -p bin) before the build commands (and/or before the loop in build-all).

Copilot uses AI. Check for mistakes.

test:
go test ./... -v

lint:
go vet ./...
@if ! command -v staticcheck >/dev/null 2>&1; then \
echo "Error: staticcheck is not installed. Run 'make tools' to install staticcheck@$(STATICCHECK_VERSION)"; \
exit 1; \
fi
Comment on lines +45 to +50
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lint target suggests installing staticcheck@latest, while CI/release workflows pin staticcheck@2026.1. This can lead to local lint results diverging from CI. Consider centralizing the staticcheck version (e.g., a Makefile var) and referencing that in both the install hint and workflows (or add a make tools target that installs the pinned version).

Copilot uses AI. Check for mistakes.
staticcheck ./...

coverage:
go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out

install:
CGO_ENABLED=0 go install -ldflags "$(LDFLAGS)" .

tools:
go install honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VERSION)

clean:
rm -rf bin/ dist/ coverage.out
2 changes: 1 addition & 1 deletion cli/test/e2e/cli_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func TestE2E_ConfigWorkflow(t *testing.T) {
}

// 7. Add another context
stdout, _, err = runStackctl(t, dir, "config", "use-context", "production")
_, _, err = runStackctl(t, dir, "config", "use-context", "production")
require.NoError(t, err)

_, _, err = runStackctl(t, dir, "config", "set", "api-url", "https://prod.example.com")
Expand Down
Loading