diff --git a/.github/actions/sync-files/action.yml b/.github/actions/sync-files/action.yml new file mode 100644 index 00000000..ee19f1b0 --- /dev/null +++ b/.github/actions/sync-files/action.yml @@ -0,0 +1,107 @@ +name: 'Sync Files' +description: 'Generic file/directory synchronization with filtering and exclusions' +inputs: + type: + description: 'Type identifier for the sync operation (for artifact naming)' + required: true + title: + description: 'Human-readable title for reports' + required: true + source_path: + description: 'Source file or directory path' + required: true + target_path: + description: 'Target file or directory path' + required: true + filter_pattern: + description: 'Regex pattern to filter files (only for directories)' + required: false + default: '.*' + exclude_files: + description: 'Comma-separated list of filenames to exclude' + required: false + default: 'README,CHANGELOG,.gitignore,.gitkeep' + sanitize_script: + description: 'Optional script path to run after sync (for sanitization/post-processing)' + required: false + default: '' +outputs: + added: + description: 'Number of files added' + value: ${{ steps.sync.outputs.added }} + updated: + description: 'Number of files updated' + value: ${{ steps.sync.outputs.updated }} + deleted: + description: 'Number of files deleted' + value: ${{ steps.sync.outputs.deleted }} + total: + description: 'Total number of changes' + value: ${{ steps.sync.outputs.total }} +runs: + using: 'composite' + steps: + - name: Prepare config file + if: inputs.type == 'config' + shell: bash + run: | + # For config type, handle special case: + # 1. Copy config.yaml.example to temp location as config.yaml + # 2. Run sanitization on the renamed file + # 3. Use the sanitized file as source for sync + + if [[ -f "${{ inputs.source_path }}" ]]; then + # Create temp directory + TEMP_DIR=$(mktemp -d) + echo "TEMP_CONFIG_DIR=$TEMP_DIR" >> $GITHUB_ENV + + # Copy and rename config.yaml.example to config.yaml + cp "${{ inputs.source_path }}" "$TEMP_DIR/config.yaml" + + # Run sanitization if script is provided + if [[ -n "${{ inputs.sanitize_script }}" ]] && [[ -f "${{ inputs.sanitize_script }}" ]]; then + echo "Sanitizing config file..." + bash "${{ inputs.sanitize_script }}" "$TEMP_DIR/config.yaml" + fi + + # Update source path for sync + echo "CONFIG_SOURCE=$TEMP_DIR/config.yaml" >> $GITHUB_ENV + else + echo "Config source file not found: ${{ inputs.source_path }}" + exit 1 + fi + + - name: Sync files + id: sync + shell: bash + run: | + # Use prepared config source if it's a config type, otherwise use original source + if [[ "${{ inputs.type }}" == "config" ]] && [[ -n "$CONFIG_SOURCE" ]]; then + SOURCE_PATH="$CONFIG_SOURCE" + else + SOURCE_PATH="${{ inputs.source_path }}" + fi + + ${{ github.action_path }}/sync.sh \ + "${{ inputs.type }}" \ + "${{ inputs.title }}" \ + "$SOURCE_PATH" \ + "${{ inputs.target_path }}" \ + "${{ inputs.filter_pattern }}" \ + "${{ inputs.exclude_files }}" + + - name: Cleanup config temp directory + if: inputs.type == 'config' && always() + shell: bash + run: | + if [[ -n "$TEMP_CONFIG_DIR" ]] && [[ -d "$TEMP_CONFIG_DIR" ]]; then + rm -rf "$TEMP_CONFIG_DIR" + fi + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + if: always() + with: + name: synced-${{ inputs.type }} + path: artifacts/ + retention-days: 1 \ No newline at end of file diff --git a/.github/actions/sync-files/sync.sh b/.github/actions/sync-files/sync.sh new file mode 100755 index 00000000..5559c3c0 --- /dev/null +++ b/.github/actions/sync-files/sync.sh @@ -0,0 +1,238 @@ +#!/bin/bash +set -e + +# File/directory synchronization script for documentation +# Syncs .md and .mdx files with pattern filtering + +# Get parameters +TYPE="$1" +TITLE="$2" +SOURCE_PATH="$3" +TARGET_PATH="$4" +FILTER_PATTERN="${5:-.*}" +EXCLUDE_FILES="${6:-README,CHANGELOG,.gitignore,.gitkeep}" + +# Initialize metrics and lists +added=0 +updated=0 +deleted=0 +added_files=() +updated_files=() +deleted_files=() + +# Convert exclusions to array +IFS=',' read -ra EXCLUSIONS <<< "$EXCLUDE_FILES" + +# Check if file should be excluded +is_excluded() { + local filename="$1" + for excluded in "${EXCLUSIONS[@]}"; do + [[ "$filename" == "$excluded" ]] && return 0 + done + return 1 +} + +# Check if filename matches pattern +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 $? +} + +# Handle single file sync +if [[ -f "$SOURCE_PATH" ]]; then + echo "Syncing file: $SOURCE_PATH -> $TARGET_PATH" + mkdir -p "$(dirname "$TARGET_PATH")" + + if [[ -f "$TARGET_PATH" ]]; then + if ! cmp -s "$SOURCE_PATH" "$TARGET_PATH"; then + cp "$SOURCE_PATH" "$TARGET_PATH" + updated=1 + updated_files+=("$(basename "$TARGET_PATH")") + echo "Updated: $(basename "$TARGET_PATH")" + fi + else + cp "$SOURCE_PATH" "$TARGET_PATH" + added=1 + added_files+=("$(basename "$TARGET_PATH")") + echo "Added: $(basename "$TARGET_PATH")" + fi + +# Handle directory sync +elif [[ -d "$SOURCE_PATH" ]]; then + echo "Syncing directory: $SOURCE_PATH -> $TARGET_PATH" + echo "Filter pattern: $FILTER_PATTERN" + echo "Exclude files: $EXCLUDE_FILES" + + # Check if source directory has any files + if [[ -z "$(ls -A "$SOURCE_PATH" 2>/dev/null)" ]]; then + echo "Warning: Source directory is empty: $SOURCE_PATH" + # Create empty target to ensure it exists + mkdir -p "$TARGET_PATH" + added=0 + updated=0 + deleted=0 + else + mkdir -p "$TARGET_PATH" + + # Create temp directory with normalized source files + TEMP_SOURCE=$(mktemp -d) + trap "rm -rf $TEMP_SOURCE" EXIT + + echo "Preparing source files..." + + # Count source files + source_count=0 + + # Process and filter source files into temp directory + shopt -s nullglob # Handle case when no .md or .mdx files exist + for file in "$SOURCE_PATH"/*.md "$SOURCE_PATH"/*.mdx; do + [[ ! -f "$file" ]] && continue + + basename_file=$(basename "$file") + basename_no_ext="${basename_file%.*}" + + # Skip excluded files + is_excluded "$basename_no_ext" && continue + + # Skip if doesn't match pattern + matches_pattern "$basename_no_ext" "$FILTER_PATTERN" || continue + + # Copy to temp with .mdx extension + cp "$file" "$TEMP_SOURCE/${basename_no_ext}.mdx" + source_count=$((source_count + 1)) + echo " Processing: $basename_file" + done + shopt -u nullglob + + echo "Processed $source_count source files" + + # Track existing target files (using simple array for compatibility) + existing_files=() + if [[ -d "$TARGET_PATH" ]]; then + while IFS= read -r file; do + [[ -n "$file" ]] && existing_files+=("$file") + done < <(find "$TARGET_PATH" -type f -name "*.mdx" 2>/dev/null) + fi + + # Sync from temp source to target + for source_file in "$TEMP_SOURCE"/*.mdx; do + [[ ! -f "$source_file" ]] && continue + + basename_file=$(basename "$source_file") + target_file="$TARGET_PATH/$basename_file" + + if [[ -f "$target_file" ]]; then + if ! cmp -s "$source_file" "$target_file"; then + cp "$source_file" "$target_file" + updated=$((updated + 1)) + updated_files+=("$basename_file") + echo "Updated: $basename_file" + fi + # Remove from existing files array + new_existing=() + for ef in "${existing_files[@]}"; do + [[ "$(basename "$ef")" != "$basename_file" ]] && new_existing+=("$ef") + done + existing_files=("${new_existing[@]}") + else + cp "$source_file" "$target_file" + added=$((added + 1)) + added_files+=("$basename_file") + echo "Added: $basename_file" + fi + done + + # Delete orphaned files only if we had source files to sync + if [[ "$source_count" -gt 0 ]]; then + # Safe to delete orphaned files (preserve _meta.json) + for target_file in "${existing_files[@]}"; do + if [[ -f "$target_file" && "$(basename "$target_file")" != "_meta.json" ]]; then + rm "$target_file" + deleted=$((deleted + 1)) + deleted_files+=("$(basename "$target_file")") + echo "Deleted: $(basename "$target_file")" + fi + done + else + echo "Warning: No source files matched filter; skipping deletion phase for safety" + echo " This prevents accidental mass deletion of target files" + fi + fi # End of non-empty directory check +else + echo "Source not found: $SOURCE_PATH" + exit 1 +fi + +# Output metrics +total=$((added + updated + deleted)) +echo "added=$added" >> "$GITHUB_OUTPUT" +echo "updated=$updated" >> "$GITHUB_OUTPUT" +echo "deleted=$deleted" >> "$GITHUB_OUTPUT" +echo "total=$total" >> "$GITHUB_OUTPUT" + +# Create artifact directory with report and synced files +mkdir -p artifacts + +# Create summary report at root of artifacts +REPORT_FILE="artifacts/sync_report_${TYPE}.md" +cat > "$REPORT_FILE" <> "$REPORT_FILE" + echo "### Added Files" >> "$REPORT_FILE" + for file in "${added_files[@]}"; do + echo "- $file" >> "$REPORT_FILE" + done +fi + +if [[ ${#updated_files[@]} -gt 0 ]]; then + echo "" >> "$REPORT_FILE" + echo "### Updated Files" >> "$REPORT_FILE" + for file in "${updated_files[@]}"; do + echo "- $file" >> "$REPORT_FILE" + done +fi + +if [[ ${#deleted_files[@]} -gt 0 ]]; then + echo "" >> "$REPORT_FILE" + echo "### Deleted Files" >> "$REPORT_FILE" + for file in "${deleted_files[@]}"; do + echo "- $file" >> "$REPORT_FILE" + done +fi + +# Copy whatever is in target to artifacts +if [[ -f "$TARGET_PATH" ]]; then + # Single file + mkdir -p "artifacts/$(dirname "$TARGET_PATH")" + cp "$TARGET_PATH" "artifacts/${TARGET_PATH}" + +elif [[ -d "$TARGET_PATH" ]]; then + # Directory - just copy everything + mkdir -p "artifacts/${TARGET_PATH}" + cp -r "$TARGET_PATH"/* "artifacts/${TARGET_PATH}/" 2>/dev/null || true +fi + +echo "Sync completed: $total changes" \ No newline at end of file diff --git a/.github/scripts/aggregate-reports.sh b/.github/scripts/aggregate-reports.sh new file mode 100755 index 00000000..19d2dea8 --- /dev/null +++ b/.github/scripts/aggregate-reports.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -euo pipefail + +# Aggregate sync reports and calculate totals +# Used by the GitHub Actions workflow to process sync results + +# Set default output file if GITHUB_OUTPUT is not available (for local testing) +if [[ -z "${GITHUB_OUTPUT:-}" ]]; then + GITHUB_OUTPUT="${TMPDIR:-/tmp}/github_output.txt" + # Create the file if it doesn't exist + touch "$GITHUB_OUTPUT" +fi + +aggregate_sync_reports() { + # Initialize counters + local TOTAL_CHANGES=0 + local TOTAL_ADDED=0 + local TOTAL_UPDATED=0 + local TOTAL_DELETED=0 + + # Collect all reports + local ALL_REPORTS="" + + if [[ -d "sync-reports" ]]; then + ls -la sync-reports/ || echo "Directory is empty" + + for report_file in sync-reports/sync_report_*.md; do + if [[ -f "$report_file" ]]; then + + # Extract metrics from report content + local REPORT_CONTENT + REPORT_CONTENT=$(cat "$report_file") + + # Look for summary section with bullet points + if echo "$REPORT_CONTENT" | grep -q "### Summary"; then + # Extract numbers from bullet points like: + # - **Added**: X files + # - **Updated**: Y files + # - **Deleted**: Z files + local ADDED UPDATED DELETED + ADDED=$(echo "$REPORT_CONTENT" | grep -o '\*\*Added\*\*: [0-9]\+ files' | grep -o '[0-9]\+' || echo "0") + UPDATED=$(echo "$REPORT_CONTENT" | grep -o '\*\*Updated\*\*: [0-9]\+ files' | grep -o '[0-9]\+' || echo "0") + DELETED=$(echo "$REPORT_CONTENT" | grep -o '\*\*Deleted\*\*: [0-9]\+ files' | grep -o '[0-9]\+' || echo "0") + + # Add to totals + TOTAL_ADDED=$((TOTAL_ADDED + ADDED)) + TOTAL_UPDATED=$((TOTAL_UPDATED + UPDATED)) + TOTAL_DELETED=$((TOTAL_DELETED + DELETED)) + + local REPORT_TOTAL=$((ADDED + UPDATED + DELETED)) + TOTAL_CHANGES=$((TOTAL_CHANGES + REPORT_TOTAL)) + + elif ! echo "$REPORT_CONTENT" | grep -q "No.*updates found"; then + echo "⚠️ Could not parse metrics from report, assuming 1 change" + TOTAL_CHANGES=$((TOTAL_CHANGES + 1)) + fi + + # Append report content + if [[ -n "$ALL_REPORTS" ]]; then + ALL_REPORTS="$ALL_REPORTS"$'\n\n---\n\n' + fi + ALL_REPORTS="$ALL_REPORTS$REPORT_CONTENT" + else + echo "⚠️ No report files found matching pattern" + fi + done + else + echo "⚠️ sync-reports directory not found, using simple aggregation" + # Simple fallback - assume basic operation succeeded + ALL_REPORTS="## Sync Results"$'\n\nDocumentation sync completed successfully.\n\n' + fi + + # Output results to GitHub Actions + echo "total_changes=$TOTAL_CHANGES" >> "$GITHUB_OUTPUT" + echo "total_added=$TOTAL_ADDED" >> "$GITHUB_OUTPUT" + echo "total_updated=$TOTAL_UPDATED" >> "$GITHUB_OUTPUT" + echo "total_deleted=$TOTAL_DELETED" >> "$GITHUB_OUTPUT" + + # Handle multiline output for reports + echo "all_reports<> "$GITHUB_OUTPUT" + echo "$ALL_REPORTS" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + +} + +# Run the aggregation +aggregate_sync_reports \ No newline at end of file diff --git a/.github/scripts/doc-generator.sh b/.github/scripts/doc-generator.sh new file mode 100755 index 00000000..c24e8de1 --- /dev/null +++ b/.github/scripts/doc-generator.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -euo pipefail + +# Documentation generation wrapper +# Runs all the npm scripts for doc generation with proper error handling + +run_doc_generation() { + echo "Running documentation generation scripts..." + + # Track success + local failed=false + + # Run each generation script + echo "::group::Running node-generate-changelog" + if npm run node-generate-changelog; then + echo "✅ Generated changelog" + else + echo "❌ node-generate-changelog failed" + failed=true + fi + echo "::endgroup::" + + echo "::group::Running node-update-setup-guide" + if npm run node-update-setup-guide; then + echo "✅ Updated setup guide versions" + else + echo "❌ node-update-setup-guide failed" + failed=true + fi + echo "::endgroup::" + + echo "::group::Running node-update-config" + if npm run node-update-config; then + echo "✅ Updated config in setup guide" + else + echo "❌ node-update-config failed" + failed=true + fi + echo "::endgroup::" + + echo "::group::Running node-generate-api-docs" + if npm run node-generate-api-docs; then + echo "✅ Generated API documentation" + else + echo "❌ node-generate-api-docs failed" + failed=true + fi + echo "::endgroup::" + + if [[ "$failed" == "true" ]]; then + echo "❌ Some documentation generation scripts failed" + return 1 + fi + + echo "✅ All documentation generation scripts completed successfully" + return 0 +} + +# Run if called directly +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + run_doc_generation +fi \ No newline at end of file diff --git a/.github/scripts/git-utils.sh b/.github/scripts/git-utils.sh new file mode 100755 index 00000000..210ea55d --- /dev/null +++ b/.github/scripts/git-utils.sh @@ -0,0 +1,83 @@ +#!/bin/bash +set -euo pipefail + +# Git utilities for branch management and PR creation +# Extracted from the complex git operations in the workflow + +# Create sync branch with proper naming +create_sync_branch() { + local version="$1" + + # Sanitize version string for use in branch name + local safe_version + safe_version=$(echo "$version" | sed 's/\//-/g') + local branch_name="docs/node/${safe_version}" + + echo "🌿 Creating sync branch: $branch_name" >&2 + + # 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" >&2 + git fetch origin "$branch_name" + fi + + # Create/recreate branch from current HEAD (main) + git switch --force-create "$branch_name" + + # Export for use in subsequent steps and return for local use + echo "BRANCH_NAME=$branch_name" >> "$GITHUB_ENV" + echo "✅ Created branch: $branch_name" >&2 + + # Return only the branch name for capture + echo "$branch_name" +} + +# Commit and push changes +commit_and_push_changes() { + local version="$1" + local total_changes="$2" + local total_added="$3" + local total_updated="$4" + local total_deleted="$5" + local branch_name="$6" + + echo "Committing changes..." + + # Add relevant directories (including deletions) + git add --all content/validators pages/api-references pages/validators + + # Check what's staged + echo "📋 Files staged for commit:" + git status --porcelain + + # Create commit with detailed message + git commit -m "$(cat <> "$GITHUB_OUTPUT" + echo "✅ Changes detected" + return 0 + else + echo "has_changes=false" >> "$GITHUB_OUTPUT" + echo "ℹ️ No changes detected" + return 1 + fi +} \ No newline at end of file diff --git a/.github/scripts/sanitize-config.sh b/.github/scripts/sanitize-config.sh new file mode 100755 index 00000000..611af825 --- /dev/null +++ b/.github/scripts/sanitize-config.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -e + +# Sanitize GenLayer config file for documentation +# Usage: sanitize-config.sh + +CONFIG_FILE="$1" + +if [[ -z "$CONFIG_FILE" ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +if [[ ! -f "$CONFIG_FILE" ]]; then + echo "File not found: $CONFIG_FILE" >&2 + exit 1 +fi + +echo "Sanitizing config file: $CONFIG_FILE" + +# Replace URLs with TODO placeholders (only on non-commented lines; preserve indent) +sed -i.bak -E '/^[[:space:]]*#/! s|^([[:space:]]*)zksyncurl:[[:space:]]*".*"|\1zksyncurl: "TODO: Set your GenLayer Chain ZKSync HTTP RPC URL here"|' "$CONFIG_FILE" +sed -i.bak -E '/^[[:space:]]*#/! s|^([[:space:]]*)zksyncwebsocketurl:[[:space:]]*".*"|\1zksyncwebsocketurl: "TODO: Set your GenLayer Chain ZKSync WebSocket RPC URL here"|' "$CONFIG_FILE" +rm -f "${CONFIG_FILE}.bak" + +# Remove node.dev sections using Python script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [[ -f "$SCRIPT_DIR/sanitize-config.py" ]]; then + python3 "$SCRIPT_DIR/sanitize-config.py" "$CONFIG_FILE" +else + echo "Warning: sanitize-config.py not found, skipping Python sanitization" +fi + +echo "Config sanitization completed" \ No newline at end of file diff --git a/.github/scripts/sync-artifact-files.sh b/.github/scripts/sync-artifact-files.sh new file mode 100755 index 00000000..53ba56e7 --- /dev/null +++ b/.github/scripts/sync-artifact-files.sh @@ -0,0 +1,64 @@ +#!/bin/bash +set -e + +# Sync artifact files to the repository +# Usage: sync-artifact-files.sh +# Example: sync-artifact-files.sh temp-merged . "content/validators" "pages/api-references/genlayer-node" + +SOURCE_ROOT="$1" +TARGET_ROOT="$2" +shift 2 +SYNC_PATHS=("$@") + +if [[ -z "$SOURCE_ROOT" || -z "$TARGET_ROOT" || ${#SYNC_PATHS[@]} -eq 0 ]]; then + echo "Usage: $0 " >&2 + echo "Example: $0 temp-merged . 'content/validators' 'pages/api-references/genlayer-node'" >&2 + exit 1 +fi + +if [[ ! -d "$SOURCE_ROOT" ]]; then + echo "Source root directory not found: $SOURCE_ROOT" >&2 + exit 1 +fi + +echo "Syncing artifact files from $SOURCE_ROOT to $TARGET_ROOT" +echo "Paths to sync: ${SYNC_PATHS[*]}" + +total_synced=0 + +# Sync each specified path +for path in "${SYNC_PATHS[@]}"; do + source_path="$SOURCE_ROOT/$path" + target_path="$TARGET_ROOT/$path" + + if [[ -d "$source_path" ]]; then + echo "" + echo "Syncing $path..." + + # Create parent directory if needed + mkdir -p "$(dirname "$target_path")" + + # Count files in this path + file_count=$(find "$source_path" -type f | wc -l) + echo " Found $file_count files in $path" + + # Always run rsync with delete to ensure stale files are removed even when source is empty + rsync -av --delete "$source_path/" "$target_path/" + total_synced=$((total_synced + file_count)) + + if [[ $file_count -gt 0 ]]; then + echo " ✅ Synced $path ($file_count files)" + else + echo " ✅ Synced $path (cleaned - no files in source)" + fi + else + echo " ⏭️ Skipping $path (not found in source)" + fi +done + +echo "" +if [[ $total_synced -gt 0 ]]; then + echo "✅ Successfully synced $total_synced files" +else + echo "⚠️ No files were synced" +fi \ No newline at end of file diff --git a/.github/scripts/test_sanitize_config.py b/.github/scripts/test_sanitize_config.py deleted file mode 100644 index 72daba56..00000000 --- a/.github/scripts/test_sanitize_config.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python3 -"""Test script for sanitize-config.py""" - -import os -import sys -import tempfile - -# Import sanitize_config function directly -import importlib.util -spec = importlib.util.spec_from_file_location("sanitize_config", - os.path.join(os.path.dirname(os.path.abspath(__file__)), "sanitize-config.py")) -module = importlib.util.module_from_spec(spec) -spec.loader.exec_module(module) -sanitize_config = module.sanitize_config - -def test_sanitize_config(): - """Test that the sanitize_config function removes only dev sections.""" - - # Test config with admin and dev sections - test_config = """# node configuration -node: - # Mode can be "validator" or "archive". - mode: "validator" - admin: - port: 9155 - rpc: - port: 9151 - endpoints: - groups: - genlayer: true - methods: - gen_call: true - ops: - port: 9153 - endpoints: - metrics: true - health: true - dev: - disableSubscription: false - -# genvm configuration -genvm: - bin_dir: ./third_party/genvm/bin - manage_modules: true -""" - - # Create a temporary file - with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: - f.write(test_config) - temp_file = f.name - - try: - # Run sanitize_config - print("Testing sanitize_config...") - sanitize_config(temp_file) - - # Read the result - with open(temp_file, 'r') as f: - result_content = f.read() - - # Verify the results by checking the content - print("\nVerifying results...") - - # Check that node section exists - assert 'node:' in result_content, "node section should exist" - - # Check that admin is preserved and dev is removed - assert 'admin:' in result_content, "admin section should be preserved" - assert 'port: 9155' in result_content, "admin port should be preserved" - assert 'dev:' not in result_content, "dev section should be removed" - assert 'disableSubscription:' not in result_content, "dev content should be removed" - - # Check that other sections are preserved - assert 'rpc:' in result_content, "rpc section should be preserved" - assert 'ops:' in result_content, "ops section should be preserved" - assert 'port: 9151' in result_content, "rpc port should be preserved" - assert 'port: 9153' in result_content, "ops port should be preserved" - assert 'endpoints:' in result_content, "endpoints should be preserved" - assert 'groups:' in result_content, "groups should be preserved" - assert 'methods:' in result_content, "methods should be preserved" - - # Check that genvm section is preserved - assert 'genvm:' in result_content, "genvm section should exist" - assert 'manage_modules: true' in result_content, "genvm settings should be preserved" - - print("✅ All tests passed!") - - # Print the sanitized config - print("\nSanitized config:") - print(result_content) - - finally: - # Clean up - os.unlink(temp_file) - -if __name__ == "__main__": - test_sanitize_config() \ No newline at end of file diff --git a/.github/scripts/version-utils.sh b/.github/scripts/version-utils.sh new file mode 100755 index 00000000..d3efb016 --- /dev/null +++ b/.github/scripts/version-utils.sh @@ -0,0 +1,100 @@ +#!/bin/bash +set -euo pipefail + +# Version detection and handling utilities + +# Detect latest version from repository +detect_latest_version() { + local token="${1:-$GITHUB_TOKEN}" + local temp_dir="/tmp/source-repo-temp-$$" + + # Clone source repo (minimal, just for tags) with token if available + if [[ -n "${token:-}" ]]; then + git clone --depth 1 --no-checkout \ + "https://${token}@github.com/genlayerlabs/genlayer-node.git" "$temp_dir" 2>/dev/null || \ + git clone --depth 1 --no-checkout \ + "https://github.com/genlayerlabs/genlayer-node.git" "$temp_dir" + else + git clone --depth 1 --no-checkout \ + "https://github.com/genlayerlabs/genlayer-node.git" "$temp_dir" + fi + + cd "$temp_dir" + + # Fetch all tags + git fetch --tags + + # Get latest stable version tag + local latest_tag + latest_tag=$(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1) + + # Cleanup + cd / + rm -rf "$temp_dir" + + if [[ -z "$latest_tag" ]]; then + echo "::error::No version tags found in source repository" >&2 + exit 1 + fi + + echo "$latest_tag" +} + +# Version validation +validate_version() { + local version="$1" + + # Support full SemVer: vX.Y.Z[-prerelease][+buildmetadata] + # Examples: v1.2.3, v1.2.3-rc.1, v1.2.3-alpha.2, v1.2.3+build.7, v1.2.3-beta.1+exp.sha.5114f85 + if [[ ! "$version" =~ ^(latest|v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?)$ ]]; then + echo "::error::Invalid version format: $version" + echo "Expected: 'latest' or SemVer format (e.g., 'v1.2.3', 'v1.2.3-rc.1', 'v1.2.3+build.7')" + return 1 + fi + + echo "✓ Version format is valid: $version" + return 0 +} + +# Extract sync parameters from workflow inputs +extract_sync_parameters() { + local event_name="${1:-}" + local version="${2:-latest}" + local changelog_path="${3:-docs/changelog}" + local api_gen_path="${4:-docs/api/rpc}" + local api_debug_path="${5:-docs/api/rpc}" + local api_gen_regex="${6:-gen_(?!dbg_).*}" + local api_debug_regex="${7:-gen_dbg_.*}" + + echo "📋 Extracting sync parameters for event: $event_name" + + # Output extracted parameters + echo "changelog_path=$changelog_path" >> "$GITHUB_OUTPUT" + echo "api_gen_path=$api_gen_path" >> "$GITHUB_OUTPUT" + echo "api_debug_path=$api_debug_path" >> "$GITHUB_OUTPUT" + echo "api_gen_regex=$api_gen_regex" >> "$GITHUB_OUTPUT" + echo "api_debug_regex=$api_debug_regex" >> "$GITHUB_OUTPUT" + + # Validate and output the requested version + validate_version "$version" + echo "requested_version=$version" >> "$GITHUB_OUTPUT" + echo "Extracted version: $version" +} + +# Detect and validate final version to use +detect_and_validate_version() { + local requested_version="$1" + local final_version="" + + if [[ "$requested_version" == "latest" || -z "$requested_version" ]]; then + final_version=$(detect_latest_version "source-repo") + else + final_version="$requested_version" + fi + + # Final validation + validate_version "$final_version" + + echo "final_version=$final_version" >> "$GITHUB_OUTPUT" + echo "✅ Using version: $final_version" +} \ No newline at end of file diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 7cd7d979..60922e62 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -11,24 +11,28 @@ This workflow automatically synchronizes documentation from the `genlayerlabs/ge ### What it does -1. Clones the specified branch from the genlayer-node repository -2. Gets the latest tag from the repository to use in the branch name -3. Copies new or updated files: - - Changelog files → `content/validators/changelog/` - - Config file → `content/validators/config.yaml` (sanitized - see note below) - - API gen method docs → `pages/api-references/genlayer-node/gen/` - - API debug method docs → `pages/api-references/genlayer-node/debug/` - - **Note**: Both `.md` and `.mdx` files are supported. `.md` files are automatically renamed to `.mdx` when copied - - **Config Sanitization**: The config file is sanitized during sync: - - ZKSync URLs are replaced with TODO placeholders - - `node.dev` and `node.admin` sections are removed - - **Regex Filtering**: API files can be filtered using regex patterns (see Customizing section below) -4. Runs documentation generation scripts: - - `generate-changelog.js` - - `update-setup-guide-versions.js` - - `update-config-in-setup-guide.js` - - `generate-api-docs.js` -5. Creates a PR with all changes, using the tag in the branch name (e.g., `sync-node-docs-v0.3.5`) +1. **Prepare**: Detects version from input or automatically finds latest tag +2. **Sync Files** (parallel matrix strategy, 5 sync types): + - **Changelog files** → `content/validators/changelog/` + - **Config file** → `content/validators/config.yaml` (with sanitization) + - **API gen method docs** → `pages/api-references/genlayer-node/gen/` (filtered by regex) + - **API debug method docs** → `pages/api-references/genlayer-node/debug/` (filtered by regex) + - **API ops method docs** → `pages/api-references/genlayer-node/ops/` +3. **Aggregate Results**: Merges all synced files from parallel jobs into single artifact +4. **Generate Docs**: + - Applies synced files to specific directories (avoids deleting unrelated content) + - Runs documentation generation scripts to create `pages/validators/` files +5. **Create PR**: + - Creates branch, commits changes, and creates/updates pull requests + - Includes detailed summary with file counts +6. **Summary**: Generates comprehensive workflow summary with detailed file lists +7. **Cleanup**: Automatically removes all intermediate artifacts when enabled + +**Important Notes**: +- Both `.md` and `.mdx` files are supported, automatically renamed to `.mdx` when synced +- README and CHANGELOG files are excluded from sync operations +- Regex filtering uses Perl-compatible patterns (supports negative lookahead) +- File deletions are properly handled with `rsync --delete` for each directory ### Triggering from genlayer-node @@ -43,10 +47,11 @@ Add this to a workflow in the genlayer-node repository: event-type: sync-docs client-payload: | { - "source_branch": "${{ github.ref_name }}", + "version": "${{ steps.get_version.outputs.version }}", "changelog_path": "docs/changelog", "api_gen_path": "docs/api/rpc", "api_debug_path": "docs/api/rpc", + "api_ops_path": "docs/api/ops", "api_gen_regex": "gen_(?!dbg_).*", "api_debug_regex": "gen_dbg_.*" } @@ -79,12 +84,13 @@ From the Actions tab: 1. Select "Sync Documentation from Node Repository" 2. Click "Run workflow" 3. Specify parameters: - - Tag for branch naming (required, e.g., v0.3.5) - - Source branch (optional, default: main) - - API gen path (optional, default: `docs/api/rpc`) - - API debug path (optional, default: `docs/api/rpc`) - - API gen regex filter (optional, default: `gen_(?!dbg_).*`) - - API debug regex filter (optional, default: `gen_dbg_.*`) + - **Version** (optional, default: `latest`) - Version/tag to sync (e.g., v0.3.5, or "latest" to auto-detect) + - **Changelog path** (optional, default: `docs/changelog`) + - **API gen path** (optional, default: `docs/api/rpc`) + - **API debug path** (optional, default: `docs/api/rpc`) + - **API ops path** (optional, default: `docs/api/ops`) + - **API gen regex** (optional, default: `gen_(?!dbg_).*`) + - **API debug regex** (optional, default: `gen_dbg_.*`) ### File Structure Expected in genlayer-node @@ -95,13 +101,14 @@ docs/ │ ├── v0.3.5.mdx # Will be copied as-is │ └── ... ├── api/ -│ ├── gen/ -│ │ ├── gen_call.md # Will be copied as gen_call.mdx -│ │ ├── gen_getContractSchema.mdx # Will be copied as-is +│ ├── rpc/ +│ │ ├── gen_call.md # API gen: copied as gen_call.mdx +│ │ ├── gen_getContractSchema.mdx # API gen: copied as-is +│ │ ├── gen_dbg_ping.md # API debug: copied as gen_dbg_ping.mdx │ │ └── ... -│ └── debug/ -│ ├── gen_dbg_ping.md # Will be copied as gen_dbg_ping.mdx -│ ├── gen_dbg_trie.mdx # Will be copied as-is +│ └── ops/ +│ ├── health.md # API ops: copied as health.mdx +│ ├── metrics.mdx # API ops: copied as-is │ └── ... configs/ └── node/ @@ -110,12 +117,13 @@ configs/ ### Customizing Paths and Filtering -The source paths and filters can be customized in the event payload: +The source paths and filters can be customized via workflow_dispatch inputs: #### Paths - `changelog_path`: Path to changelog files (default: `docs/changelog`) - `api_gen_path`: Path to API gen methods (default: `docs/api/rpc`) - `api_debug_path`: Path to API debug methods (default: `docs/api/rpc`) +- `api_ops_path`: Path to API ops methods (default: `docs/api/ops`) #### Regex Filters - `api_gen_regex`: Regex pattern to filter gen API files (default: `gen_(?!dbg_).*`) @@ -123,4 +131,102 @@ The source paths and filters can be customized in the event payload: - `api_debug_regex`: Regex pattern to filter debug API files (default: `gen_dbg_.*`) - This default pattern matches only files starting with `gen_dbg_` -The regex patterns are applied to the filename (without extension) to determine which files should be synced. \ No newline at end of file +**Note**: API ops sync includes all files (no regex filtering applied), except README files which are automatically excluded. + +## Pipeline Architecture + +### Jobs and Dependencies +The workflow uses 7 main jobs with the following dependency chain: + +``` +prepare (version detection) + ↓ +sync-files (matrix: 5 parallel jobs) + ↓ +aggregate-results (merges artifacts) + ↓ +generate-docs (runs npm scripts) + ↓ +create-pr (commits & creates PR) + ↓ +summary (always runs, shows results) + ↓ +cleanup (removes all artifacts if enabled) +``` + +### Global Configuration +The workflow uses environment variables for global settings: +- `CLEANUP_ARTIFACTS: true` - Enables automatic cleanup of intermediate artifacts after successful completion + +### Composite Actions +The workflow uses composite actions for code reusability: +- `.github/actions/sync-files/` - Handles all file synchronization types + +### Scripts Used +- `.github/actions/sync-files/sync.sh` - Core sync logic with file tracking and deletion support +- `.github/scripts/sync-artifact-files.sh` - Applies synced files to repository with rsync --delete +- `.github/scripts/aggregate-reports.sh` - Aggregates sync metrics from parallel jobs +- `.github/scripts/git-utils.sh` - Branch creation, commit, and push operations +- `.github/scripts/sanitize-config.sh` - Sanitizes config files (URLs and dev sections) +- `.github/scripts/sanitize-config.py` - Python script to remove node.dev sections +- `.github/scripts/version-utils.sh` - Version detection and validation +- `.github/scripts/doc-generator.sh` - Wrapper for npm documentation generation + +### Config File Sanitization +The config sync process includes automatic sanitization: +1. **URL Replacement**: Real URLs replaced with TODO placeholders +2. **Dev Section Removal**: `node.dev` sections stripped using Python script +3. **Comparison**: Only sanitized content is compared to detect actual changes + +### Branch Naming Convention +Sync branches follow the pattern: `docs/node/{version}` +- Example: `docs/node/v0.3.5` +- Version slashes are replaced with dashes for safety + +### Artifact Management +The workflow uses artifacts to pass data between jobs: +- `synced-{type}` - Individual sync results for each type (includes files and reports) +- `synced-merged` - All synced files and reports merged together +- `synced-final` - Final artifact with generated documentation and sync reports + +**Artifact Structure**: +- Each artifact contains: + - `sync_report_{type}.md` - Detailed report with file lists + - Synced files in their target directory structure + - `sync-reports/` directory in final artifact for reference + +**Deletion Handling**: +- Uses `rsync --delete` for each specific subdirectory to ensure proper file deletion +- Only affects synced directories (`content/validators/`, `pages/api-references/genlayer-node/`) +- Never deletes unrelated documentation content + +**Automatic Cleanup**: +- All artifacts are automatically deleted when `CLEANUP_ARTIFACTS: true` (default) +- Cleanup only runs after successful PR creation or summary generation + +### Pull Request Behavior +- Creates new PR for new versions +- Updates existing open PR for same version +- Automatically labels with "documentation" and "node" + +**PR Description includes**: +- Source repository and version +- API filter patterns used +- Total files changed with breakdown (added/updated/deleted) +- List of npm scripts that were run +- Automated checklist confirming successful sync + +### Workflow Summary +The summary job generates a comprehensive report in the GitHub Actions UI: +- **Overall Results**: Version and total change counts +- **Sync Results by Type**: For each sync type shows: + - Count of added/updated/deleted files + - Detailed file lists (e.g., "Added: gen_call.mdx") +- **Pull Request Link**: Direct link to created/updated PR + +### Documentation Generation Scripts +After syncing files, the workflow runs these npm scripts: +- `npm run node-generate-changelog` - Generates changelog page from synced files +- `npm run node-update-setup-guide` - Updates setup guide with version info +- `npm run node-update-config` - Processes configuration documentation +- `npm run node-generate-api-docs` - Generates API reference pages \ No newline at end of file diff --git a/.github/workflows/example-trigger-from-node.yml.example b/.github/workflows/example-trigger-from-node.yml.example index c2c3f50e..488e7800 100644 --- a/.github/workflows/example-trigger-from-node.yml.example +++ b/.github/workflows/example-trigger-from-node.yml.example @@ -9,8 +9,8 @@ on: branches: - main paths: - - 'docs/**' - - 'CHANGELOG.md' + - 'docs/**' # Includes changelog/, api/rpc/, api/ops/ + - 'configs/**' # Node configuration files workflow_dispatch: jobs: @@ -47,6 +47,7 @@ jobs: "changelog_path": "docs/changelog", "api_gen_path": "docs/api/rpc", "api_debug_path": "docs/api/rpc", + "api_ops_path": "docs/api/ops", "api_gen_regex": "gen_(?!dbg_).*", "api_debug_regex": "gen_dbg_.*" } diff --git a/.github/workflows/sync-docs-from-node.yml b/.github/workflows/sync-docs-from-node.yml index 12c45a58..ca73973a 100644 --- a/.github/workflows/sync-docs-from-node.yml +++ b/.github/workflows/sync-docs-from-node.yml @@ -9,6 +9,10 @@ on: description: 'Version/tag to sync from genlayer-node repo (e.g., v0.3.5, or "latest" to detect)' required: false default: 'latest' + changelog_path: + description: 'Path to changelog files in source repo' + required: false + default: 'docs/changelog' api_gen_path: description: 'Path to API gen files in source repo' required: false @@ -25,879 +29,549 @@ on: description: 'Regex pattern to filter API debug files (e.g., "gen_dbg_.*")' required: false default: 'gen_dbg_.*' + api_ops_path: + description: 'Path to API ops files in source repo' + required: false + default: 'docs/api/ops' + +# Global environment variables +env: + CLEANUP_ARTIFACTS: true + +# Prevent concurrent runs of the same workflow +concurrency: + group: sync-docs-${{ github.ref }}-${{ github.event.inputs.version || github.event.client_payload.version || 'latest' }} + cancel-in-progress: true jobs: - sync-and-create-pr: + prepare: + name: 'Determine Version' runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - + outputs: + version: ${{ steps.final_version.outputs.version }} steps: - - name: Checkout documentation repository + - name: Checkout 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 + - name: Extract version parameter + id: extract run: | - python3 -m pip install --upgrade pip - python3 -m pip install pyyaml + if [ "${{ github.event_name }}" = "repository_dispatch" ]; then + VERSION="${{ github.event.client_payload.version || 'latest' }}" + else + VERSION="${{ github.event.inputs.version || 'latest' }}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Requested version: $VERSION" - - name: Set up Git + - name: Detect latest version + id: detect + if: steps.extract.outputs.version == 'latest' + env: + GITHUB_TOKEN: ${{ secrets.NODE_REPO_TOKEN || secrets.GITHUB_TOKEN }} run: | - set -euo pipefail - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + source .github/scripts/version-utils.sh + LATEST_TAG=$(detect_latest_version "$GITHUB_TOKEN") + echo "Latest version detected: $LATEST_TAG" + echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT - - name: Extract sync parameters - id: params + - name: Set final version + id: final_version 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 + if [[ "${{ steps.extract.outputs.version }}" == "latest" ]]; then + VERSION="${{ steps.detect.outputs.version }}" 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 + VERSION="${{ steps.extract.outputs.version }}" fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "✅ Will sync version: $VERSION" - - name: Clone genlayer-node repository + sync-files: + name: 'Sync Files' + runs-on: ubuntu-latest + needs: prepare + strategy: + matrix: + sync_type: [changelog, config, api_gen, api_debug, api_ops] + fail-fast: false + steps: + - name: Checkout documentation repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Python dependencies + if: matrix.sync_type == 'config' + run: | + python3 -m pip install --upgrade pip pyyaml + + - name: Clone source 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 + fetch-depth: 1 sparse-checkout: | docs configs/node/config.yaml.example sparse-checkout-cone-mode: true path: source-repo + ref: ${{ needs.prepare.outputs.version }} - - 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 + - name: Set sync parameters + id: set_params 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" + case "${{ matrix.sync_type }}" in + "changelog") + echo "title=Changelog" >> $GITHUB_OUTPUT + echo "source_path=source-repo/${{ github.event.inputs.changelog_path || github.event.client_payload.changelog_path || 'docs/changelog' }}" >> $GITHUB_OUTPUT + echo "target_path=content/validators/changelog" >> $GITHUB_OUTPUT + echo "filter_pattern=.*" >> $GITHUB_OUTPUT + ;; + "config") + echo "title=Config File" >> $GITHUB_OUTPUT + echo "source_path=source-repo/configs/node/config.yaml.example" >> $GITHUB_OUTPUT + echo "target_path=content/validators/config.yaml" >> $GITHUB_OUTPUT + echo "filter_pattern=.*" >> $GITHUB_OUTPUT + echo "sanitize_script=.github/scripts/sanitize-config.sh" >> $GITHUB_OUTPUT + ;; + "api_gen") + echo "title=API Gen Methods" >> $GITHUB_OUTPUT + echo "source_path=source-repo/${{ github.event.inputs.api_gen_path || github.event.client_payload.api_gen_path || 'docs/api/rpc' }}" >> $GITHUB_OUTPUT + echo "target_path=pages/api-references/genlayer-node/gen" >> $GITHUB_OUTPUT + echo "filter_pattern=${{ github.event.inputs.api_gen_regex || github.event.client_payload.api_gen_regex || 'gen_(?!dbg_).*' }}" >> $GITHUB_OUTPUT + ;; + "api_debug") + echo "title=API Debug Methods" >> $GITHUB_OUTPUT + echo "source_path=source-repo/${{ github.event.inputs.api_debug_path || github.event.client_payload.api_debug_path || 'docs/api/rpc' }}" >> $GITHUB_OUTPUT + echo "target_path=pages/api-references/genlayer-node/debug" >> $GITHUB_OUTPUT + echo "filter_pattern=${{ github.event.inputs.api_debug_regex || github.event.client_payload.api_debug_regex || 'gen_dbg_.*' }}" >> $GITHUB_OUTPUT + ;; + "api_ops") + echo "title=API Ops Methods" >> $GITHUB_OUTPUT + echo "source_path=source-repo/${{ github.event.inputs.api_ops_path || github.event.client_payload.api_ops_path || 'docs/api/ops' }}" >> $GITHUB_OUTPUT + echo "target_path=pages/api-references/genlayer-node/ops" >> $GITHUB_OUTPUT + echo "filter_pattern=.*" >> $GITHUB_OUTPUT + ;; + esac + + - name: Sync files using composite action + id: sync + uses: ./.github/actions/sync-files + with: + type: ${{ matrix.sync_type }} + title: ${{ steps.set_params.outputs.title }} + source_path: ${{ steps.set_params.outputs.source_path }} + target_path: ${{ steps.set_params.outputs.target_path }} + filter_pattern: ${{ steps.set_params.outputs.filter_pattern }} + sanitize_script: ${{ steps.set_params.outputs.sanitize_script }} + + aggregate-results: + name: 'Aggregate Sync Results' + runs-on: ubuntu-latest + needs: [prepare, sync-files] + if: always() + outputs: + total_changes: ${{ steps.calculate.outputs.total_changes }} + total_added: ${{ steps.calculate.outputs.total_added }} + total_updated: ${{ steps.calculate.outputs.total_updated }} + total_deleted: ${{ steps.calculate.outputs.total_deleted }} + sync_reports: ${{ steps.calculate.outputs.all_reports }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download all sync artifacts + uses: actions/download-artifact@v4 + continue-on-error: true + with: + pattern: synced-* + merge-multiple: true + path: artifacts/ - - 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 + - name: Calculate totals and collect reports + id: calculate 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}" + # Move reports to sync-reports directory for the script + mkdir -p sync-reports + find artifacts -name "sync_report_*.md" -exec mv {} sync-reports/ \; - # 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 + # Run aggregation script + .github/scripts/aggregate-reports.sh - - name: Sync config.yaml file - id: sync_config + - name: Create merged artifact run: | - set -euo pipefail - SYNC_REPORT="${{ runner.temp }}/sync_report.md" - SOURCE_CONFIG="source-repo/configs/node/config.yaml.example" + # Create merged artifact structure + mkdir -p synced-merged - DEST_CONFIG="content/validators/config.yaml" - - echo "" >> $SYNC_REPORT - echo "## Config File Sync" >> $SYNC_REPORT - echo "" >> $SYNC_REPORT + # Copy all synced files (excluding reports) + find artifacts -type f ! -name "sync_report_*.md" | while read -r file; do + # Get relative path from artifacts/ + rel_path="${file#artifacts/}" + # Create directory structure and copy file + mkdir -p "synced-merged/$(dirname "$rel_path")" + cp "$file" "synced-merged/$rel_path" + done - # 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::" + # Copy aggregated reports + cp -r sync-reports synced-merged/ - # 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 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:" "$TEMP_CONFIG" && echo "WARNING: dev sections still present!" || echo "Good: No dev 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" || echo "No")" - 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 + echo "✅ Merged artifact created" - - 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: Upload merged synced files + uses: actions/upload-artifact@v4 + with: + name: synced-merged + path: synced-merged/ + retention-days: 1 - - 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 + generate-docs: + name: 'Generate Documentation' + runs-on: ubuntu-latest + needs: [prepare, aggregate-results] + if: always() && needs.aggregate-results.result != 'cancelled' + outputs: + generation_success: ${{ steps.generate.outputs.success }} + steps: + - name: Checkout documentation repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' - # 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 + - name: Install dependencies + run: npm install - # 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 + - name: Download merged synced files + if: needs.aggregate-results.result == 'success' + uses: actions/download-artifact@v4 + with: + name: synced-merged + path: temp-merged - # 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: Apply synced files + if: needs.aggregate-results.result == 'success' + run: | + # Sync all required paths in a single call + .github/scripts/sync-artifact-files.sh temp-merged . \ + "content/validators" \ + "pages/api-references/genlayer-node" - name: Run documentation generation scripts + id: generate 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 + .github/scripts/doc-generator.sh && echo "success=true" >> "$GITHUB_OUTPUT" - 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:" "$CONFIG_PATH" && echo "ERROR: Dev section found!" || echo "✓ No dev section" - 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 + - name: Copy sync reports for final artifact 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 + # Copy the sync-reports directory from temp-merged + if [[ -d "temp-merged/sync-reports" ]]; then + cp -r temp-merged/sync-reports . 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 + echo "Warning: No sync-reports found in temp-merged" fi - - name: Commit changes - if: steps.check_changes.outputs.has_changes == 'true' + - name: Upload final documentation + uses: actions/upload-artifact@v4 + if: steps.generate.outputs.success == 'true' + with: + name: synced-final + path: | + content/validators/ + pages/api-references/ + pages/validators/ + sync-reports/ + retention-days: 1 + + create-pr: + name: 'Create Pull Request' + runs-on: ubuntu-latest + needs: [prepare, aggregate-results, generate-docs] + if: always() && (needs.aggregate-results.result == 'success' || needs.generate-docs.result == 'success') + outputs: + pr_url: ${{ steps.create_pr.outputs.pr_url }} + 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 Git 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 }} + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" - - 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: Download final documentation + if: needs.generate-docs.result == 'success' + uses: actions/download-artifact@v4 + with: + name: synced-final + path: temp-final - - name: Read sync report - id: read_sync_report - if: steps.check_changes.outputs.has_changes == 'true' + - name: Apply final documentation + if: needs.generate-docs.result == 'success' 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<> $GITHUB_OUTPUT - echo "$SYNC_REPORT_CONTENT" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - - name: Push changes - if: steps.check_changes.outputs.has_changes == 'true' + # Sync all required paths in a single call + .github/scripts/sync-artifact-files.sh temp-final . \ + "content/validators" \ + "pages/validators" \ + "pages/api-references" + + - name: Check for changes and create branch + id: check_changes 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" + source .github/scripts/git-utils.sh + + if check_for_changes; then + BRANCH_NAME=$(create_sync_branch "${{ needs.prepare.outputs.version }}") + + # Use aggregated metrics from aggregate-results job + commit_and_push_changes \ + "${{ needs.prepare.outputs.version }}" \ + "${{ needs.aggregate-results.outputs.total_changes }}" \ + "${{ needs.aggregate-results.outputs.total_added }}" \ + "${{ needs.aggregate-results.outputs.total_updated }}" \ + "${{ needs.aggregate-results.outputs.total_deleted }}" \ + "$BRANCH_NAME" + else + echo "No changes to commit" + exit 0 + fi - name: Create Pull Request + id: create_pr if: steps.check_changes.outputs.has_changes == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - set -euo pipefail + # Get the branch name from git + BRANCH_NAME=$(git branch --show-current) + # Check if PR already exists for this branch - if PR_JSON=$(gh pr view "${{ env.BRANCH_NAME }}" --json url,state 2>/dev/null); then + if PR_JSON=$(gh pr view "$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" + echo "Open PR already exists for branch $BRANCH_NAME – updating PR description" + echo "Existing PR: $PR_URL" + UPDATE_EXISTING_PR=true else - echo "Closed PR exists for branch ${{ env.BRANCH_NAME }} (state: $PR_STATE)" + echo "Closed PR exists for branch $BRANCH_NAME (state: $PR_STATE)" echo "Creating new PR..." - # Continue with PR creation below - CREATE_PR=true + UPDATE_EXISTING_PR=false fi else - echo "No PR exists for branch ${{ env.BRANCH_NAME }}" - CREATE_PR=true + echo "No PR exists for branch $BRANCH_NAME" + UPDATE_EXISTING_PR=false 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' + # Create PR body file + PR_BODY_FILE="${{ runner.temp }}/pr_body.md" + cat >"$PR_BODY_FILE" <> $GITHUB_OUTPUT + echo "✅ PR updated: $PR_URL" + else + # Create new PR and capture URL + PR_URL=$(gh pr create \ + --title "docs: sync documentation from genlayer-node ${{ needs.prepare.outputs.version }}" \ --body-file "$PR_BODY_FILE" \ --label "documentation" \ --label "node" \ --base "main" \ - --head "${{ env.BRANCH_NAME }}" + --head "$BRANCH_NAME") + + echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT + echo "✅ PR created: $PR_URL" fi - - name: Summary + summary: + name: 'Workflow Summary' + runs-on: ubuntu-latest + needs: [prepare, aggregate-results, generate-docs, create-pr] + if: always() + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download final artifact with sync reports + uses: actions/download-artifact@v4 + continue-on-error: true + with: + name: synced-final + path: artifacts/ + + - name: Generate workflow 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 "# 📚 Documentation Sync Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + echo "## 📊 Overall Results" >> $GITHUB_STEP_SUMMARY + echo "- **Source Version:** \`${{ needs.prepare.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Total Changes:** ${{ needs.aggregate-results.outputs.total_changes }}" >> $GITHUB_STEP_SUMMARY + echo " - ➕ Added: ${{ needs.aggregate-results.outputs.total_added }} files" >> $GITHUB_STEP_SUMMARY + echo " - ✏️ Updated: ${{ needs.aggregate-results.outputs.total_updated }} files" >> $GITHUB_STEP_SUMMARY + echo " - ➖ Deleted: ${{ needs.aggregate-results.outputs.total_deleted }} files" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + echo "## 📁 Sync Results by Type" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Process each sync type report + for sync_type in changelog config api_gen api_debug api_ops; do + # Get proper title + case "$sync_type" in + "changelog") title="📝 Changelog Sync" ;; + "config") title="⚙️ Config File Sync" ;; + "api_gen") title="🔧 API Gen Methods Sync" ;; + "api_debug") title="🐛 API Debug Methods Sync" ;; + "api_ops") title="📊 API Ops Methods Sync" ;; + esac + + echo "### $title" >> $GITHUB_STEP_SUMMARY + + # Check if report exists (look in sync-reports directory) + if [[ -f "artifacts/sync-reports/sync_report_${sync_type}.md" ]]; then + # Extract summary line and file list from report + report_content=$(cat "artifacts/sync-reports/sync_report_${sync_type}.md") + + # Extract counts + added=$(echo "$report_content" | grep -o '\*\*Added\*\*: [0-9]\+' | grep -o '[0-9]\+' || echo "0") + updated=$(echo "$report_content" | grep -o '\*\*Updated\*\*: [0-9]\+' | grep -o '[0-9]\+' || echo "0") + deleted=$(echo "$report_content" | grep -o '\*\*Deleted\*\*: [0-9]\+' | grep -o '[0-9]\+' || echo "0") + total=$(echo "$report_content" | grep -o '\*\*Total changes\*\*: [0-9]\+' | grep -o '[0-9]\+' || echo "0") + + if [[ "$total" == "0" ]]; then + echo "No updates found" >> $GITHUB_STEP_SUMMARY + else + # Show counts + [[ "$added" != "0" ]] && echo "- **Added**: $added files" >> $GITHUB_STEP_SUMMARY + [[ "$updated" != "0" ]] && echo "- **Updated**: $updated files" >> $GITHUB_STEP_SUMMARY + [[ "$deleted" != "0" ]] && echo "- **Deleted**: $deleted files" >> $GITHUB_STEP_SUMMARY + + # Show file lists directly + if grep -q "### Added Files" "artifacts/sync-reports/sync_report_${sync_type}.md"; then + echo "" >> $GITHUB_STEP_SUMMARY + sed -n '/### Added Files/,/^###\|^$/p' "artifacts/sync-reports/sync_report_${sync_type}.md" | grep "^- " | sed 's/^- /- **Added:** /' >> $GITHUB_STEP_SUMMARY + fi + + if grep -q "### Updated Files" "artifacts/sync-reports/sync_report_${sync_type}.md"; then + echo "" >> $GITHUB_STEP_SUMMARY + sed -n '/### Updated Files/,/^###\|^$/p' "artifacts/sync-reports/sync_report_${sync_type}.md" | grep "^- " | sed 's/^- /- **Updated:** /' >> $GITHUB_STEP_SUMMARY + fi + + if grep -q "### Deleted Files" "artifacts/sync-reports/sync_report_${sync_type}.md"; then + echo "" >> $GITHUB_STEP_SUMMARY + sed -n '/### Deleted Files/,/^###\|^$/p' "artifacts/sync-reports/sync_report_${sync_type}.md" | grep "^- " | sed 's/^- /- **Deleted:** /' >> $GITHUB_STEP_SUMMARY + fi + fi + else + echo "No report available" >> $GITHUB_STEP_SUMMARY + fi echo "" >> $GITHUB_STEP_SUMMARY - echo "${{ steps.read_sync_report.outputs.content }}" >> $GITHUB_STEP_SUMMARY + done + + # Add PR link if created + if [[ "${{ needs.create-pr.outputs.pr_url }}" != "" ]]; then + echo "## ✅ Pull Request" >> $GITHUB_STEP_SUMMARY + echo "**PR Created:** ${{ needs.create-pr.outputs.pr_url }}" >> $GITHUB_STEP_SUMMARY else - echo "ℹ️ No documentation changes detected. No PR created." >> $GITHUB_STEP_SUMMARY + echo "## ℹ️ Result" >> $GITHUB_STEP_SUMMARY + if [[ "${{ needs.aggregate-results.outputs.total_changes }}" == "0" ]]; then + echo "No changes detected - no PR created" >> $GITHUB_STEP_SUMMARY + else + echo "Changes detected but PR creation failed or was skipped" >> $GITHUB_STEP_SUMMARY + fi + fi + + cleanup: + name: 'Cleanup Artifacts' + runs-on: ubuntu-latest + needs: [prepare, aggregate-results, generate-docs, create-pr, summary] + if: always() && (needs.create-pr.result == 'success' || needs.summary.result == 'success') + permissions: + actions: write + steps: + - name: Check cleanup configuration + id: check + run: | + if [[ "${{ env.CLEANUP_ARTIFACTS }}" == "true" ]]; then + echo "should_cleanup=true" >> $GITHUB_OUTPUT + echo "✅ Artifact cleanup is enabled" + else + echo "should_cleanup=false" >> $GITHUB_OUTPUT + echo "⏭️ Artifact cleanup is disabled (CLEANUP_ARTIFACTS=${{ env.CLEANUP_ARTIFACTS }})" fi + + - name: Build artifact list to delete + if: steps.check.outputs.should_cleanup == 'true' + id: artifacts + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Get ALL artifacts from this workflow run + ARTIFACTS_TO_DELETE=$(gh api \ + -H "Accept: application/vnd.github+json" \ + /repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts \ + --jq '.artifacts[].name' | \ + paste -sd '|' -) + + echo "artifacts_list<> $GITHUB_OUTPUT + echo "$ARTIFACTS_TO_DELETE" | tr '|' '\n' >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "Artifacts to delete: $ARTIFACTS_TO_DELETE" + + - name: Delete intermediate artifacts + if: steps.check.outputs.should_cleanup == 'true' && steps.artifacts.outputs.artifacts_list != '' + uses: geekyeggo/delete-artifact@v5 + with: + name: ${{ steps.artifacts.outputs.artifacts_list }} + failOnError: false \ No newline at end of file diff --git a/content/api-references/genlayer-node/content.mdx b/content/api-references/genlayer-node/content.mdx new file mode 100644 index 00000000..9d641012 --- /dev/null +++ b/content/api-references/genlayer-node/content.mdx @@ -0,0 +1,104 @@ +# GenLayer Node API + +The GenLayer Node provides a [JSON-RPC API](https://www.jsonrpc.org/specification) for interacting with it. This API allows you to execute contract calls, retrieve transaction information, and perform various blockchain operations. + +## GenLayer Methods + +${genMethods.join('\n\n')} + +## Debug Methods + +These methods are available for debugging and testing purposes during development. + +${debugMethods.join('\n\n')} + +## Ethereum Compatibility + +The GenLayer Node also supports Ethereum-compatible methods that are proxied to the underlying infrastructure. These methods follow the standard [Ethereum JSON-RPC specification](https://ethereum.org/en/developers/docs/apis/json-rpc/) and are prefixed with `eth_`. + +**Examples of supported Ethereum methods:** + +- `eth_blockNumber` +- `eth_getBalance` +- `eth_sendTransaction` +- `eth_call` +- And other standard Ethereum JSON-RPC methods + +## zkSync Compatibility + +[zkSync-compatible](https://docs.zksync.io/zksync-protocol/api/zks-rpc) methods are also supported and proxied to the underlying infrastructure. These methods are prefixed with `zksync_`. + +## Usage Examples + +### cURL + +```bash +# Test connectivity +curl -X POST http://localhost:9151 \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "gen_dbg_ping", + "params": [], + "id": 1 + }' + +# Execute a contract call +curl -X POST http://localhost:9151 \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "gen_call", + "params": [{ + "from": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6", + "to": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6", + "data": "0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d4c9db96c4b4d8b6", + "type": "read", + "transaction_hash_variant": "latest-nonfinal" + }], + "id": 1 + }' + +# Get contract schema +curl -X POST http://localhost:9151 \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "gen_getContractSchema", + "params": [{ + "code": "IyB7ICJEZXBlbmRzIjogInB5LWdlbmxheWVyOnRlc3QiIH0KCmZyb20gZ2VubGF5ZXIgaW1wb3J0ICoKCgojIGNvbnRyYWN0IGNsYXNzCmNsYXNzIFN0b3JhZ2UoZ2wuQ29udHJhY3QpOgogICAgc3RvcmFnZTogc3RyCgogICAgIyBjb25zdHJ1Y3RvcgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGluaXRpYWxfc3RvcmFnZTogc3RyKToKICAgICAgICBzZWxmLnN0b3JhZ2UgPSBpbml0aWFsX3N0b3JhZ2UKCiAgICAjIHJlYWQgbWV0aG9kcyBtdXN0IGJlIGFubm90YXRlZCB3aXRoIHZpZXcKICAgIEBnbC5wdWJsaWMudmlldwogICAgZGVmIGdldF9zdG9yYWdlKHNlbGYpIC0+IHN0cjoKICAgICAgICByZXR1cm4gc2VsZi5zdG9yYWdlCgogICAgIyB3cml0ZSBtZXRob2QKICAgIEBnbC5wdWJsaWMud3JpdGUKICAgIGRlZiB1cGRhdGVfc3RvcmFnZShzZWxmLCBuZXdfc3RvcmFnZTogc3RyKSAtPiBOb25lOgogICAgICAgIHNlbGYuc3RvcmFnZSA9IG5ld19zdG9yYWdlCg==" + }], + "id": 1 + }' + +# Get debug trie information +curl -X POST http://localhost:9151 \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "gen_dbg_trie", + "params": [{ + "txID": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6742d35Cc6634C0532925a3b8", + "round": 0 + }], + "id": 1 + }' + +# Get transaction receipt +curl -X POST http://localhost:9151 \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "gen_getTransactionReceipt", + "params": [{ + "txId": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49" + }], + "id": 1 + }' +``` + +## Ops Methods + +These methods provide operational endpoints for monitoring the GenLayer node. + +${opsMethods.join('\n\n')} \ No newline at end of file diff --git a/pages/api-references/genlayer-node.mdx b/pages/api-references/genlayer-node.mdx index 11580180..62fc810a 100644 --- a/pages/api-references/genlayer-node.mdx +++ b/pages/api-references/genlayer-node.mdx @@ -574,4 +574,4 @@ curl -X POST http://localhost:9151 \ }], "id": 1 }' -``` \ No newline at end of file +``` diff --git a/pages/api-references/genlayer-node/_meta.json b/pages/api-references/genlayer-node/_meta.json index 3f681c21..5560eff7 100644 --- a/pages/api-references/genlayer-node/_meta.json +++ b/pages/api-references/genlayer-node/_meta.json @@ -6,5 +6,9 @@ "debug": { "title": "Debug Methods", "type": "separator" + }, + "ops": { + "title": "Ops Methods", + "type": "separator" } } \ No newline at end of file diff --git a/pages/api-references/genlayer-node/ops/_meta.json b/pages/api-references/genlayer-node/ops/_meta.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/pages/api-references/genlayer-node/ops/_meta.json @@ -0,0 +1 @@ +{} diff --git a/scripts/generate-api-docs.js b/scripts/generate-api-docs.js index 3f6a36be..1b377c48 100644 --- a/scripts/generate-api-docs.js +++ b/scripts/generate-api-docs.js @@ -7,8 +7,10 @@ const path = require('path'); const CONFIG = { API_DIR: process.env.API_DOCS_DIR || 'pages/api-references/genlayer-node', TARGET_FILE: process.env.API_DOCS_TARGET || 'pages/api-references/genlayer-node.mdx', + TEMPLATE_FILE: process.env.API_TEMPLATE_FILE || 'content/api-references/genlayer-node/content.mdx', GEN_SUBDIR: process.env.API_GEN_SUBDIR || 'gen', - DEBUG_SUBDIR: process.env.API_DEBUG_SUBDIR || 'debug' + DEBUG_SUBDIR: process.env.API_DEBUG_SUBDIR || 'debug', + OPS_SUBDIR: process.env.API_OPS_SUBDIR || 'ops' }; /** @@ -123,109 +125,49 @@ function generateApiDocs() { console.warn(`Debug methods directory ${debugDir} does not exist - skipping`); } - // Generate the final API docs content - const apiContent = `# GenLayer Node API - -The GenLayer Node provides a [JSON-RPC API](https://www.jsonrpc.org/specification) for interacting with it. This API allows you to execute contract calls, retrieve transaction information, and perform various blockchain operations. - -## GenLayer Methods - -${genMethods.join('\n\n')} - -## Debug Methods - -These methods are available for debugging and testing purposes during development. - -${debugMethods.join('\n\n')} - -## Ethereum Compatibility - -The GenLayer Node also supports Ethereum-compatible methods that are proxied to the underlying infrastructure. These methods follow the standard [Ethereum JSON-RPC specification](https://ethereum.org/en/developers/docs/apis/json-rpc/) and are prefixed with \`eth_\`. - -**Examples of supported Ethereum methods:** - -- \`eth_blockNumber\` -- \`eth_getBalance\` -- \`eth_sendTransaction\` -- \`eth_call\` -- And other standard Ethereum JSON-RPC methods - -## zkSync Compatibility - -[zkSync-compatible](https://docs.zksync.io/zksync-protocol/api/zks-rpc) methods are also supported and proxied to the underlying infrastructure. These methods are prefixed with \`zksync_\`. - -## Usage Examples - -### cURL - -\`\`\`bash -# Test connectivity -curl -X POST http://localhost:9151 \\ - -H "Content-Type: application/json" \\ - -d '{ - "jsonrpc": "2.0", - "method": "gen_dbg_ping", - "params": [], - "id": 1 - }' - -# Execute a contract call -curl -X POST http://localhost:9151 \\ - -H "Content-Type: application/json" \\ - -d '{ - "jsonrpc": "2.0", - "method": "gen_call", - "params": [{ - "from": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6", - "to": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6", - "data": "0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d4c9db96c4b4d8b6", - "type": "read", - "transaction_hash_variant": "latest-nonfinal" - }], - "id": 1 - }' - -# Get contract schema -curl -X POST http://localhost:9151 \\ - -H "Content-Type: application/json" \\ - -d '{ - "jsonrpc": "2.0", - "method": "gen_getContractSchema", - "params": [{ - "code": "IyB7ICJEZXBlbmRzIjogInB5LWdlbmxheWVyOnRlc3QiIH0KCmZyb20gZ2VubGF5ZXIgaW1wb3J0ICoKCgojIGNvbnRyYWN0IGNsYXNzCmNsYXNzIFN0b3JhZ2UoZ2wuQ29udHJhY3QpOgogICAgc3RvcmFnZTogc3RyCgogICAgIyBjb25zdHJ1Y3RvcgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGluaXRpYWxfc3RvcmFnZTogc3RyKToKICAgICAgICBzZWxmLnN0b3JhZ2UgPSBpbml0aWFsX3N0b3JhZ2UKCiAgICAjIHJlYWQgbWV0aG9kcyBtdXN0IGJlIGFubm90YXRlZCB3aXRoIHZpZXcKICAgIEBnbC5wdWJsaWMudmlldwogICAgZGVmIGdldF9zdG9yYWdlKHNlbGYpIC0+IHN0cjoKICAgICAgICByZXR1cm4gc2VsZi5zdG9yYWdlCgogICAgIyB3cml0ZSBtZXRob2QKICAgIEBnbC5wdWJsaWMud3JpdGUKICAgIGRlZiB1cGRhdGVfc3RvcmFnZShzZWxmLCBuZXdfc3RvcmFnZTogc3RyKSAtPiBOb25lOgogICAgICAgIHNlbGYuc3RvcmFnZSA9IG5ld19zdG9yYWdlCg==" - }], - "id": 1 - }' - -# Get debug trie information -curl -X POST http://localhost:9151 \\ - -H "Content-Type: application/json" \\ - -d '{ - "jsonrpc": "2.0", - "method": "gen_dbg_trie", - "params": [{ - "txID": "0x742d35Cc6634C0532925a3b8D4C9db96c4b4d8b6742d35Cc6634C0532925a3b8", - "round": 0 - }], - "id": 1 - }' - -# Get transaction receipt -curl -X POST http://localhost:9151 \\ - -H "Content-Type: application/json" \\ - -d '{ - "jsonrpc": "2.0", - "method": "gen_getTransactionReceipt", - "params": [{ - "txId": "0x635060dd514082096d18c8eb64682cc6a944f9ce1ae6982febf7a71e9f656f49" - }], - "id": 1 - }' -\`\`\``; + // Read ops methods + const opsDir = path.join(apiDir, CONFIG.OPS_SUBDIR); + const opsMethods = []; + + if (fs.existsSync(opsDir)) { + console.log(`Processing ops methods from ${opsDir}`); + // Update _meta.json and get file order + const fileOrder = updateMetaJson(opsDir); + + // Read content for each file in order + for (const file of fileOrder) { + const filePath = path.join(opsDir, file); + if (fs.existsSync(filePath)) { + const content = fs.readFileSync(filePath, 'utf8').trim(); + opsMethods.push(content); + } + } + console.log(`Found ${opsMethods.length} ops methods`); + } else { + console.warn(`Ops methods directory ${opsDir} does not exist - skipping`); + } + + // Read the template file + const templateFile = path.join(process.cwd(), CONFIG.TEMPLATE_FILE); + + if (!fs.existsSync(templateFile)) { + console.error(`Template file ${templateFile} does not exist`); + return; + } + + let templateContent = fs.readFileSync(templateFile, 'utf8'); + console.log(`Using template from ${templateFile}`); + + // Process the template with method content using string replacement + // Replace template variables with actual content + let apiContent = templateContent + .replace('${genMethods.join(\'\\n\\n\')}', genMethods.join('\n\n')) + .replace('${debugMethods.join(\'\\n\\n\')}', debugMethods.join('\n\n')) + .replace('${opsMethods.join(\'\\n\\n\')}', opsMethods.join('\n\n')); // Write to the target file fs.writeFileSync(targetFile, apiContent); - console.log(`Generated API docs with ${genMethods.length} gen methods and ${debugMethods.length} debug methods at ${new Date().toISOString()}`); + console.log(`Generated API docs with ${genMethods.length} gen methods, ${debugMethods.length} debug methods, and ${opsMethods.length} ops methods at ${new Date().toISOString()}`); } // Run the script