Skip to content

Kitchensink Parachain #2

Kitchensink Parachain

Kitchensink Parachain #2

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