tftsr-devops_investigation/docs/wiki/CICD-Pipeline.md
Shaun Arman 52f464d8bd docs: add wiki source files and CI auto-sync pipeline
- Add docs/wiki/ with 11 wiki pages (Home, Architecture, Database,
  AI-Providers, PII-Detection, IPC-Commands, CICD-Pipeline,
  Security-Model, Integrations, Development-Setup, Troubleshooting)
- Add wiki-sync step to .woodpecker/test.yml: syncs docs/wiki/*.md to
  the Gogs wiki git repo on every push to master
- Add Wiki Maintenance section to CLAUDE.md: code→wiki file mapping
  so Claude and contributors know which wiki page to update per change

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 13:45:30 -05:00

159 lines
4.9 KiB
Markdown

# CI/CD Pipeline
## Infrastructure
| Component | URL | Notes |
|-----------|-----|-------|
| Gogs | `http://172.0.0.29:3000` / `https://gogs.tftsr.com` | Git server, version 0.14 |
| Woodpecker CI (direct) | `http://172.0.0.29:8084` | v0.15.4 |
| Woodpecker CI (proxy) | `http://172.0.0.29:8085` | nginx with custom login page |
| PostgreSQL (Gogs DB) | Container: `gogs_postgres_db` | DB: `gogsdb`, User: `gogs` |
---
## Test Pipeline (`.woodpecker/test.yml`)
**Triggers:** Every push and pull request to any branch.
```
Pipeline steps:
1. rust-fmt-check → cargo fmt --check
2. rust-clippy → cargo clippy -- -D warnings
3. rust-tests → cargo test
4. frontend-typecheck → npx tsc --noEmit
5. frontend-tests → npm run test:run (Vitest)
```
**Docker images used:**
- `rust:1.88-slim` — Rust steps (minimum for cookie_store + time + darling)
- `node:22-alpine` — Frontend steps
**System dependencies installed in CI (Rust steps):**
```
libwebkit2gtk-4.1-dev, libssl-dev, libgtk-3-dev, libsoup-3.0-dev,
librsvg2-dev, libglib2.0-dev
```
**Pipeline YAML format (Woodpecker 0.15.4 — legacy MAP format):**
```yaml
clone:
git:
image: woodpeckerci/plugin-git
network_mode: gogs_default # requires repo_trusted=1
environment:
- CI_REPO_CLONE_URL=http://gogs_app:3000/sarman/tftsr-devops_investigation.git
pipeline:
step-name: # KEY = step name (MAP, not list!)
image: rust:1.88-slim
commands:
- cargo test
```
> ⚠️ **Do NOT** use the newer `steps:` list format — Woodpecker 0.15.4 uses the Drone-legacy map format.
---
## Release Pipeline (`.woodpecker/release.yml`)
**Triggers:** Git tags matching `v*`
```
Pipeline steps:
1. build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu)
2. build-linux-arm64 → cargo tauri build (aarch64-unknown-linux-gnu, cross-compile)
3. upload-release → Create Gogs release + upload artifacts via API
```
**Artifacts per platform:**
- Linux amd64: `.deb`, `.rpm`, `.AppImage`
- Linux arm64: `.deb`, `.AppImage`
**Gogs Release API:**
```bash
# Create release
POST $API/repos/sarman/tftsr-devops_investigation/releases
Authorization: token $GOGS_TOKEN
# Upload artifact
POST $API/repos/sarman/tftsr-devops_investigation/releases/{id}/assets
```
The `GOGS_TOKEN` is stored as a Woodpecker secret.
---
## Webhook Configuration
**Hook ID:** 6 (in Gogs)
**Events:** `create`, `push`, `pull_request`
**URL:** `http://172.0.0.29:8084/hook?access_token=<JWT>`
**JWT signing:**
- Algorithm: HS256
- Secret: `repo_hash` value from Woodpecker DB (`dK8zFWtAu67qfKd3Et6N8LptqTmedumJ`)
- Payload: `{"text":"sarman/tftsr-devops_investigation","type":"hook"}`
> ⚠️ JWT has an `iat` claim. If it's stale, regenerate it.
---
## Woodpecker DB State
SQLite at `/docker_mounts/woodpecker/data/woodpecker.sqlite` (on host `172.0.0.29`).
Key values:
```sql
-- User
SELECT user_token FROM users WHERE user_login='sarman';
-- Should be: [REDACTED-ROTATED]
-- Repo
SELECT repo_active, repo_trusted, repo_config_path, repo_hash
FROM repos WHERE repo_full_name='sarman/tftsr-devops_investigation';
-- repo_active=1, repo_trusted=1
-- repo_config_path='.woodpecker/test.yml'
-- repo_hash='dK8zFWtAu67qfKd3Et6N8LptqTmedumJ'
```
---
## Known Issues & Fixes
### Webhook JWT Must Use `?access_token=`
`token.ParseRequest()` in Woodpecker 0.15.4 does **not** read `?token=` URL params. Use `?access_token=<JWT>` instead.
### JWT Signed with `repo_hash` (Not User Hash)
Hook JWT must be signed with the `repo_hash` value, not the user's hash.
### Directory-Based Config Not Supported
Woodpecker 0.15.4 only supports a **single config file**. Set `repo_config_path = .woodpecker/test.yml` in the Woodpecker DB. The `.woodpecker/` directory approach requires v2.x+.
### Step Containers Network Isolation
Pipeline step containers run on the default Docker bridge and cannot resolve `gogs_app` hostname. Fix: set `network_mode: gogs_default` in the clone section (requires `repo_trusted=1`).
### Empty Clone URL Bug
Woodpecker 0.15.4's `go-gogs-client` `PayloadRepo` struct lacks `CloneURL`/`SSHURL` fields, so `build_remote` is always empty from Gogs push payloads. Fix: override the clone URL via `CI_REPO_CLONE_URL` environment variable.
### Gogs Token Authentication
The `sha1` field in Gogs token create API response **is** the actual bearer token (not a hash). Use it directly:
```
Authorization: token <sha1_from_create_response>
```
### Gogs SPA Login Field Mismatch
Gogs 0.14 SPA login form uses `login=` field; the Gogs backend reads `username=`. A custom login page is served by nginx at `/login`.
### Gogs OAuth2 Limitation
Gogs 0.14 has no OAuth2 provider support, blocking upgrade to Woodpecker 2.x.
---
## Gogs PostgreSQL Access
```bash
docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name FROM repository;"
```
> Database name is `gogsdb`, not `gogs`.