# Gitea Actions pipeline (peptide-repo) Since 2026-10-24 every peptide-repo plugin/theme builds and deploys from `git.protopeer.com/peptide-repo`, not GitHub. GitHub's `.github/workflows` are left in the repos but Gitea ignores them (a repo with `.gitea/workflows` uses only that folder). ## How it fits together - **Shared logic lives once**, as composite actions in this repo under `.gitea/actions/`: `deploy`, `smoke`, `ci-lint-php`, `ci-phpcs`, `ci-file-size`, `ci-phpunit`, `ci-lint-js`, `audit-direct-push`. Gitea 1.25 cannot run reusable workflows (`workflow_call`), which is why this is not a 1:1 copy of the GitHub `deploy-app.yml` / `ci.yml`. - **Each repo's `.gitea/workflows/pipeline.yml` is generated** by `tools/gitea-pipeline/render.py` from `tools/gitea-pipeline/repos.json`. Change a repo's settings in `repos.json`, re-render, commit the output. Do not hand-edit the generated files. - **Job graph:** lint-php / phpcs / file-size / phpunit / lint-js (+ repo extras) -> deploy-staging -> smoke (gate) -> deploy-production. Deploys run on pushes to `main` and on manual runs of `main`. - **Runners:** CI jobs run on the shared docker runner (`ubuntu-latest`) inside `git.protopeer.com/peptide-repo/ci-php:2026-10` (PHP 8.1/8.2/8.3 + Composer, built from `ci-image/`). Deploy and smoke run on the host runner `peptide-vps-gitea` (systemd `gitea-runner-peptide`, user `gtrunner`, label `peptide-vps`) on the KVM8, one job at a time, like the old GitHub runner. - **Secrets / variables** are org-level on Gitea `peptide-repo`: `SSH_PRIVATE_KEY` (deploy key `gitea-peptide-deploy-20261024` on Hostinger), `SSH_HOST`, `SSH_PORT`, `SSH_USERNAME`, `STAGING_WP_ADMIN_USER` (`gitea-smoke`, staging-only admin), `STAGING_WP_ADMIN_PASSWORD`; variable `PROD_DEPLOY_ENABLED`. ## Switches and recovery - **Kill switch:** set org variable `PROD_DEPLOY_ENABLED` to anything but `true` -> production deploys (and `peptide-news-plugin` rollback) are skipped; staging + smoke still run. - **Redeploy / retry:** run the repo's `CI / Deploy` workflow on `main` manually. - **Roll back news plugin:** `peptide-news-plugin` -> `Rollback Deployment` (workflow_dispatch, optional commit SHA). Same deploy action as normal. - **Drift:** `deploy-health.yml` (daily 13:00 UTC / 21:00 SGT) compares the commit stamped in `~/.peptide-deploys/production/` on Hostinger with each repo's `main`, and opens one "Deploy drift detected" issue here. - **Back to GitHub (emergency):** `systemctl enable --now actions.runner.peptide-repo.peptide-vps` on the KVM8 and set `PROD_DEPLOY_ENABLED=false` here. Only works while GitHub's Hostinger deploy key is still authorised. ## Differences from the GitHub pipeline (deliberate) - `.gitea` is also excluded from rsync. - JS lint uses Node 22 (GitHub used Node 20, now end-of-life). - Smoke login confirms WordPress's periodic "Administration email verification" screen instead of timing out on it (it had been blocking smoke since 2026-09-10, when staging's admin-email check expired). - Production stamps the deployed commit for deploy-health (replaces GitHub Deployments). - PHP matrices are literal lists (avoids Gitea's `fromJson` matrix bug). ## Known gaps - `pr-vision` `cto-review.yml` (AI PR review via OpenRouter) is **not ported**: its `OPENROUTER_API_KEY` / `CTO_ADMIN_TOKEN` secrets only exist on GitHub and it calls GitHub's review API. - The LiteSpeed purge in the production deploy runs relative to `$HOME`, so it is effectively a no-op - same as on GitHub. Kept as-is, not fixed. - Issues / PR history of repos other than peptide-e2e stayed on GitHub (the migration hit GitHub's anonymous API rate limit); GitHub repos are to be archived, not deleted.