docs: sync wiki with latest changes including LiteLLM + Bedrock guide
parent
10e1491638
commit
10b14a2ffe
@ -19,7 +19,7 @@ pub trait Provider {
|
|||||||
|
|
||||||
### 1. OpenAI-Compatible
|
### 1. OpenAI-Compatible
|
||||||
|
|
||||||
Covers: OpenAI, Azure OpenAI, LM Studio, vLLM, and any OpenAI-API-compatible endpoint.
|
Covers: OpenAI, Azure OpenAI, LM Studio, vLLM, **LiteLLM (AWS Bedrock)**, and any OpenAI-API-compatible endpoint.
|
||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
@ -30,7 +30,9 @@ Covers: OpenAI, Azure OpenAI, LM Studio, vLLM, and any OpenAI-API-compatible end
|
|||||||
|
|
||||||
**Models:** `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo`
|
**Models:** `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo`
|
||||||
|
|
||||||
**Custom endpoint:** Set `config.base_url` to any OpenAI-compatible API (e.g., LM Studio at `http://localhost:1234/v1`).
|
**Custom endpoint:** Set `config.base_url` to any OpenAI-compatible API:
|
||||||
|
- LM Studio: `http://localhost:1234/v1`
|
||||||
|
- **LiteLLM (AWS Bedrock):** `http://localhost:8000/v1` — See [LiteLLM + Bedrock Setup](LiteLLM-Bedrock-Setup) for full configuration guide
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
243
CICD-Pipeline.md
243
CICD-Pipeline.md
@ -4,10 +4,26 @@
|
|||||||
|
|
||||||
| Component | URL | Notes |
|
| Component | URL | Notes |
|
||||||
|-----------|-----|-------|
|
|-----------|-----|-------|
|
||||||
| Gogs | `http://172.0.0.29:3000` / `https://gogs.tftsr.com` | 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
|
||||||
|
|
||||||
|
| Agent | Platform | Host | Purpose |
|
||||||
|
|-------|----------|------|---------|
|
||||||
|
| `gitea_act_runner_amd64` (Docker) | `linux-amd64` | 172.0.0.29 | Native x86_64 — test builds + amd64/windows release |
|
||||||
|
| `act_runner` (systemd) | `linux-arm64` | 172.0.0.29 | Native aarch64 — arm64 release builds |
|
||||||
|
| `act_runner` (launchd) | `macos-arm64` | sarman's local Mac | Native Apple Silicon — macOS `.dmg` release builds |
|
||||||
|
|
||||||
|
Agent labels configured in `~/.config/act_runner/config.yaml`:
|
||||||
|
```yaml
|
||||||
|
runner:
|
||||||
|
labels:
|
||||||
|
- "macos-arm64:host"
|
||||||
|
```
|
||||||
|
macOS runner runs jobs **directly on the host** (no Docker container) — macOS SDK cannot run in Docker.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -19,140 +35,227 @@
|
|||||||
Pipeline steps:
|
Pipeline steps:
|
||||||
1. rust-fmt-check → cargo fmt --check
|
1. rust-fmt-check → cargo fmt --check
|
||||||
2. rust-clippy → cargo clippy -- -D warnings
|
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
|
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:**
|
**Docker images used:**
|
||||||
- `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
|
||||||
|
|
||||||
**System dependencies installed in CI (Rust steps):**
|
**Pipeline YAML format (Woodpecker 2.x — steps list format):**
|
||||||
```
|
|
||||||
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
|
```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.
|
> ⚠️ Woodpecker 2.x uses the `steps:` list format. The legacy `pipeline:` map format from
|
||||||
|
> Woodpecker 0.15.4 is no longer supported.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Release Pipeline (`.woodpecker/release.yml`)
|
## Release Pipeline (`.gitea/workflows/release.yml`)
|
||||||
|
|
||||||
**Triggers:** Git tags matching `v*`
|
**Triggers:** Git tags matching `v*`
|
||||||
|
|
||||||
```
|
```
|
||||||
Pipeline steps:
|
Jobs (run in parallel):
|
||||||
1. build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu)
|
build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu)
|
||||||
2. build-linux-arm64 → cargo tauri build (aarch64-unknown-linux-gnu, cross-compile)
|
→ {.deb, .rpm, .AppImage} uploaded to Gitea release
|
||||||
3. upload-release → Create Gogs release + upload artifacts via API
|
build-windows-amd64 → cargo tauri build (x86_64-pc-windows-gnu) via mingw-w64
|
||||||
|
→ {.exe, .msi} uploaded to Gitea release
|
||||||
|
build-linux-arm64 → cargo tauri build (aarch64-unknown-linux-gnu)
|
||||||
|
→ {.deb, .rpm, .AppImage} uploaded to Gitea release
|
||||||
|
build-macos-arm64 → cargo tauri build (aarch64-apple-darwin) — runs on local Mac
|
||||||
|
→ {.dmg} uploaded to Gitea release
|
||||||
|
→ unsigned; after install run: xattr -cr /Applications/TFTSR.app
|
||||||
|
```
|
||||||
|
|
||||||
|
**Per-step agent routing (Woodpecker 2.x labels):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- name: build-linux-amd64
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64 # → woodpecker_agent on 172.0.0.29
|
||||||
|
|
||||||
|
- name: build-linux-arm64
|
||||||
|
labels:
|
||||||
|
platform: linux/arm64 # → woodpecker-agent.service on local arm64 machine
|
||||||
|
```
|
||||||
|
|
||||||
|
**Multi-agent workspace isolation:**
|
||||||
|
|
||||||
|
Steps routed to different agents do **not** share a workspace. The arm64 step clones
|
||||||
|
the repo directly within its commands (using `http://172.0.0.29:3000`, accessible from
|
||||||
|
the local machine) and uploads its artifacts inline. The `upload-release` step (amd64)
|
||||||
|
handles amd64 + windows artifacts only.
|
||||||
|
|
||||||
|
**Clone override (release.yml — amd64 workspace):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
clone:
|
||||||
|
git:
|
||||||
|
image: alpine/git
|
||||||
|
network_mode: gogs_default
|
||||||
|
commands:
|
||||||
|
- git init -b master
|
||||||
|
- git remote add origin http://gitea_app:3000/sarman/tftsr-devops_investigation.git
|
||||||
|
- git fetch --depth=1 origin +refs/tags/${CI_COMMIT_TAG}:refs/tags/${CI_COMMIT_TAG}
|
||||||
|
- git checkout ${CI_COMMIT_TAG}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows cross-compile environment:**
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
TARGET: x86_64-pc-windows-gnu
|
||||||
|
CC_x86_64_pc_windows_gnu: x86_64-w64-mingw32-gcc
|
||||||
|
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: x86_64-w64-mingw32-gcc
|
||||||
```
|
```
|
||||||
|
|
||||||
**Artifacts per platform:**
|
**Artifacts per platform:**
|
||||||
- Linux amd64: `.deb`, `.rpm`, `.AppImage`
|
- Linux amd64: `.deb`, `.rpm`, `.AppImage`
|
||||||
- Linux arm64: `.deb`, `.AppImage`
|
- Windows amd64: `.exe` (NSIS installer), `.msi`
|
||||||
|
- Linux arm64: `.deb`, `.rpm`, `.AppImage`
|
||||||
|
|
||||||
**Gogs Release API:**
|
**Upload step (requires gogs_default network for amd64, host IP for arm64):**
|
||||||
|
```yaml
|
||||||
|
# amd64 upload step
|
||||||
|
upload-release:
|
||||||
|
image: curlimages/curl:latest
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
|
network_mode: gogs_default
|
||||||
|
secrets: [GOGS_TOKEN]
|
||||||
|
```
|
||||||
|
|
||||||
|
The `GOGS_TOKEN` Woodpecker secret must be created via the Woodpecker UI or API after
|
||||||
|
migration. The secret name stays `GOGS_TOKEN` for pipeline compatibility.
|
||||||
|
|
||||||
|
**Gitea Release API (replaces Gogs API — same endpoints, different container name):**
|
||||||
```bash
|
```bash
|
||||||
# Create release
|
# Create release
|
||||||
POST $API/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 $API/repos/sarman/tftsr-devops_investigation/releases/{id}/assets
|
POST http://gitea_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases/{id}/assets
|
||||||
```
|
```
|
||||||
|
|
||||||
The `GOGS_TOKEN` is stored as a Woodpecker secret.
|
From the arm64 agent (local machine), use `http://172.0.0.29:3000/api/v1` instead.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Multi-File Pipeline Support (Woodpecker 2.x)
|
||||||
|
|
||||||
|
Woodpecker 2.x supports multiple pipeline files in the `.woodpecker/` directory.
|
||||||
|
All `.yml` files are evaluated on every trigger; `when:` conditions control which
|
||||||
|
pipelines actually run.
|
||||||
|
|
||||||
|
Current files:
|
||||||
|
- `.woodpecker/test.yml` — runs on every push/PR
|
||||||
|
- `.woodpecker/release.yml` — runs on `v*` tags only
|
||||||
|
|
||||||
|
No DB config path switching needed (unlike Woodpecker 0.15.4).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Webhook Configuration
|
## Webhook Configuration
|
||||||
|
|
||||||
**Hook ID:** 6 (in Gogs)
|
**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` value from Woodpecker DB (`dK8zFWtAu67qfKd3Et6N8LptqTmedumJ`)
|
|
||||||
- Payload: `{"text":"sarman/tftsr-devops_investigation","type":"hook"}`
|
|
||||||
|
|
||||||
> ⚠️ JWT has an `iat` claim. If it's stale, regenerate it.
|
1. Log in at `http://172.0.0.29:8085` via Gitea OAuth2
|
||||||
|
2. Add repo `sarman/tftsr-devops_investigation`
|
||||||
|
3. Woodpecker creates webhook in Gitea automatically
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Woodpecker DB State
|
## Branch Protection
|
||||||
|
|
||||||
SQLite at `/docker_mounts/woodpecker/data/woodpecker.sqlite` (on host `172.0.0.29`).
|
Master branch is protected: all changes require a PR.
|
||||||
|
|
||||||
Key values:
|
|
||||||
```sql
|
```sql
|
||||||
-- User
|
-- Gitea branch protection (via psql on gogs_postgres_db container)
|
||||||
SELECT user_token FROM users WHERE user_login='sarman';
|
-- Check protection
|
||||||
-- Should be: a6b3ee679fe9601e983f1a0c01493758d6033ef3
|
SELECT name, protected, require_pull_request FROM protect_branch WHERE repo_id=42;
|
||||||
|
|
||||||
-- Repo
|
-- Temporarily disable for urgent fixes (restore immediately after!)
|
||||||
SELECT repo_active, repo_trusted, repo_config_path, repo_hash
|
UPDATE protect_branch SET protected=false WHERE repo_id=42 AND name='master';
|
||||||
FROM repos WHERE repo_full_name='sarman/tftsr-devops_investigation';
|
-- ... push ...
|
||||||
-- repo_active=1, repo_trusted=1
|
UPDATE protect_branch SET protected=true, require_pull_request=true WHERE repo_id=42 AND name='master';
|
||||||
-- repo_config_path='.woodpecker/test.yml'
|
|
||||||
-- repo_hash='dK8zFWtAu67qfKd3Et6N8LptqTmedumJ'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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
|
||||||
|
access. Requires `repo_trusted=1`.
|
||||||
|
|
||||||
### JWT Signed with `repo_hash` (Not User Hash)
|
### `CI=woodpecker` Rejected by Tauri CLI
|
||||||
Hook JWT must be signed with the `repo_hash` value, not the user's hash.
|
Woodpecker sets `CI=woodpecker`; `cargo tauri build` expects a boolean. Fix: prefix with
|
||||||
|
`CI=true cargo tauri build`.
|
||||||
|
|
||||||
### Directory-Based Config Not Supported
|
### Agent Stalls After Server Restart
|
||||||
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+.
|
After restarting the Woodpecker server, the agent may enter a loop cleaning up orphaned
|
||||||
|
containers and stop picking up new builds. Fix:
|
||||||
### Step Containers Network Isolation
|
```bash
|
||||||
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`).
|
docker rm -f $(docker ps -aq --filter 'name=0_')
|
||||||
|
docker volume rm $(docker volume ls -q | grep '0_')
|
||||||
### Empty Clone URL Bug
|
docker restart woodpecker_agent
|
||||||
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.
|
|
||||||
|
|
||||||
### Gogs Token Authentication
|
|
||||||
The `sha1` field in Gogs token create API response **is** the actual bearer token (not a hash). Use it directly:
|
|
||||||
```
|
|
||||||
Authorization: token <sha1_from_create_response>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Gogs SPA Login Field Mismatch
|
### Windows DLL Export Ordinal Too Large
|
||||||
Gogs 0.14 SPA login form uses `login=` field; the Gogs backend reads `username=`. A custom login page is served by nginx at `/login`.
|
`/usr/bin/x86_64-w64-mingw32-ld: error: export ordinal too large: 106290`
|
||||||
|
|
||||||
### Gogs OAuth2 Limitation
|
Fix: `src-tauri/.cargo/config.toml`:
|
||||||
Gogs 0.14 has no OAuth2 provider support, blocking upgrade to Woodpecker 2.x.
|
```toml
|
||||||
|
[target.x86_64-pc-windows-gnu]
|
||||||
|
rustflags = ["-C", "link-arg=-Wl,--exclude-all-symbols"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### GOGS_TOKEN Secret Must Be Recreated After Migration
|
||||||
|
After migrating from Woodpecker 0.15.4 to 2.x, recreate the `GOGS_TOKEN` secret:
|
||||||
|
1. Log in to Gitea, create a new API token under Settings → Applications
|
||||||
|
2. In Woodpecker UI → Repository → Secrets, add secret `GOGS_TOKEN` with the token value
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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)
|
||||||
|
|||||||
@ -90,7 +90,7 @@ npm run test:coverage
|
|||||||
npx tsc --noEmit
|
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**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
27
Home.md
27
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.
|
**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:**  — rustfmt · clippy · 64 Rust tests · tsc · vitest — all green
|
||||||
|
|
||||||
## Quick Navigation
|
## Quick Navigation
|
||||||
|
|
||||||
| Topic | Description |
|
| Topic | Description |
|
||||||
@ -10,9 +12,10 @@
|
|||||||
| [Development Setup](wiki/Development-Setup) | Prerequisites, commands, environment |
|
| [Development Setup](wiki/Development-Setup) | Prerequisites, commands, environment |
|
||||||
| [Database](wiki/Database) | Schema, migrations, encryption |
|
| [Database](wiki/Database) | Schema, migrations, encryption |
|
||||||
| [AI Providers](wiki/AI-Providers) | Supported providers and configuration |
|
| [AI Providers](wiki/AI-Providers) | Supported providers and configuration |
|
||||||
|
| [LiteLLM + Bedrock Setup](wiki/LiteLLM-Bedrock-Setup) | AWS Bedrock integration via LiteLLM proxy |
|
||||||
| [PII Detection](wiki/PII-Detection) | Patterns, redaction flow, security |
|
| [PII Detection](wiki/PII-Detection) | Patterns, redaction flow, security |
|
||||||
| [IPC Commands](wiki/IPC-Commands) | Full list of Tauri backend commands |
|
| [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) | Gitea Actions setup, multi-platform builds, act_runner config |
|
||||||
| [Security Model](wiki/Security-Model) | Encryption, audit trail, capabilities |
|
| [Security Model](wiki/Security-Model) | Encryption, audit trail, capabilities |
|
||||||
| [Integrations](wiki/Integrations) | Confluence, ServiceNow, Azure DevOps (v0.2) |
|
| [Integrations](wiki/Integrations) | Confluence, ServiceNow, Azure DevOps (v0.2) |
|
||||||
| [Troubleshooting](wiki/Troubleshooting) | Known issues and fixes |
|
| [Troubleshooting](wiki/Troubleshooting) | Known issues and fixes |
|
||||||
@ -21,22 +24,30 @@
|
|||||||
|
|
||||||
- **5-Whys AI Triage** — Interactive guided root cause analysis via multi-turn AI chat
|
- **5-Whys AI Triage** — Interactive guided root cause analysis via multi-turn AI chat
|
||||||
- **PII Auto-Redaction** — Detects and redacts sensitive data before any AI send
|
- **PII Auto-Redaction** — Detects and redacts sensitive data before any AI send
|
||||||
- **Multi-Provider AI** — OpenAI, Anthropic Claude, Google Gemini, Mistral, local Ollama (fully offline)
|
- **Multi-Provider AI** — OpenAI, Anthropic Claude, Google Gemini, Mistral, AWS Bedrock (via LiteLLM), local Ollama (fully offline)
|
||||||
- **SQLCipher AES-256** — All issue history encrypted at rest
|
- **SQLCipher AES-256** — All issue history encrypted at rest
|
||||||
- **RCA + Post-Mortem Generation** — Auto-populated Markdown templates, exportable as MD/PDF
|
- **RCA + Post-Mortem Generation** — Auto-populated Markdown templates, exportable as MD/PDF
|
||||||
- **Ollama Management** — Hardware detection, model recommendations, in-app model management
|
- **Ollama Management** — Hardware detection, model recommendations, in-app model management
|
||||||
- **Audit Trail** — Every external data send logged with SHA-256 hash
|
- **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
|
- **Domain-Specific Prompts** — 8 IT domains: Linux, Windows, Network, Kubernetes, Databases, Virtualization, Hardware, Observability
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
| Version | Status | Platforms |
|
||||||
|
|---------|--------|-----------|
|
||||||
|
| v0.1.1 | 🚀 Released | linux/amd64 · linux/arm64 · windows/amd64 (.deb, .rpm, .AppImage, .exe, .msi) |
|
||||||
|
|
||||||
|
Download from [Releases](https://gogs.tftsr.com/sarman/tftsr-devops_investigation/releases). All builds are produced natively (no QEMU emulation).
|
||||||
|
|
||||||
## Project Status
|
## Project Status
|
||||||
|
|
||||||
| Phase | Status |
|
| Phase | Status |
|
||||||
|-------|--------|
|
|-------|--------|
|
||||||
| Phases 1–8 (Core) | ✅ Complete |
|
| Phases 1–8 (Core application) | ✅ Complete |
|
||||||
| Phase 9 (History/Search FTS) | 🔄 Partially integrated |
|
| Phase 9 (History/Search) | 🔲 Pending |
|
||||||
| Phase 10 (Integrations) | 🕐 v0.2 stubs only |
|
| Phase 10 (Integrations) | 🕐 v0.2 stubs only |
|
||||||
| Phase 11 (CLI) | 🕐 Planned |
|
| Phase 11 (CI/CD) | ✅ Complete — Gitea Actions fully operational |
|
||||||
| Phase 12 (Release packaging) | 🔄 Linux done; macOS/Windows pending |
|
| Phase 12 (Release packaging) | ✅ linux/amd64 · linux/arm64 (native) · windows/amd64 |
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
@ -49,5 +60,5 @@
|
|||||||
| Database | rusqlite + SQLCipher (AES-256) |
|
| Database | rusqlite + SQLCipher (AES-256) |
|
||||||
| Secret storage | tauri-plugin-stronghold |
|
| Secret storage | tauri-plugin-stronghold |
|
||||||
| State | Zustand |
|
| 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 |
|
| CI/CD | Gitea Actions (act_runner v0.3.1) + Gitea |
|
||||||
|
|||||||
396
LiteLLM-Bedrock-Setup.md
Normal file
396
LiteLLM-Bedrock-Setup.md
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
# LiteLLM + AWS Bedrock Setup
|
||||||
|
|
||||||
|
This guide covers how to use **Claude via AWS Bedrock** with TFTSR through the LiteLLM proxy, providing an OpenAI-compatible API gateway.
|
||||||
|
|
||||||
|
## Why LiteLLM + Bedrock?
|
||||||
|
|
||||||
|
- **Enterprise AWS contracts** — Use existing AWS Bedrock credits instead of direct Anthropic API
|
||||||
|
- **Multiple AWS accounts** — Run personal and business Bedrock accounts simultaneously
|
||||||
|
- **OpenAI-compatible API** — Works with any tool expecting OpenAI's API format
|
||||||
|
- **Claude Code integration** — Reuse the same AWS credentials used by Claude Code CLI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **AWS account** with Bedrock access and Claude models enabled
|
||||||
|
- **AWS credentials** configured (either default profile or named profile)
|
||||||
|
- **Python 3.8+** for LiteLLM installation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Install LiteLLM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install 'litellm[proxy]'
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify installation:
|
||||||
|
```bash
|
||||||
|
litellm --version
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Basic Setup — Single AWS Account
|
||||||
|
|
||||||
|
### 1. Create Configuration File
|
||||||
|
|
||||||
|
Create `~/.litellm/config.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
model_list:
|
||||||
|
- model_name: bedrock-claude
|
||||||
|
litellm_params:
|
||||||
|
model: bedrock/us.anthropic.claude-sonnet-4-6
|
||||||
|
aws_region_name: us-east-1
|
||||||
|
# Uses default AWS credentials from ~/.aws/credentials
|
||||||
|
|
||||||
|
general_settings:
|
||||||
|
master_key: sk-1234 # Any value — used for API authentication
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Start LiteLLM Proxy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run in background
|
||||||
|
nohup litellm --config ~/.litellm/config.yaml --port 8000 > ~/.litellm/litellm.log 2>&1 &
|
||||||
|
|
||||||
|
# Verify it's running
|
||||||
|
ps aux | grep litellm
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Test the Connection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/v1/chat/completions \
|
||||||
|
-H "Authorization: Bearer sk-1234" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"model": "bedrock-claude",
|
||||||
|
"messages": [{"role": "user", "content": "Hello"}],
|
||||||
|
"max_tokens": 50
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "chatcmpl-...",
|
||||||
|
"model": "bedrock-claude",
|
||||||
|
"choices": [{
|
||||||
|
"message": {
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "Hello! How can I help you today?"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Configure TFTSR
|
||||||
|
|
||||||
|
In **Settings → AI Providers → Add Provider**:
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Provider Name | OpenAI |
|
||||||
|
| Base URL | `http://localhost:8000/v1` |
|
||||||
|
| API Key | `sk-1234` (or your master_key from config) |
|
||||||
|
| Model | `bedrock-claude` |
|
||||||
|
| Display Name | `Bedrock Claude` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Setup — Multiple AWS Accounts
|
||||||
|
|
||||||
|
If you have **personal** and **business** Bedrock accounts, you can run both through the same LiteLLM instance.
|
||||||
|
|
||||||
|
### 1. Configure AWS Profiles
|
||||||
|
|
||||||
|
Ensure you have AWS profiles set up in `~/.aws/credentials`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[default]
|
||||||
|
aws_access_key_id = AKIA...
|
||||||
|
aws_secret_access_key = ...
|
||||||
|
|
||||||
|
[ClaudeCodeLP]
|
||||||
|
aws_access_key_id = AKIA...
|
||||||
|
aws_secret_access_key = ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Or if using credential process (like Claude Code does):
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# ~/.aws/config
|
||||||
|
[profile ClaudeCodeLP]
|
||||||
|
credential_process = /Users/${USER}/claude-code-with-bedrock/credential-process --profile ClaudeCodeLP
|
||||||
|
region = us-east-1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Update Configuration File
|
||||||
|
|
||||||
|
Edit `~/.litellm/config.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
model_list:
|
||||||
|
- model_name: bedrock-personal
|
||||||
|
litellm_params:
|
||||||
|
model: bedrock/us.anthropic.claude-sonnet-4-6
|
||||||
|
aws_region_name: us-east-1
|
||||||
|
# Uses default AWS credentials
|
||||||
|
|
||||||
|
- model_name: bedrock-business
|
||||||
|
litellm_params:
|
||||||
|
model: bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0
|
||||||
|
aws_region_name: us-east-1
|
||||||
|
aws_profile_name: ClaudeCodeLP # Named profile for business account
|
||||||
|
|
||||||
|
general_settings:
|
||||||
|
master_key: sk-1234
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Restart LiteLLM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find and stop existing process
|
||||||
|
pkill -f "litellm --config"
|
||||||
|
|
||||||
|
# Start with new config
|
||||||
|
nohup litellm --config ~/.litellm/config.yaml --port 8000 > ~/.litellm/litellm.log 2>&1 &
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Verify Both Models
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List available models
|
||||||
|
curl -s http://localhost:8000/v1/models \
|
||||||
|
-H "Authorization: Bearer sk-1234" | python3 -m json.tool
|
||||||
|
|
||||||
|
# Test personal account
|
||||||
|
curl -s http://localhost:8000/v1/chat/completions \
|
||||||
|
-H "Authorization: Bearer sk-1234" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"model": "bedrock-personal", "messages": [{"role": "user", "content": "test"}]}'
|
||||||
|
|
||||||
|
# Test business account
|
||||||
|
curl -s http://localhost:8000/v1/chat/completions \
|
||||||
|
-H "Authorization: Bearer sk-1234" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"model": "bedrock-business", "messages": [{"role": "user", "content": "test"}]}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Configure in TFTSR
|
||||||
|
|
||||||
|
Add both models as separate providers:
|
||||||
|
|
||||||
|
**Provider 1 — Personal:**
|
||||||
|
- Provider: OpenAI
|
||||||
|
- Base URL: `http://localhost:8000/v1`
|
||||||
|
- API Key: `sk-1234`
|
||||||
|
- Model: `bedrock-personal`
|
||||||
|
- Display Name: `Bedrock (Personal)`
|
||||||
|
|
||||||
|
**Provider 2 — Business:**
|
||||||
|
- Provider: OpenAI
|
||||||
|
- Base URL: `http://localhost:8000/v1`
|
||||||
|
- API Key: `sk-1234`
|
||||||
|
- Model: `bedrock-business`
|
||||||
|
- Display Name: `Bedrock (Business)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Claude Code Integration
|
||||||
|
|
||||||
|
If you're using [Claude Code](https://claude.ai/claude-code) with AWS Bedrock, you can reuse the same AWS credentials.
|
||||||
|
|
||||||
|
### 1. Check Claude Code Settings
|
||||||
|
|
||||||
|
Read your Claude Code configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat ~/.claude/settings.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Look for:
|
||||||
|
- `AWS_PROFILE` environment variable (e.g., `ClaudeCodeLP`)
|
||||||
|
- `awsAuthRefresh` credential process path
|
||||||
|
- `AWS_REGION` setting
|
||||||
|
|
||||||
|
### 2. Use Same Profile in LiteLLM
|
||||||
|
|
||||||
|
In `~/.litellm/config.yaml`, add a model using the same profile:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
model_list:
|
||||||
|
- model_name: claude-code-bedrock
|
||||||
|
litellm_params:
|
||||||
|
model: bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0
|
||||||
|
aws_region_name: us-east-1
|
||||||
|
aws_profile_name: ClaudeCodeLP # Same as Claude Code
|
||||||
|
```
|
||||||
|
|
||||||
|
Now both Claude Code and TFTSR use the same Bedrock account without duplicate credential management.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Available Claude Models on Bedrock
|
||||||
|
|
||||||
|
| Model ID | Name | Context | Best For |
|
||||||
|
|----------|------|---------|----------|
|
||||||
|
| `us.anthropic.claude-sonnet-4-6` | Claude Sonnet 4.6 | 200k tokens | Most tasks, best quality |
|
||||||
|
| `us.anthropic.claude-sonnet-4-5-20250929-v1:0` | Claude Sonnet 4.5 | 200k tokens | High performance |
|
||||||
|
| `us.anthropic.claude-opus-4-6` | Claude Opus 4.6 | 200k tokens | Complex reasoning |
|
||||||
|
| `us.anthropic.claude-haiku-4-5-20251001` | Claude Haiku 4.5 | 200k tokens | Speed + cost optimization |
|
||||||
|
|
||||||
|
Check your AWS Bedrock console for available models in your region.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Port Already in Use
|
||||||
|
|
||||||
|
If port 8000 is occupied:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find what's using the port
|
||||||
|
lsof -i :8000
|
||||||
|
|
||||||
|
# Use a different port
|
||||||
|
litellm --config ~/.litellm/config.yaml --port 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
Update the Base URL in TFTSR to match: `http://localhost:8080/v1`
|
||||||
|
|
||||||
|
### AWS Credentials Not Found
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify AWS CLI works
|
||||||
|
aws bedrock list-foundation-models --region us-east-1
|
||||||
|
|
||||||
|
# Test specific profile
|
||||||
|
aws bedrock list-foundation-models --region us-east-1 --profile ClaudeCodeLP
|
||||||
|
```
|
||||||
|
|
||||||
|
If AWS CLI fails, fix credentials first before debugging LiteLLM.
|
||||||
|
|
||||||
|
### Model Not Available
|
||||||
|
|
||||||
|
Error: `Model not found` or `Access denied`
|
||||||
|
|
||||||
|
1. Check [AWS Bedrock Console](https://console.aws.amazon.com/bedrock/) → Model Access
|
||||||
|
2. Request access to Claude models if not enabled
|
||||||
|
3. Verify model ID matches exactly (case-sensitive)
|
||||||
|
|
||||||
|
### LiteLLM Won't Start
|
||||||
|
|
||||||
|
Check logs:
|
||||||
|
```bash
|
||||||
|
cat ~/.litellm/litellm.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Common issues:
|
||||||
|
- Missing Python dependencies: `pip install 'litellm[proxy]' --upgrade`
|
||||||
|
- YAML syntax error: Validate with `python3 -c "import yaml; yaml.safe_load(open('${HOME}/.litellm/config.yaml'))"`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auto-Start LiteLLM on Boot
|
||||||
|
|
||||||
|
### macOS — LaunchAgent
|
||||||
|
|
||||||
|
Create `~/Library/LaunchAgents/com.litellm.proxy.plist`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>com.litellm.proxy</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>/opt/homebrew/bin/litellm</string>
|
||||||
|
<string>--config</string>
|
||||||
|
<string>/Users/${USER}/.litellm/config.yaml</string>
|
||||||
|
<string>--port</string>
|
||||||
|
<string>8000</string>
|
||||||
|
</array>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
|
<key>KeepAlive</key>
|
||||||
|
<true/>
|
||||||
|
<key>StandardOutPath</key>
|
||||||
|
<string>/Users/${USER}/.litellm/litellm.log</string>
|
||||||
|
<key>StandardErrorPath</key>
|
||||||
|
<string>/Users/${USER}/.litellm/litellm-error.log</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
```
|
||||||
|
|
||||||
|
Load it:
|
||||||
|
```bash
|
||||||
|
launchctl load ~/Library/LaunchAgents/com.litellm.proxy.plist
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux — systemd
|
||||||
|
|
||||||
|
Create `/etc/systemd/system/litellm.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=LiteLLM Proxy
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=${USER}
|
||||||
|
ExecStart=/usr/local/bin/litellm --config /home/${USER}/.litellm/config.yaml --port 8000
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and start:
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable litellm
|
||||||
|
sudo systemctl start litellm
|
||||||
|
sudo systemctl status litellm
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cost Comparison
|
||||||
|
|
||||||
|
| Provider | Model | Input (per 1M tokens) | Output (per 1M tokens) |
|
||||||
|
|----------|-------|----------------------|----------------------|
|
||||||
|
| **Anthropic Direct** | Claude Sonnet 4 | $3.00 | $15.00 |
|
||||||
|
| **AWS Bedrock** | Claude Sonnet 4 | $3.00 | $15.00 |
|
||||||
|
|
||||||
|
Pricing is identical, but Bedrock provides:
|
||||||
|
- AWS consolidated billing
|
||||||
|
- AWS Credits applicability
|
||||||
|
- Integration with AWS services (S3, Lambda, etc.)
|
||||||
|
- Enterprise support contracts
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
1. **Master Key** — The `master_key` in config is required but doesn't need to be complex since LiteLLM runs locally
|
||||||
|
2. **AWS Credentials** — Never commit `.aws/credentials` or credential process scripts to git
|
||||||
|
3. **Local Only** — LiteLLM proxy should only bind to `127.0.0.1` (localhost) — never expose to network
|
||||||
|
4. **Audit Logs** — TFTSR logs all AI requests with SHA-256 hashes in the audit table
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [LiteLLM Documentation](https://docs.litellm.ai/)
|
||||||
|
- [AWS Bedrock Claude Models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-claude.html)
|
||||||
|
- [Claude Code on Bedrock](https://docs.anthropic.com/claude/docs/claude-code)
|
||||||
@ -1,70 +1,133 @@
|
|||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
|
|
||||||
## CI/CD
|
## CI/CD — Gitea Actions
|
||||||
|
|
||||||
### Builds Not Triggering After Push
|
### Build Not Triggering After Push
|
||||||
|
|
||||||
**Cause:** Woodpecker 0.15.4 `token.ParseRequest()` does not read `?token=` URL params.
|
**Check:**
|
||||||
|
1. Verify the workflow file exists in `.gitea/workflows/` on the pushed branch
|
||||||
**Fix:** Webhook URL must use `?access_token=<JWT>` (not `?token=`).
|
2. Check the Actions tab at `http://172.0.0.29:3000/sarman/tftsr-devops_investigation/actions`
|
||||||
```
|
3. Confirm the act_runner is online: `docker logs gitea_act_runner_amd64 --since 5m`
|
||||||
http://172.0.0.29:8084/hook?access_token=<JWT>
|
|
||||||
```
|
|
||||||
|
|
||||||
Regenerate the JWT if it's stale (JWT has an `iat` claim):
|
|
||||||
```bash
|
|
||||||
# JWT payload: {"text":"sarman/tftsr-devops_investigation","type":"hook"}
|
|
||||||
# Signed with: repo_hash (dK8zFWtAu67qfKd3Et6N8LptqTmedumJ)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Pipeline Step Can't Reach Gogs
|
### Job Container Can't Reach Gitea (`172.0.0.29:3000` blocked)
|
||||||
|
|
||||||
**Cause:** Step containers run on the default Docker bridge, not on `gogs_default` network.
|
**Cause:** act_runner creates an isolated Docker network per job (when `container:` is specified). Traffic from the job container to `172.0.0.29:3000` is blocked by the host firewall.
|
||||||
|
|
||||||
**Fix:** Use `network_mode: gogs_default` in the clone section and ensure `repo_trusted=1`:
|
**Fix:** Ensure `container.network: host` is set in the act_runner config AND that `CONFIG_FILE=/data/config.yaml` is in the container's environment:
|
||||||
```bash
|
|
||||||
docker exec woodpecker_db sqlite3 /data/woodpecker.sqlite \
|
|
||||||
"UPDATE repos SET repo_trusted=1 WHERE repo_full_name='sarman/tftsr-devops_investigation';"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Woodpecker Login Fails
|
|
||||||
|
|
||||||
**Cause:** Gogs 0.14 SPA login form uses `login=` field; backend reads `username=`.
|
|
||||||
|
|
||||||
**Fix:** Use the nginx proxy at `http://172.0.0.29:8085/login` which serves a corrected login form.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Empty Clone URL in Pipeline
|
|
||||||
|
|
||||||
**Cause:** Woodpecker 0.15.4 `go-gogs-client` `PayloadRepo` struct is missing `CloneURL`.
|
|
||||||
|
|
||||||
**Fix:** Override with `CI_REPO_CLONE_URL` environment variable in the clone section:
|
|
||||||
```yaml
|
```yaml
|
||||||
clone:
|
# /docker_mounts/gitea/runner/amd64/config.yaml
|
||||||
git:
|
container:
|
||||||
environment:
|
network: "host"
|
||||||
- CI_REPO_CLONE_URL=http://gogs_app:3000/sarman/tftsr-devops_investigation.git
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml for act-runner-amd64
|
||||||
|
environment:
|
||||||
|
- CONFIG_FILE=/data/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Also set `capacity: 1` — with capacity > 1, concurrent jobs may not get host networking:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
runner:
|
||||||
|
capacity: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart runner: `docker restart gitea_act_runner_amd64`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `Unable to locate package git` in Rust Job
|
||||||
|
|
||||||
|
**Cause:** `rust:1.88-slim` has an empty apt package cache.
|
||||||
|
|
||||||
|
**Fix:** Always run `apt-get update` before `apt-get install`:
|
||||||
|
```yaml
|
||||||
|
- name: Checkout
|
||||||
|
run: |
|
||||||
|
apt-get update -qq && apt-get install -y -qq git
|
||||||
|
git init
|
||||||
|
git remote add origin http://172.0.0.29:3000/sarman/tftsr-devops_investigation.git
|
||||||
|
git fetch --depth=1 origin $GITHUB_SHA
|
||||||
|
git checkout FETCH_HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `exec: "node": executable file not found in $PATH`
|
||||||
|
|
||||||
|
**Cause:** `actions/checkout@v4` is a Node.js action. `rust:1.88-slim` and similar slim images don't have Node.
|
||||||
|
|
||||||
|
**Fix:** Don't use `actions/checkout@v4` — use direct git commands instead (see above).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Job Skipped (status 6) on Tag Push
|
||||||
|
|
||||||
|
**Cause:** Pattern matching issue with `on: push: tags:`. Use unquoted glob in the workflow:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Correct
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
```
|
||||||
|
|
||||||
|
Also add `workflow_dispatch` for manual triggering during testing:
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Release tag'
|
||||||
|
required: true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `CI=woodpecker` Rejected by `cargo tauri build`
|
||||||
|
|
||||||
|
**Cause:** CI runners set `CI=woodpecker` (string). Tauri CLI expects `true`/`false`.
|
||||||
|
|
||||||
|
**Fix:** Prefix the build command:
|
||||||
|
```yaml
|
||||||
|
- run: CI=true cargo tauri build --target $TARGET
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Release Artifacts Not Uploaded
|
||||||
|
|
||||||
|
**Cause 1:** `RELEASE_TOKEN` secret not set or expired.
|
||||||
|
```bash
|
||||||
|
# Recreate via admin CLI:
|
||||||
|
docker exec -u git gitea_app gitea admin user generate-access-token \
|
||||||
|
--username sarman --token-name gitea-ci-token --raw \
|
||||||
|
--scopes 'write:repository,read:user'
|
||||||
|
# Add the token as RELEASE_TOKEN in repo Settings > Actions > Secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause 2:** Each build job uploads its own artifacts independently. All jobs require host network on the runner (see above).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Rust Compilation
|
## Rust Compilation
|
||||||
|
|
||||||
### `MutexGuard` Not `Send` Across Await
|
### `MutexGuard` Not `Send` Across Await
|
||||||
|
|
||||||
**Error:**
|
|
||||||
```
|
```
|
||||||
error[E0277]: `MutexGuard<'_, Connection>` cannot be sent between threads safely
|
error[E0277]: `MutexGuard<'_, Connection>` cannot be sent between threads safely
|
||||||
```
|
```
|
||||||
|
|
||||||
**Fix:** Release the mutex lock before any `.await` point:
|
**Fix:** Release the mutex lock before any `.await` point:
|
||||||
```rust
|
```rust
|
||||||
// ✅ Correct
|
|
||||||
let result = {
|
let result = {
|
||||||
let db = state.db.lock().map_err(|e| e.to_string())?;
|
let db = state.db.lock().map_err(|e| e.to_string())?;
|
||||||
db.query_row(...)?
|
db.query_row(...)?
|
||||||
@ -76,21 +139,18 @@ async_fn().await?;
|
|||||||
|
|
||||||
### Clippy Lints Fail in CI
|
### Clippy Lints Fail in CI
|
||||||
|
|
||||||
Common lint fixes:
|
Common lint fixes required by `-D warnings` (Rust 1.88+):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// uninlined_format_args
|
|
||||||
format!("{}", x) → format!("{x}")
|
format!("{}", x) → format!("{x}")
|
||||||
|
|
||||||
// range::contains
|
|
||||||
x >= a && x < b → (a..b).contains(&x)
|
x >= a && x < b → (a..b).contains(&x)
|
||||||
|
|
||||||
// push_str single char
|
|
||||||
s.push_str("a") → s.push('a')
|
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`
|
||||||
|
|
||||||
|
Auto-fix: `cargo clippy --manifest-path src-tauri/Cargo.toml --fix --allow-dirty -- -D warnings`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `cargo tauri dev` Fails — Missing System Libraries
|
### `cargo tauri dev` Fails — Missing System Libraries
|
||||||
@ -101,6 +161,12 @@ sudo dnf install -y glib2-devel gtk3-devel webkit2gtk4.1-devel \
|
|||||||
libsoup3-devel openssl-devel librsvg2-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
|
## Database
|
||||||
@ -109,52 +175,41 @@ sudo dnf install -y glib2-devel gtk3-devel webkit2gtk4.1-devel \
|
|||||||
|
|
||||||
**Symptom:** App fails to start with SQLCipher error.
|
**Symptom:** App fails to start with SQLCipher error.
|
||||||
|
|
||||||
**Check:**
|
|
||||||
1. `TFTSR_DB_KEY` env var is set
|
1. `TFTSR_DB_KEY` env var is set
|
||||||
2. Key matches what was used when DB was created
|
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
|
|
||||||
sqlite3 tftsr.db "ATTACH 'new.db' AS newdb KEY 'new-key'; \
|
|
||||||
SELECT sqlcipher_export('newdb'); DETACH DATABASE newdb;"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Migration Fails to Run
|
### Migration Fails to Run
|
||||||
|
|
||||||
Check which migrations have already been applied:
|
Check which migrations have been applied:
|
||||||
```sql
|
```sql
|
||||||
SELECT name, applied_at FROM _migrations ORDER BY id;
|
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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Frontend
|
## Frontend
|
||||||
|
|
||||||
### TypeScript Errors After Pulling
|
### TypeScript Errors After Pulling
|
||||||
|
|
||||||
Run a fresh type check:
|
|
||||||
```bash
|
```bash
|
||||||
npx tsc --noEmit
|
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
|
### `IssueDetail` Field Access Errors
|
||||||
|
|
||||||
The `get_issue` command returns a **nested** struct:
|
`get_issue()` returns a **nested** struct:
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ Correct
|
// Correct
|
||||||
const title = detail.issue.title;
|
const title = detail.issue.title;
|
||||||
const severity = detail.issue.severity;
|
|
||||||
|
|
||||||
// ❌ Wrong — these fields don't exist at the top level
|
// Wrong — field doesn't exist at top level
|
||||||
const title = detail.title;
|
const title = detail.title;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -162,26 +217,25 @@ const title = detail.title;
|
|||||||
|
|
||||||
### Vitest Tests Fail
|
### Vitest Tests Fail
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run test:run
|
|
||||||
```
|
|
||||||
|
|
||||||
Common causes:
|
Common causes:
|
||||||
- Mocked `invoke()` return type doesn't match updated command signature
|
- Mocked `invoke()` return type doesn't match updated command signature
|
||||||
- `sessionStore` state not reset between tests (call `store.reset()` in `beforeEach`)
|
- `sessionStore` state not reset between tests (call `store.reset()` in `beforeEach`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Gogs
|
## Gitea
|
||||||
|
|
||||||
### Token Authentication
|
### API Token Authentication
|
||||||
|
|
||||||
The `sha1` field from the Gogs token create API **is** the bearer token — use it directly:
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Authorization: token <sha1_value>" https://gogs.tftsr.com/api/v1/user
|
curl -H "Authorization: token <token_value>" http://172.0.0.29:3000/api/v1/user
|
||||||
```
|
```
|
||||||
|
|
||||||
Do not confuse with the `sha1` column in the `access_token` table, which stores `sha1(token)[:40]`.
|
Create tokens in Gitea Settings > Applications > Access Tokens, or via admin CLI:
|
||||||
|
```bash
|
||||||
|
docker exec -u git gitea_app gitea admin user generate-access-token \
|
||||||
|
--username sarman --token-name mytoken --raw --scopes 'read:user,write:repository'
|
||||||
|
```
|
||||||
|
|
||||||
### PostgreSQL Access
|
### PostgreSQL Access
|
||||||
|
|
||||||
@ -189,4 +243,4 @@ Do not confuse with the `sha1` column in the `access_token` table, which stores
|
|||||||
docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name, is_private FROM repository;"
|
docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name, is_private FROM repository;"
|
||||||
```
|
```
|
||||||
|
|
||||||
Database is named `gogsdb`, not `gogs`.
|
Database is named `gogsdb`. The PostgreSQL instance uses SCRAM-SHA-256 auth (MD5 also configured for the `gogs` user for compatibility with older clients).
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user