11 KiB
title, tags
| title | tags | |
|---|---|---|
| CI-CD Pipeline |
|
CI/CD Pipeline
How code goes from a push to a running container in production. The CI is Gitea Actions running on the same Gitea instance that hosts the repos. The CD is Watchtower on the production host (covered in Deployment).
1. Where workflows live
| Repo | Path | Files |
|---|---|---|
| Backend | .gitea/workflows/ |
docker-build-simple.yml, docker-build-dev.yml, docker-build-no-cache.yml |
| Frontend | .gitea/workflows/ |
deploy.yml, devDeploy.yml |
Gitea Actions speaks the same YAML dialect as GitHub Actions — most third-party actions (actions/checkout@v4, docker/login-action@v3, docker/build-push-action@v5) work unchanged.
2. Required secrets
Configured per repo at Settings → Actions → Secrets.
| Secret | Repo | Purpose |
|---|---|---|
GITEATOKEN |
both | Personal access token for the manawenuz user with write:packages scope. Used by every workflow to log into the container registry at git.manko.yoga. |
SENTRY_AUTH_TOKEN |
frontend | (Optional) For source-map upload during Next.js build. Skipped if absent. |
The registry itself is implicit: git.manko.yoga with manawenuz as the user. Image paths are git.manko.yoga/manawenuz/<image>.
[!warning] If
GITEATOKENexpires or is rotated, all workflows fail at thedocker/login-actionstep. Rotate proactively (annual reminder).
3. Backend workflows
docker-build-simple.yml — manual build
name: Manual Build and Push Docker Image
on:
workflow_dispatch:
inputs:
version:
description: 'Version to build (leave empty for package.json)'
required: false
type: string
- Trigger. Manual only (via Gitea UI → Actions → "Run workflow").
- Steps. Checkout → buildx →
docker login→ read version (input orpackage.json) → buildDockerfile.prod→ push tags:<version>and:dev→ echo result. - When to use. Cutting an ad-hoc build of a specific commit without merging to a branch. The
:devtag is overwritten — production (:latest) is not touched. - Cache. Uses
type=ghacache to speed up subsequent runs.
docker-build-dev.yml — dev branch auto-build
on:
push:
branches: [ development ]
tags: [ 'v*' ]
- Trigger. Every push to
developmentand every tag matchingv*. - Tags pushed.
:dev-<package-version>+ moving:dev. - Effect. Refreshes the dev image. The production Watchtower does not watch
:dev, so this is safe to push as often as you want.
docker-build-no-cache.yml — production build
on:
push:
branches: [ main, master ]
tags: [ 'v*' ]
- Trigger. Every push to
main(ormaster) and everyv*tag. - Tags pushed.
:<package-version>+ moving:latest. - Effect. Watchtower polls
:latest, detects the new digest, restartsnickapp-backendon the production host. See Deployment#routine-deploy. - No cache. The file is named "No Cache" but actually does not pass
cache-from/cache-to, so each build is from scratch. Slower (~5–8 min) but eliminates a class of stale-layer bugs. Thesimpleworkflow uses GHA cache for speed.
[!tip] If you need to invalidate a cached layer in the
simpleworkflow, runno-cacheonce — the resulting tag overwrites the registry digest andsimple's next run will start from a cleaner base.
4. Frontend workflows
Both workflows share the same shape: spin up a node:22 container, run a deploy shell script that does docker login + build + push.
deploy.yml — production
on:
push:
branches: [ main, master ]
workflow_dispatch:
Calls ./scripts/deploy.sh — see Scripts#deployment. The script:
- Reads
package.jsonversion. docker login git.manko.yoga -u manawenuz -p $GITEATOKEN.- Builds
git.manko.yoga/manawenuz/escrow-frontend:<version>and:latestfromDockerfile. - Pushes both tags.
:latest is what production Watchtower watches → live deploy follows automatically.
devDeploy.yml — development branch
Same as deploy.yml but triggered on development and runs ./scripts/deployDev.sh, which pushes only :dev.
5. End-to-end timeline (production deploy)
t=0 Developer merges PR → main
t+5s Gitea webhook fires
t+10s Gitea Actions runner pulls repo, starts container
t+30s docker/setup-buildx-action initialised
t+45s docker/login-action authenticated
t+2-5m docker/build-push-action builds Dockerfile.prod
t+5m Push to git.manko.yoga/manawenuz/escrow-backend:latest
t+5m+ Watchtower (next poll, up to 5 min) detects new digest
t+10m Watchtower stops old container, starts new one
t+10m40s start_period=40s elapses, healthcheck passes
t+11m Nginx routes traffic to the new container
Typical SLA: 10–12 minutes from merge to live. For an emergency rollback see Deployment#roll-back.
6. Versioning automation
Tied to backend/scripts/auto-version.sh + ai-enhanced.sh (and the frontend mirror). Full reference in Git Workflow#versioning and Scripts#auto-version-sh.
In short:
# Developer side, on the branch they're releasing:
npm run smart-release
# → AI analyses last commit, picks bump (major/minor/patch/skip)
# → bumps package.json
# → commits "chore: bump version to vX.Y.Z"
# → tags vX.Y.Z
# → git push && git push --tags
The push to main (or the v* tag) then triggers docker-build-no-cache.yml, which:
- Reads the new version from
package.json(node -p "require('./package.json').version") - Builds and pushes
:<version>+:latest
So both the image tag and the git tag carry the same vX.Y.Z — easy to correlate when investigating an issue.
7. Adding tests to the pipeline
The workflows today only build + push; they do not run Jest or Playwright. To gate releases on tests, add a test job before the build:
jobs:
test:
runs-on: ubuntu-latest
container: node:22
steps:
- uses: actions/checkout@v4
- run: yarn install --frozen-lockfile
- run: yarn lint
- run: yarn test --ci --runInBand
- run: yarn test:e2e # if a service container is available
build-and-push:
needs: test
runs-on: ubuntu-latest
# ...existing steps...
Or run lint + typecheck as a pre-gate using a separate workflow that triggers on PR opened/synchronised.
8. Inspecting a build
In Gitea: Actions → workflow → run to see real-time logs.
Useful CLI for the registry from your laptop:
# List images and tags
curl -s -u "manawenuz:$GITEATOKEN" \
"https://git.manko.yoga/v2/manawenuz/escrow-backend/tags/list" | jq
# Pull a specific tag
docker login git.manko.yoga -u manawenuz
docker pull git.manko.yoga/manawenuz/escrow-backend:2.6.3
9. Self-hosted runner notes
Gitea Actions can use either built-in act_runner or your own. Currently the workflows are written for runs-on: ubuntu-latest, which the act_runner supplies via a generic Ubuntu container. If you need:
- More CPU/RAM for builds → register a beefier self-hosted runner and change
runs-on:to its label. - A Docker-in-Docker setup (frontend
deploy.ymldoes this withoptions: --privileged) — confirm the runner trusts the workflow.
10. Failure modes & remediation
| Failure | Most likely cause | Fix |
|---|---|---|
unauthorized: authentication required at push |
GITEATOKEN expired or lacks write:packages |
Rotate the token, update the repo secret |
Cannot perform an interactive login from a non TTY device |
Old docker-login-action version | Bump to docker/login-action@v3 |
Build hangs at yarn install |
npm registry timeout | Increase network-timeout (already 600000); re-run |
| Image pushed but Watchtower doesn't roll | Watchtower can't reach the registry | docker logs watchtower; verify /root/.docker/config.json is mounted into the container |
| New container fails healthcheck | App crash on boot | docker logs nickapp-backend; check env vars, follow Incident Response |
| Multi-arch warnings about platform | Build runner is arm64 but prod is amd64 | Add --platform=linux/amd64 to docker/build-push-action inputs |
| Image size grew suddenly | Dev dep crept into prod stage | Audit Dockerfile.prod for missing --production flag in the runtime stage |
11. Pipeline diagram
Push to development Push to main
│ │
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────┐
│ docker-build-dev.yml │ │ docker-build-no-cache.yml │
│ (backend) │ │ (backend) │
│ devDeploy.yml (frontend) │ │ deploy.yml (frontend) │
└───────────────┬───────────┘ └───────────────┬───────────┘
│ │
push :<version>,:dev push :<version>,:latest
│ │
▼ ▼
git.manko.yoga/manawenuz/...:dev git.manko.yoga/manawenuz/...:latest
│ │
│ ▼
│ ┌──────────────────────────┐
│ │ Watchtower │
│ │ (poll every 5 minutes) │
│ └──────────────┬───────────┘
│ │
manual pull on staging restart containers
│
▼
Production live
Cross-links: Deployment for what happens on the host, Git Workflow for what happens upstream, Scripts for the deploy shell scripts.