breaks-machine 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- breaks_machine-0.1.0/.claude/CLAUDE.md +204 -0
- breaks_machine-0.1.0/.claude/rules/cicd.md +409 -0
- breaks_machine-0.1.0/.claude/rules/troubleshooting.md +157 -0
- breaks_machine-0.1.0/.claude/rules/uv-guide.md +85 -0
- breaks_machine-0.1.0/.github/RELEASING.md +135 -0
- breaks_machine-0.1.0/.github/workflows/ci.yml +52 -0
- breaks_machine-0.1.0/.github/workflows/release.yml +43 -0
- breaks_machine-0.1.0/.gitignore +230 -0
- breaks_machine-0.1.0/.python-version +1 -0
- breaks_machine-0.1.0/PKG-INFO +344 -0
- breaks_machine-0.1.0/README.md +333 -0
- breaks_machine-0.1.0/pyproject.toml +46 -0
- breaks_machine-0.1.0/src/breaks_machine/__init__.py +3 -0
- breaks_machine-0.1.0/src/breaks_machine/cli.py +190 -0
- breaks_machine-0.1.0/src/breaks_machine/converter.py +95 -0
- breaks_machine-0.1.0/src/breaks_machine/detector.py +194 -0
- breaks_machine-0.1.0/src/breaks_machine/processor.py +206 -0
- breaks_machine-0.1.0/src/breaks_machine/stretcher.py +116 -0
- breaks_machine-0.1.0/tests/__init__.py +1 -0
- breaks_machine-0.1.0/tests/conftest.py +103 -0
- breaks_machine-0.1.0/tests/test_cli.py +187 -0
- breaks_machine-0.1.0/tests/test_converter.py +84 -0
- breaks_machine-0.1.0/tests/test_detector.py +116 -0
- breaks_machine-0.1.0/tests/test_processor.py +139 -0
- breaks_machine-0.1.0/tests/test_stretcher.py +105 -0
- breaks_machine-0.1.0/uv.lock +784 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# breaks-machine Development Guide
|
|
2
|
+
|
|
3
|
+
CLI tool for time-stretching drum breaks to target BPMs while preserving transient quality.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**breaks-machine** is a command-line tool designed to time-stretch drum breaks to specific BPMs using Rubberband's industry-standard algorithm. It's perfect for preparing breaks for hardware samplers, live performance, or production workflows.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:**
|
|
10
|
+
- **pyrubberband**: Python wrapper for Rubberband time-stretching
|
|
11
|
+
- **librosa**: Audio analysis and BPM detection
|
|
12
|
+
- **soundfile**: Audio I/O for WAV/FLAC files
|
|
13
|
+
- **click**: CLI framework
|
|
14
|
+
- **Python 3.13** with **uv** for package management
|
|
15
|
+
|
|
16
|
+
## Key Components
|
|
17
|
+
|
|
18
|
+
### Core Modules
|
|
19
|
+
|
|
20
|
+
- **[cli.py](../../src/breaks_machine/cli.py)** - Click-based CLI entry point
|
|
21
|
+
- Handles command-line arguments and user input
|
|
22
|
+
- Orchestrates the processing pipeline
|
|
23
|
+
- Entry point: `breaks-machine stretch`
|
|
24
|
+
|
|
25
|
+
- **[detector.py](../../src/breaks_machine/detector.py)** - BPM detection
|
|
26
|
+
- Parses BPM from filename patterns (e.g., `amen_170.wav`)
|
|
27
|
+
- Multi-strategy librosa detection with subdivision correction
|
|
28
|
+
- Priority: manual override → filename → auto-detection
|
|
29
|
+
|
|
30
|
+
- **[stretcher.py](../../src/breaks_machine/stretcher.py)** - Rubberband wrapper
|
|
31
|
+
- Time-stretching with crispness=5 (optimized for drums)
|
|
32
|
+
- Ratio calculation: `target_bpm / source_bpm`
|
|
33
|
+
- Preserves transients and minimizes phase artifacts
|
|
34
|
+
|
|
35
|
+
- **[converter.py](../../src/breaks_machine/converter.py)** - Format conversion
|
|
36
|
+
- Optional sample rate conversion
|
|
37
|
+
- Bit depth conversion (16/24-bit)
|
|
38
|
+
- Stereo to mono downmixing
|
|
39
|
+
|
|
40
|
+
- **[processor.py](../../src/breaks_machine/processor.py)** - Pipeline orchestration
|
|
41
|
+
- Single file and batch directory processing
|
|
42
|
+
- Output structure: `output/{filename}/{filename}_{bpm}bpm.{ext}`
|
|
43
|
+
- Target parsing (single, multiple, range modes)
|
|
44
|
+
|
|
45
|
+
## Development Workflow
|
|
46
|
+
|
|
47
|
+
### Testing
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Run all tests
|
|
51
|
+
uv run pytest tests
|
|
52
|
+
|
|
53
|
+
# Run with coverage
|
|
54
|
+
uv run pytest tests --cov=src/breaks_machine
|
|
55
|
+
|
|
56
|
+
# Run specific test file
|
|
57
|
+
uv run pytest tests/test_detector.py -v
|
|
58
|
+
|
|
59
|
+
# Watch mode (if using pytest-watch)
|
|
60
|
+
uv run ptw tests/
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Code Quality
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Format code
|
|
67
|
+
uvx ruff format
|
|
68
|
+
|
|
69
|
+
# Lint and fix issues
|
|
70
|
+
uvx ruff check --fix
|
|
71
|
+
|
|
72
|
+
# Check formatting without changes
|
|
73
|
+
uvx ruff format --check
|
|
74
|
+
|
|
75
|
+
# Full quality check
|
|
76
|
+
uvx ruff check && uvx ruff format --check
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Manual Testing
|
|
80
|
+
|
|
81
|
+
Test with real drum breaks in the `breaks/` directory:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Test auto-detection
|
|
85
|
+
uv run breaks-machine stretch breaks/FR_Drum_Loop_160.wav -t 140
|
|
86
|
+
|
|
87
|
+
# Test with manual BPM override
|
|
88
|
+
uv run breaks-machine stretch breaks/amen_RUDE.wav --bpm 175 -t 140
|
|
89
|
+
|
|
90
|
+
# Test batch processing
|
|
91
|
+
uv run breaks-machine stretch breaks/ --targets 90,120,140 -o test_output/
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Audio Processing Notes
|
|
95
|
+
|
|
96
|
+
### Supported Formats
|
|
97
|
+
|
|
98
|
+
- **Input**: WAV, FLAC
|
|
99
|
+
- **Output**: Same format as input (preserves original format by default)
|
|
100
|
+
|
|
101
|
+
### Rubberband Crispness Settings
|
|
102
|
+
|
|
103
|
+
The `--crispness` parameter (0-6) controls transient preservation:
|
|
104
|
+
|
|
105
|
+
- **0-2**: Smoother, less transient preservation (not ideal for drums)
|
|
106
|
+
- **3-4**: Balanced
|
|
107
|
+
- **5**: Default for breaks-machine - optimized for drums
|
|
108
|
+
- **6**: Maximum transient preservation
|
|
109
|
+
|
|
110
|
+
### BPM Detection Improvements
|
|
111
|
+
|
|
112
|
+
Recent improvements to `detector.py`:
|
|
113
|
+
|
|
114
|
+
- **Multi-strategy detection**: Tries multiple tempo priors (120, 140, 170 BPM)
|
|
115
|
+
- **Subdivision correction**: Accounts for common misdetections (half-time, 2/3 time, etc.)
|
|
116
|
+
- **Smart selection**: Prefers direct detections over derived subdivisions
|
|
117
|
+
- **Breakbeat bias**: Favors common breakbeat range (140-180 BPM)
|
|
118
|
+
|
|
119
|
+
This significantly improved accuracy on complex breaks like the Amen break (now detects 172 BPM vs previous 117 BPM).
|
|
120
|
+
|
|
121
|
+
### System Dependencies
|
|
122
|
+
|
|
123
|
+
Required for time-stretching functionality:
|
|
124
|
+
|
|
125
|
+
- **rubberband-cli**: The actual time-stretching engine
|
|
126
|
+
- **libsndfile1**: Audio file I/O library
|
|
127
|
+
- **ffmpeg**: Audio codec support
|
|
128
|
+
|
|
129
|
+
**Installation:**
|
|
130
|
+
|
|
131
|
+
**macOS:**
|
|
132
|
+
```bash
|
|
133
|
+
brew install rubberband
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Ubuntu/Debian:**
|
|
137
|
+
```bash
|
|
138
|
+
sudo apt-get install rubberband-cli libsndfile1 ffmpeg
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Windows:**
|
|
142
|
+
Download from https://breakfastquay.com/rubberband/ and add to PATH.
|
|
143
|
+
|
|
144
|
+
**Optional: Use devcontainer** - Copy `.devcontainer-template/` to `.devcontainer/` for automatic setup.
|
|
145
|
+
|
|
146
|
+
## Quick Reference
|
|
147
|
+
|
|
148
|
+
### Common Commands
|
|
149
|
+
|
|
150
|
+
- Add dependency: `uv add <package>`
|
|
151
|
+
- Add dev dependency: `uv add --dev <package>`
|
|
152
|
+
- Run CLI: `uv run breaks-machine stretch <file> [options]`
|
|
153
|
+
- Run tests: `uv run pytest tests`
|
|
154
|
+
- Format code: `uvx ruff format`
|
|
155
|
+
- Lint code: `uvx ruff check --fix`
|
|
156
|
+
|
|
157
|
+
### CLI Usage Examples
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Single target
|
|
161
|
+
breaks-machine stretch amen_170.wav --target 140
|
|
162
|
+
|
|
163
|
+
# Multiple targets
|
|
164
|
+
breaks-machine stretch break.wav --targets 90,120,140,160
|
|
165
|
+
|
|
166
|
+
# Range with step
|
|
167
|
+
breaks-machine stretch break.wav --range 80-160 --step 10
|
|
168
|
+
|
|
169
|
+
# Batch directory
|
|
170
|
+
breaks-machine stretch ./breaks/ -t 140 -o ./output/
|
|
171
|
+
|
|
172
|
+
# With format conversion
|
|
173
|
+
breaks-machine stretch break.wav -t 140 --sample-rate 44100 --mono
|
|
174
|
+
|
|
175
|
+
# Manual BPM override
|
|
176
|
+
breaks-machine stretch break.wav --bpm 175 -t 140
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Important Files
|
|
180
|
+
|
|
181
|
+
- `pyproject.toml` - Project metadata and dependency specifications
|
|
182
|
+
- `uv.lock` - Locked dependency tree (always commit!)
|
|
183
|
+
- `.python-version` - Python version pinning (3.13)
|
|
184
|
+
- `src/breaks_machine/` - Source code
|
|
185
|
+
- `tests/` - Test suite
|
|
186
|
+
- `breaks/` - Test audio files for manual testing
|
|
187
|
+
- `.devcontainer-template/` - Optional devcontainer configuration
|
|
188
|
+
- `.github/workflows/ci.yml` - CI/CD pipeline configuration
|
|
189
|
+
|
|
190
|
+
## Detailed Documentation
|
|
191
|
+
|
|
192
|
+
For more information about the development environment and tooling:
|
|
193
|
+
|
|
194
|
+
- **uv quick reference**: [.claude/rules/uv-guide.md](rules/uv-guide.md)
|
|
195
|
+
- **CI/CD pipeline**: [.claude/rules/cicd.md](rules/cicd.md)
|
|
196
|
+
- **Troubleshooting**: [.claude/rules/troubleshooting.md](rules/troubleshooting.md)
|
|
197
|
+
|
|
198
|
+
## Project Principles
|
|
199
|
+
|
|
200
|
+
- **Reproducibility**: `uv.lock` ensures identical dependencies across all environments
|
|
201
|
+
- **Standard Python package**: Installable via pip/uv without Docker
|
|
202
|
+
- **Optional devcontainer**: Available for zero-friction setup if preferred
|
|
203
|
+
- **Quality first**: Comprehensive test suite with 58 tests, all passing
|
|
204
|
+
- **Fast feedback**: Linting and formatting enforced in CI/CD
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# CI/CD Pipeline Guide
|
|
2
|
+
|
|
3
|
+
## Pipeline Overview
|
|
4
|
+
|
|
5
|
+
**File**: `.github/workflows/ci.yml`
|
|
6
|
+
|
|
7
|
+
### Pipeline Stages
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Trigger (push to any branch, pull request to main)
|
|
11
|
+
↓
|
|
12
|
+
Job: Lint, Format Check, and Test
|
|
13
|
+
↓
|
|
14
|
+
1. Checkout code (actions/checkout@v4)
|
|
15
|
+
↓
|
|
16
|
+
2. Setup Python (actions/setup-python@v5)
|
|
17
|
+
↓
|
|
18
|
+
3. Install uv (astral-sh/setup-uv@v7 with caching)
|
|
19
|
+
↓
|
|
20
|
+
4. Lint (uvx ruff check)
|
|
21
|
+
↓
|
|
22
|
+
5. Format check (uvx ruff format --check)
|
|
23
|
+
↓
|
|
24
|
+
6. Test (uv run pytest tests)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Key Characteristics
|
|
28
|
+
|
|
29
|
+
- **Runner**: `ubuntu-latest` (Ubuntu 22.04 with Python pre-installed)
|
|
30
|
+
- **Python setup**: Via `actions/setup-python@v5` reading `.python-version`
|
|
31
|
+
- **uv setup**: Via official `astral-sh/setup-uv@v7` action
|
|
32
|
+
- **Consistency**: Uses same uv commands as local development
|
|
33
|
+
- **Lock file**: Uses `uv.lock` for reproducible dependencies
|
|
34
|
+
- **Caching**: Built-in uv cache via `enable-cache: true`
|
|
35
|
+
|
|
36
|
+
## Current Configuration
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
name: CI
|
|
40
|
+
|
|
41
|
+
on:
|
|
42
|
+
push:
|
|
43
|
+
branches: ["*"]
|
|
44
|
+
pull_request:
|
|
45
|
+
branches: ["main"]
|
|
46
|
+
|
|
47
|
+
jobs:
|
|
48
|
+
lint-format-test:
|
|
49
|
+
name: Lint, Format Check, and Test
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- name: Checkout code
|
|
54
|
+
uses: actions/checkout@v4
|
|
55
|
+
|
|
56
|
+
- name: Set up Python
|
|
57
|
+
uses: actions/setup-python@v5
|
|
58
|
+
with:
|
|
59
|
+
python-version-file: ".python-version"
|
|
60
|
+
|
|
61
|
+
- name: Install uv
|
|
62
|
+
uses: astral-sh/setup-uv@v7
|
|
63
|
+
with:
|
|
64
|
+
version: "0.9.22"
|
|
65
|
+
enable-cache: true
|
|
66
|
+
|
|
67
|
+
- name: Verify uv installation
|
|
68
|
+
run: uv --version
|
|
69
|
+
|
|
70
|
+
- name: Lint with Ruff
|
|
71
|
+
run: uvx ruff check
|
|
72
|
+
|
|
73
|
+
- name: Check code formatting with Ruff
|
|
74
|
+
run: uvx ruff format --check
|
|
75
|
+
|
|
76
|
+
- name: Run tests with pytest
|
|
77
|
+
run: uv run pytest tests
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## How It Works
|
|
81
|
+
|
|
82
|
+
### Step 1: Checkout Code
|
|
83
|
+
```yaml
|
|
84
|
+
- name: Checkout code
|
|
85
|
+
uses: actions/checkout@v4
|
|
86
|
+
```
|
|
87
|
+
Clones the repository code into the runner workspace.
|
|
88
|
+
|
|
89
|
+
### Step 2: Set up Python
|
|
90
|
+
```yaml
|
|
91
|
+
- name: Set up Python
|
|
92
|
+
uses: actions/setup-python@v5
|
|
93
|
+
with:
|
|
94
|
+
python-version-file: ".python-version"
|
|
95
|
+
```
|
|
96
|
+
Installs Python 3.13 by reading the `.python-version` file. Uses GitHub's pre-cached Python versions for fast setup.
|
|
97
|
+
|
|
98
|
+
### Step 3: Install uv
|
|
99
|
+
```yaml
|
|
100
|
+
- name: Install uv
|
|
101
|
+
uses: astral-sh/setup-uv@v7
|
|
102
|
+
with:
|
|
103
|
+
version: "0.9.22"
|
|
104
|
+
enable-cache: true
|
|
105
|
+
```
|
|
106
|
+
Installs uv package manager with version pinning and automatic caching enabled.
|
|
107
|
+
|
|
108
|
+
### Step 4: Lint
|
|
109
|
+
```bash
|
|
110
|
+
uvx ruff check
|
|
111
|
+
```
|
|
112
|
+
Runs ruff linter. Fails workflow if linting errors found.
|
|
113
|
+
|
|
114
|
+
### Step 5: Format Check
|
|
115
|
+
```bash
|
|
116
|
+
uvx ruff format --check
|
|
117
|
+
```
|
|
118
|
+
Verifies code is properly formatted without modifying files.
|
|
119
|
+
|
|
120
|
+
### Step 6: Test
|
|
121
|
+
```bash
|
|
122
|
+
uv run pytest tests
|
|
123
|
+
```
|
|
124
|
+
Installs dependencies from `uv.lock` automatically and runs pytest.
|
|
125
|
+
|
|
126
|
+
## Trigger Conditions
|
|
127
|
+
|
|
128
|
+
**Push to any branch**:
|
|
129
|
+
```yaml
|
|
130
|
+
on:
|
|
131
|
+
push:
|
|
132
|
+
branches: ["*"]
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Pull requests to main**:
|
|
136
|
+
```yaml
|
|
137
|
+
on:
|
|
138
|
+
pull_request:
|
|
139
|
+
branches: ["main"]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Both triggers combined**:
|
|
143
|
+
```yaml
|
|
144
|
+
on:
|
|
145
|
+
push:
|
|
146
|
+
branches: ["*"]
|
|
147
|
+
pull_request:
|
|
148
|
+
branches: ["main"]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Common Modifications
|
|
152
|
+
|
|
153
|
+
**Run on specific branch patterns**:
|
|
154
|
+
```yaml
|
|
155
|
+
on:
|
|
156
|
+
push:
|
|
157
|
+
branches:
|
|
158
|
+
- main
|
|
159
|
+
- 'feature/**'
|
|
160
|
+
- 'release/*'
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Run only on tagged commits**:
|
|
164
|
+
```yaml
|
|
165
|
+
on:
|
|
166
|
+
push:
|
|
167
|
+
tags:
|
|
168
|
+
- 'v*'
|
|
169
|
+
|
|
170
|
+
jobs:
|
|
171
|
+
release:
|
|
172
|
+
name: Release build
|
|
173
|
+
runs-on: ubuntu-latest
|
|
174
|
+
steps:
|
|
175
|
+
- uses: actions/checkout@v4
|
|
176
|
+
- run: docker build -t myapp:${{ github.ref_name }} .
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Scheduled builds**:
|
|
180
|
+
```yaml
|
|
181
|
+
on:
|
|
182
|
+
schedule:
|
|
183
|
+
- cron: '0 0 * * *' # Daily at midnight UTC
|
|
184
|
+
push:
|
|
185
|
+
branches: ["main"]
|
|
186
|
+
|
|
187
|
+
jobs:
|
|
188
|
+
nightly-tests:
|
|
189
|
+
name: Nightly tests
|
|
190
|
+
runs-on: ubuntu-latest
|
|
191
|
+
steps:
|
|
192
|
+
- uses: actions/checkout@v4
|
|
193
|
+
- uses: actions/setup-python@v5
|
|
194
|
+
with:
|
|
195
|
+
python-version-file: ".python-version"
|
|
196
|
+
- uses: astral-sh/setup-uv@v7
|
|
197
|
+
with:
|
|
198
|
+
enable-cache: true
|
|
199
|
+
- run: uv run pytest tests
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Matrix builds** (test multiple Python versions):
|
|
203
|
+
```yaml
|
|
204
|
+
jobs:
|
|
205
|
+
test:
|
|
206
|
+
runs-on: ubuntu-latest
|
|
207
|
+
strategy:
|
|
208
|
+
matrix:
|
|
209
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
210
|
+
steps:
|
|
211
|
+
- uses: actions/checkout@v4
|
|
212
|
+
- uses: actions/setup-python@v5
|
|
213
|
+
with:
|
|
214
|
+
python-version: ${{ matrix.python-version }}
|
|
215
|
+
- uses: astral-sh/setup-uv@v7
|
|
216
|
+
with:
|
|
217
|
+
enable-cache: true
|
|
218
|
+
- run: uv run pytest tests
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Secrets and Environment Variables
|
|
222
|
+
|
|
223
|
+
### Setting Secrets
|
|
224
|
+
|
|
225
|
+
1. GitHub repo → Settings → Secrets and variables → Actions
|
|
226
|
+
2. Click "New repository secret"
|
|
227
|
+
3. Add name and value (e.g., `API_KEY`, `DATABASE_URL`, `DOCKER_PASSWORD`)
|
|
228
|
+
|
|
229
|
+
### Using Secrets
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
jobs:
|
|
233
|
+
test:
|
|
234
|
+
runs-on: ubuntu-latest
|
|
235
|
+
steps:
|
|
236
|
+
- uses: actions/checkout@v4
|
|
237
|
+
- uses: actions/setup-python@v5
|
|
238
|
+
with:
|
|
239
|
+
python-version-file: ".python-version"
|
|
240
|
+
- uses: astral-sh/setup-uv@v7
|
|
241
|
+
with:
|
|
242
|
+
enable-cache: true
|
|
243
|
+
- name: Run tests with secrets
|
|
244
|
+
env:
|
|
245
|
+
API_KEY: ${{ secrets.API_KEY }}
|
|
246
|
+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
|
247
|
+
run: uv run pytest tests
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Environment Variables
|
|
251
|
+
|
|
252
|
+
```yaml
|
|
253
|
+
jobs:
|
|
254
|
+
test:
|
|
255
|
+
runs-on: ubuntu-latest
|
|
256
|
+
env:
|
|
257
|
+
DEBUG: "true"
|
|
258
|
+
ENVIRONMENT: "ci"
|
|
259
|
+
steps:
|
|
260
|
+
- uses: actions/checkout@v4
|
|
261
|
+
- run: uv run pytest tests # DEBUG and ENVIRONMENT available
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Reusable Workflows
|
|
265
|
+
|
|
266
|
+
Create `.github/workflows/reusable-test.yml`:
|
|
267
|
+
```yaml
|
|
268
|
+
name: Reusable Test Workflow
|
|
269
|
+
|
|
270
|
+
on:
|
|
271
|
+
workflow_call:
|
|
272
|
+
inputs:
|
|
273
|
+
python-version:
|
|
274
|
+
required: false
|
|
275
|
+
type: string
|
|
276
|
+
default: "3.13"
|
|
277
|
+
|
|
278
|
+
jobs:
|
|
279
|
+
test:
|
|
280
|
+
runs-on: ubuntu-latest
|
|
281
|
+
steps:
|
|
282
|
+
- uses: actions/checkout@v4
|
|
283
|
+
- uses: actions/setup-python@v5
|
|
284
|
+
with:
|
|
285
|
+
python-version: ${{ inputs.python-version }}
|
|
286
|
+
- uses: astral-sh/setup-uv@v7
|
|
287
|
+
with:
|
|
288
|
+
enable-cache: true
|
|
289
|
+
- run: uv run pytest tests
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Use it in another workflow:
|
|
293
|
+
```yaml
|
|
294
|
+
jobs:
|
|
295
|
+
call-test:
|
|
296
|
+
uses: ./.github/workflows/reusable-test.yml
|
|
297
|
+
with:
|
|
298
|
+
python-version: "3.13"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Common CI Issues
|
|
302
|
+
|
|
303
|
+
### Tests Pass Locally But Fail in CI
|
|
304
|
+
|
|
305
|
+
**Cause**: Uncommitted `uv.lock` or local-only changes.
|
|
306
|
+
|
|
307
|
+
**Solution**:
|
|
308
|
+
```bash
|
|
309
|
+
git status # Check for uncommitted files
|
|
310
|
+
git add uv.lock
|
|
311
|
+
git commit -m "Update lock file"
|
|
312
|
+
git push
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Workflow Not Triggering
|
|
316
|
+
|
|
317
|
+
**Causes**:
|
|
318
|
+
1. Workflow file has syntax errors (check Actions tab for parsing errors)
|
|
319
|
+
2. GitHub Actions not enabled for the repository
|
|
320
|
+
3. Branch protection rules blocking workflow runs
|
|
321
|
+
|
|
322
|
+
**Solution**:
|
|
323
|
+
1. Validate YAML syntax
|
|
324
|
+
2. Settings → Actions → General → Enable "Allow all actions and reusable workflows"
|
|
325
|
+
3. Settings → Branches → Check branch protection settings
|
|
326
|
+
|
|
327
|
+
### Caching Not Working
|
|
328
|
+
|
|
329
|
+
Check workflow logs for cache hit/miss messages:
|
|
330
|
+
```
|
|
331
|
+
Run astral-sh/setup-uv@v7
|
|
332
|
+
Cache hit: ~/.cache/uv
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
If cache misses frequently, verify:
|
|
336
|
+
1. `enable-cache: true` is set in setup-uv step
|
|
337
|
+
2. `uv.lock` file is committed
|
|
338
|
+
3. Cache hasn't been manually cleared
|
|
339
|
+
|
|
340
|
+
### Debugging
|
|
341
|
+
|
|
342
|
+
Run the same commands locally:
|
|
343
|
+
```bash
|
|
344
|
+
uvx ruff check
|
|
345
|
+
uvx ruff format --check
|
|
346
|
+
uv run pytest tests
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Use GitHub Actions locally with [act](https://github.com/nektos/act):
|
|
350
|
+
```bash
|
|
351
|
+
# Install act
|
|
352
|
+
brew install act # macOS
|
|
353
|
+
# or: https://github.com/nektos/act#installation
|
|
354
|
+
|
|
355
|
+
# Run workflow locally
|
|
356
|
+
act push
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Best Practices
|
|
360
|
+
|
|
361
|
+
1. **Keep CI commands identical to local**: Use same commands in CI as local development
|
|
362
|
+
2. **Always commit `uv.lock`**: Ensures CI uses same dependency versions
|
|
363
|
+
3. **Fail fast**: Put linting before tests (faster feedback)
|
|
364
|
+
4. **Use caching**: Enable built-in caching with `enable-cache: true`
|
|
365
|
+
5. **Pin action versions**: Use `@v4` not `@latest` for reproducibility
|
|
366
|
+
6. **Protect main branch**: Require status checks to pass before merging (Settings → Branches → Branch protection rules)
|
|
367
|
+
7. **Use concurrency control**: Prevent multiple runs on same PR
|
|
368
|
+
```yaml
|
|
369
|
+
concurrency:
|
|
370
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
371
|
+
cancel-in-progress: true
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Viewing Workflow Results
|
|
375
|
+
|
|
376
|
+
1. **Actions tab**: Click "Actions" in repository navigation
|
|
377
|
+
2. **Workflow runs**: See all runs with status (success, failure, in progress)
|
|
378
|
+
3. **Logs**: Click a run → Click job name → Expand steps to see detailed logs
|
|
379
|
+
4. **Re-run failed jobs**: Click "Re-run jobs" button on failed runs
|
|
380
|
+
|
|
381
|
+
## Branch Protection
|
|
382
|
+
|
|
383
|
+
Require CI to pass before merging:
|
|
384
|
+
|
|
385
|
+
1. Settings → Branches → Add branch protection rule
|
|
386
|
+
2. Branch name pattern: `main`
|
|
387
|
+
3. Enable "Require status checks to pass before merging"
|
|
388
|
+
4. Search for and select: "Lint, Format Check, and Test"
|
|
389
|
+
5. Enable "Require branches to be up to date before merging"
|
|
390
|
+
6. Save changes
|
|
391
|
+
|
|
392
|
+
## Cost & Limits
|
|
393
|
+
|
|
394
|
+
**Free tier** (public repos):
|
|
395
|
+
- 2,000 minutes/month for private repos
|
|
396
|
+
- Unlimited minutes for public repos
|
|
397
|
+
|
|
398
|
+
**This project's usage**:
|
|
399
|
+
- ~2-3 minutes per run (first run with cache miss)
|
|
400
|
+
- ~1-2 minutes per run (subsequent runs with cache hit)
|
|
401
|
+
- Well within free tier limits
|
|
402
|
+
|
|
403
|
+
## Resources
|
|
404
|
+
|
|
405
|
+
- **GitHub Actions docs**: https://docs.github.com/en/actions
|
|
406
|
+
- **Using uv in GitHub Actions**: https://docs.astral.sh/uv/guides/integration/github/
|
|
407
|
+
- **astral-sh/setup-uv**: https://github.com/astral-sh/setup-uv
|
|
408
|
+
- **Workflow syntax**: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
|
409
|
+
- **uv documentation**: https://docs.astral.sh/uv/
|