mirror of
https://github.com/vercel/eve.git
synced 2026-09-20 05:35:39 +08:00
39662ad702
Signed-off-by: Andrew Barba <barba@hey.com>
403 lines
18 KiB
YAML
403 lines
18 KiB
YAML
name: Bundle Analysis
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
pull_request:
|
|
types:
|
|
- opened
|
|
- reopened
|
|
- synchronize
|
|
- labeled
|
|
paths:
|
|
- ".github/workflows/bundle-analysis.yml"
|
|
- "e2e/fixtures/agent-tools-sandbox/**"
|
|
- "apps/fixtures/weather-agent/**"
|
|
- "package.json"
|
|
- "packages/eve/**"
|
|
- "pnpm-lock.yaml"
|
|
- "pnpm-workspace.yaml"
|
|
- "scripts/init-install-report.mjs"
|
|
- "scripts/build-profile-report.mjs"
|
|
- "scripts/nitro-bundle-report-compare.mjs"
|
|
- "scripts/nitro-bundle-report-budget.mjs"
|
|
- "scripts/nitro-bundle-report.mjs"
|
|
- "scripts/package-publish-report.mjs"
|
|
|
|
concurrency:
|
|
group: weather-agent-bundle-analysis-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
analyze:
|
|
if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.fork == false && (github.event.action != 'labeled' || github.event.label.name == 'acknowledge-bundle-warning')) }}
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
pull-requests: write
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
BUNDLE_WARNING_ACK_LABEL: acknowledge-bundle-warning
|
|
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
|
|
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
|
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
|
|
VERCEL_USE_EXPERIMENTAL_FRAMEWORKS: "1"
|
|
|
|
steps:
|
|
- name: Resolve workflow mode
|
|
id: mode
|
|
env:
|
|
EVENT_ACTION: ${{ github.event.action }}
|
|
EVENT_LABEL_NAME: ${{ github.event.label.name }}
|
|
PR_HAS_ACK_LABEL: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'acknowledge-bundle-warning') }}
|
|
run: |
|
|
if [ "$EVENT_ACTION" = "labeled" ] && [ "$EVENT_LABEL_NAME" = "$BUNDLE_WARNING_ACK_LABEL" ]; then
|
|
echo "acknowledge_only=true" >> "$GITHUB_OUTPUT"
|
|
echo "acknowledged=true" >> "$GITHUB_OUTPUT"
|
|
echo "Bundle warning acknowledged by label; skipping regeneration." >> "$GITHUB_STEP_SUMMARY"
|
|
else
|
|
echo "acknowledge_only=false" >> "$GITHUB_OUTPUT"
|
|
if [ "$EVENT_ACTION" != "synchronize" ] && [ "$PR_HAS_ACK_LABEL" = "true" ]; then
|
|
echo "acknowledged=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "acknowledged=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
fi
|
|
|
|
- name: Clear stale bundle warning acknowledgement
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' && github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'acknowledge-bundle-warning') }}
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
with:
|
|
script: |
|
|
try {
|
|
await github.rest.issues.removeLabel({
|
|
issue_number: context.issue.number,
|
|
name: process.env.BUNDLE_WARNING_ACK_LABEL,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
});
|
|
} catch (error) {
|
|
if (error.status === 404) {
|
|
return;
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
await core.summary
|
|
.addRaw(
|
|
`Removed \`${process.env.BUNDLE_WARNING_ACK_LABEL}\` so this commit must be acknowledged separately.`,
|
|
)
|
|
.write();
|
|
|
|
- name: Checkout code
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
|
|
- name: Setup pnpm
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
|
|
|
- name: Setup Node.js
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version-file: .nvmrc
|
|
cache: "pnpm"
|
|
|
|
- name: Install dependencies
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Prepare pull request base worktree
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' }}
|
|
id: baseline
|
|
env:
|
|
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
run: |
|
|
BASELINE_ROOT="$RUNNER_TEMP/eve-pr-base"
|
|
rm -rf "$BASELINE_ROOT"
|
|
git worktree add --detach "$BASELINE_ROOT" "$BASE_SHA"
|
|
echo "root=$BASELINE_ROOT" >> "$GITHUB_OUTPUT"
|
|
echo "label=${BASE_REF} (${BASE_SHA:0:7})" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Install pull request base dependencies
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' }}
|
|
working-directory: ${{ steps.baseline.outputs.root }}
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Build eve package on pull request base
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' }}
|
|
working-directory: ${{ steps.baseline.outputs.root }}
|
|
run: pnpm --filter eve build
|
|
|
|
- name: Resolve weather app on pull request base
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' }}
|
|
id: baseline_weather_app
|
|
working-directory: ${{ steps.baseline.outputs.root }}
|
|
run: |
|
|
if [ -d apps/fixtures/weather-agent ]; then
|
|
echo "filter=weather-agent" >> "$GITHUB_OUTPUT"
|
|
echo "path=apps/fixtures/weather-agent" >> "$GITHUB_OUTPUT"
|
|
elif [ -d apps/fixtures/weather-fixture ]; then
|
|
echo "filter=weather-fixture" >> "$GITHUB_OUTPUT"
|
|
echo "path=apps/fixtures/weather-fixture" >> "$GITHUB_OUTPUT"
|
|
elif [ -d apps/weather-agent ]; then
|
|
echo "filter=weather-agent" >> "$GITHUB_OUTPUT"
|
|
echo "path=apps/weather-agent" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "Could not find weather app in baseline checkout." >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: Build weather app on pull request base for size analysis
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' }}
|
|
id: baseline_build
|
|
continue-on-error: true
|
|
working-directory: ${{ steps.baseline.outputs.root }}
|
|
env:
|
|
STEPS_BASELINE_WEATHER_APP_OUTPUTS_FILTER: ${{ steps.baseline_weather_app.outputs.filter }}
|
|
run: >-
|
|
VERCEL=1 pnpm --filter "${STEPS_BASELINE_WEATHER_APP_OUTPUTS_FILTER}" \
|
|
exec eve build --skip-sandbox-prewarm
|
|
|
|
- name: Note skipped pull request base size analysis
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' && steps.baseline_build.outcome != 'success' }}
|
|
run: |
|
|
echo "Pull request base build failed; bundle analysis will run without a baseline comparison." >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- name: Generate pull request base bundle report
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' && steps.baseline_build.outcome == 'success' }}
|
|
env:
|
|
BASELINE_REPORT_JSON: ${{ runner.temp }}/weather-agent-bundle-report-base.json
|
|
STEPS_BASELINE_WEATHER_APP_OUTPUTS_PATH: ${{ steps.baseline_weather_app.outputs.path }}
|
|
working-directory: ${{ steps.baseline.outputs.root }}
|
|
run: |
|
|
node ./scripts/nitro-bundle-report.mjs \
|
|
--app "${STEPS_BASELINE_WEATHER_APP_OUTPUTS_PATH}" \
|
|
--app-label apps/fixtures/weather-agent \
|
|
--package packages/eve \
|
|
--package-label packages/eve \
|
|
--output-json "$BASELINE_REPORT_JSON"
|
|
|
|
- name: Build sandbox benchmark on pull request base in Vercel mode
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' }}
|
|
id: baseline_sandbox_build
|
|
continue-on-error: true
|
|
working-directory: ${{ steps.baseline.outputs.root }}
|
|
env:
|
|
BASELINE_SANDBOX_BUILD_PROFILE: ${{ runner.temp }}/sandbox-build-profile-base.json
|
|
run: |
|
|
token_args=()
|
|
if [ -n "${VERCEL_TOKEN:-}" ]; then
|
|
token_args=(--token "$VERCEL_TOKEN")
|
|
fi
|
|
team_args=()
|
|
if [ -n "${VERCEL_ORG_ID:-}" ]; then
|
|
team_args=(--team "$VERCEL_ORG_ID")
|
|
fi
|
|
|
|
pnpm --filter agent-tools-sandbox exec vc link --yes \
|
|
--project "$VERCEL_PROJECT_ID" "${team_args[@]}" "${token_args[@]}"
|
|
pnpm --filter agent-tools-sandbox exec vc env pull --yes --environment=preview \
|
|
"${token_args[@]}"
|
|
|
|
profile_args=()
|
|
if pnpm --filter agent-tools-sandbox exec eve build --help | grep -Fq -- "--profile"; then
|
|
profile_args=(--profile "$BASELINE_SANDBOX_BUILD_PROFILE")
|
|
else
|
|
echo "Pull request base predates eve build --profile; build timing will report the current build only." >> "$GITHUB_STEP_SUMMARY"
|
|
fi
|
|
|
|
# Sandbox templates key on VERCEL_PROJECT_ID at build and runtime.
|
|
# Do not add VERCEL_TEAM_ID: runtime does not receive it.
|
|
VERCEL=1 \
|
|
VERCEL_ENV=preview \
|
|
VERCEL_TARGET_ENV=preview \
|
|
VERCEL_PROJECT_ID="$VERCEL_PROJECT_ID" \
|
|
pnpm --filter agent-tools-sandbox exec eve build "${profile_args[@]}"
|
|
|
|
- name: Note skipped sandbox benchmark base
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' && steps.baseline_sandbox_build.outcome != 'success' }}
|
|
run: |
|
|
echo "Pull request base sandbox benchmark failed; build timing will run without a baseline comparison." >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- name: Build eve package
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
run: pnpm --filter eve build
|
|
|
|
- name: Build weather-agent for size analysis
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
run: >-
|
|
VERCEL=1 pnpm --filter weather-agent
|
|
exec eve build --skip-sandbox-prewarm
|
|
|
|
- name: Build sandbox benchmark in Vercel mode
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
env:
|
|
SANDBOX_BUILD_PROFILE: ${{ runner.temp }}/sandbox-build-profile.json
|
|
run: |
|
|
token_args=()
|
|
if [ -n "${VERCEL_TOKEN:-}" ]; then
|
|
token_args=(--token "$VERCEL_TOKEN")
|
|
fi
|
|
team_args=()
|
|
if [ -n "${VERCEL_ORG_ID:-}" ]; then
|
|
team_args=(--team "$VERCEL_ORG_ID")
|
|
fi
|
|
|
|
pnpm --filter agent-tools-sandbox exec vc link --yes \
|
|
--project "$VERCEL_PROJECT_ID" "${team_args[@]}" "${token_args[@]}"
|
|
pnpm --filter agent-tools-sandbox exec vc env pull --yes --environment=preview \
|
|
"${token_args[@]}"
|
|
|
|
# Sandbox templates key on VERCEL_PROJECT_ID at build and runtime.
|
|
# Do not add VERCEL_TEAM_ID: runtime does not receive it.
|
|
VERCEL=1 \
|
|
VERCEL_ENV=preview \
|
|
VERCEL_TARGET_ENV=preview \
|
|
VERCEL_PROJECT_ID="$VERCEL_PROJECT_ID" \
|
|
pnpm --filter agent-tools-sandbox exec eve build --profile "$SANDBOX_BUILD_PROFILE"
|
|
|
|
- name: Generate bundle report
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
env:
|
|
BASELINE_REPORT_JSON: ${{ runner.temp }}/weather-agent-bundle-report-base.json
|
|
BASELINE_REPORT_LABEL: ${{ steps.baseline.outputs.label }}
|
|
HAS_BASELINE_REPORT: ${{ github.event_name == 'pull_request' && steps.baseline_build.outcome == 'success' }}
|
|
BUNDLE_REPORT_JSON: ${{ runner.temp }}/weather-agent-bundle-report.json
|
|
BUNDLE_REPORT_MARKDOWN: ${{ runner.temp }}/weather-agent-bundle-report.md
|
|
BUNDLE_WARNING_ACKNOWLEDGED: ${{ steps.mode.outputs.acknowledged }}
|
|
run: |
|
|
report_args=()
|
|
if [ "$HAS_BASELINE_REPORT" = "true" ]; then
|
|
report_args=(
|
|
--baseline-json "$BASELINE_REPORT_JSON"
|
|
--baseline-label "$BASELINE_REPORT_LABEL"
|
|
)
|
|
fi
|
|
if [ "$BUNDLE_WARNING_ACKNOWLEDGED" = "true" ]; then
|
|
report_args+=(--size-budget-acknowledged)
|
|
fi
|
|
|
|
node ./scripts/nitro-bundle-report.mjs \
|
|
--app apps/fixtures/weather-agent \
|
|
--app-label apps/fixtures/weather-agent \
|
|
--package packages/eve \
|
|
--package-label packages/eve \
|
|
"${report_args[@]}" \
|
|
--output-json "$BUNDLE_REPORT_JSON" \
|
|
--output-markdown "$BUNDLE_REPORT_MARKDOWN"
|
|
|
|
- name: Generate build timing report
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
env:
|
|
BASELINE_BUILD_PROFILE: ${{ runner.temp }}/sandbox-build-profile-base.json
|
|
BASELINE_BUILD_PROFILE_LABEL: ${{ steps.baseline.outputs.label }}
|
|
BUILD_PROFILE: ${{ runner.temp }}/sandbox-build-profile.json
|
|
BUILD_PROFILE_REPORT_JSON: ${{ runner.temp }}/sandbox-build-profile-report.json
|
|
BUILD_PROFILE_REPORT_MARKDOWN: ${{ runner.temp }}/sandbox-build-profile-report.md
|
|
HAS_BASELINE_BUILD_PROFILE: ${{ github.event_name == 'pull_request' && steps.baseline_sandbox_build.outcome == 'success' }}
|
|
run: |
|
|
report_args=()
|
|
if [ "$HAS_BASELINE_BUILD_PROFILE" = "true" ] && [ -f "$BASELINE_BUILD_PROFILE" ]; then
|
|
report_args=(
|
|
--baseline-profile "$BASELINE_BUILD_PROFILE"
|
|
--baseline-label "$BASELINE_BUILD_PROFILE_LABEL"
|
|
)
|
|
fi
|
|
|
|
node ./scripts/build-profile-report.mjs \
|
|
--profile "$BUILD_PROFILE" \
|
|
--app-label e2e/fixtures/agent-tools-sandbox \
|
|
--sandbox-prewarm included \
|
|
--require-phase sandbox.prewarm \
|
|
"${report_args[@]}" \
|
|
--output-json "$BUILD_PROFILE_REPORT_JSON" \
|
|
--output-markdown "$BUILD_PROFILE_REPORT_MARKDOWN"
|
|
|
|
- name: Combine bundle and timing reports
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
env:
|
|
ANALYSIS_REPORT_MARKDOWN: ${{ runner.temp }}/weather-agent-analysis-report.md
|
|
BUILD_PROFILE_REPORT_MARKDOWN: ${{ runner.temp }}/sandbox-build-profile-report.md
|
|
BUNDLE_REPORT_MARKDOWN: ${{ runner.temp }}/weather-agent-bundle-report.md
|
|
run: |
|
|
cat "$BUNDLE_REPORT_MARKDOWN" > "$ANALYSIS_REPORT_MARKDOWN"
|
|
printf '\n\n' >> "$ANALYSIS_REPORT_MARKDOWN"
|
|
cat "$BUILD_PROFILE_REPORT_MARKDOWN" >> "$ANALYSIS_REPORT_MARKDOWN"
|
|
|
|
- name: Publish job summary
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
env:
|
|
ANALYSIS_REPORT_MARKDOWN: ${{ runner.temp }}/weather-agent-analysis-report.md
|
|
run: cat "$ANALYSIS_REPORT_MARKDOWN" >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- name: Upload bundle report artifact
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' }}
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: weather-agent-bundle-report
|
|
path: |
|
|
${{ runner.temp }}/weather-agent-bundle-report.json
|
|
${{ runner.temp }}/weather-agent-bundle-report.md
|
|
${{ runner.temp }}/sandbox-build-profile*.json
|
|
${{ runner.temp }}/sandbox-build-profile*.md
|
|
${{ runner.temp }}/weather-agent-analysis-report.md
|
|
|
|
- name: Update pull request comment
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' }}
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
env:
|
|
ANALYSIS_REPORT_MARKDOWN: ${{ runner.temp }}/weather-agent-analysis-report.md
|
|
with:
|
|
script: |
|
|
const fs = require("node:fs");
|
|
|
|
const marker = "<!-- eve-weather-agent-bundle-analysis -->";
|
|
const reportBody = fs.readFileSync(process.env.ANALYSIS_REPORT_MARKDOWN, "utf8").trim();
|
|
const body = `${marker}\n${reportBody}`;
|
|
|
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
issue_number: context.issue.number,
|
|
owner: context.repo.owner,
|
|
per_page: 100,
|
|
repo: context.repo.repo,
|
|
});
|
|
|
|
const existingComment = comments.find(
|
|
(comment) => comment.user?.type === "Bot" && comment.body?.includes(marker),
|
|
);
|
|
|
|
if (existingComment) {
|
|
await github.rest.issues.updateComment({
|
|
body,
|
|
comment_id: existingComment.id,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
});
|
|
return;
|
|
}
|
|
|
|
await github.rest.issues.createComment({
|
|
body,
|
|
issue_number: context.issue.number,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
});
|
|
|
|
- name: Enforce bundle size budget
|
|
if: ${{ steps.mode.outputs.acknowledge_only != 'true' && github.event_name == 'pull_request' }}
|
|
env:
|
|
BUNDLE_REPORT_JSON: ${{ runner.temp }}/weather-agent-bundle-report.json
|
|
BUNDLE_WARNING_ACKNOWLEDGED: ${{ steps.mode.outputs.acknowledged }}
|
|
run: |
|
|
budget_args=(--report-json "$BUNDLE_REPORT_JSON")
|
|
if [ "$BUNDLE_WARNING_ACKNOWLEDGED" = "true" ]; then
|
|
budget_args+=(--acknowledged)
|
|
fi
|
|
|
|
node ./scripts/nitro-bundle-report-budget.mjs "${budget_args[@]}"
|