# 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 - run: ruff check app/ 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