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:
Shaun Arman 2026-03-29 13:28:49 -05:00
parent ac56851e4d
commit c7b66e0820
3 changed files with 176 additions and 175 deletions

View File

@ -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

View File

@ -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

View File

@ -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)