diff --git a/README.md b/README.md index b9f41200..3d914170 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ A structured, AI-backed desktop tool for IT incident triage, 5-Whys root cause a Built with **Tauri 2** (Rust + WebView), **React 18**, **TypeScript**, and **SQLCipher AES-256** encrypted storage. +**CI status:** ![CI](http://172.0.0.29:3000/sarman/tftsr-devops_investigation/badges/master/status.svg) — all checks green (rustfmt · clippy · 64 Rust tests · tsc · vitest) + --- ## Features @@ -60,16 +62,24 @@ sudo dnf install -y \ libsoup3-devel openssl-devel librsvg2-devel ``` +### System Libraries (Linux — Debian/Ubuntu) + +```bash +sudo apt-get install -y \ + libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev \ + libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config +``` + ### Toolchain ```bash -# Rust (install via rustup) +# Rust (minimum 1.88 — required by cookie_store, time, darling) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.cargo/env # Node.js 22+ (via your package manager) # Verify: -rustc --version # 1.82+ +rustc --version # 1.88+ node --version # 22+ ``` @@ -78,12 +88,13 @@ node --version # 22+ ## Getting Started ```bash -# Clone and install dependencies -git clone +# Clone +git clone https://gogs.tftsr.com/sarman/tftsr-devops_investigation.git cd tftsr-devops_investigation -npm install +npm install --legacy-peer-deps # Development mode (hot reload) +source ~/.cargo/env cargo tauri dev # Production build @@ -93,6 +104,19 @@ cargo tauri build --- +## Releases + +Pre-built installers are attached to each [tagged release](https://gogs.tftsr.com/sarman/tftsr-devops_investigation/releases): + +| Platform | Format | Notes | +|---|---|---| +| Linux amd64 | `.deb`, `.rpm`, `.AppImage` | Standard package or universal AppImage | +| Windows amd64 | `.exe` (NSIS), `.msi` | From cross-compile via mingw-w64 | +| Linux arm64 | `.deb`, `.AppImage` | Built on native arm64 runner (QEMU emulation available) | +| macOS | — | Requires macOS runner — build locally | + +--- + ## AI Provider Setup Launch the app and go to **Settings → AI Providers** to add a provider: @@ -151,11 +175,12 @@ tftsr/ │ ├── lib/ # tauriCommands.ts (typed IPC wrappers), domainPrompts.ts │ └── styles/ # Tailwind + CSS custom properties ├── tests/ -│ ├── unit/ # Vitest unit tests (PII commands, session store, settings store) -│ └── e2e/ # WebdriverIO + tauri-driver E2E test skeletons +│ ├── unit/ # Vitest unit tests (PII, session store, settings store) +│ └── e2e/ # WebdriverIO + tauri-driver E2E skeletons +├── docs/wiki/ # Source of truth for Gogs wiki (auto-synced by CI) ├── .woodpecker/ -│ ├── test.yml # CI: rustfmt, clippy, cargo test, tsc, vitest -│ └── release.yml # Release: multi-platform builds → Gogs artifacts +│ ├── test.yml # CI: rustfmt · clippy · cargo test · tsc · vitest (every push/PR) +│ └── release.yml # Release: linux/amd64 + windows/amd64 builds → Gogs release artifacts └── cli/ # Standalone CLI wrapper (tftsr-cli) ``` @@ -164,16 +189,16 @@ tftsr/ ## Testing ```bash -# Unit tests (Vitest) +# Unit tests (Vitest) — 13/13 passing npm run test:run -# Unit tests with coverage +# Frontend coverage npm run test:coverage # TypeScript type check npx tsc --noEmit -# Rust checks +# Rust checks — 64/64 tests passing cargo check --manifest-path src-tauri/Cargo.toml cargo test --manifest-path src-tauri/Cargo.toml @@ -185,14 +210,18 @@ TAURI_BINARY_PATH=./src-tauri/target/release/tftsr npm run test:e2e ## CI/CD — Woodpecker CI -The project uses **Woodpecker CI** connected to the Gogs server at `172.0.0.29:3000`. +The project uses **Woodpecker CI v0.15.4** connected to Gogs at `gogs.tftsr.com`. | Pipeline | Trigger | Steps | |---|---|---| -| `.woodpecker/test.yml` | Every push / PR | rustfmt, clippy, cargo test, tsc, vitest | -| `.woodpecker/release.yml` | Tag `v*` | Build linux/amd64 + linux/arm64 → upload to Gogs release | +| `.woodpecker/test.yml` | Every push / PR | rustfmt · clippy · cargo test (64) · tsc · vitest | +| `.woodpecker/release.yml` | Tag `v*` | Build linux/amd64 + windows/amd64 → upload to Gogs release | -> macOS builds require a macOS runner (Apple SDK). Windows cross-compilation from Linux via `cross-rs` is possible but not yet configured. +**Agents:** +- `woodpecker_agent` — linux/amd64 (native x86_64) +- `woodpecker_agent_arm64` — linux/arm64 (QEMU emulation on x86_64 host) + +> macOS builds require a macOS runner. See [CI/CD Pipeline wiki](https://gogs.tftsr.com/sarman/tftsr-devops_investigation/wiki/CICD-Pipeline) for full infrastructure docs. --- @@ -249,8 +278,8 @@ Override with the `TFTSR_DATA_DIR` environment variable. | 8 | RCA & Post-Mortem Generation | ✅ Complete | | 9 | History & Search | 🔲 Pending | | 10 | Integrations (Confluence, ServiceNow, ADO) | 🔲 v0.2 | -| 11 | CLI Interface | 🔲 Pending | -| 12 | Release Packaging | 🔲 Pending | +| 11 | CI/CD Pipeline | ✅ Complete — all checks green | +| 12 | Release Packaging | ✅ linux/amd64 · windows/amd64; arm64 via QEMU agent | --- diff --git a/docs/wiki/CICD-Pipeline.md b/docs/wiki/CICD-Pipeline.md index 5041089a..8ef5de87 100644 --- a/docs/wiki/CICD-Pipeline.md +++ b/docs/wiki/CICD-Pipeline.md @@ -4,11 +4,18 @@ | Component | URL | Notes | |-----------|-----|-------| -| Gogs | `http://172.0.0.29:3000` / `https://gogs.tftsr.com` | Git server, version 0.14 | +| Gogs | `https://gogs.tftsr.com` / `http://172.0.0.29:3000` | 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` | +### CI Agents + +| Container | Platform | Purpose | +|-----------|----------|---------| +| `woodpecker_agent` | `linux/amd64` | Native x86_64 — all test builds + amd64/windows release | +| `woodpecker_agent_arm64` | `linux/arm64` | QEMU emulation on x86_64 host — arm64 release builds | + --- ## Test Pipeline (`.woodpecker/test.yml`) @@ -19,21 +26,15 @@ Pipeline steps: 1. rust-fmt-check → cargo fmt --check 2. rust-clippy → cargo clippy -- -D warnings - 3. rust-tests → cargo test + 3. rust-tests → cargo test (64 tests) 4. frontend-typecheck → npx tsc --noEmit - 5. frontend-tests → npm run test:run (Vitest) + 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 -**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: @@ -50,7 +51,7 @@ pipeline: - cargo test ``` -> ⚠️ **Do NOT** use the newer `steps:` list format — Woodpecker 0.15.4 uses the Drone-legacy map format. +> ⚠️ **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. --- @@ -58,43 +59,128 @@ pipeline: **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: - 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 + 1. clone → alpine/git with explicit tag fetch + checkout + 2. build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu) + → artifacts/linux-amd64/{.deb, .rpm, .AppImage} + 3. build-windows-amd64 → cargo tauri build (x86_64-pc-windows-gnu via mingw-w64) + → artifacts/windows-amd64/{.exe, .msi} + 4. upload-release → Create Gogs release + upload all artifacts +``` + +**Clone override (release.yml):** + +Release builds use `alpine/git` with explicit commands because `woodpeckerci/plugin-git:latest` uses `git switch` which fails on tag refs: + +```yaml +clone: + git: + image: alpine/git + network_mode: gogs_default + commands: + - git init -b master + - git remote add origin http://gogs_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` -- Linux arm64: `.deb`, `.AppImage` +- Windows amd64: `.exe` (NSIS installer), `.msi` +- Linux arm64: `.deb`, `.AppImage` (requires arm64 agent or QEMU) + +**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 +upload-release: + image: curlimages/curl:latest + network_mode: gogs_default # host firewall blocks default bridge from reaching Gogs API + secrets: [GOGS_TOKEN] +``` + +The `GOGS_TOKEN` Woodpecker secret is inserted into the DB: +```python +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', '', '', 'tag', 0, 1) +""") +``` **Gogs Release API:** ```bash # Create release -POST $API/repos/sarman/tftsr-devops_investigation/releases +POST http://gogs_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases Authorization: token $GOGS_TOKEN # Upload artifact -POST $API/repos/sarman/tftsr-devops_investigation/releases/{id}/assets +POST http://gogs_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases/{id}/assets ``` -The `GOGS_TOKEN` is stored as a Woodpecker secret. +--- + +## Switching Between Test and Release Config + +Woodpecker 0.15.4 supports only **one config file per repo**. The workflow: + +```bash +# For regular pushes/PRs — use test pipeline +python3 -c "conn.execute(\"UPDATE repos SET repo_config_path='.woodpecker/test.yml'\")" + +# Before pushing a release tag — switch to release pipeline +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 -**Hook ID:** 6 (in Gogs) +**Hook ID:** 9 (in Gogs, `http://gogs.tftsr.com`) **Events:** `create`, `push`, `pull_request` **URL:** `http://172.0.0.29:8084/hook?access_token=` **JWT signing:** - Algorithm: HS256 -- Secret: `repo_hash` value from Woodpecker DB (`dK8zFWtAu67qfKd3Et6N8LptqTmedumJ`) -- Payload: `{"text":"sarman/tftsr-devops_investigation","type":"hook"}` +- Secret: `repo_hash` from Woodpecker DB (`dK8zFWtAu67qfKd3Et6N8LptqTmedumJ`) +- Payload: `{"text":"sarman/tftsr-devops_investigation","type":"hook","iat":}` -> ⚠️ JWT has an `iat` claim. If it's stale, regenerate it. +**Regenerate JWT when stale:** +```python +import base64, hmac, hashlib, json, time + +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/ +curl -X POST http://172.0.0.29:3000/api/v1/repos/sarman/tftsr-devops_investigation/hooks \ + -H "Authorization: token " \ + -H "Content-Type: application/json" \ + -d '{"type":"gogs","config":{"url":"http://172.0.0.29:8084/hook?access_token=","content_type":"json","secret":"af5dc60e0984f2680d0969f4a087e7100a4ece7e"},"events":["push","pull_request","create"],"active":true}' +``` --- @@ -102,47 +188,63 @@ The `GOGS_TOKEN` is stored as a Woodpecker secret. 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] +-- Verify config +SELECT user_token IS NOT NULL AND user_token != '' AS token_set FROM users WHERE user_login='sarman'; --- 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_config_path='.woodpecker/test.yml' (or release.yml during release) -- repo_hash='dK8zFWtAu67qfKd3Et6N8LptqTmedumJ' ``` --- +## Branch Protection + +Master branch is protected: all changes require a PR. Direct pushes are blocked. + +```sql +-- 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'; +``` + +> Gogs 0.14 does **not** enforce required CI status checks before merging. Only `require_pull_request=true` is supported. + +--- + ## 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=` 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+. +Woodpecker 0.15.4 only supports a **single config file**. Multi-file pipelines require 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 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. -### 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. +### 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`. -### Gogs Token Authentication -The `sha1` field in Gogs token create API response **is** the actual bearer token (not a hash). Use it directly: +### `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 +# Kill orphan containers and volumes +docker rm -f $(docker ps -aq --filter 'name=0_') +docker volume rm $(docker volume ls -q | grep '0_') +# Restart agent +docker restart woodpecker_agent ``` -Authorization: token -``` - -### 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. diff --git a/docs/wiki/Development-Setup.md b/docs/wiki/Development-Setup.md index 2e3983d7..e338eeb2 100644 --- a/docs/wiki/Development-Setup.md +++ b/docs/wiki/Development-Setup.md @@ -90,7 +90,7 @@ npm run test:coverage npx tsc --noEmit ``` -Current test status: **13/13 frontend tests passing**, Rust tests passing. +Current test status: **13/13 frontend tests passing**, **64/64 Rust tests passing**. --- diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md index f3908935..68881fdf 100644 --- a/docs/wiki/Home.md +++ b/docs/wiki/Home.md @@ -2,6 +2,8 @@ **TFTSR** is a secure desktop application for guided IT incident triage, root cause analysis (RCA), and post-mortem documentation. Built with Tauri 2.x (Rust + WebView) and React 18. +**CI:** ![build](http://172.0.0.29:3000/sarman/tftsr-devops_investigation/badges/master/status.svg) — rustfmt · clippy · 64 Rust tests · tsc · vitest — all green + ## Quick Navigation | Topic | Description | @@ -12,7 +14,7 @@ | [AI Providers](wiki/AI-Providers) | Supported providers and configuration | | [PII Detection](wiki/PII-Detection) | Patterns, redaction flow, security | | [IPC Commands](wiki/IPC-Commands) | Full list of Tauri backend commands | -| [CI/CD Pipeline](wiki/CICD-Pipeline) | Woodpecker CI + Gogs setup | +| [CI/CD Pipeline](wiki/CICD-Pipeline) | Woodpecker CI + Gogs setup, multi-platform builds | | [Security Model](wiki/Security-Model) | Encryption, audit trail, capabilities | | [Integrations](wiki/Integrations) | Confluence, ServiceNow, Azure DevOps (v0.2) | | [Troubleshooting](wiki/Troubleshooting) | Known issues and fixes | @@ -28,15 +30,23 @@ - **Audit Trail** — Every external data send logged with SHA-256 hash - **Domain-Specific Prompts** — 8 IT domains: Linux, Windows, Network, Kubernetes, Databases, Virtualization, Hardware, Observability +## Releases + +| Version | Status | Platforms | +|---------|--------|-----------| +| v0.1.0-alpha | 🚀 Released | linux/amd64 (.deb, .rpm, .AppImage), windows/amd64 (.exe, .msi) | + +Download from [Releases](https://gogs.tftsr.com/sarman/tftsr-devops_investigation/releases). + ## Project Status | Phase | Status | |-------|--------| -| Phases 1–8 (Core) | ✅ Complete | -| Phase 9 (History/Search FTS) | 🔄 Partially integrated | +| Phases 1–8 (Core application) | ✅ Complete | +| Phase 9 (History/Search) | 🔲 Pending | | Phase 10 (Integrations) | 🕐 v0.2 stubs only | -| Phase 11 (CLI) | 🕐 Planned | -| Phase 12 (Release packaging) | 🔄 Linux done; macOS/Windows pending | +| Phase 11 (CI/CD) | ✅ Complete — Woodpecker CI fully operational | +| Phase 12 (Release packaging) | ✅ linux/amd64 + windows/amd64; arm64 via QEMU agent | ## Tech Stack @@ -49,5 +59,5 @@ | Database | rusqlite + SQLCipher (AES-256) | | Secret storage | tauri-plugin-stronghold | | State | Zustand | -| Testing | Vitest (frontend) + `#[cfg(test)]` (Rust) | +| Testing | Vitest (13 frontend) + `#[cfg(test)]` (64 Rust tests) | | CI/CD | Woodpecker CI v0.15.4 + Gogs | diff --git a/docs/wiki/Troubleshooting.md b/docs/wiki/Troubleshooting.md index 8dc17aaf..f59b4342 100644 --- a/docs/wiki/Troubleshooting.md +++ b/docs/wiki/Troubleshooting.md @@ -7,26 +7,104 @@ **Cause:** Woodpecker 0.15.4 `token.ParseRequest()` does not read `?token=` URL params. **Fix:** Webhook URL must use `?access_token=` (not `?token=`). -``` -http://172.0.0.29:8084/hook?access_token= -``` -Regenerate the JWT if it's stale (JWT has an `iat` claim): -```bash +Regenerate the JWT if it's stale (see [CICD-Pipeline → Webhook Configuration](wiki/CICD-Pipeline)): +```python # JWT payload: {"text":"sarman/tftsr-devops_investigation","type":"hook"} -# Signed with: repo_hash (dK8zFWtAu67qfKd3Et6N8LptqTmedumJ) +# Signed HS256 with repo_hash = dK8zFWtAu67qfKd3Et6N8LptqTmedumJ ``` --- -### Pipeline Step Can't Reach Gogs +### Build Stuck in "Pending" or "Running" with No Containers -**Cause:** Step containers run on the default Docker bridge, not on `gogs_default` network. +**Cause:** After a Woodpecker server restart, the agent enters a loop trying to clean up orphaned containers from the previous session and stops processing new tasks. -**Fix:** Use `network_mode: gogs_default` in the clone section and ensure `repo_trusted=1`: +**Fix:** ```bash -docker exec woodpecker_db sqlite3 /data/woodpecker.sqlite \ - "UPDATE repos SET repo_trusted=1 WHERE repo_full_name='sarman/tftsr-devops_investigation';" +# 1. Kill any orphan step containers and volumes +ssh sarman@172.0.0.29 +docker rm -f $(docker ps -aq --filter 'name=0_') +docker volume rm $(docker volume ls -q | grep '0_') + +# 2. Cancel stuck builds in Woodpecker DB +python3 -c " +import sqlite3; conn = sqlite3.connect('/docker_mounts/woodpecker/data/woodpecker.sqlite') +conn.execute(\"UPDATE builds SET build_status='cancelled' WHERE build_status IN ('pending','running')\") +conn.execute('DELETE FROM tasks') +conn.commit() +" + +# 3. Restart agent +docker restart woodpecker_agent +``` + +--- + +### Pipeline Step Can't Reach Gogs (`gogs_app` not found) + +**Cause:** Step containers run on the default Docker bridge. They cannot resolve `gogs_app` hostname or reach `172.0.0.29:3000` (blocked by host firewall). + +**Fix:** Add `network_mode: gogs_default` to any step needing Gogs access. Requires `repo_trusted=1`: +```bash +python3 -c " +import sqlite3; conn = sqlite3.connect('/docker_mounts/woodpecker/data/woodpecker.sqlite') +conn.execute(\"UPDATE repos SET repo_trusted=1 WHERE repo_full_name='sarman/tftsr-devops_investigation'\") +conn.commit() +" +``` + +--- + +### `CI=woodpecker` Rejected by `cargo tauri build` + +**Cause:** Woodpecker sets `CI=woodpecker` (string). Tauri CLI's `--ci` flag expects `true`/`false`. + +**Fix:** Prefix the build command: +```yaml +commands: + - CI=true cargo tauri build --target $TARGET +``` + +--- + +### Release Artifacts Not Uploaded (Upload Step Silent Failure) + +**Cause 1:** Upload container on default bridge can't reach Gogs API at `http://172.0.0.29:3000`. +**Fix:** Add `network_mode: gogs_default` to the upload step and use `gogs_app:3000` in the API URL. + +**Cause 2:** Artifacts written to `/artifacts/` (absolute path) not visible to the upload step. +**Fix:** Write artifacts to the workspace using relative paths (`artifacts/linux-amd64/`). Only the workspace directory is shared between pipeline steps. + +**Cause 3:** `GOGS_TOKEN` secret not set or has wrong value. +**Check:** +```bash +python3 -c " +import sqlite3; conn = sqlite3.connect('/docker_mounts/woodpecker/data/woodpecker.sqlite') +s = conn.execute(\"SELECT secret_value FROM secrets WHERE secret_name='GOGS_TOKEN'\").fetchone() +print('Token length:', len(s[0]) if s else 'NOT SET') +" +# Test token (replace 172.0.0.29:3000 with gogs_app:3000 from inside gogs_default network) +curl -H "Authorization: token " http://172.0.0.29:3000/api/v1/user +``` + +--- + +### `git switch refs/tags/v*` Fails in Release Build + +**Cause:** `woodpeckerci/plugin-git:latest` uses `git switch` which doesn't support detached HEAD for tag refs. + +**Fix:** Override the clone section with `alpine/git` and explicit commands: +```yaml +clone: + git: + image: alpine/git + network_mode: gogs_default + commands: + - git init -b master + - git remote add origin http://gogs_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} ``` --- @@ -39,7 +117,7 @@ docker exec woodpecker_db sqlite3 /data/woodpecker.sqlite \ --- -### Empty Clone URL in Pipeline +### Empty Clone URL in Pipeline (Push Events) **Cause:** Woodpecker 0.15.4 `go-gogs-client` `PayloadRepo` struct is missing `CloneURL`. @@ -57,7 +135,6 @@ clone: ### `MutexGuard` Not `Send` Across Await -**Error:** ``` error[E0277]: `MutexGuard<'_, Connection>` cannot be sent between threads safely ``` @@ -76,7 +153,7 @@ async_fn().await?; ### Clippy Lints Fail in CI -Common lint fixes: +Common lint fixes required by `-D warnings` (especially on Rust 1.88+): ```rust // uninlined_format_args @@ -89,7 +166,9 @@ x >= a && x < b → (a..b).contains(&x) s.push_str("a") → s.push('a') ``` -Run locally: `cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings` +Run locally: `cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings -W clippy::uninlined_format_args` + +Auto-fix: `cargo clippy --manifest-path src-tauri/Cargo.toml --fix --allow-dirty -- -D warnings -W clippy::uninlined_format_args` --- @@ -101,6 +180,12 @@ sudo dnf install -y glib2-devel gtk3-devel webkit2gtk4.1-devel \ libsoup3-devel openssl-devel librsvg2-devel ``` +**Fix (Debian/Ubuntu):** +```bash +sudo apt-get install -y libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev \ + libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config +``` + --- ## Database @@ -112,7 +197,7 @@ sudo dnf install -y glib2-devel gtk3-devel webkit2gtk4.1-devel \ **Check:** 1. `TFTSR_DB_KEY` env var is set 2. Key matches what was used when DB was created -3. File isn't corrupted (try `file tftsr.db` — should say `SQLite 3.x database`) +3. File isn't corrupted: `file tftsr.db` should say `SQLite 3.x database` **Warning:** Changing the key requires re-encrypting the database: ```bash @@ -124,12 +209,12 @@ sqlite3 tftsr.db "ATTACH 'new.db' AS newdb KEY 'new-key'; \ ### Migration Fails to Run -Check which migrations have already been applied: +Check which migrations have been applied: ```sql SELECT name, applied_at FROM _migrations ORDER BY id; ``` -If a migration is partially applied, the DB may be in an inconsistent state. Restore from backup or recreate. +If a migration is partially applied, the DB may be inconsistent. Restore from backup or recreate. --- @@ -137,25 +222,23 @@ If a migration is partially applied, the DB may be in an inconsistent state. Res ### TypeScript Errors After Pulling -Run a fresh type check: ```bash npx tsc --noEmit ``` -Ensure `tauriCommands.ts` matches the Rust command signatures exactly (especially `IssueDetail` nesting). +Ensure `tauriCommands.ts` matches Rust command signatures exactly (especially `IssueDetail` nesting). --- ### `IssueDetail` Field Access Errors -The `get_issue` command returns a **nested** struct: +`get_issue()` returns a **nested** struct: ```typescript // ✅ Correct const title = detail.issue.title; -const severity = detail.issue.severity; -// ❌ Wrong — these fields don't exist at the top level -const title = detail.title; +// ❌ Wrong +const title = detail.title; // field doesn't exist at top level ``` --- @@ -181,7 +264,7 @@ The `sha1` field from the Gogs token create API **is** the bearer token — use curl -H "Authorization: token " https://gogs.tftsr.com/api/v1/user ``` -Do not confuse with the `sha1` column in the `access_token` table, which stores `sha1(token)[:40]`. +Do not confuse with the `sha1` column in the `access_token` table, which stores `sha1(token)[:40]` (a hash of the token, not the token itself). ### PostgreSQL Access