docs(devops): add Hostinger self-hosted GitHub runner setup prompt
Delegation prompt for the Codex agent running on the Hostinger VM to: - Install a dedicated GitHub Actions self-hosted runner under gha-runner user - Register it with saravanakumardb1/learning_ai_common_plat - Run as a systemd service with auto-start - Install Node 20 / pnpm 9 / gh CLI / Docker prerequisites - Wire up local Gitea publish token Includes full end-to-end validation that proves the actual publish pipeline works: - Creates a throwaway @bytelyst/_runner-e2e-test package - Publishes to local Gitea - Uploads tarball as GitHub Release asset - Verifies Gitea registry returns the version - Verifies pnpm install + require works from a clean directory - Verifies the byte-identical-tarball invariant (sha256 match between Gitea-served tarball and GitHub Release asset) — this is the key guarantee that lockfiles will remain portable across corp Mac's local Gitea after sync - Documents cleanup of test artifacts Plus pre-flight checks, hardening (systemd limits, log rotation, workflow approval), scaling notes, deliverables checklist, guardrails, rollback, and follow-up prompt list (publish-packages.yml, bytelyst-sync script, SKILL doc).
This commit is contained in:
parent
269b4d8784
commit
d5e0778af6
531
docs/devops/HOSTINGER_GITHUB_RUNNER_SETUP.md
Normal file
531
docs/devops/HOSTINGER_GITHUB_RUNNER_SETUP.md
Normal file
@ -0,0 +1,531 @@
|
||||
# Hostinger VM — GitHub Actions Self-Hosted Runner Setup
|
||||
|
||||
> **Delegation prompt for the Codex agent running on the Hostinger VM.**
|
||||
> Read top-to-bottom before executing. Stop and ask the human if any pre-flight check fails or any deliverable is unclear.
|
||||
|
||||
---
|
||||
|
||||
## 1. Goal
|
||||
|
||||
Set up a GitHub Actions self-hosted runner on the Hostinger VM that can:
|
||||
|
||||
1. Receive workflow triggers from `saravanakumardb1/learning_ai_common_plat` (and, later, all `@bytelyst/*` repos).
|
||||
2. Build `@bytelyst/*` npm packages from a tagged release.
|
||||
3. Publish them to **the local Gitea instance on this VM** (`http://localhost:3300/api/packages/bytelyst/npm/`).
|
||||
4. Upload the same tarballs as **GitHub Release assets** so a corp-network Mac can sync them into its own local Gitea (via a separate `bytelyst-sync` script described in a follow-up prompt).
|
||||
|
||||
Self-hosted on Hostinger beats GitHub-hosted runners because:
|
||||
|
||||
- No GitHub Actions minute cap.
|
||||
- Gitea is on `localhost` from this VM → zero-latency publish, no public TLS needed.
|
||||
- VM is always on; runner is reachable indefinitely.
|
||||
|
||||
---
|
||||
|
||||
## 2. Pre-flight checks (run first, do not skip)
|
||||
|
||||
```bash
|
||||
# 1. Confirm Linux VM
|
||||
hostname && uname -a # Expected: Linux
|
||||
|
||||
# 2. Confirm Gitea is running locally
|
||||
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3300/ # Expected: 200 or 302
|
||||
|
||||
# 3. Confirm Gitea npm registry endpoint reachable
|
||||
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3300/api/packages/bytelyst/npm/ # Expected: 200 or 401
|
||||
|
||||
# 4. Confirm Node 20 and pnpm 9 installable
|
||||
node --version 2>/dev/null || echo "Node not installed (will install in Step 5)"
|
||||
pnpm --version 2>/dev/null || echo "pnpm not installed (will install in Step 5)"
|
||||
|
||||
# 5. Confirm gh CLI exists
|
||||
gh --version 2>/dev/null || echo "gh CLI not installed (will install in Step 5)"
|
||||
|
||||
# 6. Disk free
|
||||
df -h / # Need ~5 GB headroom
|
||||
|
||||
# 7. Confirm no existing runner
|
||||
ls -la ~/actions-runner 2>/dev/null && echo "Runner dir exists — STOP and confirm with human" || echo "No existing runner"
|
||||
|
||||
# 8. Confirm github.com reachable
|
||||
curl -s -o /dev/null -w "%{http_code}\n" https://api.github.com/ # Expected: 200
|
||||
|
||||
# 9. Confirm the Gitea token file exists somewhere on this VM
|
||||
sudo find /home /root -maxdepth 3 -name ".gitea_npm_token" 2>/dev/null | head -5
|
||||
# Expected: at least one path. Note the owning user — needed in Step 6.
|
||||
```
|
||||
|
||||
If any check fails or surprises you, **stop and report back** before proceeding.
|
||||
|
||||
---
|
||||
|
||||
## 3. What you'll create
|
||||
|
||||
| Item | Path/Identifier |
|
||||
| -------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| Dedicated Linux user | `gha-runner` |
|
||||
| Runner installation | `/home/gha-runner/actions-runner/` |
|
||||
| systemd service | `actions.runner.saravanakumardb1-learning_ai_common_plat.hostinger-bytelyst-1.service` |
|
||||
| Runner labels | `self-hosted, linux, x64, hostinger, bytelyst` |
|
||||
| Gitea publish token (copy) | `/home/gha-runner/.gitea_npm_token` (mode 600) |
|
||||
| Smoke-test workflow | `.github/workflows/runner-smoke.yml` (commit on a branch) |
|
||||
| E2E test workflow | `.github/workflows/runner-e2e-publish.yml` (commit on a branch) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Installation
|
||||
|
||||
### Step 1 — Create the dedicated runner user
|
||||
|
||||
```bash
|
||||
sudo useradd -m -s /bin/bash gha-runner
|
||||
sudo usermod -aG docker gha-runner # only if any workflow uses Docker
|
||||
id gha-runner
|
||||
```
|
||||
|
||||
### Step 2 — Get a GitHub runner registration token (one-time, ~1h TTL)
|
||||
|
||||
**Preferred (via `gh` CLI auth'd as `saravanakumardb1`):**
|
||||
|
||||
```bash
|
||||
gh api -X POST /repos/saravanakumardb1/learning_ai_common_plat/actions/runners/registration-token --jq .token
|
||||
```
|
||||
|
||||
**Alternative (browser):** Open `https://github.com/saravanakumardb1/learning_ai_common_plat/settings/actions/runners/new?arch=x64&os=linux` and copy the token from the `./config.sh` command shown.
|
||||
|
||||
Hold the token in shell memory only:
|
||||
|
||||
```bash
|
||||
read -s RUNNER_TOKEN # paste, press Enter (no echo)
|
||||
```
|
||||
|
||||
### Step 3 — Download, verify, configure the runner
|
||||
|
||||
```bash
|
||||
sudo -iu gha-runner bash <<'EOF'
|
||||
mkdir -p ~/actions-runner && cd ~/actions-runner
|
||||
RUNNER_VERSION="2.319.1"
|
||||
|
||||
# Download
|
||||
curl -fSL -o "actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz" \
|
||||
"https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz"
|
||||
|
||||
# Verify SHA against the GitHub release page (https://github.com/actions/runner/releases/tag/v2.319.1).
|
||||
# If the sha doesn't match, STOP and report.
|
||||
|
||||
tar xzf "./actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz"
|
||||
EOF
|
||||
```
|
||||
|
||||
Register:
|
||||
|
||||
```bash
|
||||
sudo -u gha-runner -E -i bash -c "
|
||||
cd ~/actions-runner && \
|
||||
./config.sh \
|
||||
--url https://github.com/saravanakumardb1/learning_ai_common_plat \
|
||||
--token $RUNNER_TOKEN \
|
||||
--name hostinger-bytelyst-1 \
|
||||
--labels self-hosted,linux,x64,hostinger,bytelyst \
|
||||
--work _work \
|
||||
--replace \
|
||||
--unattended
|
||||
"
|
||||
|
||||
unset RUNNER_TOKEN
|
||||
```
|
||||
|
||||
Verify in GitHub UI: runner should show "Idle" green under `Settings → Actions → Runners`.
|
||||
|
||||
### Step 4 — Install as a systemd service
|
||||
|
||||
```bash
|
||||
sudo bash -c "
|
||||
cd /home/gha-runner/actions-runner && \
|
||||
./svc.sh install gha-runner && \
|
||||
./svc.sh start
|
||||
"
|
||||
|
||||
SVC_NAME='actions.runner.saravanakumardb1-learning_ai_common_plat.hostinger-bytelyst-1.service'
|
||||
sudo systemctl status "$SVC_NAME"
|
||||
sudo journalctl -u "$SVC_NAME" -n 30 --no-pager
|
||||
# Expected: "active (running)" + "Listening for Jobs"
|
||||
```
|
||||
|
||||
### Step 5 — Install Node 20, pnpm 9, gh CLI system-wide
|
||||
|
||||
```bash
|
||||
# Node 20 via Nodesource
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
# gh CLI (if not already)
|
||||
sudo apt-get install -y gh # or follow https://cli.github.com/manual/installation if 'gh' isn't in the apt repos
|
||||
|
||||
# pnpm
|
||||
sudo npm install -g pnpm@9
|
||||
|
||||
# Verify all reachable from the runner user
|
||||
sudo -u gha-runner bash -c 'node --version && pnpm --version && gh --version'
|
||||
```
|
||||
|
||||
### Step 6 — Give the runner access to the local Gitea publish token
|
||||
|
||||
```bash
|
||||
# Identify the source token file (from pre-flight check #9)
|
||||
SRC_TOKEN=/home/<original-user>/.gitea_npm_token
|
||||
|
||||
# Copy and lock down
|
||||
sudo cp "$SRC_TOKEN" /home/gha-runner/.gitea_npm_token
|
||||
sudo chown gha-runner:gha-runner /home/gha-runner/.gitea_npm_token
|
||||
sudo chmod 600 /home/gha-runner/.gitea_npm_token
|
||||
|
||||
# Verify
|
||||
sudo -u gha-runner bash -c 'wc -c < ~/.gitea_npm_token && stat -c "%a %U:%G" ~/.gitea_npm_token'
|
||||
# Expected: nonzero byte count, mode 600, owner gha-runner:gha-runner
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Smoke test (basic — runner picks up jobs)
|
||||
|
||||
Create branch `runner/smoke` in `learning_ai_common_plat` with this file:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/runner-smoke.yml
|
||||
name: Runner Smoke Test
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [runner/smoke]
|
||||
jobs:
|
||||
smoke:
|
||||
runs-on: [self-hosted, linux, hostinger]
|
||||
steps:
|
||||
- run: echo "host=$(hostname) user=$(whoami) cwd=$(pwd)"
|
||||
- run: node --version && pnpm --version && gh --version
|
||||
- run: |
|
||||
echo "Gitea health:"
|
||||
curl -s -o /dev/null -w " http://localhost:3300/ → %{http_code}\n" http://localhost:3300/
|
||||
curl -s -o /dev/null -w " /api/packages/bytelyst/npm/ → %{http_code}\n" \
|
||||
http://localhost:3300/api/packages/bytelyst/npm/
|
||||
- run: |
|
||||
if [ -f ~/.gitea_npm_token ]; then
|
||||
echo "Gitea token present, $(wc -c < ~/.gitea_npm_token) bytes, mode $(stat -c %a ~/.gitea_npm_token)"
|
||||
else
|
||||
echo "ERROR: Gitea token missing"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
Trigger from GitHub UI (Actions tab → Runner Smoke Test → Run workflow on `runner/smoke`). All steps must pass.
|
||||
|
||||
---
|
||||
|
||||
## 6. End-to-end validation (CRITICAL — proves the actual use case)
|
||||
|
||||
The smoke test only proves the runner can execute. The **E2E test** proves the **whole publish pipeline** works: package builds, publishes to local Gitea, uploads to GitHub Releases, and is installable on a different machine.
|
||||
|
||||
### E2E test workflow
|
||||
|
||||
Create a **temporary test package** in `learning_ai_common_plat` (will be removed after validation):
|
||||
|
||||
```bash
|
||||
# On Hostinger or from human's machine — does NOT need to run on the runner
|
||||
mkdir -p packages/_runner-e2e-test
|
||||
cat > packages/_runner-e2e-test/package.json <<'EOF'
|
||||
{
|
||||
"name": "@bytelyst/_runner-e2e-test",
|
||||
"version": "0.0.1",
|
||||
"description": "Throwaway package for E2E validating the Hostinger runner. Safe to delete after validation.",
|
||||
"main": "index.js",
|
||||
"files": ["index.js"]
|
||||
}
|
||||
EOF
|
||||
echo "module.exports = { ok: true, builtAt: new Date().toISOString() };" \
|
||||
> packages/_runner-e2e-test/index.js
|
||||
git checkout -b runner/e2e
|
||||
git add packages/_runner-e2e-test/
|
||||
git commit -m "test: add throwaway package for runner E2E validation"
|
||||
git push origin runner/e2e
|
||||
```
|
||||
|
||||
Create the E2E workflow on the same branch:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/runner-e2e-publish.yml
|
||||
name: Runner E2E — publish + release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to publish (semver)'
|
||||
required: true
|
||||
default: '0.0.1-e2e.1'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: [self-hosted, linux, hostinger]
|
||||
permissions:
|
||||
contents: write # for GitHub Release creation
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set test version
|
||||
working-directory: packages/_runner-e2e-test
|
||||
run: |
|
||||
npm version "${{ inputs.version }}" --no-git-tag-version --allow-same-version
|
||||
cat package.json
|
||||
|
||||
- name: Configure pnpm registry for Gitea
|
||||
working-directory: packages/_runner-e2e-test
|
||||
run: |
|
||||
cat > .npmrc <<NPMRC
|
||||
@bytelyst:registry=http://localhost:3300/api/packages/bytelyst/npm/
|
||||
//localhost:3300/api/packages/bytelyst/npm/:_authToken=$(cat ~/.gitea_npm_token)
|
||||
NPMRC
|
||||
echo ".npmrc written:"
|
||||
sed 's|_authToken=.*|_authToken=***|' .npmrc
|
||||
|
||||
- name: Publish to local Gitea
|
||||
working-directory: packages/_runner-e2e-test
|
||||
run: pnpm publish --no-git-checks --registry http://localhost:3300/api/packages/bytelyst/npm/
|
||||
|
||||
- name: Pack tarball for GitHub Release
|
||||
working-directory: packages/_runner-e2e-test
|
||||
run: |
|
||||
pnpm pack --pack-destination /tmp
|
||||
ls -la /tmp/bytelyst-_runner-e2e-test-*.tgz
|
||||
|
||||
- name: Create GitHub Release with tarball
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
TAG="e2e-runner-${{ inputs.version }}"
|
||||
gh release create "$TAG" \
|
||||
/tmp/bytelyst-_runner-e2e-test-*.tgz \
|
||||
--title "Runner E2E test $TAG" \
|
||||
--notes "Throwaway release from runner E2E validation. Safe to delete." \
|
||||
--prerelease
|
||||
|
||||
- name: Verify package queryable from Gitea
|
||||
run: |
|
||||
AUTH_HEADER="Authorization: Bearer $(cat ~/.gitea_npm_token)"
|
||||
curl -s -H "$AUTH_HEADER" \
|
||||
"http://localhost:3300/api/packages/bytelyst/npm/@bytelyst%2F_runner-e2e-test" \
|
||||
| head -200
|
||||
echo ""
|
||||
# Assert the version we just published is in the response
|
||||
curl -s -H "$AUTH_HEADER" \
|
||||
"http://localhost:3300/api/packages/bytelyst/npm/@bytelyst%2F_runner-e2e-test" \
|
||||
| grep -q '"${{ inputs.version }}"' || { echo "FAIL: version not found in Gitea registry"; exit 1; }
|
||||
echo "PASS: version ${{ inputs.version }} is in Gitea registry"
|
||||
|
||||
- name: Verify pnpm install works from a clean directory
|
||||
run: |
|
||||
mkdir -p /tmp/runner-e2e-consumer && cd /tmp/runner-e2e-consumer
|
||||
cat > .npmrc <<NPMRC
|
||||
@bytelyst:registry=http://localhost:3300/api/packages/bytelyst/npm/
|
||||
//localhost:3300/api/packages/bytelyst/npm/:_authToken=$(cat ~/.gitea_npm_token)
|
||||
NPMRC
|
||||
cat > package.json <<JSON
|
||||
{ "name": "runner-e2e-consumer", "version": "1.0.0", "dependencies": { "@bytelyst/_runner-e2e-test": "${{ inputs.version }}" } }
|
||||
JSON
|
||||
pnpm install --no-frozen-lockfile
|
||||
node -e "const m = require('@bytelyst/_runner-e2e-test'); console.log('Module loaded:', m); process.exit(m.ok ? 0 : 1);"
|
||||
echo "PASS: pnpm install + require works end-to-end"
|
||||
|
||||
- name: Verify GitHub Release tarball matches Gitea tarball (byte-identical)
|
||||
run: |
|
||||
# The released tarball should be byte-identical to what we pushed to Gitea
|
||||
RELEASED=$(ls /tmp/bytelyst-_runner-e2e-test-*.tgz)
|
||||
GITEA_URL=$(curl -s -H "Authorization: Bearer $(cat ~/.gitea_npm_token)" \
|
||||
"http://localhost:3300/api/packages/bytelyst/npm/@bytelyst%2F_runner-e2e-test" \
|
||||
| grep -oE '"tarball":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
curl -s -H "Authorization: Bearer $(cat ~/.gitea_npm_token)" -o /tmp/from-gitea.tgz "$GITEA_URL"
|
||||
SHA_RELEASED=$(sha256sum "$RELEASED" | awk '{print $1}')
|
||||
SHA_GITEA=$(sha256sum /tmp/from-gitea.tgz | awk '{print $1}')
|
||||
echo "Released: $SHA_RELEASED"
|
||||
echo "Gitea: $SHA_GITEA"
|
||||
[ "$SHA_RELEASED" = "$SHA_GITEA" ] || { echo "FAIL: tarball mismatch — bytes-identical guarantee broken"; exit 1; }
|
||||
echo "PASS: tarball is byte-identical between Gitea and GitHub Release"
|
||||
```
|
||||
|
||||
### Run the E2E
|
||||
|
||||
```bash
|
||||
# From any machine that has gh auth'd as saravanakumardb1:
|
||||
gh workflow run runner-e2e-publish.yml --ref runner/e2e -f version=0.0.1-e2e.1 --repo saravanakumardb1/learning_ai_common_plat
|
||||
gh run watch --repo saravanakumardb1/learning_ai_common_plat
|
||||
```
|
||||
|
||||
### E2E pass criteria — ALL must succeed
|
||||
|
||||
| Check | Step that proves it |
|
||||
| ----------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| Runner picks up the job | "Set up job" log in GH Actions UI shows `Runner name: hostinger-bytelyst-1` |
|
||||
| Package publishes to Gitea | `pnpm publish` step exits 0 |
|
||||
| GitHub Release created with tarball asset | `gh release view e2e-runner-0.0.1-e2e.1 --json assets` returns 1 asset |
|
||||
| Gitea reports the version | `Verify package queryable from Gitea` step says `PASS: version ... is in Gitea registry` |
|
||||
| Consumer install works | `Verify pnpm install works` step says `PASS: pnpm install + require works end-to-end` |
|
||||
| Release tarball ≡ Gitea tarball (sha256) | `Verify GitHub Release tarball matches Gitea tarball` says `PASS: tarball is byte-identical` |
|
||||
|
||||
The last check is the **key invariant** for the corp-Mac sync flow: same bytes → same SHA512 integrity hash → lockfiles portable across both Gitea instances.
|
||||
|
||||
### Cleanup after E2E
|
||||
|
||||
```bash
|
||||
# Delete the test release
|
||||
gh release delete e2e-runner-0.0.1-e2e.1 --yes --repo saravanakumardb1/learning_ai_common_plat
|
||||
|
||||
# Delete the test package from local Gitea (via API or UI under Packages → @bytelyst/_runner-e2e-test → Delete)
|
||||
|
||||
# Delete the test branch and package
|
||||
git checkout main
|
||||
git push origin --delete runner/e2e
|
||||
rm -rf packages/_runner-e2e-test
|
||||
git checkout main
|
||||
git commit -am "test: remove runner E2E throwaway package" || true
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Leave both workflow files (`runner-smoke.yml`, `runner-e2e-publish.yml`) **on `main`** — they're idempotent and provide a way to re-validate the runner anytime (just bump the version input).
|
||||
|
||||
---
|
||||
|
||||
## 7. Hardening (do before relying for prod)
|
||||
|
||||
### a. Workflow approval for external PRs
|
||||
|
||||
`Settings → Actions → General → Fork pull request workflows` → "Require approval for all outside collaborators". Repo is private so risk is contained, but keep approval-required on.
|
||||
|
||||
### b. systemd resource limits
|
||||
|
||||
```bash
|
||||
sudo systemctl edit "$SVC_NAME"
|
||||
```
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
CPUQuota=200%
|
||||
MemoryMax=4G
|
||||
TasksMax=2048
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart "$SVC_NAME"
|
||||
```
|
||||
|
||||
### c. Log rotation
|
||||
|
||||
```bash
|
||||
sudo tee /etc/logrotate.d/gha-runner > /dev/null <<'EOF'
|
||||
/home/gha-runner/actions-runner/_diag/*.log {
|
||||
weekly
|
||||
rotate 4
|
||||
compress
|
||||
missingok
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
### d. Update mechanism
|
||||
|
||||
GitHub auto-updates the runner agent unless disabled. Keep auto-update on. Pin a minimum version in workflows if you need a specific feature:
|
||||
|
||||
```yaml
|
||||
runs-on: [self-hosted, linux, hostinger]
|
||||
```
|
||||
|
||||
### e. Auth scope of the runner
|
||||
|
||||
The runner's `GITHUB_TOKEN` (provided by GitHub Actions automatically) is scoped per-workflow. Verify in `runner-e2e-publish.yml` that `permissions:` is set narrowly (we set `contents: write` only for release creation).
|
||||
|
||||
---
|
||||
|
||||
## 8. Scaling to more repos later
|
||||
|
||||
A single runner installation can serve multiple repos **only if** registered at org level. For your personal-account setup:
|
||||
|
||||
- **Recommended:** Move all 20+ repos to a free GitHub organization. Register the runner once at org level. Single runner serves everyone.
|
||||
- **Workaround for now:** Add the same runner to additional repos by re-running `config.sh` with each repo's URL and a fresh token (creates separate registrations sharing the same physical binary). Acceptable up to 2–3 repos.
|
||||
|
||||
Recommend evaluating the org migration before scaling beyond 2 actively-publishing repos.
|
||||
|
||||
---
|
||||
|
||||
## 9. Deliverables — report back to the human
|
||||
|
||||
When complete:
|
||||
|
||||
1. **Service status:**
|
||||
```bash
|
||||
sudo systemctl is-active "$SVC_NAME"
|
||||
```
|
||||
2. **GitHub UI confirmation:** screenshot or text "Runner shows 'Idle' green at github.com/saravanakumardb1/learning_ai_common_plat/settings/actions/runners".
|
||||
3. **Smoke test run URL** — workflow passed.
|
||||
4. **E2E test run URL** — workflow passed, all 6 pass criteria green.
|
||||
5. **Installed versions:**
|
||||
```bash
|
||||
sudo -u gha-runner bash -c 'node --version; pnpm --version; gh --version; docker --version 2>/dev/null || echo "no docker"'
|
||||
```
|
||||
6. **Log paths:**
|
||||
- systemd: `journalctl -u <service-name>`
|
||||
- runner diag: `/home/gha-runner/actions-runner/_diag/`
|
||||
7. **Confirmation that cleanup happened:** test release deleted, test package removed from Gitea, throwaway package deleted from repo.
|
||||
|
||||
---
|
||||
|
||||
## 10. Guardrails
|
||||
|
||||
- **Do not** run the runner as root.
|
||||
- **Do not** persist the GitHub registration token to disk — memory only.
|
||||
- **Do not** install under the `gitea` user or any other service user — keep concerns separated.
|
||||
- **Do not** open inbound ports on the VM firewall — the runner is outbound-only long-poll.
|
||||
- **Do not** skip the E2E test. The smoke test alone does not prove the publish pipeline works.
|
||||
- **Do not** mark E2E as passed unless all 6 pass criteria succeed, especially the byte-identical tarball check.
|
||||
- **Do not** leave the throwaway `@bytelyst/_runner-e2e-test` package in the Gitea registry — it pollutes the namespace.
|
||||
|
||||
---
|
||||
|
||||
## 11. Rollback
|
||||
|
||||
```bash
|
||||
SVC_NAME='actions.runner.saravanakumardb1-learning_ai_common_plat.hostinger-bytelyst-1.service'
|
||||
|
||||
# Stop and uninstall systemd service
|
||||
sudo bash -c "cd /home/gha-runner/actions-runner && ./svc.sh stop && ./svc.sh uninstall"
|
||||
|
||||
# Unregister from GitHub (need a fresh removal token)
|
||||
REMOVAL_TOKEN=$(gh api -X POST /repos/saravanakumardb1/learning_ai_common_plat/actions/runners/remove-token --jq .token)
|
||||
sudo -u gha-runner bash -c "cd ~/actions-runner && ./config.sh remove --token $REMOVAL_TOKEN"
|
||||
|
||||
# Remove the user and all its files
|
||||
sudo userdel -r gha-runner
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Follow-up prompts (separate tasks)
|
||||
|
||||
Once this runner is verified end-to-end, the next prompts to issue:
|
||||
|
||||
1. **`publish-packages.yml`** — the real production workflow in `learning_ai_common_plat`, modeled on the E2E template above, that triggers on `v*` tags and publishes all changed `@bytelyst/*` packages.
|
||||
2. **`bytelyst-sync` script** — runs on the corp Mac; downloads GitHub Release tarballs and republishes to the corp local Gitea. Verifies sha256 against Gitea before considering sync successful.
|
||||
3. **SKILL doc** at `AI.dev/SKILLS/gitea-package-sync.md` — describes the full three-system flow for future contributors.
|
||||
|
||||
---
|
||||
|
||||
## 13. Questions to ask the human BEFORE starting if anything is ambiguous
|
||||
|
||||
- "Which GitHub repo am I registering this runner for? (default: `saravanakumardb1/learning_ai_common_plat`)"
|
||||
- "Is Docker required on the runner — i.e., does any planned workflow run `docker` commands? (default: no, only Gitea uses Docker)"
|
||||
- "What user currently owns `~/.gitea_npm_token` on this VM? (pre-flight check #9 will tell us)"
|
||||
- "Do you have a runner registration token, or should I fetch one via `gh api`?"
|
||||
- "Are you OK with me creating a throwaway `@bytelyst/_runner-e2e-test` package, publishing it, and then deleting it as part of E2E validation?"
|
||||
|
||||
If any of these are unclear, stop and ask before installing anything.
|
||||
Loading…
Reference in New Issue
Block a user