# Self-Hosted GitHub Actions Runner

The CBB GitHub organization (`ColdBoreBallistics`) has **one self-hosted runner**, on **mac001**, used exclusively by the `deploy-test.yml` workflows in the two website repos. All other CBB workflows use GitHub-hosted runners (`ubuntu-latest`, `macos-latest`).

## Current state

| Field | Value |
|---|---|
| Host | `mac001` (macOS, Apple Silicon) |
| Org | `ColdBoreBallistics` (org-level runner, not repo-scoped) |
| Runner name | `mac001` |
| Labels | `self-hosted, macOS, ARM64, mac001` |
| Install path | `~/actions-runner/` (`/Users/jschwefel/actions-runner/`) |
| Version | `2.335.1` (auto-updates) |
| Service | macOS LaunchAgent `actions.runner.ColdBoreBallistics.mac001` at `~/Library/LaunchAgents/actions.runner.ColdBoreBallistics.mac001.plist` |
| Logs | `~/Library/Logs/actions.runner.ColdBoreBallistics.mac001/` |
| Work dir | `~/actions-runner/_work/` |

The runner runs as the logged-in user (`jschwefel`), not as `root`. That matches macOS conventions and lets the worker traverse `/Users/jschwefel/` without permission gymnastics. **Caveat:** the LaunchAgent only runs while the user is logged in — if you log out, the runner stops. mac001 is dedicated to CBB work and always logged in (sleep is disabled per the always-on server role), so this is fine in practice.

## Workflows that use this runner

| Repo | Workflow | Purpose |
|---|---|---|
| `www.scope-dope.com` | `.github/workflows/deploy-test.yml` | Deploy to `www-test.scope-dope.com` (internal-test vhost on `mac001:443`) |
| `www.coldboreballisticsllc.com` | `.github/workflows/deploy-test.yml` | Deploy to `www-test.coldboreballisticsllc.com` (internal-test vhost on `mac001:443`) |

Both workflows use bare `runs-on: self-hosted`. Since mac001 is the only self-hosted runner, all jobs land there.

## Common operations

### Service control

```bash
cd ~/actions-runner
./svc.sh status   # show LaunchAgent status
./svc.sh stop     # stop the runner
./svc.sh start    # start the runner
./svc.sh restart  # bounce
```

### Check that it's online from the org's perspective

```bash
gh api /orgs/ColdBoreBallistics/actions/runners --jq '.runners[] | {name, status, busy, labels: [.labels[].name]}'
```

### View runner logs

```bash
tail -f ~/Library/Logs/actions.runner.ColdBoreBallistics.mac001/Runner_*.log
```

### Recent workflow runs across both website repos

```bash
gh run list -R ColdBoreBallistics/www.scope-dope.com -L 5
gh run list -R ColdBoreBallistics/www.coldboreballisticsllc.com -L 5
```

## Reinstall / re-register

If the runner ever needs to be reinstalled from scratch:

```bash
# 1) Fresh registration token (needs admin:org scope on gh CLI)
TOKEN=$(gh api -X POST /orgs/ColdBoreBallistics/actions/runners/registration-token --jq '.token')

# 2) Download latest runner (check https://github.com/actions/runner/releases for current version)
RUNNER_VERSION="2.335.1"
mkdir -p ~/actions-runner && cd ~/actions-runner
curl -L -o runner.tar.gz "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-osx-arm64-${RUNNER_VERSION}.tar.gz"
tar xzf runner.tar.gz && rm runner.tar.gz

# 3) Register
./config.sh \
  --url https://github.com/ColdBoreBallistics \
  --token "$TOKEN" \
  --name mac001 \
  --labels self-hosted,macOS,ARM64,mac001 \
  --work _work \
  --unattended \
  --replace

# 4) Install as LaunchAgent and start
./svc.sh install
./svc.sh start
```

The `--replace` flag lets re-registration silently take over an existing `mac001` entry on the org side.

## Tearing down (clean uninstall)

```bash
# 1) Stop and uninstall the LaunchAgent
cd ~/actions-runner
./svc.sh stop
./svc.sh uninstall

# 2) Unregister from the org. The interactive way:
REMOVE=$(gh api -X POST /orgs/ColdBoreBallistics/actions/runners/remove-token --jq '.token')
./config.sh remove --token "$REMOVE"

# 3) Or force-delete from the org via API (use when config.sh remove can't be run):
RUNNER_ID=$(gh api /orgs/ColdBoreBallistics/actions/runners --jq '.runners[] | select(.name=="mac001") | .id')
gh api -X DELETE /orgs/ColdBoreBallistics/actions/runners/$RUNNER_ID

# 4) Remove the install dir
rm -rf ~/actions-runner
```

## Acquisition lens

A self-hosted runner on a workstation is a single-device dependency, same category flag as the `.200` nginx services. **Acceptable now**, **not acceptable for acquisition**. The runner exists specifically to deploy to LAN-internal `mac001:443` vhosts. If that internal-test workflow moves off-LAN (e.g. to Azure Static Web Apps or to a hosted CI deploy) and these `deploy-test.yml` workflows are rewritten to use GitHub-hosted runners with cloud deploy targets, the self-hosted runner can be retired entirely. Track this under the same mac001-SOT stopgap.

## History

| Date | Change |
|---|---|
| 2026-06-13 | Runner established on `mac001` (macOS, ARM64) as the org's sole self-hosted runner. Both `deploy-test.yml` workflows verified executing on mac001, checking out/pulling the website repos with `REPO=/Users/jschwefel/...` paths. Any prior runner on a non-CBB host was decommissioned and its org record deleted. |

---

## Document Control

| Field | Value |
|---|---|
| **Document owner** | `jschwefel@coldboreballisticsllc.com` |
| **Classification** | Confidential (03_-_Confidential/) |
| **Location** | `OneDrive/CBB_Corporate/03_-_Confidential/Infrastructure/Websites/Self_Hosted_Runner.md` |
| **Cross-references** | `Internal_Site_Operations.md`; CLAUDE.md → "mac001 — Standing SOT for Everything CBB" |
| **Created** | 2026-06-13 |
| **Next full review due** | 2027-06-13 |

## Revision Control Block

| Rev | Date | Changed By | Reviewed By | Approved By | Summary |
|-----|------|------------|-------------|-------------|---------|
| 0.9 | 2026-06-13 | jschwefel (via Claude Code) | — | — | Initial release — runner established on mac001 as the sole org self-hosted runner |
