Sync Documentation from Node Repository #41
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: Sync Documentation from Node Repository | |
| on: | |
| repository_dispatch: | |
| types: [sync-docs] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version/tag to sync from genlayer-node repo (e.g., v0.3.5, or "latest" to detect)' | |
| required: false | |
| default: 'latest' | |
| api_gen_path: | |
| description: 'Path to API gen files in source repo' | |
| required: false | |
| default: 'docs/api/rpc' | |
| api_debug_path: | |
| description: 'Path to API debug files in source repo' | |
| required: false | |
| default: 'docs/api/rpc' | |
| api_gen_regex: | |
| description: 'Regex pattern to filter API gen files (e.g., "gen_.*")' | |
| required: false | |
| default: 'gen_(?!dbg_).*' | |
| api_debug_regex: | |
| description: 'Regex pattern to filter API debug files (e.g., "gen_dbg_.*")' | |
| required: false | |
| default: 'gen_dbg_.*' | |
| jobs: | |
| sync-and-create-pr: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout documentation repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Setup Python dependencies | |
| run: | | |
| python3 -m pip install --upgrade pip | |
| python3 -m pip install pyyaml | |
| - name: Set up Git | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Extract sync parameters | |
| id: params | |
| run: | | |
| set -euo pipefail | |
| if [ "${{ github.event_name }}" = "repository_dispatch" ]; then | |
| # Default to "latest" if version not provided | |
| VERSION="${{ github.event.client_payload.version }}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION="latest" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "changelog_path=${{ github.event.client_payload.changelog_path || 'docs/changelog' }}" >> $GITHUB_OUTPUT | |
| echo "api_gen_path=${{ github.event.client_payload.api_gen_path || 'docs/api/rpc' }}" >> $GITHUB_OUTPUT | |
| echo "api_debug_path=${{ github.event.client_payload.api_debug_path || 'docs/api/rpc' }}" >> $GITHUB_OUTPUT | |
| echo "api_gen_regex=${{ github.event.client_payload.api_gen_regex || 'gen_(?!dbg_).*' }}" >> $GITHUB_OUTPUT | |
| echo "api_debug_regex=${{ github.event.client_payload.api_debug_regex || 'gen_dbg_.*' }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT | |
| echo "changelog_path=docs/changelog" >> $GITHUB_OUTPUT | |
| echo "api_gen_path=${{ github.event.inputs.api_gen_path || 'docs/api/rpc' }}" >> $GITHUB_OUTPUT | |
| echo "api_debug_path=${{ github.event.inputs.api_debug_path || 'docs/api/rpc' }}" >> $GITHUB_OUTPUT | |
| echo "api_gen_regex=${{ github.event.inputs.api_gen_regex || 'gen_(?!dbg_).*' }}" >> $GITHUB_OUTPUT | |
| echo "api_debug_regex=${{ github.event.inputs.api_debug_regex || 'gen_dbg_.*' }}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Clone genlayer-node repository | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: genlayerlabs/genlayer-node | |
| token: ${{ secrets.NODE_REPO_TOKEN || secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 # Fetch all history for tags | |
| sparse-checkout: | | |
| docs | |
| configs/node/config.yaml.example | |
| sparse-checkout-cone-mode: true | |
| path: source-repo | |
| - name: Detect latest version (if needed) | |
| id: detect_version | |
| if: steps.params.outputs.version == 'latest' || steps.params.outputs.version == '' | |
| run: | | |
| cd source-repo | |
| # Get the latest tag that's not a pre-release | |
| LATEST_TAG=$(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1) | |
| if [[ -z "$LATEST_TAG" ]]; then | |
| echo "No tags found in repository" | |
| exit 1 | |
| fi | |
| echo "Detected latest tag: $LATEST_TAG" | |
| echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT | |
| - name: Set final version | |
| id: set_version | |
| run: | | |
| if [[ "${{ steps.params.outputs.version }}" == "latest" || -z "${{ steps.params.outputs.version }}" ]]; then | |
| VERSION="${{ steps.detect_version.outputs.version }}" | |
| else | |
| VERSION="${{ steps.params.outputs.version }}" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Using version: $VERSION" | |
| - name: Checkout version in source repo | |
| run: | | |
| cd source-repo | |
| git checkout ${{ steps.set_version.outputs.version }} | |
| # Debug: Check what files we have after checkout | |
| echo "::group::Debug: Files after version checkout" | |
| echo "Current directory: $(pwd)" | |
| echo "All directories in source-repo:" | |
| find . -type d -name "config*" | head -20 | |
| echo "All yaml files:" | |
| find . -name "*.yaml*" -type f | head -20 | |
| echo "Checking specific paths:" | |
| ls -la configs/ 2>/dev/null || echo "No configs directory" | |
| ls -la config/ 2>/dev/null || echo "No config directory" | |
| echo "::endgroup::" | |
| - name: Create branch for changes | |
| run: | | |
| set -euo pipefail | |
| # Sanitize version string for use in branch name | |
| VERSION="${{ steps.set_version.outputs.version }}" | |
| SAFE_VERSION=$(echo "$VERSION" | sed 's/\//-/g') # replace any '/' with '-' | |
| BRANCH_NAME="docs/node/${SAFE_VERSION}" | |
| # Check if branch exists on remote | |
| if git ls-remote --exit-code --heads origin "$BRANCH_NAME" >/dev/null 2>&1; then | |
| echo "Branch $BRANCH_NAME already exists on remote, will force update" | |
| git fetch origin "$BRANCH_NAME" | |
| fi | |
| # Create/recreate branch from current HEAD (main) | |
| git switch --force-create "$BRANCH_NAME" | |
| echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV | |
| - name: Sync changelog files | |
| id: sync_changelog | |
| run: | | |
| set -euo pipefail | |
| SYNC_REPORT="${{ runner.temp }}/sync_report.md" | |
| SOURCE_CHANGELOG="source-repo/${{ steps.params.outputs.changelog_path }}" | |
| DEST_CHANGELOG="content/validators/changelog" | |
| echo "## Changelog Sync" >> $SYNC_REPORT | |
| echo "" >> $SYNC_REPORT | |
| if [ -d "$SOURCE_CHANGELOG" ]; then | |
| mkdir -p "$DEST_CHANGELOG" | |
| # Track existing files before sync | |
| declare -A EXISTING_FILES | |
| while IFS= read -r file; do | |
| [ -n "$file" ] && EXISTING_FILES["$(basename "$file")"]="$file" | |
| done < <(find "$DEST_CHANGELOG" -name "*.mdx" -type f) | |
| # Track what we'll be syncing | |
| ADDED=0 | |
| UPDATED=0 | |
| # Process all source files | |
| for file in "$SOURCE_CHANGELOG"/*.mdx "$SOURCE_CHANGELOG"/*.md; do | |
| if [ -f "$file" ]; then | |
| basename_no_ext=$(basename "$file" | sed 's/\.[^.]*$//') | |
| dest_filename="${basename_no_ext}.mdx" | |
| dest_path="$DEST_CHANGELOG/$dest_filename" | |
| if [ -f "$dest_path" ]; then | |
| # File exists - check if it's different | |
| if ! cmp -s "$file" "$dest_path"; then | |
| cp "$file" "$dest_path" | |
| echo "- Updated: \`$dest_filename\`" >> $SYNC_REPORT | |
| UPDATED=$((UPDATED + 1)) | |
| fi | |
| # Remove from tracking to identify deletions later | |
| unset EXISTING_FILES["$dest_filename"] | |
| else | |
| # New file | |
| cp "$file" "$dest_path" | |
| echo "- Added: \`$dest_filename\`" >> $SYNC_REPORT | |
| ADDED=$((ADDED + 1)) | |
| fi | |
| fi | |
| done | |
| # Remove files that no longer exist in source | |
| DELETED=0 | |
| for dest_file in "${EXISTING_FILES[@]}"; do | |
| if [ -f "$dest_file" ]; then | |
| rm "$dest_file" | |
| printf -- "- Deleted: \`%s\`\n" "$(basename "$dest_file")" >> $SYNC_REPORT | |
| DELETED=$((DELETED + 1)) | |
| fi | |
| done | |
| # Summary | |
| TOTAL=$((ADDED + UPDATED + DELETED)) | |
| if [ $TOTAL -eq 0 ]; then | |
| echo "- No changelog updates found" >> $SYNC_REPORT | |
| else | |
| echo "" >> $SYNC_REPORT | |
| echo "Summary: $ADDED added, $UPDATED updated, $DELETED deleted" >> $SYNC_REPORT | |
| fi | |
| # Output all metrics | |
| echo "changelog_added=$ADDED" >> $GITHUB_OUTPUT | |
| echo "changelog_updated=$UPDATED" >> $GITHUB_OUTPUT | |
| echo "changelog_deleted=$DELETED" >> $GITHUB_OUTPUT | |
| echo "changelog_total=$TOTAL" >> $GITHUB_OUTPUT | |
| else | |
| echo "- Source changelog directory not found: \`${{ steps.params.outputs.changelog_path }}\`" >> $SYNC_REPORT | |
| echo "changelog_added=0" >> $GITHUB_OUTPUT | |
| echo "changelog_updated=0" >> $GITHUB_OUTPUT | |
| echo "changelog_deleted=0" >> $GITHUB_OUTPUT | |
| echo "changelog_total=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Sync config.yaml file | |
| id: sync_config | |
| run: | | |
| set -euo pipefail | |
| SYNC_REPORT="${{ runner.temp }}/sync_report.md" | |
| SOURCE_CONFIG="source-repo/configs/node/config.yaml.example" | |
| DEST_CONFIG="content/validators/config.yaml" | |
| echo "" >> $SYNC_REPORT | |
| echo "## Config File Sync" >> $SYNC_REPORT | |
| echo "" >> $SYNC_REPORT | |
| # Debug: Check what files exist in source-repo/configs | |
| echo "::group::Debug: Checking source-repo/configs directory" | |
| echo "Current directory: $(pwd)" | |
| echo "Source repo structure:" | |
| ls -la source-repo/ || echo "source-repo not found" | |
| echo "Configs directory:" | |
| ls -la source-repo/configs/ 2>/dev/null || echo "configs directory not found" | |
| echo "Node directory:" | |
| ls -la source-repo/configs/node/ 2>/dev/null || echo "node directory not found" | |
| echo "All files in configs (recursive):" | |
| find source-repo/configs -type f 2>/dev/null || echo "No files found in configs" | |
| echo "YAML files in configs:" | |
| find source-repo/configs -type f -name "*.yaml*" 2>/dev/null || echo "No yaml files found" | |
| echo "::endgroup::" | |
| # Check if the source config file exists | |
| if [ -f "$SOURCE_CONFIG" ]; then | |
| echo "Found config file at: $SOURCE_CONFIG" | |
| mkdir -p "$(dirname "$DEST_CONFIG")" | |
| # Debug: Print original config | |
| echo "::group::Original config.yaml content" | |
| echo "Source: $SOURCE_CONFIG" | |
| cat "$SOURCE_CONFIG" || echo "Failed to read source config" | |
| echo "::endgroup::" | |
| # Create a temporary file for sanitized config | |
| TEMP_CONFIG="${{ runner.temp }}/config_sanitized.yaml" | |
| # Copy and sanitize the config | |
| cp "$SOURCE_CONFIG" "$TEMP_CONFIG" | |
| if [ ! -f "$TEMP_CONFIG" ]; then | |
| echo "ERROR: Failed to copy config to temp location" | |
| exit 1 | |
| fi | |
| # Debug: Show config before sed replacements | |
| echo "::group::Config before sed replacements" | |
| grep -E "zksync.*url:" "$TEMP_CONFIG" || echo "No zksync URLs found" | |
| echo "::endgroup::" | |
| # Replace actual URLs with TODO placeholders | |
| # Use sed with backup for compatibility (works on both Linux and macOS) | |
| sed -i.bak 's|zksyncurl: *"[^"]*"|zksyncurl: "TODO: Set your GenLayer Chain ZKSync HTTP RPC URL here"|' "$TEMP_CONFIG" | |
| sed -i.bak 's|zksyncwebsocketurl: *"[^"]*"|zksyncwebsocketurl: "TODO: Set your GenLayer Chain ZKSync WebSocket RPC URL here"|' "$TEMP_CONFIG" | |
| # Remove backup files | |
| rm -f "${TEMP_CONFIG}.bak" | |
| # Debug: Show config after sed replacements | |
| echo "::group::Config after sed replacements" | |
| grep -E "zksync.*url:" "$TEMP_CONFIG" || echo "No zksync URLs found after sed" | |
| echo "::endgroup::" | |
| # Remove node.dev and node.admin sections using Python for reliable YAML parsing | |
| echo "::group::Debug: Running Python sanitization" | |
| echo "Script path: .github/scripts/sanitize-config.py" | |
| echo "Config path: $TEMP_CONFIG" | |
| # Check Python and PyYAML | |
| echo "Python version:" | |
| python3 --version | |
| echo "Checking PyYAML:" | |
| python3 -c "import yaml; print('PyYAML version:', yaml.__version__)" || echo "PyYAML not installed" | |
| if [ -f ".github/scripts/sanitize-config.py" ]; then | |
| echo "Sanitization script exists" | |
| python3 .github/scripts/sanitize-config.py "$TEMP_CONFIG" | |
| SANITIZE_EXIT_CODE=$? | |
| echo "Sanitization exit code: $SANITIZE_EXIT_CODE" | |
| if [ $SANITIZE_EXIT_CODE -ne 0 ]; then | |
| echo "ERROR: Sanitization failed!" | |
| echo "Config content before sanitization:" | |
| cat "$TEMP_CONFIG" | head -20 | |
| fi | |
| else | |
| echo "ERROR: Sanitization script not found!" | |
| ls -la .github/scripts/ || echo "Scripts directory not found" | |
| fi | |
| echo "::endgroup::" | |
| # Debug: Print sanitized config | |
| echo "::group::Sanitized config.yaml content" | |
| echo "After sanitization: $TEMP_CONFIG" | |
| if [ -f "$TEMP_CONFIG" ]; then | |
| echo "File size: $(wc -c < "$TEMP_CONFIG") bytes" | |
| echo "Complete sanitized config content:" | |
| echo "=================================" | |
| cat "$TEMP_CONFIG" | |
| echo "=================================" | |
| echo "" | |
| echo "Checking for removed sections:" | |
| grep -E "^\s*(dev|admin):" "$TEMP_CONFIG" && echo "WARNING: dev/admin sections still present!" || echo "Good: No dev/admin sections found" | |
| # Verify the sanitized file has the expected structure | |
| echo "Verifying config structure:" | |
| if grep -q "^node:" "$TEMP_CONFIG"; then | |
| echo "✓ Found 'node:' section" | |
| else | |
| echo "✗ Missing 'node:' section" | |
| fi | |
| if grep -q "^consensus:" "$TEMP_CONFIG"; then | |
| echo "✓ Found 'consensus:' section" | |
| else | |
| echo "✗ Missing 'consensus:' section" | |
| fi | |
| if grep -q "^genvm:" "$TEMP_CONFIG"; then | |
| echo "✓ Found 'genvm:' section" | |
| else | |
| echo "✗ Missing 'genvm:' section" | |
| fi | |
| if grep -q "^metrics:" "$TEMP_CONFIG"; then | |
| echo "✓ Found 'metrics:' section" | |
| else | |
| echo "✗ Missing 'metrics:' section" | |
| fi | |
| else | |
| echo "ERROR: Sanitized config file not found!" | |
| fi | |
| echo "::endgroup::" | |
| # Debug: Check destination | |
| echo "::group::Debug: Destination config check" | |
| echo "Destination path: $DEST_CONFIG" | |
| if [ -f "$DEST_CONFIG" ]; then | |
| echo "Destination config exists" | |
| echo "Current destination content:" | |
| cat "$DEST_CONFIG" | head -20 | |
| else | |
| echo "Destination config does not exist" | |
| fi | |
| echo "::endgroup::" | |
| # Check if the config has changed | |
| if [ -f "$DEST_CONFIG" ]; then | |
| if ! cmp -s "$TEMP_CONFIG" "$DEST_CONFIG"; then | |
| # Force copy to ensure complete replacement | |
| cp -f "$TEMP_CONFIG" "$DEST_CONFIG" | |
| echo "- Updated: \`config.yaml\` (sanitized)" >> $SYNC_REPORT | |
| echo "config_updated=1" >> $GITHUB_OUTPUT | |
| echo "Config file was updated" | |
| # Debug: Show what changed | |
| echo "::group::Config differences" | |
| echo "File sizes:" | |
| echo " Source (sanitized): $(wc -c < "$TEMP_CONFIG") bytes" | |
| echo " Destination (after copy): $(wc -c < "$DEST_CONFIG") bytes" | |
| echo "First 10 lines of updated config:" | |
| head -10 "$DEST_CONFIG" | |
| echo "::endgroup::" | |
| else | |
| echo "- No changes to \`config.yaml\`" >> $SYNC_REPORT | |
| echo "config_updated=0" >> $GITHUB_OUTPUT | |
| echo "Config file unchanged" | |
| fi | |
| else | |
| # Config doesn't exist, create it | |
| cp -f "$TEMP_CONFIG" "$DEST_CONFIG" | |
| echo "- Added: \`config.yaml\` (sanitized)" >> $SYNC_REPORT | |
| echo "config_updated=1" >> $GITHUB_OUTPUT | |
| echo "Config file was created" | |
| fi | |
| # Debug: Verify copy worked | |
| echo "::group::Debug: Verify config copy" | |
| if [ -f "$DEST_CONFIG" ]; then | |
| echo "Destination config after operation:" | |
| echo "File size: $(wc -c < "$DEST_CONFIG") bytes" | |
| echo "First 30 lines:" | |
| head -30 "$DEST_CONFIG" | |
| echo "---" | |
| echo "Checking final content:" | |
| echo "Has node section: $(grep -q '^node:' "$DEST_CONFIG" && echo "Yes" || echo "No")" | |
| echo "Has consensus section: $(grep -q '^consensus:' "$DEST_CONFIG" && echo "Yes" || echo "No")" | |
| echo "Has dev section: $(grep -q '^\s*dev:' "$DEST_CONFIG" && echo "Yes - ERROR!" || echo "No - Good")" | |
| echo "Has admin section: $(grep -q '^\s*admin:' "$DEST_CONFIG" && echo "Yes - ERROR!" || echo "No - Good")" | |
| else | |
| echo "ERROR: Destination config still doesn't exist!" | |
| fi | |
| echo "::endgroup::" | |
| # Clean up temp file | |
| rm -f "$TEMP_CONFIG" | |
| else | |
| # Show what was searched | |
| echo "::group::Config file not found" | |
| echo "Expected config file at: $SOURCE_CONFIG" | |
| echo "::endgroup::" | |
| printf -- "- Source config file not found at: \`%s\`\n" "${SOURCE_CONFIG#source-repo/}" >> $SYNC_REPORT | |
| echo "config_updated=0" >> $GITHUB_OUTPUT | |
| # Try to create a minimal config if none exists | |
| echo "::group::Creating minimal config" | |
| echo "No config file found in source repository." | |
| echo "This might be expected for this version." | |
| echo "::endgroup::" | |
| fi | |
| - name: Sync API gen method files | |
| id: sync_api_gen | |
| run: | | |
| set -euo pipefail | |
| SYNC_REPORT="${{ runner.temp }}/sync_report.md" | |
| SOURCE_API_GEN="source-repo/${{ steps.params.outputs.api_gen_path }}" | |
| DEST_API_GEN="pages/api-references/genlayer-node/gen" | |
| API_GEN_REGEX="${{ steps.params.outputs.api_gen_regex }}" | |
| echo "" >> $SYNC_REPORT | |
| echo "## API Gen Methods Sync" >> $SYNC_REPORT | |
| printf "Using regex filter: \`%s\`\n" "$API_GEN_REGEX" >> $SYNC_REPORT | |
| echo "" >> $SYNC_REPORT | |
| # Function to check if filename matches the regex pattern | |
| # Uses perl if available for PCRE support, otherwise falls back to grep -E | |
| matches_pattern() { | |
| local filename="$1" | |
| local pattern="$2" | |
| # Try perl first (supports PCRE including negative lookahead) | |
| if command -v perl >/dev/null 2>&1; then | |
| echo "$filename" | perl -ne "exit 0 if /^($pattern)\$/; exit 1" | |
| return $? | |
| fi | |
| # Fallback to grep -E (doesn't support negative lookahead) | |
| echo "$filename" | grep -E "^($pattern)$" >/dev/null 2>&1 | |
| return $? | |
| } | |
| if [ -d "$SOURCE_API_GEN" ]; then | |
| mkdir -p "$DEST_API_GEN" | |
| # Track existing files before sync | |
| declare -A EXISTING_FILES | |
| while IFS= read -r file; do | |
| [ -n "$file" ] && EXISTING_FILES["$(basename "$file")"]="$file" | |
| done < <(find "$DEST_API_GEN" -name "*.mdx" -type f) | |
| # Track what we'll be syncing | |
| ADDED=0 | |
| UPDATED=0 | |
| # Process all source files that match the regex | |
| for file in "$SOURCE_API_GEN"/*.mdx "$SOURCE_API_GEN"/*.md; do | |
| if [ -f "$file" ]; then | |
| basename_no_ext=$(basename "$file" | sed 's/\.[^.]*$//') | |
| # Check if filename (without extension) matches the regex filter | |
| if matches_pattern "$basename_no_ext" "$API_GEN_REGEX"; then | |
| dest_filename="${basename_no_ext}.mdx" | |
| dest_path="$DEST_API_GEN/$dest_filename" | |
| if [ -f "$dest_path" ]; then | |
| # File exists - check if it's different | |
| if ! cmp -s "$file" "$dest_path"; then | |
| cp "$file" "$dest_path" | |
| echo "- Updated: \`$dest_filename\`" >> $SYNC_REPORT | |
| UPDATED=$((UPDATED + 1)) | |
| fi | |
| # Remove from tracking to identify deletions later | |
| unset EXISTING_FILES["$dest_filename"] | |
| else | |
| # New file | |
| cp "$file" "$dest_path" | |
| echo "- Added: \`$dest_filename\`" >> $SYNC_REPORT | |
| ADDED=$((ADDED + 1)) | |
| fi | |
| fi | |
| fi | |
| done | |
| # Skip _meta.json handling - it should not be touched | |
| # Remove _meta.json from tracking to prevent deletion | |
| unset EXISTING_FILES["_meta.json"] | |
| # Remove files that no longer exist in source or don't match the filter | |
| DELETED=${DELETED:-0} | |
| for dest_file in "${EXISTING_FILES[@]}"; do | |
| if [ -f "$dest_file" ]; then | |
| dest_basename_no_ext=$(basename "$dest_file" | sed 's/\.[^.]*$//') | |
| # Check if the file should still exist based on source and filter | |
| source_exists=false | |
| if [ -f "$SOURCE_API_GEN/${dest_basename_no_ext}.mdx" ] || [ -f "$SOURCE_API_GEN/${dest_basename_no_ext}.md" ]; then | |
| # Source exists, check if it matches the filter | |
| if matches_pattern "$dest_basename_no_ext" "$API_GEN_REGEX"; then | |
| source_exists=true | |
| fi | |
| fi | |
| if [ "$source_exists" = false ]; then | |
| rm "$dest_file" | |
| printf -- "- Deleted: \`%s\`\n" "$(basename "$dest_file")" >> $SYNC_REPORT | |
| DELETED=$((DELETED + 1)) | |
| fi | |
| fi | |
| done | |
| # Summary | |
| TOTAL=$((ADDED + UPDATED + DELETED)) | |
| if [ $TOTAL -eq 0 ]; then | |
| echo "- No API gen method updates found" >> $SYNC_REPORT | |
| else | |
| echo "" >> $SYNC_REPORT | |
| echo "Summary: $ADDED added, $UPDATED updated, $DELETED deleted" >> $SYNC_REPORT | |
| fi | |
| # Output all metrics | |
| echo "api_gen_added=$ADDED" >> $GITHUB_OUTPUT | |
| echo "api_gen_updated=$UPDATED" >> $GITHUB_OUTPUT | |
| echo "api_gen_deleted=$DELETED" >> $GITHUB_OUTPUT | |
| echo "api_gen_total=$TOTAL" >> $GITHUB_OUTPUT | |
| else | |
| echo "- Source API gen directory not found: \`${{ steps.params.outputs.api_gen_path }}\`" >> $SYNC_REPORT | |
| echo "api_gen_added=0" >> $GITHUB_OUTPUT | |
| echo "api_gen_updated=0" >> $GITHUB_OUTPUT | |
| echo "api_gen_deleted=0" >> $GITHUB_OUTPUT | |
| echo "api_gen_total=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Sync API debug method files | |
| id: sync_api_debug | |
| run: | | |
| set -euo pipefail | |
| SYNC_REPORT="${{ runner.temp }}/sync_report.md" | |
| SOURCE_API_DEBUG="source-repo/${{ steps.params.outputs.api_debug_path }}" | |
| DEST_API_DEBUG="pages/api-references/genlayer-node/debug" | |
| API_DEBUG_REGEX="${{ steps.params.outputs.api_debug_regex }}" | |
| echo "" >> $SYNC_REPORT | |
| echo "## API Debug Methods Sync" >> $SYNC_REPORT | |
| printf "Using regex filter: \`%s\`\n" "$API_DEBUG_REGEX" >> $SYNC_REPORT | |
| echo "" >> $SYNC_REPORT | |
| # Function to check if filename matches the regex pattern | |
| # Uses perl if available for PCRE support, otherwise falls back to grep -E | |
| matches_pattern() { | |
| local filename="$1" | |
| local pattern="$2" | |
| # Try perl first (supports PCRE including negative lookahead) | |
| if command -v perl >/dev/null 2>&1; then | |
| echo "$filename" | perl -ne "exit 0 if /^($pattern)\$/; exit 1" | |
| return $? | |
| fi | |
| # Fallback to grep -E (doesn't support negative lookahead) | |
| echo "$filename" | grep -E "^($pattern)$" >/dev/null 2>&1 | |
| return $? | |
| } | |
| if [ -d "$SOURCE_API_DEBUG" ]; then | |
| mkdir -p "$DEST_API_DEBUG" | |
| # Track existing files before sync | |
| declare -A EXISTING_FILES | |
| while IFS= read -r file; do | |
| [ -n "$file" ] && EXISTING_FILES["$(basename "$file")"]="$file" | |
| done < <(find "$DEST_API_DEBUG" -name "*.mdx" -type f) | |
| # Track what we'll be syncing | |
| ADDED=0 | |
| UPDATED=0 | |
| # Process all source files that match the regex | |
| for file in "$SOURCE_API_DEBUG"/*.mdx "$SOURCE_API_DEBUG"/*.md; do | |
| if [ -f "$file" ]; then | |
| basename_no_ext=$(basename "$file" | sed 's/\.[^.]*$//') | |
| # Check if filename (without extension) matches the regex filter | |
| if matches_pattern "$basename_no_ext" "$API_DEBUG_REGEX"; then | |
| dest_filename="${basename_no_ext}.mdx" | |
| dest_path="$DEST_API_DEBUG/$dest_filename" | |
| if [ -f "$dest_path" ]; then | |
| # File exists - check if it's different | |
| if ! cmp -s "$file" "$dest_path"; then | |
| cp "$file" "$dest_path" | |
| echo "- Updated: \`$dest_filename\`" >> $SYNC_REPORT | |
| UPDATED=$((UPDATED + 1)) | |
| fi | |
| # Remove from tracking to identify deletions later | |
| unset EXISTING_FILES["$dest_filename"] | |
| else | |
| # New file | |
| cp "$file" "$dest_path" | |
| echo "- Added: \`$dest_filename\`" >> $SYNC_REPORT | |
| ADDED=$((ADDED + 1)) | |
| fi | |
| fi | |
| fi | |
| done | |
| # Skip _meta.json handling - it should not be touched | |
| # Remove _meta.json from tracking to prevent deletion | |
| unset EXISTING_FILES["_meta.json"] | |
| # Remove files that no longer exist in source or don't match the filter | |
| DELETED=${DELETED:-0} | |
| for dest_file in "${EXISTING_FILES[@]}"; do | |
| if [ -f "$dest_file" ]; then | |
| dest_basename_no_ext=$(basename "$dest_file" | sed 's/\.[^.]*$//') | |
| # Check if the file should still exist based on source and filter | |
| source_exists=false | |
| if [ -f "$SOURCE_API_DEBUG/${dest_basename_no_ext}.mdx" ] || [ -f "$SOURCE_API_DEBUG/${dest_basename_no_ext}.md" ]; then | |
| # Source exists, check if it matches the filter | |
| if matches_pattern "$dest_basename_no_ext" "$API_DEBUG_REGEX"; then | |
| source_exists=true | |
| fi | |
| fi | |
| if [ "$source_exists" = false ]; then | |
| rm "$dest_file" | |
| printf -- "- Deleted: \`%s\`\n" "$(basename "$dest_file")" >> $SYNC_REPORT | |
| DELETED=$((DELETED + 1)) | |
| fi | |
| fi | |
| done | |
| # Summary | |
| TOTAL=$((ADDED + UPDATED + DELETED)) | |
| if [ $TOTAL -eq 0 ]; then | |
| echo "- No API debug method updates found" >> $SYNC_REPORT | |
| else | |
| echo "" >> $SYNC_REPORT | |
| echo "Summary: $ADDED added, $UPDATED updated, $DELETED deleted" >> $SYNC_REPORT | |
| fi | |
| # Output all metrics | |
| echo "api_debug_added=$ADDED" >> $GITHUB_OUTPUT | |
| echo "api_debug_updated=$UPDATED" >> $GITHUB_OUTPUT | |
| echo "api_debug_deleted=$DELETED" >> $GITHUB_OUTPUT | |
| echo "api_debug_total=$TOTAL" >> $GITHUB_OUTPUT | |
| else | |
| echo "- Source API debug directory not found: \`${{ steps.params.outputs.api_debug_path }}\`" >> $SYNC_REPORT | |
| echo "api_debug_added=0" >> $GITHUB_OUTPUT | |
| echo "api_debug_updated=0" >> $GITHUB_OUTPUT | |
| echo "api_debug_deleted=0" >> $GITHUB_OUTPUT | |
| echo "api_debug_total=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Run documentation generation scripts | |
| run: | | |
| set -euo pipefail | |
| SYNC_REPORT="${{ runner.temp }}/sync_report.md" | |
| echo "" >> $SYNC_REPORT | |
| echo "## Documentation Generation" >> $SYNC_REPORT | |
| echo "" >> $SYNC_REPORT | |
| npm run node-generate-changelog | |
| echo "- ✅ Generated changelog" >> $SYNC_REPORT | |
| npm run node-update-setup-guide | |
| echo "- ✅ Updated setup guide versions" >> $SYNC_REPORT | |
| npm run node-update-config | |
| echo "- ✅ Updated config in setup guide" >> $SYNC_REPORT | |
| npm run node-generate-api-docs | |
| echo "- ✅ Generated API documentation" >> $SYNC_REPORT | |
| # Final config verification | |
| echo "::group::Final config.yaml verification" | |
| CONFIG_PATH="content/validators/config.yaml" | |
| if [ -f "$CONFIG_PATH" ]; then | |
| echo "Config file exists at: $CONFIG_PATH" | |
| echo "File size: $(wc -c < "$CONFIG_PATH") bytes" | |
| echo "First 30 lines:" | |
| head -30 "$CONFIG_PATH" | |
| echo "---" | |
| echo "Checking for sensitive sections:" | |
| grep -E "^\s*(dev|admin):" "$CONFIG_PATH" && echo "ERROR: Sensitive sections found!" || echo "✓ No sensitive sections" | |
| echo "Checking for TODO placeholders:" | |
| grep -i "TODO:" "$CONFIG_PATH" && echo "✓ TODO placeholders found" || echo "WARNING: No TODO placeholders" | |
| else | |
| echo "ERROR: Config file not found at $CONFIG_PATH" | |
| fi | |
| echo "::endgroup::" | |
| - name: Check for changes | |
| id: check_changes | |
| run: | | |
| set -euo pipefail | |
| if [ -n "$(git status --porcelain)" ]; then | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| # Count all changes | |
| TOTAL_ADDED=$(( ${{ steps.sync_changelog.outputs.changelog_added || 0 }} + \ | |
| ${{ steps.sync_api_gen.outputs.api_gen_added || 0 }} + \ | |
| ${{ steps.sync_api_debug.outputs.api_debug_added || 0 }} )) | |
| TOTAL_UPDATED=$(( ${{ steps.sync_changelog.outputs.changelog_updated || 0 }} + \ | |
| ${{ steps.sync_config.outputs.config_updated || 0 }} + \ | |
| ${{ steps.sync_api_gen.outputs.api_gen_updated || 0 }} + \ | |
| ${{ steps.sync_api_debug.outputs.api_debug_updated || 0 }} )) | |
| TOTAL_DELETED=$(( ${{ steps.sync_changelog.outputs.changelog_deleted || 0 }} + \ | |
| ${{ steps.sync_api_gen.outputs.api_gen_deleted || 0 }} + \ | |
| ${{ steps.sync_api_debug.outputs.api_debug_deleted || 0 }} )) | |
| TOTAL_CHANGES=$(( TOTAL_ADDED + TOTAL_UPDATED + TOTAL_DELETED )) | |
| echo "total_added=$TOTAL_ADDED" >> $GITHUB_OUTPUT | |
| echo "total_updated=$TOTAL_UPDATED" >> $GITHUB_OUTPUT | |
| echo "total_deleted=$TOTAL_DELETED" >> $GITHUB_OUTPUT | |
| echo "total_changes=$TOTAL_CHANGES" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| echo "total_added=0" >> $GITHUB_OUTPUT | |
| echo "total_updated=0" >> $GITHUB_OUTPUT | |
| echo "total_deleted=0" >> $GITHUB_OUTPUT | |
| echo "total_changes=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Commit changes | |
| if: steps.check_changes.outputs.has_changes == 'true' | |
| run: | | |
| set -euo pipefail | |
| # Debug: Check what will be committed | |
| echo "::group::Debug: Files to be committed" | |
| echo "Checking git status before add:" | |
| git status --porcelain | |
| echo "::endgroup::" | |
| git add content/validators pages/api-references pages/validators | |
| echo "::group::Debug: Files staged for commit" | |
| echo "Checking git status after add:" | |
| git status --porcelain | |
| echo "Looking specifically for config.yaml:" | |
| git status --porcelain | grep -i config || echo "No config files in git status" | |
| echo "::endgroup::" | |
| git commit -m "docs: Sync documentation from node repository ${{ steps.set_version.outputs.version }} | |
| - Source: genlayerlabs/genlayer-node@${{ steps.set_version.outputs.version }} | |
| - Version: ${{ steps.set_version.outputs.version }} | |
| - Total changes: ${{ steps.check_changes.outputs.total_changes }} | |
| - Added: ${{ steps.check_changes.outputs.total_added }} files | |
| - Updated: ${{ steps.check_changes.outputs.total_updated }} files | |
| - Deleted: ${{ steps.check_changes.outputs.total_deleted }} files" | |
| - name: Read sync report | |
| id: read_sync_report | |
| if: steps.check_changes.outputs.has_changes == 'true' | |
| run: | | |
| set -euo pipefail | |
| # Read the sync report content and escape for GitHub Actions | |
| SYNC_REPORT="${{ runner.temp }}/sync_report.md" | |
| SYNC_REPORT_CONTENT=$(cat $SYNC_REPORT) | |
| # Use EOF delimiter to handle multi-line content | |
| echo "content<<EOF" >> $GITHUB_OUTPUT | |
| echo "$SYNC_REPORT_CONTENT" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| - name: Push changes | |
| if: steps.check_changes.outputs.has_changes == 'true' | |
| run: | | |
| set -euo pipefail | |
| git push --force-with-lease origin ${{ env.BRANCH_NAME }} | |
| - name: Capture timestamp | |
| id: timestamp | |
| run: echo "utc=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$GITHUB_OUTPUT" | |
| - name: Create Pull Request | |
| if: steps.check_changes.outputs.has_changes == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # Check if PR already exists for this branch | |
| if PR_JSON=$(gh pr view "${{ env.BRANCH_NAME }}" --json url,state 2>/dev/null); then | |
| PR_STATE=$(echo "$PR_JSON" | jq -r .state) | |
| PR_URL=$(echo "$PR_JSON" | jq -r .url) | |
| if [ "$PR_STATE" = "OPEN" ]; then | |
| echo "Open PR already exists for branch ${{ env.BRANCH_NAME }} – skipping creation" | |
| echo "View existing PR: $PR_URL" | |
| else | |
| echo "Closed PR exists for branch ${{ env.BRANCH_NAME }} (state: $PR_STATE)" | |
| echo "Creating new PR..." | |
| # Continue with PR creation below | |
| CREATE_PR=true | |
| fi | |
| else | |
| echo "No PR exists for branch ${{ env.BRANCH_NAME }}" | |
| CREATE_PR=true | |
| fi | |
| if [ "${CREATE_PR:-false}" = "true" ]; then | |
| # Create PR body in temp file | |
| PR_BODY_FILE="${{ runner.temp }}/pr_body.md" | |
| cat >"$PR_BODY_FILE" <<'EOF' | |
| ## 🔄 Documentation Sync from Node Repository | |
| This PR automatically syncs documentation from the genlayer-node repository. | |
| ### 📋 Summary | |
| - **Source Repository**: `genlayerlabs/genlayer-node` | |
| - **Version**: `${{ steps.set_version.outputs.version }}` | |
| - **API Gen Filter**: `${{ steps.params.outputs.api_gen_regex }}` | |
| - **API Debug Filter**: `${{ steps.params.outputs.api_debug_regex }}` | |
| - **Total Files Changed**: ${{ steps.check_changes.outputs.total_changes }} | |
| - Added: ${{ steps.check_changes.outputs.total_added }} files | |
| - Updated: ${{ steps.check_changes.outputs.total_updated }} files | |
| - Deleted: ${{ steps.check_changes.outputs.total_deleted }} files | |
| - **Timestamp**: ${{ steps.timestamp.outputs.utc }} | |
| ### 📝 Changes | |
| See details below: | |
| --- | |
| ${{ steps.read_sync_report.outputs.content }} | |
| --- | |
| ### 🤖 Automated Process | |
| This PR was automatically generated by the documentation sync workflow. The following scripts were run: | |
| - `npm run node-generate-changelog` | |
| - `npm run node-update-setup-guide` | |
| - `npm run node-generate-api-docs` | |
| Please review the changes and merge if everything looks correct. | |
| EOF | |
| # Create PR using GitHub CLI | |
| gh pr create \ | |
| --title "docs: Sync documentation from genlayer-node ${{ steps.set_version.outputs.version }}" \ | |
| --body-file "$PR_BODY_FILE" \ | |
| --label "documentation" \ | |
| --label "node" \ | |
| --base "main" \ | |
| --head "${{ env.BRANCH_NAME }}" | |
| fi | |
| - name: Summary | |
| run: | | |
| set -euo pipefail | |
| if [ "${{ steps.check_changes.outputs.has_changes }}" == "true" ]; then | |
| echo "✅ Successfully created PR with documentation updates" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "${{ steps.read_sync_report.outputs.content }}" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "ℹ️ No documentation changes detected. No PR created." >> $GITHUB_STEP_SUMMARY | |
| fi |