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
307 changes: 307 additions & 0 deletions .github/workflows/create-devnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
name: Create Devnet

on:
workflow_dispatch:
inputs:
devnet_name:
description: "Devnet name (without 'devnet-' prefix, e.g. 'mytest' creates 'devnet-mytest')"
required: true
type: string
platform_version:
description: "Platform/dashmate version to deploy (e.g. 2.0.0-rc.16)"
required: true
type: string
default: "2.0.0-rc.16"
# Advanced options - sane defaults, only change if you know what you're doing
hp_masternodes_arm_count:
description: "Advanced: Number of ARM HP masternodes"
required: false
type: string
default: "11"
hp_masternodes_amd_count:
description: "Advanced: Number of AMD HP masternodes"
required: false
type: string
default: "0"
masternodes_arm_count:
description: "Advanced: Number of ARM regular masternodes"
required: false
type: string
default: "0"
masternodes_amd_count:
description: "Advanced: Number of AMD regular masternodes"
required: false
type: string
default: "0"
seed_count:
description: "Advanced: Number of seed nodes"
required: false
type: string
default: "1"
core_version:
description: "Advanced: Core (dashd) image version (leave empty for default)"
required: false
type: string
default: ""
hpmn_disk_size:
description: "Advanced: HP masternode disk size in GB"
required: false
type: string
default: "30"

jobs:
create:
name: Create Devnet
runs-on: ubuntu-latest
timeout-minutes: 120
concurrency:
group: "devnet-${{ github.event.inputs.devnet_name }}"
cancel-in-progress: false

env:
NETWORK_NAME: "devnet-${{ github.event.inputs.devnet_name }}"
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
TERRAFORM_S3_BUCKET: ${{ secrets.TERRAFORM_S3_BUCKET }}
TERRAFORM_S3_KEY: ${{ secrets.TERRAFORM_S3_KEY }}
TERRAFORM_DYNAMODB_TABLE: ${{ secrets.TERRAFORM_DYNAMODB_TABLE }}
ANSIBLE_HOST_KEY_CHECKING: "false"

steps:
- name: Validate devnet name
env:
NAME: ${{ github.event.inputs.devnet_name }}
run: |
if [[ -z "$NAME" ]]; then
echo "Error: devnet_name is required"
exit 1
fi
if [[ "$NAME" =~ ^devnet- ]]; then
echo "Error: Do not include 'devnet-' prefix. Just provide the name (e.g. 'mytest')"
exit 1
fi
if [[ ! "$NAME" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
echo "Error: devnet_name must be lowercase alphanumeric with optional hyphens"
exit 1
fi
echo "Will create: devnet-$NAME"

- name: Checkout dash-network-deploy
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install Node.js dependencies
run: npm ci

- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.12.1"
terraform_wrapper: false
Comment on lines +101 to +105
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

latest stable Terraform CLI version February 2026

💡 Result:

As of February 24, 2026, the latest stable Terraform CLI release is v1.14.5, released February 11, 2026. [1][2]

Sources:
[1] HashiCorp Terraform GitHub Releases (shows v1.14.5 as “Latest”, dated Feb 11, 2026)
[2] endoflife.date Terraform page (lists 1.14.5 released Feb 11, 2026)


Update Terraform version from 1.12.1 to 1.14.5

Terraform 1.12.1 is outdated; the current stable release is v1.14.5 (released February 11, 2026). Upgrading closes a 2-minor-version gap and includes bug fixes and security patches necessary for a production devnet infrastructure workflow.

-          terraform_version: "1.12.1"
+          terraform_version: "1.14.5"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.12.1"
terraform_wrapper: false
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.14.5"
terraform_wrapper: false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/create-devnet.yml around lines 94 - 98, Update the
Terraform version used by the "Set up Terraform" GitHub Actions step: in the
step that uses hashicorp/setup-terraform@v3 (step name "Set up Terraform"),
change the input terraform_version from "1.12.1" to "1.14.5" so the workflow
installs Terraform v1.14.5 (keep terraform_wrapper: false unchanged).


- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y python3-pip python3-netaddr sshpass jq

- name: Install Ansible
run: |
python3 -m pip install --upgrade pip
python3 -m pip install ansible

- name: Install Ansible roles
run: |
ansible-galaxy install -r ansible/requirements.yml
mkdir -p ~/.ansible/roles
cp -r ansible/roles/* ~/.ansible/roles/

- name: Set up SSH keys
env:
DEPLOY_SERVER_KEY: ${{ secrets.DEPLOY_SERVER_KEY }}
EVO_APP_DEPLOY_KEY: ${{ secrets.EVO_APP_DEPLOY_KEY }}
run: |
mkdir -p ~/.ssh

# Server SSH key for connecting to nodes
printf '%s\n' "$DEPLOY_SERVER_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa

# Derive public key from private key
ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub
chmod 644 ~/.ssh/id_rsa.pub

# GitHub deploy key for cloning configs repo
printf '%s\n' "$EVO_APP_DEPLOY_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519

# SSH config
cat > ~/.ssh/config << 'EOL'
Host github.com
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking no

Host *
IdentityFile ~/.ssh/id_rsa
User ubuntu
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
EOL

chmod 600 ~/.ssh/config

- name: Create networks/.env
run: |
mkdir -p networks
cat > networks/.env << EOF
PRIVATE_KEY_PATH=$HOME/.ssh/id_rsa
PUBLIC_KEY_PATH=$HOME/.ssh/id_rsa.pub
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
AWS_REGION=$AWS_REGION
TERRAFORM_S3_BUCKET=$TERRAFORM_S3_BUCKET
TERRAFORM_S3_KEY=$TERRAFORM_S3_KEY
TERRAFORM_DYNAMODB_TABLE=$TERRAFORM_DYNAMODB_TABLE
EOF

- name: Generate network configs
env:
MN_AMD: ${{ github.event.inputs.masternodes_amd_count }}
MN_ARM: ${{ github.event.inputs.masternodes_arm_count }}
HP_AMD: ${{ github.event.inputs.hp_masternodes_amd_count }}
HP_ARM: ${{ github.event.inputs.hp_masternodes_arm_count }}
SEED_COUNT: ${{ github.event.inputs.seed_count }}
run: |
# Validate all counts are numeric
for var in MN_AMD MN_ARM HP_AMD HP_ARM SEED_COUNT; do
val="${!var}"
if [[ ! "$val" =~ ^[0-9]+$ ]]; then
echo "Error: $var must be a number, got '$val'"
exit 1
fi
done

echo "Generating configs for $NETWORK_NAME..."
chmod +x ./bin/generate
./bin/generate "$NETWORK_NAME" \
"$MN_AMD" "$MN_ARM" "$HP_AMD" "$HP_ARM" \
-s="$SEED_COUNT"

echo "Generated config files:"
ls -la networks/devnet-*

- name: Update platform version in config
env:
VERSION: ${{ github.event.inputs.platform_version }}
CORE_VERSION: ${{ github.event.inputs.core_version }}
run: |
CONFIG_FILE="networks/$NETWORK_NAME.yml"

# Escape sed-special characters in version strings
SAFE_VERSION=$(printf '%s' "$VERSION" | sed 's/[&\\/]/\\&/g')
SAFE_CORE_VERSION=$(printf '%s' "$CORE_VERSION" | sed 's/[&\\/]/\\&/g')

echo "Setting platform version to $VERSION..."

# Update dashmate version
sed -i "s/dashmate_version: .*/dashmate_version: $SAFE_VERSION/" "$CONFIG_FILE"

# Update platform service images
sed -i "s|drive_image: dashpay/drive:.*|drive_image: dashpay/drive:$SAFE_VERSION|" "$CONFIG_FILE"
sed -i "s|dapi_image: dashpay/dapi:.*|dapi_image: dashpay/dapi:$SAFE_VERSION|" "$CONFIG_FILE"

# Add rs_dapi_image (not in generated config, but group_vars/all defines it
# without a tag, so we must explicitly set it to get the right version)
if ! grep -q "rs_dapi_image:" "$CONFIG_FILE"; then
echo "rs_dapi_image: dashpay/rs-dapi:$VERSION" >> "$CONFIG_FILE"
else
sed -i "s|rs_dapi_image: dashpay/rs-dapi:.*|rs_dapi_image: dashpay/rs-dapi:$SAFE_VERSION|" "$CONFIG_FILE"
fi
Comment on lines +219 to +223
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

sed in the else branch won't match an existing rs_dapi_image entry that lacks a version tag

Line 194's pattern rs_dapi_image: dashpay/rs-dapi:.* requires a literal : suffix. If the generated config ever includes rs_dapi_image: dashpay/rs-dapi (no tag), grep on line 191 would find the key, skip the append, and the sed would silently do nothing. Use rs_dapi_image:.* to match any form — mirrors the fix needed in platform-deploy.yml for the same reason.

🐛 Proposed fix
-            sed -i "s|rs_dapi_image: dashpay/rs-dapi:.*|rs_dapi_image: dashpay/rs-dapi:$VERSION|" "$CONFIG_FILE"
+            sed -i "s|rs_dapi_image:.*|rs_dapi_image: dashpay/rs-dapi:$VERSION|" "$CONFIG_FILE"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/create-devnet.yml around lines 191 - 195, The sed
replacement pattern in the else branch uses "rs_dapi_image: dashpay/rs-dapi:.*"
which fails if the existing line is "rs_dapi_image: dashpay/rs-dapi" (no tag);
update the sed to match any form of the key (e.g., use "rs_dapi_image:.*") so it
will replace lines with or without a tag, and apply the same change to the
analogous occurrence referenced in platform-deploy.yml; target the block that
checks/edits rs_dapi_image and replace the sed search pattern accordingly.


# Update core version if specified
if [[ -n "$CORE_VERSION" ]]; then
echo "Setting core version to $CORE_VERSION..."
sed -i "s|dashd_image: dashpay/dashd:.*|dashd_image: dashpay/dashd:$SAFE_CORE_VERSION|" "$CONFIG_FILE"
fi

echo "Updated config:"
grep -E "(dashmate_version|drive_image|dapi_image|rs_dapi_image|dashd_image)" "$CONFIG_FILE"

- name: Update terraform config
env:
DISK_SIZE: ${{ github.event.inputs.hpmn_disk_size }}
run: |
TFVARS_FILE="networks/$NETWORK_NAME.tfvars"

# Read current value from file (empty if not set)
CURRENT_SIZE=$(grep -oP 'hpmn_node_disk_size\s*=\s*\K[0-9]+' "$TFVARS_FILE" 2>/dev/null || echo "")

if [[ -n "$DISK_SIZE" && "$DISK_SIZE" != "$CURRENT_SIZE" ]]; then
if [[ ! "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
echo "Error: hpmn_disk_size must be a number, got '$DISK_SIZE'"
exit 1
fi
echo "Setting HP masternode disk size to ${DISK_SIZE}GB..."
if [[ -n "$CURRENT_SIZE" ]]; then
sed -i "s/hpmn_node_disk_size = .*/hpmn_node_disk_size = $DISK_SIZE/" "$TFVARS_FILE"
else
echo "hpmn_node_disk_size = $DISK_SIZE" >> "$TFVARS_FILE"
fi
fi

echo "Terraform config:"
cat "$TFVARS_FILE"

- name: Deploy devnet (Terraform + Ansible)
run: |
echo "============================================"
echo "Deploying $NETWORK_NAME"
echo "============================================"

chmod +x ./bin/deploy
# GitHub Actions checks out a detached HEAD; bypass branch safety check.
./bin/deploy -f "$NETWORK_NAME"

- name: Push configs to dash-network-configs
run: |
# Clone the configs repo to a temp directory
git clone git@github.com:dashpay/dash-network-configs.git /tmp/dash-network-configs

# Copy generated config files
cp "networks/$NETWORK_NAME.yml" /tmp/dash-network-configs/
cp "networks/$NETWORK_NAME.tfvars" /tmp/dash-network-configs/
cp "networks/$NETWORK_NAME.inventory" /tmp/dash-network-configs/

# Commit and push
cd /tmp/dash-network-configs
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add .
git commit -m "Add configs for $NETWORK_NAME" || echo "No changes to commit"
git push

echo "Configs pushed to dash-network-configs repo"

- name: Verify platform services
if: success()
run: |
echo "Verifying platform services on HP masternodes..."
ansible hp_masternodes \
-i "networks/$NETWORK_NAME.inventory" \
--private-key="$HOME/.ssh/id_rsa" \
-b -m shell \
-a 'sudo -u dashmate dashmate status services --format=json | jq -r ".[] | select(.service != \"core\") | \"\(.service): \(.status)\""'

- name: Print summary
if: always()
run: |
echo "============================================"
echo "Devnet: $NETWORK_NAME"
echo "============================================"
echo ""
echo "To update this devnet later, use the 'Platform Version Deployment' workflow"
echo "To destroy this devnet, use the 'Destroy Devnet' workflow"
Loading