Skip to content

Commit c571c5a

Browse files
xernobylmattinannt
andauthored
feat: add git hooks and related changes (#5)
* feat: add git hooks and related changes * chore: split ci workflows * chore: refactor makefile * chore: optimized git commit hook * fix: only stage changed files, not all unstaged * chore: add pre-commit hooks to README * chore: pin versions, add build check * fix: correct golangci-lint v2 module path * chore: fix linting issues --------- Co-authored-by: Matti Nannt <matti@formbricks.com>
1 parent 0357a2e commit c571c5a

File tree

9 files changed

+456
-71
lines changed

9 files changed

+456
-71
lines changed

.githooks/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Git Hooks
2+
3+
This directory contains version-controlled git hooks for the repository.
4+
5+
## Installation
6+
7+
To install the hooks, run:
8+
9+
```bash
10+
make install-hooks
11+
```
12+
13+
Or manually:
14+
15+
```bash
16+
cp .githooks/pre-commit .git/hooks/pre-commit
17+
chmod +x .git/hooks/pre-commit
18+
```
19+
20+
## Hooks
21+
22+
### pre-commit
23+
24+
The pre-commit hook automatically:
25+
1. Checks for potential secrets in staged changes (warning only)
26+
2. Formats code with `gofumpt`
27+
3. Stages any auto-formatted files
28+
4. Runs the linter (`golangci-lint`)
29+
30+
If any step fails, the commit is blocked.
31+
32+
## Requirements
33+
34+
The hooks require:
35+
- `gofumpt` - Install with: `make install-tools`
36+
- `golangci-lint` - Install with: `make install-tools`
37+
38+
These are automatically installed when you run `make dev-setup`.

.githooks/pre-commit

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/bin/bash
2+
3+
# Git pre-commit hook to enforce code formatting and linting
4+
# This hook runs before each commit to ensure code quality
5+
6+
set -e
7+
8+
# Add Go bin directories to PATH (git hooks run in minimal environment)
9+
export PATH="$HOME/go/bin:${GOPATH:-$HOME/go}/bin:$PATH"
10+
11+
echo "Running pre-commit checks..."
12+
13+
# Check if gofumpt is installed
14+
if ! command -v gofumpt >/dev/null 2>&1; then
15+
echo "Error: gofumpt not found. Install it with: make install-tools"
16+
exit 1
17+
fi
18+
19+
# Check if golangci-lint is installed
20+
if ! command -v golangci-lint >/dev/null 2>&1; then
21+
echo "Error: golangci-lint not found. Install it with: make install-tools"
22+
exit 1
23+
fi
24+
25+
# Check for potential secrets and show what was found (warning only)
26+
SECRET_MATCHES=$(git diff --cached | grep -iE "(password|secret|api_key|token|private_key)" | grep -vE "(test|example|sample|TODO|FIXME)" || true)
27+
28+
if [ -n "$SECRET_MATCHES" ]; then
29+
echo ""
30+
echo "⚠️ WARNING: Potential secrets detected in staged changes!"
31+
echo ""
32+
33+
# Show which files contain matches
34+
echo "Files with potential secrets:"
35+
for file in $(git diff --cached --name-only); do
36+
if git diff --cached -- "$file" 2>/dev/null | grep -iE "(password|secret|api_key|token|private_key)" | grep -vE "(test|example|sample|TODO|FIXME)" | grep -q .; then
37+
echo " - $file"
38+
fi
39+
done
40+
41+
echo ""
42+
echo "Sample matches (showing context):"
43+
echo "----------------------------------------"
44+
git diff --cached | grep -B3 -A3 -iE "(password|secret|api_key|token|private_key)" | grep -vE "(test|example|sample|TODO|FIXME|^--$)" | head -40
45+
echo "----------------------------------------"
46+
echo ""
47+
echo "To see full details, run:"
48+
echo " git diff --cached | grep -B5 -A5 -iE '(password|secret|api_key|token|private_key)'"
49+
echo ""
50+
echo "⚠️ Please review the above matches to ensure no real credentials are being committed."
51+
echo " If these are test values or false positives, you can ignore this warning."
52+
echo " If you need to undo this commit, run: git reset HEAD~1"
53+
echo ""
54+
fi
55+
56+
# Step 1: Format only staged Go files
57+
echo "Formatting staged Go files with gofumpt..."
58+
59+
# Get list of staged Go files
60+
STAGED_GO_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$' || true)
61+
62+
if [ -n "$STAGED_GO_FILES" ]; then
63+
# Check which files need formatting (before formatting)
64+
FILES_NEEDING_FORMAT=$(echo "$STAGED_GO_FILES" | xargs gofumpt -l 2>/dev/null || true)
65+
66+
if [ -n "$FILES_NEEDING_FORMAT" ]; then
67+
# Format only the staged Go files
68+
echo "$STAGED_GO_FILES" | xargs gofumpt -w
69+
70+
# Stage ONLY the files that were formatted (not all unstaged changes)
71+
echo "Staging formatted files..."
72+
echo "$FILES_NEEDING_FORMAT" | xargs git add
73+
echo "⚠️ Some files were auto-formatted and have been staged:"
74+
echo "$FILES_NEEDING_FORMAT" | sed 's/^/ /'
75+
fi
76+
fi
77+
78+
# Step 2: Run linter
79+
echo "Running linter..."
80+
if ! make lint; then
81+
echo ""
82+
echo "❌ Linting failed. Please fix the errors above before committing."
83+
exit 1
84+
fi
85+
86+
echo "✅ Pre-commit checks passed!"
87+
exit 0
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
1-
name: API Tests
1+
name: API Contract Tests
22

33
# This workflow tests your API against the OpenAPI spec using Schemathesis.
44
# It only runs the "examples" phase (fast & deterministic).
55
# For thorough fuzzing, run locally: make schemathesis
66

77
on:
88
pull_request:
9+
paths:
10+
- '**.go'
11+
- 'go.mod'
12+
- 'go.sum'
13+
- 'openapi.yaml'
14+
- 'cmd/**'
15+
- 'internal/**'
16+
- 'pkg/**'
17+
- '.github/workflows/api-contract-tests.yml'
918

1019
concurrency:
11-
group: api-tests-${{ github.head_ref }}
12-
20+
group: api-contract-tests-${{ github.head_ref }}
1321
cancel-in-progress: true
1422

1523
jobs:
16-
api-tests:
24+
api-contract-tests:
1725
name: API Contract Tests
18-
runs-on: ubuntu-latest
26+
runs-on: ubuntu-24.04
1927
timeout-minutes: 10
2028

2129
services:

.github/workflows/code-quality.yml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: Code Quality
2+
3+
# This workflow checks code quality: formatting, linting, security, and dependency verification
4+
5+
on:
6+
pull_request:
7+
paths:
8+
- '**.go'
9+
- 'go.mod'
10+
- 'go.sum'
11+
- 'Makefile'
12+
- '.github/workflows/code-quality.yml'
13+
14+
concurrency:
15+
group: code-quality-${{ github.head_ref }}
16+
cancel-in-progress: true
17+
18+
jobs:
19+
code-quality:
20+
name: Code Quality Checks
21+
runs-on: ubuntu-24.04
22+
timeout-minutes: 5
23+
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
27+
28+
- name: Set up Go
29+
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
30+
with:
31+
go-version: '1.25.6'
32+
33+
- name: Cache Go modules
34+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
35+
with:
36+
path: |
37+
~/.cache/go-build
38+
~/go/pkg/mod
39+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
40+
restore-keys: |
41+
${{ runner.os }}-go-
42+
43+
- name: Install dependencies
44+
run: go mod download
45+
46+
- name: Verify go.mod and go.sum
47+
run: |
48+
go mod verify
49+
go mod tidy
50+
git diff --exit-code go.mod go.sum || (echo "Error: go.mod or go.sum are out of sync. Run 'go mod tidy' and commit the changes." && exit 1)
51+
52+
# Pin tool versions for reproducible builds
53+
# Update these versions periodically and bump the cache key
54+
- name: Cache development tools
55+
id: cache-tools
56+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
57+
with:
58+
path: |
59+
~/go/bin/gofumpt
60+
~/go/bin/golangci-lint
61+
~/go/bin/govulncheck
62+
key: ${{ runner.os }}-dev-tools-gofumpt0.9.2-lint2.8.0-vuln1.1.4
63+
64+
- name: Install development tools
65+
if: steps.cache-tools.outputs.cache-hit != 'true'
66+
run: |
67+
go install mvdan.cc/gofumpt@v0.9.2
68+
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.8.0
69+
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
70+
71+
- name: Check code formatting
72+
run: make fmt-check
73+
74+
- name: Run linter
75+
run: make lint
76+
77+
- name: Security scan
78+
run: govulncheck ./...
79+
80+
- name: Verify build
81+
run: go build ./...

.github/workflows/tests.yml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
name: Tests
2+
3+
# This workflow runs unit and integration tests
4+
5+
on:
6+
pull_request:
7+
paths:
8+
- '**.go'
9+
- 'go.mod'
10+
- 'go.sum'
11+
- 'sql/**'
12+
- 'tests/**'
13+
- '.github/workflows/tests.yml'
14+
15+
concurrency:
16+
group: tests-${{ github.head_ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
unit-tests:
21+
name: Unit Tests
22+
runs-on: ubuntu-24.04
23+
timeout-minutes: 5
24+
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
28+
29+
- name: Set up Go
30+
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
31+
with:
32+
go-version: '1.25.6'
33+
34+
- name: Cache Go modules
35+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
36+
with:
37+
path: |
38+
~/.cache/go-build
39+
~/go/pkg/mod
40+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
41+
restore-keys: |
42+
${{ runner.os }}-go-
43+
44+
- name: Install dependencies
45+
run: go mod download
46+
47+
- name: Run unit tests
48+
run: make test-unit
49+
50+
integration-tests:
51+
name: Integration Tests
52+
runs-on: ubuntu-24.04
53+
timeout-minutes: 10
54+
55+
services:
56+
postgres:
57+
image: pgvector/pgvector:pg18
58+
env:
59+
POSTGRES_USER: postgres
60+
POSTGRES_PASSWORD: postgres
61+
POSTGRES_DB: test_db
62+
ports:
63+
- 5432:5432
64+
options: >-
65+
--health-cmd pg_isready
66+
--health-interval 10s
67+
--health-timeout 5s
68+
--health-retries 5
69+
70+
env:
71+
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db?sslmode=disable
72+
API_KEY: test-api-key-for-ci
73+
74+
steps:
75+
- name: Checkout
76+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
77+
78+
- name: Set up Go
79+
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
80+
with:
81+
go-version: '1.25.6'
82+
83+
- name: Cache Go modules
84+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
85+
with:
86+
path: |
87+
~/.cache/go-build
88+
~/go/pkg/mod
89+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
90+
restore-keys: |
91+
${{ runner.os }}-go-
92+
93+
- name: Install dependencies
94+
run: go mod download
95+
96+
- name: Initialize database schema
97+
run: make init-db
98+
99+
- name: Run integration tests
100+
run: make tests
101+
102+
tests:
103+
name: All Tests
104+
needs: [unit-tests, integration-tests]
105+
runs-on: ubuntu-24.04
106+
if: always()
107+
steps:
108+
- name: Check test results
109+
run: |
110+
if [ "${{ needs.unit-tests.result }}" != "success" ]; then
111+
echo "Unit tests failed"
112+
exit 1
113+
fi
114+
if [ "${{ needs.integration-tests.result }}" != "success" ]; then
115+
echo "Integration tests failed"
116+
exit 1
117+
fi
118+
echo "✅ All tests passed!"

0 commit comments

Comments
 (0)