Skip to content

Commit 555e290

Browse files
authored
feat: add CI automation for devnet create and destroy workflows (#738)
feat: add CI automation for devnet create and destroy workflows
1 parent 85b6eaf commit 555e290

18 files changed

Lines changed: 244 additions & 40 deletions

File tree

.github/workflows/create-devnet.yml

Lines changed: 110 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ on:
1212
required: true
1313
type: string
1414
default: "2.0.0-rc.16"
15+
deploy_tags:
16+
description: "Ansible tags to run. Use full_deploy for full flow, or a narrower tag such as unban_hp_masternodes to resume faster."
17+
required: true
18+
type: string
19+
default: "full_deploy"
1520
# Advanced options - sane defaults, only change if you know what you're doing
1621
hp_masternodes_arm_count:
1722
description: "Advanced: Number of ARM HP masternodes"
@@ -52,14 +57,15 @@ on:
5257
jobs:
5358
create:
5459
name: Create Devnet
55-
runs-on: ubuntu-latest
60+
runs-on: ubuntu-22.04
5661
timeout-minutes: 120
5762
concurrency:
5863
group: "devnet-${{ github.event.inputs.devnet_name }}"
5964
cancel-in-progress: false
6065

6166
env:
6267
NETWORK_NAME: "devnet-${{ github.event.inputs.devnet_name }}"
68+
DEVNET_ONLY_GUARD: "true"
6369
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
6470
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
6571
AWS_REGION: ${{ secrets.AWS_REGION }}
@@ -85,6 +91,14 @@ jobs:
8591
echo "Error: devnet_name must be lowercase alphanumeric with optional hyphens"
8692
exit 1
8793
fi
94+
if [[ "$NAME" == "testnet" || "$NAME" == "mainnet" || "$NAME" == mainnet-* ]]; then
95+
echo "Error: reserved network names are not allowed in this workflow"
96+
exit 1
97+
fi
98+
if [[ ! "devnet-$NAME" =~ ^devnet-[a-z0-9][a-z0-9-]*$ ]]; then
99+
echo "Error: resulting network name is not a valid devnet name"
100+
exit 1
101+
fi
88102
echo "Will create: devnet-$NAME"
89103
90104
- name: Checkout dash-network-deploy
@@ -112,7 +126,7 @@ jobs:
112126
- name: Install Ansible
113127
run: |
114128
python3 -m pip install --upgrade pip
115-
python3 -m pip install ansible
129+
python3 -m pip install ansible-core==2.16.3 jmespath
116130
117131
- name: Install Ansible roles
118132
run: |
@@ -124,6 +138,7 @@ jobs:
124138
env:
125139
DEPLOY_SERVER_KEY: ${{ secrets.DEPLOY_SERVER_KEY }}
126140
EVO_APP_DEPLOY_KEY: ${{ secrets.EVO_APP_DEPLOY_KEY }}
141+
EVO_APP_DEPLOY_WRITE_KEY: ${{ secrets.EVO_APP_DEPLOY_WRITE_KEY }}
127142
run: |
128143
mkdir -p ~/.ssh
129144
@@ -139,6 +154,12 @@ jobs:
139154
printf '%s\n' "$EVO_APP_DEPLOY_KEY" > ~/.ssh/id_ed25519
140155
chmod 600 ~/.ssh/id_ed25519
141156
157+
# Optional write key for pushing to configs repo
158+
if [[ -n "$EVO_APP_DEPLOY_WRITE_KEY" ]]; then
159+
printf '%s\n' "$EVO_APP_DEPLOY_WRITE_KEY" > ~/.ssh/id_ed25519_write
160+
chmod 600 ~/.ssh/id_ed25519_write
161+
fi
162+
142163
# SSH config
143164
cat > ~/.ssh/config << 'EOL'
144165
Host github.com
@@ -168,7 +189,39 @@ jobs:
168189
TERRAFORM_DYNAMODB_TABLE=$TERRAFORM_DYNAMODB_TABLE
169190
EOF
170191
192+
- name: Check for existing devnet configs
193+
id: existing_configs
194+
run: |
195+
git clone git@github.com:dashpay/dash-network-configs.git /tmp/dash-network-configs-source
196+
197+
FOUND=0
198+
MISSING=0
199+
for ext in yml tfvars inventory; do
200+
SRC="/tmp/dash-network-configs-source/$NETWORK_NAME.$ext"
201+
if [[ -f "$SRC" ]]; then
202+
FOUND=$((FOUND + 1))
203+
else
204+
MISSING=$((MISSING + 1))
205+
fi
206+
done
207+
208+
if [[ $FOUND -eq 3 ]]; then
209+
echo "resume_mode=true" >> "$GITHUB_OUTPUT"
210+
echo "Found existing config set for $NETWORK_NAME. Reusing config repo files and skipping Terraform."
211+
cp "/tmp/dash-network-configs-source/$NETWORK_NAME.yml" networks/
212+
cp "/tmp/dash-network-configs-source/$NETWORK_NAME.tfvars" networks/
213+
cp "/tmp/dash-network-configs-source/$NETWORK_NAME.inventory" networks/
214+
elif [[ $FOUND -eq 0 ]]; then
215+
echo "resume_mode=false" >> "$GITHUB_OUTPUT"
216+
echo "No existing config set found for $NETWORK_NAME. Running full create flow."
217+
else
218+
echo "Error: Partial config set found for $NETWORK_NAME in dash-network-configs."
219+
ls -la /tmp/dash-network-configs-source/$NETWORK_NAME.* 2>/dev/null || true
220+
exit 1
221+
fi
222+
171223
- name: Generate network configs
224+
if: steps.existing_configs.outputs.resume_mode != 'true'
172225
env:
173226
MN_AMD: ${{ github.event.inputs.masternodes_amd_count }}
174227
MN_ARM: ${{ github.event.inputs.masternodes_arm_count }}
@@ -236,9 +289,21 @@ jobs:
236289
DISK_SIZE: ${{ github.event.inputs.hpmn_disk_size }}
237290
run: |
238291
TFVARS_FILE="networks/$NETWORK_NAME.tfvars"
292+
DEFAULT_MAIN_DOMAIN="networks.dash.org"
239293
240294
# Read current value from file (empty if not set)
241295
CURRENT_SIZE=$(grep -oP 'hpmn_node_disk_size\s*=\s*\K[0-9]+' "$TFVARS_FILE" 2>/dev/null || echo "")
296+
CURRENT_MAIN_DOMAIN=$(grep -oP 'main_domain\s*=\s*"\K[^"]*' "$TFVARS_FILE" 2>/dev/null || echo "")
297+
298+
# Generated tfvars leaves main_domain empty; ensure ACM DNS names are valid.
299+
if [[ -z "$CURRENT_MAIN_DOMAIN" ]]; then
300+
echo "Setting main_domain to $DEFAULT_MAIN_DOMAIN..."
301+
if grep -q '^main_domain\s*=' "$TFVARS_FILE"; then
302+
sed -i "s|^main_domain\\s*=.*|main_domain = \"$DEFAULT_MAIN_DOMAIN\"|" "$TFVARS_FILE"
303+
else
304+
echo "main_domain = \"$DEFAULT_MAIN_DOMAIN\"" >> "$TFVARS_FILE"
305+
fi
306+
fi
242307
243308
if [[ -n "$DISK_SIZE" && "$DISK_SIZE" != "$CURRENT_SIZE" ]]; then
244309
if [[ ! "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
@@ -257,32 +322,69 @@ jobs:
257322
cat "$TFVARS_FILE"
258323
259324
- name: Deploy devnet (Terraform + Ansible)
325+
env:
326+
TF_IN_AUTOMATION: "true"
327+
TF_CLI_ARGS_apply: "-auto-approve"
328+
DEPLOY_TAGS: ${{ github.event.inputs.deploy_tags }}
260329
run: |
261330
echo "============================================"
262331
echo "Deploying $NETWORK_NAME"
332+
echo "Ansible tags: $DEPLOY_TAGS"
263333
echo "============================================"
264334
265335
chmod +x ./bin/deploy
266336
# GitHub Actions checks out a detached HEAD; bypass branch safety check.
267-
./bin/deploy -f "$NETWORK_NAME"
337+
if [[ "${{ steps.existing_configs.outputs.resume_mode }}" == "true" ]]; then
338+
echo "Resume mode enabled. Skipping Terraform and re-running provisioning only."
339+
./bin/deploy -p -f --tags="$DEPLOY_TAGS" "$NETWORK_NAME"
340+
else
341+
./bin/deploy -f --tags="$DEPLOY_TAGS" "$NETWORK_NAME"
342+
fi
268343
269344
- name: Push configs to dash-network-configs
345+
if: always()
346+
env:
347+
EVO_APP_DEPLOY_WRITE_KEY: ${{ secrets.EVO_APP_DEPLOY_WRITE_KEY }}
270348
run: |
271349
# Clone the configs repo to a temp directory
272350
git clone git@github.com:dashpay/dash-network-configs.git /tmp/dash-network-configs
273351
274-
# Copy generated config files
275-
cp "networks/$NETWORK_NAME.yml" /tmp/dash-network-configs/
276-
cp "networks/$NETWORK_NAME.tfvars" /tmp/dash-network-configs/
277-
cp "networks/$NETWORK_NAME.inventory" /tmp/dash-network-configs/
352+
# Copy generated config files if present
353+
COPIED=0
354+
for ext in yml tfvars inventory; do
355+
SRC="networks/$NETWORK_NAME.$ext"
356+
if [[ -f "$SRC" ]]; then
357+
cp "$SRC" /tmp/dash-network-configs/
358+
COPIED=$((COPIED + 1))
359+
else
360+
echo "Skipping missing file: $SRC"
361+
fi
362+
done
363+
364+
if [[ $COPIED -eq 0 ]]; then
365+
echo "No config files found to push"
366+
exit 0
367+
fi
278368
279369
# Commit and push
280370
cd /tmp/dash-network-configs
281371
git config user.name "GitHub Actions"
282372
git config user.email "actions@github.com"
283373
git add .
284374
git commit -m "Add configs for $NETWORK_NAME" || echo "No changes to commit"
285-
git push
375+
376+
# Use optional write key if configured; otherwise try default key.
377+
if [[ -n "$EVO_APP_DEPLOY_WRITE_KEY" && -f "$HOME/.ssh/id_ed25519_write" ]]; then
378+
GIT_SSH_COMMAND='ssh -i ~/.ssh/id_ed25519_write -o StrictHostKeyChecking=no' git push || {
379+
echo "::warning::Failed to push configs with EVO_APP_DEPLOY_WRITE_KEY"
380+
exit 0
381+
}
382+
else
383+
git push || {
384+
echo "::warning::Failed to push configs (likely read-only EVO_APP_DEPLOY_KEY). Configure secret EVO_APP_DEPLOY_WRITE_KEY with write access."
385+
exit 0
386+
}
387+
fi
286388
287389
echo "Configs pushed to dash-network-configs repo"
288390

.github/workflows/destroy-devnet.yml

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ on:
2020
jobs:
2121
destroy:
2222
name: Destroy Devnet
23-
runs-on: ubuntu-latest
23+
runs-on: ubuntu-22.04
2424
timeout-minutes: 60
2525
concurrency:
2626
group: "devnet-${{ github.event.inputs.devnet_name }}"
2727
cancel-in-progress: false
2828

2929
env:
3030
NETWORK_NAME: "devnet-${{ github.event.inputs.devnet_name }}"
31+
DEVNET_ONLY_GUARD: "true"
3132
DESTROY_TARGET: ${{ github.event.inputs.destroy_target }}
3233
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
3334
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -50,6 +51,18 @@ jobs:
5051
echo "Error: Do not include 'devnet-' prefix. Just provide the name (e.g. 'mytest')"
5152
exit 1
5253
fi
54+
if [[ ! "$NAME" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
55+
echo "Error: devnet_name must be lowercase alphanumeric with optional hyphens"
56+
exit 1
57+
fi
58+
if [[ "$NAME" == "testnet" || "$NAME" == "mainnet" || "$NAME" == mainnet-* ]]; then
59+
echo "Error: reserved network names are not allowed in this workflow"
60+
exit 1
61+
fi
62+
if [[ ! "devnet-$NAME" =~ ^devnet-[a-z0-9][a-z0-9-]*$ ]]; then
63+
echo "Error: resulting network name is not a valid devnet name"
64+
exit 1
65+
fi
5366
echo "Will destroy: devnet-$NAME (target: $DESTROY_TARGET)"
5467
5568
- name: Checkout dash-network-deploy
@@ -77,7 +90,7 @@ jobs:
7790
- name: Install Ansible
7891
run: |
7992
python3 -m pip install --upgrade pip
80-
python3 -m pip install ansible
93+
python3 -m pip install ansible-core==2.16.3 jmespath
8194
8295
- name: Install Ansible roles
8396
run: |
@@ -89,6 +102,7 @@ jobs:
89102
env:
90103
DEPLOY_SERVER_KEY: ${{ secrets.DEPLOY_SERVER_KEY }}
91104
EVO_APP_DEPLOY_KEY: ${{ secrets.EVO_APP_DEPLOY_KEY }}
105+
EVO_APP_DEPLOY_WRITE_KEY: ${{ secrets.EVO_APP_DEPLOY_WRITE_KEY }}
92106
run: |
93107
mkdir -p ~/.ssh
94108
@@ -104,6 +118,12 @@ jobs:
104118
printf '%s\n' "$EVO_APP_DEPLOY_KEY" > ~/.ssh/id_ed25519
105119
chmod 600 ~/.ssh/id_ed25519
106120
121+
# Optional write key for pushing to configs repo
122+
if [[ -n "$EVO_APP_DEPLOY_WRITE_KEY" ]]; then
123+
printf '%s\n' "$EVO_APP_DEPLOY_WRITE_KEY" > ~/.ssh/id_ed25519_write
124+
chmod 600 ~/.ssh/id_ed25519_write
125+
fi
126+
107127
# SSH config
108128
cat > ~/.ssh/config << 'EOL'
109129
Host github.com
@@ -190,12 +210,17 @@ jobs:
190210
echo ""
191211
192212
- name: Destroy devnet
213+
env:
214+
TF_IN_AUTOMATION: "true"
215+
TF_CLI_ARGS_destroy: "-auto-approve"
193216
run: |
194217
chmod +x ./bin/destroy
195218
./bin/destroy "$NETWORK_NAME" -t="$DESTROY_TARGET"
196219
197220
- name: Remove configs from dash-network-configs
198221
if: github.event.inputs.destroy_target == 'all'
222+
env:
223+
EVO_APP_DEPLOY_WRITE_KEY: ${{ secrets.EVO_APP_DEPLOY_WRITE_KEY }}
199224
run: |
200225
cd /tmp/dash-network-configs
201226
git config user.name "GitHub Actions"
@@ -207,7 +232,19 @@ jobs:
207232
git rm "$NETWORK_NAME.inventory" 2>/dev/null || true
208233
209234
git commit -m "Remove configs for $NETWORK_NAME (destroyed)" || echo "No changes to commit"
210-
git push
235+
236+
# Use optional write key if configured; otherwise try default key.
237+
if [[ -n "$EVO_APP_DEPLOY_WRITE_KEY" && -f "$HOME/.ssh/id_ed25519_write" ]]; then
238+
GIT_SSH_COMMAND='ssh -i ~/.ssh/id_ed25519_write -o StrictHostKeyChecking=no' git push || {
239+
echo "::warning::Failed to push config removal with EVO_APP_DEPLOY_WRITE_KEY"
240+
exit 0
241+
}
242+
else
243+
git push || {
244+
echo "::warning::Failed to push config removal (likely read-only EVO_APP_DEPLOY_KEY). Configure secret EVO_APP_DEPLOY_WRITE_KEY with write access."
245+
exit 0
246+
}
247+
fi
211248
212249
echo "Configs removed from dash-network-configs repo"
213250

ansible/roles/activate_dashd_sporks/tasks/main.yml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,5 @@
2828
- SPORK_17_QUORUM_DKG_ENABLED
2929
- SPORK_19_CHAINLOCKS_ENABLED
3030
- SPORK_21_QUORUM_ALL_CONNECTED
31-
when: not (active_sporks.stdout | from_json)[item]
31+
when: not ((active_sporks.stdout | from_json).get(item, false))
3232
changed_when: spork_update.stdout == "success"
33-
34-
- name: Activate EHF
35-
ansible.builtin.include_role:
36-
name: generate_blocks
37-
vars:
38-
generate: spork_activation
39-
spork_name: SPORK_24_TEST_EHF
40-
when: not (active_sporks.stdout | from_json)["SPORK_24_TEST_EHF"]

ansible/roles/dashmate/tasks/build.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
ansible.builtin.command: '{{ dashmate_home }}/rustup.sh -y'
5353
become: true
5454
become_user: dashmate
55+
become_flags: '-H'
5556
when:
5657
- rustup.changed
5758
- dashmate_platform_enable
@@ -73,6 +74,7 @@
7374
cargo install wasm-bindgen-cli@0.2.86
7475
become: true
7576
become_user: dashmate
77+
become_flags: '-H'
7678
args:
7779
executable: /bin/bash
7880
changed_when: true
@@ -110,6 +112,7 @@
110112
- name: Clone dashmate source
111113
become: true
112114
become_user: dashmate
115+
become_flags: '-H'
113116
ansible.builtin.git:
114117
repo: 'https://github.com/dashpay/platform'
115118
dest: '{{ dashmate_source_dir }}'
@@ -132,6 +135,7 @@
132135
ansible.builtin.command: yarn install
133136
become: true
134137
become_user: dashmate
138+
become_flags: '-H'
135139
args:
136140
chdir: '{{ dashmate_source_dir }}'
137141
creates: '{{ dashmate_source_dir }}/.yarn/unplugged'
@@ -148,6 +152,7 @@
148152
yarn workspace dash build
149153
become: true
150154
become_user: dashmate
155+
become_flags: '-H'
151156
args:
152157
chdir: '{{ dashmate_source_dir }}'
153158
executable: /bin/bash
@@ -158,6 +163,7 @@
158163
ansible.builtin.command: "{{ dashmate_cmd }} config"
159164
become: true
160165
become_user: dashmate
166+
become_flags: '-H'
161167
args:
162168
chdir: '{{ dashmate_cwd }}'
163169
register: dashmate_config_result
@@ -168,6 +174,7 @@
168174
ansible.builtin.command: yarn dashmate config set dashmate.helper.docker.build.enabled true
169175
become: true
170176
become_user: dashmate
177+
become_flags: '-H'
171178
register: build_dashmate_helper
172179
args:
173180
chdir: '{{ dashmate_source_dir }}'

ansible/roles/dashmate/tasks/destroy_platform.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
ansible.builtin.command: "{{ dashmate_cmd }} reset --platform --force --hard"
88
become: true
99
become_user: dashmate
10+
become_flags: '-H'
1011
args:
1112
chdir: '{{ dashmate_cwd }}'
1213
register: dashmate_reset

0 commit comments

Comments
 (0)