swactor/HOW_TO_TEST_DRIVE.md

358 lines
10 KiB
Markdown
Raw Normal View History

# How to Test Drive the Local CI Runner over LAN
This guide walks through running the local CI runner on the Thinkpad and triggering it from Forgejo on your main laptop. By the end, pushes to your Forgejo repos will automatically run CI pipelines on the Thinkpad.
## Network Layout
```
┌──────────────────────┐ LAN ┌──────────────────────┐
│ Main Laptop │◄───────────────────►│ Thinkpad │
│ │ │ │
│ Forgejo instance │ webhook POST ───► │ local-runner │
│ (e.g. :3000) │ ◄── status API ──── │ (e.g. :8787) │
│ │ │ dashboard (:9090) │
└──────────────────────┘ └──────────────────────┘
```
## Prerequisites
- **Main laptop**: Forgejo running and accessible on LAN (e.g. `http://192.168.1.100:3000`)
- **Thinkpad**: Rust toolchain installed, this repo cloned, network-reachable from the main laptop
- Both machines on the same LAN (or routable to each other)
Find each machine's LAN IP:
```bash
# On each machine
ip addr show | grep 'inet ' | grep -v 127.0.0.1
# or
hostname -I
```
We'll use these example IPs throughout the guide:
- Main laptop (Forgejo): `192.168.1.100`
- Thinkpad (CI runner): `192.168.1.200`
Replace them with your actual IPs.
---
## Step 1: Create a Forgejo API Token
On your main laptop, open Forgejo in a browser:
1. Go to **Settings > Applications** (top-right user menu > Settings > Applications)
2. Create a new token with at least these permissions:
- `repo`: read/write (needed to post commit statuses)
3. Copy the token (e.g. `abc123def456`)
---
## Step 2: Write a `.ci.yml` for Your Repository
Create a `.ci.yml` file on the Thinkpad. This defines what pipelines and jobs to run.
Example for a Rust project:
```yaml
pipelines:
check:
triggers:
- event: push
branches: ["*"]
exclude: ["master"]
jobs:
fmt:
run: cargo fmt -- --check
clippy:
run: cargo clippy -- -D warnings
test:
needs: [fmt, clippy]
run: cargo test
timeout: 300
release:
triggers:
- event: push
branches: ["master"]
jobs:
test:
run: cargo test --all-features
timeout: 600
bench:
needs: [test]
run: cargo bench
```
Key points:
- `triggers[].event`: one of `push`, `tag`, `merge`
- `triggers[].branches`: glob patterns (`"*"` matches all, `"feature-*"` matches prefixed)
- `triggers[].exclude`: branches to skip
- `jobs[].run`: a single command string, or a list of commands (run sequentially)
- `jobs[].needs`: list of jobs that must pass first (DAG dependencies)
- `jobs[].timeout`: max seconds before the job is killed (default: 300)
- `jobs[].env`: extra environment variables as key-value pairs
Save this file somewhere accessible on the Thinkpad, e.g. `/home/user/ci/my-project.ci.yml`.
---
## Step 3: Build the Local Runner on the Thinkpad
SSH into the Thinkpad (or work directly on it):
```bash
# Clone the repo if not already present
git clone <repo-url> ~/swactor
cd ~/swactor
# Build the local-runner binary
cargo build --release -p local-runner
```
The binary will be at `target/release/local-runner`.
---
## Step 4: Create a Working Directory
The runner clones your repo into a working directory for each pipeline. Create it:
```bash
mkdir -p ~/ci-work
```
---
## Step 5: Start the Local Runner
```bash
./target/release/local-runner \
--port 8787 \
--forgejo-url http://192.168.1.100:3000 \
--forgejo-token abc123def456 \
--secret my-webhook-secret \
--yaml /home/user/ci/my-project.ci.yml \
--work-dir /home/user/ci-work \
--repo-url http://192.168.1.100:3000/user/repo.git \
--dashboard-port 9090
```
| Flag | Description |
|------|-------------|
| `--port` | Port the webhook listener binds to (default: `8787`) |
| `--forgejo-url` | URL of your Forgejo instance on the main laptop |
| `--forgejo-token` | API token created in Step 1 |
| `--secret` | Webhook secret (must match what you set in Forgejo, see Step 6) |
| `--yaml` | Path to the `.ci.yml` file on disk |
| `--work-dir` | Base directory for git clones (one subdirectory per pipeline) |
| `--repo-url` | Git clone URL for the repository (HTTP or SSH) |
| `--dashboard-port` | Optional: enables the web dashboard on this port |
You should see:
```
Webhook listener on http://0.0.0.0:8787
Local CI runner started
Webhook: http://0.0.0.0:8787
YAML: /home/user/ci/my-project.ci.yml
Workdir: /home/user/ci-work
Dashboard: http://0.0.0.0:9090
```
The runner is now listening for webhooks.
---
## Step 6: Configure the Forgejo Webhook
On your main laptop, open Forgejo and go to the repo's settings:
1. Navigate to **Settings > Webhooks > Add Webhook > Forgejo**
2. Fill in:
- **Target URL**: `http://192.168.1.200:8787` (Thinkpad's LAN IP and runner port)
- **Secret**: `my-webhook-secret` (must match `--secret` from Step 5)
- **Trigger On**: Choose which events to send:
- **Push Events** (for `push` triggers)
- **Create Events** (for `tag` triggers)
- **Pull Request Events** (for `merge` triggers)
- **Branch filter**: leave blank to send all branches, or set a pattern
- **Active**: checked
3. Click **Add Webhook**
### Test the webhook connection
After adding the webhook, Forgejo shows a **Test Delivery** button. Click it to send a test ping. Check the Thinkpad terminal for output. Forgejo also shows the response status — you should see `200 OK`.
---
## Step 7: Push and Watch
From your main laptop (or anywhere with push access):
```bash
cd ~/my-project
git checkout -b test-ci
echo "// test" >> src/main.rs
git add src/main.rs
git commit -m "test CI"
git push origin test-ci
```
On the Thinkpad terminal, you'll see the runner:
1. Receive the webhook
2. Match triggers in `.ci.yml`
3. Queue the pipeline
4. Clone the repo and checkout the commit SHA
5. Execute jobs one at a time, respecting the DAG order
6. Stream stdout/stderr in real time
7. Report commit statuses back to Forgejo
Back in Forgejo, the commit will show status checks (pending, then success/failure) next to the SHA.
---
## Step 8: View the Dashboard (Optional)
If you started with `--dashboard-port 9090`, open a browser on any LAN machine:
```
http://192.168.1.200:9090
```
This shows the swactor runtime dashboard with CI-specific panels: active pipelines, recent pipelines, job statuses, and actor system metrics.
---
## Behavior Reference
### One-at-a-Time Execution
Jobs run serially — only one job executes at any moment across all pipelines. This guarantees benchmark isolation with no resource contention.
### Queue Supersede
If you push twice to the same branch quickly:
- **First push** is already running: it finishes normally
- **Second push** is queued: it runs after the first finishes
- **Third push** arrives while second is still queued: the second is **superseded** (marked as error, skipped), and the third takes its place in the queue
Only queued pipelines get superseded — a running pipeline always runs to completion.
### DAG Dependencies
Within a pipeline, jobs respect their `needs` dependencies. If `test` needs `[fmt, clippy]`, then `fmt` runs first, then `clippy`, then `test`. If `fmt` fails, `test` is skipped.
### CI Environment Variables
Every job command has these injected:
| Variable | Value |
|----------|-------|
| `CI` | `true` |
| `CI_COMMIT_SHA` | The commit being tested |
| `CI_BRANCH` | The branch name |
| `CI_PIPELINE_ID` | Numeric pipeline identifier |
| `CI_JOB_NAME` | Name of the current job |
Plus any `env` keys from the job definition in `.ci.yml`.
### Commit Status Reporting
The runner posts status updates to the Forgejo API for each pipeline and each job:
- `pending` when a pipeline/job is queued
- `success` when all jobs pass
- `failure` when a job fails
- `error` when a pipeline is superseded
These appear as commit status checks in Forgejo's UI.
---
## Troubleshooting
### Webhook not reaching the Thinkpad
- Verify the Thinkpad's firewall allows inbound on the webhook port:
```bash
# On Thinkpad
sudo ufw allow 8787/tcp # if using ufw
# or
sudo iptables -A INPUT -p tcp --dport 8787 -j ACCEPT
```
- Confirm connectivity from main laptop:
```bash
curl -v http://192.168.1.200:8787
# Should get "method not allowed" (405) — that means the server is reachable
```
### Signature mismatch (401)
- The `--secret` flag on the runner must exactly match the **Secret** field in Forgejo's webhook config
- If you don't want signature verification, set both to empty strings (omit `--secret` and leave Secret blank in Forgejo)
### Git clone fails
- Make sure `--repo-url` is reachable from the Thinkpad:
```bash
# On Thinkpad
git ls-remote http://192.168.1.100:3000/user/repo.git
```
- If the repo is private, use an authenticated URL:
```
http://user:password@192.168.1.100:3000/user/repo.git
```
Or use SSH:
```
git@192.168.1.100:user/repo.git
```
### Status updates not appearing in Forgejo
- Verify the API token has `repo` write permissions
- Check the Thinkpad terminal for `StatusReporter: failed to post status` errors
- Test the token manually:
```bash
curl -H "Authorization: token abc123def456" \
http://192.168.1.100:3000/api/v1/user
```
### Jobs failing unexpectedly
- Check that the Thinkpad has the necessary toolchain (cargo, rustup, etc.)
- The working directory for each pipeline is `{work-dir}/pipeline-{id}/` — you can inspect it
- Job stdout/stderr is streamed to the runner's terminal output
---
## Quick-Start Cheat Sheet
```bash
# === Thinkpad ===
cd ~/swactor
cargo build --release -p local-runner
mkdir -p ~/ci-work
./target/release/local-runner \
--port 8787 \
--forgejo-url http://LAPTOP_IP:3000 \
--forgejo-token YOUR_TOKEN \
--secret YOUR_SECRET \
--yaml /path/to/.ci.yml \
--work-dir ~/ci-work \
--repo-url http://LAPTOP_IP:3000/user/repo.git \
--dashboard-port 9090
# === Main Laptop (Forgejo) ===
# Repo > Settings > Webhooks > Add Webhook:
# URL: http://THINKPAD_IP:8787
# Secret: YOUR_SECRET
# Events: Push, Create, Pull Request
# === Test it ===
git push origin my-branch # triggers CI on the Thinkpad
```