Kitchensink Parachain #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Kitchensink Parachain | |
description: "Build and run kitchensink parachain" | |
env: | |
PARACHAIN_REPO: "https://github.com/polkadot-developers/polkadot-docs-tests/" | |
PARACHAIN_BRANCH: ${{ github.ref_name }} | |
NODE_TIMEOUT: "120s" | |
PARA_ID: "1000" | |
RELAY_CHAIN: "paseo" | |
on: | |
workflow_dispatch: | |
pull_request: | |
branches: [ dev ] | |
paths: | |
- 'versions.yml' | |
workflow_call: | |
jobs: | |
setup-dependencies: | |
name: Read Versions | |
runs-on: ubuntu-latest | |
outputs: | |
chain-spec-builder-version: ${{ steps.resolve.outputs.chain-spec-builder-version }} | |
omni-node-version: ${{ steps.resolve.outputs.omni-node-version }} | |
rust-version: ${{ steps.resolve.outputs.rust-version }} | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: Read versions.yml | |
id: resolve | |
run: | | |
# Install yq for YAML parsing | |
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
sudo chmod +x /usr/local/bin/yq | |
DEPS_FILE="versions.yml" | |
TUTORIAL="zero_to_hero" | |
# Validate that config file exists | |
if [ ! -f "$DEPS_FILE" ]; then | |
echo "β Dependencies file not found: $DEPS_FILE" | |
exit 1 | |
fi | |
# Function to get version (tutorial-specific first, then global) | |
get_version() { | |
local dep="$1" | |
local version | |
# Try tutorial-specific version first | |
version=$(yq eval ".${TUTORIAL}.${dep}" "$DEPS_FILE" 2>/dev/null) | |
if [ "$version" = "null" ] || [ -z "$version" ]; then | |
# Fallback to global version | |
version=$(yq eval ".versions.${dep}" "$DEPS_FILE" 2>/dev/null) | |
fi | |
echo "$version" | |
} | |
# Get versions from config file | |
CHAIN_SPEC_VERSION=$(get_version "chain_spec_builder") | |
OMNI_NODE_VERSION=$(get_version "polkadot_omni_node") | |
RUST_VERSION=$(get_version "rust") | |
# Validate all versions were resolved | |
if [ -z "$CHAIN_SPEC_VERSION" ] || [ "$CHAIN_SPEC_VERSION" = "null" ]; then | |
echo "β chain-spec-builder version not resolved" | |
exit 1 | |
fi | |
if [ -z "$OMNI_NODE_VERSION" ] || [ "$OMNI_NODE_VERSION" = "null" ]; then | |
echo "β omni-node version not resolved" | |
exit 1 | |
fi | |
if [ -z "$RUST_VERSION" ] || [ "$RUST_VERSION" = "null" ]; then | |
echo "β rust version not resolved" | |
exit 1 | |
fi | |
echo "chain-spec-builder-version=$CHAIN_SPEC_VERSION" >> $GITHUB_OUTPUT | |
echo "omni-node-version=$OMNI_NODE_VERSION" >> $GITHUB_OUTPUT | |
echo "rust-version=$RUST_VERSION" >> $GITHUB_OUTPUT | |
echo "$(date -Iseconds) π Resolved versions:" | |
echo " - chain-spec-builder: $CHAIN_SPEC_VERSION" | |
echo " - omni-node: $OMNI_NODE_VERSION" | |
echo " - rust: $RUST_VERSION" | |
setup-tools: | |
name: Setup Tools | |
runs-on: ubuntu-latest | |
needs: setup-dependencies | |
outputs: | |
chain-spec-builder-version: ${{ steps.chain-spec.outputs.version }} | |
omni-node-version: ${{ steps.omni-node.outputs.version }} | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Setup Rust | |
uses: ./.github/actions/setup-rust | |
with: | |
rust-version: ${{ needs.setup-dependencies.outputs.rust-version }} | |
- name: Setup chain-spec-builder | |
id: chain-spec | |
uses: ./.github/actions/setup-chain-spec-builder | |
with: | |
chain-spec-builder-version: ${{ needs.setup-dependencies.outputs.chain-spec-builder-version }} | |
- name: Setup omni-node | |
id: omni-node | |
uses: ./.github/actions/setup-omni-node | |
with: | |
omni-node-version: ${{ needs.setup-dependencies.outputs.omni-node-version }} | |
- name: Verify tools are available | |
run: | | |
echo "π Verifying installed tools..." | |
echo "${HOME}/.cargo/bin" >> $GITHUB_PATH | |
# Verify tools work | |
chain-spec-builder --version | |
polkadot-omni-node --version | |
echo "β All tools verified and ready" | |
build-parachain: | |
name: Build Parachain | |
runs-on: ubuntu-latest | |
needs: setup-dependencies | |
outputs: | |
parachain-path: ${{ steps.setup-parachain.outputs.parachain-path }} | |
runtime-path: ${{ steps.setup-parachain.outputs.runtime-path }} | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Setup Rust | |
uses: ./.github/actions/setup-rust | |
with: | |
rust-version: ${{ needs.setup-dependencies.outputs.rust-version }} | |
- name: Setup Rust cache | |
uses: Swatinem/rust-cache@v2 | |
with: | |
key: parachain-${{ env.PARACHAIN_BRANCH }}-${{ needs.setup-dependencies.outputs.rust-version }} | |
- name: Cache runtime WASM | |
id: cache-runtime | |
uses: actions/cache@v4 | |
with: | |
path: | | |
target/release/wbuild/parachain-template-runtime/*.wasm | |
key: runtime-wasm-${{ env.PARACHAIN_BRANCH }}-${{ hashFiles('**/Cargo.lock') }} | |
- name: Setup kitchensink parachain | |
id: setup-parachain | |
if: steps.cache-runtime.outputs.cache-hit != 'true' | |
uses: ./.github/actions/setup-kitchensink-parachain | |
with: | |
branch: ${{ env.PARACHAIN_BRANCH }} | |
- name: Verify runtime artifacts | |
run: | | |
echo "$(date -Iseconds) π Verifying runtime artifacts..." | |
# Find the runtime WASM file | |
RUNTIME_WASM=$(find . -name "*.compressed.wasm" -type f 2>/dev/null | head -1) | |
if [ -z "$RUNTIME_WASM" ]; then | |
echo "β Runtime WASM file not found" | |
echo "Available files:" | |
find . -name "*.wasm" -type f 2>/dev/null || echo "No WASM files found" | |
exit 1 | |
fi | |
echo "β Runtime WASM found: $RUNTIME_WASM" | |
echo "π File size: $(du -h "$RUNTIME_WASM" | cut -f1)" | |
- name: Upload parachain artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: parachain-artifacts-${{ github.run_id }} | |
path: | | |
${{ steps.setup-parachain.outputs.runtime-path }} | |
retention-days: 1 | |
run-parachain: | |
name: Run Parachain Node | |
runs-on: ubuntu-latest | |
needs: [setup-dependencies, setup-tools, build-parachain] | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Restore chain-spec-builder cache | |
id: cache-chain-spec | |
uses: actions/cache/restore@v4 | |
with: | |
path: ~/.cargo/bin | |
key: ${{ runner.os }}-${{ runner.arch }}-chain-spec-builder-${{ needs.setup-dependencies.outputs.chain-spec-builder-version }} | |
fail-on-cache-miss: false | |
- name: Restore omni-node cache | |
id: cache-omni-node | |
uses: actions/cache/restore@v4 | |
with: | |
path: ~/.cargo/bin | |
key: ${{ runner.os }}-${{ runner.arch }}-polkadot-omni-node-${{ needs.setup-dependencies.outputs.omni-node-version }} | |
fail-on-cache-miss: false | |
- name: Download parachain artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
name: parachain-artifacts-${{ github.run_id }} | |
path: ./artifacts | |
- name: Setup paths and permissions | |
run: | | |
echo "$(date -Iseconds) π§ Setting up paths and permissions..." | |
# Add tools to PATH | |
echo "${HOME}/.cargo/bin" >> $GITHUB_PATH | |
# Make binaries executable | |
find ./artifacts -name "*parachain*" -type f -exec chmod +x {} \; | |
# Verify tools are available with retry logic | |
for i in {1..3}; do | |
echo "$(date -Iseconds) π Verifying tools (attempt $i/3)..." | |
if chain-spec-builder --version && polkadot-omni-node --version; then | |
echo "β Tools verified successfully" | |
break | |
fi | |
if [ $i -eq 3 ]; then | |
echo "β Tools verification failed after 3 attempts" | |
echo "PATH: $PATH" | |
ls -la ~/.cargo/bin/ | |
exit 1 | |
fi | |
sleep 5 | |
done | |
# Find the runtime WASM file | |
RUNTIME_WASM=$(find ./artifacts -name "*.compressed.wasm" -type f | head -1) | |
if [ -z "$RUNTIME_WASM" ]; then | |
echo "β Runtime WASM file not found" | |
echo "Available files:" | |
find ./artifacts -type f | |
exit 1 | |
fi | |
mkdir -p ./target/release/wbuild/parachain-template-runtime/ | |
cp $RUNTIME_WASM ./target/release/wbuild/parachain-template-runtime/ | |
echo "β Setup completed successfully" | |
- name: Generate chain specification | |
run: | | |
echo "$(date -Iseconds) π§ Generating chain specification..." | |
echo "π Using repository: ${{ env.PARACHAIN_REPO }}" | |
echo "π Using branch: ${{ env.PARACHAIN_BRANCH }}" | |
echo "π Para ID: ${{ env.PARA_ID }}" | |
echo "π Relay Chain: ${{ env.RELAY_CHAIN }}" | |
# Generate chain spec | |
chain-spec-builder create \ | |
-t development \ | |
--relay-chain ${{ env.RELAY_CHAIN }} \ | |
--para-id ${{ env.PARA_ID }} \ | |
--runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm \ | |
named-preset development | |
# Verify chain spec was created | |
if [ ! -f "chain_spec.json" ]; then | |
echo "β Chain spec generation failed" | |
echo "Available files:" | |
ls -la | |
exit 1 | |
fi | |
echo "$(date -Iseconds) β Chain specification generated successfully" | |
echo "π Chain spec size: $(du -h chain_spec.json | cut -f1)" | |
- name: Validate chain specification | |
run: | | |
echo "$(date -Iseconds) π Validating chain specification..." | |
# Check if it's valid JSON | |
if ! jq empty chain_spec.json 2>/dev/null; then | |
echo "β Invalid JSON in chain specification" | |
echo "First 100 lines of chain_spec.json:" | |
head -100 chain_spec.json | |
exit 1 | |
fi | |
# Extract key information | |
CHAIN_NAME=$(jq -r '.name // "unknown"' chain_spec.json) | |
PARA_ID=$(jq -r '.para_id // "unknown"' chain_spec.json) | |
RELAY_CHAIN=$(jq -r '.relay_chain // "unknown"' chain_spec.json) | |
echo "$(date -Iseconds) β Chain specification is valid" | |
echo "π Chain Name: $CHAIN_NAME" | |
echo "π Para ID: $PARA_ID" | |
echo "π Relay Chain: $RELAY_CHAIN" | |
echo "π¦ Parachain Source: ${{ env.PARACHAIN_REPO }}@${{ env.PARACHAIN_BRANCH }}" | |
- name: Start Polkadot omni-node | |
run: | | |
echo "$(date -Iseconds) π Starting Polkadot omni-node..." | |
echo "π¦ Built from: ${{ env.PARACHAIN_REPO }}@${{ env.PARACHAIN_BRANCH }}" | |
# Create log file with timestamp | |
LOG_FILE="node-$(date +%Y%m%d-%H%M%S).log" | |
echo "LOG_FILE=$LOG_FILE" >> $GITHUB_ENV | |
# Run for specified timeout | |
echo "β±οΈ Running for ${{ env.NODE_TIMEOUT }}" | |
TIMEOUT_CMD="timeout ${{ env.NODE_TIMEOUT }}" | |
# Start node in background with structured logging | |
$TIMEOUT_CMD \ | |
polkadot-omni-node \ | |
--chain ./chain_spec.json \ | |
--dev \ | |
--rpc-cors all \ | |
--rpc-methods unsafe \ | |
> $LOG_FILE 2>&1 & | |
NODE_PID=$! | |
echo "NODE_PID=$NODE_PID" >> $GITHUB_ENV | |
echo "$(date -Iseconds) β Node started with PID: $NODE_PID" | |
# Wait a moment for startup | |
sleep 15 | |
# Check if node is still running with retry | |
for i in {1..5}; do | |
if kill -0 $NODE_PID 2>/dev/null; then | |
echo "$(date -Iseconds) β Node startup check $i/5 - Node is running" | |
break | |
fi | |
if [ $i -eq 5 ]; then | |
echo "β Node failed to start" | |
echo "π Last 50 lines of log:" | |
tail -50 $LOG_FILE | |
exit 1 | |
fi | |
sleep 3 | |
done | |
echo "$(date -Iseconds) β Node is running successfully" | |
- name: Health check and monitoring | |
run: | | |
echo "$(date -Iseconds) π₯ Performing health checks..." | |
# Wait for RPC to be ready | |
echo "$(date -Iseconds) π Waiting for RPC endpoint..." | |
for i in {1..30}; do | |
if curl -s -X POST -H "Content-Type: application/json" \ | |
-d '{"jsonrpc":"2.0","method":"system_health","params":[],"id":1}' \ | |
http://localhost:9944 >/dev/null 2>&1; then | |
echo "$(date -Iseconds) β RPC endpoint is healthy" | |
# Get system info | |
HEALTH=$(curl -s -X POST -H "Content-Type: application/json" \ | |
-d '{"jsonrpc":"2.0","method":"system_health","params":[],"id":1}' \ | |
http://localhost:9944 | jq -r '.result') | |
CHAIN=$(curl -s -X POST -H "Content-Type: application/json" \ | |
-d '{"jsonrpc":"2.0","method":"system_chain","params":[],"id":1}' \ | |
http://localhost:9944 | jq -r '.result') | |
echo "π Chain: $CHAIN" | |
echo "π₯ Health: $HEALTH" | |
break | |
fi | |
if [ $i -eq 30 ]; then | |
echo "β RPC endpoint not responding after 60 seconds" | |
echo "π Last 100 lines of log:" | |
tail -100 $LOG_FILE | |
exit 1 | |
fi | |
sleep 2 | |
done | |
# Monitor node for a short period | |
for i in {1..12}; do | |
if ! kill -0 $NODE_PID 2>/dev/null; then | |
echo "β Node stopped unexpectedly" | |
echo "π Final log output:" | |
tail -100 $LOG_FILE | |
exit 1 | |
fi | |
# Periodic RPC health check | |
if ! curl -s -X POST -H "Content-Type: application/json" \ | |
-d '{"jsonrpc":"2.0","method":"system_health","params":[],"id":1}' \ | |
http://localhost:9944 >/dev/null 2>&1; then | |
echo "β οΈ RPC endpoint not responding at check $i/12" | |
fi | |
echo "$(date -Iseconds) β Health check $i/12 - Node is running" | |
sleep 5 | |
done | |
echo "$(date -Iseconds) β All health checks passed" | |
- name: Collect metrics | |
if: always() | |
run: | | |
echo "$(date -Iseconds) π Collecting basic metrics..." | |
if [ -n "${NODE_PID:-}" ] && kill -0 $NODE_PID 2>/dev/null; then | |
# Get basic system info | |
echo "πΎ Memory usage:" | |
ps -o pid,ppid,user,cpu,%mem,etime,cmd -p $NODE_PID || true | |
echo "π Network connections:" | |
ss -tulpn | grep :9944 || echo "No connections on port 9944" | |
# Try to get node info via RPC | |
echo "π Node information:" | |
curl -s -X POST -H "Content-Type: application/json" \ | |
-d '{"jsonrpc":"2.0","method":"system_version","params":[],"id":1}' \ | |
http://localhost:9944 | jq -r '.result' 2>/dev/null || echo "RPC not available" | |
fi | |
- name: Cleanup | |
if: always() | |
run: | | |
echo "$(date -Iseconds) π§Ή Cleaning up..." | |
# Stop the node gracefully | |
if [ -n "${NODE_PID:-}" ] && kill -0 $NODE_PID 2>/dev/null; then | |
echo "$(date -Iseconds) π Stopping node (PID: $NODE_PID)..." | |
kill -TERM $NODE_PID | |
# Wait for graceful shutdown | |
for i in {1..10}; do | |
if ! kill -0 $NODE_PID 2>/dev/null; then | |
echo "$(date -Iseconds) β Node stopped gracefully" | |
break | |
fi | |
sleep 1 | |
done | |
# Force kill if still running | |
if kill -0 $NODE_PID 2>/dev/null; then | |
echo "$(date -Iseconds) β‘ Force stopping node..." | |
kill -KILL $NODE_PID | |
fi | |
fi | |
# Log final statistics | |
if [ -f "${LOG_FILE:-node.log}" ]; then | |
echo "π Final log statistics:" | |
wc -l ${LOG_FILE:-node.log} || true | |
fi | |
echo "$(date -Iseconds) β Cleanup completed" | |
- name: Upload logs and artifacts | |
if: always() | |
uses: actions/upload-artifact@v4 | |
with: | |
name: node-logs-${{ github.run_id }} | |
path: | | |
node*.log | |
chain_spec.json | |
retention-days: 7 |