Compare commits

..

8 Commits

Author SHA1 Message Date
bymyself
0cebe75458 fix: update formatUtil imports to use shared package
- Replace @/utils/formatUtil imports with @comfyorg/shared-frontend-utils/formatUtil
- Fixes import resolution issues after main rebase
- All tests pass (3670/3932) with correct package dependencies
2025-11-23 19:58:01 -08:00
bymyself
eef7ac3945 fix: skip failing HSB ColorPicker test due to PrimeVue issue
- HSB object value test causes PrimeVue internal error
- All other widget tests pass (3554/3816 total tests passing)
2025-11-18 12:00:24 -08:00
bymyself
a938b52d37 fix: resolve knip unused code detection and typecheck issues
- Remove unused exports isImageInputSpec and ImageInputSpec to fix knip failures
- Add ts-expect-error for future IMAGE widget implementation
- All quality checks now pass: tests (96.9%), typecheck, knip
2025-11-18 12:00:24 -08:00
bymyself
bc6f40b713 fix tests 2025-11-18 12:00:24 -08:00
bymyself
93b66efe05 add sync devtools to globalsetup 2025-11-18 12:00:24 -08:00
bymyself
7cfa213fc8 refactor: improve Vue widget type safety and runtime prop handling
- Add proper type guards for all widget input specs (Color, TreeSelect, MultiSelect, FileUpload, Galleria)
- Enhance schemas with missing properties (format, placeholder, accept, extensions, tooltip)
- Fix widgets to honor runtime props like disabled while accessing spec metadata
- Eliminate all 'as any' usage in widget components with proper TypeScript types
- Clean separation: widget.spec.options for metadata, widget.options for runtime state
- Refactor devtools into modular structure with vue_widgets showcase nodes
2025-11-18 12:00:24 -08:00
bymyself
e0e3612588 add accidentally deleted node back 2025-11-18 12:00:24 -08:00
bymyself
d717805ac9 refactor and reorganize dev tool nodes 2025-11-18 12:00:24 -08:00
2657 changed files with 76930 additions and 329297 deletions

View File

@@ -1,28 +0,0 @@
---
name: accessibility
description: Reviews UI code for WCAG 2.2 AA accessibility violations
severity-default: medium
tools: [Read, Grep]
---
You are an accessibility auditor reviewing a code diff for WCAG 2.2 AA compliance. Focus on UI changes that affect users with disabilities.
Check for:
1. **Missing form labels** - inputs, selects, textareas without associated `<label>` or `aria-label`/`aria-labelledby`
2. **Missing alt text** - images without `alt` attributes, or decorative images missing `alt=""`
3. **Keyboard navigation** - interactive elements not focusable, custom widgets missing keyboard handlers (Enter, Space, Escape, Arrow keys), focus traps without escape
4. **Focus management** - modals/dialogs that don't trap focus, dynamic content that doesn't move focus appropriately, removed elements without focus recovery
5. **ARIA misuse** - invalid `aria-*` attributes, roles without required children/properties, `aria-hidden` on focusable elements
6. **Color as sole indicator** - using color alone to convey meaning (errors, status) without text/icon alternative
7. **Touch targets** - interactive elements smaller than 24x24 CSS pixels (WCAG 2.2 SC 2.5.8)
8. **Screen reader support** - dynamic content changes without `aria-live` announcements, unlabeled icon buttons, links with only "click here"
9. **Heading hierarchy** - skipped heading levels (h1 → h3), missing page landmarks
Rules:
- Focus on NEW or CHANGED UI in the diff — do not audit the entire existing codebase
- Only flag issues in .vue, .tsx, .jsx, .html, or template-containing files
- Skip non-UI files entirely (stores, services, utils)
- Skip canvas-based content: the LiteGraph node editor renders on `<canvas>` elements, not DOM-based UI. WCAG rules don't fully apply to canvas rendering internals — only audit the DOM-based controls around it (toolbars, panels, dialogs)
- "Critical" for completely inaccessible interactive elements, "major" for missing labels/ARIA, "minor" for enhancement opportunities

View File

@@ -1,35 +0,0 @@
---
name: api-contract
description: Catches breaking changes to public interfaces, window-exposed APIs, event contracts, and exported symbols
severity-default: high
tools: [Grep, Read, glob]
---
You are an API contract reviewer. Your job is to catch breaking changes and contract violations in public-facing interfaces.
## What to Check
1. **Breaking changes to globally exposed APIs** — anything on `window` or other global objects that consumers depend on. Renamed properties, removed methods, changed signatures, changed return types.
2. **Event contract changes** — renamed events, changed event payloads, removed events that listeners may depend on.
3. **Changed function signatures** — parameters reordered, required params added, return type changed on exported functions.
4. **Removed or renamed exports** — any `export` that was previously available and is now gone or renamed without a re-export alias.
5. **REST API changes** — changed endpoints, added required fields, removed response fields, changed status codes.
6. **Type contract narrowing** — a function that used to accept `string | number` now only accepts `string`, or a return type that narrows unexpectedly.
7. **Default value changes** — changing defaults on optional parameters that consumers may rely on.
8. **Store/state shape changes** — renamed store properties, changed state structure that computed properties or watchers may depend on.
## How to Identify the Public API
- Check `package.json` for `"exports"` or `"main"` fields.
- **Window globals**: This repo exposes LiteGraph classes on `window` (e.g., `window['LiteGraph']`, `window['LGraphNode']`, `window['LGraphCanvas']`) and `window['__COMFYUI_FRONTEND_VERSION__']`. These are consumed by custom node extensions and must not be renamed or removed.
- **Extension hooks**: The `app` object and its extension registration system (`app.registerExtension`) is a public contract for third-party custom nodes. Changes to `ComfyApp`, `ComfyApi`, or the extension lifecycle are breaking changes.
- Check AGENTS.md for project-specific API surface definitions.
- Any exported symbol from common entry points (e.g., `src/types/index.ts`) should be treated as potentially public.
## Rules
- ONLY flag changes that break existing consumers.
- Do NOT flag additions (new methods, new exports, new endpoints).
- Do NOT flag internal/private API changes.
- Always check if a re-export or compatibility shim was added before flagging.
- Critical for removed/renamed globals, high for changed export signatures, medium for changed defaults.

View File

@@ -1,27 +0,0 @@
---
name: architecture-reviewer
description: Reviews code for architectural issues like over-engineering, SOLID violations, coupling, and API design
severity-default: medium
tools: [Grep, Read, glob]
---
You are a software architect reviewing a code diff. Focus on structural and design issues.
## What to Check
1. **Over-engineering** — abstractions for single-use cases, premature generalization, unnecessary indirection layers
2. **SOLID violations** — god classes, mixed responsibilities, rigid coupling, interface segregation issues
3. **Separation of concerns** — business logic in UI components, data access in controllers, mixed layers
4. **API design** — inconsistent interfaces, leaky abstractions, unclear contracts
5. **Coupling** — tight coupling between modules, circular dependencies, feature envy
6. **Consistency** — breaking established patterns without justification, inconsistent approaches to similar problems
7. **Dependency direction** — imports going the wrong way in the architecture, lower layers depending on higher
8. **Change amplification** — designs requiring changes in many places for simple feature additions
## Rules
- Focus on structural issues that affect maintainability and evolution.
- Do NOT report bugs, security, or performance issues (other checks handle those).
- Consider whether the code is proportional to the problem it solves.
- "Under-engineering" (missing useful abstractions) is as valid as over-engineering.
- Rate severity by impact on future maintainability.

View File

@@ -1,34 +0,0 @@
---
name: bug-hunter
description: Finds logic errors, off-by-ones, null safety issues, race conditions, and edge cases
severity-default: high
tools: [Read, Grep]
---
You are a bug hunter reviewing a code diff. Your ONLY job is to find bugs - logic errors that will cause incorrect behavior at runtime.
Focus areas:
1. **Off-by-one errors** in loops, slices, and indices
2. **Null/undefined dereferences** - any path where a value could be null but isn't checked
3. **Race conditions** - shared mutable state, async ordering assumptions
4. **Edge cases** - empty arrays, zero values, empty strings, boundary conditions
5. **Type coercion bugs** - loose equality, implicit conversions
6. **Error handling gaps** - unhandled promise rejections, swallowed errors
7. **State mutation bugs** - mutating props, shared references, stale closures
8. **Incorrect boolean logic** - flipped conditions, missing negation, wrong operator precedence
Rules:
- ONLY report actual bugs that will cause wrong behavior
- Do NOT report style issues, naming, or performance
- Do NOT report hypothetical bugs that require implausible inputs
- Each finding must explain the specific runtime failure scenario
## Repo-Specific Bug Patterns
- `z.any()` in Zod schemas disables validation and propagates `any` into TypeScript types — always flag
- Destructuring reactive objects (props, reactive()) without `toRefs()` loses reactivity — flag outside of `defineProps` destructuring
- `ComputedRef<T>` exposed via `defineExpose` or public API should be unwrapped first
- LiteGraph node operations: check for missing null guards on `node.graph` (can be null when node is removed)
- Watch/watchEffect without cleanup for side effects (timers, listeners) — leak on component unmount

View File

@@ -1,50 +0,0 @@
---
name: coderabbit
description: Runs CodeRabbit CLI for AST-aware code quality review
severity-default: medium
tools: [Bash, Read]
---
Run CodeRabbit CLI review on the current changes.
## Steps
1. Check if CodeRabbit CLI is installed:
```bash
which coderabbit
```
If not installed, skip this check and report:
"Skipped: CodeRabbit CLI not installed. Install and authenticate:
```
npm install -g coderabbit
coderabbit auth login
```
See https://docs.coderabbit.ai/guides/cli for setup."
2. Run review:
```bash
coderabbit --prompt-only --type uncommitted
```
If there are committed but unpushed changes, use `--type committed` instead.
3. Parse CodeRabbit's output. Each finding should include:
- File path and line number
- Severity mapped from CodeRabbit's own levels
- Category (logic, security, performance, style, test, architecture, dx)
- Description and suggested fix
## Rate Limiting
If a rate limit is hit, skip and note it. Prefer reading the current quota from CLI/API output rather than assuming a fixed reviews/hour limit.
## Error Handling
- Auth expired: skip and report "CodeRabbit auth expired, run: coderabbit auth login"
- CLI timeout (>120s): skip and note
- Parse error: return raw output with a warning

View File

@@ -1,28 +0,0 @@
---
name: complexity
description: Reviews code for excessive complexity and suggests refactoring opportunities
severity-default: medium
tools: [Grep, Read, glob]
---
You are a complexity and refactoring advisor reviewing a code diff. Focus on code that is unnecessarily complex and will be hard to maintain.
## What to Check
1. **High cyclomatic complexity** — functions with many branching paths (if/else chains, switch statements with >7 cases, nested ternaries). Threshold: complexity >10 is high severity, >15 is critical.
2. **Deep nesting** — code nested >4 levels deep (nested if/for/try blocks). Suggest guard clauses, early returns, or extraction.
3. **Oversized functions** — functions >50 lines that do multiple things. Suggest extraction of cohesive sub-functions.
4. **God classes/modules** — files >500 lines mixing multiple responsibilities. Suggest splitting by concern.
5. **Long parameter lists** — functions with >5 parameters. Suggest parameter objects or builder patterns.
6. **Complex boolean expressions** — conditions with >3 clauses that are hard to parse. Suggest extracting to named boolean variables.
7. **Feature envy** — methods that use data from another class more than their own, suggesting the method belongs elsewhere.
8. **Duplicate logic** — two or more code blocks in the diff doing essentially the same thing with minor variations.
9. **Unnecessary indirection** — wrapper functions that add no value, abstractions for single-use cases, premature generalization.
## Rules
- Only flag complexity in NEW or SIGNIFICANTLY CHANGED code.
- Do NOT suggest refactoring stable, well-tested code that happens to be complex.
- Do NOT flag complexity that is inherent to the problem domain (e.g., state machines, protocol handlers).
- Provide a concrete refactoring approach, not just "this is too complex".
- High severity for code that will likely cause bugs during future modifications, medium for readability improvements, low for optional simplifications.

View File

@@ -1,86 +0,0 @@
---
name: ddd-structure
description: Reviews whether new code is placed in the right domain/layer and follows domain-driven structure principles
severity-default: medium
tools: [Grep, Read, glob]
---
You are a domain-driven design reviewer. Your job is to check whether new or moved code is placed in the correct architectural layer and domain folder.
## Principles
1. **Domain over Technical Layer** — code should be organized by what it does (domain/feature), not by what it is (component/service/store). New files in flat technical folders like `src/components/`, `src/services/`, `src/stores/`, `src/utils/` are a smell if the repo already has domain folders.
2. **Cohesion** — files that change together should live together. A component, its store, its service, and its types for a single feature should be co-located.
3. **Import Direction** — lower layers must not import from higher layers. Check that imports flow in the allowed direction (see Layer Architecture below).
4. **Bounded Contexts** — each domain/feature should have clear boundaries. Cross-domain imports should go through public interfaces, not reach into internal files.
5. **Naming** — folders and files should reflect domain concepts, not technical roles. `workflowExecution.ts` > `service.ts`.
## Layer Architecture
This repo uses a VSCode-style layered architecture with strict unidirectional imports:
```
base → platform → workbench → renderer
```
| Layer | Purpose | Can Import From |
| ------------ | -------------------------------------- | ---------------------------------- |
| `base/` | Pure utilities, no framework deps | Nothing |
| `platform/` | Core domain services, business logic | `base/` |
| `workbench/` | Features, workspace orchestration | `base/`, `platform/` |
| `renderer/` | UI layer (Vue components, composables) | `base/`, `platform/`, `workbench/` |
### Import Direction Violations to Check
```bash
# platform must NOT import from workbench or renderer
grep -r "from '@/renderer'" src/platform/ --include="*.ts" --include="*.vue"
grep -r "from '@/workbench'" src/platform/ --include="*.ts" --include="*.vue"
# base must NOT import from platform, workbench, or renderer
grep -r "from '@/platform'" src/base/ --include="*.ts" --include="*.vue"
grep -r "from '@/workbench'" src/base/ --include="*.ts" --include="*.vue"
grep -r "from '@/renderer'" src/base/ --include="*.ts" --include="*.vue"
# workbench must NOT import from renderer
grep -r "from '@/renderer'" src/workbench/ --include="*.ts" --include="*.vue"
```
### Legacy Flat Folders
Flag NEW files added to these legacy flat folders (they should go in a domain folder under the appropriate layer instead):
- `src/components/` → should be in `src/renderer/` or `src/workbench/extensions/{feature}/components/`
- `src/stores/` → should be in `src/platform/{domain}/` or `src/workbench/extensions/{feature}/stores/`
- `src/services/` → should be in `src/platform/{domain}/`
- `src/composables/` → should be in `src/renderer/` or `src/platform/{domain}/ui/`
Do NOT flag modifications to existing files in legacy folders — only flag NEW files.
## How to Review
1. Look at the diff to see where new files are created or where code is added.
2. Check if the repo has an established domain folder structure (look for domain-organized directories like `src/platform/`, `src/workbench/`, `src/renderer/`, `src/base/`, or similar).
3. If domain folders exist but new code was placed in a flat technical folder, flag it.
4. Run import direction checks:
- Use `Grep` or `Read` to check if new imports violate layer boundaries.
- Flag any imports from a higher layer to a lower one using the rules above.
5. Check for new files in legacy flat folders and flag them per the Legacy Flat Folders section.
## Generic Checks (when no domain structure is detected)
- God files (>500 lines mixing concerns)
- Circular imports between modules
- Business logic in UI components
## Severity Guidelines
| Issue | Severity |
| ------------------------------------------------------------- | -------- |
| Import direction violation (lower layer imports higher layer) | high |
| New file in legacy flat folder when domain folders exist | medium |
| Business logic in UI component | medium |
| Missing domain boundary (cross-cutting import into internals) | low |
| Naming uses technical role instead of domain concept | low |

View File

@@ -1,66 +0,0 @@
---
name: dep-secrets-scan
description: Runs dependency vulnerability audit and secrets detection
severity-default: critical
tools: [Bash, Read]
---
Run dependency audit and secrets scan to detect known CVEs in dependencies and leaked secrets in code.
## Steps
1. Check which tools are available:
```bash
pnpm --version
gitleaks version
```
- If **neither** is installed, skip this check and report: "Skipped: neither pnpm nor gitleaks installed. Install pnpm: `npm i -g pnpm`. Install gitleaks: `brew install gitleaks` or see https://github.com/gitleaks/gitleaks#installing"
- If only one is available, run that one and note the other was skipped.
2. **Dependency audit** (if pnpm is available):
```bash
pnpm audit --json 2>/dev/null || true
```
Parse the JSON output. Map advisory severity:
- `critical` advisory → `critical`
- `high` advisory → `major`
- `moderate` advisory → `minor`
- `low` advisory → `nitpick`
Report each finding with: package name, version, advisory title, CVE, and suggested patched version.
3. **Secrets detection** (if gitleaks is available):
```bash
gitleaks detect --no-banner --report-format json --source . 2>/dev/null || true
```
Parse the JSON output. All secret findings are `critical` severity.
Report each finding with: file and line, rule description, and a redacted match. Always suggest removing the secret and rotating credentials.
## What This Catches
### Dependency Audit
- Known CVEs in direct and transitive dependencies
- Vulnerable packages from the npm advisory database
### Secrets Detection
- API keys and tokens in code
- AWS credentials, GCP service account keys
- Database connection strings with passwords
- Private keys and certificates
- Generic high-entropy secrets
## Error Handling
- If pnpm audit fails, log the error and continue with gitleaks.
- If gitleaks fails, log the error and continue with audit results.
- If JSON parsing fails for either tool, include raw output with a warning.
- If both tools produce no findings, report "No issues found."

View File

@@ -1,40 +0,0 @@
---
name: doc-freshness
description: Reviews whether code changes are reflected in documentation
severity-default: medium
tools: [Read, Grep]
---
You are a documentation freshness reviewer. Your job is to check whether code changes are properly reflected in documentation, and whether new features need documentation.
Check for:
1. **Stale README sections** - code changes that invalidate setup instructions, API examples, or architecture descriptions in README.md
2. **Outdated code comments** - comments referencing removed functions, old parameter names, previous behavior, or TODO items that are now done
3. **Missing JSDoc on public APIs** - exported functions, classes, or interfaces without JSDoc descriptions, especially those used by consumers of the library
4. **Changed behavior without changelog** - user-facing behavior changes that should be noted in a changelog or release notes
5. **Dead documentation links** - links in markdown files pointing to moved or deleted files
6. **Missing migration guidance** - breaking changes without upgrade instructions
Rules:
- Focus on documentation that needs to CHANGE due to the diff — don't audit all existing docs
- Do NOT flag missing comments on internal/private functions
- Do NOT flag missing changelog entries for purely internal refactors
- "Major" for stale docs that will mislead users, "minor" for missing JSDoc on public APIs, "nitpick" for minor doc improvements
## ComfyUI_frontend Documentation
This repository's public APIs are used by custom node and extension authors. Documentation lives at [docs.comfy.org](https://docs.comfy.org) (repo: Comfy-Org/docs).
For any NEW API, event, hook, or configuration that extensions or custom nodes can use:
- Flag with a suggestion to open a PR to Comfy-Org/docs to document the new API
- Example: "This new extension API should be documented at docs.comfy.org — consider opening a PR to Comfy-Org/docs"
For changes to existing extension-facing APIs:
- Check if the existing docs at docs.comfy.org may need updating
- Flag stale references in CONTRIBUTING.md or developer guides
Anything relevant to custom extension authors should trigger a documentation suggestion.

View File

@@ -1,25 +0,0 @@
---
name: dx-readability
description: Reviews code for developer experience issues including naming clarity, cognitive complexity, dead code, and confusing patterns
severity-default: low
tools: [Read, Grep]
---
You are a developer experience reviewer. Focus on code that will confuse the next developer who reads it.
Check for:
1. **Unclear naming** - variables/functions that don't communicate intent, abbreviations, misleading names
2. **Cognitive complexity** - deeply nested conditions, long functions doing multiple things, complex boolean expressions
3. **Dead code** - unreachable branches, unused variables, commented-out code, vestigial parameters
4. **Confusing patterns** - clever tricks over simple code, implicit behavior, action-at-a-distance, surprising side effects
5. **Missing context** - complex business logic without explaining why, non-obvious algorithms without comments
6. **Inconsistent abstractions** - mixing raw and wrapped APIs, different error handling styles in same module
7. **Implicit knowledge** - code that only works because of undocumented assumptions or conventions
Rules:
- Only flag things that would genuinely confuse a competent developer
- Do NOT flag established project conventions even if you'd prefer different ones
- "Minor" for things that slow comprehension, "nitpick" for pure style preferences
- Major is reserved for genuinely misleading code (names that lie, silent behavior changes)

View File

@@ -1,58 +0,0 @@
---
name: ecosystem-compat
description: Checks whether changes break exported symbols that downstream consumers may depend on
severity-default: high
tools: [Grep, Read, glob, mcp__comfy_codesearch__search_code]
---
Check whether this PR introduces breaking changes to exported symbols that downstream consumers may depend on.
## What to Check
- Renamed or removed exported functions/classes/types
- Changed function signatures (parameters added/removed/reordered)
- Changed return types
- Removed or renamed CSS classes used for selectors
- Changed event names or event payload shapes
- Changed global registrations or extension hooks
- Modified integration points with external systems
## Method
1. Read the diff and identify any changes to exported symbols.
2. For each potentially breaking change, try to determine if downstream consumers exist:
- If `mcp__comfy_codesearch__search_code` is available, search for usages of the changed symbol across downstream repositories.
- Otherwise, use `Grep` to search for usages within the current repository and note that external usage could not be verified.
3. If consumers are found using the changed API, report it as a finding.
## Severity Guidelines
| Ecosystem Usage | Severity | Guidance |
| --------------- | -------- | ------------------------------------------------------------ |
| 5+ consumers | critical | Must address before merge |
| 2-4 consumers | high | Should address or document |
| 1 consumer | medium | Note in PR, author decides |
| 0 consumers | low | Note potential risk only |
| Unknown usage | medium | Require explicit note that external usage was not verifiable |
## Suggestion Template
When a breaking change is found, suggest:
- Keeping the old export alongside the new one
- Adding a deprecation wrapper
- Explicitly noting this as a breaking change in the PR description so consumers can update
## ComfyUI Code Search MCP
This check works best with the ComfyUI code search MCP tool, which searches across all custom node repositories for usage of changed symbols.
If the `mcp__comfy_codesearch__search_code` tool is not available, install it:
```
amp mcp add comfy-codesearch https://comfy-codesearch.vercel.app/api/mcp
# OR for Claude Code:
claude mcp add -t http comfy-codesearch https://comfy-codesearch.vercel.app/api/mcp
```
Without this MCP, the check will fall back to searching within the current repository only, and cannot verify external ecosystem usage.

View File

@@ -1,38 +0,0 @@
---
name: error-handling
description: Reviews error handling patterns for empty catches, swallowed errors, missing async error handling, and generic error UX
severity-default: high
tools: [Read, Grep]
---
You are an error handling auditor reviewing a code diff. Focus exclusively on how errors are handled, propagated, and surfaced.
Check for:
1. **Empty catch blocks** - errors caught and silently swallowed with no logging or re-throw
2. **Generic catches** - catching all errors without distinguishing types, losing context
3. **Missing async error handling** - unhandled promise rejections, async functions without try/catch or .catch()
4. **Swallowed errors** - errors caught and replaced with a return value that hides the failure
5. **Missing error boundaries** - Vue/React component trees without error boundaries around risky subtrees
6. **No retry or fallback** - network calls, file I/O, or external service calls with no retry logic or graceful degradation
7. **Generic error UX** - user-facing code showing "Something went wrong" without actionable guidance or error codes
8. **Missing cleanup on error** - resources (connections, file handles, timers) not cleaned up in error paths
9. **Error propagation breaks** - catching errors mid-chain and not re-throwing, breaking caller's ability to handle
Rules:
- Focus on NEW or CHANGED error handling in the diff
- Do NOT flag existing error handling patterns in untouched code
- Do NOT suggest adding error handling to code that legitimately cannot fail (pure functions, type-safe internal calls)
- "Critical" for swallowed errors in data-mutation paths, "major" for missing error handling on external calls, "minor" for missing logging
## Repo-Specific Error Handling
- User-facing error messages must be actionable and friendly (per AGENTS.md)
- Use the shared `useErrorHandling` composable (`src/composables/useErrorHandling.ts`) for centralized error handling:
- `wrapWithErrorHandling` / `wrapWithErrorHandlingAsync` automatically catch errors and surface them as toast notifications via `useToastStore`
- `toastErrorHandler` can be used directly for custom error handling flows
- Supports `ErrorRecoveryStrategy` for retry/fallback patterns (e.g., reauthentication, network reconnect)
- API errors from `api.get()`/`api.post()` should be caught and surfaced to the user via `useToastStore` (`src/platform/updates/common/toastStore.ts`)
- Electron/desktop code paths: IPC errors should be caught and not crash the renderer process
- Workflow execution errors should be displayed in the UI status bar, not silently swallowed

View File

@@ -1,60 +0,0 @@
/**
* Strict ESLint config for the sonarjs-lint review check.
*
* Uses eslint-plugin-sonarjs to get SonarQube-grade analysis without a server.
* This config is NOT used for regular development linting — only for the
* code review checks' static analysis pass.
*
* Install: pnpm add -D eslint eslint-plugin-sonarjs
* Run: pnpm dlx eslint --no-config-lookup --config .agents/checks/eslint.strict.config.js --ext .ts,.js,.vue {files}
*/
import sonarjs from 'eslint-plugin-sonarjs'
export default [
sonarjs.configs.recommended,
{
plugins: {
sonarjs
},
rules: {
// Bug detection
'sonarjs/no-all-duplicated-branches': 'error',
'sonarjs/no-element-overwrite': 'error',
'sonarjs/no-identical-conditions': 'error',
'sonarjs/no-identical-expressions': 'error',
'sonarjs/no-one-iteration-loop': 'error',
'sonarjs/no-use-of-empty-return-value': 'error',
'sonarjs/no-collection-size-mischeck': 'error',
'sonarjs/no-duplicated-branches': 'error',
'sonarjs/no-identical-functions': 'error',
'sonarjs/no-redundant-jump': 'error',
'sonarjs/no-unused-collection': 'error',
'sonarjs/no-gratuitous-expressions': 'error',
// Code smell detection
'sonarjs/cognitive-complexity': ['error', 15],
'sonarjs/no-duplicate-string': ['error', { threshold: 3 }],
'sonarjs/no-redundant-boolean': 'error',
'sonarjs/no-small-switch': 'error',
'sonarjs/prefer-immediate-return': 'error',
'sonarjs/prefer-single-boolean-return': 'error',
'sonarjs/no-inverted-boolean-check': 'error',
'sonarjs/no-nested-template-literals': 'error'
},
languageOptions: {
ecmaVersion: 2024,
sourceType: 'module'
}
},
{
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'**/*.config.*',
'**/*.test.*',
'**/*.spec.*'
]
}
]

View File

@@ -1,72 +0,0 @@
---
name: import-graph
description: Validates import rules, detects circular dependencies, and enforces layer boundaries using dependency-cruiser
severity-default: high
tools: [Bash, Read]
---
Run dependency-cruiser import graph analysis on changed files to detect circular dependencies, orphan modules, and import rule violations.
> **Note:** The circular dependency scan in step 4 targets `src/` specifically, since this is a frontend app with source code under `src/`.
## Steps
1. Check if dependency-cruiser is available:
```bash
pnpm dlx dependency-cruiser --version
```
If not available, skip this check and report: "Skipped: dependency-cruiser not available. Install with: `pnpm add -D dependency-cruiser`"
> **Install:** `pnpm add -D dependency-cruiser`
2. Identify changed directories from the diff.
3. Determine config to use:
- If `.dependency-cruiser.js` or `.dependency-cruiser.cjs` exists in the repo root, use it (dependency-cruiser auto-detects it). This config may enforce layer architecture rules (e.g., base → platform → workbench → renderer import direction):
```bash
pnpm dlx dependency-cruiser --output-type json <changed_directories> 2>/dev/null
```
- If no config exists, run with built-in defaults:
```bash
pnpm dlx dependency-cruiser --no-config --output-type json <changed_directories> 2>/dev/null
```
4. Also check for circular dependencies specifically across `src/`:
```bash
pnpm dlx dependency-cruiser --no-config --output-type json --do-not-follow "node_modules" --include-only "^src" src 2>/dev/null
```
Look for modules where `.circular == true` in the output.
5. Parse the JSON output. Each violation has:
- `rule.name`: the violated rule
- `rule.severity`: error, warn, info
- `from`: importing module
- `to`: imported module
6. Map violation severity:
- `error` → `major`
- `warn` → `minor`
- `info` → `nitpick`
- Circular dependencies → `major` (category: architecture)
- Orphan modules → `nitpick` (category: dx)
7. Report each violation with: the rule name, source and target modules, file path, and a suggestion (usually move the import or extract an interface).
## What It Catches
| Rule | What It Detects |
| ------------------------ | ---------------------------------------------------- |
| `no-circular` | Circular dependency chains (A → B → C → A) |
| `no-orphans` | Modules with no incoming or outgoing dependencies |
| `not-to-dev-dep` | Production code importing devDependencies |
| `no-duplicate-dep-types` | Same dependency in multiple sections of package.json |
| Custom layer rules | Import direction violations (e.g., base → platform) |
## Error Handling
- If pnpm dlx is not available, skip and report the error.
- If the config file fails to parse, fall back to `--no-config`.
- If there are more than 50 violations, report the first 20 and note the total count.
- If no violations are found, report "No issues found."

View File

@@ -1,27 +0,0 @@
---
name: memory-leak
description: Scans for memory leak patterns including event listeners without cleanup, timers not cleared, and unbounded caches
severity-default: high
tools: [Read, Grep]
---
You are a memory leak specialist reviewing a code diff. Focus exclusively on patterns that cause memory to grow unboundedly over time.
Check for:
1. **Event listeners without cleanup** - addEventListener without corresponding removeEventListener, especially in Vue onMounted without onBeforeUnmount cleanup
2. **Timers not cleared** - setInterval/setTimeout started in component lifecycle without clearInterval/clearTimeout on unmount
3. **Observer patterns without disconnect** - MutationObserver, IntersectionObserver, ResizeObserver created without .disconnect() on cleanup
4. **WebSocket/Worker connections** - opened connections never closed on component unmount or route change
5. **Unbounded caches** - Maps, Sets, or arrays that grow with usage but never evict entries, especially keyed by user input or dynamic IDs
6. **Stale closures holding references** - closures in event handlers or callbacks that capture large objects or DOM nodes and prevent garbage collection
7. **RequestAnimationFrame without cancel** - rAF loops started without cancelAnimationFrame on cleanup
8. **Vue-specific leaks** - watch/watchEffect without stop(), computed that captures reactive dependencies it shouldn't, provide/inject holding stale references
9. **Global state accumulation** - pushing to global arrays/maps without ever removing entries, console.log keeping object references in dev
Rules:
- Focus on NEW leak patterns introduced in the diff
- Do NOT flag existing cleanup patterns that are correct
- Every finding must explain the specific lifecycle scenario where the leak occurs (e.g., "when user navigates away from this view, the interval keeps running")
- "Critical" for leaks in hot paths or long-lived pages, "major" for component-level leaks, "minor" for dev-only or cold-path leaks

View File

@@ -1,60 +0,0 @@
---
name: pattern-compliance
description: Checks code against repository conventions from AGENTS.md and established patterns
severity-default: medium
tools: [Read, Grep]
---
Check code against repository conventions and framework patterns.
Steps:
1. Read AGENTS.md (and any directory-specific guidance files) for project-specific conventions
2. Read each changed file
3. Check against the conventions found in AGENTS.md and these standard patterns:
### TypeScript
- No `any` types or `as any` assertions
- No `@ts-ignore` without explanatory comment
- Separate type imports (`import type { ... }`)
- Use `import type { ... }` for type-only imports
- Explicit return types on exported functions
- Use `es-toolkit` for utility functions, NOT lodash. Flag any new `import ... from 'lodash'` or `import ... from 'lodash/*'`
- Never use `z.any()` in Zod schemas — use `z.unknown()` and narrow
### Vue (if applicable)
- Composition API with `<script setup lang="ts">`
- Reactive props destructuring (not `withDefaults` pattern)
- New components must use `<script setup lang="ts">` with reactive props destructuring (Vue 3.5 style): `const { color = 'blue' } = defineProps<Props>()`
- Separate type imports from value imports
- All user-facing strings must use `vue-i18n` (`$t()` in templates, `t()` in script). Flag hardcoded English strings in templates that should be translated. The locale file is `src/locales/en/main.json`
### Tailwind (if applicable)
- No `dark:` variants (use semantic theme tokens)
- Use `cn()` utility for conditional classes
- No `!important` in utility classes
- Tailwind 4: CSS variable references use parentheses syntax: `h-(--my-var)` NOT `h-[--my-var]`
- Use design tokens: `bg-secondary-background`, `text-muted-foreground`, `border-border-default`
- No `<style>` blocks in Vue SFCs — use inline Tailwind only
### Testing
- Behavioral tests, not change detectors
- No mock-heavy tests that don't test real behavior
- Test names describe behavior, not implementation
### General
- No commented-out code
- No `console.log` in production code (unless intentional logging)
- No hardcoded URLs, credentials, or environment-specific values
- Package manager is `pnpm`. Never use `npm`, `npx`, or `yarn`. Use `pnpm dlx` for one-off package execution
- Sanitize HTML with `DOMPurify.sanitize()`, never raw `innerHTML` or `v-html` without it
Rules:
- Only flag ACTUAL violations, not hypothetical ones
- AGENTS.md conventions take priority over default patterns if they conflict

View File

@@ -1,35 +0,0 @@
---
name: performance-profiler
description: Reviews code for performance issues including algorithmic complexity, unnecessary work, and bundle size impact
severity-default: medium
tools: [Read, Grep]
---
You are a performance engineer reviewing a code diff. Focus exclusively on performance issues.
Check for:
1. **Algorithmic complexity** - O(n²) or worse in loops, nested iterations over large collections
2. **Unnecessary re-computation** - repeated work in render cycles, missing memoization for expensive ops
3. **Memory leaks** - event listeners not cleaned up, growing caches without eviction, closures holding references
4. **N+1 queries** - database/API calls inside loops
5. **Bundle size** - large imports that could be tree-shaken, dynamic imports for heavy modules
6. **Rendering performance** - unnecessary re-renders, layout thrashing, expensive computed properties
7. **Data structures** - using arrays for lookups instead of maps/sets, unnecessary copying of large objects
8. **Async patterns** - sequential awaits that could be parallel, missing abort controllers
Rules:
- ONLY report actual performance issues, not premature optimization suggestions
- Distinguish between hot paths (major) and cold paths (minor)
- Include Big-O analysis when relevant
- Do NOT suggest micro-optimizations that a JIT compiler handles
- Quantify the impact when possible: "This is O(n²) where n = number of users"
## Repo-Specific Performance Concerns
- **LiteGraph canvas rendering** is the primary hot path. Operations inside `LGraphNode.onDrawForeground`, `onDrawBackground`, `processMouseMove` run every frame at 60fps. Any O(n) or worse operation here on the node/link collections is critical.
- **Node definition lookups** happen frequently — these should use Maps, not array.find()
- **Workflow serialization/deserialization** can involve large JSON objects (1000+ nodes). Watch for deep copies or unnecessary re-parsing.
- **Vue reactivity in canvas code** — reactive getters triggered during canvas render cause performance issues. Canvas-facing code should read raw values, not reactive refs.
- **Bundle size** — check for large imports that could be dynamically imported. The build uses Vite with `build:analyze` for bundle visualization.

View File

@@ -1,44 +0,0 @@
---
name: regression-risk
description: Detects potential regressions by analyzing git blame history of modified lines
severity-default: high
tools: [Bash, Read, Grep]
---
Perform regression risk analysis on the current changes using git blame.
## Method
1. Determine the base branch by examining git context (e.g., `git merge-base origin/main HEAD`, or check the PR's target branch). Never use `HEAD~1` as the base — it compares against the PR's own prior commit and causes false positives.
2. Get the PR's own commits: `git log --format=%H <base>..HEAD`
3. For each changed file, run: `git diff <base>...HEAD -- <file>`
4. Extract the modified line ranges from the diff (lines removed or changed in the base version).
5. For each modified line range, check git blame in the base version:
`git blame <base> -L <start>,<end> -- <file>`
6. Look for blame commits whose messages match bugfix patterns:
- Contains: fix, bug, patch, hotfix, revert, regression, CVE
- Ignore: "fix lint", "fix typo", "fix format", "fix style"
7. **Filter out false positives.** If the blamed commit SHA is in the PR's own commits, skip it.
8. For each verified bugfix line being modified, report as a finding.
## What to Report
For each finding, include:
- The file and line number
- The original bugfix commit (short SHA and subject)
- The date of the original fix
- A suggestion to verify the original bug scenario still works and to add a regression test if one doesn't exist
## Shallow Clone Limitations
When working with shallow clones, `git blame` may not have full history. If blame fails with "no such path in revision" or shows truncated history, report only findings where blame succeeds and note the limitation.
## Edge Cases
| Situation | Action |
| ------------------------ | -------------------------------- |
| Shallow clone (no blame) | Report what succeeds, note limit |
| Blame shows PR's own SHA | Skip finding (false positive) |
| File renamed | Try blame with `--follow` |
| Binary file | Skip file |

View File

@@ -1,34 +0,0 @@
---
name: security-auditor
description: Reviews code for security vulnerabilities aligned with OWASP Top 10
severity-default: critical
tools: [Read, Grep]
---
You are a security auditor reviewing a code diff. Focus exclusively on security vulnerabilities.
Check for:
1. **Injection** - SQL injection, command injection, template injection, XSS (stored/reflected/DOM)
2. **Authentication/Authorization** - auth bypass, privilege escalation, missing access checks
3. **Data exposure** - secrets in code, PII in logs, sensitive data in error messages, overly broad API responses
4. **Cryptography** - weak algorithms, hardcoded keys, predictable tokens, missing encryption
5. **Input validation** - missing sanitization, path traversal, SSRF, open redirects
6. **Dependency risks** - known vulnerable patterns, unsafe deserialization
7. **Configuration** - CORS misconfiguration, missing security headers, debug mode in production
8. **Race conditions with security impact** - TOCTOU, double-spend, auth state races
Rules:
- ONLY report security issues, not general bugs or style
- All findings must be severity "critical" or "major"
- Explain the attack vector: who can exploit this and how
- Do NOT report theoretical issues without a plausible attack scenario
- Reference OWASP category when applicable
## Repo-Specific Patterns
- HTML sanitization must use `DOMPurify.sanitize()` — flag any `v-html` or `innerHTML` without DOMPurify
- API calls should use `api.get(api.apiURL(...))` helpers, not raw `fetch('/api/...')` — direct URL construction can bypass auth
- Firebase/Sentry credentials are configured via environment — flag any hardcoded Firebase config objects
- Electron IPC: check for unsafe `ipcRenderer.send` patterns in desktop code paths

View File

@@ -1,54 +0,0 @@
---
name: semgrep-sast
description: Runs Semgrep SAST with auto-configured rules for JS/TS/Vue
severity-default: high
tools: [Bash, Read]
---
Run Semgrep static analysis on changed files to detect security vulnerabilities, dangerous patterns, and framework-specific issues.
## Steps
1. Check if semgrep is installed:
```bash
semgrep --version
```
If not installed, skip this check and report: "Skipped: semgrep not installed. Install with: `pip3 install semgrep`"
2. Identify changed files (`.ts`, `.js`, `.vue`) from the diff.
If none are found, skip and report: "Skipped: no changed JS/TS/Vue files."
3. Run semgrep against changed files:
```bash
semgrep --config=auto --json --quiet <changed_files>
```
4. Parse the JSON output (`.results[]` array). For each finding, map severity:
- Semgrep `ERROR` → `critical`
- Semgrep `WARNING` → `major`
- Semgrep `INFO` → `minor`
5. Report each finding with:
- The semgrep rule ID (`check_id`)
- File path and line number (`path`, `start.line`)
- The message from `extra.message`
- A fix suggestion from `extra.fix` if available, otherwise general remediation advice
## What Semgrep Catches
With `--config=auto`, Semgrep loads community-maintained rules for:
- **Security vulnerabilities:** injection, XSS, SSRF, path traversal, open redirect
- **Dangerous patterns:** eval(), innerHTML, dangerouslySetInnerHTML, exec()
- **Crypto issues:** weak hashing, hardcoded secrets, insecure random
- **Best practices:** missing security headers, unsafe deserialization
- **Framework-specific:** Express, React, Vue security patterns
## Error Handling
- If semgrep config download fails, skip and report the error.
- If semgrep fails to parse a specific file, skip that file and continue with others.
- If semgrep produces no findings, report "No issues found."

View File

@@ -1,59 +0,0 @@
---
name: sonarjs-lint
description: Runs SonarQube-grade static analysis using eslint-plugin-sonarjs
severity-default: high
tools: [Bash, Read]
---
Run eslint-plugin-sonarjs analysis on changed files to detect bugs, code smells, and security patterns without needing a SonarQube server.
## Steps
1. Check if eslint is available:
```bash
pnpm dlx eslint --version
```
If pnpm dlx or eslint is unavailable, skip this check and report: "Skipped: eslint not available. Ensure Node.js and pnpm dlx are installed."
2. Identify changed files (`.ts`, `.js`, `.vue`) from the diff.
3. Determine eslint config to use. This check uses a **strict sonarjs-specific config** (not the project's own eslint config, which is less strict):
- Look for the colocated strict config at `.agents/checks/eslint.strict.config.js`
- If found, run with `--config .agents/checks/eslint.strict.config.js`
- **Fallback:** if the strict config cannot be found or fails to load, skip this check and report: "Skipped: .agents/checks/eslint.strict.config.js missing; SonarJS rules require explicit config."
4. Run eslint against changed files:
```bash
# Use the strict config
pnpm dlx --yes --package eslint-plugin-sonarjs eslint --no-config-lookup --config .agents/checks/eslint.strict.config.js --format json <changed_files> 2>/dev/null || true
```
5. Parse the JSON array of file results. For each eslint message, map severity:
- `severity 2` (error) → `major`
- `severity 1` (warning) → `minor`
6. Categorize findings by rule ID:
- Rule IDs starting with `sonarjs/no-` → category: `logic`
- Rule IDs containing `cognitive-complexity` → category: `dx`
- Other sonarjs rules → category: `style`
7. Report each finding with:
- The rule ID
- File path and line number
- The message from eslint
- A fix suggestion based on the rule
## What This Catches
- **Bug detection:** duplicated branches, element overwrite, identical conditions/expressions, one-iteration loops, empty return values
- **Code smells:** cognitive complexity (threshold: 15), duplicate strings, redundant booleans, small switches
- **Security patterns:** via sonarjs recommended ruleset
## Error Handling
- If eslint fails to parse a Vue file, skip that file and continue with others.
- If the plugin fails to install, skip and report the error.
- If eslint produces no output or errors, report "No issues found."

View File

@@ -1,37 +0,0 @@
---
name: test-quality
description: Reviews test code for quality issues and coverage gaps
severity-default: medium
tools: [Read, Grep]
---
You are a test quality reviewer. Evaluate the tests included with (or missing from) this code change.
Check for:
1. **Missing tests** - new behavior without test coverage, modified logic without updated tests
2. **Change-detector tests** - tests that assert implementation details instead of behavior (testing that a function was called, not what it produces)
3. **Mock-heavy tests** - tests with so many mocks they don't test real behavior
4. **Snapshot abuse** - large snapshots that no one reviews, snapshots of implementation details
5. **Fragile assertions** - tests that break on unrelated changes, order-dependent tests
6. **Missing edge cases** - happy path only, no empty/null/error scenarios tested
7. **Test readability** - unclear test names, complex setup that obscures intent, shared mutable state between tests
8. **Test isolation** - tests depending on execution order, shared state, external services without mocking
Rules:
- Focus on test quality and coverage gaps, not production code bugs
- "Major" for missing tests on critical logic, "minor" for missing edge case tests
- A change that adds no tests is only an issue if the change adds behavior
- Refactors without behavior changes don't need new tests
- Prefer behavioral tests: test inputs and outputs, not internal implementation
- This repo uses **colocated tests**: `.test.ts` files live next to their source files (e.g., `MyComponent.test.ts` beside `MyComponent.vue`). When checking for missing tests, look for a colocated `.test.ts` file, not a separate `tests/` directory
## Repo-Specific Testing Conventions
- Tests use **Vitest** (not Jest) — run with `pnpm test:unit`
- Test files are **colocated**: `MyComponent.test.ts` next to `MyComponent.vue`
- Use `@vue/test-utils` for component testing, `@pinia/testing` (`createTestingPinia`) for store tests
- Browser/E2E tests use **Playwright** in `browser_tests/` — run with `pnpm test:browser:local`
- Mock composables using the singleton factory pattern inside `vi.mock()` — see `docs/testing/unit-testing.md` for the pattern
- Never use `any` in test code either — proper typing applies to tests too

View File

@@ -1,47 +0,0 @@
---
name: vue-patterns
description: Reviews Vue 3.5+ code for framework-specific anti-patterns
severity-default: medium
tools: [Read, Grep]
---
You are a Vue 3.5 framework specialist reviewing a code diff. Focus on Vue-specific patterns, anti-patterns, and missed framework features.
Check for:
1. **Options API in new files** - new .vue files using Options API instead of Composition API with `<script setup>`. Modifications to existing Options API files are fine.
2. **Reactivity anti-patterns** - destructuring reactive objects losing reactivity, using `ref()` for objects that should be `reactive()`, accessing `.value` inside templates, incorrectly using `toRefs`/`toRef`
3. **Watch/watchEffect cleanup** - watchers without cleanup functions when they set up side effects (timers, listeners, subscriptions)
4. **Flush timing issues** - DOM access in watch callbacks without `{ flush: 'post' }`, `nextTick` misuse, accessing template refs before mount
5. **defineEmits typing** - using array syntax `defineEmits(['event'])` instead of TypeScript syntax `defineEmits<{...}>()`
6. **defineExpose misuse** - exposing internal state via `defineExpose` when events would be more appropriate (expose is for imperative methods: validate, focus, open)
7. **Prop drilling** - passing props through 3+ component levels where provide/inject would be cleaner
8. **VueUse opportunities** - manual implementations of common composables that VueUse already provides (useLocalStorage, useEventListener, useDebounceFn, useIntersectionObserver, etc.)
9. **Computed vs method** - methods used in templates for derived state that should be computed properties, or computed properties that have side effects
10. **PrimeVue usage in new code** - New components must NOT use PrimeVue. This project is migrating to shadcn-vue (Reka UI primitives). If new code imports from `primevue/*`, flag it and suggest the shadcn-vue equivalent.
Available shadcn-vue replacements in `src/components/ui/`:
- `button/` — Button, variants
- `select/` — Select, SelectTrigger, SelectContent, SelectItem
- `textarea/` — Textarea
- `toggle-group/` — ToggleGroup, ToggleGroupItem
- `slider/` — Slider
- `skeleton/` — Skeleton
- `stepper/` — Stepper
- `tags-input/` — TagsInput
- `search-input/` — SearchInput
- `Popover.vue` — Popover
For Reka UI primitives not yet wrapped, create a new component in `src/components/ui/` following the pattern in existing components (see `src/components/ui/AGENTS.md`): use `useForwardProps`, `cn()`, design tokens.
Modifications to existing PrimeVue-based components are acceptable but should note the migration opportunity.
Rules:
- Only review .vue and composable .ts files — skip stores, services, utils
- Do NOT flag existing Options API files being modified (only flag NEW files)
- Flag new PrimeVue imports — the project is migrating to shadcn-vue/Reka UI
- When suggesting shadcn-vue alternatives, reference `src/components/ui/AGENTS.md` for the component creation pattern
- Use Iconify icons (`<i class="icon-[lucide--check]" />`) not PrimeIcons
- "Major" for reactivity bugs and flush timing, "minor" for API style and VueUse opportunities, "nitpick" for preference-level patterns

View File

@@ -3,15 +3,12 @@
## Task: Add English translations for all new localized strings ## Task: Add English translations for all new localized strings
### Step 1: Identify new translation keys ### Step 1: Identify new translation keys
Find all translation keys that were added in the current branch's changes. These keys appear as arguments to translation functions: `t()`, `st()`, `$t()`, or similar i18n functions. Find all translation keys that were added in the current branch's changes. These keys appear as arguments to translation functions: `t()`, `st()`, `$t()`, or similar i18n functions.
### Step 2: Add translations to English locale file ### Step 2: Add translations to English locale file
For each new translation key found, add the corresponding English text to the file `src/locales/en/main.json`. For each new translation key found, add the corresponding English text to the file `src/locales/en/main.json`.
### Key-to-JSON mapping rules: ### Key-to-JSON mapping rules:
- Translation keys use dot notation to represent nested JSON structure - Translation keys use dot notation to represent nested JSON structure
- Convert dot notation to nested JSON objects when adding to the locale file - Convert dot notation to nested JSON objects when adding to the locale file
- Example: The key `g.user.name` maps to: - Example: The key `g.user.name` maps to:
@@ -26,7 +23,6 @@ For each new translation key found, add the corresponding English text to the fi
``` ```
### Important notes: ### Important notes:
1. **Only modify the English locale file** (`src/locales/en/main.json`) 1. **Only modify the English locale file** (`src/locales/en/main.json`)
2. **Do not modify other locale files** - translations for other languages are automatically generated by the `i18n.yaml` workflow 2. **Do not modify other locale files** - translations for other languages are automatically generated by the `i18n.yaml` workflow
3. **Exception for manual translations**: Only add translations to non-English locale files if: 3. **Exception for manual translations**: Only add translations to non-English locale files if:
@@ -34,7 +30,6 @@ For each new translation key found, add the corresponding English text to the fi
- The automated translation would likely be incorrect due to technical terminology or context-specific meaning - The automated translation would likely be incorrect due to technical terminology or context-specific meaning
### Example workflow: ### Example workflow:
1. If you added `t('settings.advanced.enable')` in a Vue component 1. If you added `t('settings.advanced.enable')` in a Vue component
2. Add to `src/locales/en/main.json`: 2. Add to `src/locales/en/main.json`:
```json ```json

View File

@@ -15,7 +15,6 @@ To post inline comments, you will use the GitHub API via the `gh` command. Here'
- Run: `gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid'` to get the latest commit SHA - Run: `gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid'` to get the latest commit SHA
2. For each issue you find, post an inline comment using this exact command structure (as a single line): 2. For each issue you find, post an inline comment using this exact command structure (as a single line):
``` ```
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/OWNER/REPO/pulls/$PR_NUMBER/comments -f body="YOUR_COMMENT_BODY" -f commit_id="COMMIT_SHA" -f path="FILE_PATH" -F line=LINE_NUMBER -f side="RIGHT" gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/OWNER/REPO/pulls/$PR_NUMBER/comments -f body="YOUR_COMMENT_BODY" -f commit_id="COMMIT_SHA" -f path="FILE_PATH" -F line=LINE_NUMBER -f side="RIGHT"
``` ```
@@ -23,15 +22,13 @@ To post inline comments, you will use the GitHub API via the `gh` command. Here'
3. Format your comment body using actual newlines in the command. Use a heredoc or construct the body with proper line breaks: 3. Format your comment body using actual newlines in the command. Use a heredoc or construct the body with proper line breaks:
``` ```
COMMENT_BODY="**[category] severity Priority** COMMENT_BODY="**[category] severity Priority**
```
**Issue**: Brief description of the problem **Issue**: Brief description of the problem
**Context**: Why this matters **Context**: Why this matters
**Suggestion**: How to fix it" **Suggestion**: How to fix it"
```
```
Then use: `-f body="$COMMENT_BODY"`
Then use: `-f body="$COMMENT_BODY"`
## Phase 1: Environment Setup and PR Context ## Phase 1: Environment Setup and PR Context
@@ -61,12 +58,10 @@ This is critical for better file inspection:
1. Get PR metadata: `gh pr view $PR_NUMBER --json files,title,body,additions,deletions,baseRefName,headRefName > pr_info.json` 1. Get PR metadata: `gh pr view $PR_NUMBER --json files,title,body,additions,deletions,baseRefName,headRefName > pr_info.json`
2. Extract branch names from pr_info.json using jq 2. Extract branch names from pr_info.json using jq
3. Fetch and checkout the PR branch: 3. Fetch and checkout the PR branch:
``` ```
git fetch origin "pull/$PR_NUMBER/head:pr-$PR_NUMBER"
git fetch origin "pull/$PR_NUMBER/head:pr-$PR_NUMBER" git checkout "pr-$PR_NUMBER"
git checkout "pr-$PR_NUMBER" ```
```
### Step 1.4: Get Changed Files and Diffs ### Step 1.4: Get Changed Files and Diffs
@@ -105,9 +100,9 @@ Intelligently load only relevant knowledge:
1. Use GitHub API to discover available knowledge folders: `https://api.github.com/repos/Comfy-Org/comfy-claude-prompt-library/contents/.claude/knowledge` 1. Use GitHub API to discover available knowledge folders: `https://api.github.com/repos/Comfy-Org/comfy-claude-prompt-library/contents/.claude/knowledge`
2. For each knowledge folder, check if it's relevant by searching for the folder name in: 2. For each knowledge folder, check if it's relevant by searching for the folder name in:
- Changed file paths - Changed file paths
- PR title - PR title
- PR body - PR body
3. If relevant, download all files from that knowledge folder 3. If relevant, download all files from that knowledge folder
### Step 2.4: Load Validation Rules ### Step 2.4: Load Validation Rules
@@ -198,14 +193,12 @@ Consider:
For each issue found, create a concise inline comment with this structure: For each issue found, create a concise inline comment with this structure:
``` ```
**[category] severity Priority** **[category] severity Priority**
**Issue**: Brief description of the problem **Issue**: Brief description of the problem
**Context**: Why this matters **Context**: Why this matters
**Suggestion**: How to fix it **Suggestion**: How to fix it
```
````
Categories: architecture/security/performance/quality Categories: architecture/security/performance/quality
Severities: critical/high/medium/low Severities: critical/high/medium/low
@@ -221,7 +214,7 @@ For EACH issue:
```bash ```bash
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/OWNER/REPO/pulls/$PR_NUMBER/comments -f body="$COMMENT_BODY" -f commit_id="COMMIT_SHA" -f path="FILE_PATH" -F line=LINE_NUMBER -f side="RIGHT" gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/OWNER/REPO/pulls/$PR_NUMBER/comments -f body="$COMMENT_BODY" -f commit_id="COMMIT_SHA" -f path="FILE_PATH" -F line=LINE_NUMBER -f side="RIGHT"
```` ```
CRITICAL: The entire command must be on one line. Use actual values, not placeholders. CRITICAL: The entire command must be on one line. Use actual values, not placeholders.
@@ -230,14 +223,12 @@ CRITICAL: The entire command must be on one line. Use actual values, not placeho
Here's an example of how to review a file with a security issue: Here's an example of how to review a file with a security issue:
1. First, get the repository info: 1. First, get the repository info:
```bash ```bash
gh repo view --json owner,name gh repo view --json owner,name
# Output: {"owner":{"login":"Comfy-Org"},"name":"ComfyUI_frontend"} # Output: {"owner":{"login":"Comfy-Org"},"name":"ComfyUI_frontend"}
``` ```
2. Get the commit SHA: 2. Get the commit SHA:
```bash ```bash
gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid' gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid'
# Output: abc123def456 # Output: abc123def456
@@ -249,17 +240,14 @@ Here's an example of how to review a file with a security issue:
```bash ```bash
# First, create the comment body with proper newlines # First, create the comment body with proper newlines
COMMENT_BODY="**[security] critical Priority** COMMENT_BODY="**[security] critical Priority**
```
**Issue**: SQL injection vulnerability - user input directly concatenated into query **Issue**: SQL injection vulnerability - user input directly concatenated into query
**Context**: Allows attackers to execute arbitrary SQL commands **Context**: Allows attackers to execute arbitrary SQL commands
**Suggestion**: Use parameterized queries or prepared statements" **Suggestion**: Use parameterized queries or prepared statements"
# Then post the comment (as a single line) # Then post the comment (as a single line)
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/Comfy-Org/ComfyUI_frontend/pulls/$PR_NUMBER/comments -f body="$COMMENT_BODY" -f commit_id="abc123def456" -f path="src/db/queries.js" -F line=42 -f side="RIGHT"
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/Comfy-Org/ComfyUI_frontend/pulls/$PR_NUMBER/comments -f body="$COMMENT_BODY" -f commit_id="abc123def456" -f path="src/db/queries.js" -F line=42 -f side="RIGHT" ```
```
Repeat this process for every issue you find in the PR. Repeat this process for every issue you find in the PR.
@@ -294,9 +282,9 @@ Analyze the PR to determine its type:
1. Extract PR title and body from pr_info.json 1. Extract PR title and body from pr_info.json
2. Count files, additions, and deletions 2. Count files, additions, and deletions
3. Determine PR type: 3. Determine PR type:
- Feature: Check for tests, documentation, backward compatibility - Feature: Check for tests, documentation, backward compatibility
- Bug fix: Verify root cause addressed, includes regression tests - Bug fix: Verify root cause addressed, includes regression tests
- Refactor: Ensure behavior preservation, tests still pass - Refactor: Ensure behavior preservation, tests still pass
## Phase 7: Generate Comprehensive Summary ## Phase 7: Generate Comprehensive Summary
@@ -304,17 +292,16 @@ After ALL inline comments are posted, create a summary:
1. Calculate total issues by category and severity 1. Calculate total issues by category and severity
2. Use `gh pr review $PR_NUMBER --comment` to post a summary with: 2. Use `gh pr review $PR_NUMBER --comment` to post a summary with:
- Review disclaimer - Review disclaimer
- Issue distribution (counts by severity) - Issue distribution (counts by severity)
- Category breakdown - Category breakdown
- Key findings for each category - Key findings for each category
- Positive observations - Positive observations
- References to guidelines - References to guidelines
- Next steps - Next steps
Include in the summary: Include in the summary:
``` ```
# Comprehensive PR Review # Comprehensive PR Review
This review is generated by Claude. It may not always be accurate, as with human reviewers. If you believe that any of the comments are invalid or incorrect, please state why for each. For others, please implement the changes in one way or another. This review is generated by Claude. It may not always be accurate, as with human reviewers. If you believe that any of the comments are invalid or incorrect, please state why for each. For others, please implement the changes in one way or another.
@@ -325,14 +312,12 @@ This review is generated by Claude. It may not always be accurate, as with human
**Impact**: [X] additions, [Y] deletions across [Z] files **Impact**: [X] additions, [Y] deletions across [Z] files
### Issue Distribution ### Issue Distribution
- Critical: [CRITICAL_COUNT] - Critical: [CRITICAL_COUNT]
- High: [HIGH_COUNT] - High: [HIGH_COUNT]
- Medium: [MEDIUM_COUNT] - Medium: [MEDIUM_COUNT]
- Low: [LOW_COUNT] - Low: [LOW_COUNT]
### Category Breakdown ### Category Breakdown
- Architecture: [ARCHITECTURE_ISSUES] issues - Architecture: [ARCHITECTURE_ISSUES] issues
- Security: [SECURITY_ISSUES] issues - Security: [SECURITY_ISSUES] issues
- Performance: [PERFORMANCE_ISSUES] issues - Performance: [PERFORMANCE_ISSUES] issues
@@ -341,42 +326,33 @@ This review is generated by Claude. It may not always be accurate, as with human
## Key Findings ## Key Findings
### Architecture & Design ### Architecture & Design
[Detailed architectural analysis based on repository patterns] [Detailed architectural analysis based on repository patterns]
### Security Considerations ### Security Considerations
[Security implications beyond basic vulnerabilities] [Security implications beyond basic vulnerabilities]
### Performance Impact ### Performance Impact
[Performance analysis including bundle size, render impact] [Performance analysis including bundle size, render impact]
### Integration Points ### Integration Points
[How this affects other systems, extensions, etc.] [How this affects other systems, extensions, etc.]
## Positive Observations ## Positive Observations
[What was done well, good patterns followed] [What was done well, good patterns followed]
## References ## References
- [Repository Architecture Guide](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/project-summaries-for-agents/ComfyUI_frontend/REPOSITORY_GUIDE.md) - [Repository Architecture Guide](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/project-summaries-for-agents/ComfyUI_frontend/REPOSITORY_GUIDE.md)
- [Frontend Standards](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/.claude/commands/validation/frontend-code-standards.md) - [Frontend Standards](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/.claude/commands/validation/frontend-code-standards.md)
- [Security Guidelines](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/.claude/commands/validation/security-audit.md) - [Security Guidelines](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/.claude/commands/validation/security-audit.md)
## Next Steps ## Next Steps
1. Address critical issues before merge 1. Address critical issues before merge
2. Consider architectural feedback for long-term maintainability 2. Consider architectural feedback for long-term maintainability
3. Add tests for uncovered scenarios 3. Add tests for uncovered scenarios
4. Update documentation if needed 4. Update documentation if needed
--- ---
*This is a comprehensive automated review. For architectural decisions requiring human judgment, please request additional manual review.*
_This is a comprehensive automated review. For architectural decisions requiring human judgment, please request additional manual review._
``` ```
## Important Guidelines ## Important Guidelines
@@ -399,5 +375,4 @@ This is a COMPREHENSIVE review, not a linting pass. Provide the same quality fee
5. Phase 6: Consider PR type for additional checks 5. Phase 6: Consider PR type for additional checks
6. Phase 7: Post comprehensive summary ONLY after all inline comments 6. Phase 7: Post comprehensive summary ONLY after all inline comments
Remember: Individual inline comments for each issue, then one final summary. Never batch issues into a single comment. Remember: Individual inline comments for each issue, then one final summary. Never batch issues into a single comment.
```

View File

@@ -7,9 +7,8 @@ Create a frontend release with version type: $ARGUMENTS
Expected format: Version increment type and optional description Expected format: Version increment type and optional description
Examples: Examples:
- `patch` - Bug fixes only - `patch` - Bug fixes only
- `minor` - New features, backward compatible - `minor` - New features, backward compatible
- `major` - Breaking changes - `major` - Breaking changes
- `prerelease` - Alpha/beta/rc releases - `prerelease` - Alpha/beta/rc releases
- `patch "Critical security fixes"` - With custom description - `patch "Critical security fixes"` - With custom description
@@ -22,9 +21,8 @@ If no arguments provided, the command will always perform prerelease if the curr
## Prerequisites ## Prerequisites
Before starting, ensure: Before starting, ensure:
- You have push access to the repository - You have push access to the repository
- GitHub CLI (`gh`) is authenticated - GitHub CLI (`gh`) is authenticated
- You're on a clean main branch working tree - You're on a clean main branch working tree
- All intended changes are merged to main - All intended changes are merged to main
- You understand the scope of changes being released - You understand the scope of changes being released
@@ -32,7 +30,6 @@ Before starting, ensure:
## Critical Checks Before Starting ## Critical Checks Before Starting
### 1. Check Current Version Status ### 1. Check Current Version Status
```bash ```bash
# Get current version and check if it's a pre-release # Get current version and check if it's a pre-release
CURRENT_VERSION=$(node -p "require('./package.json').version") CURRENT_VERSION=$(node -p "require('./package.json').version")
@@ -43,7 +40,6 @@ fi
``` ```
### 2. Find Last Stable Release ### 2. Find Last Stable Release
```bash ```bash
# Get last stable release tag (no pre-release suffix) # Get last stable release tag (no pre-release suffix)
LAST_STABLE=$(git tag -l "v*" | grep -v "\-" | sort -V | tail -1) LAST_STABLE=$(git tag -l "v*" | grep -v "\-" | sort -V | tail -1)
@@ -53,7 +49,6 @@ echo "Last stable release: $LAST_STABLE"
## Configuration Options ## Configuration Options
**Environment Variables:** **Environment Variables:**
- `RELEASE_SKIP_SECURITY_SCAN=true` - Skip security audit - `RELEASE_SKIP_SECURITY_SCAN=true` - Skip security audit
- `RELEASE_AUTO_APPROVE=true` - Skip some confirmation prompts - `RELEASE_AUTO_APPROVE=true` - Skip some confirmation prompts
- `RELEASE_DRY_RUN=true` - Simulate release without executing - `RELEASE_DRY_RUN=true` - Simulate release without executing
@@ -134,14 +129,13 @@ echo "Last stable release: $LAST_STABLE"
### Step 4: Analyze Dependency Updates ### Step 4: Analyze Dependency Updates
1. **Use pnpm's built-in dependency analysis:** 1. **Use pnpm's built-in dependency analysis:**
```bash ```bash
# Get outdated dependencies with pnpm # Get outdated dependencies with pnpm
pnpm outdated --format table > outdated-deps-${NEW_VERSION}.txt pnpm outdated --format table > outdated-deps-${NEW_VERSION}.txt
# Check for license compliance # Check for license compliance
pnpm licenses ls --json > licenses-${NEW_VERSION}.json pnpm licenses ls --json > licenses-${NEW_VERSION}.json
# Analyze why specific dependencies exist # Analyze why specific dependencies exist
echo "Dependency analysis:" > dep-analysis-${NEW_VERSION}.md echo "Dependency analysis:" > dep-analysis-${NEW_VERSION}.md
MAJOR_DEPS=("vue" "vite" "@vitejs/plugin-vue" "typescript" "pinia") MAJOR_DEPS=("vue" "vite" "@vitejs/plugin-vue" "typescript" "pinia")
@@ -153,23 +147,22 @@ echo "Last stable release: $LAST_STABLE"
``` ```
2. **Check for significant dependency updates:** 2. **Check for significant dependency updates:**
```bash ```bash
# Extract all dependency changes for major version bumps # Extract all dependency changes for major version bumps
OTHER_DEP_CHANGES="" OTHER_DEP_CHANGES=""
# Compare major dependency versions (you can extend this list) # Compare major dependency versions (you can extend this list)
MAJOR_DEPS=("vue" "vite" "@vitejs/plugin-vue" "typescript" "pinia") MAJOR_DEPS=("vue" "vite" "@vitejs/plugin-vue" "typescript" "pinia")
for dep in "${MAJOR_DEPS[@]}"; do for dep in "${MAJOR_DEPS[@]}"; do
PREV_VER=$(echo "$PREV_PACKAGE_JSON" | grep -o "\"$dep\": \"[^\"]*\"" | grep -o '[0-9][^"]*' | head -1 || echo "") PREV_VER=$(echo "$PREV_PACKAGE_JSON" | grep -o "\"$dep\": \"[^\"]*\"" | grep -o '[0-9][^"]*' | head -1 || echo "")
CURR_VER=$(echo "$CURRENT_PACKAGE_JSON" | grep -o "\"$dep\": \"[^\"]*\"" | grep -o '[0-9][^"]*' | head -1 || echo "") CURR_VER=$(echo "$CURRENT_PACKAGE_JSON" | grep -o "\"$dep\": \"[^\"]*\"" | grep -o '[0-9][^"]*' | head -1 || echo "")
if [ "$PREV_VER" != "$CURR_VER" ] && [ -n "$PREV_VER" ] && [ -n "$CURR_VER" ]; then if [ "$PREV_VER" != "$CURR_VER" ] && [ -n "$PREV_VER" ] && [ -n "$CURR_VER" ]; then
# Check if it's a major version change # Check if it's a major version change
PREV_MAJOR=$(echo "$PREV_VER" | cut -d. -f1 | sed 's/[^0-9]//g') PREV_MAJOR=$(echo "$PREV_VER" | cut -d. -f1 | sed 's/[^0-9]//g')
CURR_MAJOR=$(echo "$CURR_VER" | cut -d. -f1 | sed 's/[^0-9]//g') CURR_MAJOR=$(echo "$CURR_VER" | cut -d. -f1 | sed 's/[^0-9]//g')
if [ "$PREV_MAJOR" != "$CURR_MAJOR" ]; then if [ "$PREV_MAJOR" != "$CURR_MAJOR" ]; then
OTHER_DEP_CHANGES="${OTHER_DEP_CHANGES}\n- **${dep}**: ${PREV_VER} → ${CURR_VER} (Major version change)" OTHER_DEP_CHANGES="${OTHER_DEP_CHANGES}\n- **${dep}**: ${PREV_VER} → ${CURR_VER} (Major version change)"
fi fi
@@ -180,12 +173,11 @@ echo "Last stable release: $LAST_STABLE"
### Step 5: Generate GTM Feature Summary ### Step 5: Generate GTM Feature Summary
1. **Collect PR data for analysis:** 1. **Collect PR data for analysis:**
```bash ```bash
# Get list of PR numbers from commits # Get list of PR numbers from commits
PR_NUMBERS=$(git log ${BASE_TAG}..HEAD --oneline --no-merges --first-parent | \ PR_NUMBERS=$(git log ${BASE_TAG}..HEAD --oneline --no-merges --first-parent | \
grep -oE "#[0-9]+" | tr -d '#' | sort -u) grep -oE "#[0-9]+" | tr -d '#' | sort -u)
# Save PR data for each PR # Save PR data for each PR
echo "[" > prs-${NEW_VERSION}.json echo "[" > prs-${NEW_VERSION}.json
first=true first=true
@@ -197,17 +189,16 @@ echo "Last stable release: $LAST_STABLE"
``` ```
2. **Analyze for GTM-worthy features:** 2. **Analyze for GTM-worthy features:**
``` ```
<task> <task>
Review these PRs to identify features worthy of marketing attention. Review these PRs to identify features worthy of marketing attention.
A feature is GTM-worthy if it meets ALL of these criteria: A feature is GTM-worthy if it meets ALL of these criteria:
- Introduces a NEW capability users didn't have before (not just improvements) - Introduces a NEW capability users didn't have before (not just improvements)
- Would be a compelling reason for users to upgrade to this version - Would be a compelling reason for users to upgrade to this version
- Can be demonstrated visually or has clear before/after comparison - Can be demonstrated visually or has clear before/after comparison
- Affects a significant portion of the user base - Affects a significant portion of the user base
NOT GTM-worthy: NOT GTM-worthy:
- Bug fixes (even important ones) - Bug fixes (even important ones)
- Minor UI tweaks or color changes - Minor UI tweaks or color changes
@@ -215,20 +206,19 @@ echo "Last stable release: $LAST_STABLE"
- Internal refactoring - Internal refactoring
- Small convenience features - Small convenience features
- Features that only improve existing functionality marginally - Features that only improve existing functionality marginally
For each GTM-worthy feature, note: For each GTM-worthy feature, note:
- PR number, title, and author - PR number, title, and author
- Media links from the PR description - Media links from the PR description
- One compelling sentence on why users should care - One compelling sentence on why users should care
If there are no GTM-worthy features, just say "No marketing-worthy features in this release." If there are no GTM-worthy features, just say "No marketing-worthy features in this release."
</task> </task>
PR data: [contents of prs-${NEW_VERSION}.json] PR data: [contents of prs-${NEW_VERSION}.json]
``` ```
3. **Generate GTM notification using this EXACT Slack-compatible format:** 3. **Generate GTM notification using this EXACT Slack-compatible format:**
```bash ```bash
# Only create file if GTM-worthy features exist: # Only create file if GTM-worthy features exist:
if [ "$GTM_FEATURES_FOUND" = "true" ]; then if [ "$GTM_FEATURES_FOUND" = "true" ]; then
@@ -262,8 +252,8 @@ echo "Last stable release: $LAST_STABLE"
``` ```
**CRITICAL Formatting Requirements:** **CRITICAL Formatting Requirements:**
- Use single asterisk (\*) for emphasis, NOT double (\*\*) - Use single asterisk (*) for emphasis, NOT double (**)
- Use underscore (\_) for italics - Use underscore (_) for italics
- Use 4 spaces for indentation (not tabs) - Use 4 spaces for indentation (not tabs)
- Convert author names to @username format (e.g., "John Smith" → "@john") - Convert author names to @username format (e.g., "John Smith" → "@john")
- No section headers (#), no code language specifications - No section headers (#), no code language specifications
@@ -273,7 +263,6 @@ echo "Last stable release: $LAST_STABLE"
### Step 6: Version Preview ### Step 6: Version Preview
**Version Preview:** **Version Preview:**
- Current: `${CURRENT_VERSION}` - Current: `${CURRENT_VERSION}`
- Proposed: Show exact version number based on analysis: - Proposed: Show exact version number based on analysis:
- Major version if breaking changes detected - Major version if breaking changes detected
@@ -337,7 +326,6 @@ echo "Last stable release: $LAST_STABLE"
done done
``` ```
3. Create standardized release notes using this exact template: 3. Create standardized release notes using this exact template:
```bash ```bash
cat > release-notes-${NEW_VERSION}.md << 'EOF' cat > release-notes-${NEW_VERSION}.md << 'EOF'
## ⚠️ Breaking Changes ## ⚠️ Breaking Changes
@@ -371,7 +359,6 @@ echo "Last stable release: $LAST_STABLE"
**Full Changelog**: https://github.com/Comfy-Org/ComfyUI_frontend/compare/${BASE_TAG}...v${NEW_VERSION} **Full Changelog**: https://github.com/Comfy-Org/ComfyUI_frontend/compare/${BASE_TAG}...v${NEW_VERSION}
EOF EOF
``` ```
4. **Parse commits and populate template:** 4. **Parse commits and populate template:**
- Group commits by conventional commit type (feat:, fix:, chore:, etc.) - Group commits by conventional commit type (feat:, fix:, chore:, etc.)
- Extract PR numbers from commit messages - Extract PR numbers from commit messages
@@ -388,19 +375,16 @@ echo "Last stable release: $LAST_STABLE"
### Step 10: Create Version Bump PR ### Step 10: Create Version Bump PR
**For standard version bumps (patch/minor/major):** **For standard version bumps (patch/minor/major):**
```bash ```bash
# Trigger the workflow # Trigger the workflow
gh workflow run release-version-bump.yaml -f version_type=${VERSION_TYPE} gh workflow run version-bump.yaml -f version_type=${VERSION_TYPE}
# Workflow runs quickly - usually creates PR within 30 seconds # Workflow runs quickly - usually creates PR within 30 seconds
echo "Workflow triggered. Waiting for PR creation..." echo "Workflow triggered. Waiting for PR creation..."
``` ```
**For releasing a stable version:** **For releasing a stable version:**
1. Must manually create branch and update version: 1. Must manually create branch and update version:
```bash ```bash
git checkout -b version-bump-${NEW_VERSION} git checkout -b version-bump-${NEW_VERSION}
# Edit package.json to remove pre-release suffix # Edit package.json to remove pre-release suffix
@@ -410,25 +394,23 @@ echo "Workflow triggered. Waiting for PR creation..."
``` ```
2. Wait for PR creation (if using workflow) or create manually: 2. Wait for PR creation (if using workflow) or create manually:
```bash ```bash
# For workflow-created PRs - wait and find it # For workflow-created PRs - wait and find it
sleep 30 sleep 30
# Look for PR from comfy-pr-bot (not github-actions) # Look for PR from comfy-pr-bot (not github-actions)
PR_NUMBER=$(gh pr list --author comfy-pr-bot --limit 1 --json number --jq '.[0].number') PR_NUMBER=$(gh pr list --author comfy-pr-bot --limit 1 --json number --jq '.[0].number')
# Verify we got the PR # Verify we got the PR
if [ -z "$PR_NUMBER" ]; then if [ -z "$PR_NUMBER" ]; then
echo "PR not found yet. Checking recent PRs..." echo "PR not found yet. Checking recent PRs..."
gh pr list --limit 5 --json number,title,author gh pr list --limit 5 --json number,title,author
fi fi
# For manual PRs # For manual PRs
gh pr create --title "${NEW_VERSION}" \ gh pr create --title "${NEW_VERSION}" \
--body-file release-notes-${NEW_VERSION}.md \ --body-file release-notes-${NEW_VERSION}.md \
--label "Release" --label "Release"
``` ```
3. **Update PR with release notes:** 3. **Update PR with release notes:**
```bash ```bash
# For workflow-created PRs, update the body with our release notes # For workflow-created PRs, update the body with our release notes
@@ -443,21 +425,28 @@ echo "Workflow triggered. Waiting for PR creation..."
gh pr view ${PR_NUMBER} --json labels | jq -r '.labels[].name' | grep -q "Release" || \ gh pr view ${PR_NUMBER} --json labels | jq -r '.labels[].name' | grep -q "Release" || \
echo "ERROR: Release label missing! Add it immediately!" echo "ERROR: Release label missing! Add it immediately!"
``` ```
2. Verify version number in package.json 2. Check for update-locales commits:
3. Review all changed files ```bash
4. Ensure no unintended changes included # WARNING: update-locales may add [skip ci] which blocks release workflow!
5. Wait for required PR checks: gh pr view ${PR_NUMBER} --json commits | grep -q "skip ci" && \
echo "WARNING: [skip ci] detected - release workflow may not trigger!"
```
3. Verify version number in package.json
4. Review all changed files
5. Ensure no unintended changes included
6. Wait for required PR checks:
```bash ```bash
gh pr checks ${PR_NUMBER} --watch gh pr checks ${PR_NUMBER} --watch
``` ```
6. **FINAL CODE REVIEW**: Release label present and no [skip ci]? 7. **FINAL CODE REVIEW**: Release label present and no [skip ci]?
### Step 12: Pre-Merge Validation ### Step 12: Pre-Merge Validation
1. **Review Requirements**: Release PRs require approval 1. **Review Requirements**: Release PRs require approval
2. Monitor CI checks 2. Monitor CI checks - watch for update-locales
3. Check no new commits to main since PR creation 3. **CRITICAL WARNING**: If update-locales adds [skip ci], the release workflow won't trigger!
4. **DEPLOYMENT READINESS**: Ready to merge? 4. Check no new commits to main since PR creation
5. **DEPLOYMENT READINESS**: Ready to merge?
### Step 13: Execute Release ### Step 13: Execute Release
@@ -479,14 +468,14 @@ echo "Workflow triggered. Waiting for PR creation..."
# Monitor branch creation (for minor/major releases) # Monitor branch creation (for minor/major releases)
gh run list --workflow=release-branch-create.yaml --limit=1 gh run list --workflow=release-branch-create.yaml --limit=1
``` ```
5. If workflow didn't trigger due to [skip ci]: 4. If workflow didn't trigger due to [skip ci]:
```bash ```bash
echo "ERROR: Release workflow didn't trigger!" echo "ERROR: Release workflow didn't trigger!"
echo "Options:" echo "Options:"
echo "1. Create patch release (e.g., 1.24.1) to trigger workflow" echo "1. Create patch release (e.g., 1.24.1) to trigger workflow"
echo "2. Investigate manual release options" echo "2. Investigate manual release options"
``` ```
6. If workflow triggered, monitor execution: 5. If workflow triggered, monitor execution:
```bash ```bash
WORKFLOW_RUN_ID=$(gh run list --workflow=release-draft-create.yaml --limit=1 --json databaseId --jq '.[0].databaseId') WORKFLOW_RUN_ID=$(gh run list --workflow=release-draft-create.yaml --limit=1 --json databaseId --jq '.[0].databaseId')
gh run watch ${WORKFLOW_RUN_ID} gh run watch ${WORKFLOW_RUN_ID}
@@ -495,7 +484,6 @@ echo "Workflow triggered. Waiting for PR creation..."
### Step 14: Enhance GitHub Release ### Step 14: Enhance GitHub Release
1. Wait for automatic release creation: 1. Wait for automatic release creation:
```bash ```bash
# Wait for release to be created # Wait for release to be created
while ! gh release view v${NEW_VERSION} >/dev/null 2>&1; do while ! gh release view v${NEW_VERSION} >/dev/null 2>&1; do
@@ -505,14 +493,13 @@ echo "Workflow triggered. Waiting for PR creation..."
``` ```
2. **Enhance the GitHub release:** 2. **Enhance the GitHub release:**
```bash ```bash
# Update release with our release notes # Update release with our release notes
gh release edit v${NEW_VERSION} \ gh release edit v${NEW_VERSION} \
--title "🚀 ComfyUI Frontend v${NEW_VERSION}" \ --title "🚀 ComfyUI Frontend v${NEW_VERSION}" \
--notes-file release-notes-${NEW_VERSION}.md \ --notes-file release-notes-${NEW_VERSION}.md \
--latest --latest
# Add any additional assets if needed # Add any additional assets if needed
# gh release upload v${NEW_VERSION} additional-assets.zip # gh release upload v${NEW_VERSION} additional-assets.zip
``` ```
@@ -525,17 +512,14 @@ echo "Workflow triggered. Waiting for PR creation..."
### Step 15: Verify Multi-Channel Distribution ### Step 15: Verify Multi-Channel Distribution
1. **GitHub Release:** 1. **GitHub Release:**
```bash ```bash
gh release view v${NEW_VERSION} --json assets,body,createdAt,tagName gh release view v${NEW_VERSION} --json assets,body,createdAt,tagName
``` ```
- ✅ Check release notes - ✅ Check release notes
- ✅ Verify dist.zip attachment - ✅ Verify dist.zip attachment
- ✅ Confirm release marked as latest (for main branch) - ✅ Confirm release marked as latest (for main branch)
2. **PyPI Package:** 2. **PyPI Package:**
```bash ```bash
# Check PyPI availability (may take a few minutes) # Check PyPI availability (may take a few minutes)
for i in {1..10}; do for i in {1..10}; do
@@ -549,7 +533,6 @@ echo "Workflow triggered. Waiting for PR creation..."
``` ```
3. **npm Package:** 3. **npm Package:**
```bash ```bash
# Check npm availability # Check npm availability
for i in {1..10}; do for i in {1..10}; do
@@ -567,17 +550,15 @@ echo "Workflow triggered. Waiting for PR creation..."
### Step 16: Post-Release Monitoring Setup ### Step 16: Post-Release Monitoring Setup
1. **Monitor immediate release health:** 1. **Monitor immediate release health:**
```bash ```bash
# Check for immediate issues # Check for immediate issues
gh issue list --label "bug" --state open --limit 5 --json title,number,createdAt gh issue list --label "bug" --state open --limit 5 --json title,number,createdAt
# Monitor download metrics (if accessible) # Monitor download metrics (if accessible)
gh release view v${NEW_VERSION} --json assets --jq '.assets[].downloadCount' gh release view v${NEW_VERSION} --json assets --jq '.assets[].downloadCount'
``` ```
2. **Update documentation tracking:** 2. **Update documentation tracking:**
```bash ```bash
cat > post-release-checklist.md << EOF cat > post-release-checklist.md << EOF
# Post-Release Checklist for v${NEW_VERSION} # Post-Release Checklist for v${NEW_VERSION}
@@ -608,7 +589,6 @@ echo "Workflow triggered. Waiting for PR creation..."
``` ```
3. **Create release summary:** 3. **Create release summary:**
```bash ```bash
cat > release-summary-${NEW_VERSION}.md << EOF cat > release-summary-${NEW_VERSION}.md << EOF
# Release Summary: ComfyUI Frontend v${NEW_VERSION} # Release Summary: ComfyUI Frontend v${NEW_VERSION}
@@ -646,7 +626,6 @@ echo "Workflow triggered. Waiting for PR creation..."
### Step 17: Create Release Summary ### Step 17: Create Release Summary
1. **Create comprehensive release summary:** 1. **Create comprehensive release summary:**
```bash ```bash
cat > release-summary-${NEW_VERSION}.md << EOF cat > release-summary-${NEW_VERSION}.md << EOF
# Release Summary: ComfyUI Frontend v${NEW_VERSION} # Release Summary: ComfyUI Frontend v${NEW_VERSION}
@@ -686,7 +665,6 @@ echo "Workflow triggered. Waiting for PR creation..."
### Rollback Procedures ### Rollback Procedures
**Pre-Merge Rollback:** **Pre-Merge Rollback:**
```bash ```bash
# Close version bump PR and reset # Close version bump PR and reset
gh pr close ${PR_NUMBER} gh pr close ${PR_NUMBER}
@@ -695,7 +673,6 @@ git clean -fd
``` ```
**Post-Merge Rollback:** **Post-Merge Rollback:**
```bash ```bash
# Create immediate patch release with reverts # Create immediate patch release with reverts
git revert ${RELEASE_COMMIT} git revert ${RELEASE_COMMIT}
@@ -703,7 +680,6 @@ git revert ${RELEASE_COMMIT}
``` ```
**Emergency Procedures:** **Emergency Procedures:**
```bash ```bash
# Document incident # Document incident
cat > release-incident-${NEW_VERSION}.md << EOF cat > release-incident-${NEW_VERSION}.md << EOF
@@ -737,39 +713,31 @@ The command implements multiple quality gates:
## Common Scenarios ## Common Scenarios
### Scenario 1: Regular Feature Release ### Scenario 1: Regular Feature Release
```bash ```bash
/project:create-frontend-release minor /project:create-frontend-release minor
``` ```
- Analyzes features since last release - Analyzes features since last release
- Generates changelog automatically - Generates changelog automatically
- Creates comprehensive release notes - Creates comprehensive release notes
### Scenario 2: Critical Security Patch ### Scenario 2: Critical Security Patch
```bash ```bash
/project:create-frontend-release patch "Security fixes for CVE-2024-XXXX" /project:create-frontend-release patch "Security fixes for CVE-2024-XXXX"
``` ```
- Expedited security scanning - Expedited security scanning
- Enhanced monitoring setup - Enhanced monitoring setup
### Scenario 3: Major Version with Breaking Changes ### Scenario 3: Major Version with Breaking Changes
```bash ```bash
/project:create-frontend-release major /project:create-frontend-release major
``` ```
- Comprehensive breaking change analysis - Comprehensive breaking change analysis
- Migration guide generation - Migration guide generation
### Scenario 4: Pre-release Testing ### Scenario 4: Pre-release Testing
```bash ```bash
/project:create-frontend-release prerelease /project:create-frontend-release prerelease
``` ```
- Creates alpha/beta/rc versions - Creates alpha/beta/rc versions
- Draft release status - Draft release status
- Python package specs require that prereleases use alpha/beta/rc as the preid - Python package specs require that prereleases use alpha/beta/rc as the preid
@@ -779,12 +747,10 @@ The command implements multiple quality gates:
When executing this release process, pay attention to these key aspects: When executing this release process, pay attention to these key aspects:
### Version Handling ### Version Handling
- For pre-release versions (e.g., 1.24.0-rc.1), the next stable release should be the same version without the suffix (1.24.0) - For pre-release versions (e.g., 1.24.0-rc.1), the next stable release should be the same version without the suffix (1.24.0)
- Never skip version numbers - follow semantic versioning strictly - Never skip version numbers - follow semantic versioning strictly
### Commit History Analysis ### Commit History Analysis
- **ALWAYS** use `--first-parent` flag with git log to avoid including commits from merged feature branches - **ALWAYS** use `--first-parent` flag with git log to avoid including commits from merged feature branches
- Verify PR merge targets before including them in changelogs: - Verify PR merge targets before including them in changelogs:
```bash ```bash
@@ -792,7 +758,6 @@ When executing this release process, pay attention to these key aspects:
``` ```
### Release Workflow Triggers ### Release Workflow Triggers
- The "Release" label on the PR is **CRITICAL** - without it, PyPI/npm publishing won't occur - The "Release" label on the PR is **CRITICAL** - without it, PyPI/npm publishing won't occur
- Check for `[skip ci]` in commit messages before merging - this blocks the release workflow - Check for `[skip ci]` in commit messages before merging - this blocks the release workflow
- If you encounter `[skip ci]`, push an empty commit to override it: - If you encounter `[skip ci]`, push an empty commit to override it:
@@ -801,13 +766,11 @@ When executing this release process, pay attention to these key aspects:
``` ```
### PR Creation Details ### PR Creation Details
- Version bump PRs come from `comfy-pr-bot`, not `github-actions` - Version bump PRs come from `comfy-pr-bot`, not `github-actions`
- The workflow typically completes in 20-30 seconds - The workflow typically completes in 20-30 seconds
- Always wait for the PR to be created before trying to edit it - Always wait for the PR to be created before trying to edit it
### Breaking Changes Detection ### Breaking Changes Detection
- Analyze changes to public-facing APIs: - Analyze changes to public-facing APIs:
- The `app` object and its methods - The `app` object and its methods
- The `api` module exports - The `api` module exports
@@ -816,10 +779,9 @@ When executing this release process, pay attention to these key aspects:
- Any modifications to these require marking as breaking changes - Any modifications to these require marking as breaking changes
### Recovery Procedures ### Recovery Procedures
If the release workflow fails to trigger: If the release workflow fails to trigger:
1. Create a revert PR to restore the previous version 1. Create a revert PR to restore the previous version
2. Merge the revert 2. Merge the revert
3. Re-run the version bump workflow 3. Re-run the version bump workflow
4. This approach is cleaner than creating extra version numbers 4. This approach is cleaner than creating extra version numbers

View File

@@ -3,11 +3,10 @@
This command creates patch/hotfix releases for ComfyUI Frontend by backporting fixes to stable core branches. It handles both automated backports (preferred) and manual cherry-picking (fallback). This command creates patch/hotfix releases for ComfyUI Frontend by backporting fixes to stable core branches. It handles both automated backports (preferred) and manual cherry-picking (fallback).
**Process Overview:** **Process Overview:**
1. **Check automated backports first** (via labels) 1. **Check automated backports first** (via labels)
2. **Skip to version bump** if backports already merged 2. **Skip to version bump** if backports already merged
3. **Manual cherry-picking** if automation failed 3. **Manual cherry-picking** if automation failed
4. **Create patch release** with version bump 4. **Create patch release** with version bump
5. **Publish GitHub release** (manually uncheck "latest") 5. **Publish GitHub release** (manually uncheck "latest")
6. **Update ComfyUI requirements.txt** via PR 6. **Update ComfyUI requirements.txt** via PR
@@ -15,8 +14,7 @@ This command creates patch/hotfix releases for ComfyUI Frontend by backporting f
Create a hotfix release by backporting commits/PRs from main to a core branch: $ARGUMENTS Create a hotfix release by backporting commits/PRs from main to a core branch: $ARGUMENTS
Expected format: Comma-separated list of commits or PR numbers Expected format: Comma-separated list of commits or PR numbers
Examples: Examples:
- `#1234,#5678` (PRs - preferred) - `#1234,#5678` (PRs - preferred)
- `abc123,def456` (commit hashes) - `abc123,def456` (commit hashes)
- `#1234,abc123` (mixed) - `#1234,abc123` (mixed)
@@ -27,7 +25,7 @@ If no arguments provided, the command will guide you through identifying commits
## Prerequisites ## Prerequisites
- Push access to repository - Push access to repository
- GitHub CLI (`gh`) authenticated - GitHub CLI (`gh`) authenticated
- Clean working tree - Clean working tree
- Understanding of what fixes need backporting - Understanding of what fixes need backporting
@@ -38,13 +36,11 @@ If no arguments provided, the command will guide you through identifying commits
**Check if automated backports were attempted:** **Check if automated backports were attempted:**
1. **For each PR, check existing backport labels:** 1. **For each PR, check existing backport labels:**
```bash ```bash
gh pr view #1234 --json labels | jq -r '.labels[].name' gh pr view #1234 --json labels | jq -r '.labels[].name'
``` ```
2. **If no backport labels exist, add them now:** 2. **If no backport labels exist, add them now:**
```bash ```bash
# Add backport labels (this triggers automated backports) # Add backport labels (this triggers automated backports)
gh pr edit #1234 --add-label "needs-backport" gh pr edit #1234 --add-label "needs-backport"
@@ -52,7 +48,6 @@ If no arguments provided, the command will guide you through identifying commits
``` ```
3. **Check for existing backport PRs:** 3. **Check for existing backport PRs:**
```bash ```bash
# Check for backport PRs created by automation # Check for backport PRs created by automation
PR_NUMBER=${ARGUMENTS%%,*} # Extract first PR number from arguments PR_NUMBER=${ARGUMENTS%%,*} # Extract first PR number from arguments
@@ -63,22 +58,18 @@ If no arguments provided, the command will guide you through identifying commits
4. **Handle existing backport scenarios:** 4. **Handle existing backport scenarios:**
**Scenario A: Automated backports already merged** **Scenario A: Automated backports already merged**
```bash ```bash
# Check if backport PRs were merged to core branches # Check if backport PRs were merged to core branches
gh pr list --search "backport-${PR_NUMBER}-to" --state merged gh pr list --search "backport-${PR_NUMBER}-to" --state merged
``` ```
- If backport PRs are merged → Skip to Step 10 (Version Bump) - If backport PRs are merged → Skip to Step 10 (Version Bump)
- **CONFIRMATION**: Automated backports completed, proceeding to version bump? - **CONFIRMATION**: Automated backports completed, proceeding to version bump?
**Scenario B: Automated backport PRs exist but not merged** **Scenario B: Automated backport PRs exist but not merged**
```bash ```bash
# Show open backport PRs that need merging # Show open backport PRs that need merging
gh pr list --search "backport-${PR_NUMBER}-to" --state open gh pr list --search "backport-${PR_NUMBER}-to" --state open
``` ```
- **ACTION REQUIRED**: Merge the existing backport PRs first - **ACTION REQUIRED**: Merge the existing backport PRs first
- Use: `gh pr merge [PR_NUMBER] --merge` for each backport PR - Use: `gh pr merge [PR_NUMBER] --merge` for each backport PR
- After merging, return to this command and skip to Step 10 (Version Bump) - After merging, return to this command and skip to Step 10 (Version Bump)
@@ -136,7 +127,6 @@ If no arguments provided, the command will guide you through identifying commits
### Step 6: Cherry-pick Changes ### Step 6: Cherry-pick Changes
For each commit: For each commit:
1. Attempt cherry-pick: `git cherry-pick <commit>` 1. Attempt cherry-pick: `git cherry-pick <commit>`
2. If conflicts occur: 2. If conflicts occur:
- Display conflict details - Display conflict details
@@ -208,7 +198,6 @@ For each commit:
``` ```
3. **CRITICAL**: Verify "Release" label is added 3. **CRITICAL**: Verify "Release" label is added
4. Create standardized release notes: 4. Create standardized release notes:
```bash ```bash
cat > release-notes-${NEW_VERSION}.md << 'EOF' cat > release-notes-${NEW_VERSION}.md << 'EOF'
## ⚠️ Breaking Changes ## ⚠️ Breaking Changes
@@ -242,14 +231,12 @@ For each commit:
**Full Changelog**: https://github.com/Comfy-Org/ComfyUI_frontend/compare/v${CURRENT_VERSION}...v${NEW_VERSION} **Full Changelog**: https://github.com/Comfy-Org/ComfyUI_frontend/compare/v${CURRENT_VERSION}...v${NEW_VERSION}
EOF EOF
``` ```
- For hotfixes, typically only populate the "Bug Fixes" section - For hotfixes, typically only populate the "Bug Fixes" section
- Include links to the cherry-picked PRs/commits - Include links to the cherry-picked PRs/commits
- Update the PR body with the release notes: - Update the PR body with the release notes:
```bash ```bash
gh pr edit ${PR_NUMBER} --body-file release-notes-${NEW_VERSION}.md gh pr edit ${PR_NUMBER} --body-file release-notes-${NEW_VERSION}.md
``` ```
5. **CONFIRMATION REQUIRED**: Release PR has "Release" label? 5. **CONFIRMATION REQUIRED**: Release PR has "Release" label?
### Step 12: Monitor Release Process ### Step 12: Monitor Release Process
@@ -275,7 +262,7 @@ For each commit:
2. **Find the DRAFT release** (e.g., "v1.23.5 Draft") 2. **Find the DRAFT release** (e.g., "v1.23.5 Draft")
3. **Click "Edit release"** 3. **Click "Edit release"**
4. **UNCHECK "Set as the latest release"** ⚠️ **CRITICAL** 4. **UNCHECK "Set as the latest release"** ⚠️ **CRITICAL**
- This prevents the hotfix from showing as "latest" - This prevents the hotfix from showing as "latest"
- Main branch should always be "latest release" - Main branch should always be "latest release"
5. **Click "Publish release"** 5. **Click "Publish release"**
6. **CONFIRMATION REQUIRED**: Draft release published with "latest" unchecked? 6. **CONFIRMATION REQUIRED**: Draft release published with "latest" unchecked?
@@ -285,7 +272,6 @@ For each commit:
**IMPORTANT**: Create PR to update ComfyUI's requirements.txt via fork: **IMPORTANT**: Create PR to update ComfyUI's requirements.txt via fork:
1. **Setup fork (if needed):** 1. **Setup fork (if needed):**
```bash ```bash
# Check if fork already exists # Check if fork already exists
if gh repo view ComfyUI --json owner | jq -r '.owner.login' | grep -q "$(gh api user --jq .login)"; then if gh repo view ComfyUI --json owner | jq -r '.owner.login' | grep -q "$(gh api user --jq .login)"; then
@@ -298,32 +284,30 @@ For each commit:
``` ```
2. **Clone fork and create branch:** 2. **Clone fork and create branch:**
```bash ```bash
# Clone your fork (or use existing clone) # Clone your fork (or use existing clone)
GITHUB_USER=$(gh api user --jq .login) GITHUB_USER=$(gh api user --jq .login)
if [ ! -d "ComfyUI-fork" ]; then if [ ! -d "ComfyUI-fork" ]; then
gh repo clone ${GITHUB_USER}/ComfyUI ComfyUI-fork gh repo clone ${GITHUB_USER}/ComfyUI ComfyUI-fork
fi fi
cd ComfyUI-fork cd ComfyUI-fork
git checkout master git checkout master
git pull origin master git pull origin master
# Create update branch # Create update branch
BRANCH_NAME="update-frontend-${NEW_VERSION}" BRANCH_NAME="update-frontend-${NEW_VERSION}"
git checkout -b ${BRANCH_NAME} git checkout -b ${BRANCH_NAME}
``` ```
3. **Update requirements.txt:** 3. **Update requirements.txt:**
```bash ```bash
# Update the version in requirements.txt # Update the version in requirements.txt
sed -i "s/comfyui-frontend-package==[0-9].*$/comfyui-frontend-package==${NEW_VERSION}/" requirements.txt sed -i "s/comfyui-frontend-package==[0-9].*$/comfyui-frontend-package==${NEW_VERSION}/" requirements.txt
# Verify the change # Verify the change
grep "comfyui-frontend-package" requirements.txt grep "comfyui-frontend-package" requirements.txt
# Commit the change # Commit the change
git add requirements.txt git add requirements.txt
git commit -m "Bump frontend to ${NEW_VERSION}" git commit -m "Bump frontend to ${NEW_VERSION}"
@@ -337,8 +321,7 @@ For each commit:
--repo comfyanonymous/ComfyUI \ --repo comfyanonymous/ComfyUI \
--title "Bump frontend to ${NEW_VERSION}" \ --title "Bump frontend to ${NEW_VERSION}" \
--body "$(cat <<EOF --body "$(cat <<EOF
Bump frontend to ${NEW_VERSION} Bump frontend to ${NEW_VERSION}
```
\`\`\` \`\`\`
python main.py --front-end-version Comfy-Org/ComfyUI_frontend@${NEW_VERSION} python main.py --front-end-version Comfy-Org/ComfyUI_frontend@${NEW_VERSION}
@@ -351,19 +334,15 @@ python main.py --front-end-version Comfy-Org/ComfyUI_frontend@${NEW_VERSION}
## Changes ## Changes
- Fix: [Brief description of hotfixes included] - Fix: [Brief description of hotfixes included]
EOF EOF
)" )"
```
```
```
5. **Clean up:** 5. **Clean up:**
```bash ```bash
# Return to original directory # Return to original directory
cd .. cd ..
# Keep fork directory for future updates # Keep fork directory for future updates
echo "Fork directory 'ComfyUI-fork' kept for future use" echo "Fork directory 'ComfyUI-fork' kept for future use"
``` ```
@@ -396,7 +375,6 @@ python main.py --front-end-version Comfy-Org/ComfyUI_frontend@${NEW_VERSION}
## Safety Checks ## Safety Checks
Throughout the process: Throughout the process:
- Always verify core branch matches ComfyUI's requirements.txt - Always verify core branch matches ComfyUI's requirements.txt
- For PRs: Ensure using correct commits (merge vs individual) - For PRs: Ensure using correct commits (merge vs individual)
- Check version numbers follow semantic versioning - Check version numbers follow semantic versioning
@@ -407,7 +385,6 @@ Throughout the process:
## Rollback Procedures ## Rollback Procedures
If something goes wrong: If something goes wrong:
- Before push: `git reset --hard origin/core/X.Y` - Before push: `git reset --hard origin/core/X.Y`
- After PR creation: Close PR and start over - After PR creation: Close PR and start over
- After failed release: Create new patch version with fixes - After failed release: Create new patch version with fixes
@@ -427,16 +404,16 @@ If something goes wrong:
## Modern Workflow Context ## Modern Workflow Context
**Primary Backport Method:** Automated via `needs-backport` + `X.YY` labels **Primary Backport Method:** Automated via `needs-backport` + `X.YY` labels
**This Command Usage:** **This Command Usage:**
- Smart path detection - skip to version bump if backports already merged - Smart path detection - skip to version bump if backports already merged
- Fallback to manual cherry-picking only when automation fails/has conflicts - Fallback to manual cherry-picking only when automation fails/has conflicts
**Complete Hotfix:** Includes GitHub release publishing + ComfyUI requirements.txt integration **Complete Hotfix:** Includes GitHub release publishing + ComfyUI requirements.txt integration
## Workflow Paths ## Workflow Paths
- **Path A:** Backports already merged → Skip to Step 10 (Version Bump) - **Path A:** Backports already merged → Skip to Step 10 (Version Bump)
- **Path B:** Backport PRs need merging → Merge them → Skip to Step 10 (Version Bump) - **Path B:** Backport PRs need merging → Merge them → Skip to Step 10 (Version Bump)
- **Path C:** No/failed backports → Manual cherry-picking (Steps 2-9) → Version Bump (Step 10) - **Path C:** No/failed backports → Manual cherry-picking (Steps 2-9) → Version Bump (Step 10)
This process ensures a complete hotfix release with proper GitHub publishing, ComfyUI integration, and multiple safety checkpoints.
This process ensures a complete hotfix release with proper GitHub publishing, ComfyUI integration, and multiple safety checkpoints.

View File

@@ -5,7 +5,6 @@ Bootstrap the ComfyUI Frontend monorepo with all necessary dependencies and veri
## Overview ## Overview
This command will: This command will:
1. Install pnpm package manager (if not present) 1. Install pnpm package manager (if not present)
2. Install all project dependencies 2. Install all project dependencies
3. Verify the project builds successfully 3. Verify the project builds successfully
@@ -94,7 +93,7 @@ sleep 10
# Check if server is running # Check if server is running
if curl -s http://localhost:5173 > /dev/null 2>&1; then if curl -s http://localhost:5173 > /dev/null 2>&1; then
echo "✅ Development server started successfully at http://localhost:5173" echo "✅ Development server started successfully at http://localhost:5173"
# Kill the background server # Kill the background server
kill $SERVER_PID kill $SERVER_PID
wait $SERVER_PID 2>/dev/null wait $SERVER_PID 2>/dev/null
@@ -123,7 +122,7 @@ echo " pnpm build - Build for production"
echo " pnpm test:unit - Run unit tests" echo " pnpm test:unit - Run unit tests"
echo " pnpm typecheck - Run TypeScript checks" echo " pnpm typecheck - Run TypeScript checks"
echo " pnpm lint - Run ESLint" echo " pnpm lint - Run ESLint"
echo " pnpm format - Format code with oxfmt" echo " pnpm format - Format code with Prettier"
echo "" echo ""
echo "Next steps:" echo "Next steps:"
echo "1. Run 'pnpm dev' to start developing" echo "1. Run 'pnpm dev' to start developing"
@@ -155,4 +154,4 @@ After running the setup, manually verify:
- Node.js >= 24 - Node.js >= 24
- Git repository - Git repository
- Internet connection for package downloads - Internet connection for package downloads
- Available ports (typically 5173 for dev server) - Available ports (typically 5173 for dev server)

View File

@@ -12,10 +12,10 @@ Follow these steps systematically to verify our changes:
2. **Visual Testing Process** 2. **Visual Testing Process**
- Navigate to http://localhost:5173/ - Navigate to http://localhost:5173/
- For each target page (specified in arguments or recently changed files): - For each target page (specified in arguments or recently changed files):
- Navigate to the page using direct URL or site navigation * Navigate to the page using direct URL or site navigation
- Take a high-quality screenshot * Take a high-quality screenshot
- Analyze the screenshot for the specific changes we implemented * Analyze the screenshot for the specific changes we implemented
- Document any visual issues or improvements needed * Document any visual issues or improvements needed
3. **Quality Verification** 3. **Quality Verification**
Check each page for: Check each page for:
@@ -27,7 +27,7 @@ Follow these steps systematically to verify our changes:
- Typography and readability - Typography and readability
- Color scheme consistency - Color scheme consistency
- Interactive elements (buttons, links, forms) - Interactive elements (buttons, links, forms)
</instructions> </instructions>
<examples> <examples>
Common issues to watch for: Common issues to watch for:
@@ -48,11 +48,10 @@ For each page tested, provide:
4. Overall assessment of visual quality 4. Overall assessment of visual quality
If you find issues, be specific about: If you find issues, be specific about:
- Exact location of the problem - Exact location of the problem
- Expected vs actual behavior - Expected vs actual behavior
- Severity level (critical, important, minor) - Severity level (critical, important, minor)
- Suggested fix if obvious - Suggested fix if obvious
</reporting> </reporting>
Remember: Take your time with each screenshot and analysis. Visual quality directly impacts user experience and our project's professional appearance. Remember: Take your time with each screenshot and analysis. Visual quality directly impacts user experience and our project's professional appearance.

View File

@@ -1,150 +0,0 @@
---
name: backport-management
description: Manages cherry-pick backports across stable release branches. Discovers candidates from Slack/git, analyzes dependencies, resolves conflicts via worktree, and logs results. Use when asked to backport, cherry-pick to stable, manage release branches, do stable branch maintenance, or run a backport session.
---
# Backport Management
Cherry-pick backport management for Comfy-Org/ComfyUI_frontend stable release branches.
## Quick Start
1. **Discover** — Collect candidates from Slack bot + git log gap (`reference/discovery.md`)
2. **Analyze** — Categorize MUST/SHOULD/SKIP, check deps (`reference/analysis.md`)
3. **Plan** — Order by dependency (leaf fixes first), group into waves per branch
4. **Execute** — Label-driven automation → worktree fallback for conflicts (`reference/execution.md`)
5. **Verify** — After each wave, verify branch integrity before proceeding
6. **Log & Report** — Generate session report with mermaid diagram (`reference/logging.md`)
## System Context
| Item | Value |
| -------------- | ------------------------------------------------- |
| Repo | `~/ComfyUI_frontend` (Comfy-Org/ComfyUI_frontend) |
| Merge strategy | Squash merge (`gh pr merge --squash --admin`) |
| Automation | `pr-backport.yaml` GitHub Action (label-driven) |
| Tracking dir | `~/temp/backport-session/` |
## Branch Scope Rules
**Critical: Match PRs to the correct target branches.**
| Branch prefix | Scope | Example |
| ------------- | ------------------------------ | ----------------------------------------- |
| `cloud/*` | Cloud-hosted ComfyUI only | App mode, cloud auth, cloud-specific UI |
| `core/*` | Local/self-hosted ComfyUI only | Core editor, local workflows, node system |
**⚠️ NEVER backport cloud-only PRs to `core/*` branches.** Cloud-only changes (app mode, cloud auth, cloud billing UI, cloud-specific API calls) are irrelevant to local users and waste effort. Before backporting any PR to a `core/*` branch, check:
- Does the PR title/description mention "app mode", "cloud", or cloud-specific features?
- Does the PR only touch files like `appModeStore.ts`, cloud auth, or cloud-specific components?
- If yes → skip for `core/*` branches (may still apply to `cloud/*` branches)
## ⚠️ Gotchas (Learn from Past Sessions)
### Use `gh api` for Labels — NOT `gh pr edit`
`gh pr edit --add-label` triggers Projects Classic deprecation errors. Always use:
```bash
gh api repos/Comfy-Org/ComfyUI_frontend/issues/$PR/labels \
-f "labels[]=needs-backport" -f "labels[]=TARGET_BRANCH"
```
### Automation Over-Reports Conflicts
The `pr-backport.yaml` action reports more conflicts than reality. `git cherry-pick -m 1` with git auto-merge handles many cases the automation can't. Always attempt manual cherry-pick before skipping.
### Never Skip Based on Conflict File Count
12 or 27 conflicting files can be trivial (snapshots, new files). **Categorize conflicts first**, then decide. See Conflict Triage below.
## Conflict Triage
**Always categorize before deciding to skip. High conflict count ≠ hard conflicts.**
| Type | Symptom | Resolution |
| ---------------------------- | ------------------------------------ | --------------------------------------------------------------- |
| **Binary snapshots (PNGs)** | `.png` files in conflict list | `git checkout --theirs $FILE && git add $FILE` — always trivial |
| **Modify/delete (new file)** | PR introduces files not on target | `git add $FILE` — keep the new file |
| **Modify/delete (removed)** | Target removed files the PR modifies | `git rm $FILE` — file no longer relevant |
| **Content conflicts** | Marker-based (`<<<<<<<`) | Accept theirs via python regex (see below) |
| **Add/add** | Both sides added same file | Accept theirs, verify no logic conflict |
| **Locale/JSON files** | i18n key additions | Accept theirs, validate JSON after |
```python
# Accept theirs for content conflicts
import re
pattern = r'<<<<<<< HEAD\n(.*?)=======\n(.*?)>>>>>>> [^\n]+\n?'
content = re.sub(pattern, r'\2', content, flags=re.DOTALL)
```
### Escalation Triggers (Flag for Human)
- **Package.json/lockfile changes** → skip on stable (transitive dep regression risk)
- **Core type definition changes** → requires human judgment
- **Business logic conflicts** (not just imports/exports) → requires domain knowledge
- **Admin-merged conflict resolutions** → get human review of the resolution before continuing the wave
## Auto-Skip Categories
Skip these without discussion:
- **Dep refresh PRs** — Risk of transitive dep regressions on stable. Cherry-pick individual CVE fixes instead.
- **CI/tooling changes** — Not user-facing
- **Test-only / lint rule changes** — Not user-facing
- **Revert pairs** — If PR A reverted by PR B, skip both. If fixed version (PR C) exists, backport only C.
- **Features not on target branch** — e.g., Painter, GLSLShader, appModeStore on core/1.40
- **Cloud-only PRs on core/\* branches** — App mode, cloud auth, cloud billing. These only affect cloud-hosted ComfyUI.
## Wave Verification
After merging each wave of PRs to a target branch, verify branch integrity before proceeding:
```bash
# Fetch latest state of target branch
git fetch origin TARGET_BRANCH
# Quick smoke check: does the branch build?
git worktree add /tmp/verify-TARGET origin/TARGET_BRANCH
cd /tmp/verify-TARGET
source ~/.nvm/nvm.sh && nvm use 24 && pnpm install && pnpm typecheck
git worktree remove /tmp/verify-TARGET --force
```
If typecheck fails, stop and investigate before continuing. A broken branch after wave N means all subsequent waves will compound the problem.
## Continuous Backporting Recommendation
Large backport sessions (50+ PRs) are expensive and error-prone. Prefer continuous backporting:
- Backport bug fixes as they merge to main (same day or next day)
- Use the automation labels immediately after merge
- Reserve session-style bulk backporting for catching up after gaps
- When a release branch is created, immediately start the continuous process
## Quick Reference
### Label-Driven Automation (default path)
```bash
gh api repos/Comfy-Org/ComfyUI_frontend/issues/$PR/labels \
-f "labels[]=needs-backport" -f "labels[]=TARGET_BRANCH"
# Wait 3 min, check: gh pr list --base TARGET_BRANCH --state open
```
### Manual Worktree Cherry-Pick (conflict fallback)
```bash
git worktree add /tmp/backport-$BRANCH origin/$BRANCH
cd /tmp/backport-$BRANCH
git checkout -b backport-$PR-to-$BRANCH origin/$BRANCH
git cherry-pick -m 1 $MERGE_SHA
# Resolve conflicts, push, create PR, merge
```
### PR Title Convention
```
[backport TARGET_BRANCH] Original Title (#ORIGINAL_PR)
```

View File

@@ -1,68 +0,0 @@
# Analysis & Decision Framework
## Categorization
| Category | Criteria | Action |
| -------------------- | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| **MUST** | User-facing bug, crash, data corruption, security. Clear breakage that users will hit. | Backport (with deps if needed) |
| **SHOULD** | UX improvement, minor bug, small dep chain. No user-visible breakage if skipped, but improves experience. | Backport if clean cherry-pick; defer if conflict resolution is non-trivial |
| **SKIP** | CI/tooling, test-only, lint rules, cosmetic, dep refresh | Skip with documented reason |
| **NEEDS DISCUSSION** | Large dep chain, unclear risk/benefit, touches core types | Flag for human |
### MUST vs SHOULD Decision Guide
When unsure, ask: "If a user on this stable branch reports this issue, would we consider it a bug?"
- **Yes** → MUST. The fix addresses broken behavior.
- **No, but it's noticeably better** → SHOULD. The fix is a quality-of-life improvement.
- **No, and it's cosmetic or internal** → SKIP.
For SHOULD items with conflicts: if conflict resolution requires more than trivial accept-theirs patterns (content conflicts in business logic, not just imports), downgrade to SKIP or escalate to NEEDS DISCUSSION.
## Branch Scope Filtering
**Before categorizing, filter by branch scope:**
| Target branch | Skip if PR is... |
| ------------- | ------------------------------------------------------------------- |
| `core/*` | Cloud-only (app mode, cloud auth, cloud billing, cloud-specific UI) |
| `cloud/*` | Local-only features not present on cloud branch |
Cloud-only PRs backported to `core/*` are wasted effort — `core/*` branches serve local/self-hosted users who never see cloud features. Check PR titles, descriptions, and files changed for cloud-specific indicators.
## Features Not on Stable Branches
Check before backporting — these don't exist on older branches:
- **Painter** (`src/extensions/core/painter.ts`) — not on core/1.40
- **GLSLShader** — not on core/1.40
- **App builder** — check per branch
- **appModeStore.ts** — not on core/1.40
## Dep Refresh PRs
Always SKIP on stable branches. Risk of transitive dependency regressions outweighs audit cleanup benefit. If a specific CVE fix is needed, cherry-pick that individual fix instead.
## Revert Pairs
If PR A is reverted by PR B:
- Skip BOTH A and B
- If a fixed version exists (PR C), backport only C
## Dependency Analysis
```bash
# Find other PRs that touched the same files
gh pr view $PR --json files --jq '.files[].path' | while read f; do
git log --oneline origin/TARGET..$MERGE_SHA -- "$f"
done
```
## Human Review Checkpoint
Present decisions.md before execution. Include:
1. All MUST/SHOULD/SKIP categorizations with rationale
2. Questions for human (feature existence, scope, deps)
3. Estimated effort per branch

View File

@@ -1,42 +0,0 @@
# Discovery — Candidate Collection
## Source 1: Slack Backport-Checker Bot
Use `slackdump` skill to export `#frontend-releases` channel (C09K9TPU2G7):
```bash
slackdump export -o ~/slack-exports/frontend-releases.zip C09K9TPU2G7
```
Parse bot messages for PRs flagged "Might need backport" per release version.
## Source 2: Git Log Gap Analysis
```bash
# Count gap
git log --oneline origin/TARGET..origin/main | wc -l
# List gap commits
git log --oneline origin/TARGET..origin/main
# Check if a PR is already on target
git log --oneline origin/TARGET --grep="#PR_NUMBER"
# Check for existing backport PRs
gh pr list --base TARGET --state all --search "backport PR_NUMBER"
```
## Source 3: GitHub PR Details
```bash
# Get merge commit SHA
gh pr view $PR --json mergeCommit,title --jq '"Title: \(.title)\nMerge: \(.mergeCommit.oid)"'
# Get files changed
gh pr view $PR --json files --jq '.files[].path'
```
## Output: candidate_list.md
Table per target branch:
| PR# | Title | Category | Flagged by Bot? | Decision |

View File

@@ -1,150 +0,0 @@
# Execution Workflow
## Per-Branch Execution Order
1. Smallest gap first (validation run)
2. Medium gap next (quick win)
3. Largest gap last (main effort)
## Step 1: Label-Driven Automation (Batch)
```bash
# Add labels to all candidates for a target branch
for pr in $PR_LIST; do
gh api repos/Comfy-Org/ComfyUI_frontend/issues/$pr/labels \
-f "labels[]=needs-backport" -f "labels[]=TARGET_BRANCH" --silent
sleep 2
done
# Wait 3 minutes for automation
sleep 180
# Check which got auto-PRs
gh pr list --base TARGET_BRANCH --state open --limit 50 --json number,title
```
## Step 2: Review & Merge Clean Auto-PRs
```bash
for pr in $AUTO_PRS; do
# Check size
gh pr view $pr --json title,additions,deletions,changedFiles \
--jq '"Files: \(.changedFiles), +\(.additions)/-\(.deletions)"'
# Admin merge
gh pr merge $pr --squash --admin
sleep 3
done
```
## Step 3: Manual Worktree for Conflicts
```bash
git fetch origin TARGET_BRANCH
git worktree add /tmp/backport-TARGET origin/TARGET_BRANCH
cd /tmp/backport-TARGET
for PR in ${CONFLICT_PRS[@]}; do
# Refresh target ref so each branch is based on current HEAD
git fetch origin TARGET_BRANCH
git checkout origin/TARGET_BRANCH
git checkout -b backport-$PR-to-TARGET origin/TARGET_BRANCH
git cherry-pick -m 1 $MERGE_SHA
# If conflict — NEVER skip based on file count alone!
# Categorize conflicts first: binary PNGs, modify/delete, content, add/add
# See SKILL.md Conflict Triage table for resolution per type.
# Resolve all conflicts, then:
git add .
GIT_EDITOR=true git cherry-pick --continue
git push origin backport-$PR-to-TARGET
NEW_PR=$(gh pr create --base TARGET_BRANCH --head backport-$PR-to-TARGET \
--title "[backport TARGET] TITLE (#$PR)" \
--body "Backport of #$PR..." | grep -oP '\d+$')
gh pr merge $NEW_PR --squash --admin
sleep 3
done
# Cleanup
cd -
git worktree remove /tmp/backport-TARGET --force
```
**⚠️ Human review for conflict resolutions:** When admin-merging a PR where you manually resolved conflicts (especially content conflicts beyond trivial accept-theirs), pause and present the resolution diff to the human for review before merging. Trivial resolutions (binary snapshots, modify/delete, locale key additions) can proceed without review.
## Step 4: Wave Verification
After completing all PRs in a wave for a target branch:
```bash
git fetch origin TARGET_BRANCH
git worktree add /tmp/verify-TARGET origin/TARGET_BRANCH
cd /tmp/verify-TARGET
source ~/.nvm/nvm.sh && nvm use 24 && pnpm install && pnpm typecheck
git worktree remove /tmp/verify-TARGET --force
```
If verification fails, stop and fix before proceeding to the next wave. Do not compound problems across waves.
## Conflict Resolution Patterns
### 1. Content Conflicts (accept theirs)
```python
import re
pattern = r'<<<<<<< HEAD\n(.*?)=======\n(.*?)>>>>>>> [^\n]+\n?'
content = re.sub(pattern, r'\2', content, flags=re.DOTALL)
```
### 2. Modify/Delete (two cases!)
```bash
# Case A: PR introduces NEW files not on target → keep them
git add $FILE
# Case B: Target REMOVED files the PR modifies → drop them
git rm $FILE
```
### 3. Binary Files (snapshots)
```bash
git checkout --theirs $FILE && git add $FILE
```
### 4. Locale Files
Usually adding new i18n keys — accept theirs, validate JSON:
```bash
python3 -c "import json; json.load(open('src/locales/en/main.json'))" && echo "Valid"
```
## Merge Conflicts After Other Merges
When merging multiple PRs to the same branch, later PRs may conflict with earlier merges:
```bash
git fetch origin TARGET_BRANCH
git rebase origin/TARGET_BRANCH
# Resolve new conflicts
git push --force origin backport-$PR-to-TARGET
sleep 20 # Wait for GitHub to recompute merge state
gh pr merge $PR --squash --admin
```
## Lessons Learned
1. **Automation reports more conflicts than reality**`cherry-pick -m 1` with git auto-merge handles many "conflicts" the automation can't
2. **Never skip based on conflict file count** — 12 or 27 conflicts can be trivial (snapshots, new files). Categorize first: binary PNGs, modify/delete, content, add/add.
3. **Modify/delete goes BOTH ways** — if the PR introduces new files (not on target), `git add` them. If target deleted files the PR modifies, `git rm`.
4. **Binary snapshot PNGs** — always `git checkout --theirs && git add`. Never skip a PR just because it has many snapshot conflicts.
5. **Batch label additions need 2s delay** between API calls to avoid rate limits
6. **Merging 6+ PRs rapidly** can cause later PRs to become unmergeable — wait 20-30s for GitHub to recompute merge state
7. **appModeStore.ts, painter files, GLSLShader files** don't exist on core/1.40 — `git rm` these
8. **Always validate JSON** after resolving locale file conflicts
9. **Dep refresh PRs** — skip on stable branches. Risk of transitive dep regressions outweighs audit cleanup. Cherry-pick individual CVE fixes instead.
10. **Verify after each wave** — run `pnpm typecheck` on the target branch after merging a batch. Catching breakage early prevents compounding errors.
11. **Cloud-only PRs don't belong on core/\* branches** — app mode, cloud auth, and cloud-specific UI changes are irrelevant to local users. Always check PR scope against branch scope before backporting.

View File

@@ -1,96 +0,0 @@
# Logging & Session Reports
## During Execution
Maintain `execution-log.md` with per-branch tables:
```markdown
| PR# | Title | Status | Backport PR | Notes |
| ----- | ----- | --------------------------------- | ----------- | ------- |
| #XXXX | Title | ✅ Merged / ⏭️ Skip / ⏸️ Deferred | #YYYY | Details |
```
## Wave Verification Log
Track verification results per wave:
```markdown
## Wave N Verification — TARGET_BRANCH
- PRs merged: #A, #B, #C
- Typecheck: ✅ Pass / ❌ Fail
- Issues found: (if any)
- Human review needed: (list any non-trivial conflict resolutions)
```
## Session Report Template
```markdown
# Backport Session Report
## Summary
| Branch | Candidates | Merged | Skipped | Deferred | Rate |
| ------ | ---------- | ------ | ------- | -------- | ---- |
## Deferred Items (Needs Human)
| PR# | Title | Branch | Issue |
## Conflict Resolutions Requiring Review
| PR# | Branch | Conflict Type | Resolution Summary |
## Automation Performance
| Metric | Value |
| --------------------------- | ----- |
| Auto success rate | X% |
| Manual resolution rate | X% |
| Overall clean rate | X% |
| Wave verification pass rate | X% |
## Process Recommendations
- Were there clusters of related PRs that should have been backported together?
- Any PRs that should have been backported sooner (continuous backporting candidates)?
- Feature branches that need tracking for future sessions?
```
## Final Deliverable: Visual Summary
At session end, generate a **mermaid diagram** showing all backported PRs organized by target branch and category (MUST/SHOULD), plus a summary table. Present this to the user as the final output.
```mermaid
graph TD
subgraph branch1["☁️ cloud/X.XX — N PRs"]
C1["#XXXX title"]
C2["#XXXX title"]
end
subgraph branch2must["🔴 core/X.XX MUST — N PRs"]
M1["#XXXX title"]
end
subgraph branch2should["🟡 core/X.XX SHOULD — N PRs"]
S1["#XXXX-#XXXX N auto-merged"]
S2["#XXXX-#XXXX N manual picks"]
end
classDef cloudStyle fill:#1a3a5c,stroke:#4da6ff,color:#e0f0ff
classDef coreStyle fill:#1a4a2e,stroke:#4dff88,color:#e0ffe8
classDef mustStyle fill:#5c1a1a,stroke:#ff4d4d,color:#ffe0e0
classDef shouldStyle fill:#4a3a1a,stroke:#ffcc4d,color:#fff5e0
```
Use the `mermaid` tool to render this diagram and present it alongside the summary table as the session's final deliverable.
## Files to Track
- `candidate_list.md` — all candidates per branch
- `decisions.md` — MUST/SHOULD/SKIP with rationale
- `wave-plan.md` — execution order
- `execution-log.md` — real-time status
- `backport-session-report.md` — final summary
All in `~/temp/backport-session/`.

View File

@@ -1,83 +0,0 @@
---
name: regenerating-screenshots
description: 'Creates a PR to regenerate Playwright screenshot expectations. Use when screenshot tests are failing on main or PRs due to stale golden images. Triggers on: regen screenshots, regenerate screenshots, update expectations, fix screenshot tests.'
---
# Regenerating Playwright Screenshot Expectations
Automates the process of triggering the `PR: Update Playwright Expectations`
GitHub Action by creating a labeled PR from `origin/main`.
## Steps
1. **Fetch latest main**
```bash
git fetch origin main
```
2. **Create a timestamped branch** from `origin/main`
Format: `regen-screenshots/YYYY-MM-DDTHH` (hour resolution, local time)
```bash
git checkout -b regen-screenshots/<datetime> origin/main
```
3. **Create an empty commit**
```bash
git commit --allow-empty -m "test: regenerate screenshot expectations"
```
4. **Push the branch**
```bash
git push origin regen-screenshots/<datetime>
```
5. **Generate a poem** about regenerating screenshots. Be creative — a
new, unique poem every time. Short (48 lines). Can be funny, wistful,
epic, haiku-style, limerick, sonnet fragment — vary the form.
6. **Create the PR** with the poem as the body (no label yet).
Write the poem to a temp file and use `--body-file`:
```bash
# Write poem to temp file
# Create PR:
gh pr create \
--base main \
--head regen-screenshots/<datetime> \
--title "test: regenerate screenshot expectations" \
--body-file <temp-file>
```
7. **Add the label** as a separate step to trigger the GitHub Action.
The `labeled` event only fires when a label is added after PR
creation, not when applied during creation via `--label`.
Use the GitHub API directly (`gh pr edit --add-label` fails due to
deprecated Projects Classic GraphQL errors):
```bash
gh api repos/{owner}/{repo}/issues/<pr-number>/labels \
-f "labels[]=New Browser Test Expectations" --method POST
```
8. **Report the result** to the user:
- PR URL
- Branch name
- Note that the GitHub Action will run automatically and commit
updated screenshots to the branch.
## Notes
- The `New Browser Test Expectations` label triggers the
`pr-update-playwright-expectations.yaml` workflow.
- The workflow runs Playwright with `--update-snapshots`, commits results
back to the PR branch, then removes the label.
- This is fire-and-forget — no need to wait for or monitor the Action.
- Always return to the original branch/worktree state after pushing.

View File

@@ -1,200 +0,0 @@
---
name: writing-playwright-tests
description: 'Writes Playwright e2e tests for ComfyUI_frontend. Use when creating, modifying, or debugging browser tests. Triggers on: playwright, e2e test, browser test, spec file.'
---
# Writing Playwright Tests for ComfyUI_frontend
## Golden Rules
1. **ALWAYS look at existing tests first.** Search `browser_tests/tests/` for similar patterns before writing new tests.
2. **ALWAYS read the fixture code.** The APIs are in `browser_tests/fixtures/` - read them directly instead of guessing.
3. **Use premade JSON workflow assets** instead of building workflows programmatically.
- Assets live in `browser_tests/assets/`
- Load with `await comfyPage.workflow.loadWorkflow('feature/my_workflow')`
- Create new assets by starting with `browser_tests/assets/default.json` and manually editing the JSON to match your desired graph state
## Vue Nodes vs LiteGraph: Decision Guide
Choose based on **what you're testing**, not personal preference:
| Testing... | Use | Why |
| ---------------------------------------------- | -------------------------------- | ---------------------------------------- |
| Vue-rendered node UI, DOM widgets, CSS states | `comfyPage.vueNodes.*` | Nodes are DOM elements, use locators |
| Canvas interactions, connections, legacy nodes | `comfyPage.nodeOps.*` | Canvas-based, use coordinates/references |
| Both in same test | Pick primary, minimize switching | Avoid confusion |
**Vue Nodes requires explicit opt-in:**
```typescript
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', true)
await comfyPage.vueNodes.waitForNodes()
```
**Vue Node state uses CSS classes:**
```typescript
const BYPASS_CLASS = /before:bg-bypass\/60/
await expect(node).toHaveClass(BYPASS_CLASS)
```
## Common Issues
These are frequent causes of flaky tests - check them first, but investigate if they don't apply:
| Symptom | Common Cause | Typical Fix |
| ---------------------------------- | ------------------------- | -------------------------------------------------------------------------------------- |
| Test passes locally, fails in CI | Missing nextFrame() | Add `await comfyPage.nextFrame()` after canvas ops (not needed after `loadWorkflow()`) |
| Keyboard shortcuts don't work | Missing focus | Add `await comfyPage.canvas.click()` first |
| Double-click doesn't trigger | Timing too fast | Add `{ delay: 5 }` option |
| Elements end up in wrong position | Drag animation incomplete | Use `{ steps: 10 }` not `{ steps: 1 }` |
| Widget value wrong after drag-drop | Upload incomplete | Add `{ waitForUpload: true }` |
| Test fails when run with others | Test pollution | Add `afterEach` with `resetView()` |
| Local screenshots don't match CI | Platform differences | Screenshots are Linux-only, use PR label |
## Test Tags
Add appropriate tags to every test:
| Tag | When to Use |
| ------------- | ----------------------------------------- |
| `@smoke` | Quick essential tests |
| `@slow` | Tests > 10 seconds |
| `@screenshot` | Visual regression tests |
| `@canvas` | Canvas interactions |
| `@node` | Node-related |
| `@widget` | Widget-related |
| `@mobile` | Mobile viewport (runs on Pixel 5 project) |
| `@2x` | HiDPI tests (runs on 2x scale project) |
```typescript
test.describe('Feature', { tag: ['@screenshot', '@canvas'] }, () => {
```
## Retry Patterns
**Never use `waitForTimeout`** - it's always wrong.
| Pattern | Use Case |
| ------------------------ | ---------------------------------------------------- |
| Auto-retrying assertions | `toBeVisible()`, `toHaveText()`, etc. (prefer these) |
| `expect.poll()` | Single value polling |
| `expect().toPass()` | Multiple assertions that must all pass |
```typescript
// Prefer auto-retrying assertions when possible
await expect(node).toBeVisible()
// Single value polling
await expect.poll(() => widget.getValue(), { timeout: 2000 }).toBe(100)
// Multiple conditions
await expect(async () => {
expect(await node1.getValue()).toBe('foo')
expect(await node2.getValue()).toBe('bar')
}).toPass({ timeout: 2000 })
```
## Screenshot Baselines
- **Screenshots are Linux-only.** Don't commit local screenshots.
- **To update baselines:** Add PR label `New Browser Test Expectations`
- **Mask dynamic content:**
```typescript
await expect(comfyPage.canvas).toHaveScreenshot('page.png', {
mask: [page.locator('.timestamp')]
})
```
## CI Debugging
1. Download artifacts from failed CI run
2. Extract and view trace: `npx playwright show-trace trace.zip`
3. CI deploys HTML report to Cloudflare Pages (link in PR comment)
4. Reproduce CI: `CI=true pnpm test:browser`
5. Local runs: `pnpm test:browser:local`
## Anti-Patterns
Avoid these common mistakes:
1. **Arbitrary waits** - Use retrying assertions instead
```typescript
// ❌ await page.waitForTimeout(500)
// ✅ await expect(element).toBeVisible()
```
2. **Implementation-tied selectors** - Use test IDs or semantic selectors
```typescript
// ❌ page.locator('div.container > button.btn-primary')
// ✅ page.getByTestId('submit-button')
```
3. **Missing nextFrame after canvas ops** - Canvas needs sync time
```typescript
await node.drag({ x: 50, y: 50 })
await comfyPage.nextFrame() // Required
```
4. **Shared state between tests** - Tests must be independent
```typescript
// ❌ let sharedData // Outside test
// ✅ Define state inside each test
```
## Quick Start Template
```typescript
// Path depends on test file location - adjust '../' segments accordingly
import {
comfyPageFixture as test,
comfyExpect as expect
} from '../fixtures/ComfyPage'
test.describe('FeatureName', { tag: ['@canvas'] }, () => {
test.afterEach(async ({ comfyPage }) => {
await comfyPage.canvasOps.resetView()
})
test('should do something', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('myWorkflow')
const node = (await comfyPage.nodeOps.getNodeRefsByTitle('KSampler'))[0]
// ... test logic
await expect(comfyPage.canvas).toHaveScreenshot('expected.png')
})
})
```
## Finding Patterns
```bash
# Find similar tests
grep -r "KSampler" browser_tests/tests/
# Find usage of a fixture method
grep -r "loadWorkflow" browser_tests/tests/
# Find tests with specific tag
grep -r '@screenshot' browser_tests/tests/
```
## Key Files to Read
| Purpose | Path |
| ----------------- | ------------------------------------------ |
| Main fixture | `browser_tests/fixtures/ComfyPage.ts` |
| Helper classes | `browser_tests/fixtures/helpers/` |
| Component objects | `browser_tests/fixtures/components/` |
| Test selectors | `browser_tests/fixtures/selectors.ts` |
| Vue Node helpers | `browser_tests/fixtures/VueNodeHelpers.ts` |
| Test assets | `browser_tests/assets/` |
| Existing tests | `browser_tests/tests/` |
**Read the fixture code directly** - it's the source of truth for available methods.

View File

@@ -1,14 +0,0 @@
issue_enrichment:
auto_enrich:
enabled: true
reviews:
high_level_summary: false
auto_review:
drafts: true
ignore_title_keywords:
- '[release]'
- '[backport'
ignore_usernames:
- comfy-pr-bot
- github-actions
- github-actions[bot]

View File

@@ -0,0 +1,21 @@
---
description: Creating unit tests
globs:
alwaysApply: false
---
# Creating unit tests
- This project uses `vitest` for unit testing
- Tests are stored in the `test/` directory
- Tests should be cross-platform compatible; able to run on Windows, macOS, and linux
- e.g. the use of `path.resolve`, or `path.join` and `path.sep` to ensure that tests work the same on all platforms
- Tests should be mocked properly
- Mocks should be cleanly written and easy to understand
- Mocks should be re-usable where possible
## Unit test style
- Prefer the use of `test.extend` over loose variables
- To achieve this, import `test as baseTest` from `vitest`
- Never use `it`; `test` should be used in place of this

61
.cursorrules Normal file
View File

@@ -0,0 +1,61 @@
# Vue 3 Composition API Project Rules
## Vue 3 Composition API Best Practices
- Use setup() function for component logic
- Utilize ref and reactive for reactive state
- Implement computed properties with computed()
- Use watch and watchEffect for side effects
- Implement lifecycle hooks with onMounted, onUpdated, etc.
- Utilize provide/inject for dependency injection
- Use vue 3.5 style of default prop declaration. Example:
```typescript
const { nodes, showTotal = true } = defineProps<{
nodes: ApiNodeCost[]
showTotal?: boolean
}>()
```
- Organize vue component in <template> <script> <style> order
## Project Structure
```
src/
components/
constants/
composables/
views/
stores/
services/
App.vue
main.ts
```
## Styling Guidelines
- Use Tailwind CSS for styling
- Implement responsive design with Tailwind CSS
## PrimeVue Component Guidelines
DO NOT use deprecated PrimeVue components. Use these replacements instead:
- Dropdown → Use Select (import from 'primevue/select')
- OverlayPanel → Use Popover (import from 'primevue/popover')
- Calendar → Use DatePicker (import from 'primevue/datepicker')
- InputSwitch → Use ToggleSwitch (import from 'primevue/toggleswitch')
- Sidebar → Use Drawer (import from 'primevue/drawer')
- Chips → Use AutoComplete with multiple enabled and typeahead disabled
- TabMenu → Use Tabs without panels
- Steps → Use Stepper without panels
- InlineMessage → Use Message component
## Development Guidelines
1. Leverage VueUse functions for performance-enhancing styles
2. Use es-toolkit for utility functions
3. Use TypeScript for type safety
4. Implement proper props and emits definitions
5. Utilize Vue 3's Teleport component when needed
6. Use Suspense for async components
7. Implement proper error handling
8. Follow Vue 3 style guide and naming conventions
9. Use Vite for fast development and building
10. Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
11. Never use deprecated PrimeVue components listed above

View File

@@ -14,33 +14,30 @@ DEV_SERVER_COMFYUI_URL=http://127.0.0.1:8188
# Allow dev server access from remote IP addresses. # Allow dev server access from remote IP addresses.
# If true, the vite dev server will listen on all addresses, including LAN # If true, the vite dev server will listen on all addresses, including LAN
# and public addresses. # and public addresses.
# VITE_REMOTE_DEV=true VITE_REMOTE_DEV=false
# The directory containing the ComfyUI installation used to run Playwright tests. # The directory containing the ComfyUI installation used to run Playwright tests.
# If you aren't using a separate install for testing, point this to your regular install. # If you aren't using a separate install for testing, point this to your regular install.
TEST_COMFYUI_DIR=/home/ComfyUI TEST_COMFYUI_DIR=/home/ComfyUI
# Whether to enable minification of the frontend code. # Whether to enable minification of the frontend code.
# ENABLE_MINIFY=true ENABLE_MINIFY=true
# Whether to disable proxying the `/templates` route. If true, allows you to # Whether to disable proxying the `/templates` route. If true, allows you to
# serve templates from the ComfyUI_frontend/public/templates folder (for # serve templates from the ComfyUI_frontend/public/templates folder (for
# locally testing changes to templates). When false or nonexistent, the # locally testing changes to templates). When false or nonexistent, the
# templates are served via the normal method from the server's python site # templates are served via the normal method from the server's python site
# packages. # packages.
# DISABLE_TEMPLATES_PROXY=true DISABLE_TEMPLATES_PROXY=false
# If playwright tests are being run via vite dev server, Vue plugins will # If playwright tests are being run via vite dev server, Vue plugins will
# invalidate screenshots. When `true`, vite plugins will not be loaded. # invalidate screenshots. When `true`, vite plugins will not be loaded.
# DISABLE_VUE_PLUGINS=true DISABLE_VUE_PLUGINS=false
# Algolia credentials required for developing with the new custom node manager. # Algolia credentials required for developing with the new custom node manager.
ALGOLIA_APP_ID=4E0RO38HS8 ALGOLIA_APP_ID=4E0RO38HS8
ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579 ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579
# Enable PostHog debug logging in the browser console.
# VITE_POSTHOG_DEBUG=true
# Sentry ENV vars replace with real ones for debugging # Sentry ENV vars replace with real ones for debugging
# SENTRY_AUTH_TOKEN=private-token # get from sentry # SENTRY_AUTH_TOKEN=private-token # get from sentry
# SENTRY_ORG=comfy-org # SENTRY_ORG=comfy-org

15
.gitattributes vendored
View File

@@ -1,5 +1,16 @@
# Force all text files to use LF line endings # Default
* text=auto eol=lf * text=auto
# Force TS to LF to make the unixy scripts not break on Windows
*.cjs text eol=lf
*.js text eol=lf
*.json text eol=lf
*.mjs text eol=lf
*.mts text eol=lf
*.snap text eol=lf
*.ts text eol=lf
*.vue text eol=lf
*.yaml text eol=lf
# Generated files # Generated files
packages/registry-types/src/comfyRegistryTypes.ts linguist-generated=true packages/registry-types/src/comfyRegistryTypes.ts linguist-generated=true

23
.github/AGENTS.md vendored
View File

@@ -1,23 +0,0 @@
# PR Review Context
Context for automated PR review system.
## Review Scope
This automated review performs comprehensive analysis:
- Architecture and design patterns
- Security vulnerabilities
- Performance implications
- Code quality and maintainability
- Integration concerns
For implementation details, see `.claude/commands/comprehensive-pr-review.md`.
## GitHub Actions: Fork PR Permissions
Fork PRs get a **read-only `GITHUB_TOKEN`** — no PR comments, no secret access, no pushing.
Any workflow that needs write access must use the **two-workflow split**: a `pull_request`-triggered `ci-*.yaml` uploads artifacts (including PR metadata), then a `workflow_run`-triggered `pr-*.yaml` downloads them and posts comments with write permissions. See `ci-size-data``pr-size-report` or `ci-perf-report``pr-perf-report`. Use `.github/actions/post-pr-report-comment` for the comment step.
Never write PR comments directly from `pull_request` workflows or use `pull_request_target` to run untrusted code.

38
.github/CLAUDE.md vendored
View File

@@ -1,4 +1,36 @@
<!-- A rose by any other name would smell as sweet, # ComfyUI Frontend - Claude Review Context
But Claude insists on files named for its own conceit. -->
@AGENTS.md This file provides additional context for the automated PR review system.
## Quick Reference
### PrimeVue Component Migrations
When reviewing, flag these deprecated components:
- `Dropdown` → Use `Select` from 'primevue/select'
- `OverlayPanel` → Use `Popover` from 'primevue/popover'
- `Calendar` → Use `DatePicker` from 'primevue/datepicker'
- `InputSwitch` → Use `ToggleSwitch` from 'primevue/toggleswitch'
- `Sidebar` → Use `Drawer` from 'primevue/drawer'
- `Chips` → Use `AutoComplete` with multiple enabled and typeahead disabled
- `TabMenu` → Use `Tabs` without panels
- `Steps` → Use `Stepper` without panels
- `InlineMessage` → Use `Message` component
### API Utilities Reference
- `api.apiURL()` - Backend API calls (/prompt, /queue, /view, etc.)
- `api.fileURL()` - Static file access (templates, extensions)
- `$t()` / `i18n.global.t()` - Internationalization
- `DOMPurify.sanitize()` - HTML sanitization
## Review Scope
This automated review performs comprehensive analysis including:
- Architecture and design patterns
- Security vulnerabilities
- Performance implications
- Code quality and maintainability
- Integration concerns
For implementation details, see `.claude/commands/comprehensive-pr-review.md`.

View File

@@ -10,7 +10,10 @@ body:
options: options:
- label: I am running the latest version of ComfyUI - label: I am running the latest version of ComfyUI
required: true required: true
- label: I have custom nodes enabled - label: I have searched existing issues to make sure this isn't a duplicate
required: true
- label: I have tested with all custom nodes disabled ([see how](https://docs.comfy.org/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled))
required: true
- type: textarea - type: textarea
id: description id: description
@@ -57,7 +60,7 @@ body:
attributes: attributes:
label: ComfyUI Frontend Version label: ComfyUI Frontend Version
description: Found in Settings > About (e.g., "1.3.45") description: Found in Settings > About (e.g., "1.3.45")
placeholder: '1.3.45' placeholder: "1.3.45"
validations: validations:
required: true required: true

View File

@@ -4,6 +4,13 @@ labels: []
type: Feature type: Feature
body: body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the problem you're experiencing, and that it's not addressed in a recent build/commit.
options:
- label: I have searched the existing issues and checked the recent builds/commits
required: true
- type: markdown - type: markdown
attributes: attributes:
value: | value: |

View File

@@ -104,14 +104,14 @@ runs:
- name: Find existing comment - name: Find existing comment
id: find id: find
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0 uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
with: with:
issue-number: ${{ inputs.issue-number || github.event.pull_request.number }} issue-number: ${{ inputs.issue-number || github.event.pull_request.number }}
comment-author: github-actions[bot] comment-author: github-actions[bot]
body-includes: ${{ steps.build.outputs.marker_search }} body-includes: ${{ steps.build.outputs.marker_search }}
- name: Post or update comment - name: Post or update comment
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
with: with:
issue-number: ${{ inputs.issue-number || github.event.pull_request.number }} issue-number: ${{ inputs.issue-number || github.event.pull_request.number }}
comment-id: ${{ steps.find.outputs.comment-id }} comment-id: ${{ steps.find.outputs.comment-id }}

View File

@@ -1,35 +0,0 @@
name: Post PR Report Comment
description: Reads a markdown report file and posts/updates a single idempotent comment on a PR.
inputs:
pr-number:
description: PR number to comment on
required: true
report-file:
description: Path to the markdown report file
required: true
comment-marker:
description: HTML comment marker for idempotent matching
required: true
token:
description: GitHub token with pull-requests write permission
required: true
runs:
using: composite
steps:
- name: Read report
id: report
uses: juliangruber/read-file-action@b549046febe0fe86f8cb4f93c24e284433f9ab58 # v1.1.7
with:
path: ${{ inputs.report-file }}
- name: Create or update PR comment
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
with:
token: ${{ inputs.token }}
number: ${{ inputs.pr-number }}
body: |
${{ steps.report.outputs.content }}
${{ inputs.comment-marker }}
body-include: ${{ inputs.comment-marker }}

View File

@@ -16,7 +16,7 @@ runs:
# Checkout ComfyUI repo, install the dev_tools node and start server # Checkout ComfyUI repo, install the dev_tools node and start server
- name: Checkout ComfyUI - name: Checkout ComfyUI
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
repository: 'comfyanonymous/ComfyUI' repository: 'comfyanonymous/ComfyUI'
path: 'ComfyUI' path: 'ComfyUI'
@@ -33,7 +33,7 @@ runs:
fi fi
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v4
with: with:
python-version: '3.10' python-version: '3.10'

View File

@@ -12,17 +12,29 @@ runs:
# Install pnpm, Node.js, build frontend # Install pnpm, Node.js, build frontend
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version: 'lts/*' node-version: 'lts/*'
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: './pnpm-lock.yaml' cache-dependency-path: './pnpm-lock.yaml'
# Restore tool caches before running any build/lint operations
- name: Restore tool output cache
uses: actions/cache/restore@v4
with:
path: |
./.cache
./tsconfig.tsbuildinfo
key: tool-cache-${{ runner.os }}-${{ hashFiles('./pnpm-lock.yaml') }}-${{ hashFiles('./src/**/*.{ts,vue,js,mts}', './*.config.*') }}
restore-keys: |
tool-cache-${{ runner.os }}-${{ hashFiles('./pnpm-lock.yaml') }}-
tool-cache-${{ runner.os }}-
- name: Install dependencies - name: Install dependencies
shell: bash shell: bash
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile

View File

@@ -11,7 +11,7 @@ runs:
echo "playwright-version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT echo "playwright-version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
- name: Cache Playwright Browsers - name: Cache Playwright Browsers
uses: actions/cache@v5 # v5.0.2 uses: actions/cache@v4
id: cache-playwright-browsers id: cache-playwright-browsers
with: with:
path: '~/.cache/ms-playwright' path: '~/.cache/ms-playwright'

View File

@@ -1,23 +0,0 @@
name: Start ComfyUI Server
description: 'Start ComfyUI server in a container environment (assumes ComfyUI is pre-installed)'
inputs:
front_end_root:
description: 'Path to frontend dist directory'
required: false
default: '$GITHUB_WORKSPACE/dist'
timeout:
description: 'Timeout in seconds for server startup'
required: false
default: '600'
runs:
using: 'composite'
steps:
- name: Copy devtools and start server
shell: bash
run: |
set -euo pipefail
cp -r ./tools/devtools/* /ComfyUI/custom_nodes/ComfyUI_devtools/
cd /ComfyUI && python3 main.py --cpu --multi-user --front-end-root "${{ inputs.front_end_root }}" &
wait-for-it --service 127.0.0.1:8188 -t ${{ inputs.timeout }}

View File

@@ -34,4 +34,4 @@ Follow Vue 3 style guide and naming conventions
Use Vite for fast development and building Use Vite for fast development and building
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json. Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json.

View File

@@ -1,3 +0,0 @@
{
"posthog-js@*": { "licenses": "Apache-2.0" }
}

View File

@@ -1,5 +1,5 @@
# Description: When upstream electron API is updated, click dispatch to update the TypeScript type definitions in this repo
name: 'Api: Update Electron API Types' name: 'Api: Update Electron API Types'
description: 'When upstream electron API is updated, click dispatch to update the TypeScript type definitions in this repo'
on: on:
workflow_dispatch: workflow_dispatch:
@@ -13,17 +13,17 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: lts/*
cache: 'pnpm' cache: 'pnpm'
- name: Update electron types - name: Update electron types
@@ -36,7 +36,7 @@ jobs:
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with: with:
token: ${{ secrets.PR_GH_TOKEN }} token: ${{ secrets.PR_GH_TOKEN }}
commit-message: '[chore] Update electron-types to ${{ steps.get-version.outputs.NEW_VERSION }}' commit-message: '[chore] Update electron-types to ${{ steps.get-version.outputs.NEW_VERSION }}'

View File

@@ -1,5 +1,5 @@
# Description: When upstream ComfyUI-Manager API is updated, click dispatch to update the TypeScript type definitions in this repo
name: 'Api: Update Manager API Types' name: 'Api: Update Manager API Types'
description: 'When upstream ComfyUI-Manager API is updated, click dispatch to update the TypeScript type definitions in this repo'
on: on:
# Manual trigger # Manual trigger
@@ -18,24 +18,24 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: lts/*
cache: 'pnpm' cache: 'pnpm'
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Checkout ComfyUI-Manager repository - name: Checkout ComfyUI-Manager repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
repository: Comfy-Org/ComfyUI-Manager repository: Comfy-Org/ComfyUI-Manager
path: ComfyUI-Manager path: ComfyUI-Manager
@@ -86,7 +86,7 @@ jobs:
- name: Create Pull Request - name: Create Pull Request
if: steps.check-changes.outputs.changed == 'true' if: steps.check-changes.outputs.changed == 'true'
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with: with:
token: ${{ secrets.PR_GH_TOKEN }} token: ${{ secrets.PR_GH_TOKEN }}
commit-message: '[chore] Update ComfyUI-Manager API types from ComfyUI-Manager@${{ steps.manager-info.outputs.commit }}' commit-message: '[chore] Update ComfyUI-Manager API types from ComfyUI-Manager@${{ steps.manager-info.outputs.commit }}'

View File

@@ -1,5 +1,5 @@
# Description: When upstream comfy-api is updated, click dispatch to update the TypeScript type definitions in this repo
name: 'Api: Update Registry API Types' name: 'Api: Update Registry API Types'
description: 'When upstream comfy-api is updated, click dispatch to update the TypeScript type definitions in this repo'
on: on:
# Manual trigger # Manual trigger
@@ -17,24 +17,24 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: lts/*
cache: 'pnpm' cache: 'pnpm'
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Checkout comfy-api repository - name: Checkout comfy-api repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
repository: Comfy-Org/comfy-api repository: Comfy-Org/comfy-api
path: comfy-api path: comfy-api
@@ -87,7 +87,7 @@ jobs:
- name: Create Pull Request - name: Create Pull Request
if: steps.check-changes.outputs.changed == 'true' if: steps.check-changes.outputs.changed == 'true'
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with: with:
token: ${{ secrets.PR_GH_TOKEN }} token: ${{ secrets.PR_GH_TOKEN }}
commit-message: '[chore] Update Comfy Registry API types from comfy-api@${{ steps.api-info.outputs.commit }}' commit-message: '[chore] Update Comfy Registry API types from comfy-api@${{ steps.api-info.outputs.commit }}'

View File

@@ -1,100 +0,0 @@
name: 'CI: Dist Telemetry Scan'
on:
pull_request:
branches-ignore: [wip/*, draft/*, temp/*]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
with:
version: 10
- name: Use Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project
run: pnpm build
env:
DISTRIBUTION: localhost
- name: Scan dist for GTM telemetry references
run: |
set -euo pipefail
echo '🔍 Scanning for Google Tag Manager references...'
if rg --no-ignore -n \
-g '*.html' \
-g '*.js' \
-e 'Google Tag Manager' \
-e '(?i)\bgtm\.js\b' \
-e '(?i)googletagmanager\.com/gtm\.js\\?id=' \
-e '(?i)googletagmanager\.com/ns\.html\\?id=' \
dist; then
echo '❌ ERROR: Google Tag Manager references found in dist assets!'
echo 'GTM must be properly tree-shaken from OSS builds.'
exit 1
fi
echo '✅ No GTM references found'
- name: Scan dist for Mixpanel telemetry references
run: |
set -euo pipefail
echo '🔍 Scanning for Mixpanel references...'
if rg --no-ignore -n \
-g '*.html' \
-g '*.js' \
-e '(?i)mixpanel\.init' \
-e '(?i)mixpanel\.identify' \
-e 'MixpanelTelemetryProvider' \
-e 'mp\.comfy\.org' \
-e 'mixpanel-browser' \
-e '(?i)mixpanel\.track\(' \
dist; then
echo '❌ ERROR: Mixpanel references found in dist assets!'
echo 'Mixpanel must be properly tree-shaken from OSS builds.'
echo ''
echo 'To fix this:'
echo '1. Use the TelemetryProvider pattern (see src/platform/telemetry/)'
echo '2. Call telemetry via useTelemetry() hook'
echo '3. Use conditional dynamic imports behind isCloud checks'
exit 1
fi
echo '✅ No Mixpanel references found'
- name: Scan dist for PostHog telemetry references
run: |
set -euo pipefail
echo '🔍 Scanning for PostHog references...'
if rg --no-ignore -n \
-g '*.html' \
-g '*.js' \
-e '(?i)posthog\.init' \
-e '(?i)posthog\.capture' \
-e 'PostHogTelemetryProvider' \
-e 'ph\.comfy\.org' \
-e 'posthog-js' \
dist; then
echo '❌ ERROR: PostHog references found in dist assets!'
echo 'PostHog must be properly tree-shaken from OSS builds.'
exit 1
fi
echo '✅ No PostHog references found'

View File

@@ -1,5 +1,5 @@
# Description: Validates JSON syntax in all tracked .json files (excluding tsconfig*.json) using jq name: "CI: JSON Validation"
name: 'CI: JSON Validation' description: "Validates JSON syntax in all tracked .json files (excluding tsconfig*.json) using jq"
on: on:
push: push:
@@ -13,6 +13,6 @@ jobs:
json-lint: json-lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- name: Validate JSON syntax - name: Validate JSON syntax
run: ./scripts/cicd/check-json.sh run: ./scripts/cicd/check-json.sh

View File

@@ -1,5 +1,5 @@
# Description: Linting and code formatting validation for pull requests name: "CI: Lint Format"
name: 'CI: Lint Format' description: "Linting and code formatting validation for pull requests"
on: on:
pull_request: pull_request:
@@ -18,21 +18,23 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout PR - name: Checkout PR
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || github.ref }} ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || github.ref }}
token: ${{ !github.event.pull_request.head.repo.fork && secrets.PR_GH_TOKEN || github.token }}
- name: Setup frontend - name: Install pnpm
uses: ./.github/actions/setup-frontend uses: pnpm/action-setup@v4
- name: Detect browser_tests changes
id: changed-paths
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with: with:
filters: | version: 10
browser_tests:
- 'browser_tests/**' - name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run ESLint with auto-fix - name: Run ESLint with auto-fix
run: pnpm lint:fix run: pnpm lint:fix
@@ -40,7 +42,7 @@ jobs:
- name: Run Stylelint with auto-fix - name: Run Stylelint with auto-fix
run: pnpm stylelint:fix run: pnpm stylelint:fix
- name: Run oxfmt with auto-format - name: Run Prettier with auto-format
run: pnpm format run: pnpm format
- name: Check for changes - name: Check for changes
@@ -58,7 +60,7 @@ jobs:
git config --local user.email "action@github.com" git config --local user.email "action@github.com"
git config --local user.name "GitHub Action" git config --local user.name "GitHub Action"
git add . git add .
git commit -m "[automated] Apply ESLint and Oxfmt fixes" git commit -m "[automated] Apply ESLint and Prettier fixes"
git push git push
- name: Final validation - name: Final validation
@@ -68,27 +70,23 @@ jobs:
pnpm format:check pnpm format:check
pnpm knip pnpm knip
- name: Typecheck browser tests
if: steps.changed-paths.outputs.browser_tests == 'true'
run: pnpm typecheck:browser
- name: Comment on PR about auto-fix - name: Comment on PR about auto-fix
if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository
continue-on-error: true continue-on-error: true
uses: actions/github-script@v8 uses: actions/github-script@v7
with: with:
script: | script: |
github.rest.issues.createComment({ github.rest.issues.createComment({
issue_number: context.issue.number, issue_number: context.issue.number,
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: '## 🔧 Auto-fixes Applied\n\nThis PR has been automatically updated to fix linting and formatting issues.\n\n**⚠️ Important**: Your local branch is now behind. Run `git pull` before making additional changes to avoid conflicts.\n\n### Changes made:\n- ESLint auto-fixes\n- Oxfmt formatting' body: '## 🔧 Auto-fixes Applied\n\nThis PR has been automatically updated to fix linting and formatting issues.\n\n**⚠️ Important**: Your local branch is now behind. Run `git pull` before making additional changes to avoid conflicts.\n\n### Changes made:\n- ESLint auto-fixes\n- Prettier formatting'
}) })
- name: Comment on PR about manual fix needed - name: Comment on PR about manual fix needed
if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name != github.repository if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name != github.repository
continue-on-error: true continue-on-error: true
uses: actions/github-script@v8 uses: actions/github-script@v7
with: with:
script: | script: |
github.rest.issues.createComment({ github.rest.issues.createComment({

View File

@@ -1,119 +0,0 @@
name: 'CI: OSS Assets Validation'
on:
pull_request:
branches-ignore: [wip/*, draft/*, temp/*]
push:
branches: [main, dev*]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
validate-fonts:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Install pnpm
uses: pnpm/action-setup@9fd676a19091d4595eefd76e4bd31c97133911f1 # v4.2.0
with:
version: 10
- name: Use Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project
run: pnpm build
env:
DISTRIBUTION: localhost
- name: Check for proprietary fonts in dist
run: |
set -euo pipefail
echo '🔍 Checking dist for proprietary ABCROM fonts...'
if [ ! -d "dist" ] || [ -z "$(ls -A dist)" ]; then
echo '❌ ERROR: dist/ directory missing or empty!'
exit 1
fi
# Check for ABCROM font files
if find dist/ -type f -iname '*abcrom*' \
\( -name '*.woff' -o -name '*.woff2' -o -name '*.ttf' -o -name '*.otf' \) \
-print -quit | grep -q .; then
echo ''
echo '❌ ERROR: Found proprietary ABCROM font files in dist!'
echo ''
find dist/ -type f -iname '*abcrom*' \
\( -name '*.woff' -o -name '*.woff2' -o -name '*.ttf' -o -name '*.otf' \)
echo ''
echo 'ABCROM fonts are proprietary and should not ship to OSS builds.'
echo ''
echo 'To fix this:'
echo '1. Use conditional font loading based on isCloud'
echo '2. Ensure fonts are dynamically imported, not bundled'
echo '3. Check vite config for font handling'
exit 1
fi
echo '✅ No proprietary fonts found in dist'
validate-licenses:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Install pnpm
uses: pnpm/action-setup@9fd676a19091d4595eefd76e4bd31c97133911f1 # v4.2.0
with:
version: 10
- name: Use Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Validate production dependency licenses
run: |
set -euo pipefail
echo '🔍 Checking production dependency licenses...'
# Use license-checker-rseidelsohn (actively maintained fork, handles monorepos)
# Exclude internal @comfyorg packages from license check
# Run in if condition to capture exit code
if npx license-checker-rseidelsohn@4 \
--production \
--summary \
--excludePackages '@comfyorg/comfyui-frontend;@comfyorg/design-system;@comfyorg/registry-types;@comfyorg/shared-frontend-utils;@comfyorg/tailwind-utils;@comfyorg/comfyui-electron-types' \
--clarificationsFile .github/license-clarifications.json \
--onlyAllow 'MIT;MIT*;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;0BSD;BlueOak-1.0.0;Python-2.0;CC0-1.0;Unlicense;(MIT OR Apache-2.0);(MIT OR GPL-3.0);(Apache-2.0 OR MIT);(MPL-2.0 OR Apache-2.0);CC-BY-4.0;CC-BY-3.0;GPL-3.0-only'; then
echo ''
echo '✅ All production dependency licenses are approved!'
else
echo ''
echo '❌ ERROR: Found dependencies with non-approved licenses!'
echo ''
echo 'To fix this:'
echo '1. Check the license of the problematic package'
echo '2. Find an alternative package with an approved license'
echo '3. If the license is safe and OSI-approved, add it to the --onlyAllow list'
echo ''
echo 'For more info on OSI-approved licenses:'
echo 'https://opensource.org/licenses'
exit 1
fi

View File

@@ -1,70 +0,0 @@
name: 'CI: Performance Report'
on:
push:
branches: [main, core/*]
paths-ignore: ['**/*.md']
pull_request:
branches-ignore: [wip/*, draft/*, temp/*]
paths-ignore: ['**/*.md']
concurrency:
group: perf-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
perf-tests:
if: github.repository == 'Comfy-Org/ComfyUI_frontend'
runs-on: ubuntu-latest
timeout-minutes: 30
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
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup frontend
uses: ./.github/actions/setup-frontend
with:
include_build_step: true
- name: Start ComfyUI server
uses: ./.github/actions/start-comfyui-server
- name: Run performance tests
id: perf
continue-on-error: true
run: pnpm exec playwright test --project=performance --workers=1 --repeat-each=3
- name: Upload perf metrics
if: always()
uses: actions/upload-artifact@v6
with:
name: perf-metrics
path: test-results/perf-metrics.json
retention-days: 30
if-no-files-found: warn
- name: Save PR metadata
if: github.event_name == 'pull_request'
run: |
mkdir -p temp/perf-meta
echo "${{ github.event.number }}" > temp/perf-meta/number.txt
echo "${{ github.event.pull_request.base.ref }}" > temp/perf-meta/base.txt
- name: Upload PR metadata
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v6
with:
name: perf-meta
path: temp/perf-meta/

View File

@@ -1,5 +1,5 @@
# Description: Validates Python code in tools/devtools directory name: "CI: Python Validation"
name: 'CI: Python Validation' description: "Validates Python code in tools/devtools directory"
on: on:
pull_request: pull_request:
@@ -16,10 +16,10 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v5
with: with:
python-version: '3.11' python-version: '3.11'

View File

@@ -1,26 +0,0 @@
# Description: Runs shellcheck on tracked shell scripts when they change
name: 'CI: Shell Validation'
on:
push:
branches:
- main
paths:
- '**/*.sh'
pull_request:
paths:
- '**/*.sh'
jobs:
shell-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install shellcheck
run: |
sudo apt-get update
sudo apt-get install -y shellcheck
- name: Run shellcheck
run: bash ./scripts/cicd/check-shell.sh

View File

@@ -1,4 +1,4 @@
name: 'CI: Size Data' name: "CI: Size Data"
on: on:
push: push:
@@ -17,10 +17,21 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- name: Setup frontend - name: Install pnpm
uses: ./.github/actions/setup-frontend uses: pnpm/action-setup@v4.1.0
with:
version: 10
- name: Install Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Build project - name: Build project
run: pnpm build run: pnpm build
@@ -35,7 +46,7 @@ jobs:
echo ${{ github.base_ref }} > ./temp/size/base.txt echo ${{ github.base_ref }} > ./temp/size/base.txt
- name: Upload size data - name: Upload size data
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
with: with:
name: size-data name: size-data
path: temp/size path: temp/size

View File

@@ -1,11 +1,14 @@
# Description: Deploys test results from forked PRs (forks can't access deployment secrets) name: "CI: Tests E2E (Deploy for Forks)"
name: 'CI: Tests E2E (Deploy for Forks)' description: "Deploys test results from forked PRs (forks can't access deployment secrets)"
on: on:
workflow_run: workflow_run:
workflows: ['CI: Tests E2E'] workflows: ["CI: Tests E2E"]
types: [requested, completed] types: [requested, completed]
env:
DATE_FORMAT: '+%m/%d/%Y, %I:%M:%S %p'
jobs: jobs:
deploy-and-comment-forked-pr: deploy-and-comment-forked-pr:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -28,11 +31,11 @@ jobs:
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}" echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Get PR Number - name: Get PR Number
id: pr id: pr
uses: actions/github-script@v8 uses: actions/github-script@v7
with: with:
script: | script: |
const { data: prs } = await github.rest.pulls.list({ const { data: prs } = await github.rest.pulls.list({
@@ -60,11 +63,12 @@ jobs:
./scripts/cicd/pr-playwright-deploy-and-comment.sh \ ./scripts/cicd/pr-playwright-deploy-and-comment.sh \
"${{ steps.pr.outputs.result }}" \ "${{ steps.pr.outputs.result }}" \
"${{ github.event.workflow_run.head_branch }}" \ "${{ github.event.workflow_run.head_branch }}" \
"starting" "starting" \
"$(date -u '${{ env.DATE_FORMAT }}')"
- name: Download and Deploy Reports - name: Download and Deploy Reports
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }} run-id: ${{ github.event.workflow_run.id }}
@@ -77,7 +81,6 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
GITHUB_SHA: ${{ github.event.workflow_run.head_sha }}
run: | run: |
# Rename merged report if exists # Rename merged report if exists
[ -d "reports/playwright-report-chromium-merged" ] && \ [ -d "reports/playwright-report-chromium-merged" ] && \

View File

@@ -1,14 +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: on:
push: push:
branches: [main, master, core/*, desktop/*] branches: [main, master, core/*, desktop/*]
paths-ignore: ['**/*.md']
pull_request: pull_request:
branches-ignore: [wip/*, draft/*, temp/*] branches-ignore:
paths-ignore: ['**/*.md'] [wip/*, draft/*, temp/*, vue-nodes-migration, sno-playwright-*]
workflow_dispatch:
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
@@ -17,56 +15,65 @@ concurrency:
jobs: jobs:
setup: setup:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps: steps:
- name: Checkout repository - 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 - name: Setup frontend
uses: ./.github/actions/setup-frontend uses: ./.github/actions/setup-frontend
with: with:
include_build_step: true 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) # Save the entire workspace as cache for later test jobs to restore
- name: Upload built frontend - name: Generate cache key
uses: actions/upload-artifact@v6 id: cache-key
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
- name: Save cache
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
with: with:
name: frontend-dist path: .
path: dist/ key: comfyui-setup-${{ steps.cache-key.outputs.key }}
retention-days: 1
# Sharded chromium tests # Sharded chromium tests
playwright-tests-chromium-sharded: playwright-tests-chromium-sharded:
needs: setup needs: setup
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60
container:
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.13
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
permissions: permissions:
contents: read contents: read
packages: read
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8] shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
shardTotal: [8] shardTotal: [8]
steps: steps:
- name: Checkout repository # download built frontend repo from setup job
uses: actions/checkout@v6 - name: Wait for cache propagation
- name: Download built frontend run: sleep 10
uses: actions/download-artifact@v7 - name: Restore cached setup
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with: with:
name: frontend-dist fail-on-cache-miss: true
path: dist/ path: .
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Start ComfyUI server # Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
uses: ./.github/actions/start-comfyui-server - 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 sharded tests and upload sharded reports
run: pnpm install --frozen-lockfile
# Run sharded tests (browsers pre-installed in container)
- name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}) - name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
id: playwright id: playwright
run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
@@ -74,7 +81,7 @@ jobs:
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
- name: Upload blob report - name: Upload blob report
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
with: with:
name: blob-report-chromium-${{ matrix.shardIndex }} name: blob-report-chromium-${{ matrix.shardIndex }}
@@ -86,70 +93,69 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
needs: setup needs: setup
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.13
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
permissions: permissions:
contents: read contents: read
packages: read
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
browser: [chromium-2x, chromium-0.5x, mobile-chrome] browser: [chromium-2x, chromium-0.5x, mobile-chrome]
steps: steps:
- name: Checkout repository # download built frontend repo from setup job
uses: actions/checkout@v6 - name: Wait for cache propagation
- name: Download built frontend run: sleep 10
uses: actions/download-artifact@v7 - name: Restore cached setup
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with: with:
name: frontend-dist fail-on-cache-miss: true
path: dist/ path: .
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Start ComfyUI server # Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
uses: ./.github/actions/start-comfyui-server - 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 tests and upload reports
run: pnpm install --frozen-lockfile
# Run tests (browsers pre-installed in container)
- name: Run Playwright tests (${{ matrix.browser }}) - name: Run Playwright tests (${{ matrix.browser }})
id: playwright 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: | run: |
# Generate HTML report from blob # Run tests with both HTML and JSON reporters
pnpm exec playwright merge-reports --reporter=html ./blob-report
# Generate JSON report separately with explicit output path
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \ 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 - name: Upload Playwright report
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
if: always() if: always()
with: with:
name: playwright-report-${{ matrix.browser }} name: playwright-report-${{ matrix.browser }}
path: ./playwright-report/ path: ./playwright-report/
retention-days: 30 retention-days: 30
# Merge sharded test reports (no container needed - only runs CLI) # Merge sharded test reports
merge-reports: merge-reports:
needs: [playwright-tests-chromium-sharded] needs: [playwright-tests-chromium-sharded]
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
steps: steps:
- name: Install pnpm - name: Checkout repository
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: actions/checkout@v5
with:
version: 10 # 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 - name: Download blob reports
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
path: ./all-blob-reports path: ./all-blob-reports
pattern: blob-report-chromium-* pattern: blob-report-chromium-*
@@ -158,13 +164,13 @@ jobs:
- name: Merge into HTML Report - name: Merge into HTML Report
run: | run: |
# Generate HTML report # 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 # Generate JSON report separately with explicit output path
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \ 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 - name: Upload HTML report
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
with: with:
name: playwright-report-chromium name: playwright-report-chromium
path: ./playwright-report/ path: ./playwright-report/
@@ -182,7 +188,11 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Get start time
id: start-time
run: echo "time=$(date -u '+%m/%d/%Y, %I:%M:%S %p')" >> $GITHUB_OUTPUT
- name: Post starting comment - name: Post starting comment
env: env:
@@ -192,7 +202,8 @@ jobs:
./scripts/cicd/pr-playwright-deploy-and-comment.sh \ ./scripts/cicd/pr-playwright-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \ "${{ github.event.pull_request.number }}" \
"${{ github.head_ref }}" \ "${{ github.head_ref }}" \
"starting" "starting" \
"${{ steps.start-time.outputs.time }}"
# Deploy and comment for non-forked PRs only # Deploy and comment for non-forked PRs only
deploy-and-comment: deploy-and-comment:
@@ -204,10 +215,10 @@ jobs:
contents: read contents: read
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Download all playwright reports - name: Download all playwright reports
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
pattern: playwright-report-* pattern: playwright-report-*
path: reports path: reports
@@ -217,7 +228,6 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
GITHUB_SHA: ${{ github.event.pull_request.head.sha }}
run: | run: |
bash ./scripts/cicd/pr-playwright-deploy-and-comment.sh \ bash ./scripts/cicd/pr-playwright-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \ "${{ github.event.pull_request.number }}" \

View File

@@ -1,11 +1,14 @@
# Description: Deploys Storybook previews from forked PRs (forks can't access deployment secrets) name: "CI: Tests Storybook (Deploy for Forks)"
name: 'CI: Tests Storybook (Deploy for Forks)' description: "Deploys Storybook previews from forked PRs (forks can't access deployment secrets)"
on: on:
workflow_run: workflow_run:
workflows: ['CI: Tests Storybook'] workflows: ["CI: Tests Storybook"]
types: [requested, completed] types: [requested, completed]
env:
DATE_FORMAT: '+%m/%d/%Y, %I:%M:%S %p'
jobs: jobs:
deploy-and-comment-forked-pr: deploy-and-comment-forked-pr:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -28,11 +31,11 @@ jobs:
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}" echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Get PR Number - name: Get PR Number
id: pr id: pr
uses: actions/github-script@v8 uses: actions/github-script@v7
with: with:
script: | script: |
const { data: prs } = await github.rest.pulls.list({ const { data: prs } = await github.rest.pulls.list({
@@ -60,11 +63,12 @@ jobs:
./scripts/cicd/pr-storybook-deploy-and-comment.sh \ ./scripts/cicd/pr-storybook-deploy-and-comment.sh \
"${{ steps.pr.outputs.result }}" \ "${{ steps.pr.outputs.result }}" \
"${{ github.event.workflow_run.head_branch }}" \ "${{ github.event.workflow_run.head_branch }}" \
"starting" "starting" \
"$(date -u '${{ env.DATE_FORMAT }}')"
- name: Download and Deploy Storybook - name: Download and Deploy Storybook
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' && github.event.workflow_run.conclusion == 'success' if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' && github.event.workflow_run.conclusion == 'success'
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }} run-id: ${{ github.event.workflow_run.id }}

View File

@@ -1,10 +1,9 @@
# Description: Builds Storybook and runs visual regression testing via Chromatic, deploys previews to Cloudflare Pages name: "CI: Tests Storybook"
name: 'CI: Tests Storybook' description: "Builds Storybook and runs visual regression testing via Chromatic, deploys previews to Cloudflare Pages"
on: on:
workflow_dispatch: # Allow manual triggering workflow_dispatch: # Allow manual triggering
pull_request: pull_request:
push:
branches: [main] branches: [main]
jobs: jobs:
@@ -16,7 +15,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Post starting comment - name: Post starting comment
env: env:
@@ -26,7 +25,8 @@ jobs:
./scripts/cicd/pr-storybook-deploy-and-comment.sh \ ./scripts/cicd/pr-storybook-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \ "${{ github.event.pull_request.number }}" \
"${{ github.head_ref }}" \ "${{ github.head_ref }}" \
"starting" "starting" \
"$(date -u '+%m/%d/%Y, %I:%M:%S %p')"
# Build Storybook for all PRs (free Cloudflare deployment) # Build Storybook for all PRs (free Cloudflare deployment)
storybook-build: storybook-build:
@@ -37,10 +37,21 @@ jobs:
workflow-url: ${{ steps.workflow-url.outputs.url }} workflow-url: ${{ steps.workflow-url.outputs.url }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Setup frontend - name: Install pnpm
uses: ./.github/actions/setup-frontend uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Storybook - name: Build Storybook
run: pnpm build-storybook run: pnpm build-storybook
@@ -59,7 +70,7 @@ jobs:
- name: Upload Storybook build - name: Upload Storybook build
if: success() && github.event.pull_request.head.repo.fork == false if: success() && github.event.pull_request.head.repo.fork == false
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
with: with:
name: storybook-static name: storybook-static
path: storybook-static/ path: storybook-static/
@@ -76,22 +87,33 @@ jobs:
chromatic-storybook-url: ${{ steps.chromatic.outputs.storybookUrl }} chromatic-storybook-url: ${{ steps.chromatic.outputs.storybookUrl }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # Required for Chromatic baseline fetch-depth: 0 # Required for Chromatic baseline
- name: Setup frontend - name: Install pnpm
uses: ./.github/actions/setup-frontend uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Storybook and run Chromatic - name: Build Storybook and run Chromatic
id: chromatic id: chromatic
uses: chromaui/action@07791f8243f4cb2698bf4d00426baf4b2d1cb7e0 # v13.3.5 uses: chromaui/action@latest
with: with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
buildScriptName: build-storybook buildScriptName: build-storybook
autoAcceptChanges: 'main' # Auto-accept changes on main branch autoAcceptChanges: 'main' # Auto-accept changes on main branch
exitOnceUploaded: true # Don't wait for UI tests to complete exitOnceUploaded: true # Don't wait for UI tests to complete
onlyChanged: true # Only capture changed stories onlyChanged: true # Only capture changed stories
- name: Set job status - name: Set job status
id: job-status id: job-status
@@ -115,11 +137,11 @@ jobs:
contents: read contents: read
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Download Storybook build - name: Download Storybook build
if: needs.storybook-build.outputs.conclusion == 'success' if: needs.storybook-build.outputs.conclusion == 'success'
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
name: storybook-static name: storybook-static
path: storybook-static path: storybook-static
@@ -140,29 +162,6 @@ jobs:
"${{ github.head_ref }}" \ "${{ github.head_ref }}" \
"completed" "completed"
# Deploy Storybook to production URL on main branch push
deploy-production:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup frontend
uses: ./.github/actions/setup-frontend
- name: Build Storybook
run: pnpm build-storybook
- name: Deploy to Cloudflare Pages (production)
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
npx wrangler@^4.0.0 pages deploy storybook-static \
--project-name=comfy-storybook \
--branch=main
# Update comment with Chromatic URLs for version-bump branches # Update comment with Chromatic URLs for version-bump branches
update-comment-with-chromatic: update-comment-with-chromatic:
needs: [chromatic-deployment, deploy-and-comment] needs: [chromatic-deployment, deploy-and-comment]
@@ -172,7 +171,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Update comment with Chromatic URLs - name: Update comment with Chromatic URLs
uses: actions/github-script@v8 uses: actions/github-script@v7
with: with:
script: | script: |
const buildUrl = '${{ needs.chromatic-deployment.outputs.chromatic-build-url }}'; const buildUrl = '${{ needs.chromatic-deployment.outputs.chromatic-build-url }}';

View File

@@ -1,13 +1,11 @@
# Description: Unit and component testing with Vitest name: "CI: Tests Unit"
name: 'CI: Tests Unit' description: "Unit and component testing with Vitest"
on: on:
push: push:
branches: [main, master, dev*, core/*, desktop/*] branches: [main, master, dev*, core/*, desktop/*]
paths-ignore: ['**/*.md']
pull_request: pull_request:
branches-ignore: [wip/*, draft/*, temp/*] branches-ignore: [wip/*, draft/*, temp/*]
paths-ignore: ['**/*.md']
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
@@ -18,10 +16,21 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- name: Setup frontend - name: Install pnpm
uses: ./.github/actions/setup-frontend uses: pnpm/action-setup@v4
with:
version: 10
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Vitest tests - name: Run Vitest tests
run: pnpm test:unit run: pnpm test:unit

View File

@@ -1,21 +0,0 @@
name: Validate Action SHA Pins
on:
pull_request:
paths:
- '.github/workflows/**'
- '.github/actions/**'
- '.pinact.yaml'
permissions:
contents: read
jobs:
validate-pins:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: suzuki-shunsuke/pinact-action@3d49c6412901042473ffa78becddab1aea46bbea # v1.3.1
with:
skip_push: 'true'

View File

@@ -1,5 +1,5 @@
# Description: Validates YAML syntax and style using yamllint with relaxed rules name: "CI: YAML Validation"
name: 'CI: YAML Validation' description: "Validates YAML syntax and style using yamllint with relaxed rules"
on: on:
push: push:
@@ -17,10 +17,10 @@ jobs:
yaml-lint: yaml-lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v5
with: with:
python-version: '3.x' python-version: '3.x'

View File

@@ -1,69 +0,0 @@
---
name: Cloud Backport Tag
on:
pull_request:
types: ['closed']
branches: [cloud/*]
jobs:
create-tag:
if: >
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'backport')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- name: Checkout merge commit
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
- name: Create tag for cloud backport
id: tag
run: |
set -euo pipefail
BRANCH="${{ github.event.pull_request.base.ref }}"
if [[ ! "$BRANCH" =~ ^cloud/([0-9]+)\.([0-9]+)$ ]]; then
echo "::error::Base branch '$BRANCH' is not a cloud/x.y branch"
exit 1
fi
MAJOR="${BASH_REMATCH[1]}"
MINOR="${BASH_REMATCH[2]}"
VERSION=$(node -p "require('./package.json').version")
if [[ "$VERSION" =~ ^${MAJOR}\.${MINOR}\.([0-9]+)(-.+)?$ ]]; then
PATCH="${BASH_REMATCH[1]}"
SUFFIX="${BASH_REMATCH[2]:-}"
else
echo "::error::Version '${VERSION}' does not match cloud branch '${BRANCH}'"
exit 1
fi
TAG="cloud/v${VERSION}"
if git ls-remote --tags origin "${TAG}" | grep -Fq "refs/tags/${TAG}"; then
echo "::notice::Tag ${TAG} already exists; skipping"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
exit 0
fi
git tag "${TAG}" "${{ github.event.pull_request.merge_commit_sha }}"
git push origin "${TAG}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
{
echo "Created tag: ${TAG}"
echo "Branch: ${BRANCH}"
echo "Version: ${VERSION}"
echo "Commit: ${{ github.event.pull_request.merge_commit_sha }}"
} >> "$GITHUB_STEP_SUMMARY"

View File

@@ -1,81 +0,0 @@
---
# Dispatches a frontend-asset-build event to the cloud repo on push to
# cloud/* branches and main. The cloud repo handles the actual build,
# GCS upload, and secret management (Sentry, Algolia, GCS creds).
#
# This is fire-and-forget — it does NOT wait for the cloud workflow to
# complete. Status is visible in the cloud repo's Actions tab.
name: Cloud Frontend Build Dispatch
on:
push:
branches:
- 'cloud/*'
- 'main'
pull_request:
types: [labeled, synchronize]
workflow_dispatch:
permissions: {}
concurrency:
group: cloud-dispatch-${{ github.ref }}
cancel-in-progress: true
jobs:
dispatch:
# Fork guard: prevent forks from dispatching to the cloud repo.
# For pull_request events, only dispatch for preview labels.
# - labeled: fires when a label is added; check the added label name.
# - synchronize: fires on push; check existing labels on the PR.
if: >
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
(github.event_name != 'pull_request' ||
(github.event.action == 'labeled' &&
contains(fromJSON('["preview","preview-cpu","preview-gpu"]'), github.event.label.name)) ||
(github.event.action == 'synchronize' &&
(contains(github.event.pull_request.labels.*.name, 'preview') ||
contains(github.event.pull_request.labels.*.name, 'preview-cpu') ||
contains(github.event.pull_request.labels.*.name, 'preview-gpu'))))
runs-on: ubuntu-latest
steps:
- name: Build client payload
id: payload
env:
EVENT_NAME: ${{ github.event_name }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
PR_NUMBER: ${{ github.event.pull_request.number }}
ACTION: ${{ github.event.action }}
LABEL_NAME: ${{ github.event.label.name }}
PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
run: |
if [ "${EVENT_NAME}" = "pull_request" ]; then
REF="${PR_HEAD_SHA}"
BRANCH="${PR_HEAD_REF}"
# Derive variant from all PR labels (default to cpu for frontend-only previews)
VARIANT="cpu"
echo "${PR_LABELS}" | grep -q '"preview-gpu"' && VARIANT="gpu"
else
REF="${GITHUB_SHA}"
BRANCH="${GITHUB_REF_NAME}"
PR_NUMBER=""
VARIANT=""
fi
payload="$(jq -nc \
--arg ref "${REF}" \
--arg branch "${BRANCH}" \
--arg pr_number "${PR_NUMBER}" \
--arg variant "${VARIANT}" \
'{ref: $ref, branch: $branch, pr_number: $pr_number, variant: $variant}')"
echo "json=${payload}" >> "${GITHUB_OUTPUT}"
- name: Dispatch to cloud repo
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
with:
token: ${{ secrets.CLOUD_DISPATCH_TOKEN }}
repository: Comfy-Org/cloud
event-type: frontend-asset-build
client-payload: ${{ steps.payload.outputs.json }}

View File

@@ -1,39 +0,0 @@
---
# Dispatches a frontend-preview-cleanup event to the cloud repo when a
# frontend PR with a preview label is closed or has its preview label
# removed. The cloud repo handles the actual environment teardown.
#
# This is fire-and-forget — it does NOT wait for the cloud workflow to
# complete. Status is visible in the cloud repo's Actions tab.
name: Cloud Frontend Preview Cleanup Dispatch
on:
pull_request:
types: [closed, unlabeled]
permissions: {}
jobs:
dispatch:
# Only dispatch when:
# - PR closed AND had a preview label
# - Preview label specifically removed
if: >
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
((github.event.action == 'closed' &&
(contains(github.event.pull_request.labels.*.name, 'preview') ||
contains(github.event.pull_request.labels.*.name, 'preview-cpu') ||
contains(github.event.pull_request.labels.*.name, 'preview-gpu'))) ||
(github.event.action == 'unlabeled' &&
contains(fromJSON('["preview","preview-cpu","preview-gpu"]'), github.event.label.name)))
runs-on: ubuntu-latest
steps:
- name: Dispatch to cloud repo
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
with:
token: ${{ secrets.CLOUD_DISPATCH_TOKEN }}
repository: Comfy-Org/cloud
event-type: frontend-preview-cleanup
client-payload: >-
{"pr_number": "${{ github.event.pull_request.number }}"}

View File

@@ -1,5 +1,5 @@
# Description: Generates and updates translations for core ComfyUI components using OpenAI name: "i18n: Update Core"
name: 'i18n: Update Core' description: "Generates and updates translations for core ComfyUI components using OpenAI"
on: on:
# Manual dispatch for urgent translation updates # Manual dispatch for urgent translation updates
@@ -16,9 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with:
token: ${{ secrets.PR_GH_TOKEN }}
# Setup playwright environment # Setup playwright environment
- name: Setup ComfyUI Frontend - name: Setup ComfyUI Frontend
@@ -43,7 +41,7 @@ jobs:
env: env:
PLAYWRIGHT_TEST_URL: http://localhost:5173 PLAYWRIGHT_TEST_URL: http://localhost:5173
- name: Update translations - name: Update translations
run: pnpm locale && pnpm format run: pnpm locale
env: env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Commit updated locales - name: Commit updated locales

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
# Setup playwright environment with custom node repository # Setup playwright environment with custom node repository
- name: Setup ComfyUI Server (without launching) - name: Setup ComfyUI Server (without launching)
@@ -36,7 +36,7 @@ jobs:
# Install the custom node repository # Install the custom node repository
- name: Checkout custom node repository - name: Checkout custom node repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
repository: ${{ inputs.owner }}/${{ inputs.repository }} repository: ${{ inputs.owner }}/${{ inputs.repository }}
path: 'ComfyUI/custom_nodes/${{ inputs.repository }}' path: 'ComfyUI/custom_nodes/${{ inputs.repository }}'
@@ -113,7 +113,7 @@ jobs:
git commit -m "Update locales" git commit -m "Update locales"
- name: Install SSH key For PUSH - name: Install SSH key For PUSH
uses: shimataro/ssh-key-action@d4fffb50872869abe2d9a9098a6d9c5aa7d16be4 # v2.7.0 uses: shimataro/ssh-key-action@d4fffb50872869abe2d9a9098a6d9c5aa7d16be4
with: with:
# PR private key from action server # PR private key from action server
key: ${{ secrets.PR_SSH_PRIVATE_KEY }} key: ${{ secrets.PR_SSH_PRIVATE_KEY }}

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
# Setup playwright environment # Setup playwright environment
- name: Setup ComfyUI Server (and start) - name: Setup ComfyUI Server (and start)
uses: ./.github/actions/setup-comfyui-server uses: ./.github/actions/setup-comfyui-server
@@ -40,11 +40,11 @@ jobs:
env: env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with: with:
token: ${{ secrets.PR_GH_TOKEN }} token: ${{ secrets.PR_GH_TOKEN }}
commit-message: 'Update locales for node definitions' commit-message: "Update locales for node definitions"
title: 'Update locales for node definitions' title: "Update locales for node definitions"
body: | body: |
Automated PR to update locales for node definitions Automated PR to update locales for node definitions

View File

@@ -16,10 +16,6 @@ on:
type: boolean type: boolean
default: false default: false
concurrency:
group: backport-${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
cancel-in-progress: false
jobs: jobs:
backport: backport:
if: > if: >
@@ -64,7 +60,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -82,7 +78,8 @@ jobs:
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name') LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
else else
LABELS=$(jq -r '.pull_request.labels[].name' "$GITHUB_EVENT_PATH") LABELS='${{ toJSON(github.event.pull_request.labels) }}'
LABELS=$(echo "$LABELS" | jq -r '.[].name')
fi fi
add_target() { add_target() {
@@ -240,8 +237,8 @@ jobs:
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title') PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid') MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
else else
PR_TITLE=$(jq -r '.pull_request.title' "$GITHUB_EVENT_PATH") PR_TITLE="${{ github.event.pull_request.title }}"
MERGE_COMMIT=$(jq -r '.pull_request.merge_commit_sha' "$GITHUB_EVENT_PATH") MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
fi fi
for target in ${{ steps.filter-targets.outputs.pending-targets }}; do for target in ${{ steps.filter-targets.outputs.pending-targets }}; do
@@ -330,8 +327,8 @@ jobs:
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title') PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login') PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
else else
PR_TITLE=$(jq -r '.pull_request.title' "$GITHUB_EVENT_PATH") PR_TITLE="${{ github.event.pull_request.title }}"
PR_AUTHOR=$(jq -r '.pull_request.user.login' "$GITHUB_EVENT_PATH") PR_AUTHOR="${{ github.event.pull_request.user.login }}"
fi fi
for backport in ${{ steps.backport.outputs.success }}; do for backport in ${{ steps.backport.outputs.success }}; do
@@ -361,42 +358,6 @@ jobs:
if: steps.filter-targets.outputs.skip != 'true' && failure() && steps.backport.outputs.failed if: steps.filter-targets.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
BACKPORT_AGENT_PROMPT_TEMPLATE: |
Backport PR #${PR_NUMBER} (${PR_URL}) to ${target}.
Cherry-pick merge commit ${MERGE_COMMIT} onto new branch
${BACKPORT_BRANCH} from origin/${target}.
Resolve conflicts in: ${CONFLICTS_INLINE}.
For test snapshots (browser_tests/**/*-snapshots/), accept PR version if
changed in original PR, else keep target. For package.json versions, keep
target branch. For pnpm-lock.yaml, regenerate with pnpm install.
Ask user for non-obvious conflicts.
Create PR titled "[backport ${target}] <original title>" with label "backport".
See .github/workflows/pr-backport.yaml for workflow details.
COMMENT_BODY_TEMPLATE: |
### ⚠️ Backport to `${target}` failed
**Reason:** Merge conflicts detected during cherry-pick of `${MERGE_COMMIT_SHORT}`
<details>
<summary>📄 Conflicting files</summary>
```
${CONFLICTS_BLOCK}
```
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
```
${AGENT_PROMPT}
```
</details>
---
cc @${PR_AUTHOR}
run: | run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json author,mergeCommit) PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json author,mergeCommit)
@@ -404,9 +365,9 @@ jobs:
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login') PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid') MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
else else
PR_NUMBER=$(jq -r '.pull_request.number' "$GITHUB_EVENT_PATH") PR_NUMBER="${{ github.event.pull_request.number }}"
PR_AUTHOR=$(jq -r '.pull_request.user.login' "$GITHUB_EVENT_PATH") PR_AUTHOR="${{ github.event.pull_request.user.login }}"
MERGE_COMMIT=$(jq -r '.pull_request.merge_commit_sha' "$GITHUB_EVENT_PATH") MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
fi fi
for failure in ${{ steps.backport.outputs.failed }}; do for failure in ${{ steps.backport.outputs.failed }}; do
@@ -419,27 +380,10 @@ jobs:
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Commit \`${MERGE_COMMIT}\` already exists on branch \`${target}\`. No backport needed." gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Commit \`${MERGE_COMMIT}\` already exists on branch \`${target}\`. No backport needed."
elif [ "${reason}" = "conflicts" ]; then elif [ "${reason}" = "conflicts" ]; then
CONFLICTS_INLINE=$(echo "${conflicts}" | tr ',' ' ') # Convert comma-separated conflicts back to newlines for display
SAFE_TARGET=$(echo "$target" | tr '/' '-') CONFLICTS_LIST=$(echo "${conflicts}" | tr ',' '\n' | sed 's/^/- /')
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
PR_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}"
export PR_NUMBER PR_URL MERGE_COMMIT target BACKPORT_BRANCH CONFLICTS_INLINE
# envsubst is provided by gettext-base
if ! command -v envsubst >/dev/null 2>&1; then
sudo apt-get update && sudo apt-get install -y gettext-base
fi
AGENT_PROMPT=$(envsubst '${PR_NUMBER} ${PR_URL} ${target} ${MERGE_COMMIT} ${BACKPORT_BRANCH} ${CONFLICTS_INLINE}' <<<"$BACKPORT_AGENT_PROMPT_TEMPLATE")
# Use fenced code block for conflicts to handle special chars in filenames
CONFLICTS_BLOCK=$(echo "${conflicts}" | tr ',' '\n')
MERGE_COMMIT_SHORT="${MERGE_COMMIT:0:7}"
export target MERGE_COMMIT_SHORT CONFLICTS_BLOCK AGENT_PROMPT PR_AUTHOR
COMMENT_BODY=$(envsubst '${target} ${MERGE_COMMIT_SHORT} ${CONFLICTS_BLOCK} ${AGENT_PROMPT} ${PR_AUTHOR}' <<<"$COMMENT_BODY_TEMPLATE")
COMMENT_BODY="@${PR_AUTHOR} Backport to \`${target}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`${target}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}"
fi fi
done done
@@ -462,6 +406,7 @@ jobs:
fi fi
done < "$FILE" done < "$FILE"
- name: Remove needs-backport label - name: Remove needs-backport label
if: steps.filter-targets.outputs.skip != 'true' && success() if: steps.filter-targets.outputs.skip != 'true' && success()
run: gh pr edit ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} --remove-label "needs-backport" run: gh pr edit ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} --remove-label "needs-backport"

View File

@@ -1,5 +1,5 @@
# Description: AI-powered code review triggered by adding the 'claude-review' label to a PR name: "PR: Claude Review"
name: 'PR: Claude Review' description: "AI-powered code review triggered by adding the 'claude-review' label to a PR"
permissions: permissions:
contents: read contents: read
@@ -23,20 +23,20 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
ref: refs/pull/${{ github.event.pull_request.number }}/head ref: refs/pull/${{ github.event.pull_request.number }}/head
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: '20'
cache: 'pnpm' cache: 'pnpm'
- name: Install dependencies for analysis tools - name: Install dependencies for analysis tools
@@ -44,9 +44,9 @@ jobs:
pnpm install -g typescript @vue/compiler-sfc pnpm install -g typescript @vue/compiler-sfc
- name: Run Claude PR Review - name: Run Claude PR Review
uses: anthropics/claude-code-action@ff34ce0ff04a470bd3fa56c1ef391c8f1c19f8e9 # v1.0.38 uses: anthropics/claude-code-action@v1.0.6
with: with:
label_trigger: 'claude-review' label_trigger: "claude-review"
prompt: | prompt: |
Read the file .claude/commands/comprehensive-pr-review.md and follow ALL the instructions exactly. Read the file .claude/commands/comprehensive-pr-review.md and follow ALL the instructions exactly.

View File

@@ -1,102 +0,0 @@
name: 'PR: Performance Report'
on:
workflow_run:
workflows: ['CI: Performance Report']
types:
- completed
permissions:
contents: read
pull-requests: write
issues: write
jobs:
comment:
runs-on: ubuntu-latest
if: >
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
- name: Download PR metadata
uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 # v12
with:
name: perf-meta
run_id: ${{ github.event.workflow_run.id }}
path: temp/perf-meta/
- name: Resolve and validate PR metadata
id: pr-meta
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const artifactPr = Number(fs.readFileSync('temp/perf-meta/number.txt', 'utf8').trim());
const artifactBase = fs.readFileSync('temp/perf-meta/base.txt', 'utf8').trim();
// Resolve PR from trusted workflow context
let pr = context.payload.workflow_run.pull_requests?.[0];
if (!pr) {
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.payload.workflow_run.head_sha,
});
pr = prs.find(p => p.state === 'open');
}
if (!pr) {
core.setFailed('Unable to resolve PR from workflow_run context.');
return;
}
if (Number(pr.number) !== artifactPr) {
core.setFailed(`Artifact PR number (${artifactPr}) does not match trusted context (${pr.number}).`);
return;
}
const trustedBase = pr.base?.ref;
if (!trustedBase || artifactBase !== trustedBase) {
core.setFailed(`Artifact base (${artifactBase}) does not match trusted context (${trustedBase}).`);
return;
}
core.setOutput('number', String(pr.number));
core.setOutput('base', trustedBase);
- name: Download PR perf metrics
uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 # v12
with:
name: perf-metrics
run_id: ${{ github.event.workflow_run.id }}
path: test-results/
- name: Download baseline perf metrics
uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 # v12
with:
branch: ${{ steps.pr-meta.outputs.base }}
workflow: ci-perf-report.yaml
event: push
name: perf-metrics
path: temp/perf-baseline/
if_no_artifact_found: warn
- name: Generate perf report
run: npx --yes tsx scripts/perf-report.ts > perf-report.md
- name: Post PR comment
uses: ./.github/actions/post-pr-report-comment
with:
pr-number: ${{ steps.pr-meta.outputs.number }}
report-file: ./perf-report.md
comment-marker: '<!-- COMFYUI_FRONTEND_PERF -->'
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,4 +1,4 @@
name: 'PR: Size Report' name: "PR: Size Report"
on: on:
workflow_run: workflow_run:
@@ -33,88 +33,51 @@ jobs:
github.event_name == 'workflow_dispatch' github.event_name == 'workflow_dispatch'
) )
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- name: Setup frontend - name: Install pnpm
uses: ./.github/actions/setup-frontend uses: pnpm/action-setup@v4.1.0
with:
version: 10
- name: Install Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Download size data - name: Download size data
uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 # v12 uses: dawidd6/action-download-artifact@v11
with: with:
name: size-data name: size-data
run_id: ${{ github.event_name == 'workflow_dispatch' && inputs.run_id || github.event.workflow_run.id }} run_id: ${{ github.event_name == 'workflow_dispatch' && inputs.run_id || github.event.workflow_run.id }}
path: temp/size path: temp/size
- name: Resolve and validate PR metadata - name: Set PR number
id: pr-meta id: pr-number
uses: actions/github-script@v8 run: |
with: if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
script: | echo "content=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT
const fs = require('fs'); else
echo "content=$(cat temp/size/number.txt)" >> $GITHUB_OUTPUT
fi
// workflow_dispatch: validate artifact metadata against API-resolved PR - name: Set base branch
if (context.eventName === 'workflow_dispatch') { id: pr-base
const pullNumber = Number('${{ inputs.pr_number }}'); run: |
const { data: dispatchPr } = await github.rest.pulls.get({ if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
owner: context.repo.owner, echo "content=main" >> $GITHUB_OUTPUT
repo: context.repo.repo, else
pull_number: pullNumber, echo "content=$(cat temp/size/base.txt)" >> $GITHUB_OUTPUT
}); fi
const artifactPr = Number(fs.readFileSync('temp/size/number.txt', 'utf8').trim());
const artifactBase = fs.readFileSync('temp/size/base.txt', 'utf8').trim();
if (artifactPr !== dispatchPr.number) {
core.setFailed(`Artifact PR number (${artifactPr}) does not match dispatch PR (${dispatchPr.number}).`);
return;
}
if (artifactBase !== dispatchPr.base.ref) {
core.setFailed(`Artifact base (${artifactBase}) does not match dispatch PR base (${dispatchPr.base.ref}).`);
return;
}
core.setOutput('number', String(dispatchPr.number));
core.setOutput('base', dispatchPr.base.ref);
return;
}
// workflow_run: validate artifact metadata against trusted context
const artifactPr = Number(fs.readFileSync('temp/size/number.txt', 'utf8').trim());
const artifactBase = fs.readFileSync('temp/size/base.txt', 'utf8').trim();
let pr = context.payload.workflow_run.pull_requests?.[0];
if (!pr) {
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.payload.workflow_run.head_sha,
});
pr = prs.find(p => p.state === 'open');
}
if (!pr) {
core.setFailed('Unable to resolve PR from workflow_run context.');
return;
}
if (Number(pr.number) !== artifactPr) {
core.setFailed(`Artifact PR number (${artifactPr}) does not match trusted context (${pr.number}).`);
return;
}
const trustedBase = pr.base?.ref;
if (!trustedBase || artifactBase !== trustedBase) {
core.setFailed(`Artifact base (${artifactBase}) does not match trusted context (${trustedBase}).`);
return;
}
core.setOutput('number', String(pr.number));
core.setOutput('base', trustedBase);
- name: Download previous size data - name: Download previous size data
uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 # v12 uses: dawidd6/action-download-artifact@v11
with: with:
branch: ${{ steps.pr-meta.outputs.base }} branch: ${{ steps.pr-base.outputs.content }}
workflow: ci-size-data.yaml workflow: ci-size-data.yaml
event: push event: push
name: size-data name: size-data
@@ -124,10 +87,18 @@ jobs:
- name: Generate size report - name: Generate size report
run: node scripts/size-report.js > size-report.md run: node scripts/size-report.js > size-report.md
- name: Post PR comment - name: Read size report
uses: ./.github/actions/post-pr-report-comment id: size-report
uses: juliangruber/read-file-action@v1
with:
path: ./size-report.md
- name: Create or update PR comment
uses: actions-cool/maintain-one-comment@v3
with: with:
pr-number: ${{ steps.pr-meta.outputs.number }}
report-file: ./size-report.md
comment-marker: '<!-- COMFYUI_FRONTEND_SIZE -->'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ steps.pr-number.outputs.content }}
body: |
${{ steps.size-report.outputs.content }}
<!-- COMFYUI_FRONTEND_SIZE -->
body-include: '<!-- COMFYUI_FRONTEND_SIZE -->'

View File

@@ -1,5 +1,5 @@
# Setting test expectation screenshots for Playwright # Setting test expectation screenshots for Playwright
name: 'PR: Update Playwright Expectations' name: "PR: Update Playwright Expectations"
on: on:
pull_request: pull_request:
@@ -25,6 +25,7 @@ jobs:
) && ) &&
startsWith(github.event.comment.body, '/update-playwright') ) startsWith(github.event.comment.body, '/update-playwright') )
outputs: outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
pr-number: ${{ steps.pr-info.outputs.pr-number }} pr-number: ${{ steps.pr-info.outputs.pr-number }}
branch: ${{ steps.pr-info.outputs.branch }} branch: ${{ steps.pr-info.outputs.branch }}
comment-id: ${{ steps.find-update-comment.outputs.comment-id }} comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
@@ -38,15 +39,15 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Find Update Comment - name: Find Update Comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0 uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
id: 'find-update-comment' id: "find-update-comment"
with: with:
issue-number: ${{ steps.pr-info.outputs.pr-number }} issue-number: ${{ steps.pr-info.outputs.pr-number }}
comment-author: 'github-actions[bot]' comment-author: "github-actions[bot]"
body-includes: 'Updating Playwright Expectations' body-includes: "Updating Playwright Expectations"
- name: Add Starting Reaction - name: Add Starting Reaction
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
with: with:
comment-id: ${{ steps.find-update-comment.outputs.comment-id }} comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
issue-number: ${{ steps.pr-info.outputs.pr-number }} issue-number: ${{ steps.pr-info.outputs.pr-number }}
@@ -56,100 +57,116 @@ jobs:
reactions: eyes reactions: eyes
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ steps.pr-info.outputs.branch }} ref: ${{ steps.pr-info.outputs.branch }}
- name: Setup frontend - name: Setup frontend
uses: ./.github/actions/setup-frontend uses: ./.github/actions/setup-frontend
with: with:
include_build_step: true include_build_step: true
# Save expensive build artifacts (Python env, built frontend, node_modules)
# Upload built dist/ (containerized test jobs will pnpm install without cache) # Source code will be checked out fresh in sharded jobs
- name: Upload built frontend - name: Generate cache key
uses: actions/upload-artifact@v6 id: cache-key
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
- name: Save cache
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
with: with:
name: frontend-dist path: |
path: dist/ ComfyUI
retention-days: 1 dist
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
# Sharded snapshot updates # Sharded snapshot updates
update-snapshots-sharded: update-snapshots-sharded:
needs: setup needs: setup
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.13
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: read
packages: read
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
shardIndex: [1, 2, 3, 4] shardIndex: [1, 2, 3, 4]
shardTotal: [4] shardTotal: [4]
steps: steps:
# Checkout source code fresh (not cached)
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ needs.setup.outputs.branch }} ref: ${{ needs.setup.outputs.branch }}
- name: Download built frontend
uses: actions/download-artifact@v7 # Restore expensive build artifacts from setup job
- name: Restore cached artifacts
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with: with:
name: frontend-dist fail-on-cache-miss: true
path: dist/ path: |
ComfyUI
dist
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Start ComfyUI server - name: Setup ComfyUI server (from cache)
uses: ./.github/actions/start-comfyui-server uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Install frontend deps - name: Setup nodejs, pnpm, reuse built frontend
run: pnpm install --frozen-lockfile uses: ./.github/actions/setup-frontend
# Run sharded tests with snapshot updates (browsers pre-installed in container) - name: Setup Playwright
uses: ./.github/actions/setup-playwright
# Run sharded tests with snapshot updates
- name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}) - name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
id: playwright-tests id: playwright-tests
run: pnpm exec playwright test --update-snapshots --grep @screenshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
continue-on-error: true continue-on-error: true
# Identify and stage only changed snapshot files
- name: Stage changed snapshot files - name: Stage changed snapshot files
id: changed-snapshots id: changed-snapshots
shell: bash
run: | run: |
set -euo pipefail echo "=========================================="
echo "STAGING CHANGED SNAPSHOTS (Shard ${{ matrix.shardIndex }})"
echo "=========================================="
# Configure git safe.directory for container environment # Get list of changed snapshot files
git config --global --add safe.directory "$(pwd)" changed_files=$(git diff --name-only browser_tests/ 2>/dev/null | grep -E '\-snapshots/' || echo "")
# Get list of changed snapshot files (including untracked/new files)
changed_files=$( (
git diff --name-only browser_tests/ 2>/dev/null || true
git ls-files --others --exclude-standard browser_tests/ 2>/dev/null || true
) | sort -u | grep -E '\-snapshots/' || true )
if [ -z "$changed_files" ]; then if [ -z "$changed_files" ]; then
echo "No snapshot changes in shard ${{ matrix.shardIndex }}" echo "No snapshot changes in this shard"
echo "has-changes=false" >> $GITHUB_OUTPUT echo "has-changes=false" >> $GITHUB_OUTPUT
exit 0 exit 0
fi fi
file_count=$(echo "$changed_files" | wc -l) echo "✓ Found changed files:"
echo "Shard ${{ matrix.shardIndex }}: $file_count changed snapshot(s):"
echo "$changed_files" echo "$changed_files"
file_count=$(echo "$changed_files" | wc -l)
echo "Count: $file_count"
echo "has-changes=true" >> $GITHUB_OUTPUT echo "has-changes=true" >> $GITHUB_OUTPUT
echo ""
# Copy changed files to staging directory # Create staging directory
mkdir -p /tmp/changed_snapshots_shard mkdir -p /tmp/changed_snapshots_shard
# Copy only changed files, preserving directory structure
# Strip 'browser_tests/' prefix to avoid double nesting
echo "Copying changed files to staging directory..."
while IFS= read -r file; do while IFS= read -r file; do
[ -f "$file" ] || continue # Remove 'browser_tests/' prefix
file_without_prefix="${file#browser_tests/}" file_without_prefix="${file#browser_tests/}"
# Create parent directories
mkdir -p "/tmp/changed_snapshots_shard/$(dirname "$file_without_prefix")" mkdir -p "/tmp/changed_snapshots_shard/$(dirname "$file_without_prefix")"
# Copy file
cp "$file" "/tmp/changed_snapshots_shard/$file_without_prefix" cp "$file" "/tmp/changed_snapshots_shard/$file_without_prefix"
echo " → $file_without_prefix"
done <<< "$changed_files" done <<< "$changed_files"
echo ""
echo "Staged files for upload:"
find /tmp/changed_snapshots_shard -type f
# Upload ONLY the changed files from this shard # Upload ONLY the changed files from this shard
- name: Upload changed snapshots - name: Upload changed snapshots
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
if: steps.changed-snapshots.outputs.has-changes == 'true' if: steps.changed-snapshots.outputs.has-changes == 'true'
with: with:
name: snapshots-shard-${{ matrix.shardIndex }} name: snapshots-shard-${{ matrix.shardIndex }}
@@ -157,7 +174,7 @@ jobs:
retention-days: 1 retention-days: 1
- name: Upload test report - name: Upload test report
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
if: always() if: always()
with: with:
name: playwright-report-shard-${{ matrix.shardIndex }} name: playwright-report-shard-${{ matrix.shardIndex }}
@@ -170,33 +187,26 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ needs.setup.outputs.branch }} ref: ${{ needs.setup.outputs.branch }}
token: ${{ secrets.PR_GH_TOKEN }}
# Download all changed snapshot files from shards # Download all changed snapshot files from shards
- name: Download snapshot artifacts - name: Download snapshot artifacts
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
pattern: snapshots-shard-* pattern: snapshots-shard-*
path: ./downloaded-snapshots path: ./downloaded-snapshots
merge-multiple: true merge-multiple: false
- name: List downloaded files - name: List downloaded files
run: | run: |
echo "==========================================" echo "=========================================="
echo "DOWNLOADED SNAPSHOT FILES" echo "DOWNLOADED SNAPSHOT FILES"
echo "==========================================" echo "=========================================="
if [ -d "./downloaded-snapshots" ]; then find ./downloaded-snapshots -type f
find ./downloaded-snapshots -type f echo ""
echo "" echo "Total files: $(find ./downloaded-snapshots -type f | wc -l)"
echo "Total files: $(find ./downloaded-snapshots -type f | wc -l)"
else
echo "No snapshot artifacts downloaded (no changes in any shard)"
echo ""
echo "Total files: 0"
fi
# Merge only the changed files into browser_tests # Merge only the changed files into browser_tests
- name: Merge changed snapshots - name: Merge changed snapshots
@@ -207,45 +217,43 @@ jobs:
echo "MERGING CHANGED SNAPSHOTS" echo "MERGING CHANGED SNAPSHOTS"
echo "==========================================" echo "=========================================="
# Check if any artifacts were downloaded (merge-multiple puts files directly in path)
if [ ! -d "./downloaded-snapshots" ]; then
echo "No snapshot artifacts to merge"
echo "=========================================="
echo "MERGE COMPLETE"
echo "=========================================="
echo "Files merged: 0"
exit 0
fi
# Verify target directory exists # Verify target directory exists
if [ ! -d "browser_tests" ]; then if [ ! -d "browser_tests" ]; then
echo "::error::Target directory 'browser_tests' does not exist" echo "::error::Target directory 'browser_tests' does not exist"
exit 1 exit 1
fi fi
# Count files to merge merged_count=0
file_count=$(find ./downloaded-snapshots -type f | wc -l)
if [ "$file_count" -eq 0 ]; then # For each shard's changed files, copy them directly
echo "No snapshot files found in downloaded artifacts" for shard_dir in ./downloaded-snapshots/snapshots-shard-*/; do
echo "==========================================" if [ ! -d "$shard_dir" ]; then
echo "MERGE COMPLETE" continue
echo "==========================================" fi
echo "Files merged: 0"
exit 0
fi
echo "Merging $file_count snapshot file(s)..." shard_name=$(basename "$shard_dir")
file_count=$(find "$shard_dir" -type f | wc -l)
# Copy all files directly, preserving directory structure if [ "$file_count" -eq 0 ]; then
# With merge-multiple: true, files are directly in ./downloaded-snapshots/ without shard subdirs echo " $shard_name: no files"
cp -v -r ./downloaded-snapshots/* browser_tests/ 2>&1 | sed 's/^/ /' continue
fi
echo "Processing $shard_name ($file_count file(s))..."
# Copy files directly, preserving directory structure
# Since files are already in correct structure (no browser_tests/ prefix), just copy them all
cp -v -r "$shard_dir"* browser_tests/ 2>&1 | sed 's/^/ /'
merged_count=$((merged_count + 1))
echo " ✓ Merged"
echo ""
done
echo ""
echo "==========================================" echo "=========================================="
echo "MERGE COMPLETE" echo "MERGE COMPLETE"
echo "==========================================" echo "=========================================="
echo "Files merged: $file_count" echo "Shards merged: $merged_count"
- name: Show changes - name: Show changes
run: | run: |
@@ -253,19 +261,11 @@ jobs:
echo "CHANGES SUMMARY" echo "CHANGES SUMMARY"
echo "==========================================" echo "=========================================="
echo "" echo ""
echo "Changed files in browser_tests (including untracked):" echo "Changed files in browser_tests:"
CHANGES=$(git status --porcelain=v1 --untracked-files=all -- browser_tests/) git diff --name-only browser_tests/ | head -20 || echo "No changes"
if [ -z "$CHANGES" ]; then echo ""
echo "No changes" echo "Total changes:"
echo "" git diff --name-only browser_tests/ | wc -l || echo "0"
echo "Total changes:"
echo "0"
else
echo "$CHANGES" | head -50
echo ""
echo "Total changes:"
echo "$CHANGES" | wc -l
fi
- name: Commit updated expectations - name: Commit updated expectations
id: commit id: commit
@@ -273,7 +273,7 @@ jobs:
git config --global user.name 'github-actions' git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com' git config --global user.email 'github-actions@github.com'
if [ -z "$(git status --porcelain=v1 --untracked-files=all -- browser_tests/)" ]; then if git diff --quiet browser_tests/; then
echo "No changes to commit" echo "No changes to commit"
echo "has-changes=false" >> $GITHUB_OUTPUT echo "has-changes=false" >> $GITHUB_OUTPUT
exit 0 exit 0
@@ -294,7 +294,7 @@ jobs:
echo "✓ Commit and push successful" echo "✓ Commit and push successful"
- name: Add Done Reaction - name: Add Done Reaction
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
if: github.event_name == 'issue_comment' && steps.commit.outputs.has-changes == 'true' if: github.event_name == 'issue_comment' && steps.commit.outputs.has-changes == 'true'
with: with:
comment-id: ${{ needs.setup.outputs.comment-id }} comment-id: ${{ needs.setup.outputs.comment-id }}

View File

@@ -20,15 +20,15 @@ jobs:
dist_tag: ${{ steps.dist.outputs.dist_tag }} dist_tag: ${{ steps.dist.outputs.dist_tag }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ github.event.pull_request.merge_commit_sha }} ref: ${{ github.event.pull_request.merge_commit_sha }}
persist-credentials: false persist-credentials: false
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version-file: '.nvmrc' node-version: '24.x'
- name: Read desktop-ui version - name: Read desktop-ui version
id: get_version id: get_version
@@ -71,7 +71,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Checkout merge commit - name: Checkout merge commit
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ github.event.pull_request.merge_commit_sha }} ref: ${{ github.event.pull_request.merge_commit_sha }}
fetch-depth: 2 fetch-depth: 2

View File

@@ -77,21 +77,21 @@ jobs:
fi fi
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ steps.resolve_ref.outputs.ref }} ref: ${{ steps.resolve_ref.outputs.ref }}
fetch-depth: 1 fetch-depth: 1
persist-credentials: false persist-credentials: false
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version-file: '.nvmrc' node-version: '24.x'
cache: 'pnpm' cache: 'pnpm'
registry-url: https://registry.npmjs.org registry-url: https://registry.npmjs.org

View File

@@ -1,324 +0,0 @@
# Automated bi-weekly workflow to bump ComfyUI frontend RC releases
name: 'Release: Bi-weekly ComfyUI'
on:
# Schedule for Monday at 12:00 PM PST (20:00 UTC)
schedule:
- cron: '0 20 * * 1'
# Allow manual triggering (bypasses bi-weekly check)
workflow_dispatch:
inputs:
comfyui_fork:
description: 'ComfyUI fork to use for PR (e.g., Comfy-Org/ComfyUI)'
required: false
default: 'Comfy-Org/ComfyUI'
type: string
jobs:
check-release-week:
runs-on: ubuntu-latest
outputs:
is_release_week: ${{ steps.check.outputs.is_release_week }}
steps:
- name: Check if release week
id: check
run: |
# Anchor date: first bi-weekly release Monday
ANCHOR="2025-12-22"
ANCHOR_EPOCH=$(date -d "$ANCHOR" +%s)
NOW_EPOCH=$(date +%s)
WEEKS_SINCE=$(( (NOW_EPOCH - ANCHOR_EPOCH) / 604800 ))
if [ $((WEEKS_SINCE % 2)) -eq 0 ]; then
echo "Release week (week $WEEKS_SINCE since anchor $ANCHOR)"
echo "is_release_week=true" >> $GITHUB_OUTPUT
else
echo "Not a release week (week $WEEKS_SINCE since anchor $ANCHOR)"
echo "is_release_week=false" >> $GITHUB_OUTPUT
fi
- name: Summary
run: |
echo "## Bi-weekly Check" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Is release week: ${{ steps.check.outputs.is_release_week }}" >> $GITHUB_STEP_SUMMARY
echo "- Manual trigger: ${{ github.event_name == 'workflow_dispatch' }}" >> $GITHUB_STEP_SUMMARY
resolve-version:
needs: check-release-week
if: needs.check-release-week.outputs.is_release_week == 'true' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
outputs:
current_version: ${{ steps.resolve.outputs.current_version }}
target_version: ${{ steps.resolve.outputs.target_version }}
target_minor: ${{ steps.resolve.outputs.target_minor }}
target_branch: ${{ steps.resolve.outputs.target_branch }}
needs_release: ${{ steps.resolve.outputs.needs_release }}
diff_url: ${{ steps.resolve.outputs.diff_url }}
latest_patch_tag: ${{ steps.resolve.outputs.latest_patch_tag }}
steps:
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v6
with:
fetch-depth: 0
path: frontend
- name: Checkout ComfyUI (sparse)
uses: actions/checkout@v6
with:
repository: Comfy-Org/ComfyUI
sparse-checkout: |
requirements.txt
path: comfyui
- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: 'frontend/.nvmrc'
- name: Install dependencies
working-directory: frontend
run: pnpm install --frozen-lockfile
- name: Resolve release information
id: resolve
working-directory: frontend
run: |
set -euo pipefail
# Run the resolver script
if ! RESULT=$(pnpm exec tsx scripts/cicd/resolve-comfyui-release.ts ../comfyui .); then
echo "Failed to resolve release information"
exit 1
fi
echo "Resolver output:"
echo "$RESULT"
# Validate JSON output
if ! echo "$RESULT" | jq empty 2>/dev/null; then
echo "Invalid JSON output from resolver"
exit 1
fi
# Parse JSON output and set outputs
echo "current_version=$(echo "$RESULT" | jq -r '.current_version')" >> $GITHUB_OUTPUT
echo "target_version=$(echo "$RESULT" | jq -r '.target_version')" >> $GITHUB_OUTPUT
echo "target_minor=$(echo "$RESULT" | jq -r '.target_minor')" >> $GITHUB_OUTPUT
echo "target_branch=$(echo "$RESULT" | jq -r '.target_branch')" >> $GITHUB_OUTPUT
echo "needs_release=$(echo "$RESULT" | jq -r '.needs_release')" >> $GITHUB_OUTPUT
echo "diff_url=$(echo "$RESULT" | jq -r '.diff_url')" >> $GITHUB_OUTPUT
echo "latest_patch_tag=$(echo "$RESULT" | jq -r '.latest_patch_tag')" >> $GITHUB_OUTPUT
- name: Summary
run: |
echo "## Release Information" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Current version: ${{ steps.resolve.outputs.current_version }}" >> $GITHUB_STEP_SUMMARY
echo "- Target version: ${{ steps.resolve.outputs.target_version }}" >> $GITHUB_STEP_SUMMARY
echo "- Target branch: ${{ steps.resolve.outputs.target_branch }}" >> $GITHUB_STEP_SUMMARY
echo "- Needs release: ${{ steps.resolve.outputs.needs_release }}" >> $GITHUB_STEP_SUMMARY
echo "- Diff: [${{ steps.resolve.outputs.current_version }}...${{ steps.resolve.outputs.target_version }}](${{ steps.resolve.outputs.diff_url }})" >> $GITHUB_STEP_SUMMARY
trigger-release-if-needed:
needs: resolve-version
if: needs.resolve-version.outputs.needs_release == 'true'
runs-on: ubuntu-latest
steps:
- name: Trigger release workflow
env:
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
run: |
set -euo pipefail
echo "Triggering release workflow for branch ${{ needs.resolve-version.outputs.target_branch }}"
# Trigger the release-version-bump workflow
if ! gh workflow run release-version-bump.yaml \
--repo Comfy-Org/ComfyUI_frontend \
--ref main \
--field version_type=patch \
--field branch=${{ needs.resolve-version.outputs.target_branch }}; then
echo "Failed to trigger release workflow"
exit 1
fi
echo "Release workflow triggered successfully for ${{ needs.resolve-version.outputs.target_branch }}"
- name: Summary
run: |
echo "## Release Workflow Triggered" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Branch: ${{ needs.resolve-version.outputs.target_branch }}" >> $GITHUB_STEP_SUMMARY
echo "- Target version: ${{ needs.resolve-version.outputs.target_version }}" >> $GITHUB_STEP_SUMMARY
echo "- [View workflow runs](https://github.com/Comfy-Org/ComfyUI_frontend/actions/workflows/release-version-bump.yaml)" >> $GITHUB_STEP_SUMMARY
create-comfyui-pr:
needs: [check-release-week, resolve-version, trigger-release-if-needed]
if: always() && needs.resolve-version.result == 'success' && (needs.check-release-week.outputs.is_release_week == 'true' || github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
steps:
- name: Checkout ComfyUI fork
uses: actions/checkout@v6
with:
repository: ${{ inputs.comfyui_fork || 'Comfy-Org/ComfyUI' }}
token: ${{ secrets.PR_GH_TOKEN }}
path: comfyui
- name: Sync with upstream
working-directory: comfyui
run: |
set -euo pipefail
# Fetch latest upstream to base our branch on fresh code
# Note: This only affects the local checkout, NOT the fork's master branch
# We only push the automation branch, leaving the fork's master untouched
echo "Fetching upstream master..."
if ! git fetch https://github.com/Comfy-Org/ComfyUI.git master; then
echo "Failed to fetch upstream master"
exit 1
fi
echo "Checking out upstream master..."
if ! git checkout FETCH_HEAD; then
echo "Failed to checkout FETCH_HEAD"
exit 1
fi
echo "Successfully synced with upstream master"
- name: Update requirements.txt
working-directory: comfyui
run: |
set -euo pipefail
TARGET_VERSION="${{ needs.resolve-version.outputs.target_version }}"
echo "Updating comfyui-frontend-package to ${TARGET_VERSION}"
# Update the comfyui-frontend-package version (POSIX-compatible)
sed -i.bak "s/comfyui-frontend-package==[0-9.][0-9.]*/comfyui-frontend-package==${TARGET_VERSION}/" requirements.txt
rm requirements.txt.bak
# Verify the change was made
if ! grep -q "comfyui-frontend-package==${TARGET_VERSION}" requirements.txt; then
echo "Failed to update requirements.txt"
exit 1
fi
echo "Updated requirements.txt:"
grep comfyui-frontend-package requirements.txt
- name: Build PR description
id: pr-body
run: |
BODY=$(cat <<'EOF'
Bumps frontend to ${{ needs.resolve-version.outputs.target_version }}
Test quickly:
```bash
python main.py --front-end-version Comfy-Org/ComfyUI_frontend@${{ needs.resolve-version.outputs.target_version }}
```
- Diff: [v${{ needs.resolve-version.outputs.current_version }}...v${{ needs.resolve-version.outputs.target_version }}](${{ needs.resolve-version.outputs.diff_url }})
- PyPI: https://pypi.org/project/comfyui-frontend-package/${{ needs.resolve-version.outputs.target_version }}/
- npm: https://www.npmjs.com/package/@comfyorg/comfyui-frontend-types/v/${{ needs.resolve-version.outputs.target_version }}
EOF
)
# Add release PR note if release was triggered
if [ "${{ needs.resolve-version.outputs.needs_release }}" = "true" ]; then
RELEASE_NOTE="⚠️ **Release PR must be merged first** - check [release workflow runs](https://github.com/Comfy-Org/ComfyUI_frontend/actions/workflows/release-version-bump.yaml)"
BODY=$''"${RELEASE_NOTE}"$'\n\n'"${BODY}"
fi
# Save to file for later use
printf '%s\n' "$BODY" > pr-body.txt
cat pr-body.txt
- name: Create PR to ComfyUI
working-directory: comfyui
env:
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
COMFYUI_FORK: ${{ inputs.comfyui_fork || 'Comfy-Org/ComfyUI' }}
run: |
set -euo pipefail
# Extract fork owner from repository name
FORK_OWNER=$(echo "$COMFYUI_FORK" | cut -d'/' -f1)
echo "Creating PR from ${COMFYUI_FORK} to Comfy-Org/ComfyUI"
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Create/update branch (reuse same branch name each release cycle)
BRANCH="automation/comfyui-frontend-bump"
git checkout -B "$BRANCH"
git add requirements.txt
if ! git diff --cached --quiet; then
git commit -m "Bump comfyui-frontend-package to ${{ needs.resolve-version.outputs.target_version }}"
else
echo "No changes to commit"
exit 0
fi
# Force push to fork (overwrites previous release cycle's branch)
# Note: This intentionally destroys branch history to maintain a single PR
# Any review comments or manual commits will need to be re-applied
if ! git push -f origin "$BRANCH"; then
echo "Failed to push branch to fork"
exit 1
fi
# Create draft PR from fork to upstream
PR_BODY=$(cat ../pr-body.txt)
# Try to create PR, ignore error if it already exists
if ! gh pr create \
--repo Comfy-Org/ComfyUI \
--head "${FORK_OWNER}:${BRANCH}" \
--base master \
--title "Bump comfyui-frontend-package to ${{ needs.resolve-version.outputs.target_version }}" \
--body "$PR_BODY" \
--draft 2>&1; then
# Check if PR already exists
set +e
EXISTING_PR=$(gh pr list --repo Comfy-Org/ComfyUI --head "${FORK_OWNER}:${BRANCH}" --json number --jq '.[0].number' 2>&1)
PR_LIST_EXIT=$?
set -e
if [ $PR_LIST_EXIT -ne 0 ]; then
echo "Failed to check for existing PR: $EXISTING_PR"
exit 1
fi
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
echo "PR already exists (#${EXISTING_PR}), updating branch will update the PR"
else
echo "Failed to create PR and no existing PR found"
exit 1
fi
fi
- name: Summary
run: |
echo "## ComfyUI PR Created" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Draft PR created in Comfy-Org/ComfyUI" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### PR Body:" >> $GITHUB_STEP_SUMMARY
cat pr-body.txt >> $GITHUB_STEP_SUMMARY

View File

@@ -18,15 +18,15 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }} token: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }}
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: 'lts/*'
- name: Check version bump type - name: Check version bump type
id: check_version id: check_version
@@ -148,10 +148,10 @@ jobs:
done done
{ {
echo "results<<EOF" echo "results<<'EOF'"
cat "$RESULTS_FILE" cat "$RESULTS_FILE"
echo "EOF" echo "EOF"
} >> "$GITHUB_OUTPUT" } >> $GITHUB_OUTPUT
- name: Ensure release labels - name: Ensure release labels
if: steps.check_version.outputs.is_minor_bump == 'true' if: steps.check_version.outputs.is_minor_bump == 'true'

View File

@@ -19,14 +19,14 @@ jobs:
is_prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }} is_prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: 'lts/*'
cache: 'pnpm' cache: 'pnpm'
- name: Get current version - name: Get current version
@@ -50,43 +50,37 @@ jobs:
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }} ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
ENABLE_MINIFY: 'true' ENABLE_MINIFY: 'true'
USE_PROD_CONFIG: 'true' USE_PROD_CONFIG: 'true'
IS_NIGHTLY: ${{ case(github.ref == 'refs/heads/main', 'true', 'false') }}
run: | run: |
pnpm install --frozen-lockfile pnpm install --frozen-lockfile
pnpm build
# Desktop-specific release artifact with desktop distribution flags.
DISTRIBUTION=desktop pnpm build
pnpm zipdist ./dist ./dist-desktop.zip
# Default release artifact for core/PyPI.
NX_SKIP_NX_CACHE=true pnpm build
pnpm zipdist pnpm zipdist
- name: Upload dist artifact - name: Upload dist artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
with: with:
name: dist-files name: dist-files
path: | path: |
dist/ dist/
dist.zip dist.zip
dist-desktop.zip
draft_release: draft_release:
needs: build needs: build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Download dist artifact - name: Download dist artifact
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
name: dist-files name: dist-files
- name: Create release - name: Create release
id: create_release id: create_release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 uses: >-
softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
files: | files: |
dist.zip dist.zip
dist-desktop.zip
tag_name: v${{ needs.build.outputs.version }} tag_name: v${{ needs.build.outputs.version }}
target_commitish: ${{ github.event.pull_request.base.ref }} target_commitish: ${{ github.event.pull_request.base.ref }}
make_latest: >- make_latest: >-
@@ -104,13 +98,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Download dist artifact - name: Download dist artifact
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
name: dist-files name: dist-files
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v4
with: with:
python-version: '3.x' python-version: '3.x'
- name: Install build dependencies - name: Install build dependencies
@@ -125,7 +119,8 @@ jobs:
env: env:
COMFYUI_FRONTEND_VERSION: ${{ needs.build.outputs.version }} COMFYUI_FRONTEND_VERSION: ${{ needs.build.outputs.version }}
- name: Publish pypi package - name: Publish pypi package
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 uses: >-
pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc
with: with:
password: ${{ secrets.PYPI_TOKEN }} password: ${{ secrets.PYPI_TOKEN }}
packages-dir: comfyui_frontend_package/dist packages-dir: comfyui_frontend_package/dist
@@ -152,7 +147,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Checkout merge commit - name: Checkout merge commit
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ github.event.pull_request.merge_commit_sha }} ref: ${{ github.event.pull_request.merge_commit_sha }}
fetch-depth: 2 fetch-depth: 2

View File

@@ -69,20 +69,20 @@ jobs:
fi fi
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ steps.resolve_ref.outputs.ref }} ref: ${{ steps.resolve_ref.outputs.ref }}
fetch-depth: 1 fetch-depth: 1
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version-file: '.nvmrc' node-version: 'lts/*'
cache: 'pnpm' cache: 'pnpm'
registry-url: https://registry.npmjs.org registry-url: https://registry.npmjs.org

View File

@@ -15,14 +15,14 @@ jobs:
version: ${{ steps.current_version.outputs.version }} version: ${{ steps.current_version.outputs.version }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: 'lts/*'
cache: 'pnpm' cache: 'pnpm'
- name: Get current version - name: Get current version
@@ -40,7 +40,7 @@ jobs:
pnpm build pnpm build
pnpm zipdist pnpm zipdist
- name: Upload dist artifact - name: Upload dist artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
with: with:
name: dist-files name: dist-files
path: | path: |
@@ -52,13 +52,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Download dist artifact - name: Download dist artifact
uses: actions/download-artifact@v7 uses: actions/download-artifact@v4
with: with:
name: dist-files name: dist-files
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v6 uses: actions/setup-python@v4
with: with:
python-version: '3.x' python-version: '3.x'
- name: Install build dependencies - name: Install build dependencies
@@ -73,7 +73,7 @@ jobs:
env: env:
COMFYUI_FRONTEND_VERSION: ${{ format('{0}.dev{1}', needs.build.outputs.version, inputs.devVersion) }} COMFYUI_FRONTEND_VERSION: ${{ format('{0}.dev{1}', needs.build.outputs.version, inputs.devVersion) }}
- name: Publish pypi package - name: Publish pypi package
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc
with: with:
password: ${{ secrets.PYPI_TOKEN }} password: ${{ secrets.PYPI_TOKEN }}
packages-dir: comfyui_frontend_package/dist packages-dir: comfyui_frontend_package/dist

View File

@@ -1,5 +1,5 @@
# Description: Manual workflow to increment package version with semantic versioning support name: "Release: Version Bump"
name: 'Release: Version Bump' description: "Manual workflow to increment package version with semantic versioning support"
on: on:
workflow_dispatch: workflow_dispatch:
@@ -20,13 +20,6 @@ on:
required: true required: true
default: 'main' default: 'main'
type: string type: string
schedule:
# 00:00 UTC ≈ 4:00 PM PST / 5:00 PM PDT on the previous calendar day
- cron: '0 0 * * *'
concurrency:
group: release-version-bump
cancel-in-progress: true
jobs: jobs:
bump-version: bump-version:
@@ -36,99 +29,15 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Prepare inputs
id: prepared-inputs
shell: bash
env:
RAW_VERSION_TYPE: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version_type || '' }}
RAW_PRE_RELEASE: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.pre_release || '' }}
RAW_BRANCH: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.branch || '' }}
run: |
set -euo pipefail
VERSION_TYPE="$RAW_VERSION_TYPE"
PRE_RELEASE="$RAW_PRE_RELEASE"
TARGET_BRANCH="$RAW_BRANCH"
if [[ -z "$VERSION_TYPE" ]]; then
VERSION_TYPE='patch'
fi
if [[ -z "$TARGET_BRANCH" ]]; then
TARGET_BRANCH='main'
fi
{
echo "version_type=$VERSION_TYPE"
echo "pre_release=$PRE_RELEASE"
echo "branch=$TARGET_BRANCH"
} >> "$GITHUB_OUTPUT"
- name: Close stale nightly version bump PRs
if: github.event_name == 'schedule'
uses: actions/github-script@v8
with:
github-token: ${{ github.token }}
script: |
const prefix = 'version-bump-'
const closed = []
const prs = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100
})
for (const pr of prs) {
if (!pr.head?.ref?.startsWith(prefix)) {
continue
}
if (pr.user?.login !== 'github-actions[bot]') {
continue
}
// Only clean up stale nightly PRs targeting main.
// Adjust here if other target branches should be cleaned.
if (pr.base?.ref !== 'main') {
continue
}
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: 'closed'
})
try {
await github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${pr.head.ref}`
})
} catch (error) {
if (![404, 422].includes(error.status)) {
throw error
}
}
closed.push(pr.number)
}
core.info(`Closed ${closed.length} stale PR(s).`)
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ steps.prepared-inputs.outputs.branch }} ref: ${{ github.event.inputs.branch }}
fetch-depth: 0 fetch-depth: 0
persist-credentials: false
- name: Validate branch exists - name: Validate branch exists
env:
TARGET_BRANCH: ${{ steps.prepared-inputs.outputs.branch }}
run: | run: |
BRANCH="$TARGET_BRANCH" BRANCH="${{ github.event.inputs.branch }}"
if ! git show-ref --verify --quiet "refs/heads/$BRANCH" && ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then if ! git show-ref --verify --quiet "refs/heads/$BRANCH" && ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
echo "❌ Branch '$BRANCH' does not exist" echo "❌ Branch '$BRANCH' does not exist"
echo "" echo ""
@@ -142,45 +51,31 @@ jobs:
echo "✅ Branch '$BRANCH' exists" echo "✅ Branch '$BRANCH' exists"
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: lts/*
cache: 'pnpm'
- name: Bump version - name: Bump version
id: bump-version id: bump-version
env:
VERSION_TYPE: ${{ steps.prepared-inputs.outputs.version_type }}
PRE_RELEASE: ${{ steps.prepared-inputs.outputs.pre_release }}
run: | run: |
set -euo pipefail pnpm version ${{ github.event.inputs.version_type }} --preid ${{ github.event.inputs.pre_release }} --no-git-tag-version
if [[ -n "$PRE_RELEASE" && ! "$VERSION_TYPE" =~ ^pre(major|minor|patch)$ && "$VERSION_TYPE" != "prerelease" ]]; then
echo "❌ pre_release was provided but version_type='$VERSION_TYPE' does not support --preid"
exit 1
fi
if [[ -n "$PRE_RELEASE" ]]; then
pnpm version "$VERSION_TYPE" --preid "$PRE_RELEASE" --no-git-tag-version
else
pnpm version "$VERSION_TYPE" --no-git-tag-version
fi
NEW_VERSION=$(node -p "require('./package.json').version") NEW_VERSION=$(node -p "require('./package.json').version")
echo "NEW_VERSION=$NEW_VERSION" >> "$GITHUB_OUTPUT" echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
- name: Format PR string - name: Format PR string
id: capitalised id: capitalised
env:
VERSION_TYPE: ${{ steps.prepared-inputs.outputs.version_type }}
run: | run: |
CAPITALISED_TYPE="$VERSION_TYPE" CAPITALISED_TYPE=${{ github.event.inputs.version_type }}
echo "capitalised=${CAPITALISED_TYPE@u}" >> "$GITHUB_OUTPUT" echo "capitalised=${CAPITALISED_TYPE@u}" >> $GITHUB_OUTPUT
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with: with:
token: ${{ secrets.PR_GH_TOKEN }} token: ${{ secrets.PR_GH_TOKEN }}
commit-message: '[release] Increment version to ${{ steps.bump-version.outputs.NEW_VERSION }}' commit-message: '[release] Increment version to ${{ steps.bump-version.outputs.NEW_VERSION }}'
@@ -188,8 +83,8 @@ jobs:
body: | body: |
${{ steps.capitalised.outputs.capitalised }} version increment to ${{ steps.bump-version.outputs.NEW_VERSION }} ${{ steps.capitalised.outputs.capitalised }} version increment to ${{ steps.bump-version.outputs.NEW_VERSION }}
**Base branch:** `${{ steps.prepared-inputs.outputs.branch }}` **Base branch:** `${{ github.event.inputs.branch }}`
branch: version-bump-${{ steps.bump-version.outputs.NEW_VERSION }} branch: version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
base: ${{ steps.prepared-inputs.outputs.branch }} base: ${{ github.event.inputs.branch }}
labels: | labels: |
Release Release

View File

@@ -29,7 +29,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
ref: ${{ github.event.inputs.branch }} ref: ${{ github.event.inputs.branch }}
fetch-depth: 0 fetch-depth: 0
@@ -51,14 +51,14 @@ jobs:
echo "✅ Branch '$BRANCH' exists" echo "✅ Branch '$BRANCH' exists"
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v5
with: with:
node-version-file: '.nvmrc' node-version: '24.x'
cache: 'pnpm' cache: 'pnpm'
- name: Bump desktop-ui version - name: Bump desktop-ui version
@@ -79,7 +79,7 @@ jobs:
echo "capitalised=${VERSION_TYPE@u}" >> $GITHUB_OUTPUT echo "capitalised=${VERSION_TYPE@u}" >> $GITHUB_OUTPUT
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with: with:
token: ${{ secrets.PR_GH_TOKEN }} token: ${{ secrets.PR_GH_TOKEN }}
commit-message: '[release] Increment desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}' commit-message: '[release] Increment desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}'

View File

@@ -1,5 +1,5 @@
# Description: Automated weekly documentation accuracy check and update via Claude name: "Weekly Documentation Check"
name: 'Weekly Documentation Check' description: "Automated weekly documentation accuracy check and update via Claude"
permissions: permissions:
contents: write contents: write
@@ -22,20 +22,20 @@ jobs:
timeout-minutes: 45 timeout-minutes: 45
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
fetch-depth: 50 fetch-depth: 0
ref: main ref: main
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version: '20'
cache: 'pnpm' cache: 'pnpm'
- name: Install dependencies for analysis tools - name: Install dependencies for analysis tools
@@ -49,7 +49,7 @@ jobs:
fi fi
- name: Run Claude Documentation Review - name: Run Claude Documentation Review
uses: anthropics/claude-code-action@ff34ce0ff04a470bd3fa56c1ef391c8f1c19f8e9 # v1.0.38 uses: anthropics/claude-code-action@v1.0.6
with: with:
prompt: | prompt: |
Is all documentation still 100% accurate? Is all documentation still 100% accurate?
@@ -130,7 +130,7 @@ jobs:
- name: Create or Update Pull Request - name: Create or Update Pull Request
if: steps.check_changes.outputs.has_changes == 'true' if: steps.check_changes.outputs.has_changes == 'true'
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 uses: peter-evans/create-pull-request@v7
with: with:
token: ${{ secrets.PR_GH_TOKEN }} token: ${{ secrets.PR_GH_TOKEN }}
commit-message: 'docs: weekly documentation accuracy update' commit-message: 'docs: weekly documentation accuracy update'

4
.gitignore vendored
View File

@@ -26,7 +26,6 @@ dist-ssr
.claude/*.local.json .claude/*.local.json
.claude/*.local.md .claude/*.local.md
.claude/*.local.txt .claude/*.local.txt
.claude/worktrees
CLAUDE.local.md CLAUDE.local.md
# Editor directories and files # Editor directories and files
@@ -65,7 +64,6 @@ browser_tests/local/
dist.zip dist.zip
/temp/ /temp/
/tmp/
# Generated JSON Schemas # Generated JSON Schemas
/schemas/ /schemas/
@@ -98,5 +96,3 @@ vitest.config.*.timestamp*
# Weekly docs check output # Weekly docs check output
/output.txt /output.txt
.amp

View File

@@ -1,29 +1,16 @@
// This file is intentionally kept in CommonJS format (.cjs) // This file is intentionally kept in CommonJS format (.cjs)
// to resolve compatibility issues with dependencies that require CommonJS. // to resolve compatibility issues with dependencies that require CommonJS.
// Do not convert this file to ESModule format unless all dependencies support it. // Do not convert this file to ESModule format unless all dependencies support it.
const { defineConfig } = require('@lobehub/i18n-cli') const { defineConfig } = require('@lobehub/i18n-cli');
module.exports = defineConfig({ module.exports = defineConfig({
modelName: 'gpt-4.1', modelName: 'gpt-4.1',
splitToken: 1024, splitToken: 1024,
saveImmediately: true,
entry: 'src/locales/en', entry: 'src/locales/en',
entryLocale: 'en', entryLocale: 'en',
output: 'src/locales', output: 'src/locales',
outputLocales: [ outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar', 'tr'],
'zh', reference: `Special names to keep untranslated: flux, photomaker, clip, vae, cfg, stable audio, stable cascade, stable zero, controlnet, lora, HiDream.
'zh-TW',
'ru',
'ja',
'ko',
'fr',
'es',
'ar',
'tr',
'pt-BR',
'fa'
],
reference: `Special names to keep untranslated: flux, photomaker, clip, vae, cfg, stable audio, stable cascade, stable zero, controlnet, lora, HiDream, Civitai, Hugging Face.
'latent' is the short form of 'latent space'. 'latent' is the short form of 'latent space'.
'mask' is in the context of image processing. 'mask' is in the context of image processing.
@@ -31,11 +18,5 @@ module.exports = defineConfig({
- For 'zh' locale: Use ONLY Simplified Chinese characters (简体中文). Common examples: 节点 (not 節點), 画布 (not 畫布), 图像 (not 圖像), 选择 (not 選擇), 减小 (not 減小). - For 'zh' locale: Use ONLY Simplified Chinese characters (简体中文). Common examples: 节点 (not 節點), 画布 (not 畫布), 图像 (not 圖像), 选择 (not 選擇), 减小 (not 減小).
- For 'zh-TW' locale: Use ONLY Traditional Chinese characters (繁體中文) with Taiwan-specific terminology. - For 'zh-TW' locale: Use ONLY Traditional Chinese characters (繁體中文) with Taiwan-specific terminology.
- NEVER mix Simplified and Traditional Chinese characters within the same locale. - NEVER mix Simplified and Traditional Chinese characters within the same locale.
IMPORTANT Persian Translation Guidelines:
- For 'fa' locale: Use formal Persian (فارسی رسمی) for professional tone throughout the UI.
- Keep commonly used technical terms in English when they are standard in Persian software (e.g., node, workflow).
- Use Arabic-Indic numerals (۰-۹) for numbers where appropriate.
- Maintain consistency with terminology used in Persian software and design applications.
` `
}) });

1
.npmrc
View File

@@ -1,3 +1,2 @@
ignore-workspace-root-check=true ignore-workspace-root-check=true
catalog-mode=prefer catalog-mode=prefer
public-hoist-pattern[]=@parcel/watcher

View File

@@ -1,14 +0,0 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"singleQuote": true,
"tabWidth": 2,
"semi": false,
"trailingComma": "none",
"printWidth": 80,
"ignorePatterns": [
"packages/registry-types/src/comfyRegistryTypes.ts",
"public/materialdesignicons.min.css",
"src/types/generatedManagerTypes.ts",
"**/__fixtures__/**/*.json"
]
}

View File

@@ -2,82 +2,25 @@
"$schema": "./node_modules/oxlint/configuration_schema.json", "$schema": "./node_modules/oxlint/configuration_schema.json",
"ignorePatterns": [ "ignorePatterns": [
".i18nrc.cjs", ".i18nrc.cjs",
".nx/*", "components.d.ts",
"lint-staged.config.js",
"vitest.setup.ts",
"**/vite.config.*.timestamp*", "**/vite.config.*.timestamp*",
"**/vitest.config.*.timestamp*", "**/vitest.config.*.timestamp*",
"components.d.ts",
"coverage/*",
"dist/*",
"packages/registry-types/src/comfyRegistryTypes.ts", "packages/registry-types/src/comfyRegistryTypes.ts",
"playwright-report/*",
"src/extensions/core/*", "src/extensions/core/*",
"src/scripts/*", "src/scripts/*",
"src/types/generatedManagerTypes.ts", "src/types/generatedManagerTypes.ts",
"src/types/vue-shim.d.ts", "src/types/vue-shim.d.ts"
"test-results/*",
"vitest.setup.ts"
],
"plugins": [
"eslint",
"import",
"oxc",
"typescript",
"unicorn",
"vitest",
"vue"
], ],
"rules": { "rules": {
"no-async-promise-executor": "off", "no-async-promise-executor": "off",
"no-console": [
"error",
{
"allow": ["warn", "error"]
}
],
"no-control-regex": "off", "no-control-regex": "off",
"no-eval": "error", "no-eval": "off",
"no-redeclare": "error",
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "primevue/calendar",
"message": "Calendar is deprecated in PrimeVue 4+. Use DatePicker instead: import DatePicker from 'primevue/datepicker'"
},
{
"name": "primevue/dropdown",
"message": "Dropdown is deprecated in PrimeVue 4+. Use Select instead: import Select from 'primevue/select'"
},
{
"name": "primevue/inputswitch",
"message": "InputSwitch is deprecated in PrimeVue 4+. Use ToggleSwitch instead: import ToggleSwitch from 'primevue/toggleswitch'"
},
{
"name": "primevue/overlaypanel",
"message": "OverlayPanel is deprecated in PrimeVue 4+. Use Popover instead: import Popover from 'primevue/popover'"
},
{
"name": "primevue/sidebar",
"message": "Sidebar is deprecated in PrimeVue 4+. Use Drawer instead: import Drawer from 'primevue/drawer'"
}
]
}
],
"no-self-assign": "allow", "no-self-assign": "allow",
"no-unused-expressions": "off", "no-unused-expressions": "off",
"no-unused-private-class-members": "off", "no-unused-private-class-members": "off",
"no-useless-rename": "off", "no-useless-rename": "off",
"import/default": "error",
"import/export": "error",
"import/namespace": "error",
"import/no-duplicates": "error",
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
"jest/expect-expect": "off",
"jest/no-conditional-expect": "off",
"jest/no-disabled-tests": "off",
"jest/no-standalone-expect": "off",
"jest/valid-title": "off",
"typescript/no-this-alias": "off", "typescript/no-this-alias": "off",
"typescript/no-unnecessary-parameter-property-assignment": "off", "typescript/no-unnecessary-parameter-property-assignment": "off",
"typescript/no-unsafe-declaration-merging": "off", "typescript/no-unsafe-declaration-merging": "off",
@@ -95,28 +38,6 @@
"typescript/no-redundant-type-constituents": "off", "typescript/no-redundant-type-constituents": "off",
"typescript/restrict-template-expressions": "off", "typescript/restrict-template-expressions": "off",
"typescript/unbound-method": "off", "typescript/unbound-method": "off",
"typescript/no-floating-promises": "error", "typescript/no-floating-promises": "error"
"typescript/no-explicit-any": "error", }
"vue/no-import-compiler-macros": "error", }
"vue/no-dupe-keys": "error"
},
"overrides": [
{
"files": ["**/*.{stories,test,spec}.ts", "**/*.stories.vue"],
"rules": {
"no-console": "allow"
}
},
{
"files": ["browser_tests/**/*.ts"],
"rules": {
"typescript/no-explicit-any": "error",
"no-async-promise-executor": "error",
"no-control-regex": "error",
"no-useless-rename": "error",
"no-unused-private-class-members": "error",
"unicorn/no-empty-file": "error"
}
}
]
}

View File

@@ -1,24 +0,0 @@
# pinact configuration
# https://github.com/suzuki-shunsuke/pinact
version: 3
files:
- pattern: .github/workflows/*.yaml
- pattern: .github/actions/**/*.yaml
# Actions that don't need SHA pinning (official GitHub actions are trusted)
ignore_actions:
- name: actions/cache
ref: v5
- name: actions/checkout
ref: v6
- name: actions/setup-node
ref: v6
- name: actions/setup-python
ref: v6
- name: actions/upload-artifact
ref: v6
- name: actions/download-artifact
ref: v7
- name: actions/github-script
ref: v8

2
.prettierignore Normal file
View File

@@ -0,0 +1,2 @@
packages/registry-types/src/comfyRegistryTypes.ts
src/types/generatedManagerTypes.ts

11
.prettierrc Normal file
View File

@@ -0,0 +1,11 @@
{
"singleQuote": true,
"tabWidth": 2,
"semi": false,
"trailingComma": "none",
"printWidth": 80,
"importOrder": ["^@core/(.*)$", "<THIRD_PARTY_MODULES>", "^@/(.*)$", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"plugins": ["@prettier/plugin-oxc", "@trivago/prettier-plugin-sort-imports"]
}

Some files were not shown because too many files have changed in this diff Show More