# Self-Hosted GitHub Actions Runner Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Betreiben eines self-hosted GitHub Actions Runners auf `api.trucko.org` (128.140.47.53) mit dem Label `raynis-builder`, um Backend- und Admin-Builds für das rebreak-monorepo kostenlos auszuführen und weiterhin auf `staging.rebreak.org` zu deployen.

**Architecture:** Code-Hosting bleibt auf GitHub. GitHub Actions Workflows werden auf `runs-on: [self-hosted, raynis-builder]` umgestellt. Der Runner auf `api.trucko.org` checkt aus, baut das Artifact und kopiert es per SCP auf `staging.rebreak.org`. Dort übernimmt das bestehende `deploy-from-artifact.sh` das Entpacken, Migrations-Check und PM2-Restart.

**Tech Stack:** GitHub Actions, self-hosted Runner, Hetzner VPS, pnpm, Node.js 24.11.1, SSH/SCP, PM2.

---

## File Structure

| File / Pfad | Verantwortung | Aktion |
|---|---|---|
| `api.trucko.org` (Server) | Self-hosted Runner + Build-Umgebung | Einrichten |
| `staging.rebreak.org` (Server) | Production/Staging + Deploy-Script | SSH-Key hinzufügen |
| `.github/workflows/deploy-staging.yml` | Backend-Deploy-Workflow | Ändern: `runs-on` + SSH-Secret |
| `.github/workflows/deploy-admin-staging.yml` | Admin-Deploy-Workflow | Ändern: `runs-on` + SSH-Secret |
| GitHub Repo → Settings → Environments → staging | Secrets/Variables | `STAGING_DEPLOY_KEY` hinzufügen |

---

### Task 1: Server `api.trucko.org` vorbereiten

**Files:**
- Ausführen auf: `api.trucko.org` (per SSH)

- [ ] **Step 1: SSH auf Server verbinden**

```bash
ssh root@128.140.47.53
```

- [ ] **Step 2: System aktualisieren und Basis-Tools installieren**

```bash
apt-get update && apt-get install -y curl git build-essential
```

- [ ] **Step 3: Node.js 24.11.1 installieren (via nvm)**

```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 24.11.1
nvm use 24.11.1
node -v
```

Expected output: `v24.11.1`

- [ ] **Step 4: pnpm 10.23.0 installieren**

```bash
corepack enable
corepack prepare pnpm@10.23.0 --activate
pnpm -v
```

Expected output: `10.23.0`

- [ ] **Step 5: Git-Config für den Runner setzen**

```bash
git config --global user.name "Raynis Builder"
git config --global user.email "builder@raynis.dev"
```

- [ ] **Step 6: Workspace-Verzeichnis anlegen**

```bash
mkdir -p /srv/raynis-builder
chown root:root /srv/raynis-builder
```

---

### Task 2: GitHub Actions Runner installieren und registrieren

**Files:**
- Ausführen auf: `api.trucko.org`
- Zu prüfen in GitHub: Settings → Actions → Runners

- [ ] **Step 1: Runner-Version ermitteln (aktuellste)**

```bash
RUNNER_VERSION=$(curl -sL https://api.github.com/repos/actions/runner/releases/latest | jq -r '.tag_name' | sed 's/^v//')
echo "$RUNNER_VERSION"
```

- [ ] **Step 2: Runner herunterladen und entpacken**

```bash
cd /srv/raynis-builder
curl -o actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
tar xzf actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
rm actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
```

- [ ] **Step 3: Runner konfigurieren (Token aus GitHub holen)**

In GitHub: Repository `rebreak-monorepo` → Settings → Actions → Runners → New self-hosted runner → Linux → x64.

Dort angezeigte `config.sh`-Befehle auf dem Server ausführen, z. B.:

```bash
cd /srv/raynis-builder
./config.sh --url https://github.com/RaynisDev/rebreak --token <TOKEN_AUS_GITHUB> --name raynis-builder-01 --labels raynis-builder --work _work
```

- [ ] **Step 4: Runner als systemd-Service registrieren**

```bash
cd /srv/raynis-builder
./svc.sh install
./svc.sh start
./svc.sh status
```

Expected: `active (running)`

- [ ] **Step 5: Runner in GitHub UI als Online verifizieren**

In GitHub: Settings → Actions → Runners → Status muss `Idle` oder `Online` zeigen.

---

### Task 3: SSH-Deploy-Key zwischen Servern einrichten

**Files:**
- Ausführen auf: `api.trucko.org` und `staging.rebreak.org`

- [ ] **Step 1: SSH-Key auf `api.trucko.org` erzeugen**

```bash
ssh-keygen -t ed25519 -f /root/.ssh/rebreak-deploy -C "raynis-builder@api.trucko.org" -N ""
cat /root/.ssh/rebreak-deploy.pub
```

- [ ] **Step 2: Public Key auf `staging.rebreak.org` autorisieren**

```bash
ssh root@staging.rebreak.org
mkdir -p /root/.ssh
cat >> /root/.ssh/authorized_keys << 'EOF'
<PUBLIC_KEY_VON_OBEN>
EOF
chmod 600 /root/.ssh/authorized_keys
```

- [ ] **Step 3: Verbindung vom Builder zum Staging testen**

```bash
ssh -i /root/.ssh/rebreak-deploy root@staging.rebreak.org "whoami"
```

Expected output: `root`

- [ ] **Step 4: SSH-Konfiguration für einfacheren Zugriff anlegen**

Auf `api.trucko.org`:

```bash
cat >> /root/.ssh/config << 'EOF'
Host rebreak-staging
    HostName staging.rebreak.org
    User root
    IdentityFile ~/.ssh/rebreak-deploy
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null
EOF
chmod 600 /root/.ssh/config
```

- [ ] **Step 5: Verbindung mit Alias testen**

```bash
ssh rebreak-staging "whoami"
```

Expected output: `root`

---

### Task 4: GitHub Secrets aktualisieren

**Files:**
- GitHub Repository: Settings → Environments → staging

- [ ] **Step 1: Neuen Private Key als Secret hinzufügen**

Auf `api.trucko.org`:

```bash
cat /root/.ssh/rebreak-deploy
```

Inhalt kopieren und in GitHub einfügen:

- Environment: `staging`
- Secret-Name: `STAGING_DEPLOY_KEY`
- Wert: Inhalt von `/root/.ssh/rebreak-deploy`

- [ ] **Step 2: Bestehende Secrets überprüfen**

In GitHub prüfen, dass folgende Secrets/Vars im Environment `staging` vorhanden sind:

- `HETZNER_SSH_KEY` → kann später entfernt werden, wenn neuer Key funktioniert
- `HETZNER_HOST` → `staging.rebreak.org`
- `HETZNER_USER` → `root`

- [ ] **Step 3: Altes Secret nicht löschen (Fallback)**

`HETZNER_SSH_KEY` erst nach erfolgreichem Test-Deploy entfernen.

---

### Task 5: Workflows auf self-hosted Runner umstellen

**Files:**
- Modify: `.github/workflows/deploy-staging.yml`
- Modify: `.github/workflows/deploy-admin-staging.yml`

- [ ] **Step 1: `deploy-staging.yml` anpassen**

Ändere in beiden Jobs `runs-on: ubuntu-latest` zu:

```yaml
runs-on: [self-hosted, raynis-builder]
```

Ändere den SSH-Setup-Step, sodass er `STAGING_DEPLOY_KEY` verwendet:

```yaml
      - name: Setup SSH
        env:
          SSH_PRIVATE_KEY: ${{ secrets.STAGING_DEPLOY_KEY }}
          SSH_HOST: ${{ vars.HETZNER_HOST }}
        run: |
          if [ -z "$SSH_PRIVATE_KEY" ] || [ -z "$SSH_HOST" ]; then
            echo "FATAL: STAGING_DEPLOY_KEY oder HETZNER_HOST nicht gesetzt"
            exit 1
          fi
          mkdir -p ~/.ssh
          printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts
```

- [ ] **Step 2: `deploy-admin-staging.yml` analog anpassen**

Gleiche Änderungen wie bei `deploy-staging.yml`:

```yaml
runs-on: [self-hosted, raynis-builder]
```

und SSH-Setup-Step auf `STAGING_DEPLOY_KEY` umstellen.

- [ ] **Step 3: Node-Setup überprüfen**

Da der Runner Node.js 24.11.1 und pnpm 10.23.0 bereits hat, kann der Schritt `actions/setup-node` theoretisch entfallen. Für Robustheit aber beibehalten:

```yaml
      - uses: actions/setup-node@v4
        with:
          node-version: 24.11.1
          cache: pnpm
```

**Hinweis:** `cache: pnpm` funktioniert auf self-hosted Runnern nur, wenn der Runner persistent ist. Da der Server nicht bei jedem Job neu aufgesetzt wird, ist das gegeben.

- [ ] **Step 4: Beide Dateien lokal validieren**

```bash
cd /Users/chahinebrini/mono/rebreak-monorepo
git diff .github/workflows/deploy-staging.yml .github/workflows/deploy-admin-staging.yml
```

- [ ] **Step 5: Änderungen commiten**

```bash
git add .github/workflows/deploy-staging.yml .github/workflows/deploy-admin-staging.yml
git commit -m "ci: use self-hosted runner raynis-builder on api.trucko.org"
```

---

### Task 6: Test-Deploy durchführen

**Files:**
- GitHub Actions UI
- Server-Logs auf `api.trucko.org` und `staging.rebreak.org`

- [ ] **Step 1: Workflow manuell triggern**

In GitHub: Actions → Deploy Staging → Run workflow → Branch: main → Run.

- [ ] **Step 2: Build-Logs auf Runner verfolgen**

Auf `api.trucko.org`:

```bash
tail -f /srv/raynis-builder/_diag/Worker_*.log
tail -f /srv/raynis-builder/_diag/SelfUpdate-*.log
```

- [ ] **Step 3: Deploy-Logs auf Staging verfolgen**

Auf `staging.rebreak.org`:

```bash
pm2 logs rebreak-staging --lines 50
```

- [ ] **Step 4: Health-Check manuell ausführen**

Lokal oder auf `api.trucko.org`:

```bash
for i in $(seq 1 12); do
  STATUS=$(curl -sS -o /dev/null -w '%{http_code}' https://staging.rebreak.org/api/auth/me 2>/dev/null || echo "000")
  echo "Attempt $i: $STATUS"
  [ "$STATUS" = "401" ] || [ "$STATUS" = "200" ] && echo "PASSED" && break
  sleep 5
done
```

Expected: `PASSED`

- [ ] **Step 5: Admin-Deploy testen**

In GitHub: Actions → Deploy Admin Staging → Run workflow.

Health-Check:

```bash
curl -sS -o /dev/null -w '%{http_code}' https://admin.staging.rebreak.org/
```

Expected: Nicht `000`, `502` oder `503`.

---

### Task 7: Alten Webhook-Deploy abschalten (optional, nach stabilem Betrieb)

**Files:**
- Ausführen auf: `staging.rebreak.org`
- GitHub Repo: Settings → Webhooks

- [ ] **Step 1: Mindestens 3–5 erfolgreiche Deploys über neuen Runner abwarten**

- [ ] **Step 2: GitHub-Webhook deaktivieren**

In GitHub: Settings → Webhooks → `https://staging.rebreak.org/webhook` → Active: aus.

- [ ] **Step 3: Webhook-Service auf Server stoppen**

```bash
ssh root@staging.rebreak.org
pm2 stop rebreak-webhook
pm2 save
```

- [ ] **Step 4: Legacy-Dateien archivieren (nicht löschen)**

```bash
cd /srv/rebreak
mkdir -p scripts/legacy
mv scripts/deploy.sh scripts/legacy/deploy.sh
mv scripts/deploy-webhook scripts/legacy/deploy-webhook
```

- [ ] **Step 5: `ecosystem.config.js` bereinigen**

Entferne den Block für `rebreak-webhook` aus `ecosystem.config.js` und deploye die Änderung.

---

### Task 8: Cleanup und Monitoring

- [ ] **Step 1: Altes `HETZNER_SSH_KEY` Secret aus GitHub entfernen**

Nur nachdem `STAGING_DEPLOY_KEY` erfolgreich getestet wurde.

- [ ] **Step 2: Runner-Verfügbarkeit überwachen**

In GitHub: Settings → Actions → Runners → Status regelmäßig prüfen.

- [ ] **Step 3: Log-Rotation auf Runner einrichten**

```bash
logrotate --version || apt-get install -y logrotate
cat > /etc/logrotate.d/github-runner << 'EOF'
/srv/raynis-builder/_diag/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
}
EOF
```

- [ ] **Step 4: pnpm-Store auf Runner bereinigen (monatlich)**

```bash
pnpm store prune
```

---

## Self-Review

### Spec coverage

- Self-hosted Runner auf `api.trucko.org`: Task 1 + 2
- Label `raynis-builder`: Task 2
- Code-Hosting bleibt auf GitHub: implizit durch GitHub Actions
- Workflows auf `runs-on: [self-hosted, raynis-builder]`: Task 5
- Deploy zu `staging.rebreak.org`: Task 3 + 4 + 6
- Sicherheit (kein PR-Trigger): Task 5 (Workflow-Trigger bleibt `push: branches: [main]`)
- Webhook abschalten: Task 7

### Placeholder scan

Keine TBD/TODO. Alle Befehle sind konkret.

### Konsistenz

- Label durchgehend `raynis-builder`
- Secret-Name durchgehend `STAGING_DEPLOY_KEY`
- Node-Version durchgehend `24.11.1`
- pnpm-Version durchgehend `10.23.0`
- Server-IP durchgehend `128.140.47.53`
