162 lines
5.2 KiB
Bash
Executable File
162 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# entrypoint.sh — Container startup script for the GitHub Actions runner.
|
|
#
|
|
# Lifecycle:
|
|
# 1. Validate required environment variables
|
|
# 2. Generate a short-lived registration token from GITHUB_PAT
|
|
# 3. Configure the runner in ephemeral mode (one job, then exit)
|
|
# 4. Trap SIGTERM/SIGINT for graceful deregistration
|
|
# 5. Start the runner (run.sh)
|
|
#
|
|
# Docker's restart policy (restart: unless-stopped) brings the container
|
|
# back after each job completes, repeating this cycle.
|
|
|
|
set -euo pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Configuration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
RUNNER_DIR="/home/runner/actions-runner"
|
|
RUNNER_LABELS="${RUNNER_LABELS:-self-hosted,Linux,X64}"
|
|
RUNNER_GROUP="${RUNNER_GROUP:-default}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
log() {
|
|
printf '[entrypoint] %s\n' "$*"
|
|
}
|
|
|
|
die() {
|
|
printf '[entrypoint] ERROR: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Environment validation — fail fast with clear errors
|
|
# ---------------------------------------------------------------------------
|
|
|
|
validate_env() {
|
|
local missing=()
|
|
|
|
[[ -z "${GITHUB_PAT:-}" ]] && missing+=("GITHUB_PAT")
|
|
[[ -z "${REPO_URL:-}" ]] && missing+=("REPO_URL")
|
|
[[ -z "${RUNNER_NAME:-}" ]] && missing+=("RUNNER_NAME")
|
|
|
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
die "Missing required environment variables: ${missing[*]}. Check your .env and envs/*.env files."
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Token generation — PAT → short-lived registration token
|
|
# ---------------------------------------------------------------------------
|
|
|
|
generate_token() {
|
|
# Extract OWNER/REPO from the full URL.
|
|
# Supports: https://github.com/OWNER/REPO or https://github.com/OWNER/REPO.git
|
|
local repo_slug
|
|
repo_slug="$(printf '%s' "$REPO_URL" \
|
|
| sed -E 's#^https?://github\.com/##' \
|
|
| sed -E 's/\.git$//')"
|
|
|
|
if [[ -z "$repo_slug" ]] || ! printf '%s' "$repo_slug" | grep -qE '^[^/]+/[^/]+$'; then
|
|
die "Could not parse OWNER/REPO from REPO_URL: $REPO_URL"
|
|
fi
|
|
|
|
log "Generating registration token for ${repo_slug}..."
|
|
|
|
local response
|
|
response="$(curl -fsSL \
|
|
-X POST \
|
|
-H "Authorization: token ${GITHUB_PAT}" \
|
|
-H "Accept: application/vnd.github+json" \
|
|
"https://api.github.com/repos/${repo_slug}/actions/runners/registration-token")"
|
|
|
|
REG_TOKEN="$(printf '%s' "$response" | jq -r '.token // empty')"
|
|
|
|
if [[ -z "$REG_TOKEN" ]]; then
|
|
die "Failed to generate registration token. Check that GITHUB_PAT has 'repo' scope and is valid."
|
|
fi
|
|
|
|
log "Registration token obtained (expires in 1 hour)."
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Cleanup — deregister runner on container stop
|
|
# ---------------------------------------------------------------------------
|
|
|
|
cleanup() {
|
|
log "Caught signal, removing runner registration..."
|
|
|
|
# Generate a removal token (different from registration token)
|
|
local repo_slug
|
|
repo_slug="$(printf '%s' "$REPO_URL" \
|
|
| sed -E 's#^https?://github\.com/##' \
|
|
| sed -E 's/\.git$//')"
|
|
|
|
local remove_token
|
|
remove_token="$(curl -fsSL \
|
|
-X POST \
|
|
-H "Authorization: token ${GITHUB_PAT}" \
|
|
-H "Accept: application/vnd.github+json" \
|
|
"https://api.github.com/repos/${repo_slug}/actions/runners/remove-token" \
|
|
| jq -r '.token // empty' || true)"
|
|
|
|
if [[ -n "$remove_token" ]]; then
|
|
"${RUNNER_DIR}/config.sh" remove --token "$remove_token" 2>/dev/null || true
|
|
log "Runner deregistered."
|
|
else
|
|
log "WARNING: Could not obtain removal token. Runner may appear stale in GitHub until it expires."
|
|
fi
|
|
|
|
exit 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Main
|
|
# ---------------------------------------------------------------------------
|
|
|
|
main() {
|
|
validate_env
|
|
generate_token
|
|
|
|
# Trap signals for graceful shutdown
|
|
trap cleanup SIGTERM SIGINT
|
|
|
|
# Remove stale configuration from previous run.
|
|
# On container restart (vs recreate), the runner's writable layer persists
|
|
# and config.sh refuses to re-configure if .runner already exists.
|
|
# The --replace flag only handles server-side name conflicts, not this local check.
|
|
if [[ -f "${RUNNER_DIR}/.runner" ]]; then
|
|
log "Removing stale runner configuration from previous run..."
|
|
rm -f "${RUNNER_DIR}/.runner" "${RUNNER_DIR}/.credentials" "${RUNNER_DIR}/.credentials_rsaparams"
|
|
fi
|
|
|
|
log "Configuring runner '${RUNNER_NAME}' for ${REPO_URL}..."
|
|
log "Labels: ${RUNNER_LABELS}"
|
|
log "Group: ${RUNNER_GROUP}"
|
|
|
|
"${RUNNER_DIR}/config.sh" \
|
|
--url "${REPO_URL}" \
|
|
--token "${REG_TOKEN}" \
|
|
--name "${RUNNER_NAME}" \
|
|
--labels "${RUNNER_LABELS}" \
|
|
--runnergroup "${RUNNER_GROUP}" \
|
|
--work "/home/runner/_work" \
|
|
--ephemeral \
|
|
--unattended \
|
|
--replace
|
|
|
|
log "Runner configured. Starting..."
|
|
|
|
# exec replaces the shell with the runner process.
|
|
# The runner picks up one job, executes it, and exits.
|
|
# Docker's restart policy restarts the container for the next job.
|
|
exec "${RUNNER_DIR}/run.sh"
|
|
}
|
|
|
|
main "$@"
|