Files
signal-platform/.gitea/workflows/deploy.yml
T
dennisthiessenandClaude Opus 5 98b41629e7
Deploy / lint (push) Successful in 8s
Deploy / test (push) Successful in 1m14s
Deploy / deploy (push) Successful in 39s
ci: lint the whole repo, and pin the rule set so it stays deterministic
Widens the lint step from `ruff check app/` to `ruff check .`, since tests/ and
scripts/ had drifted to 11 findings while unchecked (fixed in 1c6ccce).

Widening alone would have been unsafe, and the check turned up something worse
than the drift: CI installs ruff unpinned, the repo had no [tool.ruff] config,
and ruff's default rule set is not stable across releases. Local 0.15.4 reports
0 findings in app/; 0.16.2 -- what `pip install ruff` resolves to today --
reports 376. 168 of those are B008 flagging FastAPI's `Depends()` in a signature
default, which is the framework's documented idiom, not a defect. So the lint
job would have failed the deploy pipeline on untouched code at the next push,
independent of this change.

Pinning the ruff version would freeze the bug in place. Pinning the *rule set*
is the actual fix: select = ["E4", "E7", "E9", "F"] in pyproject.toml, the set
the tree was already clean under, now enforced repo-wide. The ruff version can
float freely without changing what CI enforces.

Verified both scopes against both versions with caches disabled: `ruff check .`
passes under 0.15.4 and 0.16.2. Confirmed the pin actually binds rather than
passing by luck -- a probe file using `Depends()` in a default is clean under
the committed config and reports B008 + I001 under `--isolated` 0.16.2 defaults.
Full suite still 852 passed, 1 skipped.

Adding rules is welcome; do it in pyproject.toml with the fixes in the same
commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 19:28:26 +02:00

181 lines
5.8 KiB
YAML

# Gitea Actions CI/CD pipeline: lint → test → deploy
# Triggers on push to main branch.
# Required variables and secrets (set in Gitea repo settings):
# Variables:
# DEPLOY_HOST — server IP or hostname
# DEPLOY_PATH — absolute path to the directory on the server
# DEPLOY_USER — SSH username on the server
# SSH_KNOWN_HOSTS — SSH known_host fingerprint for security
# SSH_PORT — SSH port of the server
# Secrets:
# SSH_PRIVATE_KEY — SSH private key for deployment
name: Deploy
on:
push:
branches: [main]
workflow_dispatch:
inputs:
run_setup_db:
description: "Run deploy/setup_db.sh to configure DB and run migrations"
required: false
type: boolean
default: false
# Serialize deploys so two quick pushes to main can't rsync/restart on top of
# each other. Don't cancel an in-flight deploy mid-restart.
concurrency:
group: deploy-main
cancel-in-progress: false
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- run: pip install ruff
# Whole repo, not just app/: tests/ and scripts/ drifted to 11 findings
# while unchecked. Rules are pinned in pyproject.toml, so the unpinned
# ruff above cannot change what this enforces.
- run: ruff check .
test:
needs: lint
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: test_db
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_pass
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- run: pip install -e ".[dev]"
# The Postgres service exists only to validate the migrations against real
# Postgres (what prod runs). The test suite itself uses an in-memory SQLite
# engine (tests/conftest.py), so pytest doesn't touch this service.
- run: alembic upgrade head
env:
DATABASE_URL: postgresql+asyncpg://test_user:test_pass@postgres:5432/test_db
- run: pytest --tb=short
- run: |
cd frontend
npm ci
if node -e "require.resolve('vitest/package.json')" >/dev/null 2>&1; then
npm test
else
echo "vitest not configured; skipping frontend tests"
fi
npm run build
deploy:
needs: test
runs-on: ubuntu-latest
env:
DEPLOY_HOST: ${{ vars.DEPLOY_HOST }}
DEPLOY_USER: ${{ vars.DEPLOY_USER }}
DEPLOY_PATH: ${{ vars.DEPLOY_PATH }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_KNOWN_HOSTS: ${{ vars.SSH_KNOWN_HOSTS }}
SSH_PORT: ${{ vars.SSH_PORT || '22' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- name: Build frontend
run: |
cd frontend
npm ci
npm run build
- name: Install deploy tools
run: sudo apt-get update -qq && sudo apt-get install -y -qq rsync openssh-client > /dev/null 2>&1 || true
- name: Set up SSH
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
# known_hosts is supplied, so verify the host key instead of blindly
# trusting it (StrictHostKeyChecking=no would defeat the fingerprint).
echo "SSH_OPTS=-i $HOME/.ssh/deploy_key -o StrictHostKeyChecking=yes -p ${SSH_PORT}" >> "$GITHUB_ENV"
- name: Sync files to server
run: |
rsync -avz --delete \
--exclude '.git/' \
--exclude '.gitea/' \
--exclude '.env' \
--exclude '.venv/' \
--exclude '__pycache__/' \
--exclude '.pytest_cache/' \
--exclude 'logs/' \
--exclude '*.pyc' \
--exclude 'frontend/node_modules/' \
-e "ssh $SSH_OPTS" \
./ "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/"
- name: Install deps & run migrations
run: |
ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_HOST}" << REMOTE_SCRIPT
set -e
cd ${DEPLOY_PATH}
# Create venv if not exists
if [ ! -d ".venv" ]; then
python3 -m venv .venv
fi
source .venv/bin/activate
pip install --quiet --upgrade pip
pip install --quiet -e .
# Setup DB and run migrations
if [ "${{ inputs.run_setup_db }}" = "true" ] || [ "${{ github.event.inputs.run_setup_db }}" = "true" ]; then
chmod +x deploy/setup_db.sh
./deploy/setup_db.sh
else
alembic upgrade head
fi
REMOTE_SCRIPT
- name: Restart service & health check
run: |
ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_HOST}" << REMOTE_SCRIPT
set -e
sudo systemctl restart signalplatform.service
sleep 3
curl -fsS http://127.0.0.1:8998/api/v1/health > /dev/null \
|| { echo "✗ health check failed after restart"; exit 1; }
echo "✓ signalplatform deployed"
REMOTE_SCRIPT
- name: Clean up SSH key
if: always()
run: rm -f ~/.ssh/deploy_key