Skip to content

fix: add per-command timeouts and CI optimizations #34

fix: add per-command timeouts and CI optimizations

fix: add per-command timeouts and CI optimizations #34

Workflow file for this run

name: Package Manager Benchmarks
on:
push:
workflow_dispatch:
inputs:
fixtures:
description: "The fixture to run the benchmarks on"
default: '["next", "astro", "vue", "svelte", "large", "babylon"]'
variations:
description: "The benchmark variations to run"
default: '["clean", "node_modules", "cache", "cache+node_modules", "cache+lockfile", "cache+lockfile+node_modules", "lockfile", "lockfile+node_modules", "registry-clean", "registry-lockfile"]'
binaries:
description: "The binaries to run the benchmarks on"
default: '"npm,yarn,berry,zpm,pnpm,vlt,bun,deno,nx,turbo,node"'
warmup:
description: "The number of warmup runs on each benchmark"
default: "2"
runs:
description: "The number of runs on each benchmark"
default: "5"
schedule:
# GitHub Actions cron uses UTC. 10:17 UTC ~= 2:17 AM Pacific (PST).
- cron: "17 10 * * *"
# Prevent multiple runs from interfering with each other
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-bust
cancel-in-progress: true
jobs:
detect-changes:
name: "Detect Changes"
runs-on: ubuntu-24.04-arm
outputs:
app_only: ${{ steps.check.outputs.app_only }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check changed files
id: check
run: |
# For scheduled runs and manual triggers, always run benchmarks
if [[ "${{ github.event_name }}" != "push" ]]; then
echo "app_only=false" >> "$GITHUB_OUTPUT"
echo "Non-push event (${{ github.event_name }}), running full benchmarks"
exit 0
fi
# Get the list of changed files between this push and the previous commit
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
if [ -z "$CHANGED_FILES" ]; then
echo "app_only=false" >> "$GITHUB_OUTPUT"
echo "Could not determine changed files, running full benchmarks"
exit 0
fi
echo "Changed files:"
echo "$CHANGED_FILES"
# Check if ALL changed files are under app/ (or non-benchmark files like README, .editorconfig)
APP_ONLY=true
while IFS= read -r file; do
case "$file" in
app/*|README.md|.editorconfig|.cursor/*|.github/workflows/*)
# App or non-benchmark files — safe to skip benchmarks
;;
*)
# Benchmark-relevant file changed
APP_ONLY=false
echo "Benchmark-relevant file changed: $file"
break
;;
esac
done <<< "$CHANGED_FILES"
echo "app_only=$APP_ONLY" >> "$GITHUB_OUTPUT"
echo "App-only changes: $APP_ONLY"
benchmark:
name: "Run Benchmarks"
runs-on: ubuntu-24.04-arm
needs: [detect-changes]
if: needs.detect-changes.outputs.app_only != 'true'
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
fixture: ${{ fromJson((github.event_name == 'push' && github.ref != 'refs/heads/main') && '["next", "astro", "vue", "svelte"]' || (inputs.fixtures || '["next", "astro", "vue", "svelte", "large", "babylon"]')) }}
variation: ${{ fromJson(inputs.variations || '["clean", "node_modules", "cache", "cache+node_modules", "cache+lockfile", "cache+lockfile+node_modules", "lockfile", "lockfile+node_modules", "registry-clean", "registry-lockfile"]') }}
include:
- variation: "run"
fixture: "run"
permissions:
contents: read
id-token: write
env:
BENCH_INCLUDE: ${{ fromJson(inputs.binaries || '"npm,yarn,berry,zpm,pnpm,vlt,bun,deno,nx,turbo,node"') }}
BENCH_WARMUP: ${{ (github.event_name == 'push' && github.ref != 'refs/heads/main') && '1' || (inputs.warmup || '2') }}
BENCH_RUNS: ${{ (github.event_name == 'push' && github.ref != 'refs/heads/main') && '1' || (inputs.runs || '5') }}
steps:
- uses: actions/checkout@v6
- name: Install Node
uses: actions/setup-node@v6
with:
node-version: '24'
package-manager-cache: false
- name: Install & Setup Tools
run: |
bash ./scripts/setup.sh
- name: Configure AWS Credentials
if: startsWith(matrix.variation, 'registry-')
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Get CodeArtifact Token
if: startsWith(matrix.variation, 'registry-')
id: aws
run: |
CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
--domain vlt \
--domain-owner 451504312483 \
--region us-east-1 \
--query authorizationToken \
--output text)
echo "::add-mask::$CODEARTIFACT_AUTH_TOKEN"
echo "token=$CODEARTIFACT_AUTH_TOKEN" >> "$GITHUB_OUTPUT"
- name: Tune settings for heavy fixtures
id: tune
run: |
# Heavy fixtures (large, babylon) get reduced warmup/runs to keep
# individual jobs under the timeout while still producing useful data.
case "${{ matrix.fixture }}" in
large|babylon)
echo "warmup=1" >> "$GITHUB_OUTPUT"
echo "runs=3" >> "$GITHUB_OUTPUT"
echo "Heavy fixture detected — using warmup=1, runs=3"
;;
*)
echo "warmup=$BENCH_WARMUP" >> "$GITHUB_OUTPUT"
echo "runs=$BENCH_RUNS" >> "$GITHUB_OUTPUT"
;;
esac
- name: Run Benchmarks variations
env:
CODEARTIFACT_AUTH_TOKEN: ${{ steps.aws.outputs.token }}
VLT_REGISTRY_AUTH_TOKEN: ${{ secrets.VLT_REGISTRY_AUTH_TOKEN }}
CLOUDSMITH_REGISTRY: ${{ secrets.CLOUDSMITH_REGISTRY }}
CLOUDSMITH_AUTH_TOKEN: ${{ secrets.CLOUDSMITH_AUTH_TOKEN }}
BENCH_WARMUP: ${{ steps.tune.outputs.warmup }}
BENCH_RUNS: ${{ steps.tune.outputs.runs }}
run: |
./bench run \
--fixtures="${{ matrix.fixture }}" \
--variation="${{ matrix.variation }}"
- name: Upload Benchmark Results
uses: actions/upload-artifact@v7
with:
name: results-${{ matrix.fixture }}-${{ matrix.variation }}
path: ./results/${{ matrix.fixture }}/${{ matrix.variation }}/
retention-days: 7
- name: Upload Versions Info
uses: actions/upload-artifact@v7
with:
name: versions-${{ matrix.fixture }}-${{ matrix.variation }}
path: ./results/versions.json
retention-days: 7
process:
name: "Process Results"
runs-on: ubuntu-24.04-arm
needs: [detect-changes, benchmark]
if: needs.detect-changes.outputs.app_only != 'true'
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
- name: Install Node
uses: actions/setup-node@v6
with:
node-version: "24"
- name: Download Results
uses: actions/download-artifact@v8
with:
path: results
pattern: results-*
- name: Download Versions
uses: actions/download-artifact@v8
with:
path: versions-temp
pattern: versions-*
- name: Process Results
run: |
./bench process
- name: Install vlt
run: |
npm install -g vlt@latest
- name: Build Charts View
run: |
pushd app
vlt install || true
vlt run build
popd
- name: Upload Processed Results
uses: actions/upload-artifact@v7
with:
name: results
path: results/
retention-days: 7
deploy:
name: "Deploy Results"
runs-on: ubuntu-24.04-arm
needs: [detect-changes, process]
permissions:
contents: write
if: github.ref == 'refs/heads/main' && needs.detect-changes.outputs.app_only != 'true'
steps:
- uses: actions/checkout@v6
- name: Download Results
uses: actions/download-artifact@v8
with:
name: results
path: results/
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: results
keep_files: true
deploy-app:
name: "Deploy App Only"
runs-on: ubuntu-24.04-arm
needs: [detect-changes]
permissions:
contents: write
if: github.ref == 'refs/heads/main' && needs.detect-changes.outputs.app_only == 'true'
steps:
- uses: actions/checkout@v6
- name: Install Node
uses: actions/setup-node@v6
with:
node-version: "24"
- name: Checkout gh-pages data
uses: actions/checkout@v6
with:
ref: gh-pages
path: gh-pages-data
- name: Install vlt
run: |
npm install -g vlt@latest
- name: Build App
run: |
pushd app
vlt install || true
vlt run build
popd
- name: Prepare deploy directory
run: |
# Copy the latest data files from gh-pages into the built results
cp -r gh-pages-data/latest results/latest
# Copy index.html and assets from the fresh build are already in results/
# The deploy action with keep_files merges with existing gh-pages content
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: results
keep_files: true