From 442ac9f580d0de4ad12d2a97f533c010ee00865d Mon Sep 17 00:00:00 2001 From: mr-forust Date: Fri, 3 Jul 2026 01:49:00 +0200 Subject: [PATCH] feat: add linter configs, Dockerfile, CI/CD pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .golangci.yml — 50+ linters enabled - .yamllint — yaml lint rules - cicd.yaml — CI/CD with lint-go (golangci, staticcheck, gosec, revive), lint-yaml, build+test, publish - Dockerfile — multi-stage scratch build - go vet passes clean --- .golangci.yml | 108 ++++++++++++++++++++++++++++++++++++++ .yamllint | 12 +++++ Dockerfile | 12 +++++ cicd.yaml | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 .golangci.yml create mode 100644 .yamllint create mode 100644 Dockerfile create mode 100644 cicd.yaml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..1e80ac3 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,108 @@ +run: + timeout: 5m + skip-dirs: + - vendor/ + +linters: + enable: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - typecheck + - unused + - asciicheck + - bodyclose + - cyclop + - decorder + - dupl + - dupword + - durationcheck + - errname + - errorlint + - exhaustive + - forcetypeassert + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godot + - goimports + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - grouper + - ifshort + - importas + - lll + - makezero + - mirror + - misspell + - nakedret + - nestif + - nilerr + - nilnil + - nlreturn + - noctx + - nolintlint + - nosprintfhostport + - prealloc + - predeclared + - promlinter + - reassign + - revive + - rowserrcheck + - sqlclosecheck + - stylecheck + - tagalign + - tagliatelle + - tenv + - testableexamples + - thelper + - tparallel + - unconvert + - unparam + - usestdlibvars + - wastedassign + - whitespace + - wsl + - zerologlint + +linters-settings: + gocyclo: + min-complexity: 20 + gocognit: + min-complexity: 30 + lll: + line-length: 140 + misspell: + locale: US + funlen: + lines: 80 + statements: 60 + revive: + rules: + - name: exported + severity: warning + - name: unexported-return + severity: warning + gosec: + excludes: + - G204 # subprocess launched with variables + +issues: + exclude-rules: + - path: _test\.go + linters: + - errcheck + - gosec + - path: main\.go + linters: + - gochecknoinits + +output: + formats: + - format: colored-line-number diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..cea87d0 --- /dev/null +++ b/.yamllint @@ -0,0 +1,12 @@ +extends: default + +rules: + line-length: + max: 140 + document-start: disable + braces: + min-spaces-inside: 0 + max-spaces-inside: 1 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..afc2764 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.23-alpine AS builder +RUN apk add --no-cache git ca-certificates +WORKDIR /src +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /gosleep-timer . + +FROM scratch +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /gosleep-timer /gosleep-timer +ENTRYPOINT ["/gosleep-timer"] diff --git a/cicd.yaml b/cicd.yaml new file mode 100644 index 0000000..fd325dd --- /dev/null +++ b/cicd.yaml @@ -0,0 +1,141 @@ +name: ci + +'on': + push: + branches: + - '**' + pull_request: + workflow_dispatch: + +env: + REGISTRY: gcr.forust.xyz + IMAGE_NAME: forust/gosleep-timer + +jobs: + lint-go: + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + + - name: Lint with golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + args: --config .golangci.yml --timeout 10m + + - name: Run staticcheck + shell: bash + run: | + go install honnef.co/go/tools/cmd/staticcheck@latest + staticcheck ./... + + - name: Run gosec + shell: bash + run: | + go install github.com/securego/gosec/v2/cmd/gosec@latest + gosec -no-fail -fmt=text ./... + + - name: Run revive + shell: bash + run: | + go install github.com/mgechev/revive@latest + revive -config .revive.toml ./... + + lint-yaml: + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Lint YAML syntax + shell: bash + run: | + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + cytopia/yamllint:latest \ + -c .yamllint . + + build: + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + + - name: Build + shell: bash + run: go build -ldflags="-s -w" -o gosleep-timer . + + - name: Test + shell: bash + run: go test -v -race -coverprofile=cover.out ./... + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: gosleep-timer + path: gosleep-timer + + publish: + needs: [lint-go, lint-yaml, build] + if: github.event_name != 'pull_request' && (github.ref_name == 'main' || github.ref_name == 'dev') + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + + - name: Read project version + id: version + shell: bash + run: | + version="$(go version -m 2>/dev/null || date +%Y%m%d)" + echo "version=$version" >> "$GITHUB_OUTPUT" + + - name: Log in to registry + shell: bash + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \ + -u "${{ secrets.REGISTRY_USERNAME }}" \ + --password-stdin + + - name: Build and push image + shell: bash + run: | + image="${REGISTRY}/${IMAGE_NAME}" + tags=("latest" "${{ steps.version.outputs.version }}") + + case "${GITHUB_REF_NAME}" in + main) + tags+=("main" "prod") + ;; + dev) + tags+=("dev") + ;; + esac + + build_args=() + for tag in "${tags[@]}"; do + build_args+=(-t "${image}:${tag}") + done + + docker build "${build_args[@]}" . + + for tag in "${tags[@]}"; do + docker push "${image}:${tag}" + done