Skip to content

Commit 529782a

Browse files
committed
refactor: separate AMI build into workflow_run triggered workflow
- Create standalone .github/workflows/build-ami.yml triggered on Pre-release workflow completion - Remove build-ami job and OIDC permissions from release.yml - Update scripts/dappnode_ami_build.sh to require PROFILE_URL env var - Workflow creates thin per-release component that downloads and runs the repo script with pinned PROFILE_URL for that specific release tag - Remove IMAGE_BUILDER_COMPONENT_ARN secret (no longer needed)
1 parent 28f2069 commit 529782a

3 files changed

Lines changed: 136 additions & 99 deletions

File tree

.github/workflows/build-ami.yml

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
name: Build DAppNode AMI
2+
3+
on:
4+
workflow_run:
5+
workflows: ["Pre-release"]
6+
types: [completed]
7+
8+
permissions:
9+
id-token: write
10+
contents: read
11+
12+
jobs:
13+
build-ami:
14+
name: Build DAppNode AMI
15+
runs-on: ubuntu-latest
16+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
17+
steps:
18+
- name: Get release tag
19+
id: tag
20+
env:
21+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22+
run: |
23+
# Get the latest pre-release tag (the one just created by the triggering workflow)
24+
TAG=$(gh release list --repo ${{ github.repository }} \
25+
--limit 1 --json tagName --jq '.[0].tagName')
26+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
27+
echo "Release tag: $TAG"
28+
29+
- name: Configure AWS credentials via OIDC
30+
uses: aws-actions/configure-aws-credentials@v4
31+
with:
32+
role-to-assume: ${{ secrets.IMAGE_BUILDER_ROLE_ARN }}
33+
aws-region: us-east-1
34+
35+
- name: Create component, recipe, and trigger AMI build
36+
env:
37+
PIPELINE_ARN: ${{ secrets.IMAGE_BUILDER_PIPELINE_ARN }}
38+
INFRA_ARN: ${{ secrets.IMAGE_BUILDER_INFRA_ARN }}
39+
DIST_ARN: ${{ secrets.IMAGE_BUILDER_DIST_ARN }}
40+
TAG: ${{ steps.tag.outputs.tag }}
41+
run: |
42+
PROFILE_URL="https://github.com/dappnode/DAppNode/releases/download/${TAG}/dappnode_profile.sh"
43+
SCRIPT_URL="https://raw.githubusercontent.com/dappnode/DAppNode/${TAG}/scripts/dappnode_ami_build.sh"
44+
45+
# Derive component version from tag (v0.2.47 -> 0.2.47)
46+
COMP_VERSION="${TAG#v}"
47+
48+
# Create per-release component (thin wrapper: downloads repo script and runs it)
49+
COMPONENT_ARN=$(aws imagebuilder create-component \
50+
--name "dappnode-build" \
51+
--semantic-version "$COMP_VERSION" \
52+
--platform "Linux" \
53+
--supported-os-versions "Ubuntu" \
54+
--data "$(cat <<EOF
55+
name: DAppNodeBuild
56+
schemaVersion: 1.0
57+
phases:
58+
- name: build
59+
steps:
60+
- name: RunBuildScript
61+
action: ExecuteBash
62+
inputs:
63+
commands:
64+
- |
65+
export PROFILE_URL="${PROFILE_URL}"
66+
wget -O /tmp/dappnode_ami_build.sh "${SCRIPT_URL}"
67+
chmod +x /tmp/dappnode_ami_build.sh
68+
/tmp/dappnode_ami_build.sh
69+
EOF
70+
)" \
71+
--query 'componentBuildVersionArn' --output text)
72+
echo "Component: $COMPONENT_ARN"
73+
74+
# Get current recipe version and patch bump
75+
CURRENT_RECIPE=$(aws imagebuilder get-image-pipeline \
76+
--image-pipeline-arn "$PIPELINE_ARN" \
77+
--query 'imagePipeline.imageRecipeArn' --output text)
78+
CURRENT_VERSION=$(echo "$CURRENT_RECIPE" | grep -oP '[0-9]+\.[0-9]+\.[0-9]+$')
79+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
80+
NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
81+
echo "Recipe: $CURRENT_VERSION -> $NEW_VERSION"
82+
83+
# Create recipe with per-release component
84+
RECIPE_ARN=$(aws imagebuilder create-image-recipe \
85+
--name "dappnode-image" \
86+
--semantic-version "$NEW_VERSION" \
87+
--parent-image "arn:aws:imagebuilder:us-east-1:aws:image/ubuntu-server-24-lts-x86/x.x.x" \
88+
--components "[{\"componentArn\":\"$COMPONENT_ARN\"}]" \
89+
--block-device-mappings '[{"deviceName":"/dev/sda1","ebs":{"volumeSize":16,"volumeType":"gp3","deleteOnTermination":true}}]' \
90+
--working-directory "/tmp" \
91+
--query 'imageRecipeArn' --output text)
92+
93+
# Update pipeline and trigger
94+
aws imagebuilder update-image-pipeline \
95+
--image-pipeline-arn "$PIPELINE_ARN" \
96+
--image-recipe-arn "$RECIPE_ARN" \
97+
--infrastructure-configuration-arn "$INFRA_ARN" \
98+
--distribution-configuration-arn "$DIST_ARN" \
99+
--image-tests-configuration "imageTestsEnabled=false"
100+
101+
EXECUTION=$(aws imagebuilder start-image-pipeline-execution \
102+
--image-pipeline-arn "$PIPELINE_ARN" \
103+
--query 'imageBuildVersionArn' --output text)
104+
105+
echo "### AMI Build Triggered 🚀" >> "$GITHUB_STEP_SUMMARY"
106+
echo "- **Tag:** ${TAG}" >> "$GITHUB_STEP_SUMMARY"
107+
echo "- **Recipe:** ${NEW_VERSION}" >> "$GITHUB_STEP_SUMMARY"
108+
echo "- **Profile:** ${PROFILE_URL}" >> "$GITHUB_STEP_SUMMARY"
109+
echo "- **Image ARN:** ${EXECUTION}" >> "$GITHUB_STEP_SUMMARY"

.github/workflows/release.yml

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
name: Pre-release
22

3-
permissions:
4-
id-token: write
5-
contents: write
6-
73
on:
84
workflow_dispatch:
95
inputs:
@@ -267,57 +263,3 @@ jobs:
267263
body_path: CHANGELOG.md
268264
env:
269265
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
270-
271-
build-ami:
272-
name: Build DAppNode AMI
273-
runs-on: ubuntu-latest
274-
needs: release
275-
steps:
276-
- name: Configure AWS credentials via OIDC
277-
uses: aws-actions/configure-aws-credentials@v4
278-
with:
279-
role-to-assume: ${{ secrets.IMAGE_BUILDER_ROLE_ARN }}
280-
aws-region: us-east-1
281-
282-
- name: Bump recipe version and trigger AMI build
283-
env:
284-
PIPELINE_ARN: ${{ secrets.IMAGE_BUILDER_PIPELINE_ARN }}
285-
INFRA_ARN: ${{ secrets.IMAGE_BUILDER_INFRA_ARN }}
286-
DIST_ARN: ${{ secrets.IMAGE_BUILDER_DIST_ARN }}
287-
COMPONENT_ARN: ${{ secrets.IMAGE_BUILDER_COMPONENT_ARN }}
288-
run: |
289-
# Get current recipe version and patch bump
290-
CURRENT_RECIPE=$(aws imagebuilder get-image-pipeline \
291-
--image-pipeline-arn "$PIPELINE_ARN" \
292-
--query 'imagePipeline.imageRecipeArn' --output text)
293-
CURRENT_VERSION=$(echo "$CURRENT_RECIPE" | grep -oP '[0-9]+\.[0-9]+\.[0-9]+$')
294-
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
295-
NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
296-
echo "Bumping recipe: $CURRENT_VERSION -> $NEW_VERSION"
297-
298-
# Create new recipe version (same component, fresh Ubuntu 24 base)
299-
RECIPE_ARN=$(aws imagebuilder create-image-recipe \
300-
--name "dappnode-image" \
301-
--semantic-version "$NEW_VERSION" \
302-
--parent-image "arn:aws:imagebuilder:us-east-1:aws:image/ubuntu-server-24-lts-x86/x.x.x" \
303-
--components "[{\"componentArn\":\"$COMPONENT_ARN\"}]" \
304-
--block-device-mappings '[{"deviceName":"/dev/sda1","ebs":{"volumeSize":8,"volumeType":"gp2","deleteOnTermination":true}}]' \
305-
--working-directory "/tmp" \
306-
--query 'imageRecipeArn' --output text)
307-
308-
# Update pipeline and trigger build
309-
aws imagebuilder update-image-pipeline \
310-
--image-pipeline-arn "$PIPELINE_ARN" \
311-
--image-recipe-arn "$RECIPE_ARN" \
312-
--infrastructure-configuration-arn "$INFRA_ARN" \
313-
--distribution-configuration-arn "$DIST_ARN" \
314-
--image-tests-configuration "imageTestsEnabled=false"
315-
316-
EXECUTION=$(aws imagebuilder start-image-pipeline-execution \
317-
--image-pipeline-arn "$PIPELINE_ARN" \
318-
--query 'imageBuildVersionArn' --output text)
319-
320-
echo "🚀 AMI build started: $EXECUTION (recipe $NEW_VERSION)"
321-
echo "### AMI Build Triggered" >> "$GITHUB_STEP_SUMMARY"
322-
echo "- **Recipe version:** $NEW_VERSION" >> "$GITHUB_STEP_SUMMARY"
323-
echo "- **Image ARN:** $EXECUTION" >> "$GITHUB_STEP_SUMMARY"

scripts/dappnode_ami_build.sh

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,76 +3,63 @@
33
# Purpose: Install prerequisites, pre-download core Docker images, and set up
44
# first-boot installer for EC2 Image Builder.
55
#
6+
# Env vars:
7+
# PROFILE_URL — URL to dappnode_profile.sh with pinned versions (required)
8+
#
69
# The installer still runs at first boot (via rc.local), but finds the heavy
7-
# Docker images already cached in /usr/src/dappnode/DNCORE/, making boot fast
8-
# and not dependent on network for bulk downloads.
10+
# Docker images already cached in /usr/src/dappnode/DNCORE/, making boot fast.
911

1012
set -euo pipefail
1113

14+
: "${PROFILE_URL:?PROFILE_URL env var is required}"
15+
1216
DAPPNODE_DIR="/usr/src/dappnode"
1317
DNCORE_DIR="$DAPPNODE_DIR/DNCORE"
1418
LOGS_DIR="$DAPPNODE_DIR/logs"
1519
LOG_FILE="$LOGS_DIR/ami_build.log"
1620

21+
export DEBIAN_FRONTEND=noninteractive
22+
1723
mkdir -p "$DAPPNODE_DIR/scripts" "$DNCORE_DIR" "$LOGS_DIR"
1824
touch "$LOG_FILE"
25+
exec > >(tee -a "$LOG_FILE") 2>&1
1926

20-
log() { echo "[AMI-BUILD] $*" | tee -a "$LOG_FILE"; }
27+
log() { echo "[AMI-BUILD] $*"; }
2128

2229
lsb_dist="$(. /etc/os-release && echo "$ID")"
23-
log "Detected OS: $lsb_dist"
30+
log "OS: $lsb_dist | Profile: $PROFILE_URL"
31+
32+
# ─── Phase 1: Prerequisites ──────────────────────────────────────────────────
33+
log "=== Phase 1: Prerequisites ==="
2434

25-
# ─── Docker ───────────────────────────────────────────────────────────────────
26-
install_docker() {
35+
apt-get update -y
36+
37+
if ! docker -v >/dev/null 2>&1; then
2738
log "Installing Docker..."
28-
apt-get update -y
2939
apt-get remove -y docker docker-engine docker.io containerd runc || true
30-
3140
apt-get install -y ca-certificates curl lsb-release
3241
install -m 0755 -d /etc/apt/keyrings
3342
curl -fsSL "https://download.docker.com/linux/${lsb_dist}/gpg" -o /etc/apt/keyrings/docker.asc
3443
chmod a+r /etc/apt/keyrings/docker.asc
35-
3644
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$lsb_dist $(lsb_release -cs) stable" \
3745
| tee /etc/apt/sources.list.d/docker.list >/dev/null
38-
3946
apt-get update -y
4047
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
41-
[ -f "/usr/bin/xz" ] || apt-get install -y xz-utils
42-
log "Docker installed successfully"
43-
}
48+
fi
4449

45-
# ─── Docker Compose alias (legacy compatibility) ──────────────────────────────
46-
install_compose_alias() {
47-
cat >/usr/local/bin/docker-compose <<'EOL'
50+
cat >/usr/local/bin/docker-compose <<'EOL'
4851
#!/bin/bash
4952
docker compose "$@"
5053
EOL
51-
chmod +x /usr/local/bin/docker-compose
52-
}
53-
54-
# ─── Prerequisites ────────────────────────────────────────────────────────────
55-
log "=== Phase 1: Prerequisites ==="
56-
57-
apt-get update -y | tee -a "$LOG_FILE"
58-
59-
if ! docker -v >/dev/null 2>&1; then
60-
install_docker 2>&1 | tee -a "$LOG_FILE"
61-
else
62-
log "Docker already installed"
63-
fi
64-
65-
install_compose_alias
54+
chmod +x /usr/local/bin/docker-compose
6655

6756
modprobe wireguard 2>/dev/null || apt-get install -y wireguard-dkms || apt-get install -y wireguard-tools || true
6857
apt-get install -y lsof iptables xz-utils || true
6958

70-
# ─── Pre-download core Docker images ─────────────────────────────────────────
59+
# ─── Phase 2: Pre-download core images ───────────────────────────────────────
7160
log "=== Phase 2: Pre-downloading core images ==="
7261

73-
# Download latest released profile (contains version pins)
74-
wget -O "$DNCORE_DIR/.dappnode_profile" \
75-
"https://github.com/dappnode/DAppNode/releases/latest/download/dappnode_profile.sh"
62+
wget -O "$DNCORE_DIR/.dappnode_profile" "$PROFILE_URL"
7663

7764
# Source only the version variables (up to ISOBUILD marker)
7865
sed '/^\#\!ISOBUILD/q' "$DNCORE_DIR/.dappnode_profile" > /tmp/vars.sh
@@ -106,24 +93,23 @@ for comp in "${COMPONENTS[@]}"; do
10693
log "WARNING: Failed to download $comp manifest"
10794
done
10895

109-
# Grab content hashes for execution/consensus clients
96+
# Content hashes for execution/consensus clients
11097
CONTENT_HASH_PKGS=(besu geth nethermind erigon prysm teku lighthouse lodestar nimbus)
11198
HASH_FILE="$DNCORE_DIR/packages-content-hash.csv"
11299
rm -f "$HASH_FILE"
113100
for pkg in "${CONTENT_HASH_PKGS[@]}"; do
114101
HASH=$(wget -q -O- "https://github.com/dappnode/DAppNodePackage-${pkg}/releases/latest/download/content-hash" || true)
115102
if [ -n "$HASH" ]; then
116103
echo "${pkg}.dnp.dappnode.eth,${HASH}" >> "$HASH_FILE"
117-
log "Got content hash for $pkg"
104+
log "Got content hash: $pkg"
118105
fi
119106
done
120107

121108
log "Pre-download complete:"
122-
ls -lh "$DNCORE_DIR/"
123109
du -sh "$DNCORE_DIR/"
124110

125-
# ─── Set up first-boot installer ─────────────────────────────────────────────
126-
log "=== Phase 3: First-boot installer ==="
111+
# ─── Phase 3: First-boot installer ───────────────────────────────────────────
112+
log "=== Phase 3: First-boot setup ==="
127113

128114
wget -O "$DAPPNODE_DIR/scripts/dappnode_install.sh" https://installer.dappnode.io
129115
chmod +x "$DAPPNODE_DIR/scripts/dappnode_install.sh"
@@ -136,4 +122,4 @@ RC
136122
chmod +x /etc/rc.local
137123
touch "$DAPPNODE_DIR/.firstboot"
138124

139-
log "=== AMI build complete. First boot will find pre-cached images in DNCORE/ ==="
125+
log "=== AMI build complete ==="

0 commit comments

Comments
 (0)