Bash · 4604 bytes Raw Blame History
1 #!/usr/bin/env bash
2 # SPDX-License-Identifier: AGPL-3.0-or-later
3 #
4 # Generate an Ansible inventory for DigitalOcean shithub Actions runners.
5
6 set -euo pipefail
7
8 POOL_NAME="${POOL_NAME:-shared-linux}"
9 RESOURCE_TAG="${RESOURCE_TAG:-shithub-actions-runner}"
10 NAME_PREFIX="${NAME_PREFIX:-}"
11 OUTPUT="${OUTPUT:-}"
12 ANSIBLE_USER="${ANSIBLE_USER:-root}"
13 SERVER_URL="${SHITHUB_RUNNER_SERVER_URL:-https://shithub.sh}"
14 LABELS="${SHITHUB_RUNNER_LABELS:-self-hosted,linux,ubuntu-latest,x64}"
15 CAPACITY="${SHITHUB_RUNNER_CAPACITY:-1}"
16 DEFAULT_IMAGE="${SHITHUB_RUNNER_DEFAULT_IMAGE:-ghcr.io/tenseleyflow/shithub/runner-nix:1.0}"
17 TOKEN_PLACEHOLDER="${SHITHUB_RUNNER_TOKEN_PLACEHOLDER:-REPLACE_WITH_RUNNER_TOKEN}"
18
19 usage() {
20 cat <<'USAGE'
21 Usage:
22 deploy/doctl/generate-actions-runner-inventory.sh [flags]
23
24 Flags:
25 --pool-name NAME Pool slug used in droplet names (default: shared-linux)
26 --resource-tag TAG DigitalOcean tag to read (default: shithub-actions-runner)
27 --name-prefix PREFIX Droplet name prefix (default: shithub-runner-<pool-name>-)
28 --output PATH Write inventory to PATH instead of stdout
29 --ansible-user USER SSH user for Ansible (default: root)
30 --server-url URL shithub server URL (default: https://shithub.sh)
31 --labels LIST Runner labels (default: self-hosted,linux,ubuntu-latest,x64)
32 --capacity N Runner capacity per host (default: 1)
33 --default-image IMAGE Runner default image
34 --token-placeholder S Placeholder text for per-host runner tokens
35 -h, --help Show this help
36 USAGE
37 }
38
39 fatal() {
40 echo "fatal: $*" >&2
41 exit 2
42 }
43
44 while [[ $# -gt 0 ]]; do
45 case "$1" in
46 --pool-name)
47 POOL_NAME="${2:?missing value for --pool-name}"
48 shift 2
49 ;;
50 --resource-tag)
51 RESOURCE_TAG="${2:?missing value for --resource-tag}"
52 shift 2
53 ;;
54 --name-prefix)
55 NAME_PREFIX="${2:?missing value for --name-prefix}"
56 shift 2
57 ;;
58 --output)
59 OUTPUT="${2:?missing value for --output}"
60 shift 2
61 ;;
62 --ansible-user)
63 ANSIBLE_USER="${2:?missing value for --ansible-user}"
64 shift 2
65 ;;
66 --server-url)
67 SERVER_URL="${2:?missing value for --server-url}"
68 shift 2
69 ;;
70 --labels)
71 LABELS="${2:?missing value for --labels}"
72 shift 2
73 ;;
74 --capacity)
75 CAPACITY="${2:?missing value for --capacity}"
76 shift 2
77 ;;
78 --default-image)
79 DEFAULT_IMAGE="${2:?missing value for --default-image}"
80 shift 2
81 ;;
82 --token-placeholder)
83 TOKEN_PLACEHOLDER="${2:?missing value for --token-placeholder}"
84 shift 2
85 ;;
86 -h | --help)
87 usage
88 exit 0
89 ;;
90 *)
91 fatal "unknown flag: $1"
92 ;;
93 esac
94 done
95
96 if [[ -z "$NAME_PREFIX" ]]; then
97 NAME_PREFIX="shithub-runner-$POOL_NAME-"
98 fi
99
100 command -v doctl >/dev/null 2>&1 || fatal "doctl not on PATH"
101 command -v jq >/dev/null 2>&1 || fatal "jq not on PATH"
102
103 if ! doctl account get >/dev/null 2>&1; then
104 fatal "doctl is not authenticated; run 'doctl auth init'"
105 fi
106
107 droplets_json="$(doctl compute droplet list --tag-name "$RESOURCE_TAG" --output json |
108 jq --arg prefix "$NAME_PREFIX" '[.[] | select(.name | startswith($prefix)) | {
109 id: (.id | tostring),
110 name: .name,
111 status: .status,
112 public_ipv4: ((.networks.v4 // []) | map(select(.type == "public")) | first | .ip_address // ""),
113 private_ipv4: ((.networks.v4 // []) | map(select(.type == "private")) | first | .ip_address // "")
114 }] | sort_by(.name)')"
115
116 count="$(jq 'length' <<<"$droplets_json")"
117 (( count > 0 )) || fatal "no droplets tagged $RESOURCE_TAG with prefix $NAME_PREFIX"
118 public_count="$(jq '[.[] | select(.public_ipv4 != "")] | length' <<<"$droplets_json")"
119 (( public_count > 0 )) || fatal "no matching droplets have public IPv4 addresses for direct Ansible SSH"
120
121 render_inventory() {
122 cat <<HEADER
123 # Generated by deploy/doctl/generate-actions-runner-inventory.sh.
124 # Store the real shithub_runner_token values in ansible-vault or host_vars.
125
126 [actions_runners]
127 HEADER
128 jq -r \
129 --arg user "$ANSIBLE_USER" \
130 --arg token "$TOKEN_PLACEHOLDER" \
131 '.[] | select(.public_ipv4 != "") |
132 "\(.name) ansible_host=\(.public_ipv4) ansible_user=\($user) do_droplet_id=\(.id) do_private_ipv4=\(.private_ipv4) shithub_runner_token=\($token)_\(.name | ascii_upcase | gsub("[^A-Z0-9]"; "_"))"' \
133 <<<"$droplets_json"
134
135 cat <<VARS
136
137 [actions_runners:vars]
138 shithub_runner_enabled=true
139 shithub_runner_server_url=$SERVER_URL
140 shithub_runner_engine=docker
141 shithub_runner_labels=$LABELS
142 shithub_runner_capacity=$CAPACITY
143 shithub_runner_default_image=$DEFAULT_IMAGE
144 VARS
145 }
146
147 if [[ -n "$OUTPUT" ]]; then
148 mkdir -p "$(dirname "$OUTPUT")"
149 tmp="$OUTPUT.tmp"
150 render_inventory >"$tmp"
151 mv "$tmp" "$OUTPUT"
152 echo "wrote $OUTPUT" >&2
153 else
154 render_inventory
155 fi