```
**Common CI failure patterns** π‘
.font-sm.mb-xs[
| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| Python 3.9 fails, 3.11 passes | PEP 604 `X \bar Y` syntax | `from __future__ import annotations` |
| Windows fails, Linux passes | Path separator, EOL, float precision | Use `os.path.join`, `pytest.approx` |
| Timeout | Stress test too aggressive | Reduce problem size / iterations |
| ImportError | Missing dep or circular import | Check `install_requires` in `setup.cfg` |
]
---
### βοΈ Bulk tag + release creation
```bash
# Create tags for all repos
for repo in $(gh repo list luk036 --limit 50 --json name -q '.[].name'); do
gh release create 0.1 --repo "luk036/$repo" \
--title "v0.1" --notes "Initial release"
done
```
.center[.mermaid[
graph LR
A["git tag 0.1"] --> B["git push origin 0.1"]
B --> C["gh release create 0.1"]
C --> D["gh release edit 0.1 --notes ..."]
D --> E["Release live on GitHub!"]
style A fill:#e3f2fd,stroke:#1565c0
style B fill:#e8f5e9,stroke:#2e7d32
style C fill:#fff9c4,stroke:#f9a825
style D fill:#fce4ec,stroke:#c62828
style E fill:#e8f5e9,stroke:#2e7d32
]]
**20 releases in ~60 seconds** β‘
- 7 new releases (initial)
- 13 version bumps (0.1 β 0.6)
---
class: nord-light, middle, center
## 3οΈβ£ CI Diagnostics
---
### π CI Matrix Strategy
```yaml
# python-package.yml β test multiple versions
strategy:
fail-fast: false # π Don't cancel all on first failure!
matrix:
python-version: ["3.9", "3.11"]
os: ["ubuntu-latest", "windows-latest"]
```
.pull-left[
**`fail-fast: false`** β critical for debugging
- See ALL failures, not just the first
- Python 3.9 fails β 3.11 fails
- Windows fails β Linux fails
]
.pull-right[
**Minimal matrix** for PyPI publishing:
- 3.9 = oldest supported
- 3.12/3.13 = latest features
- Ubuntu + Windows = portability
- Skip macOS unless you use macOS-only features
]
---
class: nord-light, middle, center
## 4οΈβ£ CI Optimization
---
### π¦ Dependency Management: PyPI vs git+https
**The problem**: 4 repos installed deps via `pip install git+https://github.com/luk036/...`
```yaml
# β Slow: clones from GitHub every time
pip install git+https://github.com/luk036/mywheel.git
# β
Fast: pulls from PyPI cache
pip install mywheel>=0.3
```
**But**: git+https gives you the *latest* code. PyPI may be outdated.
**Solution**: Declare deps in `setup.cfg`:
```ini
install_requires =
mywheel>=0.3
digraphx>=0.3
```
Then `pip install -e .` pulls from PyPI automatically.
For CI that needs HEAD: keep `git+https` BUT pin to specific branch:
```bash
pip install git+https://github.com/luk036/mywheel.git@main
```
---
### π§ Workflow Hygiene
.pull-left[
**Remove redundant installs** π§Ή
.font-sm.mb-xs[
```yaml
# Before: 4 slow pip lines
pip install -r requirements.txt
pip install git+https://.../ellalgo.git
pip install git+https://.../mywheel.git
pip install git+https://.../digraphx.git
pip install -e .
# After: 1 fast pip line
pip install -e . # pulls deps from setup.cfg
```
]
]
.pull-right[
**Use `actions/cache`** πΎ
.font-sm.mb-xs[
```yaml
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
```
]
Saves 30-60s per run on dependency installs.
**Upgrade action versions** π
- `checkout@v2` β `checkout@v4`
- `setup-python@v2` β `setup-python@v5`
- Node.js 20 β Node.js 24 (default)
]
---
class: nord-light, middle, center
## 5οΈβ£ Release Engineering
---
### π·οΈ Tag β Release β Notes Pipeline
```bash
# Step 1: Create tag
git tag 0.6
git push origin 0.6
# Step 2: Create GitHub Release
gh release create 0.6 --repo luk036/physdes-py \
--title "v0.6" --notes "Release v0.6"
# Step 3: Edit with proper changelog
gh release edit 0.6 --repo luk036/physdes-py \
--notes "$(cat CHANGELOG.md)"
```
.center[.mermaid[
graph LR
A["git tag"] --> B["git push --tags"]
B --> C["gh release create"]
C --> D["gh release edit"]
D --> E["β¨ Published!"]
]]
---
### π Changelog Curation
Extract meaningful changes from git log:
```bash
# Get commit range between releases
git log --oneline --no-merges 0.5..0.6
# Categorize changes
git shortlog --no-merges 0.5..0.6
```
**Categorization template**:
```markdown
## v0.6 β What's New
### π New Features
### π Documentation
### π§ͺ Testing & Quality
### π§ CI & Infrastructure
### π Bug Fixes
### π§Ή Chores
```
**Full Changelog**: `https://github.com/luk036/physdes-py/compare/0.5...0.6`
---
### π‘ Release Notes Best Practices
.pull-left[
**Do** β
- Group by category (features, fixes, etc.)
- Link to compare view
- Mention breaking changes prominently
- Credit contributors
- Keep a changelog file (`CHANGELOG.md`)
]
.pull-right[
**Don't** β
- List every single commit (183 commits!)
- Use vague descriptions
- Forget to update version in setup.cfg
- Skip deprecation warnings
- Write "minor bug fixes"
]
**Example structure**:
```markdown
### π New Features
- **3D Global Router** with layer assignment
- **DME Algorithm** for clock tree synthesis
- Steiner forest for multi-net routing
### π Bug Fixes
- Fixed RST title underlines
- Fixed flake8 syntax errors in build scripts
```
---
class: nord-light, middle, center
## 6οΈβ£ Coverage-Driven Development
---
### π The 80% Rule
.center[.mermaid[
graph LR
A["Codecov badge"] --> B{"Coverage >e; 80%?"}
B -->|"Yes β
"| C["Ship it"]
B -->|"No β"| D["Add targeted tests"]
D --> E["Run pytest --cov"]
E --> B
style A fill:#e3f2fd,stroke:#1565c0
style B fill:#fff9c4,stroke:#f9a825
style C fill:#e8f5e9,stroke:#2e7d32
style D fill:#fce4ec,stroke:#c62828
]]
Check coverage at a glance:
```bash
https://img.shields.io/codecov/c/github/luk036/{repo}.svg
# β "coverage: 72%" β below 80% β add tests
# β "coverage: 93%" β above 80% β good
```
**In this session**: 3 of 16 repos with Codecov were below 80%:
- `digraphx` (72%), `netlistx` (78%), `sphere_n` (53%)
---
### β οΈ Why 100% Coverage is a Bad Idea
.pull-left[
**The Law of Diminishing Returns** π
```text
80% β catches most bugs
90% β diminishing returns
95% β test maintenance > value
100% β actively harmful
```
**100% coverage creates perverse incentives**:
- Tests for trivial getters/setters
- Testing generated or boilerplate code
- Mocking everything to hit impossible paths
- Brittle tests that break on refactoring
]
.pull-right[
**Better approach** β Strategic Coverage π―
.font-sm.mb-xs[
| Focus | Why |
|-------|-----|
| Core algorithms | Highest impact |
| Edge cases | Where bugs hide |
| Error paths | Untested = broken |
| Public API | Contract verification |
| I/O boundaries | Integration risk |
]
**80% guideline is a floor, not a ceiling.**
**95% is the practical maximum.**
**The last 5% costs more than it's worth.** π
]
**Remember**: Coverage measures *what was executed*, not *what was verified*.
A test that asserts nothing still counts as "covered."
---
### π¬ Full Session Recap
.center[.mermaid[
graph TD
subgraph "What we did in ~3 hours"
A["Check-in 20 repos"] --> B["Merge devβmain (20 repos)"]
B --> C["Tag + Release (20 repos)"]
C --> D["Release notes (12 repos)"]
D --> E["Check Codecov (3 below 80%)"]
E --> F["Add targeted tests"]
F --> G["Create presentation"]
end
style A fill:#e3f2fd,stroke:#1565c0
style G fill:#fff9c4,stroke:#f9a825
]]
**Key numbers**:
- π·οΈ 20 repos tagged and released
- π 12 detailed changelogs written
- π 3 repos with coverage below 80% identified
---
count: false
class: nord-dark, middle, center
## π Thank You
### Advanced Git + gh + CI
**Slides**: [`luk036.github.io/idea/git-gh-ci-advanced-remark`](https://luk036.github.io/idea/git-gh-ci-advanced-remark.html)
**Predecessor**: [`git-gh-ci-remark`](https://luk036.github.io/idea/git-gh-ci-remark.html) β The Basics
**Key takeaway**: *CI is not a gate β it's your debugging partner.*
@luk036 π¨βπ» Β· 2026 π