Files
PacketStation/.gitea/workflows/ci.yml
T
spencermandClaude Opus 5.5 7a43236630
ci / frontend (pull_request) Successful in 52s
ci / server (pull_request) Successful in 11m16s
ci / image (pull_request) Skipped
ci / e2e (pull_request) Failing after 34s
deps: pypdf 6.21.1 to 6.21.2
The CI dependency scan flags GHSA-6c7f-9265-g8fw, GHSA-jr96-gvgh-j3r4
and GHSA-gmh7-3g9r-mjh5 against 6.21.1, all fixed in 6.21.2.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 14:05:18 -06:00

203 lines
7.1 KiB
YAML

# Packet Station CI. Runs on every push to main, every v* tag and every pull request.
# server and frontend run in parallel. image builds and pushes :sha-<sha> only on a push to
# main or a v* tag, after both pass. release.yml re-tags that image. Nothing is ever :latest.
name: ci
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
env:
REGISTRY: gt.pescoinc.biz
IMAGE_NAME: gt.pescoinc.biz/spencerm/packetstation
# PESCO's Gitea index. It serves visualkit, mfileskit and their dependencies only.
PESCO_INDEX_URL: https://gt.pescoinc.biz/api/packages/spencerm/pypi/simple/
jobs:
server:
runs-on: ubuntu-latest
services:
# Throwaway database for the tests. Trust auth inside the job network, so no password.
postgres:
image: postgres:16-bookworm
env:
POSTGRES_HOST_AUTH_METHOD: trust
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 20
env:
# act_runner runs the job in a container on the service network, so the host is "postgres".
TEST_PG_SUPERUSER_URL: postgresql://postgres@postgres:5432/postgres
STACK: ci
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: |
server/requirements.txt
server/requirements-pesco.txt
- name: unixODBC for pyodbc imports
run: |
set -euo pipefail
sudo_cmd=""
if [ "$(id -u)" != "0" ]; then sudo_cmd="sudo"; fi
$sudo_cmd apt-get update
$sudo_cmd apt-get install -y --no-install-recommends unixodbc
- name: Install dependencies
run: |
set -euo pipefail
python -m pip install --upgrade pip
# Public packages from PyPI.
pip install -r server/requirements.txt
# PESCO packages from Gitea only. Never --extra-index-url.
pip install --index-url "$PESCO_INDEX_URL" -r server/requirements-pesco.txt
# The package itself, editable, with its dependencies already in place.
pip install --no-deps -e .
# Dev tools from pyproject's dev extra, installed by name so pip never
# resolves visualkit or mfileskit against PyPI.
python -c 'import tomllib; print("\n".join(tomllib.load(open("pyproject.toml", "rb"))["project"]["optional-dependencies"]["dev"]))' > "$RUNNER_TEMP/dev-requirements.txt"
pip install -r "$RUNNER_TEMP/dev-requirements.txt"
pip check
# Requirement S3. Audits every pin in server/requirements.txt (transitive included) against
# the PyPI advisory database. visualkit and mfileskit are not on PyPI: they come from the Gitea
# index (requirements-pesco.txt) and are not audited here. pip-audit has no severity filter, so
# any known vulnerability fails the job, which covers high and critical. pip-audit runs from its
# own venv so its dependencies never touch the pinned set above.
- name: Dependency scan (pip-audit)
run: |
set -euo pipefail
python -m venv "$RUNNER_TEMP/audit"
"$RUNNER_TEMP/audit/bin/pip" install --quiet pip-audit==2.9.0
"$RUNNER_TEMP/audit/bin/pip-audit" -r server/requirements.txt --no-deps --disable-pip --progress-spinner off
- name: Lint (ruff)
run: |
ruff check .
ruff format --check .
- name: Types (mypy)
run: mypy
- name: Tests (pytest)
run: pytest
frontend:
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install
run: npm ci --no-audit --no-fund
# Requirement S3. Fails on a high or critical advisory in any dependency, dev tools included.
- name: Dependency scan (npm audit)
run: npm audit --audit-level=high
- name: Lint
run: npm run lint
- name: Types (tsc)
run: npm run typecheck
- name: Unit tests
run: npm test
- name: Build
run: npm run build
image:
needs: [server, frontend]
if: gitea.event_name == 'push' && (gitea.ref == 'refs/heads/main' || startsWith(gitea.ref, 'refs/tags/v'))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push sha image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
platforms: linux/amd64
push: true
tags: ${{ env.IMAGE_NAME }}:sha-${{ gitea.sha }}
build-args: |
PS_BUILD=sha-${{ gitea.sha }}
cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache,mode=max
provenance: false
# End to end. Compose up with the CI overlay and the throwaway Samba DC, run the Playwright
# scenarios, upload the report, then down -v. The runner needs a Docker daemon whose bind
# mounts see this checkout (open question 15, deploy/samba/README.md).
e2e:
needs: [server, frontend]
runs-on: ubuntu-latest
timeout-minutes: 30
env:
COMPOSE: >-
docker compose -p packetstation-ci --env-file deploy/ci/ci.env
-f deploy/compose.yaml -f deploy/compose.ci.yaml
steps:
- uses: actions/checkout@v4
- name: Throwaway secrets
run: bash deploy/ci/make-secrets.sh
- uses: actions/setup-python@v5
with:
python-version: "3.12"
# The worker reads these through FEED_SOURCE=fixture and DRAWING_SOURCE=fixture (deploy/ci/ci.env).
# The generator needs pypdf only, so nothing else is installed here.
- name: Generate feed fixtures
run: |
set -euo pipefail
python -m pip install pypdf==6.21.2
python -m tests.fixtures.generator --out tests/fixtures/generated
# The Playwright container writes state; the worker reads as uid 10001.
chmod -R a+rwX tests/fixtures/generated
- name: Stack up (app, worker, postgres, Samba DC)
run: $COMPOSE up -d --build --wait --wait-timeout 300
- name: Playwright
run: $COMPOSE --profile e2e run --rm playwright
- name: Stack logs
if: failure()
run: $COMPOSE logs --no-color --tail 300 samba-dc migrate app worker
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-report
path: tests/e2e/playwright-report
if-no-files-found: ignore
- name: Stack down
if: always()
run: $COMPOSE --profile e2e down -v