Skip to content

Commit c910acd

Browse files
committed
Add comprehensive CI/CD pipeline with validation and quality checks
- Add pre-release validation workflow for automated testing before releases - Add CI workflow for pull request validation - Enhance release workflow with prerequisite validation and dependency checks - Add golangci-lint configuration for consistent code quality - Expand Makefile with comprehensive testing, formatting, and linting targets - Add test coverage reporting and CI-optimized test commands - Clean up test helper trailing whitespace
1 parent 798f713 commit c910acd

7 files changed

Lines changed: 1082 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ "*" ]
6+
pull_request:
7+
branches: [ "*" ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
test:
12+
name: Test
13+
runs-on: ${{ matrix.os }}
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
os: [ubuntu-latest, macos-latest, windows-latest]
18+
go-version: [1.21.x, 1.22.x, 1.23.x]
19+
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Set up Go
25+
uses: actions/setup-go@v4
26+
with:
27+
go-version: ${{ matrix.go-version }}
28+
29+
- name: Cache Go modules
30+
uses: actions/cache@v3
31+
with:
32+
path: |
33+
~/.cache/go-build
34+
~/go/pkg/mod
35+
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
36+
restore-keys: |
37+
${{ runner.os }}-go-${{ matrix.go-version }}-
38+
${{ runner.os }}-go-
39+
40+
- name: Download dependencies
41+
run: go mod download
42+
43+
- name: Verify dependencies
44+
run: go mod verify
45+
46+
- name: Run go vet
47+
run: go vet ./...
48+
49+
- name: Run go fmt check
50+
run: |
51+
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
52+
echo "The following files are not formatted:"
53+
gofmt -s -l .
54+
echo "Please run 'go fmt ./...' to format your code."
55+
exit 1
56+
fi
57+
shell: bash
58+
59+
- name: Run tests
60+
run: go test -v -race -coverprofile=coverage.out ./...
61+
62+
- name: Run tests with coverage
63+
run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
64+
65+
- name: Upload coverage to Codecov
66+
uses: codecov/codecov-action@v3
67+
with:
68+
file: ./coverage.out
69+
flags: unittests
70+
name: codecov-${{ matrix.os }}-go${{ matrix.go-version }}
71+
72+
lint:
73+
name: Lint
74+
runs-on: ubuntu-latest
75+
steps:
76+
- name: Checkout code
77+
uses: actions/checkout@v4
78+
79+
- name: Set up Go
80+
uses: actions/setup-go@v4
81+
with:
82+
go-version: 1.23.x
83+
84+
- name: Run golangci-lint
85+
uses: golangci/golangci-lint-action@v3
86+
with:
87+
version: latest
88+
args: --timeout=5m
89+
90+
build:
91+
name: Build
92+
runs-on: ${{ matrix.os }}
93+
strategy:
94+
matrix:
95+
os: [ubuntu-latest, macos-latest, windows-latest]
96+
97+
steps:
98+
- name: Checkout code
99+
uses: actions/checkout@v4
100+
101+
- name: Set up Go
102+
uses: actions/setup-go@v4
103+
with:
104+
go-version: 1.23.x
105+
106+
- name: Build binary
107+
run: |
108+
go build -v -o pyinit ./cmd/pyinit
109+
shell: bash
110+
111+
- name: Test binary (Unix)
112+
if: runner.os != 'Windows'
113+
run: ./pyinit --version
114+
115+
- name: Test binary (Windows)
116+
if: runner.os == 'Windows'
117+
run: .\pyinit.exe --version
118+
119+
integration:
120+
name: Integration Tests
121+
runs-on: ubuntu-latest
122+
needs: [test, build]
123+
124+
steps:
125+
- name: Checkout code
126+
uses: actions/checkout@v4
127+
128+
- name: Set up Go
129+
uses: actions/setup-go@v4
130+
with:
131+
go-version: 1.23.x
132+
133+
- name: Run integration tests
134+
run: go test -v ./test/integration/...
135+
136+
- name: Test template rendering
137+
run: go test -v ./pkg/template/...
138+
139+
security:
140+
name: Security Scan
141+
runs-on: ubuntu-latest
142+
steps:
143+
- name: Checkout code
144+
uses: actions/checkout@v4
145+
146+
- name: Set up Go
147+
uses: actions/setup-go@v4
148+
with:
149+
go-version: 1.23.x
150+
151+
- name: Run Gosec Security Scanner
152+
uses: securecodewarrior/github-action-gosec@master
153+
with:
154+
args: '-no-fail -fmt sarif -out results.sarif ./...'
155+
156+
- name: Upload SARIF file
157+
uses: github/codeql-action/upload-sarif@v2
158+
with:
159+
sarif_file: results.sarif
160+
161+
test-coverage:
162+
name: Test Coverage Report
163+
runs-on: ubuntu-latest
164+
needs: test
165+
166+
steps:
167+
- name: Checkout code
168+
uses: actions/checkout@v4
169+
170+
- name: Set up Go
171+
uses: actions/setup-go@v4
172+
with:
173+
go-version: 1.23.x
174+
175+
- name: Run tests with coverage
176+
run: |
177+
go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
178+
go tool cover -html=coverage.out -o coverage.html
179+
180+
- name: Coverage Summary
181+
run: |
182+
echo "## Coverage Report" >> $GITHUB_STEP_SUMMARY
183+
echo "| Package | Coverage |" >> $GITHUB_STEP_SUMMARY
184+
echo "|---------|----------|" >> $GITHUB_STEP_SUMMARY
185+
go tool cover -func=coverage.out | while read line; do
186+
if [[ $line == *"total:"* ]]; then
187+
coverage=$(echo $line | awk '{print $3}')
188+
echo "| **Total** | **$coverage** |" >> $GITHUB_STEP_SUMMARY
189+
elif [[ $line == *".go:"* ]]; then
190+
file=$(echo $line | awk '{print $1}' | sed 's|.*/||')
191+
coverage=$(echo $line | awk '{print $3}')
192+
echo "| $file | $coverage |" >> $GITHUB_STEP_SUMMARY
193+
fi
194+
done
195+
196+
- name: Upload coverage artifact
197+
uses: actions/upload-artifact@v3
198+
with:
199+
name: coverage-report
200+
path: |
201+
coverage.out
202+
coverage.html
203+
204+
validate-release:
205+
name: Validate Release Readiness
206+
runs-on: ubuntu-latest
207+
if: startsWith(github.ref, 'refs/tags/')
208+
needs: [test, lint, build, integration, security]
209+
210+
steps:
211+
- name: Checkout code
212+
uses: actions/checkout@v4
213+
214+
- name: Set up Go
215+
uses: actions/setup-go@v4
216+
with:
217+
go-version: 1.23.x
218+
219+
- name: Validate all tests pass
220+
run: |
221+
echo "Running final validation for release ${{ github.ref_name }}"
222+
go test -v ./...
223+
224+
- name: Build release binary
225+
run: |
226+
go build -ldflags "-X github.com/Pradyothsp/pyinit/internal/version.Version=${{ github.ref_name }} -X github.com/Pradyothsp/pyinit/internal/version.GitCommit=${{ github.sha }} -X github.com/Pradyothsp/pyinit/internal/version.BuildDate=$(date -u +%Y-%m-%dT%H:%M:%SZ)" -o pyinit ./cmd/pyinit
227+
228+
- name: Test release binary
229+
run: |
230+
./pyinit --version
231+
echo "Release binary validated successfully"
232+
233+
- name: Check version consistency
234+
run: |
235+
binary_version=$(./pyinit --version | head -1 | awk '{print $2}')
236+
tag_version="${{ github.ref_name }}"
237+
if [ "$binary_version" != "$tag_version" ]; then
238+
echo "Version mismatch: binary=$binary_version, tag=$tag_version"
239+
exit 1
240+
fi
241+
echo "Version consistency validated: $binary_version"
242+
243+
notify-status:
244+
name: Notify Build Status
245+
runs-on: ubuntu-latest
246+
needs: [test, lint, build, integration]
247+
if: always()
248+
249+
steps:
250+
- name: Notify Success
251+
if: needs.test.result == 'success' && needs.lint.result == 'success' && needs.build.result == 'success' && needs.integration.result == 'success'
252+
run: |
253+
echo "✅ All CI checks passed successfully!"
254+
echo "- Tests: ✅"
255+
echo "- Linting: ✅"
256+
echo "- Build: ✅"
257+
echo "- Integration: ✅"
258+
259+
- name: Notify Failure
260+
if: needs.test.result == 'failure' || needs.lint.result == 'failure' || needs.build.result == 'failure' || needs.integration.result == 'failure'
261+
run: |
262+
echo "❌ CI checks failed!"
263+
echo "- Tests: ${{ needs.test.result }}"
264+
echo "- Linting: ${{ needs.lint.result }}"
265+
echo "- Build: ${{ needs.build.result }}"
266+
echo "- Integration: ${{ needs.integration.result }}"
267+
exit 1

0 commit comments

Comments
 (0)