Paperjet/.forgejo/workflows/ci.yml
Elijah 5e19b78259
All checks were successful
CI / Backend (Python) (push) Successful in 21s
CI / Frontend (TypeScript) (push) Successful in 13s
CI / Container (Docker) (push) Successful in 12s
Harden Docker CI smoke test readiness checks
2026-08-15 11:29:59 -07:00

168 lines
5.2 KiB
YAML

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
backend:
name: Backend (Python)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
working-directory: backend
run: |
pip install -e ".[dev]"
- name: Lint (ruff)
working-directory: backend
run: ruff check .
- name: Type check (mypy)
working-directory: backend
run: mypy app/
- name: Test (pytest)
working-directory: backend
run: pytest --tb=short -q
env:
PAPERJET_DATABASE_PATH: ./test.sqlite
PAPERJET_PDF_STORAGE_PATH: ./test_pdfs
PAPERJET_THUMBNAILS_PATH: ./test_thumbs
frontend:
name: Frontend (TypeScript)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js 22
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
working-directory: frontend
run: npm ci
- name: Lint (eslint)
working-directory: frontend
run: npm run lint
- name: Type check (tsc)
working-directory: frontend
run: npx tsc -b
- name: Test (vitest)
working-directory: frontend
run: npx vitest run
container:
name: Container (Docker)
runs-on: ubuntu-latest
needs: [backend, frontend]
steps:
- uses: actions/checkout@v4
- name: Validate Compose configuration
run: docker compose --env-file .env.example config --quiet
- name: Build container image
run: |
IMAGE_PATH=$(printf 'git.elijahkuntz.com/${{ gitea.actor }}/${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')
docker build \
-t paperjet:ci \
-t "$IMAGE_PATH:latest" \
-t "$IMAGE_PATH:${{ github.sha }}" \
.
- name: Run container smoke test
run: |
set -Eeuo pipefail
docker rm --force paperjet-ci >/dev/null 2>&1 || true
docker run \
--detach \
--name paperjet-ci \
--env PAPERJET_SECRET_KEY=ci-only-secret \
--env PAPERJET_COOKIE_SECURE=false \
--env PAPERJET_DEBUG=false \
paperjet:ci
cleanup() {
echo "----- PaperJet container logs -----"
docker logs paperjet-ci || true
docker rm --force paperjet-ci >/dev/null 2>&1 || true
}
trap cleanup EXIT
for attempt in $(seq 1 30); do
running=$(docker inspect --format '{{.State.Running}}' paperjet-ci 2>/dev/null || true)
if [ "$running" != "true" ]; then
echo "PaperJet container exited before becoming ready"
exit 1
fi
if docker exec paperjet-ci \
python -c "import urllib.request; response = urllib.request.urlopen('http://127.0.0.1/', timeout=2); assert response.status == 200" \
>/dev/null 2>&1 \
&& docker exec paperjet-ci \
python -c "import urllib.request; response = urllib.request.urlopen('http://127.0.0.1/api/v1/health', timeout=2); assert response.status == 200" \
>/dev/null 2>&1; then
break
fi
sleep 1
done
running=$(docker inspect --format '{{.State.Running}}' paperjet-ci)
if [ "$running" != "true" ]; then
echo "PaperJet container did not become ready within 30 seconds"
exit 1
fi
docker exec paperjet-ci \
python -c "import urllib.request; response = urllib.request.urlopen('http://127.0.0.1/', timeout=2); assert response.status == 200"
docker exec paperjet-ci \
python -c "import urllib.request; response = urllib.request.urlopen('http://127.0.0.1/api/v1/health', timeout=2); assert response.status == 200"
for attempt in $(seq 1 30); do
status=$(docker inspect --format '{{.State.Health.Status}}' paperjet-ci)
if [ "$status" = "healthy" ]; then
echo "PaperJet smoke test passed"
exit 0
fi
if [ "$status" = "unhealthy" ]; then
echo "PaperJet container healthcheck reported unhealthy"
exit 1
fi
sleep 1
done
echo "PaperJet container did not become healthy within 30 seconds"
exit 1
- name: Log into Local Registry
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
echo "${{ secrets.FORGEJO_PAT }}" | docker login git.elijahkuntz.com -u "${{ gitea.actor }}" --password-stdin
- name: Push main image tags
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
IMAGE_PATH=$(printf 'git.elijahkuntz.com/${{ gitea.actor }}/${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')
docker push "$IMAGE_PATH:latest"
docker push "$IMAGE_PATH:${{ github.sha }}"