Skip to content

Commit bad0cb9

Browse files
authored
Modernize tooling: Switch to ty and add local CI testing (#8)
* Replace mypy with ty for type checking Adopts Astral's new ty type checker to modernize the development tooling and align with the Ruff ecosystem for faster, more integrated type checking. * Switch from uv pip sync to uv sync for dependency management Modernizes dependency installation to use uv sync which reads directly from pyproject.toml and uv.lock, eliminating the need for intermediate requirements.txt files in the setup flow. This approach preserves environment markers for conditional dependencies and provides more accurate dependency resolution. Key changes: - Makefile: setup target now uses 'uv sync --all-extras' instead of pip sync - GitHub Actions: simplified dependency installation workflow - Dockerfile: uses 'uv sync --system --all-extras' with uv.lock for reproducible builds The compile-deps target remains available for cases requiring requirements.txt files. * Add local CI testing with act for GitHub Actions workflows Implements local execution of GitHub Actions workflows using act, reducing feedback time from minutes to seconds. This allows developers to test workflows locally before pushing to GitHub, saving CI/CD minutes and enabling faster iteration. Key additions: - .actrc: Configuration for act with smart defaults (catthehacker image, amd64 arch) - .github/workflows/ci-debug.yml: Fast debug workflow for iterative development - Makefile targets: - act-install: One-time setup with Homebrew or install script - act-check: Auto-installs act if missing - docker-check: Validates container runtime is running with helpful error messages - ci-list: List available workflows and jobs - ci-local: Run full test workflow locally - ci-local-docs: Run documentation build check - ci-debug: Fast iteration with customizable workflow - ci-clean: Clean up act containers and cache - Smart Docker socket detection (Colima/Docker/Podman) - README and CHANGELOG updates with usage instructions - Linux troubleshooting documentation for Docker permissions Benefits: - 5-20 second local feedback vs 2-5 minute GitHub runs - Test workflows before commit/push - Zero GitHub Actions minutes consumption - Debug workflows with full local inspection - Zero-setup experience: just run 'make ci-local' and it handles everything - Clear, actionable error messages when prerequisites missing
1 parent 7eb0cc5 commit bad0cb9

16 files changed

Lines changed: 729 additions & 537 deletions

.actrc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Runner image configuration - use maintained catthehacker images
2+
-P ubuntu-latest=catthehacker/ubuntu:act-22.04
3+
4+
# Container architecture (explicit for M1/M2 Macs)
5+
--container-architecture=linux/amd64
6+
7+
# Container daemon socket (will be overridden by Makefile)
8+
--container-daemon-socket=-
9+
10+
# Verbose output by default
11+
--verbose

.github/workflows/ci-debug.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: CI Debug (Local Testing)
2+
3+
# This workflow is designed for fast local iteration with act
4+
# It runs a subset of checks for quick feedback
5+
#
6+
# Usage: make ci-debug
7+
#
8+
# Customize the steps below for the specific tests you're working on
9+
10+
on:
11+
workflow_dispatch:
12+
push:
13+
branches:
14+
- '**'
15+
16+
jobs:
17+
quick-check:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v3
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v4
24+
with:
25+
python-version: '3.12'
26+
27+
- name: Install uv
28+
run: pip install uv
29+
30+
- name: Install dependencies
31+
run: |
32+
uv venv
33+
uv sync --all-extras
34+
35+
# Customize these steps based on what you're debugging
36+
- name: Run Linter
37+
run: uv run -m ruff check src
38+
39+
- name: Run Type Checker
40+
run: uv run ty check src
41+
42+
# Add more steps as needed for debugging
43+
# - name: Run Specific Test
44+
# run: uv run -m pytest tests/test_specific.py -v

.github/workflows/tests.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@ jobs:
1919

2020
- name: Install dependencies
2121
run: |
22-
uv pip compile pyproject.toml -o requirements.txt
23-
uv pip compile pyproject.toml --extra dev -o requirements-dev.txt
2422
uv venv
25-
uv pip sync requirements.txt requirements-dev.txt
23+
uv sync --all-extras
2624
2725
- name: Determine module name
2826
id: module
@@ -43,8 +41,8 @@ jobs:
4341
- name: Run Tests
4442
run: uv run -m pytest tests --cov=${{ steps.module.outputs.name }} --cov-report=term-missing --cov-report=xml
4543

46-
- name: Run MyPy
47-
run: uv run -m mypy ${{ steps.module.outputs.name }}
44+
- name: Run ty
45+
run: uv run ty check ${{ steps.module.outputs.name }}
4846

4947
- name: Upload coverage to Codecov
5048
uses: codecov/codecov-action@v3

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,11 @@ CLAUDE.md
2020
# Documentation build artifacts
2121
site/
2222
.cache/
23+
24+
# AI/planning artifacts
25+
.ai/**
26+
27+
# act (GitHub Actions local runner)
28+
.actrc.local
29+
.secrets
30+
.env.act

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ repos:
1919
language: system
2020
always_run: true
2121
pass_filenames: false
22-
- id: mypy
23-
name: Run MyPy
24-
entry: make mypy
22+
- id: ty
23+
name: Run ty
24+
entry: make ty
2525
language: system
2626
always_run: true
2727
pass_filenames: false

CHANGELOG.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Integration with uv for dependency management
1212
- Modern Python development tools:
1313
- ruff for linting and formatting
14-
- mypy for type checking
14+
- ty for type checking
1515
- pytest with coverage reporting
1616
- GitHub Actions workflow for automated testing
1717
- Docker development environment improvements
18+
- Local CI testing with act for running GitHub Actions workflows locally
19+
- Fast debug workflow for iterative development
20+
- Make targets: `act-install`, `ci-list`, `ci-local`, `ci-local-docs`, `ci-debug`, `ci-clean`
1821

1922
### Changed
2023
- Switched from pip/venv to uv for environment management
21-
- Updated example code to pass mypy type checking
24+
- Updated example code to pass ty type checking
2225
- Modernized project structure and development workflow
2326
- Updated Python version to 3.12
2427

@@ -27,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2730
- Outdated Docker configuration elements
2831

2932
### Fixed
30-
- Type hints in example code to pass mypy checks
33+
- Type hints in example code to pass ty checks
3134
- Docker environment management
3235
- Development workflow and quality checks
3336

Makefile

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: compile-deps setup clean-pyc clean-test clean-venv clean test mypy lint format check clean-example docs-install docs-build docs-serve docs-check docs-clean dev-env refresh-containers rebuild-images build-image push-image
1+
.PHONY: compile-deps setup clean-pyc clean-test clean-venv clean test ty lint format check clean-example docs-install docs-build docs-serve docs-check docs-clean dev-env refresh-containers rebuild-images build-image push-image
22

33
# Module name - will be updated by init script
44
MODULE_NAME := src
@@ -14,9 +14,8 @@ PYTHON_VERSION ?= 3.12
1414
ensure-uv: # Install uv if not present
1515
@which uv > /dev/null || (curl -LsSf https://astral.sh/uv/install.sh | sh)
1616

17-
setup: ensure-uv compile-deps ensure-scripts # Install dependencies
18-
UV_PYTHON_VERSION=$(PYTHON_VERSION) uv venv
19-
UV_PYTHON_VERSION=$(PYTHON_VERSION) uv pip sync requirements.txt requirements-dev.txt
17+
setup: ensure-uv ensure-scripts # Install dependencies
18+
UV_PYTHON_VERSION=$(PYTHON_VERSION) uv sync --all-extras
2019
$(MAKE) install-hooks
2120

2221
install-hooks: # Install pre-commit hooks if in a git repo with hooks configured
@@ -51,16 +50,127 @@ clean: clean-pyc clean-test clean-venv
5150
test: setup # Run pytest with coverage
5251
uv run -m pytest tests --cov=$(MODULE_NAME) --cov-report=term-missing
5352

54-
mypy: setup # Run type checking
55-
uv run -m mypy $(MODULE_NAME)
53+
ty: setup # Run type checking
54+
uv run ty check $(MODULE_NAME)
5655

5756
lint: setup # Run ruff linter with auto-fix
5857
uv run -m ruff check --fix $(MODULE_NAME)
5958

6059
format: setup # Run ruff formatter
6160
uv run -m ruff format $(MODULE_NAME)
6261

63-
check: setup lint format test mypy # Run all quality checks
62+
check: setup lint format test ty # Run all quality checks
63+
64+
# Local CI Testing with act
65+
###########################
66+
67+
# Detect docker socket (Colima, Docker, or Podman)
68+
define docker_socket
69+
$(shell \
70+
if [ -S $$HOME/.colima/default/docker.sock ]; then \
71+
echo "$$HOME/.colima/default/docker.sock"; \
72+
elif [ -S /var/run/docker.sock ]; then \
73+
echo "/var/run/docker.sock"; \
74+
elif [ -S $$HOME/.docker/run/docker.sock ]; then \
75+
echo "$$HOME/.docker/run/docker.sock"; \
76+
elif [ -S /run/user/$$(id -u)/podman/podman.sock ]; then \
77+
echo "/run/user/$$(id -u)/podman/podman.sock"; \
78+
else \
79+
echo "/var/run/docker.sock"; \
80+
fi \
81+
)
82+
endef
83+
84+
DOCKER_SOCKET := $(docker_socket)
85+
ACT_IMAGE := catthehacker/ubuntu:act-22.04
86+
ACT_ARCH := linux/amd64
87+
88+
act-check: # Check if act is installed, install if missing
89+
@if ! which act > /dev/null 2>&1; then \
90+
echo "⚠️ act is not installed. Installing automatically..."; \
91+
$(MAKE) act-install; \
92+
fi
93+
94+
docker-check: # Check if Docker/Podman/Colima is running
95+
@if ! DOCKER_HOST="unix://$(DOCKER_SOCKET)" docker ps >/dev/null 2>&1; then \
96+
echo "❌ Cannot connect to Docker daemon"; \
97+
echo ""; \
98+
echo "Please start your container runtime first:"; \
99+
echo " • Docker Desktop: Open Docker Desktop app"; \
100+
echo " • Colima: run 'colima start'"; \
101+
echo " • Podman: run 'podman machine start'"; \
102+
echo " • Docker (Linux): run 'sudo systemctl start docker'"; \
103+
echo ""; \
104+
echo "Attempted socket: $(DOCKER_SOCKET)"; \
105+
exit 1; \
106+
fi
107+
@echo "✓ Docker is running (socket: $(DOCKER_SOCKET))"
108+
109+
act-install: ## Install act for local CI testing
110+
@echo "Installing act (GitHub Actions local runner)..."
111+
@if which act > /dev/null 2>&1; then \
112+
echo "✅ act is already installed: $$(which act)"; \
113+
act --version; \
114+
elif which brew > /dev/null 2>&1; then \
115+
echo "📦 Installing act via Homebrew..."; \
116+
brew install act; \
117+
else \
118+
echo "📦 Installing act via install script..."; \
119+
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash; \
120+
fi
121+
@echo ""
122+
@echo "✅ act installed successfully!"
123+
@echo "💡 Run 'make ci-list' to see available workflows"
124+
@echo "💡 Run 'make ci-local' to run CI checks locally"
125+
126+
ci-list: act-check docker-check ## List available GitHub Actions workflows and jobs
127+
@echo "📋 Available workflows and jobs:"
128+
@DOCKER_HOST="unix://$(DOCKER_SOCKET)" act -l
129+
130+
ci-local: act-check docker-check ## Run CI checks locally (tests workflow)
131+
@echo "🔬 Running local CI checks..."
132+
@echo "Using Docker socket: $(DOCKER_SOCKET)"
133+
@echo "Container architecture: $(ACT_ARCH)"
134+
@echo "Image: $(ACT_IMAGE)"
135+
@echo ""
136+
@JOB=$${JOB:-checks}; \
137+
DOCKER_HOST="unix://$(DOCKER_SOCKET)" act pull_request \
138+
-W .github/workflows/tests.yml \
139+
-j $$JOB \
140+
--container-daemon-socket - \
141+
--container-architecture $(ACT_ARCH) \
142+
-P ubuntu-latest=$(ACT_IMAGE)
143+
@echo ""
144+
@echo "✅ Local CI checks complete!"
145+
146+
ci-local-docs: act-check docker-check ## Run documentation build check locally
147+
@echo "📚 Running local documentation build check..."
148+
@DOCKER_HOST="unix://$(DOCKER_SOCKET)" act pull_request \
149+
-W .github/workflows/docs.yml \
150+
-j build-check \
151+
--container-daemon-socket - \
152+
--container-architecture $(ACT_ARCH) \
153+
-P ubuntu-latest=$(ACT_IMAGE)
154+
@echo ""
155+
@echo "✅ Documentation build check complete!"
156+
157+
ci-debug: act-check docker-check ## Fast local test debugging (uses ci-debug.yml workflow)
158+
@echo "🔧 Running CI debug workflow for fast iteration"
159+
@echo "💡 Edit .github/workflows/ci-debug.yml to customize which tests run"
160+
@echo ""
161+
@DOCKER_HOST="unix://$(DOCKER_SOCKET)" act workflow_dispatch \
162+
-W .github/workflows/ci-debug.yml \
163+
--container-daemon-socket - \
164+
--container-architecture $(ACT_ARCH) \
165+
-P ubuntu-latest=$(ACT_IMAGE)
166+
@echo ""
167+
@echo "✅ Debug workflow complete!"
168+
169+
ci-clean: ## Clean up act cache and containers
170+
@echo "🧹 Cleaning up act cache and containers..."
171+
@-docker ps -a | grep "act-" | awk '{print $$1}' | xargs docker rm -f 2>/dev/null || true
172+
@-docker images | grep "act-" | awk '{print $$3}' | xargs docker rmi -f 2>/dev/null || true
173+
@echo "✅ Cleanup complete!"
64174

65175
# Documentation
66176
###############

README.md

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A modern Python project template with best practices for development and collabo
77
## Features
88
- 🚀 Fast dependency management with [uv](https://github.com/astral-sh/uv)
99
- ✨ Code formatting with [ruff](https://github.com/astral-sh/ruff)
10-
- 🔍 Type checking with [mypy](https://github.com/python/mypy)
10+
- 🔍 Type checking with [ty](https://astral.sh/blog/ty)
1111
- 🧪 Testing with [pytest](https://github.com/pytest-dev/pytest)
1212
- 🐳 Docker support for development and deployment
1313
- 👷 CI/CD with GitHub Actions
@@ -48,7 +48,7 @@ This will:
4848
- Configure pre-commit hooks (optional, enabled by default)
4949

5050
Pre-commit hooks will automatically run these checks before each commit:
51-
- Type checking (mypy)
51+
- Type checking (ty)
5252
- Linting (ruff)
5353
- Formatting (ruff)
5454
- Tests (pytest)
@@ -69,13 +69,73 @@ make clean-example
6969

7070
### Quality Checks
7171
```bash
72-
make check # Run all checks (test, mypy, lint, format)
72+
make check # Run all checks (test, ty, lint, format)
7373
make test # Run tests with coverage
74-
make mypy # Run type checking
74+
make ty # Run type checking
7575
make lint # Run linter
7676
make format # Run code formatter
7777
```
7878

79+
### Local CI Testing
80+
81+
Run GitHub Actions workflows locally before pushing using [act](https://github.com/nektos/act):
82+
83+
```bash
84+
# Run full test suite locally (auto-installs act if needed)
85+
make ci-local
86+
87+
# List available workflows
88+
make ci-list
89+
90+
# Run specific job
91+
JOB=checks make ci-local
92+
93+
# Run documentation build check
94+
make ci-local-docs
95+
96+
# Fast debugging (customize .github/workflows/ci-debug.yml)
97+
make ci-debug
98+
99+
# Clean up act containers
100+
make ci-clean
101+
```
102+
103+
**Note:** The first run will automatically install `act` if it's not present.
104+
105+
**Benefits:**
106+
- 5-20 second feedback vs. 2-5 minutes on GitHub
107+
- Test before commit/push
108+
- No GitHub Actions minutes consumed
109+
- Debug workflows locally
110+
111+
**Troubleshooting:**
112+
113+
*Linux: Docker permissions*
114+
```bash
115+
# Add your user to the docker group
116+
sudo usermod -aG docker $USER
117+
118+
# Log out and back in for changes to take effect
119+
# Or run: newgrp docker
120+
121+
# Verify it works
122+
docker ps
123+
```
124+
125+
*macOS: Colima disk lock errors*
126+
```bash
127+
# If you get "disk in use" or similar errors:
128+
colima stop
129+
colima delete
130+
colima start
131+
```
132+
133+
*General: Stale act containers*
134+
```bash
135+
# Clean up old containers and images
136+
make ci-clean
137+
```
138+
79139
### Example Code
80140
The repository includes a simple example showing:
81141
- Type hints

docker/Dockerfile

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,12 @@ RUN apt-get update && apt-get install -y \
1717
WORKDIR /workspace
1818

1919
# Copy essential files for dependency resolution to temp location
20-
COPY pyproject.toml /tmp/
20+
COPY pyproject.toml uv.lock /tmp/
2121
COPY README.md /tmp/
2222

2323
RUN pip install -U pip uv \
2424
&& cd /tmp \
25-
&& uv pip compile pyproject.toml -o requirements.txt \
26-
&& uv pip compile pyproject.toml --extra dev -o requirements-dev.txt \
27-
&& uv pip sync --system requirements.txt requirements-dev.txt \
28-
&& rm -rf /tmp/pyproject.toml /tmp/README.md
25+
&& uv sync --system --all-extras \
26+
&& rm -rf /tmp/pyproject.toml /tmp/uv.lock /tmp/README.md
2927

3028
CMD ["/bin/bash"]

0 commit comments

Comments
 (0)