Skip to content

Commit 6782d64

Browse files
committed
feat: Add Makefile, golangci-lint configuration, and Lefthook setup for improved development workflow
1 parent 32f5950 commit 6782d64

5 files changed

Lines changed: 440 additions & 1 deletion

File tree

.claude/settings.local.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
"Bash(goreleaser check:*)",
1212
"Bash(find:*)",
1313
"Bash(grep:*)",
14-
"WebFetch(domain:github.com)"
14+
"WebFetch(domain:github.com)",
15+
"Bash(golangci-lint run:*)",
16+
"Bash(golangci-lint:*)"
1517
]
1618
}
1719
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Automatically merge Dependabot PRs for non-breaking updates
2+
name: Dependabot Automerge
3+
4+
on:
5+
pull_request_target:
6+
types:
7+
- opened
8+
- synchronize
9+
- reopened
10+
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
15+
jobs:
16+
automerge:
17+
runs-on: ubuntu-latest
18+
if: github.actor == 'dependabot[bot]'
19+
20+
steps:
21+
- name: Dependabot metadata
22+
id: metadata
23+
uses: dependabot/fetch-metadata@v2
24+
with:
25+
github-token: "${{ secrets.GITHUB_TOKEN }}"
26+
27+
- name: Approve patch and minor updates
28+
if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
29+
run: gh pr review --approve "$PR_URL"
30+
env:
31+
PR_URL: ${{ github.event.pull_request.html_url }}
32+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
34+
- name: Enable auto-merge for patch updates
35+
if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
36+
run: gh pr merge --auto --squash "$PR_URL"
37+
env:
38+
PR_URL: ${{ github.event.pull_request.html_url }}
39+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40+
41+
- name: Enable auto-merge for minor updates
42+
if: steps.metadata.outputs.update-type == 'version-update:semver-minor'
43+
run: gh pr merge --auto --squash "$PR_URL"
44+
env:
45+
PR_URL: ${{ github.event.pull_request.html_url }}
46+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47+
48+
- name: Comment on major updates
49+
if: steps.metadata.outputs.update-type == 'version-update:semver-major'
50+
run: |
51+
gh pr comment "$PR_URL" --body "This is a **major version update** and requires manual review before merging.
52+
53+
Please review the changelog and breaking changes before approving."
54+
env:
55+
PR_URL: ${{ github.event.pull_request.html_url }}
56+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.golangci.yml

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# golangci-lint configuration for kspec
2+
# https://golangci-lint.run/usage/configuration/
3+
version: "2"
4+
5+
run:
6+
timeout: 5m
7+
modules-download-mode: readonly
8+
go: "1.21"
9+
10+
output:
11+
print-issued-lines: true
12+
print-linter-name: true
13+
sort-results: true
14+
15+
formatters:
16+
enable:
17+
- goimports # Check import order
18+
settings:
19+
goimports:
20+
local-prefixes:
21+
- github.com/kopexa-grc/kspec
22+
23+
linters:
24+
default: standard
25+
enable:
26+
# Additional linters
27+
- bodyclose # Check HTTP response body is closed
28+
- dogsled # Check for excessive blank identifiers
29+
- dupl # Code clone detection
30+
- errname # Check error naming conventions
31+
- errorlint # Find problems with error wrapping
32+
- exhaustive # Check exhaustiveness of enum switch statements
33+
- goconst # Find repeated strings that could be constants
34+
- gocritic # Various checks
35+
- goprintffuncname # Check printf-like function names
36+
- gosec # Security checks
37+
- misspell # Spell checker
38+
- nakedret # Check for naked returns
39+
- nilerr # Find code returning nil error with non-nil
40+
- noctx # Check for http requests without context
41+
- nolintlint # Check nolint directives
42+
- prealloc # Check for slice preallocation
43+
- predeclared # Check for shadowing predeclared identifiers
44+
- revive # Fast, extensible linter
45+
- tparallel # Check for t.Parallel() in tests
46+
47+
settings:
48+
errcheck:
49+
check-type-assertions: true
50+
check-blank: true
51+
exclude-functions:
52+
- io.Copy
53+
- (io.Closer).Close
54+
- (net/http.ResponseWriter).Write
55+
56+
govet:
57+
disable:
58+
- fieldalignment # Too noisy
59+
60+
dupl:
61+
threshold: 150
62+
63+
goconst:
64+
min-len: 3
65+
min-occurrences: 3
66+
67+
gocritic:
68+
enabled-tags:
69+
- diagnostic
70+
- performance
71+
- style
72+
disabled-checks:
73+
- hugeParam # Too noisy for struct parameters
74+
- rangeValCopy # Too noisy
75+
76+
gosec:
77+
excludes:
78+
- G104 # Audit errors not checked - too noisy
79+
- G304 # File path provided as taint input - false positives
80+
81+
misspell:
82+
locale: US
83+
84+
nakedret:
85+
max-func-lines: 30
86+
87+
revive:
88+
rules:
89+
- name: blank-imports
90+
- name: context-as-argument
91+
- name: context-keys-type
92+
- name: dot-imports
93+
- name: error-return
94+
- name: error-naming
95+
- name: exported
96+
- name: if-return
97+
- name: increment-decrement
98+
- name: var-declaration
99+
- name: package-comments
100+
- name: range
101+
- name: receiver-naming
102+
- name: time-naming
103+
- name: unexported-return
104+
- name: indent-error-flow
105+
- name: errorf
106+
- name: empty-block
107+
- name: superfluous-else
108+
- name: unused-parameter
109+
disabled: true # Too noisy
110+
- name: unreachable-code
111+
112+
issues:
113+
max-issues-per-linter: 50
114+
max-same-issues: 10
115+
116+
exclude-rules:
117+
# Exclude some linters from running on tests files
118+
- path: _test\.go
119+
linters:
120+
- dupl
121+
- gosec
122+
- errcheck
123+
- goconst
124+
125+
# Exclude some linters from running on generated files
126+
- path: generated
127+
linters:
128+
- all
129+
130+
# Allow fmt.Print in main and cmd packages
131+
- path: (cmd|main)\.go
132+
linters:
133+
- forbidigo
134+
135+
# Test files can have longer functions
136+
- path: _test\.go
137+
linters:
138+
- funlen
139+
140+
severity:
141+
default: warning
142+
case-sensitive: false

Makefile

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# kspec Makefile
2+
# Run `make help` for available commands
3+
4+
.PHONY: help build install clean test lint lint-fix fmt vet check \
5+
run dev release snapshot hooks deps tidy
6+
7+
# Build variables
8+
BINARY_NAME := kspec
9+
BUILD_DIR := ./bin
10+
MAIN_PKG := ./cmd/kspec
11+
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
12+
COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "none")
13+
DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
14+
LDFLAGS := -s -w \
15+
-X main.version=$(VERSION) \
16+
-X main.commit=$(COMMIT) \
17+
-X main.date=$(DATE)
18+
19+
# Go variables
20+
GOBIN := $(shell go env GOBIN)
21+
ifeq ($(GOBIN),)
22+
GOBIN := $(shell go env GOPATH)/bin
23+
endif
24+
25+
# Colors
26+
BLUE := \033[34m
27+
GREEN := \033[32m
28+
YELLOW := \033[33m
29+
RED := \033[31m
30+
RESET := \033[0m
31+
32+
##@ General
33+
34+
help: ## Display this help
35+
@awk 'BEGIN {FS = ":.*##"; printf "\n$(BLUE)Usage:$(RESET)\n make $(GREEN)<target>$(RESET)\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " $(GREEN)%-15s$(RESET) %s\n", $$1, $$2 } /^##@/ { printf "\n$(YELLOW)%s$(RESET)\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
36+
37+
##@ Development
38+
39+
build: ## Build the binary
40+
@echo "$(BLUE)Building $(BINARY_NAME)...$(RESET)"
41+
@mkdir -p $(BUILD_DIR)
42+
CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o $(BUILD_DIR)/$(BINARY_NAME) $(MAIN_PKG)
43+
@echo "$(GREEN)Built $(BUILD_DIR)/$(BINARY_NAME)$(RESET)"
44+
45+
install: build ## Install binary to GOBIN
46+
@echo "$(BLUE)Installing $(BINARY_NAME) to $(GOBIN)...$(RESET)"
47+
@cp $(BUILD_DIR)/$(BINARY_NAME) $(GOBIN)/$(BINARY_NAME)
48+
@echo "$(GREEN)Installed to $(GOBIN)/$(BINARY_NAME)$(RESET)"
49+
50+
dev: ## Build and run with dev version
51+
@go run -ldflags="$(LDFLAGS)" $(MAIN_PKG) $(ARGS)
52+
53+
run: build ## Build and run the binary
54+
@$(BUILD_DIR)/$(BINARY_NAME) $(ARGS)
55+
56+
clean: ## Remove build artifacts
57+
@echo "$(BLUE)Cleaning...$(RESET)"
58+
@rm -rf $(BUILD_DIR)
59+
@rm -f coverage.out coverage.html
60+
@echo "$(GREEN)Cleaned$(RESET)"
61+
62+
##@ Testing
63+
64+
test: ## Run tests
65+
@echo "$(BLUE)Running tests...$(RESET)"
66+
go test -v -race ./...
67+
68+
test-coverage: ## Run tests with coverage
69+
@echo "$(BLUE)Running tests with coverage...$(RESET)"
70+
go test -v -race -coverprofile=coverage.out ./...
71+
go tool cover -html=coverage.out -o coverage.html
72+
@echo "$(GREEN)Coverage report: coverage.html$(RESET)"
73+
74+
test-short: ## Run short tests only
75+
go test -v -short ./...
76+
77+
##@ Code Quality
78+
79+
lint: ## Run linter
80+
@echo "$(BLUE)Running linter...$(RESET)"
81+
@if command -v golangci-lint >/dev/null 2>&1; then \
82+
golangci-lint run --timeout=5m; \
83+
else \
84+
echo "$(RED)golangci-lint not installed. Run: make lint-install$(RESET)"; \
85+
exit 1; \
86+
fi
87+
88+
lint-fix: ## Run linter with auto-fix
89+
@echo "$(BLUE)Running linter with fixes...$(RESET)"
90+
@if command -v golangci-lint >/dev/null 2>&1; then \
91+
golangci-lint run --fix --timeout=5m; \
92+
else \
93+
echo "$(RED)golangci-lint not installed. Run: make lint-install$(RESET)"; \
94+
exit 1; \
95+
fi
96+
97+
lint-install: ## Install golangci-lint
98+
@echo "$(BLUE)Installing golangci-lint...$(RESET)"
99+
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
100+
@echo "$(GREEN)Installed golangci-lint$(RESET)"
101+
102+
fmt: ## Format code
103+
@echo "$(BLUE)Formatting code...$(RESET)"
104+
go fmt ./...
105+
@echo "$(GREEN)Done$(RESET)"
106+
107+
vet: ## Run go vet
108+
@echo "$(BLUE)Running go vet...$(RESET)"
109+
go vet ./...
110+
111+
check: fmt vet lint test ## Run all checks (fmt, vet, lint, test)
112+
@echo "$(GREEN)All checks passed!$(RESET)"
113+
114+
##@ Dependencies
115+
116+
deps: ## Download dependencies
117+
@echo "$(BLUE)Downloading dependencies...$(RESET)"
118+
go mod download
119+
120+
tidy: ## Tidy and verify dependencies
121+
@echo "$(BLUE)Tidying dependencies...$(RESET)"
122+
go mod tidy
123+
go mod verify
124+
125+
##@ Git Hooks
126+
127+
hooks: ## Install git hooks via lefthook
128+
@echo "$(BLUE)Installing git hooks...$(RESET)"
129+
@if command -v lefthook >/dev/null 2>&1; then \
130+
lefthook install; \
131+
echo "$(GREEN)Git hooks installed$(RESET)"; \
132+
else \
133+
echo "$(RED)lefthook not installed. Run: make hooks-install$(RESET)"; \
134+
exit 1; \
135+
fi
136+
137+
hooks-install: ## Install lefthook
138+
@echo "$(BLUE)Installing lefthook...$(RESET)"
139+
go install github.com/evilmartians/lefthook@latest
140+
@echo "$(GREEN)Installed lefthook$(RESET)"
141+
142+
hooks-uninstall: ## Uninstall git hooks
143+
@if command -v lefthook >/dev/null 2>&1; then \
144+
lefthook uninstall; \
145+
echo "$(GREEN)Git hooks uninstalled$(RESET)"; \
146+
fi
147+
148+
##@ Release
149+
150+
release: ## Create a release build (requires goreleaser)
151+
@echo "$(BLUE)Creating release...$(RESET)"
152+
goreleaser release --clean
153+
154+
snapshot: ## Create a snapshot release (no publish)
155+
@echo "$(BLUE)Creating snapshot...$(RESET)"
156+
goreleaser release --snapshot --clean
157+
158+
release-check: ## Validate goreleaser config
159+
@echo "$(BLUE)Checking goreleaser config...$(RESET)"
160+
goreleaser check
161+
162+
##@ Docker (future)
163+
164+
# docker-build: ## Build Docker image
165+
# docker build -t kspec:$(VERSION) .
166+
167+
##@ Utilities
168+
169+
version: ## Show version info
170+
@echo "Version: $(VERSION)"
171+
@echo "Commit: $(COMMIT)"
172+
@echo "Date: $(DATE)"
173+
174+
tools: lint-install hooks-install ## Install all development tools
175+
@echo "$(GREEN)All tools installed$(RESET)"
176+
177+
.DEFAULT_GOAL := help

0 commit comments

Comments
 (0)