Skip to content

fix: disable debug=True / Werkzeug debugger exposure (closes #9) (#20) #6

fix: disable debug=True / Werkzeug debugger exposure (closes #9) (#20)

fix: disable debug=True / Werkzeug debugger exposure (closes #9) (#20) #6

Workflow file for this run

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'
- name: Run unittest suite
run: python -m unittest discover tests -v
# ── 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 on first run. continue-on-error keeps findings as
# warnings during the surface-cleanup phase; flip to required by removing
# continue-on-error once the surface is clean.
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
# Transitional only (maintainer consensus): keeps CI green until `mypy` exits
# zero on this repo — then delete this line so type errors fail the job.
continue-on-error: true
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