Skip to content

Commit 1a0339f

Browse files
dugshubclaude
andcommitted
CI: Add GitHub Actions workflow with Pattern Stack standards
- Create main CI workflow with matrix strategy - Implement parallel test execution by category - Add composite actions for quality and test targets - Create Pattern Stack abstractions for reusability - Support both Docker and native execution paths - Add sync verification workflow template This establishes a standardized CI architecture that can be extended across Pattern Stack projects. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c4d6ad4 commit 1a0339f

File tree

6 files changed

+362
-0
lines changed

6 files changed

+362
-0
lines changed

.github/AUTH_SETUP.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Pattern Stack Authentication Setup
2+
3+
This document explains how to set up authentication for Pattern Stack repositories to access private dependencies.
4+
5+
## Current Setup: Service Account + PAT
6+
7+
### 1. Create Service Account (One-time per organization)
8+
9+
1. **Create GitHub Account**
10+
- Create a new GitHub account: `pattern-stack-ci`
11+
- Use an email like `ci@pattern-stack.com`
12+
13+
2. **Add to Organization**
14+
- Invite `pattern-stack-ci` to the `pattern-stack` organization
15+
- Grant `Read` access to repositories that need to be accessed by CI
16+
- Specifically ensure access to:
17+
- `pattern-stack/geography-patterns`
18+
- `pattern-stack/backend-patterns`
19+
20+
### 2. Generate Personal Access Token
21+
22+
1. **Login as Service Account**
23+
- Login to GitHub as `pattern-stack-ci`
24+
25+
2. **Create PAT**
26+
- Go to Settings → Developer settings → Personal access tokens → Tokens (classic)
27+
- Click "Generate new token (classic)"
28+
- **Name**: `Pattern Stack CI Access`
29+
- **Expiration**: 1 year (set calendar reminder to rotate)
30+
- **Scopes**:
31+
-`repo` (Full control of private repositories)
32+
- Generate and copy the token
33+
34+
### 3. Add to Repository Secrets
35+
36+
For each repository that needs access:
37+
38+
1. Go to repository Settings → Secrets and variables → Actions
39+
2. Click "New repository secret"
40+
3. **Name**: `PATTERN_STACK_TOKEN`
41+
4. **Value**: The PAT from step 2
42+
5. Save
43+
44+
### 4. Verify Setup
45+
46+
The workflows should now:
47+
- Use `PATTERN_STACK_TOKEN` for checkout and git configuration
48+
- Successfully install dependencies from private repositories
49+
- Pass all CI checks
50+
51+
## Auth Pattern Used in Workflows
52+
53+
All workflows use this consistent pattern:
54+
55+
```yaml
56+
steps:
57+
- uses: actions/checkout@v4
58+
with:
59+
token: ${{ secrets.PATTERN_STACK_TOKEN }}
60+
61+
- name: Configure git for private repo access
62+
run: |
63+
git config --global url."https://x-access-token:${{ secrets.PATTERN_STACK_TOKEN }}@github.com/".insteadOf "https://github.com/"
64+
65+
- name: Install dependencies
66+
run: |
67+
uv sync --frozen
68+
# Dependencies from private repos now work
69+
```
70+
71+
## Future Migration: GitHub App
72+
73+
When scaling to multiple repositories, we'll migrate to a GitHub App approach:
74+
75+
1. **Benefits**: Better security, automatic token rotation, granular permissions
76+
2. **Implementation**: Pattern Stack tooling will automate the creation and installation
77+
3. **Migration**: Seamless - workflows use same `PATTERN_STACK_TOKEN` interface
78+
79+
## Troubleshooting
80+
81+
### Common Issues
82+
83+
1. **"fatal: could not read Username"**
84+
- Verify `PATTERN_STACK_TOKEN` secret exists in repository
85+
- Check service account has access to target repositories
86+
- Verify PAT has `repo` scope
87+
88+
2. **PAT Expired**
89+
- Generate new PAT with same scopes
90+
- Update `PATTERN_STACK_TOKEN` secret in all repositories
91+
- Set calendar reminder for next rotation
92+
93+
3. **403 Forbidden**
94+
- Service account needs to be added to private repositories
95+
- Check organization membership and repository access
96+
97+
### Security Notes
98+
99+
- PAT has broad access - rotate regularly (annually)
100+
- Only add to repositories that need private dependency access
101+
- Consider GitHub App migration for better security posture
102+
- Monitor usage in organization audit logs

.github/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# GitHub Actions Workflows
2+
3+
This directory contains CI/CD workflows for the geography-patterns monorepo.
4+
5+
## Workflow Structure
6+
7+
### Per-Project Testing
8+
- **`test-wof-explorer.yml`** - Tests for WOF Explorer package
9+
- **`test-geo-platform.yml`** - Tests for Geo Platform package
10+
11+
### Quality Checks
12+
- **`quality-checks.yml`** - Linting, type checking, and formatting checks across both packages
13+
14+
### Orchestration
15+
- **`ci.yml`** - Main CI workflow that runs all checks together
16+
17+
## Workflow Details
18+
19+
### Test WOF Explorer (`test-wof-explorer.yml`)
20+
- **Triggers**: Changes to `wof-explorer/` directory, workflow file, or dependencies
21+
- **Python versions**: 3.11, 3.12, 3.13
22+
- **Test database**: Downloads Barbados WOF database for integration tests
23+
- **Commands**:
24+
- `uv run pytest tests/ -v`
25+
- `uv run pytest tests/test_examples.py -v`
26+
27+
### Test Geo Platform (`test-geo-platform.yml`)
28+
- **Triggers**: Changes to `geo-platform/` directory, workflow file, or dependencies
29+
- **Python versions**: 3.11, 3.12, 3.13
30+
- **Services**: PostgreSQL with PostGIS extension
31+
- **Commands**:
32+
- `uv run pytest __tests__/unit/ -v`
33+
- `uv run pytest __tests__/integration/ -v`
34+
- `uv run pytest __tests__/ -v`
35+
36+
### Quality Checks (`quality-checks.yml`)
37+
- **Triggers**: All pushes and PRs
38+
- **Matrix**: Runs for both `wof-explorer` and `geo-platform`
39+
- **Jobs**:
40+
- **Lint**: `uv run ruff check .`
41+
- **Typecheck**: `uv run mypy src/`
42+
- **Format Check**: `uv run ruff format --check .` (+ black for WOF Explorer)
43+
44+
### Main CI (`ci.yml`)
45+
- **Triggers**: Pushes to main/develop branches, all PRs
46+
- **Strategy**: Orchestrates all other workflows
47+
- **Final check**: Ensures all sub-workflows pass before marking CI as successful
48+
49+
## Quality Standards
50+
51+
### Expected Results
52+
- **Geo Platform**: All checks should pass (0 linting issues, 0 type issues)
53+
- **WOF Explorer**: Known issues documented (41 linting issues, 343 type issues)
54+
55+
### Failure Handling
56+
- Geo Platform failures block CI
57+
- WOF Explorer quality issues are documented but don't block CI (`continue-on-error: true`)
58+
- Test failures always block CI for both packages
59+
60+
## Local Development
61+
62+
Run the same checks locally using Make commands:
63+
64+
```bash
65+
# Run all checks
66+
make check
67+
68+
# Per-package testing
69+
make test-wof
70+
make test-geo
71+
72+
# Quality checks
73+
make lint
74+
make typecheck
75+
make format
76+
```
77+
78+
## Path-Based Triggers
79+
80+
Workflows are optimized to only run when relevant files change:
81+
82+
- Package-specific workflows only trigger on changes to their respective directories
83+
- Quality checks run on all changes
84+
- Dependencies changes (pyproject.toml, uv.lock) trigger relevant workflows

.github/actions/setup/action.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Setup Environment
2+
description: Setup cli-patterns environment
3+
4+
inputs:
5+
python-version:
6+
description: Python version
7+
default: '3.9'
8+
9+
runs:
10+
using: composite
11+
steps:
12+
# In future, this would be: pattern-stack/actions/setup@v1
13+
# For now, use local pattern-stack standard
14+
- uses: ./.github/workflows/pattern-stack/setup
15+
with:
16+
python-version: ${{ inputs.python-version }}

.github/workflows/ci.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
workflow_dispatch:
8+
inputs:
9+
use_docker:
10+
description: 'Run tests in Docker'
11+
type: boolean
12+
default: false
13+
14+
env:
15+
PYTHONPATH: src
16+
17+
jobs:
18+
quality:
19+
name: Quality Checks
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
- uses: ./.github/actions/setup
24+
- run: make quality
25+
26+
test:
27+
name: Test - ${{ matrix.suite }}
28+
runs-on: ubuntu-latest
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
suite: [unit, integration, parser, executor, design]
33+
34+
steps:
35+
- uses: actions/checkout@v4
36+
37+
# Native path (default)
38+
- if: ${{ !inputs.use_docker }}
39+
uses: ./.github/actions/setup
40+
- if: ${{ !inputs.use_docker }}
41+
run: make test-${{ matrix.suite }}
42+
43+
# Docker path (on demand)
44+
- if: ${{ inputs.use_docker }}
45+
run: |
46+
docker compose -f docker-compose.ci.yml run \
47+
ci make test-${{ matrix.suite }}
48+
49+
test-fast:
50+
name: Fast Tests
51+
runs-on: ubuntu-latest
52+
steps:
53+
- uses: actions/checkout@v4
54+
- uses: ./.github/actions/setup
55+
- run: make test-fast
56+
57+
# Python compatibility check (on main branch)
58+
compatibility:
59+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
60+
name: Python ${{ matrix.python }}
61+
runs-on: ubuntu-latest
62+
strategy:
63+
matrix:
64+
python: ["3.9", "3.11", "3.13"]
65+
66+
steps:
67+
- uses: actions/checkout@v4
68+
- uses: ./.github/actions/setup
69+
with:
70+
python-version: ${{ matrix.python }}
71+
- run: make test-fast
72+
73+
# Future: Performance benchmarks
74+
benchmark:
75+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
76+
name: Performance Benchmark
77+
runs-on: ubuntu-latest
78+
continue-on-error: true # Don't fail CI if benchmarks regress (for now)
79+
80+
steps:
81+
- uses: actions/checkout@v4
82+
- name: Run benchmarks in consistent environment
83+
run: |
84+
docker compose -f docker-compose.ci.yml run \
85+
benchmark make benchmark || echo "No benchmarks yet"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Pattern Stack Environment Setup
2+
description: Standard environment setup for Pattern Stack projects
3+
4+
inputs:
5+
python-version:
6+
description: Python version to use
7+
default: '3.9'
8+
9+
runs:
10+
using: composite
11+
steps:
12+
- name: Install uv
13+
uses: astral-sh/setup-uv@v3
14+
with:
15+
enable-cache: true
16+
17+
- name: Setup Python
18+
shell: bash
19+
run: uv python install ${{ inputs.python-version }}
20+
21+
- name: Install dependencies
22+
shell: bash
23+
run: |
24+
uv sync --frozen
25+
echo "✅ Pattern Stack environment ready (Python ${{ inputs.python-version }})"

.github/workflows/sync-check.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Environment Sync Check
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- '.github/**'
7+
- 'docker-compose.ci.yml'
8+
- 'pyproject.toml'
9+
- 'uv.lock'
10+
- 'Makefile'
11+
12+
jobs:
13+
verify:
14+
name: Verify Native/Docker Sync
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Get native environment
21+
uses: ./.github/actions/setup
22+
- run: |
23+
uv pip freeze | sort > /tmp/native-deps.txt
24+
python --version > /tmp/native-version.txt
25+
26+
- name: Get Docker environment
27+
run: |
28+
docker compose -f docker-compose.ci.yml run ci \
29+
sh -c "uv pip freeze | sort" > /tmp/docker-deps.txt
30+
docker compose -f docker-compose.ci.yml run ci \
31+
python --version > /tmp/docker-version.txt
32+
33+
- name: Compare environments
34+
run: |
35+
echo "=== Python Version Diff ==="
36+
diff /tmp/native-version.txt /tmp/docker-version.txt || true
37+
echo "=== Dependencies Diff ==="
38+
diff /tmp/native-deps.txt /tmp/docker-deps.txt || true
39+
40+
# Fail if there are differences
41+
if ! diff -q /tmp/native-version.txt /tmp/docker-version.txt; then
42+
echo "❌ Python versions differ!"
43+
exit 1
44+
fi
45+
46+
if ! diff -q /tmp/native-deps.txt /tmp/docker-deps.txt; then
47+
echo "⚠️ Dependencies differ (may be OK for different platforms)"
48+
fi
49+
50+
echo "✅ Environments are in sync!"

0 commit comments

Comments
 (0)