refactor: migrate branch status pages from Vercel to Cloudflare Pages

Replace Vercel deployment with Cloudflare Pages using the same wrangler
deploy pattern as Storybook and Playwright CI workflows.

- Delete vercel.json, trigger-vercel-rebuild.yml, fetch-branch-artifacts.sh
- Add deploy-branch-status.yml with wrangler pages deploy
- Simplify build-pages.sh (no artifact fetching needed)
- Revert unrelated CI workflow changes

URLs: https://<branch>.comfyui-frontend-reports.pages.dev

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
snomiao
2026-03-17 04:37:54 +00:00
parent 604d9e70f6
commit 7c0bdf5af7
12 changed files with 296 additions and 659 deletions

View File

@@ -1,12 +1,12 @@
# Description: End-to-end testing with Playwright across multiple browsers, deploys test reports to Cloudflare Pages
name: 'CI: Tests E2E'
name: "CI: Tests E2E"
description: "End-to-end testing with Playwright across multiple browsers, deploys test reports to Cloudflare Pages"
on:
push:
branches: [main, master, core/*, desktop/*, sno-deploy-ghpage]
branches: [main, master, core/*, desktop/*]
pull_request:
branches-ignore: [wip/*, draft/*, temp/*]
workflow_dispatch:
branches-ignore:
[wip/*, draft/*, temp/*, vue-nodes-migration, sno-playwright-*]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -15,56 +15,65 @@ concurrency:
jobs:
setup:
runs-on: ubuntu-latest
outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v5
# Setup Test Environment, build frontend but do not start server yet
- name: Setup ComfyUI server
uses: ./.github/actions/setup-comfyui-server
- name: Setup frontend
uses: ./.github/actions/setup-frontend
with:
include_build_step: true
- name: Setup Playwright
uses: ./.github/actions/setup-playwright # Setup Playwright and cache browsers
# Upload only built dist/ (containerized test jobs will pnpm install without cache)
- name: Upload built frontend
uses: actions/upload-artifact@v6
# Save the entire workspace as cache for later test jobs to restore
- name: Generate cache key
id: cache-key
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
- name: Save cache
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
with:
name: frontend-dist
path: dist/
retention-days: 1
path: .
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
# Sharded chromium tests
playwright-tests-chromium-sharded:
needs: setup
runs-on: ubuntu-latest
timeout-minutes: 60
container:
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.12
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: read
packages: read
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
shardTotal: [8]
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Download built frontend
uses: actions/download-artifact@v7
# download built frontend repo from setup job
- name: Wait for cache propagation
run: sleep 10
- name: Restore cached setup
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with:
name: frontend-dist
path: dist/
fail-on-cache-miss: true
path: .
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Start ComfyUI server
uses: ./.github/actions/start-comfyui-server
# Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
- name: Setup ComfyUI server
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup nodejs, pnpm, reuse built frontend
uses: ./.github/actions/setup-frontend
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Install frontend deps
run: pnpm install --frozen-lockfile
# Run sharded tests (browsers pre-installed in container)
# Run sharded tests and upload sharded reports
- name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
id: playwright
run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
@@ -72,7 +81,7 @@ jobs:
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
- name: Upload blob report
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: blob-report-chromium-${{ matrix.shardIndex }}
@@ -84,70 +93,69 @@ jobs:
timeout-minutes: 15
needs: setup
runs-on: ubuntu-latest
container:
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.12
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: read
packages: read
strategy:
fail-fast: false
matrix:
browser: [chromium-2x, chromium-0.5x, mobile-chrome]
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Download built frontend
uses: actions/download-artifact@v7
# download built frontend repo from setup job
- name: Wait for cache propagation
run: sleep 10
- name: Restore cached setup
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with:
name: frontend-dist
path: dist/
fail-on-cache-miss: true
path: .
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Start ComfyUI server
uses: ./.github/actions/start-comfyui-server
# Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
- name: Setup ComfyUI server
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup nodejs, pnpm, reuse built frontend
uses: ./.github/actions/setup-frontend
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Install frontend deps
run: pnpm install --frozen-lockfile
# Run tests (browsers pre-installed in container)
# Run tests and upload reports
- name: Run Playwright tests (${{ matrix.browser }})
id: playwright
run: pnpm exec playwright test --project=${{ matrix.browser }} --reporter=blob
env:
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
- name: Generate HTML and JSON reports
if: always()
run: |
# Generate HTML report from blob
pnpm exec playwright merge-reports --reporter=html ./blob-report
# Generate JSON report separately with explicit output path
# Run tests with both HTML and JSON reporters
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
pnpm exec playwright merge-reports --reporter=json ./blob-report
pnpm exec playwright test --project=${{ matrix.browser }} \
--reporter=list \
--reporter=html \
--reporter=json
- name: Upload Playwright report
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-${{ matrix.browser }}
path: ./playwright-report/
retention-days: 30
# Merge sharded test reports (no container needed - only runs CLI)
# Merge sharded test reports
merge-reports:
needs: [playwright-tests-chromium-sharded]
runs-on: ubuntu-latest
if: ${{ !cancelled() }}
steps:
- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
with:
version: 10
- name: Checkout repository
uses: actions/checkout@v5
# Setup Test Environment, we only need playwright to merge reports
- name: Setup frontend
uses: ./.github/actions/setup-frontend
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Download blob reports
uses: actions/download-artifact@v7
uses: actions/download-artifact@v4
with:
path: ./all-blob-reports
pattern: blob-report-chromium-*
@@ -156,13 +164,13 @@ jobs:
- name: Merge into HTML Report
run: |
# Generate HTML report
pnpm dlx @playwright/test merge-reports --reporter=html ./all-blob-reports
pnpm exec playwright merge-reports --reporter=html ./all-blob-reports
# Generate JSON report separately with explicit output path
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
pnpm dlx @playwright/test merge-reports --reporter=json ./all-blob-reports
pnpm exec playwright merge-reports --reporter=json ./all-blob-reports
- name: Upload HTML report
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: playwright-report-chromium
path: ./playwright-report/
@@ -180,7 +188,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Get start time
id: start-time
@@ -207,10 +215,10 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Download all playwright reports
uses: actions/download-artifact@v7
uses: actions/download-artifact@v4
with:
pattern: playwright-report-*
path: reports
@@ -220,7 +228,6 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GITHUB_TOKEN: ${{ github.token }}
GITHUB_SHA: ${{ github.event.pull_request.head.sha }}
run: |
bash ./scripts/cicd/pr-playwright-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \

View File

@@ -1,13 +1,10 @@
name: Storybook and Chromatic CI
# - [Automate Chromatic with GitHub Actions • Chromatic docs]( https://www.chromatic.com/docs/github-actions/ )
name: "CI: Tests Storybook"
description: "Builds Storybook and runs visual regression testing via Chromatic, deploys previews to Cloudflare Pages"
on:
workflow_dispatch: # Allow manual triggering
pull_request:
branches: [main, sno-deploy-ghpage]
push:
branches: [sno-deploy-ghpage]
branches: [main]
jobs:
# Post starting comment for non-forked PRs
@@ -19,7 +16,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Post starting comment
env:
GITHUB_TOKEN: ${{ github.token }}
@@ -34,7 +31,7 @@ jobs:
# Build Storybook for all PRs (free Cloudflare deployment)
storybook-build:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || github.event_name == 'push'
if: github.event_name == 'pull_request'
outputs:
conclusion: ${{ steps.job-status.outputs.conclusion }}
workflow-url: ${{ steps.workflow-url.outputs.url }}
@@ -53,19 +50,6 @@ jobs:
node-version: '20'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
storybook-static
tsconfig.tsbuildinfo
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
restore-keys: |
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
storybook-cache-${{ runner.os }}-
storybook-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -118,19 +102,6 @@ jobs:
node-version: '20'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
storybook-static
tsconfig.tsbuildinfo
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
restore-keys: |
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
storybook-cache-${{ runner.os }}-
storybook-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -167,17 +138,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Download Storybook build
if: needs.storybook-build.outputs.conclusion == 'success'
uses: actions/download-artifact@v4
with:
name: storybook-static
path: storybook-static
- name: Make deployment script executable
run: chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
- name: Deploy Storybook and comment on PR
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
@@ -205,25 +176,25 @@ jobs:
script: |
const buildUrl = '${{ needs.chromatic-deployment.outputs.chromatic-build-url }}';
const storybookUrl = '${{ needs.chromatic-deployment.outputs.chromatic-storybook-url }}';
// Find the existing Storybook comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.pull_request.number }}
});
const storybookComment = comments.find(comment =>
const storybookComment = comments.find(comment =>
comment.body.includes('<!-- STORYBOOK_BUILD_STATUS -->')
);
if (storybookComment && buildUrl && storybookUrl) {
// Append Chromatic info to existing comment
const updatedBody = storybookComment.body.replace(
/---\n(.*)$/s,
`---\n### 🎨 Chromatic Visual Tests\n- 📊 [View Chromatic Build](${buildUrl})\n- 📚 [View Chromatic Storybook](${storybookUrl})\n\n$1`
);
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,

View File

@@ -0,0 +1,122 @@
name: 'Deploy: Branch Status Pages'
on:
workflow_run:
workflows:
- 'CI: Tests Storybook'
- 'CI: Tests E2E'
types:
- completed
branches-ignore:
- main
push:
branches: [main]
workflow_dispatch:
concurrency:
group: deploy-branch-status-${{ github.event.workflow_run.head_branch || github.ref_name }}
cancel-in-progress: true
jobs:
deploy:
name: Deploy to Cloudflare Pages
runs-on: ubuntu-latest
if: |
github.event_name == 'workflow_dispatch' ||
github.event_name == 'push' ||
(github.event.workflow_run.conclusion == 'success')
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
- name: Setup frontend
uses: ./.github/actions/setup-frontend
- name: Build status pages
run: pnpm pages:build
- name: Install wrangler
run: npm install -g wrangler@^4.0.0
- name: Deploy to Cloudflare Pages
id: deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
BRANCH="${{ github.event.workflow_run.head_branch || github.ref_name }}"
CF_BRANCH=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')
echo "Deploying branch status pages for: $BRANCH (cf: $CF_BRANCH)"
OUTPUT=$(wrangler pages deploy .pages-dist \
--project-name="comfyui-frontend-reports" \
--branch="$CF_BRANCH" 2>&1) || {
echo "Deployment failed: $OUTPUT"
exit 1
}
URL=$(echo "$OUTPUT" | grep -oE 'https://[a-zA-Z0-9.-]+\.pages\.dev\S*' | head -1)
URL="${URL:-https://${CF_BRANCH}.comfyui-frontend-reports.pages.dev}"
echo "url=$URL" >> "$GITHUB_OUTPUT"
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
echo "Deployed to: $URL"
- name: Find PR number
id: pr
if: github.event_name == 'workflow_run'
uses: actions/github-script@v7
with:
script: |
const branch = '${{ steps.deploy.outputs.branch }}';
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:${branch}`,
state: 'open'
});
return prs.length > 0 ? prs[0].number : null;
- name: Comment on PR
if: steps.pr.outputs.result && steps.pr.outputs.result != 'null'
uses: actions/github-script@v7
with:
script: |
const marker = '<!-- BRANCH_STATUS_DEPLOY -->';
const prNumber = ${{ steps.pr.outputs.result }};
const url = '${{ steps.deploy.outputs.url }}';
const body = `${marker}
**Branch Status Page** deployed: ${url}
Includes: Storybook links, Nx graph, Knip report, test report links`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber
});
const existing = comments.find(c => c.body.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body
});
}

View File

@@ -1,134 +0,0 @@
name: Trigger Vercel Rebuild
# Trigger when all important CI workflows complete
on:
workflow_run:
workflows:
- "Storybook and Chromatic CI"
- "Tests CI"
types:
- completed
branches-ignore:
- main
jobs:
check-and-trigger:
name: Trigger Vercel Rebuild
runs-on: ubuntu-latest
# Only run on PRs and only on success
if: |
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
steps:
- name: Get PR branch
id: pr
uses: actions/github-script@v7
with:
script: |
const { data: pullRequests } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`,
state: 'open'
});
if (pullRequests.length === 0) {
console.log('No open PR found for this branch');
return null;
}
return pullRequests[0].number;
- name: Check all required workflows completed
id: check
uses: actions/github-script@v7
with:
script: |
if (!${{ steps.pr.outputs.result }}) {
console.log('No PR found, skipping');
return false;
}
const requiredWorkflows = [
'Storybook and Chromatic CI',
'Tests CI'
];
const { data: workflowRuns } = await github.rest.actions.listWorkflowRunsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
branch: context.payload.workflow_run.head_branch,
per_page: 100
});
// Get the latest run for each required workflow
const latestRuns = {};
for (const run of workflowRuns.workflow_runs) {
const workflowName = run.name;
if (requiredWorkflows.includes(workflowName)) {
if (!latestRuns[workflowName] || run.created_at > latestRuns[workflowName].created_at) {
latestRuns[workflowName] = run;
}
}
}
// Check if all required workflows have completed successfully
const allComplete = requiredWorkflows.every(name => {
const run = latestRuns[name];
return run && run.conclusion === 'success';
});
console.log('Workflow status:');
requiredWorkflows.forEach(name => {
const run = latestRuns[name];
console.log(` ${name}: ${run ? run.conclusion : 'not found'}`);
});
console.log(`All workflows complete: ${allComplete}`);
return allComplete;
- name: Trigger Vercel Deploy Hook
if: steps.check.outputs.result == 'true'
run: |
echo "🚀 All CI workflows completed successfully"
echo "📦 Triggering Vercel rebuild with fresh artifacts..."
curl -X POST "${{ secrets.VERCEL_DEPLOY_HOOK_URL }}" \
-H "Content-Type: application/json" \
-d '{
"ref": "${{ github.event.workflow_run.head_branch }}",
"reason": "All CI workflows completed"
}'
echo "✅ Vercel rebuild triggered"
- name: Comment on PR
if: steps.check.outputs.result == 'true' && steps.pr.outputs.result
uses: actions/github-script@v7
with:
script: |
const prNumber = ${{ steps.pr.outputs.result }};
const branch = '${{ github.event.workflow_run.head_branch }}';
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: `🔄 **Vercel Rebuild Triggered**
All CI workflows completed successfully. Vercel is rebuilding with fresh artifacts:
- ✅ Storybook
- ✅ E2E Test Reports
- ✅ Vitest Reports
Your branch status page will update shortly with the latest test results.
Preview URL: \`https://${branch}-comfyui-frontend-reports.vercel.app\`
`
});
- name: Skip rebuild
if: steps.check.outputs.result != 'true'
run: |
echo "⏭️ Skipping Vercel rebuild - not all workflows complete yet"

5
.gitignore vendored
View File

@@ -96,8 +96,3 @@ vitest.config.*.timestamp*
# Generated reports in .pages (exclude generated, keep HTML templates)
/.pages/*/**/*
/.pages-dist/
# Fetched artifacts for Vercel builds
/.page/
.vercel

View File

@@ -1,153 +1,54 @@
# Branch Status Pages
This directory contains the source for the branch status pages that aggregate development tools and test reports.
This directory contains the source for branch status pages deployed to **Cloudflare Pages**.
## Deployment
The branch status pages are automatically deployed to **Vercel** on every push to any branch.
### Architecture
## Architecture
```
Push to Branch
├─→ GitHub Actions (runs tests, deploys artifacts)
├─→ Storybook → Cloudflare Pages
├─→ E2E Tests → GitHub Actions Artifacts
└─→ Vitest → GitHub Actions Artifacts
└─→ Vercel (auto-triggered)
Runs: pnpm pages:build:branch-status
1. Fetches test results from deployed sources
2. Builds status pages
3. Deploys automatically
|
+-> GitHub Actions (runs tests, deploys artifacts)
| +-> Storybook -> comfy-storybook.pages.dev
| +-> E2E Tests -> comfyui-playwright-*.pages.dev
| +-> Branch Status -> comfyui-frontend-reports.pages.dev
|
+-> deploy-branch-status.yml
|
Runs: pnpm pages:build
|
Deploys to Cloudflare Pages via wrangler
```
### URLs
## URLs
- **Production** (main branch): `https://comfyui-frontend-reports.vercel.app`
- **Preview** (PR branches): `https://<branch>-comfyui-frontend-reports.vercel.app`
- **Production** (main branch): `https://comfyui-frontend-reports.pages.dev`
- **Preview** (PR branches): `https://<branch>.comfyui-frontend-reports.pages.dev`
## What's Included
The branch status page aggregates:
1. **Storybook** - Component library documentation
2. **Nx Dependency Graph** - Project structure visualization
3. **Playwright Reports** - E2E test results
4. **Vitest Reports** - Unit test results
5. **Coverage Report** - Code coverage metrics
6. **Knip Report** - Unused code and dependency analysis
## How It Works
### Artifact Fetching
The `scripts/fetch-branch-artifacts.sh` script fetches test results from:
- **Storybook**: Deployed to Cloudflare Pages (fetches URL from PR comments)
- **E2E/Vitest**: Downloaded from GitHub Actions artifacts using `gh` CLI
- **Knip**: Generated fresh during build (fast)
### Graceful Fallbacks
If artifacts aren't available yet (CI still running), the build script creates placeholder pages with loading indicators. This allows Vercel to deploy immediately without waiting for all CI to complete.
1. **Storybook** - Links to Cloudflare Pages deployment
2. **Nx Dependency Graph** - Generated during build
3. **Playwright Reports** - Links to Cloudflare Pages deployments
4. **Vitest Reports** - Links to CI artifacts
5. **Coverage Report** - Links to CI artifacts
6. **Knip Report** - Generated fresh during build
## Local Development
```bash
# Develop the index page
pnpm pages:dev
# Build without fetching artifacts (uses local builds)
pnpm pages:build
# Build with artifact fetching (simulates Vercel)
pnpm pages:build:branch-status
pnpm pages:dev # Dev server
pnpm pages:build # Build
```
## Environment Variables (Vercel)
## Secrets
Configure these in Vercel project settings:
- `GITHUB_TOKEN` - For fetching artifacts via GitHub API (required)
- `CLOUDFLARE_ACCOUNT_ID` - For Cloudflare API (optional)
- `CLOUDFLARE_API_TOKEN` - For Cloudflare API (optional)
Required in GitHub Actions:
- `CLOUDFLARE_API_TOKEN`
- `CLOUDFLARE_ACCOUNT_ID`
## Files
- **index.html** - Main landing page with links to all reports
- **vite.config.ts** - Vite configuration for building the static site
- **knip.html** - Wrapper for displaying Knip markdown report
- **playwright-reports.html** - Wrapper for E2E test reports
## Adding New Reports
To add a new report to the status page:
1. Update `scripts/fetch-branch-artifacts.sh` to fetch the new artifact
2. Update `scripts/build-pages.sh` to process and copy it
3. Add a link in `index.html`
4. Optionally create a wrapper HTML file for custom styling
## Troubleshooting
### Artifacts not appearing
- Check Vercel build logs for fetch errors
- Verify `GITHUB_TOKEN` is set in Vercel environment
- Ensure GitHub Actions workflows completed successfully
- Artifacts may not be available for old commits (7-day retention)
### Slow builds
- Artifact fetching is designed to be fast (<30s)
- If builds are slow, check network connectivity to GitHub/Cloudflare
- Consider skipping slow-to-generate reports (like coverage)
### Storybook redirect not working
- Verify Cloudflare Pages deployment succeeded
- Check PR comments for correct Storybook URL
- Fallback: Storybook will show placeholder page
## CI Integration
No GitHub Actions workflow is needed for Vercel deployments. Vercel automatically:
1. Detects new commits via GitHub webhook
2. Triggers build with `pnpm pages:build:branch-status`
3. Deploys to branch-specific URL
4. Comments on PR with deployment URL
## Migration Notes
This replaces the previous GitHub Pages deployment approach which:
- ❌ Required complex artifact passing between GitHub Actions and Vercel
- ❌ Had to wait for all CI to complete before deploying
- ❌ Was prone to failure due to artifact extraction issues
The new Vercel-native approach:
- ✅ Deploys immediately on every push
- ✅ Fetches artifacts on-demand during build
- ✅ Shows placeholders for pending CI
- ✅ Simpler, more reliable architecture
## Primary Use Case: Development Team
This deployment provides:
1. **For Design Team**: Consistent, bookmarkable URL to reference the latest component system state
2. **For Developers**: Quick access to test results and coverage for any branch
3. **For PR Reviews**: Easy verification of Storybook changes and test results
## Related Documentation
- [Vercel Documentation](https://vercel.com/docs)
- [Storybook Documentation](https://storybook.js.org/docs)
- [Nx Documentation](https://nx.dev)
- [Vitest Documentation](https://vitest.dev)
- [Knip Documentation](https://knip.dev)
- [Playwright Documentation](https://playwright.dev)
- **index.html** - Landing page with links to all reports
- **vite.config.ts** - Vite config for building the static site
- **knip.html** - Knip markdown report viewer
- **playwright-reports.html** - E2E test report viewer

View File

@@ -10,8 +10,13 @@ const discoverHtmlEntries = () => {
const topLevel = resolve(rootDir, 'index.html')
if (fs.existsSync(topLevel)) entries.set('index', topLevel)
// Directories with pre-built assets (e.g. nx-graph) are copied directly
// in build-pages.sh to avoid CSS minification issues
const skipDirs = new Set(['nx-graph'])
for (const dirent of fs.readdirSync(rootDir, { withFileTypes: true })) {
if (!dirent.isDirectory() || dirent.name.startsWith('.')) continue
if (skipDirs.has(dirent.name)) continue
const candidate = resolve(rootDir, dirent.name, 'index.html')
if (fs.existsSync(candidate)) entries.set(dirent.name, candidate)
}
@@ -21,7 +26,7 @@ const discoverHtmlEntries = () => {
export default defineConfig({
root: rootDir,
base: '/ComfyUI_frontend/',
base: '/',
appType: 'mpa',
logLevel: 'info',
publicDir: false,

View File

@@ -15,15 +15,11 @@ const config: KnipConfig = {
'packages/tailwind-utils': {
project: ['src/**/*.{js,ts}']
},
'packages/design-system': {
entry: ['src/**/*.ts'],
project: ['src/**/*.{js,ts}', '*.{js,ts,mts}']
},
'packages/registry-types': {
project: ['src/**/*.{js,ts}']
}
},
ignoreBinaries: ['python3'],
ignoreBinaries: ['python3', 'gh'],
ignoreDependencies: [
// Weird importmap things
'@iconify/json',
@@ -31,11 +27,10 @@ const config: KnipConfig = {
'@primeuix/styled',
'@primeuix/utils',
'@primevue/icons',
// Dev
'@trivago/prettier-plugin-sort-imports',
// 3D and collaboration libraries
'three',
'@types/three',
// Unused but kept for potential future use
'@sparkjsdev/spark',
'wwobjloader2',
'@iconify-json/lucide',
'yjs'
],
ignore: [
@@ -54,7 +49,22 @@ const config: KnipConfig = {
'src/renderer/extensions/vueNodes/composables/slotLinkDragContext.ts',
'src/types/spatialIndex.ts',
'src/lib/litegraph/src/litegraph.ts',
'src/utils/vintageClipboard.ts'
'src/utils/vintageClipboard.ts',
'src/platform/auth/session/useSessionCookie.ts',
'src/platform/support/config.ts',
'src/renderer/utils/nodeTypeGuards.ts',
'src/schemas/nodeDefSchema.ts',
'src/scripts/defaultGraph.ts',
'src/scripts/ui.ts',
'src/scripts/widgets.ts',
'src/services/litegraphService.ts',
'src/storybook/mocks/useJobActions.ts',
'src/storybook/mocks/useJobList.ts',
'src/utils/executableGroupNodeDto.ts',
'src/utils/litegraphUtil.ts',
'src/lib/litegraph/src/LGraph.ts',
'src/schemas/apiSchema.ts',
'src/schemas/nodeDef/nodeDefSchemaV2.ts'
],
compilers: {
// https://github.com/webpro-nl/knip/issues/1008#issuecomment-3207756199

View File

@@ -12,7 +12,6 @@
"build-storybook": "storybook build",
"build:types": "nx build --config vite.types.config.mts && node scripts/prepare-types.js",
"build:analyze": "cross-env ANALYZE_BUNDLE=true pnpm build",
"build:branch-status": "...",
"build": "cross-env NODE_OPTIONS='--max-old-space-size=8192' pnpm typecheck && nx build",
"size:collect": "node scripts/size-collect.js",
"size:report": "node scripts/size-report.js",
@@ -37,8 +36,7 @@
"lint:desktop": "nx run @comfyorg/desktop-ui:lint",
"locale": "lobe-i18n locale",
"oxlint": "oxlint src --type-aware",
"pages:build": "bash scripts/build-pages.sh && vite build --config ./.pages/vite.config.ts",
"pages:build:branch-status": "bash scripts/fetch-branch-artifacts.sh && bash scripts/build-pages.sh && vite build --config ./.pages/vite.config.ts",
"pages:build": "bash scripts/build-pages.sh && vite build --config ./.pages/vite.config.ts && cp -r .pages/nx-graph .pages-dist/nx-graph 2>/dev/null || true",
"pages:dev": "vite --config ./.pages/vite.config.ts",
"preinstall": "pnpm dlx only-allow pnpm",
"prepare": "husky || true && git config blame.ignoreRevsFile .git-blame-ignore-revs || true",

View File

@@ -43,24 +43,11 @@ create_placeholder() {
max-width: 600px;
}
h1 { margin-top: 0; }
.spinner {
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid white;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 2rem auto;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="container">
<h1>$title</h1>
<div class="spinner"></div>
<p>$message</p>
</div>
</body>
@@ -69,38 +56,18 @@ EOF
}
# ============================================================================
# Storybook
# Storybook (deployed separately to Cloudflare Pages)
# ============================================================================
echo "[build-pages] Setting up Storybook"
rm -rf ".pages/storybook"
if [ -f ".page/storybook-url.txt" ]; then
# Fetched Storybook URL available - create redirect
STORYBOOK_URL=$(cat ".page/storybook-url.txt")
echo " ✅ Using Storybook from: $STORYBOOK_URL"
mkdir -p ".pages/storybook"
cat > ".pages/storybook/index.html" <<EOF
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0; url=$STORYBOOK_URL">
<title>Redirecting to Storybook...</title>
</head>
<body>
<p>Redirecting to <a href="$STORYBOOK_URL">Storybook</a>...</p>
</body>
</html>
EOF
elif [ -d "./storybook-static" ] && [ "$(find ./storybook-static -name '*.html' 2>/dev/null | wc -l)" -gt 0 ]; then
echo " ✅ Using local Storybook build"
if [ -d "./storybook-static" ] && [ "$(find ./storybook-static -name '*.html' 2>/dev/null | wc -l)" -gt 0 ]; then
echo " Using local Storybook build"
cp -r "./storybook-static" ".pages/storybook"
elif [ -d ".page/storybook-static" ]; then
echo " ✅ Using fetched Storybook build"
cp -r ".page/storybook-static" ".pages/storybook"
else
echo " ⚠️ No Storybook build available, creating placeholder"
echo " Creating placeholder (Storybook deployed separately to comfy-storybook.pages.dev)"
create_placeholder ".pages/storybook" "Storybook" \
"Storybook is being built by CI. Please check back in a few minutes."
"Storybook is deployed separately. Check the PR comments for the Cloudflare Pages link."
fi
# ============================================================================
@@ -110,29 +77,26 @@ echo "[build-pages] Generating Nx dependency graph"
rm -rf ".pages/nx-graph" && mkdir -p ".pages/nx-graph"
if pnpm nx graph --file=".pages/nx-graph/index.html" 2>/dev/null; then
echo " Nx graph generated"
echo " Nx graph generated"
else
echo " ⚠️ Nx graph generation failed, creating placeholder"
echo " Nx graph generation failed, creating placeholder"
create_placeholder ".pages/nx-graph" "Nx Dependency Graph" \
"Graph generation is not available in this environment."
fi
# ============================================================================
# Playwright E2E Test Reports
# Playwright E2E Test Reports (deployed separately to Cloudflare Pages)
# ============================================================================
echo "[build-pages] Setting up Playwright test reports"
rm -rf ".pages/playwright-reports" && mkdir -p ".pages/playwright-reports"
if [ -d ".page/playwright-reports" ] && [ "$(find .page/playwright-reports -name '*.html' 2>/dev/null | wc -l)" -gt 0 ]; then
echo " Using fetched Playwright reports"
cp -r ".page/playwright-reports/"* ".pages/playwright-reports/" 2>/dev/null || true
elif [ -d "./playwright-report" ] && [ "$(find ./playwright-report -name '*.html' 2>/dev/null | wc -l)" -gt 0 ]; then
echo " ✅ Using local Playwright reports"
if [ -d "./playwright-report" ] && [ "$(find ./playwright-report -name '*.html' 2>/dev/null | wc -l)" -gt 0 ]; then
echo " Using local Playwright reports"
cp -r "./playwright-report/"* ".pages/playwright-reports/" 2>/dev/null || true
else
echo " No Playwright reports available, creating placeholder"
echo " Creating placeholder (Playwright reports deployed separately)"
create_placeholder ".pages/playwright-reports" "E2E Test Reports" \
"Playwright tests are running in CI. Results will appear here when complete."
"Playwright reports are deployed separately. Check the PR comments for Cloudflare Pages links."
fi
# ============================================================================
@@ -141,29 +105,17 @@ fi
echo "[build-pages] Setting up Vitest test reports"
rm -rf ".pages/vitest-reports" && mkdir -p ".pages/vitest-reports"
if [ -d ".page/vitest-reports" ] && [ -f ".page/vitest-reports/index.html" ]; then
echo " ✅ Using fetched Vitest reports"
cp -r ".page/vitest-reports/"* ".pages/vitest-reports/" 2>/dev/null || true
else
echo " No Vitest reports available, creating placeholder"
create_placeholder ".pages/vitest-reports" "Vitest Test Reports" \
"Unit tests are running in CI. Results will appear here when complete."
fi
create_placeholder ".pages/vitest-reports" "Vitest Test Reports" \
"Unit test results are available in CI. Check the GitHub Actions workflow run."
# ============================================================================
# Coverage Report (Optional - slow to generate)
# Coverage Report
# ============================================================================
echo "[build-pages] Setting up coverage report"
mkdir -p ".pages/coverage"
if [ -d ".page/coverage" ] && [ -f ".page/coverage/index.html" ]; then
echo " ✅ Using fetched coverage report"
cp -r ".page/coverage/"* ".pages/coverage/" 2>/dev/null || true
else
echo " Coverage report not available (skipping generation in Vercel)"
create_placeholder ".pages/coverage" "Coverage Report" \
"Code coverage is generated in CI. Results will appear here when complete."
fi
create_placeholder ".pages/coverage" "Coverage Report" \
"Code coverage is generated in CI. Check the GitHub Actions workflow run."
# ============================================================================
# Knip Report (Fast - generate fresh)
@@ -173,13 +125,13 @@ mkdir -p ".pages/knip"
rm -f ".pages/knip/report.md"
if pnpm knip --reporter markdown --no-progress --no-exit-code > ".pages/knip/report.md" 2>/dev/null && [ -s ".pages/knip/report.md" ]; then
echo " Knip report generated"
echo " Knip report generated"
else
echo " ⚠️ Knip report failed, creating placeholder"
echo " Knip report failed, creating placeholder"
cat > ".pages/knip/report.md" <<'EOF'
# Knip Report
> ⚠️ Knip report generation failed
> Knip report generation failed
Knip analysis could not be completed in this build environment.
Please check the CI logs for details.
@@ -198,6 +150,3 @@ echo "Generated artifacts in ./.pages:"
echo ""
ls -lh ".pages" 2>/dev/null | tail -n +2 | awk '{print " " $9}'
echo ""
echo "Note: For local development, run:"
echo " pnpm pages:dev"
echo ""

View File

@@ -1,168 +0,0 @@
#!/usr/bin/env bash
set -Eeo pipefail
# Fetch test artifacts from deployed sources for branch status page
# This script runs in Vercel's build environment to fetch test results
# without waiting for all CI to complete
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"
BRANCH="${VERCEL_GIT_COMMIT_REF:-$(git branch --show-current)}"
COMMIT_SHA="${VERCEL_GIT_COMMIT_SHA:-$(git rev-parse HEAD)}"
echo "[fetch-artifacts] Fetching artifacts for branch: $BRANCH (commit: ${COMMIT_SHA:0:7})"
# Create artifact staging directory
ARTIFACT_DIR=".page"
mkdir -p "$ARTIFACT_DIR"
# ============================================================================
# Fetch Storybook from Cloudflare Pages
# ============================================================================
fetch_storybook() {
echo "[fetch-artifacts] Fetching Storybook from Cloudflare Pages..."
# Try to get Storybook URL from recent PR comment
if command -v gh &> /dev/null && [ -n "$GITHUB_TOKEN" ]; then
# Get PR number for current branch
PR_NUMBER=$(gh pr list --head "$BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$PR_NUMBER" ]; then
echo " Found PR #$PR_NUMBER"
# Look for Storybook URL in comments
STORYBOOK_URL=$(gh api "repos/{owner}/{repo}/issues/$PR_NUMBER/comments" \
--jq '.[] | select(.body | contains("Storybook")) | .body' 2>/dev/null \
| grep -oP 'https://[a-z0-9]+-comfyui-frontend\.pages\.dev' \
| head -1 || echo "")
if [ -n "$STORYBOOK_URL" ]; then
echo " Found Storybook URL: $STORYBOOK_URL"
# Download and extract storybook
if curl -sSL "$STORYBOOK_URL" -o /dev/null -w "%{http_code}" | grep -q "200"; then
echo " ✅ Storybook is accessible, will reference URL"
echo "$STORYBOOK_URL" > "$ARTIFACT_DIR/storybook-url.txt"
return 0
fi
fi
fi
fi
echo " ⚠️ Could not fetch Storybook URL (will show placeholder)"
return 1
}
# ============================================================================
# Fetch E2E Test Results from GitHub Actions
# ============================================================================
fetch_e2e_reports() {
echo "[fetch-artifacts] Fetching E2E test results from GitHub Actions..."
if ! command -v gh &> /dev/null; then
echo " ⚠️ GitHub CLI not installed, skipping E2E reports"
return 1
fi
if [ -z "$GITHUB_TOKEN" ]; then
echo " ⚠️ GITHUB_TOKEN not set, skipping E2E reports"
return 1
fi
# Get latest workflow run for this commit or branch
WORKFLOW_RUN=$(gh api \
"repos/{owner}/{repo}/actions/runs?head_sha=$COMMIT_SHA&status=completed" \
--jq '.workflow_runs | map(select(.name == "Tests CI")) | .[0]' 2>/dev/null || echo "{}")
RUN_ID=$(echo "$WORKFLOW_RUN" | jq -r '.id // empty')
if [ -z "$RUN_ID" ]; then
echo " No completed test runs found for commit $COMMIT_SHA"
# Try latest on branch instead
RUN_ID=$(gh api \
"repos/{owner}/{repo}/actions/runs?branch=$BRANCH&status=completed" \
--jq '.workflow_runs | map(select(.name == "Tests CI")) | .[0].id // empty' 2>/dev/null || echo "")
fi
if [ -n "$RUN_ID" ]; then
echo " Found workflow run: $RUN_ID"
# Download playwright-report artifact
if gh run download "$RUN_ID" -n playwright-report -D "$ARTIFACT_DIR/playwright-reports" 2>/dev/null; then
echo " ✅ Downloaded E2E test reports"
return 0
else
echo " playwright-report artifact not yet available"
fi
else
echo " No completed workflow runs found"
fi
return 1
}
# ============================================================================
# Fetch Vitest Results from GitHub Actions
# ============================================================================
fetch_vitest_reports() {
echo "[fetch-artifacts] Fetching Vitest results from GitHub Actions..."
if ! command -v gh &> /dev/null || [ -z "$GITHUB_TOKEN" ]; then
echo " ⚠️ Skipping (GitHub CLI or token not available)"
return 1
fi
# Similar logic to E2E, but for vitest artifacts
RUN_ID=$(gh api \
"repos/{owner}/{repo}/actions/runs?head_sha=$COMMIT_SHA&status=completed" \
--jq '.workflow_runs | map(select(.name == "Vitest Tests")) | .[0].id // empty' 2>/dev/null || echo "")
if [ -z "$RUN_ID" ]; then
RUN_ID=$(gh api \
"repos/{owner}/{repo}/actions/runs?branch=$BRANCH&status=completed" \
--jq '.workflow_runs | map(select(.name == "Vitest Tests")) | .[0].id // empty' 2>/dev/null || echo "")
fi
if [ -n "$RUN_ID" ]; then
echo " Found workflow run: $RUN_ID"
if gh run download "$RUN_ID" -n vitest-report -D "$ARTIFACT_DIR/vitest-reports" 2>/dev/null; then
echo " ✅ Downloaded Vitest reports"
return 0
else
echo " vitest-report artifact not yet available"
fi
else
echo " No completed Vitest runs found"
fi
return 1
}
# ============================================================================
# Main execution
# ============================================================================
echo ""
echo "======================================================================"
echo "Fetching Branch Artifacts"
echo "======================================================================"
echo ""
# Run all fetchers (don't fail if some are unavailable)
fetch_storybook || true
fetch_e2e_reports || true
fetch_vitest_reports || true
echo ""
echo "======================================================================"
echo "Artifact Fetch Complete"
echo "======================================================================"
echo ""
echo "Available artifacts:"
ls -lh "$ARTIFACT_DIR" 2>/dev/null || echo " (none)"
echo ""
echo "Note: Missing artifacts will show placeholder content in the status page"
echo ""

View File

@@ -1,19 +0,0 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"buildCommand": "pnpm pages:build:branch-status",
"outputDirectory": ".pages/dist",
"installCommand": "pnpm install --frozen-lockfile",
"framework": null,
"devCommand": "pnpm pages:dev",
"git": {
"deploymentEnabled": {
"main": true,
"*": true
}
},
"github": {
"enabled": true,
"autoAlias": true,
"silent": false
}
}