Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .githooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Git Hooks

This directory contains version-controlled git hooks for the repository.

## Installation

To install the hooks, run:

```bash
make install-hooks
```

Or manually:

```bash
cp .githooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
```

## Hooks

### pre-commit

The pre-commit hook automatically:
1. Checks for potential secrets in staged changes (warning only)
2. Formats code with `gofumpt`
3. Stages any auto-formatted files
4. Runs the linter (`golangci-lint`)

If any step fails, the commit is blocked.

## Requirements

The hooks require:
- `gofumpt` - Install with: `make install-tools`
- `golangci-lint` - Install with: `make install-tools`

These are automatically installed when you run `make dev-setup`.
87 changes: 87 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/bin/bash

# Git pre-commit hook to enforce code formatting and linting
# This hook runs before each commit to ensure code quality

set -e

# Add Go bin directories to PATH (git hooks run in minimal environment)
export PATH="$HOME/go/bin:${GOPATH:-$HOME/go}/bin:$PATH"

echo "Running pre-commit checks..."

# Check if gofumpt is installed
if ! command -v gofumpt >/dev/null 2>&1; then
echo "Error: gofumpt not found. Install it with: make install-tools"
exit 1
fi

# Check if golangci-lint is installed
if ! command -v golangci-lint >/dev/null 2>&1; then
echo "Error: golangci-lint not found. Install it with: make install-tools"
exit 1
fi

# Check for potential secrets and show what was found (warning only)
SECRET_MATCHES=$(git diff --cached | grep -iE "(password|secret|api_key|token|private_key)" | grep -vE "(test|example|sample|TODO|FIXME)" || true)

if [ -n "$SECRET_MATCHES" ]; then
echo ""
echo "⚠️ WARNING: Potential secrets detected in staged changes!"
echo ""

# Show which files contain matches
echo "Files with potential secrets:"
for file in $(git diff --cached --name-only); do
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
echo " - $file"
fi
done

echo ""
echo "Sample matches (showing context):"
echo "----------------------------------------"
git diff --cached | grep -B3 -A3 -iE "(password|secret|api_key|token|private_key)" | grep -vE "(test|example|sample|TODO|FIXME|^--$)" | head -40
echo "----------------------------------------"
echo ""
echo "To see full details, run:"
echo " git diff --cached | grep -B5 -A5 -iE '(password|secret|api_key|token|private_key)'"
echo ""
echo "⚠️ Please review the above matches to ensure no real credentials are being committed."
echo " If these are test values or false positives, you can ignore this warning."
echo " If you need to undo this commit, run: git reset HEAD~1"
echo ""
fi

# Step 1: Format only staged Go files
echo "Formatting staged Go files with gofumpt..."

# Get list of staged Go files
STAGED_GO_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$' || true)

if [ -n "$STAGED_GO_FILES" ]; then
# Check which files need formatting (before formatting)
FILES_NEEDING_FORMAT=$(echo "$STAGED_GO_FILES" | xargs gofumpt -l 2>/dev/null || true)

if [ -n "$FILES_NEEDING_FORMAT" ]; then
# Format only the staged Go files
echo "$STAGED_GO_FILES" | xargs gofumpt -w

# Stage ONLY the files that were formatted (not all unstaged changes)
echo "Staging formatted files..."
echo "$FILES_NEEDING_FORMAT" | xargs git add
echo "⚠️ Some files were auto-formatted and have been staged:"
echo "$FILES_NEEDING_FORMAT" | sed 's/^/ /'
fi
fi

# Step 2: Run linter
echo "Running linter..."
if ! make lint; then
echo ""
echo "❌ Linting failed. Please fix the errors above before committing."
exit 1
fi

echo "✅ Pre-commit checks passed!"
exit 0
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
name: API Tests
name: API Contract Tests

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

on:
pull_request:
paths:
- '**.go'
- 'go.mod'
- 'go.sum'
- 'openapi.yaml'
- 'cmd/**'
- 'internal/**'
- 'pkg/**'
- '.github/workflows/api-contract-tests.yml'

concurrency:
group: api-tests-${{ github.head_ref }}

group: api-contract-tests-${{ github.head_ref }}
cancel-in-progress: true

jobs:
api-tests:
api-contract-tests:
name: API Contract Tests
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 10

services:
Expand Down
81 changes: 81 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Code Quality

# This workflow checks code quality: formatting, linting, security, and dependency verification

on:
pull_request:
paths:
- '**.go'
- 'go.mod'
- 'go.sum'
- 'Makefile'
- '.github/workflows/code-quality.yml'

concurrency:
group: code-quality-${{ github.head_ref }}
cancel-in-progress: true

jobs:
code-quality:
name: Code Quality Checks
runs-on: ubuntu-24.04
timeout-minutes: 5

steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
with:
go-version: '1.25.6'

- name: Cache Go modules
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-

- name: Install dependencies
run: go mod download

- name: Verify go.mod and go.sum
run: |
go mod verify
go mod tidy
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)

# Pin tool versions for reproducible builds
# Update these versions periodically and bump the cache key
- name: Cache development tools
id: cache-tools
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
with:
path: |
~/go/bin/gofumpt
~/go/bin/golangci-lint
~/go/bin/govulncheck
key: ${{ runner.os }}-dev-tools-gofumpt0.9.2-lint2.8.0-vuln1.1.4

- name: Install development tools
if: steps.cache-tools.outputs.cache-hit != 'true'
run: |
go install mvdan.cc/gofumpt@v0.9.2
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.8.0
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4

- name: Check code formatting
run: make fmt-check

- name: Run linter
run: make lint

- name: Security scan
run: govulncheck ./...

- name: Verify build
run: go build ./...
118 changes: 118 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: Tests

# This workflow runs unit and integration tests

on:
pull_request:
paths:
- '**.go'
- 'go.mod'
- 'go.sum'
- 'sql/**'
- 'tests/**'
- '.github/workflows/tests.yml'

concurrency:
group: tests-${{ github.head_ref }}
cancel-in-progress: true

jobs:
unit-tests:
name: Unit Tests
runs-on: ubuntu-24.04
timeout-minutes: 5

steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
with:
go-version: '1.25.6'

- name: Cache Go modules
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-

- name: Install dependencies
run: go mod download

- name: Run unit tests
run: make test-unit

integration-tests:
name: Integration Tests
runs-on: ubuntu-24.04
timeout-minutes: 10

services:
postgres:
image: pgvector/pgvector:pg18
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db?sslmode=disable
API_KEY: test-api-key-for-ci

steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff
with:
go-version: '1.25.6'

- name: Cache Go modules
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-

- name: Install dependencies
run: go mod download

- name: Initialize database schema
run: make init-db

- name: Run integration tests
run: make tests

tests:
name: All Tests
needs: [unit-tests, integration-tests]
runs-on: ubuntu-24.04
if: always()
steps:
- name: Check test results
run: |
if [ "${{ needs.unit-tests.result }}" != "success" ]; then
echo "Unit tests failed"
exit 1
fi
if [ "${{ needs.integration-tests.result }}" != "success" ]; then
echo "Integration tests failed"
exit 1
fi
echo "✅ All tests passed!"
Loading
Loading