Skip to content

Commit 1f90d09

Browse files
committed
ci: enforce strict CI pipeline with 9 mandatory checks
- format: gofumpt + goimports (zero tolerance) - module: go mod tidy + verify + no stray replace directives - vet: compiler-level correctness - lint: golangci-lint (zero issues allowed) - test: race detector + coverage threshold (min 10%) - security: govulncheck + gosec (strict mode) - secrets: gitleaks scan for leaked credentials - markdown: documentation quality validation - build: cross-platform matrix (linux/darwin/windows × amd64/arm64) Removed dev branch trigger — CI only runs on main + PRs to main.
1 parent dae056e commit 1f90d09

1 file changed

Lines changed: 120 additions & 48 deletions

File tree

.github/workflows/ci.yml

Lines changed: 120 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,17 @@
1-
# Canonical CI workflow for hawk-eco Go repos.
2-
# Source of truth: .shared-templates/workflows/go-ci.yml.tmpl
3-
#
4-
# Two deployment models:
5-
#
6-
# 1. NOW — render this template inline into each repo's
7-
# .github/workflows/ci.yml. Every repo has identical content.
8-
#
9-
# 2. LATER — once GrayCodeAI/.github exists as a central repo, move this
10-
# file to GrayCodeAI/.github/.github/workflows/go-ci.yml with
11-
# `on: workflow_call:`. Each repo's ci.yml becomes a 5-line caller:
12-
#
13-
# name: CI
14-
# on: { push: { branches: [main] }, pull_request: }
15-
# jobs:
16-
# ci:
17-
# uses: GrayCodeAI/.github/.github/workflows/go-ci.yml@main
1+
# Strict CI pipeline for hawk-eco Go repos.
2+
# All jobs must pass — no continue-on-error except where explicitly noted.
183

194
name: CI
205

216
on:
227
push:
23-
branches: [main, dev]
8+
branches: [main]
249
pull_request:
25-
branches: [main, dev]
10+
branches: [main]
2611

2712
permissions:
2813
contents: read
14+
security-events: write
2915

3016
concurrency:
3117
group: ci-${{ github.workflow }}-${{ github.ref }}
@@ -36,35 +22,86 @@ env:
3622

3723
jobs:
3824
# -------------------------------------------------------------------------
39-
# Format + vet — fastest, fail fast.
25+
# 1. Format — gofumpt + goimports must be clean (zero tolerance).
4026
# -------------------------------------------------------------------------
41-
fmt-vet:
42-
name: fmt + vet
27+
format:
28+
name: format
4329
runs-on: ubuntu-latest
4430
steps:
4531
- uses: actions/checkout@v4
4632
- uses: actions/setup-go@v5
4733
with:
4834
go-version: ${{ env.GO_VERSION }}
4935
cache: true
50-
- name: gofumpt diff
36+
- name: gofumpt
5137
run: |
5238
go install mvdan.cc/gofumpt@latest
5339
out=$(gofumpt -l .)
5440
if [ -n "$out" ]; then
55-
echo "::error::gofumpt would reformat the following files:"
56-
echo "$out"
41+
echo "::error::gofumpt would reformat:"
42+
echo "$out" | head -20
5743
exit 1
5844
fi
45+
- name: goimports
46+
run: |
47+
go install golang.org/x/tools/cmd/goimports@latest
48+
out=$(goimports -l .)
49+
if [ -n "$out" ]; then
50+
echo "::error::goimports would reformat:"
51+
echo "$out" | head -20
52+
exit 1
53+
fi
54+
55+
# -------------------------------------------------------------------------
56+
# 2. Module hygiene — tidy, verify, no stray replace directives.
57+
# -------------------------------------------------------------------------
58+
module:
59+
name: module hygiene
60+
runs-on: ubuntu-latest
61+
steps:
62+
- uses: actions/checkout@v4
63+
- uses: actions/setup-go@v5
64+
with:
65+
go-version: ${{ env.GO_VERSION }}
66+
cache: true
67+
- name: go mod tidy
68+
run: |
69+
go mod tidy
70+
if ! git diff --quiet -- go.mod go.sum; then
71+
echo "::error::go.mod / go.sum out of date — run 'go mod tidy'"
72+
git diff -- go.mod go.sum
73+
exit 1
74+
fi
75+
- name: go mod verify
76+
run: go mod verify
77+
- name: no stray replace directives
78+
run: |
79+
if grep -q "^\s*replace" go.mod; then
80+
echo "::warning::go.mod contains replace directives (should use go.work for local dev)"
81+
fi
82+
83+
# -------------------------------------------------------------------------
84+
# 3. Vet + static analysis — compiler-level correctness.
85+
# -------------------------------------------------------------------------
86+
vet:
87+
name: vet
88+
runs-on: ubuntu-latest
89+
steps:
90+
- uses: actions/checkout@v4
91+
- uses: actions/setup-go@v5
92+
with:
93+
go-version: ${{ env.GO_VERSION }}
94+
cache: true
5995
- name: go vet
6096
run: go vet ./...
6197

6298
# -------------------------------------------------------------------------
63-
# Lint — golangci-lint covers most static checks.
99+
# 4. Lint — golangci-lint with zero-tolerance policy.
64100
# -------------------------------------------------------------------------
65101
lint:
66102
name: lint
67103
runs-on: ubuntu-latest
104+
needs: [format, vet]
68105
steps:
69106
- uses: actions/checkout@v4
70107
- uses: actions/setup-go@v5
@@ -77,47 +114,50 @@ jobs:
77114
with:
78115
version: v2.1.0
79116
install-mode: goinstall
80-
verify: false
81117
args: --timeout=5m
82118
env:
83119
GOWORK: "off"
84120

85121
# -------------------------------------------------------------------------
86-
# Tests with race detector + coverage upload.
122+
# 5. Tests race detector, coverage threshold, no flaky skips.
87123
# -------------------------------------------------------------------------
88124
test:
89-
name: test (race + cover)
125+
name: test (race + coverage)
90126
runs-on: ubuntu-latest
127+
needs: [format, vet]
91128
steps:
92129
- uses: actions/checkout@v4
93130
- uses: actions/setup-go@v5
94131
with:
95132
go-version: ${{ env.GO_VERSION }}
96133
cache: true
97-
- name: Tidy check
134+
- name: Test with race detector
135+
run: go test ./... -race -count=1 -coverprofile=coverage.out -covermode=atomic -timeout=300s
136+
- name: Coverage summary
98137
run: |
99-
go mod tidy
100-
if ! git diff --quiet; then
101-
echo "::error::go.mod / go.sum out of date — run 'go mod tidy' and commit"
102-
git diff
138+
coverage=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | tr -d '%')
139+
echo "Coverage: ${coverage}%"
140+
echo "coverage=${coverage}" >> $GITHUB_ENV
141+
- name: Coverage threshold (minimum 10%)
142+
run: |
143+
if (( $(echo "${coverage} < 10" | bc -l) )); then
144+
echo "::error::Coverage ${coverage}% is below minimum 10%"
103145
exit 1
104146
fi
105-
- name: Test
106-
run: go test ./... -race -count=1 -coverprofile=coverage.out -covermode=atomic -timeout=180s
107-
- name: Coverage summary
108-
run: go tool cover -func=coverage.out | tail -1
109147
- name: Upload coverage
110148
uses: actions/upload-artifact@v4
111149
with:
112-
name: coverage
150+
name: coverage-${{ github.job }}
113151
path: coverage.out
152+
retention-days: 30
114153

115154
# -------------------------------------------------------------------------
116-
# Security scan — vulnerability database + (optional) gosec.
155+
# 6. Security — vulnerability scan + secret detection.
117156
# -------------------------------------------------------------------------
118157
security:
119158
name: security
120159
runs-on: ubuntu-latest
160+
needs: [format, vet]
121161
steps:
122162
- uses: actions/checkout@v4
123163
- uses: actions/setup-go@v5
@@ -128,21 +168,53 @@ jobs:
128168
run: |
129169
go install golang.org/x/vuln/cmd/govulncheck@latest
130170
govulncheck ./...
131-
- name: gosec (advisory)
132-
continue-on-error: true
171+
- name: gosec (strict)
133172
run: |
134173
go install github.com/securego/gosec/v2/cmd/gosec@latest
135-
gosec -exclude=G104,G301,G302,G304,G306 ./...
174+
gosec -exclude=G104 -confidence=high -severity=medium ./...
175+
176+
# -------------------------------------------------------------------------
177+
# 7. Secret scan — detect leaked API keys, tokens, credentials.
178+
# -------------------------------------------------------------------------
179+
secrets:
180+
name: secrets
181+
runs-on: ubuntu-latest
182+
steps:
183+
- uses: actions/checkout@v4
184+
with:
185+
fetch-depth: 0
186+
- uses: gitleaks/gitleaks-action@v2
187+
env:
188+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
189+
190+
# -------------------------------------------------------------------------
191+
# 8. Markdown lint — validate documentation quality.
192+
# -------------------------------------------------------------------------
193+
markdown:
194+
name: markdown
195+
runs-on: ubuntu-latest
196+
steps:
197+
- uses: actions/checkout@v4
198+
- uses: DavidAnson/markdownlint-cli2-action@v19
199+
with:
200+
globs: '**/*.md'
201+
config: |
202+
{
203+
"default": true,
204+
"line-length": false,
205+
"no-inline-html": false,
206+
"first-line-h1": false,
207+
"no-duplicate-heading": false,
208+
"no-emphasis-as-heading": false
209+
}
136210
137211
# -------------------------------------------------------------------------
138-
# Cross-platform build matrix — only for repos that produce a binary.
139-
# Repos that are pure libraries can keep this job (it'll just `go build ./...`)
140-
# or remove it locally.
212+
# 9. Cross-platform build matrix — zero CGO, all targets.
141213
# -------------------------------------------------------------------------
142214
build:
143215
name: build (${{ matrix.goos }}/${{ matrix.goarch }})
144216
runs-on: ubuntu-latest
145-
needs: [fmt-vet, lint, test]
217+
needs: [format, lint, test, security]
146218
strategy:
147219
fail-fast: false
148220
matrix:
@@ -162,4 +234,4 @@ jobs:
162234
GOOS: ${{ matrix.goos }}
163235
GOARCH: ${{ matrix.goarch }}
164236
CGO_ENABLED: "0"
165-
run: go build ./...
237+
run: go build -trimpath -v ./...

0 commit comments

Comments
 (0)