tftsr-devops_investigation/docs/wiki/CICD-Pipeline.md
Shaun Arman d5e180740e
Some checks failed
Test / rust-test (push) Failing after 6s
Test / frontend-test (push) Failing after 54s
docs: remove all Gitea/Gogs/172.0.0.29 references; update to GitHub
Replace every remaining reference to the old Gitea infrastructure with the
new GitHub-hosted equivalents across all documentation, wiki pages, test
files, and historical ticket summaries.

- README.md: CI badge, clone URL, releases link, CI/CD section, project structure
- docs/wiki/CICD-Pipeline.md: full rewrite for GitHub Actions + ghcr.io
- docs/wiki/Home.md: CI badge, releases link, phase status, tech stack
- docs/wiki/Troubleshooting.md: rewrite CI troubleshooting for GitHub Actions
- docs/architecture/README.md: update CI/CD pipeline diagram
- AGENTS.md: CI/CD section, environment references
- PLAN.md: directory structure, pipeline table
- SECURITY_AUDIT.md: mark C3 and L4 findings as resolved
- ticket-git-cliff-changelog.md: workflow path updated
- tickets/ci-runner-speed-optimization.md: image registry updated
- 2026-hackathon_AgenticFeature.md: workflow path updated
- tests: workflow path assertions updated in all three test files
2026-06-01 16:18:34 -05:00

254 lines
9.8 KiB
Markdown

# CI/CD Pipeline
## Infrastructure
| Component | URL | Notes |
|-----------|-----|-------|
| GitHub | `https://github.com/msicie/apollo_nxt-trcaa` | Git server and CI/CD platform |
| GitHub Actions | Native to GitHub | Hosted runners (`ubuntu-latest`, `macos-latest`, `macos-13`) |
| ghcr.io | `ghcr.io/msicie/` | GitHub Container Registry for pre-baked CI images |
---
## Pre-baked Builder Images
CI build and test jobs use pre-baked Docker images pushed to `ghcr.io/msicie/`. These images bake in all system dependencies (Tauri libs, Node.js, Rust toolchain, cross-compilers) so that CI jobs skip package installation entirely.
| Image | Used by jobs | Contents |
|-------|-------------|----------|
| `ghcr.io/msicie/trcaa-linux-amd64:rust1.88-node22` | `rust-test`, `build-linux-amd64` | Rust 1.88 + rustfmt + clippy + Tauri amd64 libs + Node.js 22 |
| `ghcr.io/msicie/trcaa-windows-cross:rust1.88-node22` | `build-windows-amd64` | Rust 1.88 + mingw-w64 + NSIS + Node.js 22 |
| `ghcr.io/msicie/trcaa-linux-arm64:rust1.88-node22` | `build-linux-arm64` | Rust 1.88 + aarch64 cross-toolchain + arm64 multiarch libs + Node.js 22 |
**Rebuild triggers:** Rust toolchain version bump, webkit2gtk/gtk major version change, Node.js major version change.
**How to rebuild images:**
1. Trigger `build-images.yml` via Actions → Build CI Docker Images → Run workflow
2. Confirm all 3 images appear in `ghcr.io/msicie/`
3. Only then merge workflow changes that depend on the new image contents
---
## Cargo and npm Caching
All Rust and build jobs use `actions/cache@v4` to cache downloaded package artifacts.
**Cargo cache** (Rust jobs):
```yaml
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
```
**npm cache** (frontend and build jobs):
```yaml
- name: Cache npm
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
```
Cache keys for cross-compile jobs use a suffix to avoid collisions:
- Windows build: `${{ runner.os }}-cargo-windows-${{ hashFiles('**/Cargo.lock') }}`
- arm64 build: `${{ runner.os }}-cargo-arm64-${{ hashFiles('**/Cargo.lock') }}`
---
## Test Pipeline (`.github/workflows/test.yml`)
**Triggers:** Every push to `main`, `feature/**`, `bug/**`, `fix/**` branches; pull requests targeting `main`.
```
Jobs (run in parallel):
rust-test → cargo fmt --check
→ cargo clippy -- -D warnings
→ cargo test (64 tests)
frontend-test → npx tsc --noEmit
→ npm run test:run (Vitest tests)
```
**Docker image used:**
- `ghcr.io/msicie/trcaa-linux-amd64:rust1.88-node22` — both Rust and frontend steps
Job names `rust-test` and `frontend-test` must match exactly — these are the required status check names in branch protection on `main`.
---
## Release Pipeline (`.github/workflows/release.yml`)
**Triggers:** Push to `main` (auto-tag job), then release build/upload jobs run after `autotag` completes on `v*` tags.
Auto-tags are created by the `autotag` job using `git tag` + `git push`. Release jobs depend on `autotag` and run in parallel.
```
Jobs (run in parallel after autotag):
build-linux-amd64 → image: ghcr.io/msicie/trcaa-linux-amd64:rust1.88-node22
→ cargo tauri build (x86_64-unknown-linux-gnu)
→ {.deb, .rpm, .AppImage} uploaded to GitHub Release
→ fails fast if no Linux artifacts are produced
build-windows-amd64 → image: ghcr.io/msicie/trcaa-windows-cross:rust1.88-node22
→ cargo tauri build (x86_64-pc-windows-gnu) via mingw-w64
→ {.exe, .msi} uploaded to GitHub Release
→ fails fast if no Windows artifacts are produced
build-linux-arm64 → image: ghcr.io/msicie/trcaa-linux-arm64:rust1.88-node22
→ cargo tauri build (aarch64-unknown-linux-gnu)
→ {.deb, .rpm, .AppImage} uploaded to GitHub Release
→ fails fast if no Linux artifacts are produced
build-macos-arm64 → runs-on: macos-latest (Apple Silicon)
→ cargo tauri build (aarch64-apple-darwin) natively
→ {.dmg} uploaded to GitHub Release
→ existing same-name assets are deleted before upload (rerun-safe)
→ unsigned; after install run: xattr -cr /Applications/TRCAA.app
build-macos-intel → runs-on: macos-13 (Intel x86_64)
→ cargo tauri build (x86_64-apple-darwin) natively
→ {.dmg} uploaded to GitHub Release
```
**Windows cross-compile environment:**
```yaml
env:
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
OPENSSL_NO_VENDOR: "0"
OPENSSL_STATIC: "1"
```
**Artifacts per platform:**
- Linux amd64: `.deb`, `.rpm`, `.AppImage`
- Windows amd64: `.exe` (NSIS installer), `.msi`
- Linux arm64: `.deb`, `.rpm`, `.AppImage`
- macOS ARM64: `.dmg`
- macOS Intel: `.dmg`
---
## Build Images Workflow (`.github/workflows/build-images.yml`)
**Triggers:** Changes to `.docker/**` on `main`; manual `workflow_dispatch`.
Builds and pushes all three pre-baked CI images to `ghcr.io/msicie/`:
```
Jobs (all run on ubuntu-latest):
build-linux-amd64-image → .docker/Dockerfile.linux-amd64
→ ghcr.io/msicie/trcaa-linux-amd64:rust1.88-node22
build-windows-cross-image → .docker/Dockerfile.windows-cross
→ ghcr.io/msicie/trcaa-windows-cross:rust1.88-node22
build-linux-arm64-image → .docker/Dockerfile.linux-arm64
→ ghcr.io/msicie/trcaa-linux-arm64:rust1.88-node22
```
Authentication: `docker login ghcr.io -u ${{ github.actor }} --password-stdin <<< "${{ secrets.GITHUB_TOKEN }}"`. Requires `packages: write` permission.
---
## Branch Protection
`main` branch requires:
- All changes via PR
- 1 approving review
- CODEOWNER review (`@Shaun-Arman-VFK387_moto` and `@github-copilot`)
- Required status checks: `rust-test`, `frontend-test`
- `enforce_admins: false` — owner and admins can bypass with `gh pr merge --admin`
---
## Changelog Generation
Changelogs are generated automatically by **git-cliff** on every release.
Configuration lives in `cliff.toml` at the repo root.
### How it works
A `changelog` job in `release.yml` runs in parallel with the build jobs, immediately
after `autotag` completes:
1. Clones the full repo history with all tags (`--depth=2147483647` — git-cliff needs
every tag to compute version boundaries).
2. Downloads the git-cliff v2.7.0 static musl binary (~5 MB, no image change needed).
3. Runs `git-cliff --output CHANGELOG.md` to regenerate the full cumulative changelog.
4. Runs `git-cliff --latest --strip all` to produce release notes for the new tag only.
5. Updates the GitHub Release body with those notes via `gh release edit`.
6. Commits `CHANGELOG.md` to `main` with `[skip ci]` appended to the message.
The `[skip ci]` token prevents `release.yml` from re-triggering on the CHANGELOG commit.
7. Uploads `CHANGELOG.md` as a release asset (replaces any previous version).
### cliff.toml reference
| Setting | Value |
|---------|-------|
| `tag_pattern` | `v[0-9].*` |
| `ignore_tags` | `rc\|alpha\|beta` |
| `filter_unconventional` | `true` — non-conventional commits are dropped |
| Included types | `feat`, `fix`, `perf`, `docs`, `refactor` |
| Excluded types | `ci`, `chore`, `build`, `test`, `style` |
### Loop prevention
The `[skip ci]` suffix on the CHANGELOG commit message is recognised by GitHub Actions
and causes the workflow to be skipped for that push. Without it, the CHANGELOG commit
would trigger `release.yml` again, incrementing the patch version forever.
---
## 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 (see `Dockerfile.linux-arm64`). 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`.
### `CI=true` Required by Tauri CLI
GitHub Actions sets `CI=true` by default — `cargo tauri build` accepts this without
modification. Prefix the command explicitly if your runner environment is unusual:
```yaml
- run: CI=true cargo tauri build --target $TARGET
```
### 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"]
```
### Release Artifacts Not Uploaded
**Cause:** `GITHUB_TOKEN` needs `contents: write` permission in the workflow.
Verify the `release.yml` permissions block includes:
```yaml
permissions:
contents: write
```
### Runner Group Restrictions (Enterprise)
GitHub Enterprise organizations may restrict which repositories can use GitHub-hosted
runners. If jobs are stuck in queue, contact your org admin to add the repository to the
allowed runner group, or temporarily make the repo public for the bootstrap run.