-
Notifications
You must be signed in to change notification settings - Fork 1
142 lines (124 loc) · 6.05 KB
/
tests.yml
File metadata and controls
142 lines (124 loc) · 6.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
name: Tests
on:
push:
branches: [master]
pull_request:
# Least-privilege GITHUB_TOKEN scope. None of these jobs need write access
# (no commit, no PR comment, no release publish) — read-only is enough.
# A compromised action in any matrix cell can't write back to the repo.
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── Unit tests: matrix across OS and Python version ───────────────────────
# Closes #13. The unittest suite is the merge gate. Multi-OS catches the
# rare path / line-ending issue that a single-OS run hides; multi-Python
# catches API drift across LTS / current / latest interpreters.
unittest:
name: Unit tests (${{ matrix.os }} / Python ${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.11", "3.12", "3.13"]
steps:
# Pinned to immutable commit SHAs (not @v4 / @v5) so a compromised tag
# cannot silently swap the underlying action code on this CI runner.
# When bumping, verify the new SHA via:
# gh api repos/actions/<name>/git/ref/tags/<vN> --jq '.object.sha'
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python-version }}
- name: Install runtime + test dependencies
# Only what the tests actually exercise. `pywebview` from
# requirements.txt is the desktop-launcher dep and pulls GTK / Qt
# system packages on Linux — out of scope for the unittest suite.
run: |
python -m pip install --upgrade pip
python -m pip install 'flask>=3.0' 'fpdf2>=2.7' 'pytest>=8'
- name: Run unittest suite
run: python -m unittest discover tests -v
- name: Run pytest integration suite
# Pytest fixtures (tests/conftest.py) build a temp workspaceStorage
# and exercise the Flask routes via app.test_client(). Scoped to the
# new endpoint file because `pytest tests/` would also re-collect the
# 178 unittest.TestCase subclasses already run in the step above —
# ~2× the CI minutes for zero extra signal.
run: python -m pytest tests/test_api_endpoints.py -v --tb=short
# ── Typecheck: mypy ───────────────────────────────────────────────────────
# Codebase already has type hints across most of the surface (~70+ typed
# functions). Mypy runs in lenient mode (--ignore-missing-imports for
# untyped third-party deps; no strict-optional) so the gate isn't a wall
# of false positives. The transitional `continue-on-error: true` was
# removed in #29 once mypy reached zero errors on this repo — type
# failures now block merges.
typecheck:
name: Typecheck (mypy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: Install runtime deps + mypy
run: |
python -m pip install --upgrade pip
python -m pip install 'flask>=3.0' 'fpdf2>=2.7' 'mypy>=1.10'
- name: Run mypy
# No `continue-on-error` — mypy now exits zero on this repo (closes #29),
# so type errors must fail the job from here on.
run: mypy --ignore-missing-imports --no-strict-optional --pretty .
# ── Secret scan: gitleaks ─────────────────────────────────────────────────
# Catches accidentally committed credentials. Runs over full git history
# (fetch-depth: 0). No project-specific .gitleaks.toml — defaults cover
# standard credential patterns (API keys, AWS, GitHub tokens, etc.).
secret-scan:
name: Secret scan (gitleaks)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- name: Install gitleaks
run: |
GITLEAKS_VERSION=8.21.2
base_url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}"
tarball="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
checksums="gitleaks_${GITLEAKS_VERSION}_checksums.txt"
# Download tarball and checksums file to temp; retries prevent
# transient 5xx failures.
curl --fail --location --silent --show-error \
--retry 5 --retry-delay 2 --retry-all-errors \
-o "/tmp/${tarball}" "${base_url}/${tarball}"
curl --fail --location --silent --show-error \
--retry 5 --retry-delay 2 --retry-all-errors \
-o "/tmp/${checksums}" "${base_url}/${checksums}"
# Verify SHA-256 before extraction; fail and clean up on mismatch.
expected=$(grep " ${tarball}$" "/tmp/${checksums}" | awk '{print $1}')
if [ -z "${expected}" ]; then
echo "::error::No checksum entry found for ${tarball}" >&2
rm -f "/tmp/${tarball}" "/tmp/${checksums}"
exit 1
fi
actual=$(sha256sum "/tmp/${tarball}" | awk '{print $1}')
if [ "${expected}" != "${actual}" ]; then
echo "::error::SHA-256 mismatch for ${tarball}: expected ${expected}, got ${actual}" >&2
rm -f "/tmp/${tarball}" "/tmp/${checksums}"
exit 1
fi
tar -xz -f "/tmp/${tarball}" gitleaks
sudo mv gitleaks /usr/local/bin/gitleaks
rm -f "/tmp/${tarball}" "/tmp/${checksums}"
- name: Run gitleaks
run: |
gitleaks detect \
--source . \
--verbose \
--redact \
--exit-code 1