Per-file module bindings: const X = @import(...) is scoped to its fil… #190
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Benchmarks | ||
| on: | ||
| push: | ||
| branches: [trunk] | ||
| schedule: | ||
| # Run every 15 minutes to ensure benchmark coverage | ||
| # This provides predictable resource usage and handles high commit velocity | ||
| - cron: '*/15 * * * *' | ||
| workflow_dispatch: | ||
| inputs: | ||
| iterations: | ||
| description: 'Number of benchmark iterations' | ||
| default: '5' | ||
| type: string | ||
| permissions: | ||
| contents: write # Needed to push to perf branch (used by collector job) | ||
| # Time-based batching: Cancel older queued runs when new ones arrive | ||
| # This handles high commit velocity by ensuring only the latest commit is benchmarked | ||
| # Scheduled runs every 15 minutes ensure coverage even during quiet periods | ||
| concurrency: | ||
| group: benchmarks | ||
| cancel-in-progress: true | ||
| jobs: | ||
| # Graceful degradation: Check queue depth and decide whether to run | ||
| # This prevents resource exhaustion during periods of high commit velocity | ||
| check-queue: | ||
| name: Check queue depth | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| should_run: ${{ steps.decide.outputs.should_run }} | ||
| queue_depth: ${{ steps.queue.outputs.queue_depth }} | ||
| reason: ${{ steps.decide.outputs.reason }} | ||
| steps: | ||
| - name: Check workflow queue depth | ||
| id: queue | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| // Get all workflow runs for this workflow in queued or in_progress state | ||
| const runs = await github.rest.actions.listWorkflowRuns({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| workflow_id: 'benchmarks.yml', | ||
| status: 'queued', | ||
| per_page: 100 | ||
| }); | ||
| const queueDepth = runs.data.workflow_runs.length; | ||
| core.setOutput('queue_depth', queueDepth); | ||
| core.info(`Current queue depth: ${queueDepth} queued runs`); | ||
| return queueDepth; | ||
| - name: Decide whether to run | ||
| id: decide | ||
| run: | | ||
| queue_depth="${{ steps.queue.outputs.queue_depth }}" | ||
| # Manual runs always proceed | ||
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | ||
| echo "should_run=true" >> $GITHUB_OUTPUT | ||
| echo "reason=Manual run always proceeds" >> $GITHUB_OUTPUT | ||
| echo "✓ Manual run - proceeding regardless of queue depth" | ||
| exit 0 | ||
| fi | ||
| # Adaptive sampling based on queue depth | ||
| if [ "$queue_depth" -gt 20 ]; then | ||
| echo "should_run=false" >> $GITHUB_OUTPUT | ||
| echo "reason=Queue depth ($queue_depth) exceeds threshold (20) - skipping to reduce backlog" >> $GITHUB_OUTPUT | ||
| echo "⚠️ Queue depth too high ($queue_depth > 20) - skipping this run" | ||
| elif [ "$queue_depth" -gt 10 ]; then | ||
| # For medium queue depth, only run scheduled jobs (skip push-triggered ones) | ||
| if [ "${{ github.event_name }}" = "schedule" ]; then | ||
| echo "should_run=true" >> $GITHUB_OUTPUT | ||
| echo "reason=Moderate queue depth ($queue_depth) - running scheduled job only" >> $GITHUB_OUTPUT | ||
| echo "✓ Scheduled run with moderate queue ($queue_depth) - proceeding" | ||
| else | ||
| echo "should_run=false" >> $GITHUB_OUTPUT | ||
| echo "reason=Queue depth ($queue_depth) exceeds threshold (10) - skipping push-triggered run, waiting for scheduled run" >> $GITHUB_OUTPUT | ||
| echo "⚠️ Moderate queue depth ($queue_depth > 10) - skipping push-triggered run" | ||
| fi | ||
| else | ||
| echo "should_run=true" >> $GITHUB_OUTPUT | ||
| echo "reason=Queue depth acceptable ($queue_depth <= 10)" >> $GITHUB_OUTPUT | ||
| echo "✓ Queue depth acceptable ($queue_depth) - proceeding" | ||
| fi | ||
| - name: Log skip decision | ||
| if: steps.decide.outputs.should_run == 'false' | ||
| run: | | ||
| echo "## ⚠️ Benchmark Run Skipped" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Reason:** ${{ steps.decide.outputs.reason }}" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Queue depth:** ${{ steps.queue.outputs.queue_depth }} queued runs" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Commit:** \`$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')\`" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "This run was skipped to prevent resource exhaustion during high commit velocity." >> $GITHUB_STEP_SUMMARY | ||
| echo "Scheduled runs every 15 minutes ensure benchmark coverage." >> $GITHUB_STEP_SUMMARY | ||
| benchmark: | ||
| needs: check-queue | ||
| if: needs.check-queue.outputs.should_run == 'true' | ||
| strategy: | ||
| fail-fast: false | ||
| # Parallel execution - no max-parallel constraint | ||
| # Each job uploads artifacts instead of pushing to perf branch | ||
| matrix: | ||
| include: | ||
| - runner: ubuntu-latest | ||
| platform: x86-64-linux | ||
| name: Linux x86-64 | ||
| - runner: ubuntu-24.04-arm | ||
| platform: aarch64-linux | ||
| name: Linux ARM64 | ||
| - runner: macos-latest | ||
| platform: aarch64-macos | ||
| name: macOS ARM64 | ||
| runs-on: ${{ matrix.runner }} | ||
| name: benchmark (${{ matrix.name }}) | ||
| steps: | ||
| - name: Checkout trunk | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: trunk | ||
| fetch-depth: 0 # Full history for commit info | ||
| - name: Install dotslash | ||
| uses: facebook/install-dotslash@latest | ||
| # Cache the dotslash-downloaded buck2 binary (~/.cache/dotslash on | ||
| # Linux, ~/Library/Caches/dotslash on macOS). The previous cache of | ||
| # buck-out/v2/gen + ~/.cache/buck2 was inert — OSS buck2 has no | ||
| # persistent action cache across daemon restarts, and ~/.cache/buck2 | ||
| # is not a path buck2 uses. (RUE-144) | ||
| - name: Cache dotslash artifacts | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| ~/.cache/dotslash | ||
| ~/Library/Caches/dotslash | ||
| key: dotslash-${{ matrix.platform }}-${{ hashFiles('buck2') }} | ||
| restore-keys: | | ||
| dotslash-${{ matrix.platform }}- | ||
| - name: Get commit info | ||
| id: commit | ||
| run: | | ||
| echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT | ||
| echo "full_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | ||
| - name: Run benchmarks | ||
| id: bench | ||
| run: | | ||
| iterations="${{ github.event.inputs.iterations || '5' }}" | ||
| echo "Running benchmarks with $iterations iterations on ${{ matrix.platform }}..." | ||
| ./bench.sh --iterations "$iterations" --output /tmp/results.json --no-history | ||
| # Extract summary for PR comment | ||
| echo "benchmark_results<<EOF" >> $GITHUB_OUTPUT | ||
| cat /tmp/results.json >> $GITHUB_OUTPUT | ||
| echo "EOF" >> $GITHUB_OUTPUT | ||
| - name: Validate benchmark results | ||
| run: | | ||
| echo "=== Benchmark Results ===" | ||
| cat /tmp/results.json | ||
| echo "" | ||
| echo "=== Validation ===" | ||
| python3 << 'EOF' | ||
| import json | ||
| import sys | ||
| with open('/tmp/results.json') as f: | ||
| data = json.load(f) | ||
| benchmarks = data.get('benchmarks', []) | ||
| print(f'Platform: ${{ matrix.platform }}') | ||
| print(f'Commit: {data.get("commit", "unknown")}') | ||
| print(f'Benchmarks collected: {len(benchmarks)}') | ||
| if not benchmarks: | ||
| print('::error::No benchmark results collected! All benchmarks failed.') | ||
| sys.exit(1) | ||
| for b in benchmarks: | ||
| name = b.get('name', 'unknown') | ||
| mean = b.get('mean_ms', 0) | ||
| print(f' - {name}: {mean:.2f}ms') | ||
| print('Validation passed!') | ||
| EOF | ||
| - name: Upload benchmark results as artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: benchmark-results-${{ steps.commit.outputs.full_sha }}-${{ matrix.platform }} | ||
| path: /tmp/results.json | ||
| retention-days: 90 | ||
| - name: Summary | ||
| run: | | ||
| echo "## Benchmark Results (${{ matrix.name }})" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Platform:** ${{ matrix.platform }}" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Commit:** \`${{ steps.commit.outputs.sha }}\`" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "### Results" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "\`\`\`json" >> $GITHUB_STEP_SUMMARY | ||
| cat /tmp/results.json >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | ||
| collect-results: | ||
| name: Collect and push results | ||
| needs: [check-queue, benchmark] | ||
| runs-on: ubuntu-latest | ||
| # Only run if benchmarks were executed and at least one succeeded | ||
| if: ${{ needs.check-queue.outputs.should_run == 'true' && !cancelled() && needs.benchmark.result != 'failure' }} | ||
| steps: | ||
| - name: Checkout trunk for scripts | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: trunk | ||
| path: trunk | ||
| - name: Get commit info and range | ||
| id: commit | ||
| run: | | ||
| cd trunk | ||
| echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT | ||
| echo "full_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | ||
| # Get all commits since the last benchmark run (or last 24 hours as fallback) | ||
| # This helps track which commits this benchmark represents | ||
| since_time=$(date -u -d '24 hours ago' '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date -u -v-24H '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "24 hours ago") | ||
| commit_range=$(git log --since="$since_time" --format="%H" --reverse | tr '\n' ',' | sed 's/,$//') | ||
| # Export for later use | ||
| echo "commit_range=$commit_range" >> $GITHUB_OUTPUT | ||
| # Count commits in range | ||
| commit_count=$(echo "$commit_range" | tr ',' '\n' | wc -l | tr -d ' ') | ||
| echo "commit_count=$commit_count" >> $GITHUB_OUTPUT | ||
| echo "Commit range: $commit_count commits in last 24h" | ||
| - name: Download all benchmark artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| path: artifacts | ||
| pattern: benchmark-results-${{ steps.commit.outputs.full_sha }}-* | ||
| merge-multiple: false | ||
| - name: List downloaded artifacts | ||
| run: | | ||
| echo "=== Downloaded Artifacts ===" | ||
| ls -R artifacts/ | ||
| echo "" | ||
| echo "=== Artifact Contents ===" | ||
| for dir in artifacts/benchmark-results-*; do | ||
| if [ -d "$dir" ]; then | ||
| echo "Directory: $dir" | ||
| cat "$dir/results.json" | head -20 | ||
| echo "" | ||
| fi | ||
| done | ||
| - name: Checkout perf branch | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: perf | ||
| path: perf-branch | ||
| fetch-depth: 1 | ||
| continue-on-error: true | ||
| - name: Initialize perf branch if needed | ||
| run: | | ||
| if [ ! -d "perf-branch" ]; then | ||
| mkdir -p perf-branch | ||
| cd perf-branch | ||
| git init | ||
| git checkout -b perf | ||
| mkdir -p benchmarks | ||
| echo "# Performance History" > README.md | ||
| echo "" >> README.md | ||
| echo "This branch stores benchmark history for the Rue compiler." >> README.md | ||
| echo "See [Performance Dashboard](https://rue-lang.dev/performance/) for visualization." >> README.md | ||
| fi | ||
| - name: Append results to history | ||
| run: | | ||
| # Determine benchmark reason based on trigger | ||
| if [ "${{ github.event_name }}" = "schedule" ]; then | ||
| benchmark_reason="scheduled" | ||
| elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | ||
| benchmark_reason="manual" | ||
| else | ||
| benchmark_reason="push" | ||
| fi | ||
| # Process each platform's results | ||
| for artifact_dir in artifacts/benchmark-results-*; do | ||
| if [ ! -d "$artifact_dir" ]; then | ||
| continue | ||
| fi | ||
| # Extract platform from directory name | ||
| # Format: benchmark-results-{sha}-{platform} | ||
| platform=$(basename "$artifact_dir" | sed 's/benchmark-results-.*-//') | ||
| results_file="$artifact_dir/results.json" | ||
| if [ ! -f "$results_file" ]; then | ||
| echo "::warning::No results.json found in $artifact_dir" | ||
| continue | ||
| fi | ||
| echo "Processing results for platform: $platform" | ||
| # Enhance results with commit range metadata (convert to version 2 schema) | ||
| enhanced_file="$artifact_dir/results_enhanced.json" | ||
| python3 << EOF | ||
| import json | ||
| import sys | ||
| with open('$results_file') as f: | ||
| data = json.load(f) | ||
| data['version'] = 2 | ||
| data['benchmark_reason'] = '$benchmark_reason' | ||
| commit_range_str = '${{ steps.commit.outputs.commit_range }}' | ||
| if commit_range_str: | ||
| data['commit_range'] = [c.strip() for c in commit_range_str.split(',') if c.strip()] | ||
| else: | ||
| data['commit_range'] = ['${{ steps.commit.outputs.full_sha }}'] | ||
| with open('$enhanced_file', 'w') as f: | ||
| json.dump(data, f, indent=2) | ||
| EOF | ||
| # Create platform-specific history file if it doesn't exist | ||
| history_file="perf-branch/benchmarks/history-$platform.json" | ||
| if [ ! -f "$history_file" ]; then | ||
| mkdir -p perf-branch/benchmarks | ||
| echo '{"version": 1, "runs": []}' > "$history_file" | ||
| fi | ||
| # Append enhanced results to history using the Python script | ||
| if ! python3 trunk/scripts/append-benchmark.py "$enhanced_file" "$history_file"; then | ||
| echo "::error::Failed to append benchmark results for $platform" | ||
| exit 1 | ||
| fi | ||
| echo "✓ Appended results for $platform (covering ${{ steps.commit.outputs.commit_count }} commits)" | ||
| done | ||
| - name: Commit and push to perf branch | ||
| run: | | ||
| cd perf-branch | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git add benchmarks/ | ||
| # Only commit if there are changes | ||
| if git diff --staged --quiet; then | ||
| echo "No changes to commit" | ||
| else | ||
| # Count how many platforms were updated | ||
| platform_count=$(git diff --staged --name-only | wc -l) | ||
| git commit -m "Benchmark results for ${{ steps.commit.outputs.sha }} ($platform_count platforms)" | ||
| # Push to perf branch (create if doesn't exist) | ||
| # Only this job pushes, so no race conditions | ||
| git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git HEAD:perf | ||
| - name: Summary | ||
| run: | | ||
| echo "## Benchmark Collection Complete" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "**Commit:** \`${{ steps.commit.outputs.sha }}\`" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "### Platforms Collected" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| for artifact_dir in artifacts/benchmark-results-*; do | ||
| if [ -d "$artifact_dir" ]; then | ||
| platform=$(basename "$artifact_dir" | sed 's/benchmark-results-.*-//') | ||
| echo "- ✓ $platform" >> $GITHUB_STEP_SUMMARY | ||
| fi | ||
| done | ||