Files

143 lines
4.8 KiB
Bash

#!/usr/bin/env bash
# servers/gcp/runners/startup.sh
#
# Boot script for the Gitea Actions runner GCE VM.
# - Installs docker + git + jq + the Forgejo/Gitea act_runner binary.
# - Pulls the runner registration token from GCP Secret Manager
# (the VM's attached service account has secretmanager.secretAccessor on it).
# - Registers against https://git.neuralplatform.ai with labels matching
# the workflows that target ubuntu-latest hosts.
# - Starts act_runner under systemd so it auto-recovers on reboot.
#
# Logs land at /var/log/runner-bootstrap.log for debugging from IAP SSH.
set -euxo pipefail
exec > >(tee /var/log/runner-bootstrap.log) 2>&1
apt-get update
# Core system tools and C build dependencies required by CI jobs.
# libcurl4-openssl-dev + build-essential are needed by the El compiler C build;
# libssl-dev/libsqlite3-dev/libpq-dev for downstream projects.
# python3 is for the inline label-rewrite step below.
apt-get install -y --no-install-recommends \
curl \
ca-certificates \
docker.io \
git \
jq \
python3 \
wget \
unzip \
zip \
xz-utils \
rsync \
file \
sudo \
make \
build-essential \
pkg-config \
gcc \
libcurl4-openssl-dev \
libssl-dev \
libsqlite3-dev \
libpq-dev \
libffi-dev \
zlib1g-dev
# Node.js 20 LTS via NodeSource — Ubuntu 24.04's bundled nodejs is 18.x
# which works but 20 LTS matches what our other CI images use.
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y --no-install-recommends nodejs
npm install -g yarn
# Make docker usable by the unprivileged runner user
systemctl enable --now docker
useradd -m -s /bin/bash runner || true
usermod -aG docker runner
# Allow the runner user to install packages and run system commands
# in CI workflow steps without a password prompt.
echo "runner ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# act_runner — pinned to a known-good release. Bump RUNNER_VERSION when
# upgrading. The project moved from gitea/act_runner to gitea/runner around
# the 0.6.x series; the binary inside the asset is still called act_runner.
# Latest releases at: https://gitea.com/gitea/runner/releases
RUNNER_VERSION="0.6.1"
curl -fsSL \
"https://gitea.com/gitea/runner/releases/download/v${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-linux-amd64" \
-o /usr/local/bin/act_runner
chmod +x /usr/local/bin/act_runner
mkdir -p /opt/runner
chown -R runner:runner /opt/runner
# Pull registration token. Fail fast if the secret is missing — the runner
# is useless without it, and a silent fallback would leave us debugging an
# unregistered VM later.
GITEA_RUNNER_TOKEN="$(gcloud secrets versions access latest \
--secret=gitea-runner-token \
--project=neuron-785695)"
# Generate a baseline config so the daemon has a sane starting point. The
# register step writes the .runner state file into /opt/runner.
sudo -u runner /usr/local/bin/act_runner generate-config > /opt/runner/config.yaml
chown runner:runner /opt/runner/config.yaml
cd /opt/runner
sudo -u runner /usr/local/bin/act_runner register \
--no-interactive \
--instance https://git.neuralplatform.ai \
--token "${GITEA_RUNNER_TOKEN}" \
--name "gcp-us-central1-runner-1" \
--labels "ubuntu-latest,ubuntu-22.04,ubuntu-24.04"
# Belt-and-braces: act_runner v0.6 has a habit of wrapping bare labels with
# the default `:docker://docker.gitea.com/runner-images:<label>` shape on
# first daemon start. We want host execution (build-stage.sh uses the
# host's Docker socket), so re-write the labels in the persisted state
# file before we start the daemon.
python3 - <<'PY'
import json
p = "/opt/runner/.runner"
with open(p) as f: d = json.load(f)
d["labels"] = ["ubuntu-latest", "ubuntu-22.04", "ubuntu-24.04"]
with open(p, "w") as f: json.dump(d, f, indent=2)
PY
chown runner:runner /opt/runner/.runner
cat > /etc/systemd/system/act_runner.service <<'EOF'
[Unit]
Description=Gitea Actions runner (act_runner)
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service
[Service]
Type=simple
User=runner
WorkingDirectory=/opt/runner
# Wipe the act cache on each daemon start. Without this, host-mode
# execution leaves stale action source trees with .git/objects/pack/*.idx
# files whose permissions trip the next run's `cp -a` step
# ("open ...idx: permission denied"). The cache is not load-bearing
# (act re-downloads actions on demand), so a clean start each restart
# is the simplest fix.
ExecStartPre=/bin/sh -c "rm -rf /home/runner/.cache/act/* /home/runner/.cache/act/.* 2>/dev/null || true"
ExecStart=/usr/local/bin/act_runner daemon --config /opt/runner/config.yaml
Restart=always
RestartSec=5
# Pass through GCP metadata-server access so workflows can call gcloud
Environment=HOME=/home/runner
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now act_runner
echo "act_runner registered and running"