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
194name : CI
205
216on :
227 push :
23- branches : [main, dev ]
8+ branches : [main]
249 pull_request :
25- branches : [main, dev ]
10+ branches : [main]
2611
2712permissions :
2813 contents : read
14+ security-events : write
2915
3016concurrency :
3117 group : ci-${{ github.workflow }}-${{ github.ref }}
3622
3723jobs :
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