Files

123 lines
4.3 KiB
YAML

name: CI - Security & Lint
on:
push:
branches: [main, master, 'audit/**']
pull_request:
branches: [main, master]
workflow_dispatch: # Allow manual triggers
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Least privilege: nothing here writes back to the repository.
permissions:
contents: read
# Third-party actions are pinned to a commit, with the version in a comment
# (CI-003). A tag is a moving pointer: whoever can move `v4` runs code in a
# job that holds this repository's token. The comment is what makes the pin
# maintainable — a bare 40-character hash tells a reader nothing about
# whether it is current. Dependabot updates both together.
env:
PYTHON_VERSION: '3.12'
jobs:
security-audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install pip-audit
run: pip install pip-audit==2.9.0
# Previously: `pip-audit --require-hashes --no-deps || pip-audit`.
# Neither form named the requirements file, so the fallback audited the
# runner's environment — which contained pip-audit and nothing else.
# The job passed green while checking none of the application's
# dependencies. -r makes it audit what the application actually pins.
- name: Scan declared dependencies for known vulnerabilities
run: pip-audit -r requirements.txt
lint:
name: Lint with Ruff
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install ruff
run: pip install ruff==0.14.4
# Rule selection and per-file ignores live in pyproject.toml. Before it
# existed, this step ran ruff's bare defaults with no configuration.
- name: Run ruff linter
run: ruff check . --output-format=github
# Enabled now that the repository has been formatted once, in its own
# commit (QUA-002). Reaching this step before that would have failed on
# 72 of 76 files for reasons unrelated to correctness.
- name: Check formatting
run: ruff format --check .
security-scan:
name: Security Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install app dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
# The path was `security_scan.py`, but the script lives under
# app/supporting_scripts/. The step had therefore failed on every run
# since the file was moved.
- name: Run security scan
env:
SECRET_KEY: ${{ secrets.CI_SECRET_KEY || 'test-key-not-for-production-1234567890' }}
DATABASE_URL: 'sqlite:///:memory:'
FLASK_DEBUG: 'false'
run: python app/supporting_scripts/security_scan.py --skip-http
test:
name: Tests
runs-on: ubuntu-latest
needs: [security-audit, lint]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
# Previously an `echo` guarded by continue-on-error: the job reported
# success without executing anything. The suite needs no environment
# variables and no database server: create_app() takes its configuration
# as an argument and the fixtures use a temporary SQLite file.
- name: Run tests
run: pytest --cov=app --cov-report=term-missing --cov-report=xml