Files
AI_template/docs/ci-cd.md
olekhondera 425227c6b8 feat: add Woodpecker CI + Gitea deployment templates
- .woodpecker.yml: full pipeline template (install → lint-fix → lint → test → deploy)
- scripts/setup-project.sh: one-command VPS setup (user, dir, deploy, sudoers, systemd, nginx)
- scripts/deploy.sh: deploy script template (rsync + npm ci + systemctl + health check)
- scripts/ci-lint-fix.sh: ESLint auto-fix with [CI SKIP] commit-back
- docs/ci-cd.md: complete CI/CD documentation and troubleshooting
- .env.example: added WOODPECKER_TOKEN
- DOCS.md: added CI/CD section

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 23:24:41 +02:00

115 lines
3.6 KiB
Markdown

# CI/CD — Woodpecker + Gitea
## Overview
Pipeline runs on Woodpecker CI with **local backend** (no Docker) on the same VPS.
Triggered by push to `main` branch via Gitea webhook.
## Pipeline Flow
```
push to main → install → lint-fix (optional) → lint + test → deploy
```
### Steps
| Step | What it does |
|------|-------------|
| **install** | `npm ci` — clean install dependencies |
| **lint-fix** | ESLint `--fix`, auto-commits back with `[CI SKIP]` |
| **lint** | ESLint check — fails pipeline on errors |
| **test** | `npm test` — fails pipeline on test failures |
| **deploy** | Runs deploy script via sudo on the VPS |
### lint-fix auto-commit
When ESLint can auto-fix issues (formatting, `var``const`, `==``===`, etc.),
the pipeline commits the fix back to Gitea with `[CI SKIP]` in the message.
Woodpecker natively skips pipelines for commits containing `[CI SKIP]`.
Requires `gitea_token` secret — a Gitea access token with repo write permission.
## Configuration
### `.woodpecker.yml`
Located at project root. Uses `image: bash` for local backend (specifies shell, not Docker image).
### Secrets (Woodpecker UI)
| Secret | Where | Purpose |
|--------|-------|---------|
| `gitea_token` | Global or repo | Gitea access token for lint-fix auto-commit |
Add secrets in Woodpecker UI → Settings → Secrets (repo level) or global level.
## Deploy Setup (one-time on VPS)
### Automated setup (recommended)
```bash
# On VPS as root:
bash setup-project.sh <project-name> <domain> [port]
# Example:
bash setup-project.sh my-app app.spektr.design 3000
```
This creates: service user, `/opt/<project>` directory, deploy script, sudoers rule, systemd service, nginx config.
After running, create `.env` and push code to Gitea.
### Manual setup
If you need more control, see `scripts/setup-project.sh` for individual steps:
1. Create service user (`useradd -r -s /sbin/nologin`)
2. Create `/opt/<project>` directory
3. Install deploy script to `/usr/local/bin/deploy-<project>`
4. Add sudoers rule for Woodpecker agent user
5. Create systemd service
6. Create nginx config
### Deploy script details
`scripts/deploy.sh` does:
1. rsync files (excludes node_modules, .env, .git)
2. `npm ci --omit=dev` as service user (with `HOME=` set to avoid npm cache errors)
3. `systemctl restart` + health check
The script runs as root via sudo. Only this specific script is allowed in sudoers.
## Troubleshooting
| Problem | Cause | Fix |
|---------|-------|-----|
| `secret "X" not found` | Secret not added in Woodpecker | Add in UI → Settings → Secrets |
| `Invalid or missing image` | Missing `image:` field | Use `image: bash` for local backend |
| `Permission denied` on deploy | Sudoers not configured | Follow deploy setup above |
| `command not found` for deploy script | Script not at `/usr/local/bin/` | Copy and chmod 755 |
| npm `EACCES mkdir /home/user` | Service user has no home dir | Set `HOME=/opt/<project>` in deploy script |
| Pipeline not triggered | Webhook issue or `[CI SKIP]` in message | Check Gitea webhook settings |
## Gitea Access Token
Create in Gitea: Settings → Applications → Generate New Token.
Permissions needed: `repo` (read/write).
## Woodpecker API
Check pipeline status programmatically:
```bash
# List recent pipelines
curl -H "Authorization: Bearer $TOKEN" \
https://ci.spektr.design/api/repos/<REPO_ID>/pipelines?page=1&per_page=5
# Get specific pipeline
curl -H "Authorization: Bearer $TOKEN" \
https://ci.spektr.design/api/repos/<REPO_ID>/pipelines/<NUMBER>
```
Store token in `.env.local` (gitignored):
```
WOODPECKER_TOKEN=<your-token>
```