Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ jobs:

| Name | Required | Description | Default |
|---------------------|----------|-------------|---------|
| `create_wait` | | Wait up to 'create_wait' retries (10 sec each) to create the Hetzner Cloud Server resource. | `360` (1 hour) |
| `create_wait` | | Wait up to `create_wait` retries (10 sec each) to create the Server resource via the Hetzner Cloud API. Retry if: Resource is not available ([Limited availability of Cloud plans](https://status.hetzner.com/incident/aa5ce33b-faa5-4fd0-9782-fde43cd270cf)). | `360` (1 hour) |
| `delete_wait` | | Wait up to `delete_wait` retries (10 sec each) to delete the Server resource via the Hetzner Cloud API. Retry if: Temporary outage of the API ([Fault report on Cloud API and Cloud Console](https://status.hetzner.com/incident/440e6b5f-249c-45fd-8074-e5d79cc4e2a6)). | `360` (1 hour) |
| `enable_ipv4` | | Attach an IPv4 on the public NIC (true/false). If false, no IPv4 address will be attached. Warning: The GitHub API requires IPv4. Disabling it will result in connection failures. | `true` |
| `enable_ipv6` | | Attach an IPv6 on the public NIC (true/false). If false, no IPv6 address will be attached. | `true` |
| `github_token` | ✓ (always) | Fine-grained GitHub Personal Access Token (PAT) with 'Read and write' access to 'Administration' assigned. | |
Expand Down
123 changes: 68 additions & 55 deletions action.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ for MY_FILE in "${MY_FILES[@]}"; do
fi
done

# Retry wait time in secounds
WAIT_SEC=10

#
# INPUT
#
Expand All @@ -59,11 +62,32 @@ done
# https://docs.github.com/en/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions#inputs
# When you specify an input, GitHub creates an environment variable for the input with the name INPUT_<VARIABLE_NAME>.

# Set the Hetzner Cloud API token.
# Retrieves the value from the INPUT_HCLOUD_TOKEN environment variable.
MY_HETZNER_TOKEN=${INPUT_HCLOUD_TOKEN}
if [[ -z "$MY_HETZNER_TOKEN" ]]; then
exit_with_failure "Hetzner Cloud API token is not set."
# Set maximum retries * WAIT_SEC (10 sec) for Hetzner Server creation via the Hetzer Cloud API (default: 360 [1 hour])
# If INPUT_CREATE_WAIT is set, use its value; otherwise, use "360".
MY_CREATE_WAIT=${INPUT_CREATE_WAIT:-360}
if [[ ! "$MY_CREATE_WAIT" =~ ^[0-9]+$ ]]; then
exit_with_failure "The maximum retries for Hetzner Server creation via the Hetzer Cloud API must be an integer!"
fi

# Set maximum retries * WAIT_SEC (10 sec) for Hetzner Server deletion via the Hetzer Cloud API (default: 360 [1 hour])
# If INPUT_DELETE_WAIT is set, use its value; otherwise, use "360".
MY_DELETE_WAIT=${INPUT_DELETE_WAIT:-360}
if [[ ! "$MY_DELETE_WAIT" =~ ^[0-9]+$ ]]; then
exit_with_failure "The maximum retries for Hetzner Server deletion via the Hetzer Cloud API must be an integer!"
fi

# Enable IPv4 (default: false)
# If INPUT_ENABLE_IPV4 is set, use its value; otherwise, use "false".
MY_ENABLE_IPV4=${INPUT_ENABLE_IPV4:-"true"}
if [[ "$MY_ENABLE_IPV4" != "true" && "$MY_ENABLE_IPV4" != "false" ]]; then
exit_with_failure "Enable IPv4 must be 'true' or 'false'."
fi

# Enable IPv6 (default: true)
# If INPUT_ENABLE_IPV6 is set, use its value; otherwise, use "true".
MY_ENABLE_IPV6=${INPUT_ENABLE_IPV6:-"true"}
if [[ "$MY_ENABLE_IPV6" != "true" && "$MY_ENABLE_IPV6" != "false" ]]; then
exit_with_failure "Enable IPv6 must be 'true' or 'false'."
fi

# Set the GitHub Personal Access Token (PAT).
Expand All @@ -86,27 +110,11 @@ MY_GITHUB_REPOSITORY_OWNER_ID=${GITHUB_REPOSITORY_OWNER_ID:-"0"}
# Set The ID of the repository (used for Hetzner Cloud Server label).
MY_GITHUB_REPOSITORY_ID=${GITHUB_REPOSITORY_ID:-"0"}

# Specify here which mode you want to use (default: create):
# - create : Create a new runner
# - delete : Delete the previously created runner
# If INPUT_MODE is set, use its value; otherwise, use "create".
MY_MODE=${INPUT_MODE:-"create"}
if [[ "$MY_MODE" != "create" && "$MY_MODE" != "delete" ]]; then
exit_with_failure "Mode must be 'create' or 'delete'."
fi

# Enable IPv4 (default: false)
# If INPUT_ENABLE_IPV4 is set, use its value; otherwise, use "false".
MY_ENABLE_IPV4=${INPUT_ENABLE_IPV4:-"true"}
if [[ "$MY_ENABLE_IPV4" != "true" && "$MY_ENABLE_IPV4" != "false" ]]; then
exit_with_failure "Enable IPv4 must be 'true' or 'false'."
fi

# Enable IPv6 (default: true)
# If INPUT_ENABLE_IPV6 is set, use its value; otherwise, use "true".
MY_ENABLE_IPV6=${INPUT_ENABLE_IPV6:-"true"}
if [[ "$MY_ENABLE_IPV6" != "true" && "$MY_ENABLE_IPV6" != "false" ]]; then
exit_with_failure "Enable IPv6 must be 'true' or 'false'."
# Set the Hetzner Cloud API token.
# Retrieves the value from the INPUT_HCLOUD_TOKEN environment variable.
MY_HETZNER_TOKEN=${INPUT_HCLOUD_TOKEN}
if [[ -z "$MY_HETZNER_TOKEN" ]]; then
exit_with_failure "Hetzner Cloud API token is not set."
fi

# Set the image to use for the instance (default: ubuntu-24.04)
Expand All @@ -121,6 +129,15 @@ fi
# If INPUT_LOCATION is set, use its value; otherwise, use "nbg1".
MY_LOCATION=${INPUT_LOCATION:-"nbg1"}

# Specify here which mode you want to use (default: create):
# - create : Create a new runner
# - delete : Delete the previously created runner
# If INPUT_MODE is set, use its value; otherwise, use "create".
MY_MODE=${INPUT_MODE:-"create"}
if [[ "$MY_MODE" != "create" && "$MY_MODE" != "delete" ]]; then
exit_with_failure "Mode must be 'create' or 'delete'."
fi

# Set the name of the instance (default: gh-runner-$RANDOM)
# If INPUT_NAME is set, use its value; otherwise, generate a random name using "gh-runner-$RANDOM".
MY_NAME=${INPUT_NAME:-"gh-runner-$RANDOM"}
Expand Down Expand Up @@ -160,26 +177,6 @@ if [[ "$MY_PRIMARY_IPV6" != "null" && ! "$MY_PRIMARY_IPV6" =~ ^[0-9]+$ ]]; then
exit_with_failure "The primary IPv6 ID must be 'null' or an integer!"
fi

# Set the server type/instance type (default: cx22)
# If INPUT_SERVER_TYPE is set, use its value; otherwise, use "cx22".
MY_SERVER_TYPE=${INPUT_SERVER_TYPE:-"cx22"}

# Set maximal wait time (retries * 10 sec) for Hetzner Cloud Server (default: 30 [5 min])
# If INPUT_SERVER_WAIT is set, use its value; otherwise, use "30".
MY_SERVER_WAIT=${INPUT_SERVER_WAIT:-"30"}
# Check if MY_RUNNER_WAIT is an integer
if [[ ! "$MY_SERVER_WAIT" =~ ^[0-9]+$ ]]; then
exit_with_failure "The maximum wait time (reties) for a running Hetzner Cloud Server must be an integer!"
fi

# Set the SSH key to use for the instance (default: null)
# If INPUT_SSH_KEY is set, use its value; otherwise, use "null".
MY_SSH_KEY=${INPUT_SSH_KEY:-"null"}
# Check if MY_SSH_KEY is an integer
if [[ "$MY_SSH_KEY" != "null" && ! "$MY_SSH_KEY" =~ ^[0-9]+$ ]]; then
exit_with_failure "The SSH key ID must be 'null' or an integer!"
fi

# Set default GitHub Actions Runner installation directory (default: /actions-runner)
# If INPUT_RUNNER_DIR is set, its value is used. Otherwise, the default value "/actions-runner" is used.
MY_RUNNER_DIR=${INPUT_RUNNER_DIR:-"/actions-runner"}
Expand All @@ -197,24 +194,37 @@ if [[ "$MY_RUNNER_VERSION" != "latest" && "$MY_RUNNER_VERSION" != "skip" && ! "$
exit_with_failure "'$MY_RUNNER_VERSION' is not a valid GitHub Actions Runner version! Enter 'latest', 'skip' or the version without 'v'."
fi

# Set maximal wait time (retries * 10 sec) for GitHub Actions Runner registration (default: 60 [10 min])
# Set maximal retries * WAIT_SEC (10 sec) for GitHub Actions Runner registration (default: 60 [10 min])
# If INPUT_RUNNER_WAIT is set, use its value; otherwise, use "60".
MY_RUNNER_WAIT=${INPUT_RUNNER_WAIT:-"60"}
# Check if MY_RUNNER_WAIT is an integer
if [[ ! "$MY_RUNNER_WAIT" =~ ^[0-9]+$ ]]; then
exit_with_failure "The maximum wait time (retries) for GitHub Action Runner registration must be an integer!"
fi

# Set maximal wait time (retries * 10 sec) for Hetzner Server creation (default: 360 [1 hour])
# If INPUT_CREATE_WAIT is set, use its value; otherwise, use "360".
MY_CREATE_WAIT=${INPUT_CREATE_WAIT:-360}
if [[ ! "$MY_CREATE_WAIT" =~ ^[0-9]+$ ]]; then
exit_with_failure "The maximum wait time (retries) for Hetzner Server creation must be an integer!"
fi

# Set Hetzner Cloud Server ID
# Check only if mode is delete.
MY_HETZNER_SERVER_ID=${INPUT_SERVER_ID}

# Set the server type/instance type (default: cx22)
# If INPUT_SERVER_TYPE is set, use its value; otherwise, use "cx22".
MY_SERVER_TYPE=${INPUT_SERVER_TYPE:-"cx22"}

# Set maximal retries * WAIT_SEC (10 sec) for Hetzner Cloud Server (default: 30 [5 min])
# If INPUT_SERVER_WAIT is set, use its value; otherwise, use "30".
MY_SERVER_WAIT=${INPUT_SERVER_WAIT:-"30"}
# Check if MY_RUNNER_WAIT is an integer
if [[ ! "$MY_SERVER_WAIT" =~ ^[0-9]+$ ]]; then
exit_with_failure "The maximum wait time (reties) for a running Hetzner Cloud Server must be an integer!"
fi

# Set the SSH key to use for the instance (default: null)
# If INPUT_SSH_KEY is set, use its value; otherwise, use "null".
MY_SSH_KEY=${INPUT_SSH_KEY:-"null"}
# Check if MY_SSH_KEY is an integer
if [[ "$MY_SSH_KEY" != "null" && ! "$MY_SSH_KEY" =~ ^[0-9]+$ ]]; then
exit_with_failure "The SSH key ID must be 'null' or an integer!"
fi

#
# DELETE
Expand All @@ -228,9 +238,13 @@ if [[ "$MY_MODE" == "delete" ]]; then

# Send a DELETE request to the Hetzner Cloud API to delete the server.
# https://docs.hetzner.cloud/#servers-delete-a-server
# curl retry: https://everything.curl.dev/usingcurl/downloads/retry.html
echo "Delete server..."
curl \
-X DELETE \
--retry "$MY_DELETE_WAIT" \
--retry-delay "$WAIT_SEC" \
--retry-all-errors \
--fail-with-body \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${MY_HETZNER_TOKEN}" \
Expand Down Expand Up @@ -372,7 +386,6 @@ fi
# https://docs.hetzner.cloud/#servers-create-a-server
MAX_RETRIES=$MY_CREATE_WAIT
RETRY_COUNT=0
WAIT_SEC=10
while [[ $RETRY_COUNT -lt $MAX_RETRIES ]]; do
echo "Create Server..."
if curl \
Expand Down
58 changes: 32 additions & 26 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,16 @@ branding:
color: 'red'

inputs:
mode:
description: >-
Choose either 'create' to create a new GitHub Actions Runner or 'delete' to delete a previously created one.
required: true
github_token:
create_wait:
description: >-
Fine-grained GitHub Personal Access Token (PAT) with 'Read and write' access to 'Administration' assigned.
required: true
hcloud_token:
Wait up to 'create_wait' retries (10 sec each) to create the Server resource via the Hetzner Cloud API (default: 360 = 1 hour).
required: false
default: '360'
delete_wait:
description: >-
Hetzner Cloud API token with 'Read & Write' permissions assigned.
required: true
Wait up to 'delete_wait' retries (10 sec each) to delete the Server resource via the Hetzner Cloud API (default: 360 = 1 hour).
required: false
default: '360'
enable_ipv4:
description: >-
Attach an IPv4 on the public NIC (true/false) . If false, no IPv4 address will be attached.
Expand All @@ -30,6 +28,14 @@ inputs:
Attach an IPv6 on the public NIC (true/false). If false, no IPv6 address will be attached.
required: false
default: 'true'
github_token:
description: >-
Fine-grained GitHub Personal Access Token (PAT) with 'Read and write' access to 'Administration' assigned.
required: true
hcloud_token:
description: >-
Hetzner Cloud API token with 'Read & Write' permissions assigned.
required: true
image:
description: >-
Name or ID (integer) of the Image the Server is created from.
Expand All @@ -40,6 +46,10 @@ inputs:
Name of Location to create Server in.
required: false
default: 'nbg1'
mode:
description: >-
Choose either 'create' to create a new GitHub Actions Runner or 'delete' to delete a previously created one.
required: true
name:
description: >-
The name for the server and label for the GitHub Actions Runner (must be unique within the project and conform to hostname rules: '[a-zA-Z0-9_-]').
Expand All @@ -49,6 +59,11 @@ inputs:
Network ID (integer) which should be attached to the Server private network interface at the creation time.
required: false
default: 'null'
pre_runner_script:
description: >-
Specifies bash commands to run before the GitHub Actions Runner starts.
It's useful for installing dependencies with apt-get, dnf, zypper etc.
required: false
primary_ipv4:
description: >-
ID (integer) of the IPv4 Primary IP to use.
Expand All @@ -66,23 +81,18 @@ inputs:
GitHub Actions Runner installation directory (created automatically; no trailing slash).
required: false
default: '/actions-runner'
runner_wait:
description: >-
Wait up to 'runner_wait' retries (10 sec each) for runner registration (default: 10 minutes).
required: false
default: '60'
runner_version:
description: >-
GitHub Actions Runner version (omit 'v'; e.g., '2.321.0').
'latest' will install the latest version.
'skip' will skip the installation. A working installation is expected in the 'runner_dir'.
required: false
default: 'latest'
create_wait:
description: >-
Wait up to 'create_wait' retries (10 sec each) to create the Hetzner Cloud Server resource (default: 360 = 1 hour).
required: false
default: '360'
runner_wait:
description: >-
Wait up to 'runner_wait' retries (10 sec each) for runner registration (default: 10 minutes).
required: false
default: '60'
server_id:
description: >-
ID (integer) of Hetzner Cloud Server to delete.
Expand All @@ -102,11 +112,6 @@ inputs:
SSH key ID (integer) or name which should be injected into the Server at creation time.
required: false
default: 'null'
pre_runner_script:
description: >-
Specifies bash commands to run before the GitHub Actions Runner starts.
It's useful for installing dependencies with apt-get, dnf, zypper etc.
required: false

outputs:
label:
Expand All @@ -128,6 +133,8 @@ runs:
working-directory: ${{ github.action_path }}
run: bash action.sh
env:
INPUT_CREATE_WAIT: ${{ inputs.create_wait }}
INPUT_DELETE_WAIT: ${{ inputs.delete_wait }}
INPUT_ENABLE_IPV4: ${{ inputs.enable_ipv4 }}
INPUT_ENABLE_IPV6: ${{ inputs.enable_ipv6 }}
INPUT_GITHUB_TOKEN: ${{ inputs.github_token }}
Expand All @@ -147,4 +154,3 @@ runs:
INPUT_SERVER_TYPE: ${{ inputs.server_type }}
INPUT_SERVER_WAIT: ${{ inputs.server_wait }}
INPUT_SSH_KEY: ${{ inputs.ssh_key }}
INPUT_CREATE_WAIT: ${{ inputs.create_wait }}