diff --git a/.forgejo/workflows/example-docker-compose.yml b/.forgejo/workflows/example-docker-compose.yml index 4e2f547..2a18988 100644 --- a/.forgejo/workflows/example-docker-compose.yml +++ b/.forgejo/workflows/example-docker-compose.yml @@ -39,8 +39,11 @@ jobs: # Launch Forgejo & the runner # $cli up -d - for delay in $(seq 60) ; do test -f /srv/runner-data/.runner && break ; sleep 30 ; done - test -f /srv/runner-data/.runner + for delay in $(seq 60) ; do + test -f ./forgejo-runner/runner.json && break + sleep 30 + done + test -f ./forgejo-runner/runner.json # # Run the demo workflow # @@ -56,11 +59,11 @@ jobs: grep --quiet "$success" /tmp/out && break grep --quiet "$failure" /tmp/out && break $cli ps --all - $cli logs --tail=20 runner-daemon demo-workflow + $cli logs --tail=20 forgejo-runner demo-workflow sleep 30 done grep --quiet "$success" /tmp/out - $cli logs runner-daemon > /tmp/runner.log + $cli logs forgejo-runner > /tmp/runner.log grep --quiet 'Start image=code.forgejo.org/oci/node:20-bookworm' /tmp/runner.log - name: full docker compose logs diff --git a/Dockerfile b/Dockerfile index c40304b..ed94063 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/tonistiigi/xx AS xx -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.21-alpine3.19 as build-env +FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.21-alpine3.19 AS build-env # # Transparently cross compile for the target platform diff --git a/Dockerfile.rootless b/Dockerfile.rootless new file mode 100644 index 0000000..882be5c --- /dev/null +++ b/Dockerfile.rootless @@ -0,0 +1,6 @@ +FROM code.forgejo.org/forgejo/runner:3.4.1 + +USER 1000:1000 +## In Theory these can be removed on next release of the runner image +COPY --chown=forgejo-runner:forgejo-runner --chmod=555 ./entrypoint.sh /entrypoint +ENTRYPOINT [ "/entrypoint" ] \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index c19b9cd..c6145e9 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,11 +2,15 @@ set -e -# Check if the script is run as root -if [ "$EUID" -ne 0 ]; then - echo "This script must be run as root" - exit 1 -fi +run_command() { + local cmd="$1" + echo "Running $cmd as $(id -u)" + if [[ "$ISROOT" == true ]]; then + su -c "$cmd" forgejo-runner + else + eval "$cmd" + fi +} # Initial setup if [[ ! -d /data ]]; then @@ -16,38 +20,48 @@ cd /data RUNNER_USERID="${RUNNER_USERID:-1000}" -# Setup User -if id "forgejo-runner" &>/dev/null; then - if [[ ! -z "${RUNNER_USERID}" ]]; then - echo "Changing UID of forgejo-runner to ${RUNNER_USERID}" - sed -i "s/^forgejo-runner:[^:]*:[^:]*:/forgejo-runner:x:${RUNNER_USERID}:/" /etc/passwd - fi -else - echo "Creating user forgejo-runner with UID ${RUNNER_USERID}" - adduser --uid "${RUNNER_USERID}" --home /home/forgejo-runner --disabled-password --gecos "" forgejo-runner +# Check if the script is running as root +if [[ $(id -u) -eq 0 ]]; then + ISROOT=true fi -# Ensure /data is owned by the runner user -chown -R forgejo-runner:forgejo-runner /data +if [[ "$ISROOT" == true ]]; then + # Check if the forgejo-runner user exists + if id "forgejo-runner" &>/dev/null; then + echo "forgejo-runner user exists." + + # Change the user ID if needed + CURRENT_UID=$(id -u forgejo-runner) + if [[ "${CURRENT_UID}" -ne "${RUNNER_USERID}" ]]; then + echo "Changing UID of forgejo-runner to ${RUNNER_USERID}" + sed -i "s/^forgejo-runner:[^:]*:[^:]*:/forgejo-runner:x:${RUNNER_USERID}:/" /etc/passwd + fi + else + echo "Creating user forgejo-runner with UID ${RUNNER_USERID}" + adduser --uid "${RUNNER_USERID}" --home /home/forgejo-runner --disabled-password --gecos "" forgejo-runner + fi + + # Ensure /data is owned by the runner user + chown -R forgejo-runner:forgejo-runner /data +fi # Handle and alter the config file if [[ -z "${CONFIG_FILE}" ]]; then + echo "CONFIG_FILE is not set" CONFIG_FILE="/data/config.yml" fi CONFIG_ARG="--config ${CONFIG_FILE}" DOCKER_HOST=${DOCKER_HOST:-docker} -echo "DOCKER_HOST: ${DOCKER_HOST}" if [[ ! -f "${CONFIG_FILE}" ]]; then - su -c "forgejo-runner generate-config > ${CONFIG_FILE}" forgejo-runner + run_command "forgejo-runner generate-config > ${CONFIG_FILE}" forgejo-runner # Remove test environment variables if they exist in the config file sed -i "/^ A_TEST_ENV_NAME_1:/d" ${CONFIG_FILE} sed -i "/^ A_TEST_ENV_NAME_2:/d" ${CONFIG_FILE} # Apply default values for docker - sed -i "/\"labels\":/c\ \"labels\": [\"docker:docker://code.forgejo.org/oci/node:20-bookworm\", \"ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04\"]" ${CONFIG_FILE} - + sed -i "/^ labels:/c\ \"labels\": [\"docker:docker://code.forgejo.org/oci/node:20-bookworm\", \"ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04\"]" ${CONFIG_FILE} sed -i "/^ network:/c\ network: host" ${CONFIG_FILE} sed -i "/^ privileged:/c\ privileged: true" ${CONFIG_FILE} sed -i "/^ options:/c\ options: -v /certs/client:/certs/client" ${CONFIG_FILE} @@ -63,7 +77,7 @@ fi if [[ ! -f "${ENV_FILE}" ]]; then echo "Creating ${ENV_FILE} and populating with default values" - cat < ${ENV_FILE} + cat <${ENV_FILE} DOCKER_TLS_VERIFY: 1 DOCKER_CERT_PATH: /certs/client EOF @@ -76,7 +90,7 @@ fi # Set the runner file if [[ -z "${RUNNER_FILE}" ]]; then - RUNNER_FILE=".runner.json" # use json so editors know how to highlight + RUNNER_FILE=".runner.json" # use json so editors know how to highlight fi sed -i "/^ file:/c\ file: ${RUNNER_FILE}" ${CONFIG_FILE} @@ -84,9 +98,15 @@ if [[ ! -s "${RUNNER_FILE}" ]]; then touch ${RUNNER_FILE} try=$((try + 1)) success=0 - if [[ -z "${RUNNER_TOKEN}" ]]; then - echo "RUNNER_TOKEN is not set" - exit 1 + + if [[ ! -z "${FORGEJO_SECRET}" ]]; then + EXTRA_ARGS="${EXTRA_ARGS} --secret ${FORGEJO_SECRET}" + else + if [[ -z "${RUNNER_TOKEN}" ]]; then + echo "RUNNER_TOKEN is not set" + exit 1 + fi + EXTRA_ARGS="${EXTRA_ARGS} --token ${RUNNER_TOKEN}" fi # The point of this loop is to make it simple, when running both forgejo-runner and gitea in docker, @@ -94,13 +114,18 @@ if [[ ! -s "${RUNNER_FILE}" ]]; then # the context of a single docker-compose, something similar could be done via healthchecks, but # this is more flexible. while [[ $success -eq 0 ]] && [[ $try -lt ${MAX_REG_ATTEMPTS:-10} ]]; do - su -c "forgejo-runner register \ - --instance \"${FORGEJO_URL:-http://forgejo:3000}\" \ - --token \"${RUNNER_TOKEN}\" \ - --name \"${RUNNER_NAME:-$(hostname)}\" \ - ${CONFIG_ARG} ${EXTRA_ARGS} --no-interactive 2>&1 | tee /tmp/reg.log" forgejo-runner + # run_command "forgejo-runner register \ + # --instance \"${FORGEJO_URL:-http://forgejo:3000}\" \ + # --name \"${RUNNER_NAME:-$(hostname)}\" \ + # ${CONFIG_ARG} ${EXTRA_ARGS} --no-interactive 2>&1 | tee /tmp/reg.log" - cat /tmp/reg.log | grep 'Runner registered successfully' >/dev/null + run_command "forgejo-runner create-runner-file --connect \ + --instance \"${FORGEJO_URL:-http://forgejo:3000}\" \ + --name \"${RUNNER_NAME:-$(hostname)}\" \ + ${CONFIG_ARG} ${EXTRA_ARGS} 2>&1 | tee /tmp/reg.log" + + + cat /tmp/reg.log | grep 'connection successful' >/dev/null if [[ $? -eq 0 ]]; then echo "SUCCESS" success=1 @@ -114,4 +139,4 @@ fi # Prevent reading the token from the forgejo-runner process unset RUNNER_TOKEN -su -c "forgejo-runner daemon ${CONFIG_ARG}" forgejo-runner +run_command "forgejo-runner daemon ${CONFIG_ARG}" diff --git a/examples/docker-compose/compose-forgejo-and-runner.yml b/examples/docker-compose/compose-forgejo-and-runner.yml index c15c63d..eb6006e 100644 --- a/examples/docker-compose/compose-forgejo-and-runner.yml +++ b/examples/docker-compose/compose-forgejo-and-runner.yml @@ -11,7 +11,7 @@ # NOTE: a token obtained from the Forgejo web interface cannot be used # as a shared secret. # -# Replace {RUNNER_TOKEN} with the token obtained from the Forgejo web interface. +# Replace ${RUNNER_TOKEN} with the token obtained from the Forgejo web interface. # networks: @@ -43,14 +43,24 @@ services: - ./forgejo:/data ports: - 8080:3000 + command: >- + bash -c ' + /bin/s6-svscan /etc/s6 & + sleep 10 ; + su -c "forgejo forgejo-cli actions register --secret {SHARED_SECRET}" git ; + sleep infinity + ' forgejo-runner: ## TODO: Update image to the the release ## made from this PR: https://code.forgejo.org/forgejo/runner/pulls/283 # image: code.forgejo.org/forgejo/runner:3.4.1 - build: ../../ + build: + context: ../../ + dockerfile: Dockerfile container_name: forgejo-runner + # user: "1000" # set to run rootless, overrides RUNNER_USER volumes: - ./forgejo-runner:/data - docker_certs:/certs @@ -63,8 +73,36 @@ services: CONFIG_FILE: config.yml # defaults to /data/config.yml FORGEJO_URL: ${FORGEJO_URL} # defaults to http://forgejo:3000 + FORGEJO_SECRET: "{SHARED_SECRET}" # shared secret, must match Forgejo's RUNNER_FILE: runner.json # defaults to /data/runner.json RUNNER_NAME: forgejo-runner # defaults to forgejo-runner, used for registration - RUNNER_TOKEN: "{RUNNER_TOKEN}" + RUNNER_TOKEN: "${RUNNER_TOKEN}" RUNNER_USER: 1000 # defaults to 1000 + + forgejo-runner-rootless: + ## TODO: Update image to the the release + ## made from this PR: https://code.forgejo.org/forgejo/runner/pulls/283 + + # image: code.forgejo.org/forgejo/runner:3.4.1 + build: + context: ../../ + dockerfile: Dockerfile.rootless + container_name: forgejo-runner-rootless + volumes: + - ./forgejo-runner:/data + - docker_certs:/certs + networks: + - forgejo + depends_on: + - docker-in-docker + - forgejo + environment: + CONFIG_FILE: config-rootless.yml # defaults to /data/config.yml + + FORGEJO_URL: ${FORGEJO_URL} # defaults to http://forgejo:3000 + FORGEJO_SECRET: "{SHARED_SECRET}" # shared secret, must match Forgejo's + + RUNNER_FILE: runner-rootless.json # defaults to /data/runner.json + RUNNER_NAME: forgejo-runner # defaults to forgejo-runner, used for registration + RUNNER_TOKEN: "${RUNNER_TOKEN}" \ No newline at end of file