#!/usr/bin/dumb-init /bin/bash # shellcheck shell=bash export RUNNER_ALLOW_RUNASROOT=1 export PATH=${PATH}:/actions-runner # Un-export these, so that they must be passed explicitly to the environment of # any command that needs them. This may help prevent leaks. export -n ACCESS_TOKEN export -n RUNNER_TOKEN export -n APP_ID export -n APP_PRIVATE_KEY trap_with_arg() { func="$1" ; shift for sig ; do # shellcheck disable=SC2064 trap "$func $sig" "$sig" done } _DEREGISTERED=false deregister_runner() { if [[ "${_DEREGISTERED}" == "true" ]]; then return fi _DEREGISTERED=true local CAUGHT="${1:-EXIT}" echo "Caught ${CAUGHT} - Deregistering runner" if [[ -n "${ACCESS_TOKEN}" ]]; then # If using GitHub App authentication, refresh the access token before deregistration if [[ -n "${APP_ID}" ]] && [[ -n "${APP_PRIVATE_KEY}" ]] && [[ -n "${APP_LOGIN}" ]]; then echo "Refreshing access token for deregistration" nl=" " NEW_ACCESS_TOKEN=$(APP_ID="${APP_ID}" APP_PRIVATE_KEY="${APP_PRIVATE_KEY//\\n/${nl}}" APP_LOGIN="${APP_LOGIN}" bash /app_token.sh) if [[ -z "${NEW_ACCESS_TOKEN}" ]] || [[ "${NEW_ACCESS_TOKEN}" == "null" ]]; then echo "ERROR: Failed to refresh access token for deregistration" exit 1 fi ACCESS_TOKEN="${NEW_ACCESS_TOKEN}" echo "Access token refreshed successfully" fi _TOKEN=$(ACCESS_TOKEN="${ACCESS_TOKEN}" bash /token.sh) RUNNER_TOKEN=$(echo "${_TOKEN}" | jq -r .token) fi ./config.sh remove --token "${RUNNER_TOKEN}" [[ -f "/actions-runner/.runner" ]] && rm -f /actions-runner/.runner if [[ "${CAUGHT}" != "EXIT" ]]; then exit fi } _DEBUG_ONLY=${DEBUG_ONLY:-false} _DEBUG_OUTPUT=${DEBUG_OUTPUT:-false} _DISABLE_AUTOMATIC_DEREGISTRATION=${DISABLE_AUTOMATIC_DEREGISTRATION:-false} _RANDOM_RUNNER_SUFFIX=${RANDOM_RUNNER_SUFFIX:="true"} _RUNNER_NAME=${RUNNER_NAME:-${RUNNER_NAME_PREFIX:-github-runner}-$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo '')} if [[ ${RANDOM_RUNNER_SUFFIX} != "true" ]]; then # In some cases this file does not exist if [[ -f "/etc/hostname" ]]; then # in some cases it can also be empty if [[ $(stat --printf="%s" /etc/hostname) -ne 0 ]]; then _RUNNER_NAME_PREFIX=${RUNNER_NAME_PREFIX-"github-runner"} _RUNNER_NAME=${RUNNER_NAME:-${_RUNNER_NAME_PREFIX:+${_RUNNER_NAME_PREFIX}-}$(cat /etc/hostname)} echo "RANDOM_RUNNER_SUFFIX is ${RANDOM_RUNNER_SUFFIX}. /etc/hostname exists and has content. Setting runner name to ${_RUNNER_NAME}" else echo "RANDOM_RUNNER_SUFFIX is ${RANDOM_RUNNER_SUFFIX} ./etc/hostname exists but is empty. Not using /etc/hostname." fi else echo "RANDOM_RUNNER_SUFFIX is ${RANDOM_RUNNER_SUFFIX} but /etc/hostname does not exist. Not using /etc/hostname." fi fi _RUNNER_WORKDIR=${RUNNER_WORKDIR:-/_work/${_RUNNER_NAME}} _LABELS=${RUNNER_LABELS:-${LABELS:-default}} _RUNNER_GROUP=${RUNNER_GROUP:-Default} _GITHUB_HOST=${GITHUB_HOST:="github.com"} _GITHUB_HOST="${_GITHUB_HOST#http://}" _GITHUB_HOST="${_GITHUB_HOST#https://}" _GITHUB_HOST="${_GITHUB_HOST%%/}" _RUN_AS_ROOT=${RUN_AS_ROOT:="true"} _START_DOCKER_SERVICE=${START_DOCKER_SERVICE:="false"} _UNSET_CONFIG_VARS=${UNSET_CONFIG_VARS:="false"} _CONFIGURED_ACTIONS_RUNNER_FILES_DIR=${CONFIGURED_ACTIONS_RUNNER_FILES_DIR:-""} # ensure backwards compatibility if [[ -z ${RUNNER_SCOPE} ]]; then if [[ ${ORG_RUNNER} == "true" ]]; then echo 'ORG_RUNNER is now deprecated. Please use RUNNER_SCOPE="org" instead.' export RUNNER_SCOPE="org" else export RUNNER_SCOPE="repo" fi fi RUNNER_SCOPE="${RUNNER_SCOPE,,}" # to lowercase case ${RUNNER_SCOPE} in org*) [[ -z ${ORG_NAME} ]] && ( echo "ORG_NAME required for org runners"; exit 1 ) _SHORT_URL="https://${_GITHUB_HOST}/${ORG_NAME}" RUNNER_SCOPE="org" if [[ -n "${APP_ID}" ]] && [[ -z "${APP_LOGIN}" ]]; then APP_LOGIN=${ORG_NAME} fi ;; ent*) [[ -z ${ENTERPRISE_NAME} ]] && ( echo "ENTERPRISE_NAME required for enterprise runners"; exit 1 ) _SHORT_URL="https://${_GITHUB_HOST}/enterprises/${ENTERPRISE_NAME}" RUNNER_SCOPE="enterprise" ;; *) [[ -z ${REPO_URL} ]] && ( echo "REPO_URL required for repo runners"; exit 1 ) _SHORT_URL=${REPO_URL} RUNNER_SCOPE="repo" if [[ -n "${APP_ID}" ]] && [[ -z "${APP_LOGIN}" ]]; then APP_LOGIN=${REPO_URL%/*} APP_LOGIN=${APP_LOGIN##*/} fi ;; esac configure_runner() { ARGS=() if [[ -n "${APP_ID}" ]] && [[ -n "${APP_PRIVATE_KEY}" ]] && [[ -n "${APP_LOGIN}" ]]; then if [[ -n "${ACCESS_TOKEN}" ]] || [[ -n "${RUNNER_TOKEN}" ]]; then echo "ERROR: ACCESS_TOKEN or RUNNER_TOKEN provided but are mutually exclusive with APP_ID, APP_PRIVATE_KEY and APP_LOGIN." >&2 exit 1 fi echo "Obtaining access token for app_id ${APP_ID} and login ${APP_LOGIN}" nl=" " ACCESS_TOKEN=$(APP_ID="${APP_ID}" APP_PRIVATE_KEY="${APP_PRIVATE_KEY//\\n/${nl}}" APP_LOGIN="${APP_LOGIN}" bash /app_token.sh) elif [[ -n "${APP_ID}" ]] || [[ -n "${APP_PRIVATE_KEY}" ]] || [[ -n "${APP_LOGIN}" ]]; then echo "ERROR: All of APP_ID, APP_PRIVATE_KEY and APP_LOGIN must be specified." >&2 exit 1 fi if [[ -n "${ACCESS_TOKEN}" ]]; then echo "Obtaining the token of the runner" _TOKEN=$(ACCESS_TOKEN="${ACCESS_TOKEN}" bash /token.sh) RUNNER_TOKEN=$(echo "${_TOKEN}" | jq -r .token) fi # shellcheck disable=SC2153 if [ -n "${EPHEMERAL}" ]; then echo "Ephemeral option is enabled" ARGS+=("--ephemeral") fi if [ -n "${DISABLE_AUTO_UPDATE}" ]; then echo "Disable auto update option is enabled" ARGS+=("--disableupdate") fi if [ -n "${NO_DEFAULT_LABELS}" ]; then echo "Disable adding the default self-hosted, platform, and architecture labels" ARGS+=("--no-default-labels") fi echo "Configuring" ./config.sh \ --url "${_SHORT_URL}" \ --token "${RUNNER_TOKEN}" \ --name "${_RUNNER_NAME}" \ --work "${_RUNNER_WORKDIR}" \ --labels "${_LABELS}" \ --runnergroup "${_RUNNER_GROUP}" \ --unattended \ --replace \ "${ARGS[@]}" [[ ! -d "${_RUNNER_WORKDIR}" ]] && mkdir -p "${_RUNNER_WORKDIR}" } unset_config_vars() { echo "Unsetting configuration environment variables" unset RUN_AS_ROOT unset RUNNER_NAME unset RUNNER_NAME_PREFIX unset RANDOM_RUNNER_SUFFIX unset ACCESS_TOKEN unset APP_ID unset APP_PRIVATE_KEY unset APP_LOGIN unset RUNNER_SCOPE unset ORG_NAME unset ENTERPRISE_NAME unset LABELS unset REPO_URL unset RUNNER_TOKEN unset RUNNER_WORKDIR unset RUNNER_GROUP unset GITHUB_HOST unset GITHUB_API_HOST unset GITHUB_API_PATH unset DISABLE_AUTOMATIC_DEREGISTRATION unset CONFIGURED_ACTIONS_RUNNER_FILES_DIR unset EPHEMERAL unset DISABLE_AUTO_UPDATE unset START_DOCKER_SERVICE unset NO_DEFAULT_LABELS unset UNSET_CONFIG_VARS } # Opt into runner reusage because a value was given if [[ -n "${_CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then echo "Runner reusage is enabled" # directory exists, copy the data if [[ -d "${_CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then echo "Copying previous data" cp -p -r "${_CONFIGURED_ACTIONS_RUNNER_FILES_DIR}/." "/actions-runner" fi if [ -f "/actions-runner/.runner" ]; then echo "The runner has already been configured" else if [[ ${_DEBUG_ONLY} == "false" ]]; then configure_runner fi fi else echo "Runner reusage is disabled" if [[ ${_DEBUG_ONLY} == "false" ]]; then [[ -f "/actions-runner/.runner" ]] && rm -f /actions-runner/.runner configure_runner fi fi if [[ -n "${_CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then echo "Reusage is enabled. Storing data to ${_CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" if [[ ${_DISABLE_AUTOMATIC_DEREGISTRATION} == "false" ]]; then echo "DISABLE_AUTOMATIC_DEREGISTRATION should be set to true to avoid issues with re-using a deregistered runner." exit 1 fi # Quoting (even with double-quotes) the regexp brokes the copying cp -p -r "/actions-runner/_diag" "/actions-runner/svc.sh" /actions-runner/.[^.]* "${_CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" fi if [[ ${_DISABLE_AUTOMATIC_DEREGISTRATION} == "false" ]]; then if [[ ${_DEBUG_ONLY} == "false" ]]; then trap_with_arg deregister_runner SIGINT SIGQUIT SIGTERM INT TERM QUIT EXIT fi fi # Start docker service if needed (e.g. for docker-in-docker) if [[ ${_START_DOCKER_SERVICE} == "true" ]]; then echo "Starting docker service" _PREFIX="" [[ ${_RUN_AS_ROOT} != "true" ]] && _PREFIX="sudo" if [[ ${_DEBUG_ONLY} == "true" ]]; then echo ${_PREFIX} service docker start else ${_PREFIX} service docker start fi fi # Unset configuration environment variables if the flag is set if [[ ${_UNSET_CONFIG_VARS} == "true" ]]; then unset_config_vars fi # Container's command (CMD) execution as runner user if [[ ${_DEBUG_ONLY} == "true" ]] || [[ ${_DEBUG_OUTPUT} == "true" ]] ; then echo "" echo "Disable automatic registration: ${_DISABLE_AUTOMATIC_DEREGISTRATION}" echo "Random runner suffix: ${_RANDOM_RUNNER_SUFFIX}" echo "Runner name: ${_RUNNER_NAME}" echo "Runner workdir: ${_RUNNER_WORKDIR}" echo "Labels: ${_LABELS}" echo "Runner Group: ${_RUNNER_GROUP}" echo "Github Host: ${_GITHUB_HOST}" echo "Run as root:${_RUN_AS_ROOT}" echo "Start docker: ${_START_DOCKER_SERVICE}" fi if [[ ${_RUN_AS_ROOT} == "true" ]]; then if [[ $(id -u) -eq 0 ]]; then if [[ ${_DEBUG_ONLY} == "true" ]] || [[ ${_DEBUG_OUTPUT} == "true" ]] ; then # shellcheck disable=SC2145 echo "Running $@" fi if [[ ${_DEBUG_ONLY} == "false" ]]; then "$@" fi else echo "ERROR: RUN_AS_ROOT env var is set to true but the user has been overridden and is not running as root, but UID '$(id -u)'" exit 1 fi else if [[ $(id -u) -eq 0 ]]; then [[ -n "${_CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]] && chown -R runner "${_CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" # /actions-runner/{bin,externals} ship runner-owned from the image # (~380 MB / 9k+ files). Recursing over them triggers overlay copy-up # per file even when ownership already matches, which dominates startup # under parallel runners. Only config.sh (run as root earlier) may have # written new root-owned files at the top level — chown those plus # /actions-runner itself and ${_RUNNER_WORKDIR}, but not the big dirs. chown runner /actions-runner "${_RUNNER_WORKDIR}" find /actions-runner -mindepth 1 -maxdepth 1 ! -name bin ! -name externals -exec chown -R runner {} + 2>/dev/null || true # The toolcache is not recursively chowned to avoid recursing over prepulated tooling in derived docker images chown runner /opt/hostedtoolcache/ if [[ ${_DEBUG_ONLY} == "true" ]] || [[ ${_DEBUG_OUTPUT} == "true" ]] ; then # shellcheck disable=SC2145 echo "Running /usr/sbin/gosu runner $@" fi if [[ ${_DEBUG_ONLY} == "false" ]]; then /usr/sbin/gosu runner "$@" fi else if [[ ${_DEBUG_ONLY} == "true" ]] || [[ ${_DEBUG_OUTPUT} == "true" ]] ; then # shellcheck disable=SC2145 echo "Running $@" fi if [[ ${_DEBUG_ONLY} == "false" ]]; then "$@" fi fi fi