Skip to content

Commit d9f7678

Browse files
authored
CI: Validate bootnodes in workflow (#3626)
* ci: Validate bootnodes * increase timeout * address remark
1 parent 9864fe0 commit d9f7678

File tree

4 files changed

+235
-14
lines changed

4 files changed

+235
-14
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
name: Check Bootnodes
2+
3+
on:
4+
schedule:
5+
- cron: "0 5 * * 0" # Runs every Sunday at 5:00 AM UTC
6+
workflow_dispatch:
7+
8+
jobs:
9+
check-bootnodes:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
network:
17+
- name: moonbeam
18+
chain: moonbeam
19+
spec_file: specs/moonbeam/parachain-embedded-specs.json
20+
type: parachain
21+
- name: moonriver
22+
chain: moonriver
23+
spec_file: specs/moonriver/parachain-embedded-specs.json
24+
type: parachain
25+
- name: moonbase-alpha
26+
chain: moonbase-alpha
27+
spec_file: specs/alphanet/parachain-embedded-specs-v8.json
28+
type: parachain
29+
- name: moonbase-alpha-relay
30+
chain: moonbase-alpha
31+
spec_file: specs/alphanet/westend-embedded-specs-v8.json
32+
type: relay
33+
steps:
34+
- name: Checkout
35+
uses: actions/checkout@v4
36+
37+
- name: Get latest moonbeam release
38+
if: matrix.network.type == 'parachain'
39+
id: get-moonbeam-release
40+
run: |
41+
LATEST_CLIENT=$(curl -s https://api.github.com/repos/moonbeam-foundation/moonbeam/releases | jq -r '.[] | select(.name | test("v";"i")) | .tag_name' | sort -rs | head -n 1 | tr -d '[:blank:]')
42+
echo "latest_client=$LATEST_CLIENT" >> $GITHUB_OUTPUT
43+
echo "Latest Moonbeam release: $LATEST_CLIENT"
44+
45+
- name: Download moonbeam binary
46+
if: matrix.network.type == 'parachain'
47+
run: |
48+
mkdir -p target/release
49+
wget -q https://github.com/moonbeam-foundation/moonbeam/releases/download/${{ steps.get-moonbeam-release.outputs.latest_client }}/moonbeam -O target/release/moonbeam
50+
chmod +x target/release/moonbeam
51+
52+
- name: Get latest polkadot release
53+
if: matrix.network.type == 'relay'
54+
id: get-polkadot-release
55+
run: |
56+
LATEST_POLKADOT=$(curl -s https://api.github.com/repos/paritytech/polkadot-sdk/releases/latest | jq -r '.tag_name')
57+
echo "latest_polkadot=$LATEST_POLKADOT" >> $GITHUB_OUTPUT
58+
echo "Latest Polkadot SDK release: $LATEST_POLKADOT"
59+
60+
- name: Download polkadot binary
61+
if: matrix.network.type == 'relay'
62+
run: |
63+
mkdir -p target/release
64+
wget -q https://github.com/paritytech/polkadot-sdk/releases/download/${{ steps.get-polkadot-release.outputs.latest_polkadot }}/polkadot -O target/release/polkadot
65+
chmod +x target/release/polkadot
66+
67+
- name: Validate bootnodes
68+
run: |
69+
BOOTNODES=$(jq -r '.bootNodes[]' ${{ matrix.network.spec_file }})
70+
TOTAL=$(echo "$BOOTNODES" | wc -l | tr -d ' ')
71+
SPEC_TYPE="${{ matrix.network.type }}"
72+
73+
echo "Validating $TOTAL $SPEC_TYPE bootnodes for ${{ matrix.network.name }}..."
74+
mkdir -p test_results reports
75+
76+
test_bootnode() {
77+
local bootnode="$1" idx="$2"
78+
local base_port=$((30000 + idx * 100))
79+
local peer_id=$(echo "$bootnode" | grep -oE "12D3KooW[a-zA-Z0-9]+")
80+
local log_file="test_results/node_${idx}.log"
81+
local result="TIMEOUT"
82+
83+
if [ "$SPEC_TYPE" = "parachain" ]; then
84+
timeout 65s ./target/release/moonbeam \
85+
--chain=${{ matrix.network.chain }} --reserved-only --reserved-nodes "$bootnode" \
86+
--tmp --port "$base_port" --rpc-port 0 --no-prometheus --no-hardware-benchmarks \
87+
--network-backend libp2p \
88+
-- --port "$((base_port + 1))" --rpc-port 0 --no-prometheus --network-backend libp2p \
89+
> "$log_file" 2>&1 &
90+
else
91+
timeout 65s ./target/release/polkadot \
92+
--chain=${{ matrix.network.spec_file }} --reserved-only --reserved-nodes "$bootnode" \
93+
--tmp --port "$base_port" --rpc-port 0 --no-prometheus --no-hardware-benchmarks \
94+
--network-backend libp2p \
95+
> "$log_file" 2>&1 &
96+
fi
97+
local pid=$!
98+
99+
local new_peer_id=""
100+
for _ in {1..60}; do
101+
sleep 1
102+
if [ -n "$peer_id" ] && grep -q "provided a different peer ID.*$peer_id" "$log_file" 2>/dev/null; then
103+
new_peer_id=$(grep "provided a different peer ID.*$peer_id" "$log_file" | grep -oE "provided a different peer ID \`[^\`]+\`" | grep -oE "12D3KooW[a-zA-Z0-9]+" | head -1)
104+
result="PEER_ID_MISMATCH"; break
105+
fi
106+
if [ "$SPEC_TYPE" = "parachain" ]; then
107+
grep -qE "\[🌗\].*\([1-9][0-9]* peers?\)" "$log_file" 2>/dev/null && { result="SUCCESS"; break; }
108+
else
109+
grep -qE "\([1-9][0-9]* peers?\)" "$log_file" 2>/dev/null && { result="SUCCESS"; break; }
110+
fi
111+
done
112+
113+
kill $pid 2>/dev/null; wait $pid 2>/dev/null
114+
if [ "$result" = "PEER_ID_MISMATCH" ] && [ -n "$new_peer_id" ]; then
115+
echo "${result}:${new_peer_id}:${bootnode}" > "test_results/result_${idx}.txt"
116+
else
117+
echo "${result}::${bootnode}" > "test_results/result_${idx}.txt"
118+
fi
119+
}
120+
121+
idx=0
122+
while IFS= read -r bootnode; do
123+
[ -z "$bootnode" ] && continue
124+
test_bootnode "$bootnode" "$idx" &
125+
idx=$((idx + 1))
126+
sleep 0.5
127+
done <<< "$BOOTNODES"
128+
wait
129+
130+
# Collect results
131+
SUCCESS=0 FAILED=0 TIMEOUTS="" MISMATCHES=""
132+
for f in test_results/result_*.txt; do
133+
[ -f "$f" ] || continue
134+
content=$(cat "$f")
135+
status="${content%%:*}"
136+
rest="${content#*:}"
137+
new_peer="${rest%%:*}"
138+
node="${rest#*:}"
139+
case "$status" in
140+
SUCCESS) echo "SUCCESS: $node"; SUCCESS=$((SUCCESS + 1)) ;;
141+
PEER_ID_MISMATCH)
142+
echo "PEER_ID_MISMATCH: $node (actual: $new_peer)"
143+
FAILED=$((FAILED + 1))
144+
MISMATCHES+="- \`$node\` → actual peer ID: \`$new_peer\`"$'\n'
145+
;;
146+
*) echo "TIMEOUT: $node"; FAILED=$((FAILED + 1)); TIMEOUTS+="- \`$node\`"$'\n' ;;
147+
esac
148+
done
149+
150+
echo -e "\nResults: $SUCCESS/$TOTAL successful"
151+
152+
# Save report
153+
{
154+
echo "network=${{ matrix.network.name }}"
155+
echo "total=$TOTAL"
156+
echo "success=$SUCCESS"
157+
echo "failed=$FAILED"
158+
echo "---TIMEOUT---"
159+
echo -n "$TIMEOUTS"
160+
echo "---PEER_ID_MISMATCH---"
161+
echo -n "$MISMATCHES"
162+
} > reports/${{ matrix.network.name }}.txt
163+
164+
- name: Upload results
165+
uses: actions/upload-artifact@v4
166+
with:
167+
name: bootnode-results-${{ matrix.network.name }}
168+
path: reports/
169+
retention-days: 1
170+
171+
report:
172+
needs: check-bootnodes
173+
runs-on: ubuntu-latest
174+
if: always()
175+
permissions:
176+
issues: write
177+
steps:
178+
- name: Download all results
179+
uses: actions/download-artifact@v4
180+
with:
181+
path: results
182+
pattern: bootnode-results-*
183+
merge-multiple: true
184+
185+
- name: Create issue if failures detected
186+
env:
187+
GH_TOKEN: ${{ github.token }}
188+
run: |
189+
TOTAL_FAILURES=0
190+
191+
# Build summary table
192+
TABLE="| Network | Status | Working |\n|---------|--------|---------|"
193+
DETAILS=""
194+
195+
for report in results/*.txt; do
196+
[ -f "$report" ] || continue
197+
network=$(grep "^network=" "$report" | cut -d= -f2)
198+
total=$(grep "^total=" "$report" | cut -d= -f2)
199+
success=$(grep "^success=" "$report" | cut -d= -f2)
200+
failed=$(grep "^failed=" "$report" | cut -d= -f2)
201+
TOTAL_FAILURES=$((TOTAL_FAILURES + failed))
202+
203+
if [ "$failed" -gt 0 ]; then
204+
TABLE+="\n| **$network** | :x: | $success/$total |"
205+
timeouts=$(sed -n '/---TIMEOUT---/,/---PEER_ID_MISMATCH---/p' "$report" | grep -v "^---" | grep -v "^$" || true)
206+
mismatches=$(sed -n '/---PEER_ID_MISMATCH---/,$ p' "$report" | grep -v "^---" | grep -v "^$" || true)
207+
208+
DETAILS+="\n### $network\n"
209+
[ -n "$mismatches" ] && DETAILS+="\n<details>\n<summary>:warning: Peer ID Mismatch</summary>\n\n$mismatches\n</details>\n"
210+
[ -n "$timeouts" ] && DETAILS+="\n<details>\n<summary>:hourglass: Connection Timeout</summary>\n\n$timeouts\n</details>\n"
211+
else
212+
TABLE+="\n| **$network** | :white_check_mark: | $success/$total |"
213+
fi
214+
done
215+
216+
[ "$TOTAL_FAILURES" -eq 0 ] && { echo "All bootnodes healthy!"; exit 0; }
217+
218+
BODY="The weekly bootnode validation check has detected connectivity issues.\n\n$(echo -e "$TABLE")\n$DETAILS\n---\n**Workflow Run:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
219+
220+
ISSUE_TITLE="Bootnode Validation Failures"
221+
EXISTING=$(gh issue list --repo ${{ github.repository }} --state open --search "in:title \"$ISSUE_TITLE\"" --json number --jq '.[0].number' 2>/dev/null || true)
222+
223+
if [ -n "$EXISTING" ]; then
224+
echo "Updating issue #$EXISTING"
225+
echo -e "$BODY" | gh issue edit "$EXISTING" --repo ${{ github.repository }} --body-file -
226+
else
227+
echo "Creating new issue"
228+
echo -e "$BODY" | gh issue create --repo ${{ github.repository }} --title "$ISSUE_TITLE" --body-file - --label "bootnodes"
229+
fi

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88

99
# Cancel any previous job still running this workflow for this branch
1010
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs#example-using-concurrency-to-cancel-any-in-progress-job-or-run
11-
concurrency:
11+
concurrency:
1212
group: ${{ github.workflow }}-${{ github.ref }}
1313
cancel-in-progress: true
1414

specs/README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This directory contains chain specs for well-known public networks.
44

55
## Context
66

7-
The Moonbase node is designed to support multiple networks including Moonbase Alpha, MoonRiver
7+
The Moonbase node is designed to support multiple networks including Moonbase Alpha, Moonriver
88
(Kusama) and Moonbeam (Polkadot). Some of these networks are already live and others are planned.
99

1010
In order to support multiple networks with the same binary, Moonbase relies on a chain specification
@@ -13,11 +13,9 @@ it is convenient to "bake" specs for popular networks into the node.
1313

1414
## Which specs will come pre-baked?
1515

16-
- Moonbase Stage V6 - internal
17-
- Moonbase Alpha V6 - live
18-
- MoonRock - Potential future deployment to Rococo
19-
- MoonRiver - Future Kusama Deployment
20-
- Moonbeam - Future Polkadot deployment
16+
- Moonbase Alpha - Dev Deployment
17+
- Moonriver - Kusama Deployment
18+
- Moonbeam - Polkadot deployment
2119

2220
## Relay chain specs
2321

specs/alphanet/westend-embedded-specs-v8.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@
44
"chainType": "Live",
55
"bootNodes": [
66
"/dns4/fro-moonbase-relay-boot-1.moonbase.ol-infra.network/tcp/30334/p2p/12D3KooWJHEah35MCoUWzVnDDK6sBYHahrcSGaKkpt6qeQoUYM9o",
7-
"/dns4/qcl-moonbase-relay-boot-2.moonbase.ol-infra.network/tcp/30334/p2p/12D3KooWGo3UbxeropDcVaWKYXmeGC5wzLG9ZPip7CyN997cFChk",
8-
"/dns/eu-02.unitedbloc.com/tcp/35060/p2p/12D3KooWG8KzsySP5pEoG5oh1MzjYJhq7QZRbNHKZiGWF7FNQHuk",
9-
"/dns/eu-03.unitedbloc.com/tcp/37060/p2p/12D3KooWGkQhFJZAVYhDi5bp3hUm3QouwGmYugvqNLuS77TKyNZd",
10-
"/dns/eu-01.unitedbloc.com/tcp/35060/p2p/12D3KooWAu26xXQy9Q8P9rXim3yAGkAZ38BS3xrF2J7uZUbz1A8n",
11-
"/dns/apac-01.unitedbloc.com/tcp/35060/p2p/12D3KooWH1zRsVBRtTNiEbKHSees6ESw7UPFJBws3StgXcqTUQDt",
12-
"/dns/sa-01.unitedbloc.com/tcp/35060/p2p/12D3KooWKbo2qnyNdea2uR55z7dfg1BVDJw15Xr9xkUHd1csQnG2",
13-
"/dns/na-01a.unitedbloc.com/tcp/30333/p2p/12D3KooWEe1hn1YMxqzhacCDYrQVrNHyP8MXZSSjHkTwnGmKXRhh"
7+
"/dns4/qcl-moonbase-relay-boot-2.moonbase.ol-infra.network/tcp/30334/p2p/12D3KooWGo3UbxeropDcVaWKYXmeGC5wzLG9ZPip7CyN997cFChk"
148
],
159
"telemetryEndpoints": null,
1610
"protocolId": "moonbase-alpha-relay",

0 commit comments

Comments
 (0)