#!/usr/bin/env bash # SPDX-License-Identifier: AGPL-3.0-or-later # # Generate an Ansible inventory for DigitalOcean shithub Actions runners. set -euo pipefail POOL_NAME="${POOL_NAME:-shared-linux}" RESOURCE_TAG="${RESOURCE_TAG:-shithub-actions-runner}" NAME_PREFIX="${NAME_PREFIX:-}" OUTPUT="${OUTPUT:-}" ANSIBLE_USER="${ANSIBLE_USER:-root}" SERVER_URL="${SHITHUB_RUNNER_SERVER_URL:-https://shithub.sh}" LABELS="${SHITHUB_RUNNER_LABELS:-self-hosted,linux,ubuntu-latest,x64}" CAPACITY="${SHITHUB_RUNNER_CAPACITY:-1}" DEFAULT_IMAGE="${SHITHUB_RUNNER_DEFAULT_IMAGE:-ghcr.io/tenseleyflow/shithub/runner-nix:1.0}" TOKEN_PLACEHOLDER="${SHITHUB_RUNNER_TOKEN_PLACEHOLDER:-REPLACE_WITH_RUNNER_TOKEN}" usage() { cat <<'USAGE' Usage: deploy/doctl/generate-actions-runner-inventory.sh [flags] Flags: --pool-name NAME Pool slug used in droplet names (default: shared-linux) --resource-tag TAG DigitalOcean tag to read (default: shithub-actions-runner) --name-prefix PREFIX Droplet name prefix (default: shithub-runner--) --output PATH Write inventory to PATH instead of stdout --ansible-user USER SSH user for Ansible (default: root) --server-url URL shithub server URL (default: https://shithub.sh) --labels LIST Runner labels (default: self-hosted,linux,ubuntu-latest,x64) --capacity N Runner capacity per host (default: 1) --default-image IMAGE Runner default image --token-placeholder S Placeholder text for per-host runner tokens -h, --help Show this help USAGE } fatal() { echo "fatal: $*" >&2 exit 2 } while [[ $# -gt 0 ]]; do case "$1" in --pool-name) POOL_NAME="${2:?missing value for --pool-name}" shift 2 ;; --resource-tag) RESOURCE_TAG="${2:?missing value for --resource-tag}" shift 2 ;; --name-prefix) NAME_PREFIX="${2:?missing value for --name-prefix}" shift 2 ;; --output) OUTPUT="${2:?missing value for --output}" shift 2 ;; --ansible-user) ANSIBLE_USER="${2:?missing value for --ansible-user}" shift 2 ;; --server-url) SERVER_URL="${2:?missing value for --server-url}" shift 2 ;; --labels) LABELS="${2:?missing value for --labels}" shift 2 ;; --capacity) CAPACITY="${2:?missing value for --capacity}" shift 2 ;; --default-image) DEFAULT_IMAGE="${2:?missing value for --default-image}" shift 2 ;; --token-placeholder) TOKEN_PLACEHOLDER="${2:?missing value for --token-placeholder}" shift 2 ;; -h | --help) usage exit 0 ;; *) fatal "unknown flag: $1" ;; esac done if [[ -z "$NAME_PREFIX" ]]; then NAME_PREFIX="shithub-runner-$POOL_NAME-" fi command -v doctl >/dev/null 2>&1 || fatal "doctl not on PATH" command -v jq >/dev/null 2>&1 || fatal "jq not on PATH" if ! doctl account get >/dev/null 2>&1; then fatal "doctl is not authenticated; run 'doctl auth init'" fi droplets_json="$(doctl compute droplet list --tag-name "$RESOURCE_TAG" --output json | jq --arg prefix "$NAME_PREFIX" '[.[] | select(.name | startswith($prefix)) | { id: (.id | tostring), name: .name, status: .status, public_ipv4: ((.networks.v4 // []) | map(select(.type == "public")) | first | .ip_address // ""), private_ipv4: ((.networks.v4 // []) | map(select(.type == "private")) | first | .ip_address // "") }] | sort_by(.name)')" count="$(jq 'length' <<<"$droplets_json")" (( count > 0 )) || fatal "no droplets tagged $RESOURCE_TAG with prefix $NAME_PREFIX" public_count="$(jq '[.[] | select(.public_ipv4 != "")] | length' <<<"$droplets_json")" (( public_count > 0 )) || fatal "no matching droplets have public IPv4 addresses for direct Ansible SSH" render_inventory() { cat <
"$tmp" mv "$tmp" "$OUTPUT" echo "wrote $OUTPUT" >&2 else render_inventory fi