Documents the Ubuntu 22.04 + ports.ubuntu.com approach for arm64 cross-compilation and adds a Known Issues entry explaining the Debian single-mirror multiarch root cause that was replaced. Co-Authored-By: fix/yaml-heredoc-indent <noreply@local>
282 lines
10 KiB
Markdown
282 lines
10 KiB
Markdown
# CI/CD Pipeline
|
|
|
|
## Infrastructure
|
|
|
|
| Component | URL | Notes |
|
|
|-----------|-----|-------|
|
|
| Gitea | `https://gogs.tftsr.com` / `http://172.0.0.29:3000` | Git server (migrated from Gogs 0.14) |
|
|
| Woodpecker CI (direct) | `http://172.0.0.29:8084` | v2.x |
|
|
| Woodpecker CI (proxy) | `http://172.0.0.29:8085` | nginx reverse proxy |
|
|
| PostgreSQL (Gitea DB) | Container: `gogs_postgres_db` | DB: `gogsdb`, User: `gogs` |
|
|
|
|
### CI Agents
|
|
|
|
| Agent | Platform | Host | Purpose |
|
|
|-------|----------|------|---------|
|
|
| `gitea_act_runner_amd64` (Docker) | `linux-amd64` | 172.0.0.29 | Native x86_64 — test builds + amd64/windows release |
|
|
| `act_runner` (systemd) | `linux-arm64` | 172.0.0.29 | Native aarch64 — arm64 release builds |
|
|
| `act_runner` (launchd) | `macos-arm64` | sarman's local Mac | Native Apple Silicon — macOS `.dmg` release builds |
|
|
|
|
Agent labels configured in `~/.config/act_runner/config.yaml`:
|
|
```yaml
|
|
runner:
|
|
labels:
|
|
- "macos-arm64:host"
|
|
```
|
|
macOS runner runs jobs **directly on the host** (no Docker container) — macOS SDK cannot run in Docker.
|
|
|
|
---
|
|
|
|
## Test Pipeline (`.woodpecker/test.yml`)
|
|
|
|
**Triggers:** Pull requests only.
|
|
|
|
```
|
|
Pipeline steps:
|
|
1. rust-fmt-check → cargo fmt --check
|
|
2. rust-clippy → cargo clippy -- -D warnings
|
|
3. rust-tests → cargo test (64 tests)
|
|
4. frontend-typecheck → npx tsc --noEmit
|
|
5. frontend-tests → npm run test:run (13 Vitest tests)
|
|
```
|
|
|
|
**Docker images used:**
|
|
- `rust:1.88-slim` — Rust steps (minimum for cookie_store + time + darling)
|
|
- `node:22-alpine` — Frontend steps
|
|
|
|
**Pipeline YAML format (Woodpecker 2.x — steps list format):**
|
|
```yaml
|
|
clone:
|
|
git:
|
|
image: woodpeckerci/plugin-git
|
|
network_mode: gogs_default # requires repo_trusted=1
|
|
environment:
|
|
- CI_REPO_CLONE_URL=http://gitea_app:3000/sarman/tftsr-devops_investigation.git
|
|
|
|
steps:
|
|
- name: step-name # LIST format (- name:)
|
|
image: rust:1.88-slim
|
|
commands:
|
|
- cargo test
|
|
```
|
|
|
|
> ⚠️ Woodpecker 2.x uses the `steps:` list format. The legacy `pipeline:` map format from
|
|
> Woodpecker 0.15.4 is no longer supported.
|
|
|
|
---
|
|
|
|
## Release Pipeline (`.gitea/workflows/auto-tag.yml`)
|
|
|
|
**Triggers:** Pushes to `master` (auto-tag), then release build/upload jobs run after `autotag`.
|
|
|
|
Auto tags are created by `.gitea/workflows/auto-tag.yml` using `git tag` + `git push`.
|
|
Release jobs are executed in the same workflow and depend on `autotag` completion.
|
|
|
|
```
|
|
Jobs (run in parallel):
|
|
build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu)
|
|
→ {.deb, .rpm, .AppImage} uploaded to Gitea release
|
|
→ fails fast if no Linux artifacts are produced
|
|
build-windows-amd64 → cargo tauri build (x86_64-pc-windows-gnu) via mingw-w64
|
|
→ {.exe, .msi} uploaded to Gitea release
|
|
→ fails fast if no Windows artifacts are produced
|
|
build-linux-arm64 → Ubuntu 22.04 base (ports.ubuntu.com for arm64 packages)
|
|
→ cargo tauri build (aarch64-unknown-linux-gnu)
|
|
→ {.deb, .rpm, .AppImage} uploaded to Gitea release
|
|
→ fails fast if no Linux artifacts are produced
|
|
build-macos-arm64 → cargo tauri build (aarch64-apple-darwin) — runs on local Mac
|
|
→ {.dmg} uploaded to Gitea release
|
|
→ existing same-name assets are deleted before upload (rerun-safe)
|
|
→ unsigned; after install run: xattr -cr /Applications/TFTSR.app
|
|
```
|
|
|
|
**Per-step agent routing (Woodpecker 2.x labels):**
|
|
|
|
```yaml
|
|
steps:
|
|
- name: build-linux-amd64
|
|
labels:
|
|
platform: linux/amd64 # → woodpecker_agent on 172.0.0.29
|
|
|
|
- name: build-linux-arm64
|
|
labels:
|
|
platform: linux/arm64 # → woodpecker-agent.service on local arm64 machine
|
|
```
|
|
|
|
**Multi-agent workspace isolation:**
|
|
|
|
Steps routed to different agents do **not** share a workspace. The arm64 step clones
|
|
the repo directly within its commands (using `http://172.0.0.29:3000`, accessible from
|
|
the local machine) and uploads its artifacts inline. The `upload-release` step (amd64)
|
|
handles amd64 + windows artifacts only.
|
|
|
|
**Clone override (auto-tag.yml — amd64 workspace):**
|
|
|
|
```yaml
|
|
clone:
|
|
git:
|
|
image: alpine/git
|
|
network_mode: gogs_default
|
|
commands:
|
|
- git init -b master
|
|
- git remote add origin http://gitea_app:3000/sarman/tftsr-devops_investigation.git
|
|
- git fetch --depth=1 origin +refs/tags/${CI_COMMIT_TAG}:refs/tags/${CI_COMMIT_TAG}
|
|
- git checkout ${CI_COMMIT_TAG}
|
|
```
|
|
|
|
**Windows cross-compile environment:**
|
|
```yaml
|
|
environment:
|
|
TARGET: x86_64-pc-windows-gnu
|
|
CC_x86_64_pc_windows_gnu: x86_64-w64-mingw32-gcc
|
|
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: x86_64-w64-mingw32-gcc
|
|
```
|
|
|
|
**Artifacts per platform:**
|
|
- Linux amd64: `.deb`, `.rpm`, `.AppImage`
|
|
- Windows amd64: `.exe` (NSIS installer), `.msi`
|
|
- Linux arm64: `.deb`, `.rpm`, `.AppImage`
|
|
|
|
**Upload step (requires gogs_default network for amd64, host IP for arm64):**
|
|
```yaml
|
|
# amd64 upload step
|
|
upload-release:
|
|
image: curlimages/curl:latest
|
|
labels:
|
|
platform: linux/amd64
|
|
network_mode: gogs_default
|
|
secrets: [GOGS_TOKEN]
|
|
```
|
|
|
|
The `GOGS_TOKEN` Woodpecker secret must be created via the Woodpecker UI or API after
|
|
migration. The secret name stays `GOGS_TOKEN` for pipeline compatibility.
|
|
|
|
**Gitea Release API (replaces Gogs API — same endpoints, different container name):**
|
|
```bash
|
|
# Create release
|
|
POST http://gitea_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases
|
|
Authorization: token $GOGS_TOKEN
|
|
|
|
# Upload artifact
|
|
POST http://gitea_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases/{id}/assets
|
|
```
|
|
|
|
From the arm64 agent (local machine), use `http://172.0.0.29:3000/api/v1` instead.
|
|
|
|
---
|
|
|
|
## Multi-File Pipeline Support (Woodpecker 2.x)
|
|
|
|
Woodpecker 2.x supports multiple pipeline files in the `.woodpecker/` directory.
|
|
All `.yml` files are evaluated on every trigger; `when:` conditions control which
|
|
pipelines actually run.
|
|
|
|
Current files:
|
|
- `.woodpecker/test.yml` — runs on every push/PR
|
|
- `.woodpecker/release.yml` — runs on `v*` tags only
|
|
|
|
No DB config path switching needed (unlike Woodpecker 0.15.4).
|
|
|
|
---
|
|
|
|
## Webhook Configuration
|
|
|
|
**Woodpecker 2.x with Gitea OAuth2:**
|
|
|
|
After migration, Woodpecker 2.x registers webhooks automatically when a repo is
|
|
activated via the UI. No manual JWT-signed webhook setup required.
|
|
|
|
1. Log in at `http://172.0.0.29:8085` via Gitea OAuth2
|
|
2. Add repo `sarman/tftsr-devops_investigation`
|
|
3. Woodpecker creates webhook in Gitea automatically
|
|
|
|
---
|
|
|
|
## Branch Protection
|
|
|
|
Master branch is protected: all changes require a PR.
|
|
|
|
```sql
|
|
-- Gitea branch protection (via psql on gogs_postgres_db container)
|
|
-- Check protection
|
|
SELECT name, protected, require_pull_request FROM protect_branch WHERE repo_id=42;
|
|
|
|
-- Temporarily disable for urgent fixes (restore immediately after!)
|
|
UPDATE protect_branch SET protected=false WHERE repo_id=42 AND name='master';
|
|
-- ... push ...
|
|
UPDATE protect_branch SET protected=true, require_pull_request=true WHERE repo_id=42 AND name='master';
|
|
```
|
|
|
|
---
|
|
|
|
## Known Issues & Fixes
|
|
|
|
### Debian Multiarch Breaks arm64 Cross-Compile (`held broken packages`)
|
|
When using `rust:1.88-slim` (Debian Bookworm) with `dpkg --add-architecture arm64`, apt
|
|
resolves amd64 and arm64 simultaneously against the same mirror. The `binary-all` package
|
|
index is duplicated and certain `-dev` package pairs cannot be co-installed because they
|
|
don't declare `Multi-Arch: same`. This produces `E: Unable to correct problems, you have
|
|
held broken packages` and cannot be fixed by tweaking `sources.list` entries.
|
|
|
|
**Fix**: Use `ubuntu:22.04` as the container image. Ubuntu routes arm64 through
|
|
`ports.ubuntu.com/ubuntu-ports` — a separate mirror from `archive.ubuntu.com` (amd64).
|
|
There are no cross-arch index overlaps and the dependency resolver succeeds. Rust must be
|
|
installed manually via `rustup` since it is not pre-installed in the Ubuntu base image.
|
|
|
|
### Step Containers Cannot Reach `gitea_app`
|
|
Default Docker bridge containers cannot resolve `gitea_app` or reach `172.0.0.29:3000`
|
|
(host firewall). Fix: use `network_mode: gogs_default` in any step that needs Gitea
|
|
access. Requires `repo_trusted=1`.
|
|
|
|
### `CI=woodpecker` Rejected by Tauri CLI
|
|
Woodpecker sets `CI=woodpecker`; `cargo tauri build` expects a boolean. Fix: prefix with
|
|
`CI=true cargo tauri build`.
|
|
|
|
### Agent Stalls After Server Restart
|
|
After restarting the Woodpecker server, the agent may enter a loop cleaning up orphaned
|
|
containers and stop picking up new builds. Fix:
|
|
```bash
|
|
docker rm -f $(docker ps -aq --filter 'name=0_')
|
|
docker volume rm $(docker volume ls -q | grep '0_')
|
|
docker restart woodpecker_agent
|
|
```
|
|
|
|
### Windows DLL Export Ordinal Too Large
|
|
`/usr/bin/x86_64-w64-mingw32-ld: error: export ordinal too large: 106290`
|
|
|
|
Fix: `src-tauri/.cargo/config.toml`:
|
|
```toml
|
|
[target.x86_64-pc-windows-gnu]
|
|
rustflags = ["-C", "link-arg=-Wl,--exclude-all-symbols"]
|
|
```
|
|
|
|
### GOGS_TOKEN Secret Must Be Recreated After Migration
|
|
After migrating from Woodpecker 0.15.4 to 2.x, recreate the `GOGS_TOKEN` secret:
|
|
1. Log in to Gitea, create a new API token under Settings → Applications
|
|
2. In Woodpecker UI → Repository → Secrets, add secret `GOGS_TOKEN` with the token value
|
|
|
|
---
|
|
|
|
## Gitea PostgreSQL Access
|
|
|
|
```bash
|
|
docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name FROM repository;"
|
|
```
|
|
|
|
> Database name is `gogsdb` (unchanged from Gogs migration).
|
|
|
|
---
|
|
|
|
## Migration Notes (Gogs 0.14 → Gitea)
|
|
|
|
Gitea auto-migrates the Gogs PostgreSQL schema on first start. Users, repos, teams, and
|
|
issues are preserved. API tokens stored in the DB are also migrated but should be
|
|
regenerated for security.
|
|
|
|
Key changes after migration:
|
|
- Container name: `gogs_app` → `gitea_app`
|
|
- Config dir: `/data/gitea` (was `/data/gogs` inside container, same host volume)
|
|
- Repo dir: `gogs-repositories` → `gitea-repositories` (renamed on host during migration)
|
|
- OAuth2 provider: Gitea now supports OAuth2 (Woodpecker 2.x uses this for login)
|
|
- Woodpecker 2.x multi-file pipeline support enabled (no more single config file limitation)
|