ci: migrate pipelines to Woodpecker 2.x + Gitea
- Convert pipeline: map format → steps: list format (Woodpecker 2.x) - Add per-step labels for agent routing (platform: linux/amd64 / arm64) - Add native build-linux-arm64 step (routes to local arm64 agent) - Arm64 step self-clones via host IP (isolated workspace per agent) - Arm64 step uploads artifacts inline to Gitea API - Replace gogs_app → gitea_app in all clone/upload URLs - Remove Woodpecker 0.15.4 per-step platform routing workaround note - Update wiki: Gitea migration notes, new pipeline format docs, agent labels Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ac56851e4d
commit
c7b66e0820
@ -1,9 +1,11 @@
|
|||||||
---
|
---
|
||||||
# Release pipeline — triggered on v* tags
|
# Release pipeline — triggered on v* tags
|
||||||
# Agents:
|
# Agents:
|
||||||
# linux/amd64 → woodpecker_agent (native x86_64 on 172.0.0.29)
|
# linux/amd64 → woodpecker_agent (native x86_64 on 172.0.0.29, gogs_default network)
|
||||||
# linux/arm64 → woodpecker-agent.service on sarman's local arm64 machine (native)
|
# linux/arm64 → woodpecker-agent.service on sarman's local arm64 machine (direct IP access)
|
||||||
# macOS requires a separate Mac runner — not covered here.
|
#
|
||||||
|
# Note: amd64 steps share a workspace (same agent). The arm64 step runs on a separate
|
||||||
|
# agent with its own workspace and clones the repo directly via host IP.
|
||||||
|
|
||||||
clone:
|
clone:
|
||||||
git:
|
git:
|
||||||
@ -11,17 +13,19 @@ clone:
|
|||||||
network_mode: gogs_default
|
network_mode: gogs_default
|
||||||
commands:
|
commands:
|
||||||
- git init -b master
|
- git init -b master
|
||||||
- git remote add origin http://gogs_app:3000/sarman/tftsr-devops_investigation.git
|
- 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 fetch --depth=1 origin +refs/tags/${CI_COMMIT_TAG}:refs/tags/${CI_COMMIT_TAG}
|
||||||
- git checkout ${CI_COMMIT_TAG}
|
- git checkout ${CI_COMMIT_TAG}
|
||||||
|
|
||||||
pipeline:
|
steps:
|
||||||
build-linux-amd64:
|
- name: build-linux-amd64
|
||||||
image: rust:1.88-slim
|
image: rust:1.88-slim
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
environment:
|
environment:
|
||||||
TARGET: x86_64-unknown-linux-gnu
|
TARGET: x86_64-unknown-linux-gnu
|
||||||
when:
|
when:
|
||||||
event: tag
|
- event: tag
|
||||||
commands:
|
commands:
|
||||||
- apt-get update -qq && apt-get install -y -qq libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config curl perl
|
- apt-get update -qq && apt-get install -y -qq libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config curl perl
|
||||||
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
||||||
@ -33,8 +37,10 @@ pipeline:
|
|||||||
- mkdir -p artifacts/linux-amd64
|
- mkdir -p artifacts/linux-amd64
|
||||||
- find src-tauri/target/$TARGET/release/bundle -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" | xargs -I{} cp {} artifacts/linux-amd64/
|
- find src-tauri/target/$TARGET/release/bundle -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" | xargs -I{} cp {} artifacts/linux-amd64/
|
||||||
|
|
||||||
build-windows-amd64:
|
- name: build-windows-amd64
|
||||||
image: rust:1.88-slim
|
image: rust:1.88-slim
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
environment:
|
environment:
|
||||||
TARGET: x86_64-pc-windows-gnu
|
TARGET: x86_64-pc-windows-gnu
|
||||||
CC_x86_64_pc_windows_gnu: x86_64-w64-mingw32-gcc
|
CC_x86_64_pc_windows_gnu: x86_64-w64-mingw32-gcc
|
||||||
@ -42,7 +48,7 @@ pipeline:
|
|||||||
AR_x86_64_pc_windows_gnu: x86_64-w64-mingw32-ar
|
AR_x86_64_pc_windows_gnu: x86_64-w64-mingw32-ar
|
||||||
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: x86_64-w64-mingw32-gcc
|
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: x86_64-w64-mingw32-gcc
|
||||||
when:
|
when:
|
||||||
event: tag
|
- event: tag
|
||||||
commands:
|
commands:
|
||||||
- apt-get update -qq && apt-get install -y -qq mingw-w64 curl nsis perl make
|
- apt-get update -qq && apt-get install -y -qq mingw-w64 curl nsis perl make
|
||||||
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
||||||
@ -54,23 +60,65 @@ pipeline:
|
|||||||
- mkdir -p artifacts/windows-amd64
|
- mkdir -p artifacts/windows-amd64
|
||||||
- find src-tauri/target/$TARGET/release/bundle -name "*.exe" -o -name "*.msi" | xargs -I{} cp {} artifacts/windows-amd64/ 2>/dev/null || true
|
- find src-tauri/target/$TARGET/release/bundle -name "*.exe" -o -name "*.msi" | xargs -I{} cp {} artifacts/windows-amd64/ 2>/dev/null || true
|
||||||
|
|
||||||
# NOTE: linux/arm64 is built locally via 'make release-arm64 GOGS_TOKEN=<token>'
|
- name: build-linux-arm64
|
||||||
# Woodpecker 0.15.4 does not support per-step agent platform routing —
|
image: rust:1.88-slim
|
||||||
# when: platform: is evaluated at compile time (server=amd64) and drops the step.
|
labels:
|
||||||
|
platform: linux/arm64
|
||||||
|
environment:
|
||||||
|
TARGET: aarch64-unknown-linux-gnu
|
||||||
|
when:
|
||||||
|
- event: tag
|
||||||
|
secrets: [GOGS_TOKEN]
|
||||||
|
commands:
|
||||||
|
- apt-get update -qq && apt-get install -y -qq libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config curl perl git
|
||||||
|
- curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
||||||
|
- apt-get install -y nodejs
|
||||||
|
# Clone repo directly (arm64 agent is on local machine, not on gogs_default network)
|
||||||
|
- git init -b master
|
||||||
|
- git remote add origin http://172.0.0.29: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}
|
||||||
|
- npm ci --legacy-peer-deps
|
||||||
|
- rustup target add $TARGET
|
||||||
|
- cargo install tauri-cli --version "^2" --locked
|
||||||
|
- CI=true cargo tauri build --target $TARGET
|
||||||
|
- mkdir -p artifacts/linux-arm64
|
||||||
|
- find src-tauri/target/$TARGET/release/bundle -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" | xargs -I{} cp {} artifacts/linux-arm64/
|
||||||
|
# Upload arm64 artifacts inline (uses host IP, accessible from local arm64 machine)
|
||||||
|
- |
|
||||||
|
TAG=${CI_COMMIT_TAG}
|
||||||
|
REPO=${CI_REPO}
|
||||||
|
API="http://172.0.0.29:3000/api/v1"
|
||||||
|
curl -sf -X POST "$API/repos/$REPO/releases" \
|
||||||
|
-H "Authorization: token $GOGS_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"tag_name\":\"$TAG\",\"name\":\"TFTSR $TAG\",\"body\":\"Release $TAG\",\"draft\":false}" || true
|
||||||
|
RELEASE_ID=$(curl -sf "$API/repos/$REPO/releases/tags/$TAG" \
|
||||||
|
-H "Authorization: token $GOGS_TOKEN" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
||||||
|
echo "Release ID: $RELEASE_ID"
|
||||||
|
for f in artifacts/linux-arm64/*; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
echo "Uploading $f..."
|
||||||
|
curl -sf -X POST "$API/repos/$REPO/releases/$RELEASE_ID/assets" \
|
||||||
|
-H "Authorization: token $GOGS_TOKEN" \
|
||||||
|
-F "attachment=@$f;filename=$(basename $f)" && echo "OK" || echo "Upload failed: $f"
|
||||||
|
done
|
||||||
|
|
||||||
upload-release:
|
- name: upload-release
|
||||||
image: curlimages/curl:latest
|
image: curlimages/curl:latest
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
network_mode: gogs_default
|
network_mode: gogs_default
|
||||||
when:
|
when:
|
||||||
event: tag
|
- event: tag
|
||||||
secrets: [GOGS_TOKEN]
|
secrets: [GOGS_TOKEN]
|
||||||
commands:
|
commands:
|
||||||
- |
|
- |
|
||||||
TAG=${CI_COMMIT_TAG}
|
TAG=${CI_COMMIT_TAG}
|
||||||
REPO=${CI_REPO}
|
REPO=${CI_REPO}
|
||||||
API="http://gogs_app:3000/api/v1"
|
API="http://gitea_app:3000/api/v1"
|
||||||
|
|
||||||
# Create release
|
# Create release (idempotent — arm64 step may have already created it)
|
||||||
curl -sf -X POST "$API/repos/$REPO/releases" \
|
curl -sf -X POST "$API/repos/$REPO/releases" \
|
||||||
-H "Authorization: token $GOGS_TOKEN" \
|
-H "Authorization: token $GOGS_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
@ -81,8 +129,8 @@ pipeline:
|
|||||||
-H "Authorization: token $GOGS_TOKEN" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
-H "Authorization: token $GOGS_TOKEN" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
|
||||||
echo "Release ID: $RELEASE_ID"
|
echo "Release ID: $RELEASE_ID"
|
||||||
|
|
||||||
# Upload all available artifacts
|
# Upload linux-amd64 and windows-amd64 artifacts
|
||||||
for dir in artifacts/linux-amd64 artifacts/linux-arm64 artifacts/windows-amd64; do
|
for dir in artifacts/linux-amd64 artifacts/windows-amd64; do
|
||||||
[ -d "$dir" ] || continue
|
[ -d "$dir" ] || continue
|
||||||
for f in "$dir"/*; do
|
for f in "$dir"/*; do
|
||||||
[ -f "$f" ] || continue
|
[ -f "$f" ] || continue
|
||||||
|
|||||||
@ -4,35 +4,35 @@ clone:
|
|||||||
image: woodpeckerci/plugin-git
|
image: woodpeckerci/plugin-git
|
||||||
network_mode: gogs_default
|
network_mode: gogs_default
|
||||||
environment:
|
environment:
|
||||||
- CI_REPO_CLONE_URL=http://gogs_app:3000/sarman/tftsr-devops_investigation.git
|
- CI_REPO_CLONE_URL=http://gitea_app:3000/sarman/tftsr-devops_investigation.git
|
||||||
|
|
||||||
pipeline:
|
steps:
|
||||||
rust-fmt-check:
|
- name: rust-fmt-check
|
||||||
image: rust:1.88-slim
|
image: rust:1.88-slim
|
||||||
commands:
|
commands:
|
||||||
- rustup component add rustfmt
|
- rustup component add rustfmt
|
||||||
- cargo fmt --manifest-path src-tauri/Cargo.toml --check
|
- cargo fmt --manifest-path src-tauri/Cargo.toml --check
|
||||||
|
|
||||||
rust-clippy:
|
- name: rust-clippy
|
||||||
image: rust:1.88-slim
|
image: rust:1.88-slim
|
||||||
commands:
|
commands:
|
||||||
- apt-get update -qq && apt-get install -y -qq libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config perl
|
- apt-get update -qq && apt-get install -y -qq libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config perl
|
||||||
- rustup component add clippy
|
- rustup component add clippy
|
||||||
- cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings
|
- cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings
|
||||||
|
|
||||||
rust-tests:
|
- name: rust-tests
|
||||||
image: rust:1.88-slim
|
image: rust:1.88-slim
|
||||||
commands:
|
commands:
|
||||||
- apt-get update -qq && apt-get install -y -qq libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config perl
|
- apt-get update -qq && apt-get install -y -qq libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config perl
|
||||||
- cargo test --manifest-path src-tauri/Cargo.toml
|
- cargo test --manifest-path src-tauri/Cargo.toml
|
||||||
|
|
||||||
frontend-typecheck:
|
- name: frontend-typecheck
|
||||||
image: node:22-alpine
|
image: node:22-alpine
|
||||||
commands:
|
commands:
|
||||||
- npm ci --legacy-peer-deps
|
- npm ci --legacy-peer-deps
|
||||||
- npx tsc --noEmit
|
- npx tsc --noEmit
|
||||||
|
|
||||||
frontend-tests:
|
- name: frontend-tests
|
||||||
image: node:22-alpine
|
image: node:22-alpine
|
||||||
commands:
|
commands:
|
||||||
- npm ci --legacy-peer-deps
|
- npm ci --legacy-peer-deps
|
||||||
|
|||||||
@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
| Component | URL | Notes |
|
| Component | URL | Notes |
|
||||||
|-----------|-----|-------|
|
|-----------|-----|-------|
|
||||||
| Gogs | `https://gogs.tftsr.com` / `http://172.0.0.29:3000` | Git server, version 0.14 |
|
| 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` | v0.15.4 |
|
| Woodpecker CI (direct) | `http://172.0.0.29:8084` | v2.x |
|
||||||
| Woodpecker CI (proxy) | `http://172.0.0.29:8085` | nginx with custom login page |
|
| Woodpecker CI (proxy) | `http://172.0.0.29:8085` | nginx reverse proxy |
|
||||||
| PostgreSQL (Gogs DB) | Container: `gogs_postgres_db` | DB: `gogsdb`, User: `gogs` |
|
| PostgreSQL (Gitea DB) | Container: `gogs_postgres_db` | DB: `gogsdb`, User: `gogs` |
|
||||||
|
|
||||||
### CI Agents
|
### CI Agents
|
||||||
|
|
||||||
@ -17,6 +17,10 @@
|
|||||||
| `woodpecker-agent` (systemd) | `linux/arm64` | sarman's local machine | Native aarch64 — arm64 release builds |
|
| `woodpecker-agent` (systemd) | `linux/arm64` | sarman's local machine | Native aarch64 — arm64 release builds |
|
||||||
| `woodpecker_agent_arm64` (Docker) | `linux/arm64` | 172.0.0.29 | QEMU fallback — kept as backup |
|
| `woodpecker_agent_arm64` (Docker) | `linux/arm64` | 172.0.0.29 | QEMU fallback — kept as backup |
|
||||||
|
|
||||||
|
Agent labels configured via `WOODPECKER_LABELS`:
|
||||||
|
- Docker agents: `WOODPECKER_LABELS=platform=linux/amd64` (or arm64)
|
||||||
|
- Local systemd agent: `~/.config/woodpecker-agent/config.env` → `WOODPECKER_LABELS=platform=linux/arm64`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Test Pipeline (`.woodpecker/test.yml`)
|
## Test Pipeline (`.woodpecker/test.yml`)
|
||||||
@ -36,23 +40,24 @@ Pipeline steps:
|
|||||||
- `rust:1.88-slim` — Rust steps (minimum for cookie_store + time + darling)
|
- `rust:1.88-slim` — Rust steps (minimum for cookie_store + time + darling)
|
||||||
- `node:22-alpine` — Frontend steps
|
- `node:22-alpine` — Frontend steps
|
||||||
|
|
||||||
**Pipeline YAML format (Woodpecker 0.15.4 — legacy MAP format):**
|
**Pipeline YAML format (Woodpecker 2.x — steps list format):**
|
||||||
```yaml
|
```yaml
|
||||||
clone:
|
clone:
|
||||||
git:
|
git:
|
||||||
image: woodpeckerci/plugin-git
|
image: woodpeckerci/plugin-git
|
||||||
network_mode: gogs_default # requires repo_trusted=1
|
network_mode: gogs_default # requires repo_trusted=1
|
||||||
environment:
|
environment:
|
||||||
- CI_REPO_CLONE_URL=http://gogs_app:3000/sarman/tftsr-devops_investigation.git
|
- CI_REPO_CLONE_URL=http://gitea_app:3000/sarman/tftsr-devops_investigation.git
|
||||||
|
|
||||||
pipeline:
|
steps:
|
||||||
step-name: # KEY = step name (MAP, not list!)
|
- name: step-name # LIST format (- name:)
|
||||||
image: rust:1.88-slim
|
image: rust:1.88-slim
|
||||||
commands:
|
commands:
|
||||||
- cargo test
|
- cargo test
|
||||||
```
|
```
|
||||||
|
|
||||||
> ⚠️ **Do NOT** use the newer `steps:` list format — Woodpecker 0.15.4 uses the Drone-legacy map format. Using `steps:` causes "Invalid or missing pipeline section" error.
|
> ⚠️ Woodpecker 2.x uses the `steps:` list format. The legacy `pipeline:` map format from
|
||||||
|
> Woodpecker 0.15.4 is no longer supported.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -60,23 +65,40 @@ pipeline:
|
|||||||
|
|
||||||
**Triggers:** Git tags matching `v*`
|
**Triggers:** Git tags matching `v*`
|
||||||
|
|
||||||
**Active config path:** Woodpecker DB must have `repo_config_path = .woodpecker/release.yml` when the tag is pushed. Switch back to `test.yml` after tagging to restore PR/push CI.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
Pipeline steps:
|
Pipeline steps:
|
||||||
1. clone → alpine/git with explicit tag fetch + checkout
|
1. clone (amd64 workspace) → alpine/git with explicit tag fetch + checkout
|
||||||
2. build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu)
|
2. build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu)
|
||||||
→ artifacts/linux-amd64/{.deb, .rpm, .AppImage}
|
→ artifacts/linux-amd64/{.deb, .rpm, .AppImage}
|
||||||
3. build-windows-amd64 → cargo tauri build (x86_64-pc-windows-gnu)
|
3. build-windows-amd64 → cargo tauri build (x86_64-pc-windows-gnu)
|
||||||
→ artifacts/windows-amd64/{.exe, .msi}
|
→ artifacts/windows-amd64/{.exe, .msi}
|
||||||
4. upload-release → Create Gogs release + upload all artifacts
|
4. build-linux-arm64 → cargo tauri build (aarch64-unknown-linux-gnu)
|
||||||
|
→ artifacts/linux-arm64/{.deb, .rpm, .AppImage}
|
||||||
linux/arm64 (manual): make release-arm64 GOGS_TOKEN=<token> (see below)
|
→ uploads arm64 artifacts inline to Gitea release
|
||||||
|
5. upload-release → Create Gitea release + upload amd64 + windows artifacts
|
||||||
```
|
```
|
||||||
|
|
||||||
**Clone override (release.yml):**
|
**Per-step agent routing (Woodpecker 2.x labels):**
|
||||||
|
|
||||||
Release builds use `alpine/git` with explicit commands because `woodpeckerci/plugin-git:latest` uses `git switch` which fails on tag refs:
|
```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 (release.yml — amd64 workspace):**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
clone:
|
clone:
|
||||||
@ -85,7 +107,7 @@ clone:
|
|||||||
network_mode: gogs_default
|
network_mode: gogs_default
|
||||||
commands:
|
commands:
|
||||||
- git init -b master
|
- git init -b master
|
||||||
- git remote add origin http://gogs_app:3000/sarman/tftsr-devops_investigation.git
|
- 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 fetch --depth=1 origin +refs/tags/${CI_COMMIT_TAG}:refs/tags/${CI_COMMIT_TAG}
|
||||||
- git checkout ${CI_COMMIT_TAG}
|
- git checkout ${CI_COMMIT_TAG}
|
||||||
```
|
```
|
||||||
@ -101,130 +123,69 @@ environment:
|
|||||||
**Artifacts per platform:**
|
**Artifacts per platform:**
|
||||||
- Linux amd64: `.deb`, `.rpm`, `.AppImage`
|
- Linux amd64: `.deb`, `.rpm`, `.AppImage`
|
||||||
- Windows amd64: `.exe` (NSIS installer), `.msi`
|
- Windows amd64: `.exe` (NSIS installer), `.msi`
|
||||||
- Linux arm64: `.deb`, `.rpm`, `.AppImage` — built via `make release-arm64` (see below)
|
- Linux arm64: `.deb`, `.rpm`, `.AppImage`
|
||||||
|
|
||||||
**Linux arm64 build (Woodpecker 0.15.4 workaround):**
|
**Upload step (requires gogs_default network for amd64, host IP for arm64):**
|
||||||
|
|
||||||
Woodpecker 0.15.4 evaluates `when: platform:` at compile time against the server's
|
|
||||||
platform (amd64), dropping arm64 steps before any agent can claim them. Per-step
|
|
||||||
agent routing is a Woodpecker 2.x feature.
|
|
||||||
|
|
||||||
To build and upload arm64 artifacts from the local aarch64 machine:
|
|
||||||
```bash
|
|
||||||
# On the local arm64 machine (Fedora Asahi 42)
|
|
||||||
cd ~/Documents/tftsr-devops_investigation
|
|
||||||
make release-arm64 TAG=v0.1.0-alpha GOGS_TOKEN=<bearer_token>
|
|
||||||
```
|
|
||||||
|
|
||||||
`make build-arm64` runs the full Tauri build inside a `rust:1.88-slim` ARM64 Docker
|
|
||||||
container. `make upload-arm64` uploads the resulting artifacts to the Gogs release.
|
|
||||||
|
|
||||||
**Important:** Artifacts must be written to the **workspace** (relative paths like `artifacts/linux-amd64/`), not to absolute paths like `/artifacts/`. Only the workspace is shared between pipeline steps via Docker volume.
|
|
||||||
|
|
||||||
**Upload step (requires gogs_default network):**
|
|
||||||
```yaml
|
```yaml
|
||||||
|
# amd64 upload step
|
||||||
upload-release:
|
upload-release:
|
||||||
image: curlimages/curl:latest
|
image: curlimages/curl:latest
|
||||||
network_mode: gogs_default # host firewall blocks default bridge from reaching Gogs API
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
|
network_mode: gogs_default
|
||||||
secrets: [GOGS_TOKEN]
|
secrets: [GOGS_TOKEN]
|
||||||
```
|
```
|
||||||
|
|
||||||
The `GOGS_TOKEN` Woodpecker secret is inserted into the DB:
|
The `GOGS_TOKEN` Woodpecker secret must be created via the Woodpecker UI or API after
|
||||||
```python
|
migration. The secret name stays `GOGS_TOKEN` for pipeline compatibility.
|
||||||
conn.execute("""
|
|
||||||
INSERT INTO secrets (secret_repo_id, secret_name, secret_value, secret_images, secret_events, secret_skip_verify, secret_conceal)
|
|
||||||
VALUES (1, 'GOGS_TOKEN', '<bearer_token>', '', 'tag', 0, 1)
|
|
||||||
""")
|
|
||||||
```
|
|
||||||
|
|
||||||
**Gogs Release API:**
|
**Gitea Release API (replaces Gogs API — same endpoints, different container name):**
|
||||||
```bash
|
```bash
|
||||||
# Create release
|
# Create release
|
||||||
POST http://gogs_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases
|
POST http://gitea_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases
|
||||||
Authorization: token $GOGS_TOKEN
|
Authorization: token $GOGS_TOKEN
|
||||||
|
|
||||||
# Upload artifact
|
# Upload artifact
|
||||||
POST http://gogs_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases/{id}/assets
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Switching Between Test and Release Config
|
## Multi-File Pipeline Support (Woodpecker 2.x)
|
||||||
|
|
||||||
Woodpecker 0.15.4 supports only **one config file per repo**. The workflow:
|
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.
|
||||||
|
|
||||||
```bash
|
Current files:
|
||||||
# For regular pushes/PRs — use test pipeline
|
- `.woodpecker/test.yml` — runs on every push/PR
|
||||||
python3 -c "conn.execute(\"UPDATE repos SET repo_config_path='.woodpecker/test.yml'\")"
|
- `.woodpecker/release.yml` — runs on `v*` tags only
|
||||||
|
|
||||||
# Before pushing a release tag — switch to release pipeline
|
No DB config path switching needed (unlike Woodpecker 0.15.4).
|
||||||
python3 -c "conn.execute(\"UPDATE repos SET repo_config_path='.woodpecker/release.yml'\")"
|
|
||||||
git tag -a v1.0.0 -m "Release v1.0.0"
|
|
||||||
git push origin v1.0.0
|
|
||||||
# → Switch back to test.yml after build starts
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Webhook Configuration
|
## Webhook Configuration
|
||||||
|
|
||||||
**Hook ID:** 9 (in Gogs, `http://gogs.tftsr.com`)
|
**Woodpecker 2.x with Gitea OAuth2:**
|
||||||
**Events:** `create`, `push`, `pull_request`
|
|
||||||
**URL:** `http://172.0.0.29:8084/hook?access_token=<JWT>`
|
|
||||||
|
|
||||||
**JWT signing:**
|
After migration, Woodpecker 2.x registers webhooks automatically when a repo is
|
||||||
- Algorithm: HS256
|
activated via the UI. No manual JWT-signed webhook setup required.
|
||||||
- Secret: `repo_hash` from Woodpecker DB (`dK8zFWtAu67qfKd3Et6N8LptqTmedumJ`)
|
|
||||||
- Payload: `{"text":"sarman/tftsr-devops_investigation","type":"hook","iat":<timestamp>}`
|
|
||||||
|
|
||||||
**Regenerate JWT when stale:**
|
1. Log in at `http://172.0.0.29:8085` via Gitea OAuth2
|
||||||
```python
|
2. Add repo `sarman/tftsr-devops_investigation`
|
||||||
import base64, hmac, hashlib, json, time
|
3. Woodpecker creates webhook in Gitea automatically
|
||||||
|
|
||||||
def b64url(data):
|
|
||||||
if isinstance(data, str): data = data.encode()
|
|
||||||
return base64.urlsafe_b64encode(data).rstrip(b'=').decode()
|
|
||||||
|
|
||||||
header = b64url(json.dumps({'alg':'HS256','typ':'JWT'}, separators=(',',':')))
|
|
||||||
payload = b64url(json.dumps({'text':'sarman/tftsr-devops_investigation','type':'hook','iat':int(time.time())}, separators=(',',':')))
|
|
||||||
msg = f'{header}.{payload}'
|
|
||||||
sig = hmac.new(b'dK8zFWtAu67qfKd3Et6N8LptqTmedumJ', msg.encode(), hashlib.sha256).digest()
|
|
||||||
print(f'{msg}.{b64url(sig)}')
|
|
||||||
```
|
|
||||||
|
|
||||||
Then update the webhook in Gogs via API:
|
|
||||||
```bash
|
|
||||||
curl -X DELETE http://172.0.0.29:3000/api/v1/repos/sarman/tftsr-devops_investigation/hooks/<old_id>
|
|
||||||
curl -X POST http://172.0.0.29:3000/api/v1/repos/sarman/tftsr-devops_investigation/hooks \
|
|
||||||
-H "Authorization: token <bearer_token>" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"type":"gogs","config":{"url":"http://172.0.0.29:8084/hook?access_token=<NEW_JWT>","content_type":"json","secret":"af5dc60e0984f2680d0969f4a087e7100a4ece7e"},"events":["push","pull_request","create"],"active":true}'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Woodpecker DB State
|
|
||||||
|
|
||||||
SQLite at `/docker_mounts/woodpecker/data/woodpecker.sqlite` (on host `172.0.0.29`).
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Verify config
|
|
||||||
SELECT user_token IS NOT NULL AND user_token != '' AS token_set FROM users WHERE user_login='sarman';
|
|
||||||
|
|
||||||
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' (or release.yml during release)
|
|
||||||
-- repo_hash='dK8zFWtAu67qfKd3Et6N8LptqTmedumJ'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Branch Protection
|
## Branch Protection
|
||||||
|
|
||||||
Master branch is protected: all changes require a PR. Direct pushes are blocked.
|
Master branch is protected: all changes require a PR.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
|
-- Gitea branch protection (via psql on gogs_postgres_db container)
|
||||||
-- Check protection
|
-- Check protection
|
||||||
SELECT name, protected, require_pull_request FROM protect_branch WHERE repo_id=42;
|
SELECT name, protected, require_pull_request FROM protect_branch WHERE repo_id=42;
|
||||||
|
|
||||||
@ -234,71 +195,63 @@ UPDATE protect_branch SET protected=false WHERE repo_id=42 AND name='master';
|
|||||||
UPDATE protect_branch SET protected=true, require_pull_request=true WHERE repo_id=42 AND name='master';
|
UPDATE protect_branch SET protected=true, require_pull_request=true WHERE repo_id=42 AND name='master';
|
||||||
```
|
```
|
||||||
|
|
||||||
> Gogs 0.14 does **not** enforce required CI status checks before merging. Only `require_pull_request=true` is supported.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Known Issues & Fixes
|
## Known Issues & Fixes
|
||||||
|
|
||||||
### Webhook JWT Must Use `?access_token=`
|
### Step Containers Cannot Reach `gitea_app`
|
||||||
`token.ParseRequest()` in Woodpecker 0.15.4 does **not** read `?token=` URL params. Use `?access_token=<JWT>` instead.
|
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
|
||||||
### Directory-Based Config Not Supported
|
access. Requires `repo_trusted=1`.
|
||||||
Woodpecker 0.15.4 only supports a **single config file**. Multi-file pipelines require v2.x+.
|
|
||||||
|
|
||||||
### Empty Clone URL in Push Events
|
|
||||||
Woodpecker 0.15.4's `go-gogs-client` `PayloadRepo` struct lacks `CloneURL`, so `build_remote` is always empty. Fix: set `CI_REPO_CLONE_URL` in the clone step environment.
|
|
||||||
|
|
||||||
### Step Containers Cannot Reach `gogs_app`
|
|
||||||
Default Docker bridge containers cannot resolve `gogs_app` or reach `172.0.0.29:3000` (host firewall). Fix: use `network_mode: gogs_default` in any step that needs Gogs access. Requires `repo_trusted=1`.
|
|
||||||
|
|
||||||
### `CI=woodpecker` Rejected by Tauri CLI
|
### `CI=woodpecker` Rejected by Tauri CLI
|
||||||
Woodpecker sets `CI=woodpecker`; `cargo tauri build` expects a boolean. Fix: prefix with `CI=true cargo tauri build`.
|
Woodpecker sets `CI=woodpecker`; `cargo tauri build` expects a boolean. Fix: prefix with
|
||||||
|
`CI=true cargo tauri build`.
|
||||||
|
|
||||||
### Agent Stalls After Server Restart
|
### 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:
|
After restarting the Woodpecker server, the agent may enter a loop cleaning up orphaned
|
||||||
|
containers and stop picking up new builds. Fix:
|
||||||
```bash
|
```bash
|
||||||
# Kill orphan containers and volumes
|
|
||||||
docker rm -f $(docker ps -aq --filter 'name=0_')
|
docker rm -f $(docker ps -aq --filter 'name=0_')
|
||||||
docker volume rm $(docker volume ls -q | grep '0_')
|
docker volume rm $(docker volume ls -q | grep '0_')
|
||||||
# Restart agent
|
|
||||||
docker restart woodpecker_agent
|
docker restart woodpecker_agent
|
||||||
```
|
```
|
||||||
|
|
||||||
### Per-Step Agent Platform Routing Not Supported
|
|
||||||
Woodpecker 0.15.4 evaluates `when: platform:` conditions at pipeline compile time
|
|
||||||
against the **server's** platform (amd64). Steps filtered by platform are dropped
|
|
||||||
before any agent can claim them, so arm64 steps never reach the arm64 agent.
|
|
||||||
|
|
||||||
The `platform:` step-level key (e.g. `platform: linux/arm64`) is treated as a plugin
|
|
||||||
attribute and causes `Cannot configure both commands and custom attributes [platform]`.
|
|
||||||
|
|
||||||
Workaround: build arm64 artifacts locally via `make release-arm64`. This is fixed in
|
|
||||||
Woodpecker 2.x which supports proper per-step label-based agent routing.
|
|
||||||
|
|
||||||
### Windows DLL Export Ordinal Too Large
|
### Windows DLL Export Ordinal Too Large
|
||||||
`/usr/bin/x86_64-w64-mingw32-ld: error: export ordinal too large: 106290`
|
`/usr/bin/x86_64-w64-mingw32-ld: error: export ordinal too large: 106290`
|
||||||
|
|
||||||
MinGW's `ld` auto-exports ALL public Rust symbols into the DLL export table. With a
|
Fix: `src-tauri/.cargo/config.toml`:
|
||||||
large dependency tree (~106k symbols), this exceeds the 65,535 PE ordinal limit.
|
|
||||||
|
|
||||||
Fix: `src-tauri/.cargo/config.toml` tells `ld` to suppress auto-export:
|
|
||||||
```toml
|
```toml
|
||||||
[target.x86_64-pc-windows-gnu]
|
[target.x86_64-pc-windows-gnu]
|
||||||
rustflags = ["-C", "link-arg=-Wl,--exclude-all-symbols"]
|
rustflags = ["-C", "link-arg=-Wl,--exclude-all-symbols"]
|
||||||
```
|
```
|
||||||
The desktop `main.exe` links against `rlib` (static), so the cdylib export table is
|
|
||||||
unused at runtime. An empty export table is valid for a DLL.
|
|
||||||
|
|
||||||
### Gogs OAuth2 Limitation
|
### GOGS_TOKEN Secret Must Be Recreated After Migration
|
||||||
Gogs 0.14 has no OAuth2 provider support, blocking upgrade to Woodpecker 2.x.
|
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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Gogs PostgreSQL Access
|
## Gitea PostgreSQL Access
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name FROM repository;"
|
docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name FROM repository;"
|
||||||
```
|
```
|
||||||
|
|
||||||
> Database name is `gogsdb`, not `gogs`.
|
> 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)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user