Skip to content

Commit cc0e9a1

Browse files
committed
ci: expand to multi-OS + multi-Python matrix, add mypy + gitleaks (closes #13)
The previous shape was a single ubuntu-latest / Python 3.12 unittest job. Expanded to match the broader gate quality the team adopted on the-claw: - unittest: 3 OSes × 3 Pythons = 9 cells (3.11 / 3.12 / 3.13 across ubuntu-latest, macos-latest, windows-latest). Catches Python version drift and the rare path / line-ending issue single-OS hides. fail-fast false so cells run independently. - typecheck: mypy on Python 3.12. Codebase already has 70+ typed functions across 30 .py files, so mypy actually does work. Lenient config (--ignore-missing-imports, --no-strict-optional) + continue-on-error step until the surface is clean. - secret-scan: gitleaks 8.21.2 with checksum verification (mirrors the-claw's setup verbatim). No project-specific .gitleaks.toml; uses defaults for standard credential patterns. Concurrency block added so a new push to the same ref cancels the in-flight run, reducing CI minutes. Action SHAs unchanged from the previous workflow (already pinned).
1 parent 7177d9d commit cc0e9a1

1 file changed

Lines changed: 100 additions & 7 deletions

File tree

.github/workflows/tests.yml

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,23 @@ on:
55
branches: [master]
66
pull_request:
77

8+
concurrency:
9+
group: ${{ github.workflow }}-${{ github.ref }}
10+
cancel-in-progress: true
11+
812
jobs:
13+
# ── Unit tests: matrix across OS and Python version ───────────────────────
14+
# Closes #13. The unittest suite is the merge gate. Multi-OS catches the
15+
# rare path / line-ending issue that a single-OS run hides; multi-Python
16+
# catches API drift across LTS / current / latest interpreters.
917
unittest:
10-
name: Unit tests
11-
runs-on: ubuntu-latest
12-
18+
name: Unit tests (${{ matrix.os }} / Python ${{ matrix.python-version }})
19+
runs-on: ${{ matrix.os }}
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
os: [ubuntu-latest, macos-latest, windows-latest]
24+
python-version: ["3.11", "3.12", "3.13"]
1325
steps:
1426
# Pinned to immutable commit SHAs (not @v4 / @v5) so a compromised tag
1527
# cannot silently swap the underlying action code on this CI runner.
@@ -20,15 +32,96 @@ jobs:
2032
- name: Set up Python
2133
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
2234
with:
23-
python-version: '3.12'
35+
python-version: ${{ matrix.python-version }}
2436

2537
- name: Install runtime + test dependencies
26-
# Only what the tests actually exercise. `pywebview` from requirements.txt
27-
# is the desktop-launcher dep and pulls GTK / Qt system packages on Linux
28-
# — out of scope for the unittest suite, so it's deliberately omitted here.
38+
# Only what the tests actually exercise. `pywebview` from
39+
# requirements.txt is the desktop-launcher dep and pulls GTK / Qt
40+
# system packages on Linux — out of scope for the unittest suite.
2941
run: |
3042
python -m pip install --upgrade pip
3143
python -m pip install 'flask>=3.0' 'fpdf2>=2.7'
3244
3345
- name: Run unittest suite
3446
run: python -m unittest discover tests -v
47+
48+
# ── Typecheck: mypy ───────────────────────────────────────────────────────
49+
# Codebase already has type hints across most of the surface (~70+ typed
50+
# functions). Mypy runs in lenient mode (--ignore-missing-imports for
51+
# untyped third-party deps; no strict-optional) so the gate isn't a wall
52+
# of false positives on first run. continue-on-error keeps findings as
53+
# warnings during the surface-cleanup phase; flip to required by removing
54+
# continue-on-error once the surface is clean.
55+
typecheck:
56+
name: Typecheck (mypy)
57+
runs-on: ubuntu-latest
58+
steps:
59+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
60+
61+
- name: Set up Python
62+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
63+
with:
64+
python-version: "3.12"
65+
66+
- name: Install runtime deps + mypy
67+
run: |
68+
python -m pip install --upgrade pip
69+
python -m pip install 'flask>=3.0' 'fpdf2>=2.7' 'mypy>=1.10'
70+
71+
- name: Run mypy
72+
continue-on-error: true
73+
run: mypy --ignore-missing-imports --no-strict-optional --pretty .
74+
75+
# ── Secret scan: gitleaks ─────────────────────────────────────────────────
76+
# Catches accidentally committed credentials. Runs over full git history
77+
# (fetch-depth: 0). No project-specific .gitleaks.toml — defaults cover
78+
# standard credential patterns (API keys, AWS, GitHub tokens, etc.).
79+
secret-scan:
80+
name: Secret scan (gitleaks)
81+
runs-on: ubuntu-latest
82+
steps:
83+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
84+
with:
85+
fetch-depth: 0
86+
87+
- name: Install gitleaks
88+
run: |
89+
GITLEAKS_VERSION=8.21.2
90+
base_url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}"
91+
tarball="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
92+
checksums="gitleaks_${GITLEAKS_VERSION}_checksums.txt"
93+
94+
# Download tarball and checksums file to temp; retries prevent
95+
# transient 5xx failures.
96+
curl --fail --location --silent --show-error \
97+
--retry 5 --retry-delay 2 --retry-all-errors \
98+
-o "/tmp/${tarball}" "${base_url}/${tarball}"
99+
curl --fail --location --silent --show-error \
100+
--retry 5 --retry-delay 2 --retry-all-errors \
101+
-o "/tmp/${checksums}" "${base_url}/${checksums}"
102+
103+
# Verify SHA-256 before extraction; fail and clean up on mismatch.
104+
expected=$(grep " ${tarball}$" "/tmp/${checksums}" | awk '{print $1}')
105+
if [ -z "${expected}" ]; then
106+
echo "::error::No checksum entry found for ${tarball}" >&2
107+
rm -f "/tmp/${tarball}" "/tmp/${checksums}"
108+
exit 1
109+
fi
110+
actual=$(sha256sum "/tmp/${tarball}" | awk '{print $1}')
111+
if [ "${expected}" != "${actual}" ]; then
112+
echo "::error::SHA-256 mismatch for ${tarball}: expected ${expected}, got ${actual}" >&2
113+
rm -f "/tmp/${tarball}" "/tmp/${checksums}"
114+
exit 1
115+
fi
116+
117+
tar -xz -f "/tmp/${tarball}" gitleaks
118+
sudo mv gitleaks /usr/local/bin/gitleaks
119+
rm -f "/tmp/${tarball}" "/tmp/${checksums}"
120+
121+
- name: Run gitleaks
122+
run: |
123+
gitleaks detect \
124+
--source . \
125+
--verbose \
126+
--redact \
127+
--exit-code 1

0 commit comments

Comments
 (0)