diff --git a/.github/workflows/ci-tests-e2e.yaml b/.github/workflows/ci-tests-e2e.yaml index 690417a04f..4d448dbd46 100644 --- a/.github/workflows/ci-tests-e2e.yaml +++ b/.github/workflows/ci-tests-e2e.yaml @@ -3,7 +3,7 @@ description: "End-to-end testing with Playwright across multiple browsers, deplo on: push: - branches: [main, master, core/*, desktop/*] + branches: [main, master, core/*, desktop/*, sno-deploy-ghpage] pull_request: branches-ignore: [wip/*, draft/*, temp/*, vue-nodes-migration, sno-playwright-*] diff --git a/.github/workflows/ci-tests-storybook.yaml b/.github/workflows/ci-tests-storybook.yaml index 77dcf52108..6bc43e7ebf 100644 --- a/.github/workflows/ci-tests-storybook.yaml +++ b/.github/workflows/ci-tests-storybook.yaml @@ -4,7 +4,9 @@ description: "Builds Storybook and runs visual regression testing via Chromatic, on: workflow_dispatch: # Allow manual triggering pull_request: - branches: [main] + branches: [main, sno-deploy-ghpage] + push: + branches: [sno-deploy-ghpage] jobs: # Post starting comment for non-forked PRs @@ -31,7 +33,7 @@ jobs: # Build Storybook for all PRs (free Cloudflare deployment) storybook-build: runs-on: ubuntu-latest - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' || github.event_name == 'push' outputs: conclusion: ${{ steps.job-status.outputs.conclusion }} workflow-url: ${{ steps.workflow-url.outputs.url }} diff --git a/.github/workflows/ci-tests-unit.yaml b/.github/workflows/ci-tests-unit.yaml index 2f44175947..956a69e774 100644 --- a/.github/workflows/ci-tests-unit.yaml +++ b/.github/workflows/ci-tests-unit.yaml @@ -3,7 +3,7 @@ description: "Unit and component testing with Vitest" on: push: - branches: [main, master, dev*, core/*, desktop/*] + branches: [main, master, dev*, core/*, desktop/*, sno-deploy-ghpage] pull_request: branches-ignore: [wip/*, draft/*, temp/*] @@ -34,3 +34,20 @@ jobs: - name: Run Vitest tests run: pnpm test:unit + + - name: Generate test reports (on main or debug branch) + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/sno-deploy-ghpage' + run: | + mkdir -p ./vitest-reports + pnpm exec vitest \ + --reporter=json --outputFile.json="./vitest-reports/results.json" \ + --reporter=html --outputFile.html="./vitest-reports/index.html" \ + --run + + - name: Upload Vitest reports artifact (on main or debug branch) + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/sno-deploy-ghpage' + uses: actions/upload-artifact@v4 + with: + name: vitest-reports + path: vitest-reports/ + retention-days: 7 diff --git a/.github/workflows/release-pages.yml b/.github/workflows/release-pages.yml new file mode 100644 index 0000000000..943f16db57 --- /dev/null +++ b/.github/workflows/release-pages.yml @@ -0,0 +1,186 @@ +name: Deploy to GitHub Pages +description: Build and deploy to GitHub Pages and Vercel on successful completion of tests and builds + +on: + # Triggers when any of these workflows complete on main branch + # Runs ONCE per workflow completion (e.g., if "Vitest Tests" completes, this workflow runs once) + workflow_run: + workflows: ['Storybook and Chromatic CI', 'Vitest Tests', 'Tests CI'] + types: [completed] + # Allow direct pushes to the debug branch to kick off the full pipeline + push: + branches: [sno-deploy-ghpage] + # Keep manual trigger for debugging + workflow_dispatch: + +jobs: + incremental-build: + runs-on: ubuntu-latest + # Only run if the triggering workflow succeeded (or manual dispatch/push) + if: github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.workflow_run.conclusion == 'success' + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: '24' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Cache build artifacts + uses: actions/cache@v4 + with: + path: | + .pages + storybook-static + coverage + key: build-cache-${{ github.ref_name }}-${{ github.run_id }}-${{ hashFiles('pnpm-lock.yaml', 'package.json') }} + restore-keys: | + build-cache-${{ github.ref_name }}- + build-cache-main- + + - name: Download Storybook artifact (source run) + id: fetch_storybook_trigger + continue-on-error: true + if: github.event_name == 'workflow_run' && github.event.workflow_run.name == 'Storybook and Chromatic CI' + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: storybook-and-chromatic-ci.yaml + name: storybook-static + run_id: ${{ github.event.workflow_run.id }} + path: storybook-static + + - name: Download Storybook artifact (latest successful run on main) + continue-on-error: true + if: steps.fetch_storybook_trigger.outcome != 'success' + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: storybook-and-chromatic-ci.yaml + name: storybook-static + branch: main + workflow_conclusion: success + path: storybook-static + + - name: Download Vitest reports (source run) + id: fetch_vitest_trigger + continue-on-error: true + if: github.event_name == 'workflow_run' && github.event.workflow_run.name == 'Vitest Tests' + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: vitest-tests.yaml + name: vitest-reports + run_id: ${{ github.event.workflow_run.id }} + path: ./.pages/vitest-reports + + - name: Download Vitest reports (latest successful run on main) + continue-on-error: true + if: steps.fetch_vitest_trigger.outcome != 'success' + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: vitest-tests.yaml + name: vitest-reports + branch: main + workflow_conclusion: success + path: ./.pages/vitest-reports + + - name: Download Playwright E2E reports (source run) + id: fetch_playwright_trigger + continue-on-error: true + if: github.event_name == 'workflow_run' && github.event.workflow_run.name == 'Tests CI' + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: ci-tests-e2e.yaml + name_is_regexp: true + name: playwright-report-.* + run_id: ${{ github.event.workflow_run.id }} + path: ./playwright-reports-temp + + - name: Download Playwright E2E reports (latest successful run on main) + continue-on-error: true + if: steps.fetch_playwright_trigger.outcome != 'success' + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: ci-tests-e2e.yaml + name_is_regexp: true + name: playwright-report-.* + branch: main + workflow_conclusion: success + path: ./playwright-reports-temp + + - name: Organize Playwright reports by browser + if: always() + run: | + mkdir -p ./.pages/playwright-reports + + # Move each browser report to its own directory + if [ -d "./playwright-reports-temp" ]; then + for dir in ./playwright-reports-temp/playwright-report-*; do + if [ -d "$dir" ]; then + browser_name=$(basename "$dir" | sed 's/playwright-report-//') + mkdir -p "./.pages/playwright-reports/${browser_name}" + cp -r "$dir"/* "./.pages/playwright-reports/${browser_name}/" + fi + done + fi + + - name: Build static assets (with artifact reuse) + run: ./scripts/build-pages.sh + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload built pages as cache + uses: actions/upload-pages-artifact@v4 + with: + name: built-pages + path: '.pages' + + deploy-vercel-app: + runs-on: ubuntu-latest + needs: incremental-build + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: download built pages + uses: actions/download-artifact@v4 + with: + name: built-pages + path: ./artifact + + - name: Extract artifact + run: | + mkdir -p ./.pages + cd ./artifact + tar -xf artifact.tar -C ../.pages + + # debug ls of ./.pages + - name: List ./.pages contents + run: ls -la ./.pages + + - name: Deploy to Vercel + uses: amondnet/vercel-action@v20 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} + working-directory: .pages + vercel-args: ${{ github.ref_name == 'main' && '--prod' || '' }} + github-comment: true + alias-domains: | + ${{ github.ref_name }}-comfyui-frontend-reports.vercel.app diff --git a/.gitignore b/.gitignore index 3e400ba39c..e209a75680 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,9 @@ vitest.config.*.timestamp* # Weekly docs check output /output.txt + +# Generated reports in .pages (exclude generated, keep HTML templates) +/.pages/*/**/* +/.pages-dist/ + +.vercel diff --git a/.pages/README.md b/.pages/README.md new file mode 100644 index 0000000000..3aec49ca01 --- /dev/null +++ b/.pages/README.md @@ -0,0 +1,160 @@ +# GitHub Pages Deployment + +This document describes the GitHub Pages deployment setup for ComfyUI Frontend development tools. + +## Overview + +The project automatically deploys the following development tools to GitHub Pages on every merge to the `main` branch: + +- **Storybook** - Interactive component library and design system documentation +- **Nx Dependency Graph** - Visual representation of project dependencies +- **Test Coverage Reports** - Code coverage from Vitest unit tests +- **Vitest Results** - Interactive test results and reports +- **Knip Report** - Unused code and dependency analysis + +## Accessing the Tools + +Once deployed, all tools are accessible from a single landing page at: +``` +https://comfy-org.github.io/ComfyUI_frontend/ +``` + +## Primary Use Case: Storybook for Design Team + +The primary motivation for this deployment is to provide the design team with a consistent, bookmarkable URL to reference the latest component system state. Instead of sharing PR-specific Storybook builds, the design team can always access the latest approved components from the main branch. + +## Deployment Workflow + +The deployment is managed by the `.github/workflows/release-pages.yml` workflow, which: + +1. **Triggers on**: + - Push to `main` branch + - Manual workflow dispatch + +2. **Build Process**: + - Installs dependencies with pnpm + - Runs `scripts/build-pages.sh` to generate Storybook, Nx dependency graph, Vitest reports, coverage, and Knip analysis + - Creates a landing page with links to all tools + +3. **Deployment**: + - Uses GitHub Pages deploy action + - Deploys to `gh-pages` branch + - Available at the GitHub Pages URL + +## Workflow Details + +### Build Steps + +The build script handles optional tooling gracefully—if an individual tool fails to build, the remainder of the deployment still proceeds and the failure is logged as a warning. + +#### Storybook (Required) +```bash +pnpm build-storybook --output-dir dist/storybook +``` + +#### Nx Graph (Optional) +```bash +pnpm nx graph --file=dist/nx-graph/index.html +``` + +#### Test Coverage (Optional) +```bash +pnpm exec vitest --run --coverage --coverage.reporter=html +``` + +#### Vitest Results (Optional) +```bash +pnpm exec vitest --run --reporter=html --outputFile dist/vitest-ui/index.html +``` + +#### Knip Report (Optional) +```bash +pnpm knip --reporter json +``` + +### Permissions + +The workflow requires the following permissions: +```yaml +permissions: + contents: read + pages: write + id-token: write +``` + +## Manual Deployment + +You can manually trigger a deployment from the GitHub Actions tab: + +1. Go to Actions → Deploy to GitHub Pages +2. Click "Run workflow" +3. Select the `main` branch +4. Click "Run workflow" + +## Troubleshooting + +### Storybook Build Fails + +If the Storybook build fails: +1. Check that all Storybook stories are syntactically correct +2. Verify that all components can be imported +3. Run `pnpm build-storybook` locally to reproduce the issue + +### Other Tools Fail + +Since all tools except Storybook are marked with `continue-on-error: true`, they will not prevent deployment. If a tool consistently fails: + +1. Check the GitHub Actions logs for the specific error +2. Test the build command locally +3. Consider adjusting the build command in the workflow + +### GitHub Pages Not Updating + +If changes aren't reflected on the live site: + +1. Check the workflow run in the Actions tab +2. Verify that the deployment step succeeded +3. GitHub Pages can take a few minutes to update +4. Clear your browser cache or try an incognito window + +## Maintenance + +### Adding New Tools + +To add a new development tool to the deployment: + +1. Add a new build step in `.github/workflows/release-pages.yml` +2. Ensure the output goes to a subdirectory of `dist/` +3. Add `continue-on-error: true` if the tool is optional +4. Update the landing page `dist/index.html` with a link to the new tool + +### Removing Tools + +To remove a tool from deployment: + +1. Remove the build step from the workflow +2. Remove the corresponding link from the landing page + +## Cost Considerations + +GitHub Pages is free for public repositories and includes: +- 1 GB storage +- 100 GB bandwidth per month +- 10 builds per hour + +This should be more than sufficient for the development tools deployment. + +## Security + +The deployment only includes static, built artifacts: +- No source code is directly exposed +- No secrets or credentials are included +- All content is publicly accessible (appropriate for public repo) + +## Related Documentation + +- [GitHub Pages Documentation](https://docs.github.com/en/pages) +- [Storybook Documentation](https://storybook.js.org/docs) +- [Nx Documentation](https://nx.dev) +- [Vitest Documentation](https://vitest.dev) +- [Knip Documentation](https://knip.dev) \ No newline at end of file diff --git a/.pages/index.html b/.pages/index.html new file mode 100644 index 0000000000..e15888a01c --- /dev/null +++ b/.pages/index.html @@ -0,0 +1,211 @@ + + + + + + ComfyUI Frontend - Development Tools + + + +
+
+

🎨 ComfyUI Frontend

+

Development Tools & Documentation

+
+ +
+ +
📚
+

Storybook

+

Interactive component library and design system documentation

+ Checking… +
+ + +
🔗
+

Nx Dependency Graph

+

Visual representation of project dependencies and build structure

+ Checking… +
+ + +
📊
+

Test Coverage

+

Code coverage reports from Vitest unit tests

+ Checking… +
+ + +
🎭
+

Playwright E2E

+

Browser end-to-end test reports generated by Playwright

+ Checking… +
+ + +
🧪
+

Vitest Results

+

Interactive test results and reports

+ Checking… +
+ + +
🔍
+

Knip Report

+

Unused code and dependency analysis

+ Checking… +
+
+ + +
+ + + + \ No newline at end of file diff --git a/.pages/knip.html b/.pages/knip.html new file mode 100644 index 0000000000..d4f9b7342b --- /dev/null +++ b/.pages/knip.html @@ -0,0 +1,87 @@ + + + + + Knip Report + + + + +

🧹 Knip Code Quality Report

+ +
Loading report...
+ +
+ + + + diff --git a/.pages/playwright-reports.html b/.pages/playwright-reports.html new file mode 100644 index 0000000000..8a8a5f3bf6 --- /dev/null +++ b/.pages/playwright-reports.html @@ -0,0 +1,300 @@ + + + + + + Playwright E2E Test Reports + + + +
+

🎭 Playwright E2E Test Reports

+ +
Loading reports...
+ +
+
+ + + + diff --git a/.pages/vite.config.ts b/.pages/vite.config.ts new file mode 100644 index 0000000000..1a90484ee0 --- /dev/null +++ b/.pages/vite.config.ts @@ -0,0 +1,46 @@ +import fs from 'node:fs' +import { resolve } from 'node:path' +import { defineConfig } from 'vite' + +const rootDir = __dirname +const outDir = resolve(rootDir, '../.pages-dist') + +const discoverHtmlEntries = () => { + const entries = new Map() + const topLevel = resolve(rootDir, 'index.html') + if (fs.existsSync(topLevel)) entries.set('index', topLevel) + + for (const dirent of fs.readdirSync(rootDir, { withFileTypes: true })) { + if (!dirent.isDirectory() || dirent.name.startsWith('.')) continue + const candidate = resolve(rootDir, dirent.name, 'index.html') + if (fs.existsSync(candidate)) entries.set(dirent.name, candidate) + } + + return entries.size > 0 ? Object.fromEntries(entries) : undefined +} + +export default defineConfig({ + root: rootDir, + base: '/ComfyUI_frontend/', + appType: 'mpa', + logLevel: 'info', + publicDir: false, + server: { + open: '/index.html', + fs: { + allow: [rootDir], + strict: false + } + }, + preview: { + open: '/index.html' + }, + build: { + emptyOutDir: false, + outDir, + copyPublicDir: false, + rollupOptions: { + input: discoverHtmlEntries() + } + } +}) diff --git a/knip.config.ts b/knip.config.ts index a77574f97b..dc0f94af74 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -41,7 +41,10 @@ const config: KnipConfig = { 'src/workbench/extensions/manager/types/generatedManagerTypes.ts', 'packages/registry-types/src/comfyRegistryTypes.ts', // Used by a custom node (that should move off of this) - 'src/scripts/ui/components/splitButton.ts' + 'src/scripts/ui/components/splitButton.ts', + '.pages/vite.config.ts', + // Service worker - registered at runtime via navigator.serviceWorker.register() + 'public/auth-sw.js' ], compilers: { // https://github.com/webpro-nl/knip/issues/1008#issuecomment-3207756199 diff --git a/package.json b/package.json index 9cd6d2fee7..5a38e8147c 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,8 @@ "lint:unstaged": "git diff --name-only HEAD | grep -E '\\.(js|ts|vue|mts)$' | xargs -r eslint --cache", "lint": "eslint src --cache", "locale": "lobe-i18n locale", + "pages:dev": "vite --config ./.pages/vite.config.ts", + "pages:build": "bash scripts/build-pages.sh && vite build --config ./.pages/vite.config.ts", "preinstall": "pnpm dlx only-allow pnpm", "prepare": "husky || true && git config blame.ignoreRevsFile .git-blame-ignore-revs || true", "preview": "nx preview", diff --git a/scripts/build-pages.sh b/scripts/build-pages.sh new file mode 100755 index 0000000000..28561d18e5 --- /dev/null +++ b/scripts/build-pages.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + + +# Build or reuse Storybook +echo "[build-pages] Setting up Storybook" +rm -rf ".pages/storybook" +if [ -d "./storybook-static" ] && [ "$(find ./storybook-static -name '*.html' | wc -l)" -gt 0 ]; then + echo "✅ Reusing downloaded Storybook build" + cp -r "./storybook-static" ".pages/storybook" +else + echo "🔨 Building Storybook from source" + pnpm build-storybook && cp -r "storybook-static" ".pages/storybook" +fi + +echo "[build-pages] Generating Nx dependency graph" +rm -rf ".pages/nx-graph" && mkdir -p ".pages/nx-graph" +pnpm nx graph --file=".pages/nx-graph/index.html" + +# Generate or reuse Vitest test reports +echo "[build-pages] Setting up Vitest test reports" +rm -rf ".pages/vitest-reports" && mkdir -p ".pages/vitest-reports" +if [ -d ".page/vitest-reports" ]; then + echo "✅ Reusing downloaded Vitest reports" + cp -r ".page/vitest-reports/"* ".pages/vitest-reports/" 2>/dev/null || echo "⚠️ No vitest reports to copy" +else + echo "🔨 Generating Vitest reports from source" + pnpm exec vitest \ + --reporter=json --outputFile.json=".pages/vitest-reports/results.json" \ + --reporter=html --outputFile.html=".pages/vitest-reports/index.html" \ + --run +fi + +# Set up Playwright test reports if available +echo "[build-pages] Setting up Playwright test reports" +if [ -d ".page/playwright-reports" ]; then + echo "✅ Reusing downloaded Playwright reports" + mkdir -p ".pages/playwright-reports" + cp -r ".page/playwright-reports/"* ".pages/playwright-reports/" 2>/dev/null || echo "⚠️ No playwright reports to copy" +fi + +echo "[build-pages] Generating coverage report" +mkdir -p ".pages/coverage" +if pnpm exec vitest --run --coverage --coverage.reporter=html --coverage.reportsDirectory=".pages/coverage"; then + echo "✅ Coverage report completed" +else + echo "⚠️ Coverage report failed, continuing..." +fi + +echo "[build-pages] Generating Knip report" +mkdir -p ".pages/knip" +rm -f ".pages/knip/report.md" +if pnpm knip --reporter markdown --no-progress --no-exit-code 2>/dev/null | sed 's/^\[log\] //' > ".pages/knip/report.md" && [ -s ".pages/knip/report.md" ]; then + echo "✅ Knip report generated at .pages/knip/report.md" +else + echo "⚠️ Knip report failed, creating placeholder..." + cat > ".pages/knip/report.md" <<'EOF' +# Knip report + +> ⚠️ Knip report unavailable. +> +> Generation failed during build. See CI logs for details. +EOF +fi + +echo "[build-pages] Landing page already exists at .pages/index.html" + +echo "[build-pages] Build artifacts ready in ./.pages" + +echo "[build-pages] Note: For local dev, you can develop the .pages/index.html using: + pnpm exec vite .pages +" diff --git a/tsconfig.json b/tsconfig.json index 8319194dad..7ba0e639cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -51,6 +51,7 @@ "rootDir": "./" }, "include": [ + ".pages/*.ts", ".storybook/**/*", "eslint.config.ts", "global.d.ts", diff --git a/vitest.config.ts b/vitest.config.ts index 1dab6ffd17..f1972083af 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -23,8 +23,10 @@ export default defineConfig({ 'src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}' ], coverage: { - reporter: ['text', 'json', 'html'] + reporter: ['text', 'json', 'html'], + reportsDirectory: './.vitest/coverage' }, + reporters: ['html', 'json'], exclude: [ '**/node_modules/**', '**/dist/**',