CI: dedicated cached runner for this repo (deploy script + volume caches)
- scripts/deploy-runner.sh: compose-managed BigBrainParking Forgejo runner (adapted from the Camp Scan one), with valid_volumes for bigbrainparking-gradle and bigbrainparking-npm. No android-sdk volume — the RN build image ships it. - Workflow mounts those caches (GRADLE_USER_HOME + npm cache) and enables the Gradle build cache (org.gradle.caching + --build-cache), so repeat builds skip the dep re-download and recompile that dominate a cold ~35 min build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
208cddee33
commit
ba3ae8b3e6
2 changed files with 161 additions and 1 deletions
|
|
@ -26,6 +26,15 @@ jobs:
|
||||||
# and NO container swap (--memory-swap == --memory) so it OOM-fails cleanly
|
# and NO container swap (--memory-swap == --memory) so it OOM-fails cleanly
|
||||||
# instead of thrashing the disk with swap. With the JVM caps below it uses ~5 GB.
|
# instead of thrashing the disk with swap. With the JVM caps below it uses ~5 GB.
|
||||||
options: --cpus=2 --memory=8g --memory-swap=8g
|
options: --cpus=2 --memory=8g --memory-swap=8g
|
||||||
|
# Persistent caches (whitelisted in the runner's config.yaml valid_volumes).
|
||||||
|
# Gradle home = downloaded Maven deps + build cache; npm = package cache.
|
||||||
|
# Repeat builds skip the re-download/re-compile that dominate a cold build.
|
||||||
|
volumes:
|
||||||
|
- bigbrainparking-gradle:/opt/cache/gradle
|
||||||
|
- bigbrainparking-npm:/opt/cache/npm
|
||||||
|
env:
|
||||||
|
GRADLE_USER_HOME: /opt/cache/gradle
|
||||||
|
npm_config_cache: /opt/cache/npm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|
@ -60,6 +69,7 @@ jobs:
|
||||||
org.gradle.workers.max=2
|
org.gradle.workers.max=2
|
||||||
org.gradle.parallel=false
|
org.gradle.parallel=false
|
||||||
org.gradle.daemon=false
|
org.gradle.daemon=false
|
||||||
|
org.gradle.caching=true
|
||||||
kotlin.daemon.jvmargs=-Xmx1536m
|
kotlin.daemon.jvmargs=-Xmx1536m
|
||||||
PROPS
|
PROPS
|
||||||
|
|
||||||
|
|
@ -73,10 +83,11 @@ jobs:
|
||||||
# Also cap the no-daemon launcher JVM (org.gradle.jvmargs targets the daemon).
|
# Also cap the no-daemon launcher JVM (org.gradle.jvmargs targets the daemon).
|
||||||
GRADLE_OPTS: "-Xmx1536m -Djava.net.preferIPv4Stack=true"
|
GRADLE_OPTS: "-Xmx1536m -Djava.net.preferIPv4Stack=true"
|
||||||
# --max-workers=2 limits concurrent tasks; container --cpus=2 caps clang -j.
|
# --max-workers=2 limits concurrent tasks; container --cpus=2 caps clang -j.
|
||||||
|
# --build-cache reuses task outputs from GRADLE_USER_HOME across runs.
|
||||||
run: >-
|
run: >-
|
||||||
./gradlew :app:assembleRelease
|
./gradlew :app:assembleRelease
|
||||||
-PreactNativeArchitectures=arm64-v8a
|
-PreactNativeArchitectures=arm64-v8a
|
||||||
--no-daemon --max-workers=2
|
--no-daemon --max-workers=2 --build-cache
|
||||||
|
|
||||||
# Self-contained publish via the Forgejo API (no external action to fetch).
|
# Self-contained publish via the Forgejo API (no external action to fetch).
|
||||||
- name: Publish release + upload APK
|
- name: Publish release + upload APK
|
||||||
|
|
|
||||||
149
scripts/deploy-runner.sh
Executable file
149
scripts/deploy-runner.sh
Executable file
|
|
@ -0,0 +1,149 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Deploy the BigBrainParking Forgejo Actions runner on a host (Ubuntu/Debian).
|
||||||
|
# Self-contained: installs Docker if missing, writes the runner compose + env,
|
||||||
|
# registers against git.mowden.top, and starts the runner.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# sudo bash deploy-runner.sh <REGISTRATION_TOKEN> [INSTALL_DIR]
|
||||||
|
#
|
||||||
|
# REGISTRATION_TOKEN From Forgejo -> hank/BigBrainParking ->
|
||||||
|
# Settings -> Actions -> Runners -> Create new runner.
|
||||||
|
# INSTALL_DIR Where to place the runner (default /opt/bigbrainparking-runner).
|
||||||
|
#
|
||||||
|
# Re-running is safe: it updates files and restarts. The token is only consumed
|
||||||
|
# on the first registration (stored afterwards in <INSTALL_DIR>/data/.runner).
|
||||||
|
#
|
||||||
|
# NOTE: this host also runs the Camp Scan runner and several databases on a small
|
||||||
|
# box (20 GB / 4 cores). The BigBrainParking build workflow hard-caps itself to
|
||||||
|
# --cpus=2 --memory=8g, but if a Camp Scan build and a BigBrainParking build run
|
||||||
|
# at the SAME time they'll together want ~4 cores / ~16 GB. Keep an eye on that,
|
||||||
|
# or lower the caps in .forgejo/workflows/build-apk.yml if the box gets tight.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
FORGEJO_INSTANCE="https://git.mowden.top"
|
||||||
|
RUNNER_IMAGE="code.forgejo.org/forgejo/runner:6.3.1"
|
||||||
|
RUNNER_NAME="${RUNNER_NAME:-$(hostname)-bigbrainparking}"
|
||||||
|
JOB_LABEL="docker:docker://node:22-bookworm"
|
||||||
|
|
||||||
|
TOKEN="${1:-${REGISTRATION_TOKEN:-}}"
|
||||||
|
INSTALL_DIR="${2:-/opt/bigbrainparking-runner}"
|
||||||
|
|
||||||
|
log() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
|
||||||
|
err() { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; }
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
err "Run with sudo/root (needs to install packages and access the Docker socket)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$TOKEN" ] && [ ! -f "$INSTALL_DIR/data/.runner" ]; then
|
||||||
|
err "No registration token given and no existing registration found."
|
||||||
|
echo "Usage: sudo bash deploy-runner.sh <REGISTRATION_TOKEN> [INSTALL_DIR]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 1. Docker ---------------------------------------------------------------
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
log "Installing Docker (get.docker.com)..."
|
||||||
|
curl -fsSL https://get.docker.com | sh
|
||||||
|
systemctl enable --now docker
|
||||||
|
else
|
||||||
|
log "Docker already installed: $(docker --version)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker compose version >/dev/null 2>&1; then
|
||||||
|
log "Installing docker compose plugin..."
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y docker-compose-plugin
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOCKER_GID="$(stat -c '%g' /var/run/docker.sock)"
|
||||||
|
log "Docker socket group id: $DOCKER_GID"
|
||||||
|
|
||||||
|
# --- 2. Files ----------------------------------------------------------------
|
||||||
|
log "Writing runner files to $INSTALL_DIR"
|
||||||
|
mkdir -p "$INSTALL_DIR/data"
|
||||||
|
# The runner image runs as uid/gid 1000 and must own its config dir.
|
||||||
|
chown -R 1000:1000 "$INSTALL_DIR/data"
|
||||||
|
|
||||||
|
# Runner config: allow the build workflow to mount the persistent cache volumes
|
||||||
|
# (Gradle home = Maven deps + build cache, npm cache) so APK builds don't
|
||||||
|
# redownload and recompile everything each run. Without valid_volumes listed,
|
||||||
|
# the runner rejects the workflow's `volumes:` and the job fails.
|
||||||
|
# (No android-sdk volume: the build image already ships the Android SDK/NDK.)
|
||||||
|
cat > "$INSTALL_DIR/config.yaml" <<'CONFIG'
|
||||||
|
container:
|
||||||
|
valid_volumes:
|
||||||
|
- bigbrainparking-gradle
|
||||||
|
- bigbrainparking-npm
|
||||||
|
CONFIG
|
||||||
|
chown 1000:1000 "$INSTALL_DIR/config.yaml"
|
||||||
|
|
||||||
|
# Pre-create the named cache volumes (idempotent).
|
||||||
|
docker volume create bigbrainparking-gradle >/dev/null
|
||||||
|
docker volume create bigbrainparking-npm >/dev/null
|
||||||
|
|
||||||
|
cat > "$INSTALL_DIR/docker-compose.yml" <<COMPOSE
|
||||||
|
services:
|
||||||
|
runner:
|
||||||
|
image: ${RUNNER_IMAGE}
|
||||||
|
container_name: bigbrainparking-runner
|
||||||
|
restart: unless-stopped
|
||||||
|
working_dir: /data
|
||||||
|
group_add:
|
||||||
|
- "\${DOCKER_GID}"
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./config.yaml:/data/config.yaml:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
env_file: .env
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
if [ ! -f /data/.runner ]; then
|
||||||
|
echo "Registering runner with ${FORGEJO_INSTANCE} ..."
|
||||||
|
forgejo-runner register --no-interactive \\
|
||||||
|
--instance "${FORGEJO_INSTANCE}" \\
|
||||||
|
--token "\${REGISTRATION_TOKEN}" \\
|
||||||
|
--name "\${RUNNER_NAME}" \\
|
||||||
|
--labels "${JOB_LABEL}"
|
||||||
|
fi
|
||||||
|
exec forgejo-runner daemon --config /data/config.yaml
|
||||||
|
COMPOSE
|
||||||
|
|
||||||
|
# Preserve an existing token line if re-running without a new token.
|
||||||
|
cat > "$INSTALL_DIR/.env" <<ENV
|
||||||
|
REGISTRATION_TOKEN=${TOKEN}
|
||||||
|
RUNNER_NAME=${RUNNER_NAME}
|
||||||
|
DOCKER_GID=${DOCKER_GID}
|
||||||
|
ENV
|
||||||
|
chmod 600 "$INSTALL_DIR/.env"
|
||||||
|
|
||||||
|
# --- 3. Start ----------------------------------------------------------------
|
||||||
|
log "Starting runner..."
|
||||||
|
cd "$INSTALL_DIR"
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
sleep 6
|
||||||
|
echo
|
||||||
|
log "Status:"
|
||||||
|
docker compose ps --format 'table {{.Name}}\t{{.Status}}'
|
||||||
|
echo
|
||||||
|
log "Recent logs (look for 'declared successfully' then 'poller launched'):"
|
||||||
|
docker compose logs --no-log-prefix 2>&1 | tail -8
|
||||||
|
|
||||||
|
cat <<DONE
|
||||||
|
|
||||||
|
------------------------------------------------------------------
|
||||||
|
BigBrainParking runner deployed to $INSTALL_DIR
|
||||||
|
Logs: cd $INSTALL_DIR && docker compose logs -f
|
||||||
|
Restart: cd $INSTALL_DIR && docker compose restart
|
||||||
|
Stop: cd $INSTALL_DIR && docker compose down
|
||||||
|
|
||||||
|
The signing secrets (ANDROID_KEYSTORE_B64 etc.) are already set on the repo.
|
||||||
|
Push a tag (e.g. v0.1.3) to trigger the APK build; it runs on THIS host and
|
||||||
|
reuses the bigbrainparking-gradle / bigbrainparking-npm caches.
|
||||||
|
------------------------------------------------------------------
|
||||||
|
DONE
|
||||||
Loading…
Add table
Add a link
Reference in a new issue