Compare commits
1 Commits
graph-mode
...
refactor/t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8461252c1 |
17
.env_example
@@ -5,10 +5,6 @@ PLAYWRIGHT_TEST_URL=http://localhost:5173
|
||||
|
||||
# Proxy target of the local development server
|
||||
# Note: localhost:8188 does not work.
|
||||
# Cloud auto-detection: Setting this to any *.comfy.org URL automatically enables
|
||||
# cloud mode (DISTRIBUTION=cloud) without needing to set DISTRIBUTION separately.
|
||||
# Examples: https://testcloud.comfy.org/, https://stagingcloud.comfy.org/,
|
||||
# https://pr-123.testenvs.comfy.org/, https://cloud.comfy.org/
|
||||
DEV_SERVER_COMFYUI_URL=http://127.0.0.1:8188
|
||||
|
||||
# Allow dev server access from remote IP addresses.
|
||||
@@ -23,10 +19,10 @@ TEST_COMFYUI_DIR=/home/ComfyUI
|
||||
# Whether to enable minification of the frontend code.
|
||||
ENABLE_MINIFY=true
|
||||
|
||||
# Whether to disable proxying the `/templates` route. If true, allows you to
|
||||
# serve templates from the ComfyUI_frontend/public/templates folder (for
|
||||
# locally testing changes to templates). When false or nonexistent, the
|
||||
# templates are served via the normal method from the server's python site
|
||||
# Whether to disable proxying the `/templates` route. If true, allows you to
|
||||
# serve templates from the ComfyUI_frontend/public/templates folder (for
|
||||
# locally testing changes to templates). When false or nonexistent, the
|
||||
# templates are served via the normal method from the server's python site
|
||||
# packages.
|
||||
DISABLE_TEMPLATES_PROXY=false
|
||||
|
||||
@@ -37,8 +33,3 @@ DISABLE_VUE_PLUGINS=false
|
||||
# Algolia credentials required for developing with the new custom node manager.
|
||||
ALGOLIA_APP_ID=4E0RO38HS8
|
||||
ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579
|
||||
|
||||
# Sentry ENV vars replace with real ones for debugging
|
||||
# SENTRY_AUTH_TOKEN=private-token # get from sentry
|
||||
# SENTRY_ORG=comfy-org
|
||||
# SENTRY_PROJECT=cloud-frontend-staging
|
||||
|
||||
@@ -25,6 +25,3 @@ e3bb29ceb8174b8bbca9e48ec7d42cd540f40efa
|
||||
|
||||
# [refactor] Improve updates/notifications domain organization (#5590)
|
||||
27ab355f9c73415dc39f4d3f512b02308f847801
|
||||
|
||||
# Migrate Tailwind styles to design-system package
|
||||
9f19d8fb4bd22518879343b49c05634dca777df0
|
||||
|
||||
1
.gitattributes
vendored
@@ -7,7 +7,6 @@
|
||||
*.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
|
||||
|
||||
@@ -26,6 +26,15 @@ jobs:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
key: electron-types-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
electron-types-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Update electron types
|
||||
run: pnpm install --workspace-root @comfyorg/comfyui-electron-types@latest
|
||||
|
||||
|
||||
@@ -31,9 +31,26 @@ jobs:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
key: update-manager-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
update-manager-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Cache ComfyUI-Manager repository
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ComfyUI-Manager
|
||||
key: comfyui-manager-repo-${{ runner.os }}-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
comfyui-manager-repo-${{ runner.os }}-
|
||||
|
||||
- name: Checkout ComfyUI-Manager repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
|
||||
@@ -30,9 +30,26 @@ jobs:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
key: update-registry-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
update-registry-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Cache comfy-api repository
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: comfy-api
|
||||
key: comfy-api-repo-${{ runner.os }}-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
comfy-api-repo-${{ runner.os }}-
|
||||
|
||||
- name: Checkout comfy-api repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
|
||||
19
.github/workflows/ci-lint-format.yaml
vendored
@@ -33,15 +33,27 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
.eslintcache
|
||||
tsconfig.tsbuildinfo
|
||||
.prettierCache
|
||||
.knip-cache
|
||||
key: lint-format-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js,mts}', '*.config.*', '.eslintrc.*', '.prettierrc.*', 'tsconfig.json') }}
|
||||
restore-keys: |
|
||||
lint-format-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
lint-format-cache-${{ runner.os }}-
|
||||
ci-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run ESLint with auto-fix
|
||||
run: pnpm lint:fix
|
||||
|
||||
- name: Run Stylelint with auto-fix
|
||||
run: pnpm stylelint:fix
|
||||
|
||||
- name: Run Prettier with auto-format
|
||||
run: pnpm format
|
||||
|
||||
@@ -66,7 +78,6 @@ jobs:
|
||||
- name: Final validation
|
||||
run: |
|
||||
pnpm lint
|
||||
pnpm stylelint
|
||||
pnpm format:check
|
||||
pnpm knip
|
||||
|
||||
|
||||
52
.github/workflows/ci-size-data.yaml
vendored
@@ -1,52 +0,0 @@
|
||||
name: "CI: Size Data"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
collect:
|
||||
if: github.repository == 'Comfy-Org/ComfyUI_frontend'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
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
|
||||
run: pnpm build
|
||||
|
||||
- name: Collect size data
|
||||
run: node scripts/size-collect.js
|
||||
|
||||
- name: Save PR number & base branch
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
echo ${{ github.event.number }} > ./temp/size/number.txt
|
||||
echo ${{ github.base_ref }} > ./temp/size/base.txt
|
||||
|
||||
- name: Upload size data
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: size-data
|
||||
path: temp/size
|
||||
26
.github/workflows/ci-tests-storybook.yaml
vendored
@@ -50,6 +50,19 @@ jobs:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
storybook-static
|
||||
tsconfig.tsbuildinfo
|
||||
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
|
||||
restore-keys: |
|
||||
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
storybook-cache-${{ runner.os }}-
|
||||
storybook-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
@@ -102,6 +115,19 @@ jobs:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
storybook-static
|
||||
tsconfig.tsbuildinfo
|
||||
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
|
||||
restore-keys: |
|
||||
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
storybook-cache-${{ runner.os }}-
|
||||
storybook-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
|
||||
13
.github/workflows/ci-tests-unit.yaml
vendored
@@ -29,6 +29,19 @@ jobs:
|
||||
node-version: "lts/*"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
coverage
|
||||
.vitest-cache
|
||||
key: vitest-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', 'vitest.config.*', 'tsconfig.json') }}
|
||||
restore-keys: |
|
||||
vitest-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
vitest-cache-${{ runner.os }}-
|
||||
test-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
|
||||
223
.github/workflows/pr-backport.yaml
vendored
@@ -69,139 +69,70 @@ jobs:
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Collect backport targets
|
||||
id: targets
|
||||
- name: Check if backports already exist
|
||||
id: check-existing
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
run: |
|
||||
TARGETS=()
|
||||
declare -A SEEN=()
|
||||
# Check for existing backport PRs for this PR number
|
||||
EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName')
|
||||
|
||||
if [ -z "$EXISTING_BACKPORTS" ]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# For manual triggers with force_rerun, proceed anyway
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "::warning::Force rerun requested - existing backports will be updated"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Found existing backport PRs:"
|
||||
echo "$EXISTING_BACKPORTS"
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
echo "::warning::Backport PRs already exist for PR #${PR_NUMBER}, skipping to avoid duplicates"
|
||||
|
||||
- name: Extract version labels
|
||||
if: steps.check-existing.outputs.skip != 'true'
|
||||
id: versions
|
||||
run: |
|
||||
# Extract version labels (e.g., "1.24", "1.22")
|
||||
VERSIONS=""
|
||||
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
# For manual triggers, get labels from the PR
|
||||
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
|
||||
else
|
||||
# For automatic triggers, extract from PR event
|
||||
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
|
||||
LABELS=$(echo "$LABELS" | jq -r '.[].name')
|
||||
fi
|
||||
|
||||
add_target() {
|
||||
local label="$1"
|
||||
local target="$2"
|
||||
|
||||
if [ -z "$target" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
target=$(echo "$target" | xargs)
|
||||
|
||||
if [ -z "$target" ] || [ -n "${SEEN[$target]}" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if git ls-remote --exit-code origin "$target" >/dev/null 2>&1; then
|
||||
TARGETS+=("$target")
|
||||
SEEN["$target"]=1
|
||||
else
|
||||
echo "::warning::Label '${label}' references missing branch '${target}'"
|
||||
fi
|
||||
}
|
||||
|
||||
while IFS= read -r label; do
|
||||
[ -z "$label" ] && continue
|
||||
|
||||
if [[ "$label" =~ ^branch:(.+)$ ]]; then
|
||||
add_target "$label" "${BASH_REMATCH[1]}"
|
||||
elif [[ "$label" =~ ^backport:(.+)$ ]]; then
|
||||
add_target "$label" "${BASH_REMATCH[1]}"
|
||||
elif [[ "$label" =~ ^core\/([0-9]+)\.([0-9]+)$ ]]; then
|
||||
SAFE_MAJOR="${BASH_REMATCH[1]}"
|
||||
SAFE_MINOR="${BASH_REMATCH[2]}"
|
||||
add_target "$label" "core/${SAFE_MAJOR}.${SAFE_MINOR}"
|
||||
elif [[ "$label" =~ ^cloud\/([0-9]+)\.([0-9]+)$ ]]; then
|
||||
SAFE_MAJOR="${BASH_REMATCH[1]}"
|
||||
SAFE_MINOR="${BASH_REMATCH[2]}"
|
||||
add_target "$label" "cloud/${SAFE_MAJOR}.${SAFE_MINOR}"
|
||||
elif [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
add_target "$label" "core/${label}"
|
||||
fi
|
||||
done <<< "$LABELS"
|
||||
|
||||
if [ "${#TARGETS[@]}" -eq 0 ]; then
|
||||
echo "::error::No backport targets found (use labels like '1.24' or 'branch:release/hotfix')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "targets=${TARGETS[*]}" >> $GITHUB_OUTPUT
|
||||
echo "Found backport targets: ${TARGETS[*]}"
|
||||
|
||||
- name: Filter already backported targets
|
||||
id: filter-targets
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
FORCE_RERUN_INPUT: >-
|
||||
${{ github.event_name == 'workflow_dispatch' && inputs.force_rerun
|
||||
|| 'false' }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: >-
|
||||
${{ github.event_name == 'workflow_dispatch' && inputs.pr_number
|
||||
|| github.event.pull_request.number }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
REQUESTED_TARGETS="${{ steps.targets.outputs.targets }}"
|
||||
if [ -z "$REQUESTED_TARGETS" ]; then
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
echo "pending-targets=" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
FORCE_RERUN=false
|
||||
if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$FORCE_RERUN_INPUT" = "true" ]; then
|
||||
FORCE_RERUN=true
|
||||
fi
|
||||
|
||||
mapfile -t EXISTING_BRANCHES < <(
|
||||
git ls-remote --heads origin "backport-${PR_NUMBER}-to-*" || true
|
||||
)
|
||||
|
||||
PENDING=()
|
||||
SKIPPED=()
|
||||
|
||||
for target in $REQUESTED_TARGETS; do
|
||||
SAFE_TARGET=$(echo "$target" | tr '/' '-')
|
||||
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
|
||||
|
||||
if [ "$FORCE_RERUN" = true ]; then
|
||||
PENDING+=("$target")
|
||||
continue
|
||||
fi
|
||||
|
||||
if printf '%s\n' "${EXISTING_BRANCHES[@]:-}" |
|
||||
grep -Fq "refs/heads/${BACKPORT_BRANCH}"; then
|
||||
SKIPPED+=("$target")
|
||||
else
|
||||
PENDING+=("$target")
|
||||
|
||||
for label in $LABELS; do
|
||||
# Match version labels like "1.24" (major.minor only)
|
||||
if [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
# Validate the branch exists before adding to list
|
||||
if git ls-remote --exit-code origin "core/${label}" >/dev/null 2>&1; then
|
||||
VERSIONS="${VERSIONS}${label} "
|
||||
else
|
||||
echo "::warning::Label '${label}' found but branch 'core/${label}' does not exist"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
SKIPPED_JOINED="${SKIPPED[*]:-}"
|
||||
PENDING_JOINED="${PENDING[*]:-}"
|
||||
|
||||
echo "already-exists=${SKIPPED_JOINED}" >> $GITHUB_OUTPUT
|
||||
echo "pending-targets=${PENDING_JOINED}" >> $GITHUB_OUTPUT
|
||||
|
||||
if [ -z "$PENDING_JOINED" ]; then
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
if [ -n "$SKIPPED_JOINED" ]; then
|
||||
echo "::warning::Backport branches already exist for: ${SKIPPED_JOINED}"
|
||||
fi
|
||||
else
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
if [ -n "$SKIPPED_JOINED" ]; then
|
||||
echo "::notice::Skipping already backported targets: ${SKIPPED_JOINED}"
|
||||
fi
|
||||
if [ -z "$VERSIONS" ]; then
|
||||
echo "::error::No version labels found (e.g., 1.24, 1.22)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "versions=${VERSIONS}" >> $GITHUB_OUTPUT
|
||||
echo "Found version labels: ${VERSIONS}"
|
||||
|
||||
- name: Backport commits
|
||||
if: steps.filter-targets.outputs.skip != 'true'
|
||||
if: steps.check-existing.outputs.skip != 'true'
|
||||
id: backport
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
@@ -219,25 +150,16 @@ jobs:
|
||||
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
fi
|
||||
|
||||
for target in ${{ steps.filter-targets.outputs.pending-targets }}; do
|
||||
TARGET_BRANCH="${target}"
|
||||
SAFE_TARGET=$(echo "$TARGET_BRANCH" | tr '/' '-')
|
||||
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
|
||||
for version in ${{ steps.versions.outputs.versions }}; do
|
||||
echo "::group::Backporting to core/${version}"
|
||||
|
||||
echo "::group::Backporting to ${TARGET_BRANCH}"
|
||||
TARGET_BRANCH="core/${version}"
|
||||
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${version}"
|
||||
|
||||
# Fetch target branch (fail if doesn't exist)
|
||||
if ! git fetch origin "${TARGET_BRANCH}"; then
|
||||
echo "::error::Target branch ${TARGET_BRANCH} does not exist"
|
||||
FAILED="${FAILED}${TARGET_BRANCH}:branch-missing "
|
||||
echo "::endgroup::"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if commit already exists on target branch
|
||||
if git branch -r --contains "${MERGE_COMMIT}" | grep -q "origin/${TARGET_BRANCH}"; then
|
||||
echo "::notice::Commit ${MERGE_COMMIT} already exists on ${TARGET_BRANCH}, skipping backport"
|
||||
FAILED="${FAILED}${TARGET_BRANCH}:already-exists "
|
||||
FAILED="${FAILED}${version}:branch-missing "
|
||||
echo "::endgroup::"
|
||||
continue
|
||||
fi
|
||||
@@ -248,7 +170,7 @@ jobs:
|
||||
# Try cherry-pick
|
||||
if git cherry-pick "${MERGE_COMMIT}"; then
|
||||
git push origin "${BACKPORT_BRANCH}"
|
||||
SUCCESS="${SUCCESS}${TARGET_BRANCH}:${BACKPORT_BRANCH} "
|
||||
SUCCESS="${SUCCESS}${version}:${BACKPORT_BRANCH} "
|
||||
echo "Successfully created backport branch: ${BACKPORT_BRANCH}"
|
||||
# Return to main (keep the branch, we need it for PR)
|
||||
git checkout main
|
||||
@@ -258,7 +180,7 @@ jobs:
|
||||
git cherry-pick --abort
|
||||
|
||||
echo "::error::Cherry-pick failed due to conflicts"
|
||||
FAILED="${FAILED}${TARGET_BRANCH}:conflicts:${CONFLICTS} "
|
||||
FAILED="${FAILED}${version}:conflicts:${CONFLICTS} "
|
||||
|
||||
# Clean up the failed branch
|
||||
git checkout main
|
||||
@@ -276,7 +198,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Create PR for each successful backport
|
||||
if: steps.filter-targets.outputs.skip != 'true' && steps.backport.outputs.success
|
||||
if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
@@ -292,13 +214,13 @@ jobs:
|
||||
fi
|
||||
|
||||
for backport in ${{ steps.backport.outputs.success }}; do
|
||||
IFS=':' read -r target branch <<< "${backport}"
|
||||
IFS=':' read -r version branch <<< "${backport}"
|
||||
|
||||
if PR_URL=$(gh pr create \
|
||||
--base "${target}" \
|
||||
--base "core/${version}" \
|
||||
--head "${branch}" \
|
||||
--title "[backport ${target}] ${PR_TITLE}" \
|
||||
--body "Backport of #${PR_NUMBER} to \`${target}\`"$'\n\n'"Automatically created by backport workflow." \
|
||||
--title "[backport ${version}] ${PR_TITLE}" \
|
||||
--body "Backport of #${PR_NUMBER} to \`core/${version}\`"$'\n\n'"Automatically created by backport workflow." \
|
||||
--label "backport" 2>&1); then
|
||||
|
||||
# Extract PR number from URL
|
||||
@@ -308,14 +230,14 @@ jobs:
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Successfully backported to #${PR_NUM}"
|
||||
fi
|
||||
else
|
||||
echo "::error::Failed to create PR for ${target}: ${PR_URL}"
|
||||
echo "::error::Failed to create PR for ${version}: ${PR_URL}"
|
||||
# Still try to comment on the original PR about the failure
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`${target}\`. Please create the PR manually from branch \`${branch}\`"
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`core/${version}\`. Please create the PR manually from branch \`${branch}\`"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Comment on failures
|
||||
if: steps.filter-targets.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
|
||||
if: steps.check-existing.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
@@ -331,25 +253,16 @@ jobs:
|
||||
fi
|
||||
|
||||
for failure in ${{ steps.backport.outputs.failed }}; do
|
||||
IFS=':' read -r target reason conflicts <<< "${failure}"
|
||||
IFS=':' read -r version reason conflicts <<< "${failure}"
|
||||
|
||||
if [ "${reason}" = "branch-missing" ]; then
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`${target}\` does not exist"
|
||||
|
||||
elif [ "${reason}" = "already-exists" ]; then
|
||||
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} Backport failed: Branch \`core/${version}\` does not exist"
|
||||
|
||||
elif [ "${reason}" = "conflicts" ]; then
|
||||
# Convert comma-separated conflicts back to newlines for display
|
||||
CONFLICTS_LIST=$(echo "${conflicts}" | tr ',' '\n' | sed 's/^/- /')
|
||||
|
||||
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>"
|
||||
COMMENT_BODY="@${PR_AUTHOR} Backport to \`core/${version}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`core/${version}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
|
||||
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Remove needs-backport label
|
||||
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"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
32
.github/workflows/pr-claude-review.yaml
vendored
@@ -17,9 +17,39 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
claude-review:
|
||||
wait-for-ci:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == 'claude-review'
|
||||
outputs:
|
||||
should-proceed: ${{ steps.check-status.outputs.proceed }}
|
||||
steps:
|
||||
- name: Wait for other CI checks
|
||||
uses: lewagon/wait-on-check-action@e106e5c43e8ca1edea6383a39a01c5ca495fd812
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
check-regexp: '^(lint-and-format|test|playwright-tests)'
|
||||
wait-interval: 30
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check if we should proceed
|
||||
id: check-status
|
||||
run: |
|
||||
CHECK_RUNS=$(gh api repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs --jq '.check_runs[] | select(.name | test("lint-and-format")) | {name, conclusion}')
|
||||
|
||||
if echo "$CHECK_RUNS" | grep -Eq '"conclusion": "(failure|cancelled|timed_out|action_required)"'; then
|
||||
echo "Some CI checks failed - skipping Claude review"
|
||||
echo "proceed=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "All CI checks passed - proceeding with Claude review"
|
||||
echo "proceed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
claude-review:
|
||||
needs: wait-for-ci
|
||||
if: needs.wait-for-ci.outputs.should-proceed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
104
.github/workflows/pr-size-report.yaml
vendored
@@ -1,104 +0,0 @@
|
||||
name: "PR: Size Report"
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['CI: Size Data']
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to report on'
|
||||
required: true
|
||||
type: number
|
||||
run_id:
|
||||
description: 'Size data workflow run ID'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
|
||||
(
|
||||
(github.event_name == 'workflow_run' &&
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success') ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
)
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
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
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
with:
|
||||
name: size-data
|
||||
run_id: ${{ github.event_name == 'workflow_dispatch' && inputs.run_id || github.event.workflow_run.id }}
|
||||
path: temp/size
|
||||
|
||||
- name: Set PR number
|
||||
id: pr-number
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
echo "content=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "content=$(cat temp/size/number.txt)" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Set base branch
|
||||
id: pr-base
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
echo "content=main" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "content=$(cat temp/size/base.txt)" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Download previous size data
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
with:
|
||||
branch: ${{ steps.pr-base.outputs.content }}
|
||||
workflow: ci-size-data.yaml
|
||||
event: push
|
||||
name: size-data
|
||||
path: temp/size-prev
|
||||
if_no_artifact_found: warn
|
||||
|
||||
- name: Generate size report
|
||||
run: node scripts/size-report.js > size-report.md
|
||||
|
||||
- name: Read size report
|
||||
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:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
number: ${{ steps.pr-number.outputs.content }}
|
||||
body: |
|
||||
${{ steps.size-report.outputs.content }}
|
||||
<!-- COMFYUI_FRONTEND_SIZE -->
|
||||
body-include: '<!-- COMFYUI_FRONTEND_SIZE -->'
|
||||
@@ -12,11 +12,11 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
( github.event_name == 'pull_request' && github.event.label.name == 'New Browser Test Expectations' ) ||
|
||||
( github.event.issue.pull_request &&
|
||||
( github.event.issue.pull_request &&
|
||||
github.event_name == 'issue_comment' &&
|
||||
(
|
||||
github.event.comment.author_association == 'OWNER' ||
|
||||
@@ -24,25 +24,12 @@ jobs:
|
||||
github.event.comment.author_association == 'COLLABORATOR'
|
||||
) &&
|
||||
startsWith(github.event.comment.body, '/update-playwright') )
|
||||
outputs:
|
||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
||||
pr-number: ${{ steps.pr-info.outputs.pr-number }}
|
||||
branch: ${{ steps.pr-info.outputs.branch }}
|
||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||
steps:
|
||||
- name: Get PR info
|
||||
id: pr-info
|
||||
run: |
|
||||
echo "pr-number=${{ github.event.number || github.event.issue.number }}" >> $GITHUB_OUTPUT
|
||||
echo "branch=$(gh pr view ${{ github.event.number || github.event.issue.number }} --repo ${{ github.repository }} --json headRefName --jq '.headRefName')" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Find Update Comment
|
||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
|
||||
id: "find-update-comment"
|
||||
with:
|
||||
issue-number: ${{ steps.pr-info.outputs.pr-number }}
|
||||
issue-number: ${{ github.event.number || github.event.issue.number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: "Updating Playwright Expectations"
|
||||
|
||||
@@ -50,260 +37,72 @@ jobs:
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
|
||||
with:
|
||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||
issue-number: ${{ steps.pr-info.outputs.pr-number }}
|
||||
issue-number: ${{ github.event.number || github.event.issue.number }}
|
||||
body: |
|
||||
Updating Playwright Expectations
|
||||
edit-mode: replace
|
||||
reactions: eyes
|
||||
|
||||
- name: Checkout repository
|
||||
- name: Get Branch SHA
|
||||
id: "get-branch"
|
||||
run: echo ::set-output name=branch::$(gh pr view $PR_NO --repo $REPO --json headRefName --jq '.headRefName')
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NO: ${{ github.event.number || github.event.issue.number }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Initial Checkout
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ steps.pr-info.outputs.branch }}
|
||||
- name: Setup frontend
|
||||
ref: ${{ steps.get-branch.outputs.branch }}
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
include_build_step: true
|
||||
# Save expensive build artifacts (Python env, built frontend, node_modules)
|
||||
# Source code will be checked out fresh in sharded jobs
|
||||
- name: Generate cache key
|
||||
id: cache-key
|
||||
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
|
||||
- name: Save cache
|
||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
|
||||
with:
|
||||
path: |
|
||||
ComfyUI
|
||||
dist
|
||||
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
|
||||
|
||||
# Sharded snapshot updates
|
||||
update-snapshots-sharded:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shardIndex: [1, 2, 3, 4]
|
||||
shardTotal: [4]
|
||||
steps:
|
||||
# Checkout source code fresh (not cached)
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.branch }}
|
||||
|
||||
# Restore expensive build artifacts from setup job
|
||||
- name: Restore cached artifacts
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
|
||||
with:
|
||||
fail-on-cache-miss: true
|
||||
path: |
|
||||
ComfyUI
|
||||
dist
|
||||
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
|
||||
|
||||
- name: Setup ComfyUI server (from cache)
|
||||
- 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
|
||||
|
||||
# Run sharded tests with snapshot updates
|
||||
- name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
|
||||
- name: Run Playwright tests and update snapshots
|
||||
id: playwright-tests
|
||||
run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
run: pnpm exec playwright test --update-snapshots
|
||||
continue-on-error: true
|
||||
|
||||
# Identify and stage only changed snapshot files
|
||||
- name: Stage changed snapshot files
|
||||
id: changed-snapshots
|
||||
run: |
|
||||
echo "=========================================="
|
||||
echo "STAGING CHANGED SNAPSHOTS (Shard ${{ matrix.shardIndex }})"
|
||||
echo "=========================================="
|
||||
|
||||
# Get list of changed snapshot files
|
||||
changed_files=$(git diff --name-only browser_tests/ 2>/dev/null | grep -E '\-snapshots/' || echo "")
|
||||
|
||||
if [ -z "$changed_files" ]; then
|
||||
echo "No snapshot changes in this shard"
|
||||
echo "has-changes=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "✓ Found changed files:"
|
||||
echo "$changed_files"
|
||||
file_count=$(echo "$changed_files" | wc -l)
|
||||
echo "Count: $file_count"
|
||||
echo "has-changes=true" >> $GITHUB_OUTPUT
|
||||
echo ""
|
||||
|
||||
# Create staging directory
|
||||
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
|
||||
# Remove 'browser_tests/' prefix
|
||||
file_without_prefix="${file#browser_tests/}"
|
||||
# Create parent directories
|
||||
mkdir -p "/tmp/changed_snapshots_shard/$(dirname "$file_without_prefix")"
|
||||
# Copy file
|
||||
cp "$file" "/tmp/changed_snapshots_shard/$file_without_prefix"
|
||||
echo " → $file_without_prefix"
|
||||
done <<< "$changed_files"
|
||||
|
||||
echo ""
|
||||
echo "Staged files for upload:"
|
||||
find /tmp/changed_snapshots_shard -type f
|
||||
|
||||
# Upload ONLY the changed files from this shard
|
||||
- name: Upload changed snapshots
|
||||
uses: actions/upload-artifact@v4
|
||||
if: steps.changed-snapshots.outputs.has-changes == 'true'
|
||||
with:
|
||||
name: snapshots-shard-${{ matrix.shardIndex }}
|
||||
path: /tmp/changed_snapshots_shard/
|
||||
retention-days: 1
|
||||
|
||||
- name: Upload test report
|
||||
uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-shard-${{ matrix.shardIndex }}
|
||||
name: playwright-report
|
||||
path: ./playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
# Merge snapshots and commit
|
||||
merge-and-commit:
|
||||
needs: [setup, update-snapshots-sharded]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.branch }}
|
||||
|
||||
# Download all changed snapshot files from shards
|
||||
- name: Download snapshot artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: snapshots-shard-*
|
||||
path: ./downloaded-snapshots
|
||||
merge-multiple: false
|
||||
|
||||
- name: List downloaded files
|
||||
- name: Debugging info
|
||||
run: |
|
||||
echo "=========================================="
|
||||
echo "DOWNLOADED SNAPSHOT FILES"
|
||||
echo "=========================================="
|
||||
find ./downloaded-snapshots -type f
|
||||
echo ""
|
||||
echo "Total files: $(find ./downloaded-snapshots -type f | wc -l)"
|
||||
|
||||
# Merge only the changed files into browser_tests
|
||||
- name: Merge changed snapshots
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "=========================================="
|
||||
echo "MERGING CHANGED SNAPSHOTS"
|
||||
echo "=========================================="
|
||||
|
||||
# Verify target directory exists
|
||||
if [ ! -d "browser_tests" ]; then
|
||||
echo "::error::Target directory 'browser_tests' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
merged_count=0
|
||||
|
||||
# For each shard's changed files, copy them directly
|
||||
for shard_dir in ./downloaded-snapshots/snapshots-shard-*/; do
|
||||
if [ ! -d "$shard_dir" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
shard_name=$(basename "$shard_dir")
|
||||
file_count=$(find "$shard_dir" -type f | wc -l)
|
||||
|
||||
if [ "$file_count" -eq 0 ]; then
|
||||
echo " $shard_name: no files"
|
||||
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 "MERGE COMPLETE"
|
||||
echo "=========================================="
|
||||
echo "Shards merged: $merged_count"
|
||||
|
||||
- name: Show changes
|
||||
run: |
|
||||
echo "=========================================="
|
||||
echo "CHANGES SUMMARY"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Changed files in browser_tests:"
|
||||
git diff --name-only browser_tests/ | head -20 || echo "No changes"
|
||||
echo ""
|
||||
echo "Total changes:"
|
||||
git diff --name-only browser_tests/ | wc -l || echo "0"
|
||||
|
||||
echo "PR: ${{ github.event.issue.number }}"
|
||||
echo "Branch: ${{ steps.get-branch.outputs.branch }}"
|
||||
git status
|
||||
- name: Commit updated expectations
|
||||
id: commit
|
||||
run: |
|
||||
git config --global user.name 'github-actions'
|
||||
git config --global user.email 'github-actions@github.com'
|
||||
|
||||
if git diff --quiet browser_tests/; then
|
||||
git add browser_tests
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
echo "has-changes=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
else
|
||||
git commit -m "[automated] Update test expectations"
|
||||
git push origin ${{ steps.get-branch.outputs.branch }}
|
||||
fi
|
||||
|
||||
echo "=========================================="
|
||||
echo "COMMITTING CHANGES"
|
||||
echo "=========================================="
|
||||
|
||||
echo "has-changes=true" >> $GITHUB_OUTPUT
|
||||
|
||||
git add browser_tests/
|
||||
git commit -m "[automated] Update test expectations"
|
||||
|
||||
echo "Pushing to ${{ needs.setup.outputs.branch }}..."
|
||||
git push origin ${{ needs.setup.outputs.branch }}
|
||||
|
||||
echo "✓ Commit and push successful"
|
||||
|
||||
- name: Add Done Reaction
|
||||
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'
|
||||
with:
|
||||
comment-id: ${{ needs.setup.outputs.comment-id }}
|
||||
issue-number: ${{ needs.setup.outputs.pr-number }}
|
||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.number || github.event.issue.number }}
|
||||
reactions: +1
|
||||
reactions-edit-mode: replace
|
||||
|
||||
- name: Remove New Browser Test Expectations label
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
run: gh pr edit ${{ needs.setup.outputs.pr-number }} --remove-label "New Browser Test Expectations"
|
||||
run: gh pr edit ${{ github.event.pull_request.number }} --remove-label "New Browser Test Expectations"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
15
.github/workflows/publish-desktop-ui.yaml
vendored
@@ -44,7 +44,6 @@ jobs:
|
||||
contents: read
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||
ENABLE_MINIFY: 'true'
|
||||
steps:
|
||||
- name: Validate inputs
|
||||
env:
|
||||
@@ -161,6 +160,20 @@ jobs:
|
||||
echo "publish_dir=$PUBLISH_DIR" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Pack (preview only)
|
||||
shell: bash
|
||||
working-directory: ${{ steps.pkg.outputs.publish_dir }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npm pack --json | tee pack-result.json
|
||||
|
||||
- name: Upload package tarball artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: desktop-ui-npm-tarball-${{ inputs.version }}
|
||||
path: ${{ steps.pkg.outputs.publish_dir }}/*.tgz
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Check if version already on npm
|
||||
id: check_npm
|
||||
env:
|
||||
|
||||
118
.github/workflows/release-branch-create.yaml
vendored
@@ -69,9 +69,6 @@ jobs:
|
||||
echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "prev_minor=$PREV_MINOR" >> $GITHUB_OUTPUT
|
||||
|
||||
BASE_COMMIT=$(git rev-parse HEAD)
|
||||
echo "base_commit=$BASE_COMMIT" >> $GITHUB_OUTPUT
|
||||
|
||||
# Get previous major version for comparison
|
||||
PREV_MAJOR=$(echo $PREV_VERSION | cut -d. -f1)
|
||||
|
||||
@@ -90,13 +87,13 @@ jobs:
|
||||
elif [[ "$MAJOR" -gt "$PREV_MAJOR" && "$MINOR" == "0" && "$PATCH" == "0" ]]; then
|
||||
# Major version bump (e.g., 1.99.x → 2.0.0)
|
||||
echo "is_minor_bump=true" >> $GITHUB_OUTPUT
|
||||
BRANCH_BASE="${PREV_MAJOR}.${PREV_MINOR}"
|
||||
echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT
|
||||
BRANCH_NAME="core/${PREV_MAJOR}.${PREV_MINOR}"
|
||||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
elif [[ "$MAJOR" == "$PREV_MAJOR" && "$MINOR" -gt "$PREV_MINOR" && "$PATCH" == "0" ]]; then
|
||||
# Minor version bump (e.g., 1.23.x → 1.24.0)
|
||||
echo "is_minor_bump=true" >> $GITHUB_OUTPUT
|
||||
BRANCH_BASE="${MAJOR}.${PREV_MINOR}"
|
||||
echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT
|
||||
BRANCH_NAME="core/${MAJOR}.${PREV_MINOR}"
|
||||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "is_minor_bump=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
@@ -104,97 +101,64 @@ jobs:
|
||||
# Return to main branch
|
||||
git checkout main
|
||||
|
||||
- name: Create release branches
|
||||
id: create_branches
|
||||
- name: Create release branch
|
||||
if: steps.check_version.outputs.is_minor_bump == 'true'
|
||||
run: |
|
||||
BRANCH_BASE="${{ steps.check_version.outputs.branch_base }}"
|
||||
PREV_VERSION="${{ steps.check_version.outputs.prev_version }}"
|
||||
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
|
||||
|
||||
if [[ -z "$BRANCH_BASE" ]]; then
|
||||
echo "::error::Branch base not set; unable to determine release branches"
|
||||
exit 1
|
||||
# Check if branch already exists
|
||||
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
|
||||
echo "⚠️ Branch $BRANCH_NAME already exists, skipping creation"
|
||||
echo "branch_exists=true" >> $GITHUB_ENV
|
||||
exit 0
|
||||
else
|
||||
echo "branch_exists=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
BASE_COMMIT="${{ steps.check_version.outputs.base_commit }}"
|
||||
# Create branch from the commit BEFORE the version bump
|
||||
# This ensures the release branch has the previous minor version
|
||||
git checkout -b "$BRANCH_NAME" HEAD^1
|
||||
|
||||
if [[ -z "$BASE_COMMIT" ]]; then
|
||||
echo "::error::Base commit not provided; cannot create release branches"
|
||||
exit 1
|
||||
fi
|
||||
# Push the new branch
|
||||
git push origin "$BRANCH_NAME"
|
||||
|
||||
RESULTS_FILE=$(mktemp)
|
||||
trap 'rm -f "$RESULTS_FILE"' EXIT
|
||||
echo "✅ Created release branch: $BRANCH_NAME"
|
||||
echo "This branch is now in feature freeze and will only receive:"
|
||||
echo "- Bug fixes"
|
||||
echo "- Critical security patches"
|
||||
echo "- Documentation updates"
|
||||
|
||||
for PREFIX in core cloud; do
|
||||
BRANCH_NAME="${PREFIX}/${BRANCH_BASE}"
|
||||
|
||||
if git ls-remote --exit-code --heads origin \
|
||||
"$BRANCH_NAME" >/dev/null 2>&1; then
|
||||
echo "⚠️ Branch $BRANCH_NAME already exists"
|
||||
echo "ℹ️ Skipping creation for $BRANCH_NAME"
|
||||
STATUS="exists"
|
||||
else
|
||||
# Create branch from the commit BEFORE the version bump
|
||||
if ! git push origin "$BASE_COMMIT:refs/heads/$BRANCH_NAME"; then
|
||||
echo "::error::Failed to push release branch $BRANCH_NAME"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Created release branch: $BRANCH_NAME"
|
||||
STATUS="created"
|
||||
fi
|
||||
|
||||
echo "$BRANCH_NAME|$STATUS|$PREV_VERSION" >> "$RESULTS_FILE"
|
||||
done
|
||||
|
||||
{
|
||||
echo "results<<'EOF'"
|
||||
cat "$RESULTS_FILE"
|
||||
echo "EOF"
|
||||
} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Post summary
|
||||
if: steps.check_version.outputs.is_minor_bump == 'true'
|
||||
run: |
|
||||
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
|
||||
PREV_VERSION="${{ steps.check_version.outputs.prev_version }}"
|
||||
CURRENT_VERSION="${{ steps.check_version.outputs.current_version }}"
|
||||
RESULTS="${{ steps.create_branches.outputs.results }}"
|
||||
|
||||
if [[ -z "$RESULTS" ]]; then
|
||||
if [[ "${{ env.branch_exists }}" == "true" ]]; then
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
## 🌿 Release Branch Summary
|
||||
## 🌿 Release Branch Already Exists
|
||||
|
||||
Release branch creation skipped; no eligible branches were found.
|
||||
The release branch for the previous minor version already exists:
|
||||
EOF
|
||||
else
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
## 🌿 Release Branch Created
|
||||
|
||||
A new release branch has been created for the previous minor version:
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
## 🌿 Release Branch Summary
|
||||
|
||||
- **Branch**: \`$BRANCH_NAME\`
|
||||
- **Version**: \`$PREV_VERSION\` (feature frozen)
|
||||
- **Main branch**: \`$CURRENT_VERSION\` (active development)
|
||||
|
||||
### Branch Status
|
||||
EOF
|
||||
|
||||
while IFS='|' read -r BRANCH STATUS PREV_VERSION; do
|
||||
if [[ "$STATUS" == "created" ]]; then
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
|
||||
- \`$BRANCH\` created from version \`$PREV_VERSION\`
|
||||
EOF
|
||||
else
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
|
||||
- \`$BRANCH\` already existed (based on version \`$PREV_VERSION\`)
|
||||
EOF
|
||||
fi
|
||||
done <<< "$RESULTS"
|
||||
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
|
||||
### Branch Policy
|
||||
|
||||
Release branches are feature-frozen and only accept:
|
||||
The \`$BRANCH_NAME\` branch is now in **feature freeze** and will only accept:
|
||||
- 🐛 Bug fixes
|
||||
- 🔒 Security patches
|
||||
- 📚 Documentation updates
|
||||
@@ -203,9 +167,9 @@ jobs:
|
||||
|
||||
### Backporting Changes
|
||||
|
||||
To backport a fix:
|
||||
To backport a fix to this release branch:
|
||||
1. Create your fix on \`main\` first
|
||||
2. Cherry-pick to the target release branch
|
||||
3. Create a PR targeting that branch
|
||||
4. Apply the matching \`core/x.y\` or \`cloud/x.y\` label
|
||||
2. Cherry-pick to \`$BRANCH_NAME\`
|
||||
3. Create a PR targeting \`$BRANCH_NAME\`
|
||||
4. Use the \`Release\` label on the PR
|
||||
EOF
|
||||
|
||||
11
.github/workflows/release-draft-create.yaml
vendored
@@ -28,6 +28,16 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
tsconfig.tsbuildinfo
|
||||
key: release-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
release-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Get current version
|
||||
id: current_version
|
||||
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||
@@ -45,7 +55,6 @@ jobs:
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
|
||||
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
|
||||
ENABLE_MINIFY: 'true'
|
||||
USE_PROD_CONFIG: 'true'
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
12
.github/workflows/release-pypi-dev.yaml
vendored
@@ -25,6 +25,17 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
dist
|
||||
tsconfig.tsbuildinfo
|
||||
key: dev-release-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
dev-release-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Get current version
|
||||
id: current_version
|
||||
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||
@@ -33,7 +44,6 @@ jobs:
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
|
||||
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
|
||||
ENABLE_MINIFY: 'true'
|
||||
USE_PROD_CONFIG: 'true'
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
28
.github/workflows/release-version-bump.yaml
vendored
@@ -15,11 +15,6 @@ on:
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
branch:
|
||||
description: 'Base branch to bump (e.g., main, core/1.29, core/1.30)'
|
||||
required: true
|
||||
default: 'main'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
bump-version:
|
||||
@@ -31,24 +26,6 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Validate branch exists
|
||||
run: |
|
||||
BRANCH="${{ github.event.inputs.branch }}"
|
||||
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 ""
|
||||
echo "Available core branches:"
|
||||
git branch -r | grep 'origin/core/' | sed 's/.*origin\// - /' || echo " (none found)"
|
||||
echo ""
|
||||
echo "Main branch:"
|
||||
echo " - main"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Branch '$BRANCH' exists"
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -59,6 +36,7 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Bump version
|
||||
id: bump-version
|
||||
@@ -81,9 +59,7 @@ jobs:
|
||||
title: ${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
body: |
|
||||
${{ steps.capitalised.outputs.capitalised }} version increment to ${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
|
||||
**Base branch:** `${{ github.event.inputs.branch }}`
|
||||
branch: version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
base: ${{ github.event.inputs.branch }}
|
||||
base: main
|
||||
labels: |
|
||||
Release
|
||||
|
||||
26
.github/workflows/version-bump-desktop-ui.yaml
vendored
@@ -14,11 +14,6 @@ on:
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
branch:
|
||||
description: 'Base branch to bump (e.g., main, core/1.29, core/1.30)'
|
||||
required: true
|
||||
default: 'main'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
bump-version-desktop-ui:
|
||||
@@ -31,25 +26,8 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Validate branch exists
|
||||
run: |
|
||||
BRANCH="${{ github.event.inputs.branch }}"
|
||||
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 ""
|
||||
echo "Available core branches:"
|
||||
git branch -r | grep 'origin/core/' | sed 's/.*origin\// - /' || echo " (none found)"
|
||||
echo ""
|
||||
echo "Main branch:"
|
||||
echo " - main"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Branch '$BRANCH' exists"
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
@@ -86,10 +64,8 @@ jobs:
|
||||
title: desktop-ui ${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
body: |
|
||||
${{ steps.capitalised.outputs.capitalised }} version increment for @comfyorg/desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
|
||||
**Base branch:** `${{ github.event.inputs.branch }}`
|
||||
branch: desktop-ui-version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
base: ${{ github.event.inputs.branch }}
|
||||
base: main
|
||||
labels: |
|
||||
Release
|
||||
|
||||
|
||||
144
.github/workflows/weekly-docs-check.yaml
vendored
@@ -1,144 +0,0 @@
|
||||
name: "Weekly Documentation Check"
|
||||
description: "Automated weekly documentation accuracy check and update via Claude"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run every Monday at 9 AM UTC
|
||||
- cron: '0 9 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
docs-check:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: main
|
||||
|
||||
- name: Install pnpm
|
||||
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 for analysis tools
|
||||
run: |
|
||||
# Check if packages are already available locally
|
||||
if ! pnpm list typescript @vue/compiler-sfc >/dev/null 2>&1; then
|
||||
echo "Installing TypeScript and Vue compiler globally..."
|
||||
pnpm install -g typescript @vue/compiler-sfc
|
||||
else
|
||||
echo "TypeScript and Vue compiler already available locally"
|
||||
fi
|
||||
|
||||
- name: Run Claude Documentation Review
|
||||
uses: anthropics/claude-code-action@v1.0.6
|
||||
with:
|
||||
prompt: |
|
||||
Is all documentation still 100% accurate?
|
||||
|
||||
INSTRUCTIONS:
|
||||
1. Fact-check all documentation against the current codebase
|
||||
2. Look for:
|
||||
- Outdated API references
|
||||
- Deprecated functions or components still documented
|
||||
- Missing documentation for new features
|
||||
- Incorrect code examples
|
||||
- Broken internal references
|
||||
- Configuration examples that no longer work
|
||||
- Documentation that contradicts actual implementation
|
||||
3. Update any inaccurate or outdated documentation
|
||||
4. Add documentation for significant undocumented features
|
||||
5. Ensure all code examples are valid and tested against current code
|
||||
|
||||
Focus on these key areas:
|
||||
- docs/**/*.md (all documentation files)
|
||||
- CLAUDE.md (project guidelines)
|
||||
- README.md files throughout the repository
|
||||
- .claude/commands/*.md (Claude command documentation)
|
||||
|
||||
Make changes directly to the documentation files as needed.
|
||||
DO NOT modify any source code files unless absolutely necessary for documentation accuracy.
|
||||
|
||||
After making all changes, create a comprehensive PR message summary:
|
||||
1. Write a detailed PR body to /tmp/pr-body-${{ github.run_id }}.md in markdown format
|
||||
2. Include:
|
||||
- ## Summary section with bullet points of what was changed
|
||||
- ## Changes Made section with details organized by category
|
||||
- ## Review Notes section with any important context
|
||||
3. Be specific about which files were updated and why
|
||||
4. If no changes were needed, write a brief message stating documentation is up to date
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: "--max-turns 256 --allowedTools 'Bash(git status),Bash(git diff),Bash(git log),Bash(pnpm:*),Bash(npm:*),Bash(node:*),Bash(tsc:*),Bash(echo:*),Read,Write,Edit,Glob,Grep'"
|
||||
continue-on-error: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
echo "No documentation changes needed"
|
||||
else
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
echo "Documentation changes detected"
|
||||
fi
|
||||
|
||||
- name: Create default PR body if not generated
|
||||
if: steps.check_changes.outputs.has_changes == 'true'
|
||||
run: |
|
||||
if [ ! -f /tmp/pr-body-${{ github.run_id }}.md ]; then
|
||||
cat > /tmp/pr-body-${{ github.run_id }}.md <<'EOF'
|
||||
## Automated Documentation Review
|
||||
|
||||
This PR contains documentation updates identified by the weekly automated review.
|
||||
|
||||
### Review Process
|
||||
- Automated fact-checking against current codebase
|
||||
- Verification of code examples and API references
|
||||
- Detection of outdated or missing documentation
|
||||
|
||||
### What was checked
|
||||
- All markdown documentation in `docs/`
|
||||
- Project guidelines in `CLAUDE.md`
|
||||
- README files throughout the repository
|
||||
- Claude command documentation in `.claude/commands/`
|
||||
|
||||
**Note**: This is an automated PR. Please review all changes carefully before merging.
|
||||
|
||||
🤖 Generated by weekly documentation check workflow
|
||||
EOF
|
||||
fi
|
||||
|
||||
- name: Create or Update Pull Request
|
||||
if: steps.check_changes.outputs.has_changes == 'true'
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.PR_GH_TOKEN }}
|
||||
commit-message: 'docs: weekly documentation accuracy update'
|
||||
branch: docs/weekly-update
|
||||
delete-branch: true
|
||||
title: 'docs: Weekly Documentation Update'
|
||||
body-path: /tmp/pr-body-${{ github.run_id }}.md
|
||||
labels: |
|
||||
documentation
|
||||
automated
|
||||
draft: true
|
||||
6
.gitignore
vendored
@@ -18,7 +18,6 @@ yarn.lock
|
||||
.stylelintcache
|
||||
|
||||
node_modules
|
||||
.pnpm-store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
@@ -79,7 +78,7 @@ templates_repo/
|
||||
vite.config.mts.timestamp-*.mjs
|
||||
|
||||
# Linux core dumps
|
||||
/core
|
||||
./core
|
||||
|
||||
*storybook.log
|
||||
storybook-static
|
||||
@@ -93,6 +92,3 @@ storybook-static
|
||||
.github/instructions/nx.instructions.md
|
||||
vite.config.*.timestamp*
|
||||
vitest.config.*.timestamp*
|
||||
|
||||
# Weekly docs check output
|
||||
/output.txt
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Run Knip with cache via package script
|
||||
pnpm knip 1>&2
|
||||
pnpm knip
|
||||
|
||||
|
||||
@@ -7,5 +7,12 @@
|
||||
"importOrder": ["^@core/(.*)$", "<THIRD_PARTY_MODULES>", "^@/(.*)$", "^[./]"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"plugins": ["@prettier/plugin-oxc", "@trivago/prettier-plugin-sort-imports"]
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.{js,cjs,mjs,ts,cts,mts,tsx,vue}",
|
||||
"options": {
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -62,11 +62,6 @@ Key Nx features:
|
||||
|
||||
## Project Philosophy
|
||||
|
||||
- Follow good software engineering principles
|
||||
- YAGNI
|
||||
- AHA
|
||||
- DRY
|
||||
- SOLID
|
||||
- Clean, stable public APIs
|
||||
- Domain-driven design
|
||||
- Thousands of users and extensions
|
||||
|
||||
@@ -43,7 +43,7 @@ Have another idea? Drop into Discord or open an issue, and let's chat!
|
||||
```
|
||||
|
||||
3. Configure environment (optional):
|
||||
Create a `.env` file in the project root based on the provided [.env_example](.env_example) file.
|
||||
Create a `.env` file in the project root based on the provided [.env.example](.env.example) file.
|
||||
|
||||
**Note about ports**: By default, the dev server expects the ComfyUI backend at `localhost:8188`. If your ComfyUI instance runs on a different port, update this in your `.env` file.
|
||||
|
||||
@@ -325,4 +325,4 @@ If you have questions about contributing:
|
||||
- Ask in our [Discord](https://discord.com/invite/comfyorg)
|
||||
- Open a new issue for clarification
|
||||
|
||||
Thank you for contributing to ComfyUI Frontend!
|
||||
Thank you for contributing to ComfyUI Frontend!
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>ComfyUI</title>
|
||||
<title>ComfyUI Desktop</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@comfyorg/desktop-ui",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"nx": {
|
||||
"tags": [
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div
|
||||
class="mx-auto grid h-[40rem] w-full max-w-3xl grid-rows-[1fr_auto_auto_1fr] select-none"
|
||||
class="grid grid-rows-[1fr_auto_auto_1fr] w-full max-w-3xl mx-auto h-[40rem] select-none"
|
||||
>
|
||||
<h2 class="text-center font-inter text-3xl font-bold text-neutral-100">
|
||||
<h2 class="font-inter font-bold text-3xl text-neutral-100 text-center">
|
||||
{{ $t('install.gpuPicker.title') }}
|
||||
</h2>
|
||||
|
||||
<!-- GPU Selection buttons - takes up remaining space and centers content -->
|
||||
<div class="flex flex-1 items-center justify-center gap-8">
|
||||
<div class="flex-1 flex gap-8 justify-center items-center">
|
||||
<!-- Apple Metal / NVIDIA -->
|
||||
<HardwareOption
|
||||
v-if="platform === 'darwin'"
|
||||
:image-path="'./assets/images/apple-mps-logo.png'"
|
||||
:image-path="'/assets/images/apple-mps-logo.png'"
|
||||
placeholder-text="Apple Metal"
|
||||
subtitle="Apple Metal"
|
||||
:value="'mps'"
|
||||
@@ -21,7 +21,7 @@
|
||||
/>
|
||||
<HardwareOption
|
||||
v-else
|
||||
:image-path="'./assets/images/nvidia-logo-square.jpg'"
|
||||
:image-path="'/assets/images/nvidia-logo-square.jpg'"
|
||||
placeholder-text="NVIDIA"
|
||||
:subtitle="$t('install.gpuPicker.nvidiaSubtitle')"
|
||||
:value="'nvidia'"
|
||||
@@ -47,17 +47,17 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="h-16 px-24 pt-12">
|
||||
<div class="pt-12 px-24 h-16">
|
||||
<div v-show="showRecommendedBadge" class="flex items-center gap-2">
|
||||
<Tag
|
||||
:value="$t('install.gpuPicker.recommended')"
|
||||
class="rounded-full bg-neutral-300 px-2 py-[1px] text-sm font-bold text-neutral-900"
|
||||
class="bg-neutral-300 text-neutral-900 rounded-full text-sm font-bold px-2 py-[1px]"
|
||||
/>
|
||||
<i class="icon-[lucide--badge-check] text-lg text-neutral-300" />
|
||||
<i class="icon-[lucide--badge-check] text-neutral-300 text-lg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-24 text-neutral-300">
|
||||
<div class="text-neutral-300 px-24">
|
||||
<p v-show="descriptionText" class="leading-relaxed">
|
||||
{{ descriptionText }}
|
||||
</p>
|
||||
|
||||
@@ -1,163 +1,67 @@
|
||||
// Import only English locale eagerly as the default/fallback
|
||||
// ESLint cannot statically resolve dynamic imports with path aliases (@frontend-locales/*),
|
||||
// but these are properly configured in tsconfig.json and resolved by Vite at build time.
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import arCommands from '@frontend-locales/ar/commands.json' with { type: 'json' }
|
||||
import ar from '@frontend-locales/ar/main.json' with { type: 'json' }
|
||||
import arNodes from '@frontend-locales/ar/nodeDefs.json' with { type: 'json' }
|
||||
import arSettings from '@frontend-locales/ar/settings.json' with { type: 'json' }
|
||||
import enCommands from '@frontend-locales/en/commands.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import en from '@frontend-locales/en/main.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enNodes from '@frontend-locales/en/nodeDefs.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enSettings from '@frontend-locales/en/settings.json' with { type: 'json' }
|
||||
import esCommands from '@frontend-locales/es/commands.json' with { type: 'json' }
|
||||
import es from '@frontend-locales/es/main.json' with { type: 'json' }
|
||||
import esNodes from '@frontend-locales/es/nodeDefs.json' with { type: 'json' }
|
||||
import esSettings from '@frontend-locales/es/settings.json' with { type: 'json' }
|
||||
import frCommands from '@frontend-locales/fr/commands.json' with { type: 'json' }
|
||||
import fr from '@frontend-locales/fr/main.json' with { type: 'json' }
|
||||
import frNodes from '@frontend-locales/fr/nodeDefs.json' with { type: 'json' }
|
||||
import frSettings from '@frontend-locales/fr/settings.json' with { type: 'json' }
|
||||
import jaCommands from '@frontend-locales/ja/commands.json' with { type: 'json' }
|
||||
import ja from '@frontend-locales/ja/main.json' with { type: 'json' }
|
||||
import jaNodes from '@frontend-locales/ja/nodeDefs.json' with { type: 'json' }
|
||||
import jaSettings from '@frontend-locales/ja/settings.json' with { type: 'json' }
|
||||
import koCommands from '@frontend-locales/ko/commands.json' with { type: 'json' }
|
||||
import ko from '@frontend-locales/ko/main.json' with { type: 'json' }
|
||||
import koNodes from '@frontend-locales/ko/nodeDefs.json' with { type: 'json' }
|
||||
import koSettings from '@frontend-locales/ko/settings.json' with { type: 'json' }
|
||||
import ruCommands from '@frontend-locales/ru/commands.json' with { type: 'json' }
|
||||
import ru from '@frontend-locales/ru/main.json' with { type: 'json' }
|
||||
import ruNodes from '@frontend-locales/ru/nodeDefs.json' with { type: 'json' }
|
||||
import ruSettings from '@frontend-locales/ru/settings.json' with { type: 'json' }
|
||||
import trCommands from '@frontend-locales/tr/commands.json' with { type: 'json' }
|
||||
import tr from '@frontend-locales/tr/main.json' with { type: 'json' }
|
||||
import trNodes from '@frontend-locales/tr/nodeDefs.json' with { type: 'json' }
|
||||
import trSettings from '@frontend-locales/tr/settings.json' with { type: 'json' }
|
||||
import zhTWCommands from '@frontend-locales/zh-TW/commands.json' with { type: 'json' }
|
||||
import zhTW from '@frontend-locales/zh-TW/main.json' with { type: 'json' }
|
||||
import zhTWNodes from '@frontend-locales/zh-TW/nodeDefs.json' with { type: 'json' }
|
||||
import zhTWSettings from '@frontend-locales/zh-TW/settings.json' with { type: 'json' }
|
||||
import zhCommands from '@frontend-locales/zh/commands.json' with { type: 'json' }
|
||||
import zh from '@frontend-locales/zh/main.json' with { type: 'json' }
|
||||
import zhNodes from '@frontend-locales/zh/nodeDefs.json' with { type: 'json' }
|
||||
import zhSettings from '@frontend-locales/zh/settings.json' with { type: 'json' }
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
function buildLocale<
|
||||
M extends Record<string, unknown>,
|
||||
N extends Record<string, unknown>,
|
||||
C extends Record<string, unknown>,
|
||||
S extends Record<string, unknown>
|
||||
>(main: M, nodes: N, commands: C, settings: S) {
|
||||
function buildLocale<M, N, C, S>(main: M, nodes: N, commands: C, settings: S) {
|
||||
return {
|
||||
...main,
|
||||
nodeDefs: nodes,
|
||||
commands: commands,
|
||||
settings: settings
|
||||
} as M & { nodeDefs: N; commands: C; settings: S }
|
||||
}
|
||||
|
||||
// Locale loader map - dynamically import locales only when needed
|
||||
// ESLint cannot statically resolve these dynamic imports, but they are valid at build time
|
||||
/* eslint-disable import-x/no-unresolved */
|
||||
const localeLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/main.json'),
|
||||
es: () => import('@frontend-locales/es/main.json'),
|
||||
fr: () => import('@frontend-locales/fr/main.json'),
|
||||
ja: () => import('@frontend-locales/ja/main.json'),
|
||||
ko: () => import('@frontend-locales/ko/main.json'),
|
||||
ru: () => import('@frontend-locales/ru/main.json'),
|
||||
tr: () => import('@frontend-locales/tr/main.json'),
|
||||
zh: () => import('@frontend-locales/zh/main.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/main.json')
|
||||
}
|
||||
|
||||
const nodeDefsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/nodeDefs.json'),
|
||||
es: () => import('@frontend-locales/es/nodeDefs.json'),
|
||||
fr: () => import('@frontend-locales/fr/nodeDefs.json'),
|
||||
ja: () => import('@frontend-locales/ja/nodeDefs.json'),
|
||||
ko: () => import('@frontend-locales/ko/nodeDefs.json'),
|
||||
ru: () => import('@frontend-locales/ru/nodeDefs.json'),
|
||||
tr: () => import('@frontend-locales/tr/nodeDefs.json'),
|
||||
zh: () => import('@frontend-locales/zh/nodeDefs.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json')
|
||||
}
|
||||
|
||||
const commandsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/commands.json'),
|
||||
es: () => import('@frontend-locales/es/commands.json'),
|
||||
fr: () => import('@frontend-locales/fr/commands.json'),
|
||||
ja: () => import('@frontend-locales/ja/commands.json'),
|
||||
ko: () => import('@frontend-locales/ko/commands.json'),
|
||||
ru: () => import('@frontend-locales/ru/commands.json'),
|
||||
tr: () => import('@frontend-locales/tr/commands.json'),
|
||||
zh: () => import('@frontend-locales/zh/commands.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/commands.json')
|
||||
}
|
||||
|
||||
const settingsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/settings.json'),
|
||||
es: () => import('@frontend-locales/es/settings.json'),
|
||||
fr: () => import('@frontend-locales/fr/settings.json'),
|
||||
ja: () => import('@frontend-locales/ja/settings.json'),
|
||||
ko: () => import('@frontend-locales/ko/settings.json'),
|
||||
ru: () => import('@frontend-locales/ru/settings.json'),
|
||||
tr: () => import('@frontend-locales/tr/settings.json'),
|
||||
zh: () => import('@frontend-locales/zh/settings.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/settings.json')
|
||||
}
|
||||
|
||||
// Track which locales have been loaded
|
||||
const loadedLocales = new Set<string>(['en'])
|
||||
|
||||
// Track locales currently being loaded to prevent race conditions
|
||||
const loadingLocales = new Map<string, Promise<void>>()
|
||||
|
||||
/**
|
||||
* Dynamically load a locale and its associated files (nodeDefs, commands, settings)
|
||||
*/
|
||||
export async function loadLocale(locale: string): Promise<void> {
|
||||
if (loadedLocales.has(locale)) {
|
||||
return
|
||||
}
|
||||
|
||||
// If already loading, return the existing promise to prevent duplicate loads
|
||||
const existingLoad = loadingLocales.get(locale)
|
||||
if (existingLoad) {
|
||||
return existingLoad
|
||||
}
|
||||
|
||||
const loader = localeLoaders[locale]
|
||||
const nodeDefsLoader = nodeDefsLoaders[locale]
|
||||
const commandsLoader = commandsLoaders[locale]
|
||||
const settingsLoader = settingsLoaders[locale]
|
||||
|
||||
if (!loader || !nodeDefsLoader || !commandsLoader || !settingsLoader) {
|
||||
console.warn(`Locale "${locale}" is not supported`)
|
||||
return
|
||||
}
|
||||
|
||||
// Create and track the loading promise
|
||||
const loadPromise = (async () => {
|
||||
try {
|
||||
const [main, nodes, commands, settings] = await Promise.all([
|
||||
loader(),
|
||||
nodeDefsLoader(),
|
||||
commandsLoader(),
|
||||
settingsLoader()
|
||||
])
|
||||
|
||||
const messages = buildLocale(
|
||||
main.default,
|
||||
nodes.default,
|
||||
commands.default,
|
||||
settings.default
|
||||
)
|
||||
|
||||
i18n.global.setLocaleMessage(locale, messages as LocaleMessages)
|
||||
loadedLocales.add(locale)
|
||||
} catch (error) {
|
||||
console.error(`Failed to load locale "${locale}":`, error)
|
||||
throw error
|
||||
} finally {
|
||||
// Clean up the loading promise once complete
|
||||
loadingLocales.delete(locale)
|
||||
}
|
||||
})()
|
||||
|
||||
loadingLocales.set(locale, loadPromise)
|
||||
return loadPromise
|
||||
}
|
||||
|
||||
// Only include English in the initial bundle
|
||||
const messages = {
|
||||
en: buildLocale(en, enNodes, enCommands, enSettings)
|
||||
en: buildLocale(en, enNodes, enCommands, enSettings),
|
||||
zh: buildLocale(zh, zhNodes, zhCommands, zhSettings),
|
||||
'zh-TW': buildLocale(zhTW, zhTWNodes, zhTWCommands, zhTWSettings),
|
||||
ru: buildLocale(ru, ruNodes, ruCommands, ruSettings),
|
||||
ja: buildLocale(ja, jaNodes, jaCommands, jaSettings),
|
||||
ko: buildLocale(ko, koNodes, koCommands, koSettings),
|
||||
fr: buildLocale(fr, frNodes, frCommands, frSettings),
|
||||
es: buildLocale(es, esNodes, esCommands, esSettings),
|
||||
ar: buildLocale(ar, arNodes, arCommands, arSettings),
|
||||
tr: buildLocale(tr, trNodes, trCommands, trSettings)
|
||||
}
|
||||
|
||||
// Type for locale messages - inferred from the English locale structure
|
||||
type LocaleMessages = typeof messages.en
|
||||
|
||||
export const i18n = createI18n({
|
||||
// Must set `false`, as Vue I18n Legacy API is for Vue 2
|
||||
legacy: false,
|
||||
|
||||
@@ -66,6 +66,17 @@
|
||||
@click="troubleshoot"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<button
|
||||
v-if="!terminalVisible"
|
||||
class="text-sm text-neutral-500 hover:text-neutral-300 transition-colors flex items-center gap-2 mx-auto"
|
||||
@click="terminalVisible = true"
|
||||
>
|
||||
<i class="pi pi-search"></i>
|
||||
{{ $t('serverStart.showTerminal') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Terminal Output (positioned at bottom when manually toggled in error state) -->
|
||||
|
||||
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 82 KiB |
@@ -1657,8 +1657,7 @@ export const comfyPageFixture = base.extend<{
|
||||
'Comfy.userId': userId,
|
||||
// Set tutorial completed to true to avoid loading the tutorial workflow.
|
||||
'Comfy.TutorialCompleted': true,
|
||||
'Comfy.SnapToGrid.GridSize': testComfySnapToGridGridSize,
|
||||
'Comfy.VueNodes.AutoScaleLayout': false
|
||||
'Comfy.SnapToGrid.GridSize': testComfySnapToGridGridSize
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
||||
@@ -503,7 +503,7 @@ export class NodeReference {
|
||||
for (const position of clickPositions) {
|
||||
// Clear any selection first
|
||||
await this.comfyPage.canvas.click({
|
||||
position: { x: 250, y: 250 },
|
||||
position: { x: 50, y: 50 },
|
||||
force: true
|
||||
})
|
||||
await this.comfyPage.nextFrame()
|
||||
|
||||
@@ -13,7 +13,7 @@ export class ComfyActionbar {
|
||||
|
||||
async isDocked() {
|
||||
const className = await this.root.getAttribute('class')
|
||||
return className?.includes('static') ?? false
|
||||
return className?.includes('is-docked') ?? false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -301,9 +301,7 @@ test.describe('Settings', () => {
|
||||
})
|
||||
|
||||
test.describe('Support', () => {
|
||||
test('Should open external zendesk link with OSS tag', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Should open external zendesk link', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
const pagePromise = comfyPage.page.context().waitForEvent('page')
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Support'])
|
||||
@@ -311,10 +309,6 @@ test.describe('Support', () => {
|
||||
|
||||
await newPage.waitForLoadState('networkidle')
|
||||
await expect(newPage).toHaveURL(/.*support\.comfy\.org.*/)
|
||||
|
||||
const url = new URL(newPage.url())
|
||||
expect(url.searchParams.get('tf_42243568391700')).toBe('oss')
|
||||
|
||||
await newPage.close()
|
||||
})
|
||||
})
|
||||
|
||||
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 43 KiB |
@@ -1,28 +0,0 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Record Audio Node', () => {
|
||||
test('should add a record audio node and take a screenshot', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open the search box by double clicking on the canvas
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||
|
||||
// Search for and add the RecordAudio node
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('RecordAudio')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Verify the RecordAudio node was added
|
||||
const recordAudioNodes = await comfyPage.getNodeRefsByType('RecordAudio')
|
||||
expect(recordAudioNodes.length).toBe(1)
|
||||
|
||||
// Take a screenshot of the canvas with the RecordAudio node
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('record_audio_node.png')
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 21 KiB |
@@ -9,9 +9,6 @@ const BYPASS_CLASS = /before:bg-bypass\/60/
|
||||
test.describe('Vue Node Bypass', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
await comfyPage.setSetting('Comfy.Minimap.Visible', false)
|
||||
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true)
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
@@ -23,7 +20,6 @@ test.describe('Vue Node Bypass', () => {
|
||||
|
||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
||||
await comfyPage.nextFrame()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'vue-node-bypassed-state.png'
|
||||
)
|
||||
|
||||
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 98 KiB |
@@ -23,10 +23,10 @@ test.describe('Vue Nodes - LOD', () => {
|
||||
|
||||
const vueNodesContainer = comfyPage.vueNodes.nodes
|
||||
const textboxesInNodes = vueNodesContainer.getByRole('textbox')
|
||||
const comboboxesInNodes = vueNodesContainer.getByRole('combobox')
|
||||
const buttonsInNodes = vueNodesContainer.getByRole('button')
|
||||
|
||||
await expect(textboxesInNodes.first()).toBeVisible()
|
||||
await expect(comboboxesInNodes.first()).toBeVisible()
|
||||
await expect(buttonsInNodes.first()).toBeVisible()
|
||||
|
||||
await comfyPage.zoom(120, 10)
|
||||
await comfyPage.nextFrame()
|
||||
@@ -34,7 +34,7 @@ test.describe('Vue Nodes - LOD', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('vue-nodes-lod-active.png')
|
||||
|
||||
await expect(textboxesInNodes.first()).toBeHidden()
|
||||
await expect(comboboxesInNodes.first()).toBeHidden()
|
||||
await expect(buttonsInNodes.first()).toBeHidden()
|
||||
|
||||
await comfyPage.zoom(-120, 10)
|
||||
await comfyPage.nextFrame()
|
||||
@@ -43,6 +43,6 @@ test.describe('Vue Nodes - LOD', () => {
|
||||
'vue-nodes-lod-inactive.png'
|
||||
)
|
||||
await expect(textboxesInNodes.first()).toBeVisible()
|
||||
await expect(comboboxesInNodes.first()).toBeVisible()
|
||||
await expect(buttonsInNodes.first()).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 81 KiB |
@@ -6,34 +6,6 @@ interface ShimResult {
|
||||
exports: string[]
|
||||
}
|
||||
|
||||
const SKIP_WARNING_FILES = new Set(['scripts/app', 'scripts/api'])
|
||||
|
||||
/** Files that will be removed in v1.34 */
|
||||
const DEPRECATED_FILES = [
|
||||
'scripts/ui',
|
||||
'extensions/core/maskEditorOld',
|
||||
'extensions/core/groupNode'
|
||||
] as const
|
||||
|
||||
function getWarningMessage(
|
||||
fileKey: string,
|
||||
shimFileName: string
|
||||
): string | null {
|
||||
if (SKIP_WARNING_FILES.has(fileKey)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isDeprecated = DEPRECATED_FILES.some((deprecatedPath) =>
|
||||
fileKey.startsWith(deprecatedPath)
|
||||
)
|
||||
|
||||
if (isDeprecated) {
|
||||
return `[ComfyUI Deprecated] Importing from "${shimFileName}" is deprecated and will be removed in v1.34.`
|
||||
}
|
||||
|
||||
return `[ComfyUI Notice] "${shimFileName}" is an internal module, not part of the public API. Future updates may break this import.`
|
||||
}
|
||||
|
||||
function isLegacyFile(id: string): boolean {
|
||||
return (
|
||||
id.endsWith('.ts') &&
|
||||
@@ -91,22 +63,12 @@ export function comfyAPIPlugin(isDev: boolean): Plugin {
|
||||
const relativePath = path.relative(path.join(projectRoot, 'src'), id)
|
||||
const shimFileName = relativePath.replace(/\.ts$/, '.js')
|
||||
|
||||
let shimContent = `// Shim for ${relativePath}\n`
|
||||
|
||||
const fileKey = relativePath.replace(/\.ts$/, '').replace(/\\/g, '/')
|
||||
const warningMessage = getWarningMessage(fileKey, shimFileName)
|
||||
|
||||
if (warningMessage) {
|
||||
// It will only display once because it is at the root of the file.
|
||||
shimContent += `console.warn('${warningMessage}');\n`
|
||||
}
|
||||
|
||||
shimContent += result.exports.join('')
|
||||
const shimComment = `// Shim for ${relativePath}\n`
|
||||
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: shimFileName,
|
||||
source: shimContent
|
||||
source: shimComment + result.exports.join('')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescrip
|
||||
import { importX } from 'eslint-plugin-import-x'
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
|
||||
import storybook from 'eslint-plugin-storybook'
|
||||
import tailwind from 'eslint-plugin-tailwindcss'
|
||||
import unusedImports from 'eslint-plugin-unused-imports'
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
import { defineConfig } from 'eslint/config'
|
||||
@@ -33,7 +34,11 @@ const settings = {
|
||||
],
|
||||
noWarnOnMultipleProjects: true
|
||||
})
|
||||
]
|
||||
],
|
||||
tailwindcss: {
|
||||
config: `${import.meta.dirname}/packages/design-system/src/css/style.css`,
|
||||
functions: ['cn', 'clsx', 'tw']
|
||||
}
|
||||
} as const
|
||||
|
||||
const commonParserOptions = {
|
||||
@@ -92,6 +97,7 @@ export default defineConfig([
|
||||
// Difference in typecheck on CI vs Local
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore Bad types in the plugin
|
||||
tailwind.configs['flat/recommended'],
|
||||
pluginVue.configs['flat/recommended'],
|
||||
eslintPluginPrettierRecommended,
|
||||
storybook.configs['flat/recommended'],
|
||||
@@ -123,6 +129,7 @@ export default defineConfig([
|
||||
'import-x/no-relative-packages': 'error',
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
'tailwindcss/no-custom-classname': 'off', // TODO: fix
|
||||
'vue/no-v-html': 'off',
|
||||
// Enforce dark-theme: instead of dark: prefix
|
||||
'vue/no-restricted-class': ['error', '/^dark:/'],
|
||||
@@ -247,17 +254,5 @@ export default defineConfig([
|
||||
rules: {
|
||||
'no-console': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['scripts/**/*.js'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'no-console': 'off'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
13
global.d.ts
vendored
@@ -5,19 +5,6 @@ declare const __ALGOLIA_APP_ID__: string
|
||||
declare const __ALGOLIA_API_KEY__: string
|
||||
declare const __USE_PROD_CONFIG__: boolean
|
||||
|
||||
interface Window {
|
||||
__CONFIG__: {
|
||||
mixpanel_token?: string
|
||||
subscription_required?: boolean
|
||||
server_health_alert?: {
|
||||
message: string
|
||||
tooltip?: string
|
||||
severity?: 'info' | 'warning' | 'error'
|
||||
badge?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Navigator {
|
||||
/**
|
||||
* Used by the electron API. This is a WICG non-standard API, but is guaranteed to exist in Electron.
|
||||
|
||||
@@ -12,10 +12,6 @@ const config: KnipConfig = {
|
||||
],
|
||||
project: ['**/*.{js,ts,vue}', '*.{js,ts,mts}']
|
||||
},
|
||||
'apps/desktop-ui': {
|
||||
entry: ['src/main.ts', 'src/i18n.ts'],
|
||||
project: ['src/**/*.{js,ts,vue}']
|
||||
},
|
||||
'packages/tailwind-utils': {
|
||||
project: ['src/**/*.{js,ts}']
|
||||
},
|
||||
@@ -34,7 +30,9 @@ const config: KnipConfig = {
|
||||
'@primeuix/forms',
|
||||
'@primeuix/styled',
|
||||
'@primeuix/utils',
|
||||
'@primevue/icons'
|
||||
'@primevue/icons',
|
||||
// Dev
|
||||
'@trivago/prettier-plugin-sort-imports'
|
||||
],
|
||||
ignore: [
|
||||
// Auto generated manager types
|
||||
|
||||
@@ -8,10 +8,8 @@ export default {
|
||||
}
|
||||
|
||||
function formatAndEslint(fileNames) {
|
||||
// Convert absolute paths to relative paths for better ESLint resolution
|
||||
const relativePaths = fileNames.map((f) => f.replace(process.cwd() + '/', ''))
|
||||
return [
|
||||
`pnpm exec eslint --cache --fix ${relativePaths.join(' ')}`,
|
||||
`pnpm exec prettier --cache --write ${relativePaths.join(' ')}`
|
||||
`pnpm exec eslint --cache --fix ${fileNames.join(' ')}`,
|
||||
`pnpm exec prettier --cache --write ${fileNames.join(' ')}`
|
||||
]
|
||||
}
|
||||
|
||||
15
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"private": true,
|
||||
"version": "1.32.1",
|
||||
"version": "1.30.0",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
||||
"homepage": "https://comfy.org",
|
||||
@@ -13,8 +13,6 @@
|
||||
"build:types": "nx build --config vite.types.config.mts && node scripts/prepare-types.js",
|
||||
"build:analyze": "cross-env ANALYZE_BUNDLE=true pnpm build",
|
||||
"build": "cross-env NODE_OPTIONS='--max-old-space-size=8192' pnpm typecheck && nx build",
|
||||
"size:collect": "node scripts/size-collect.js",
|
||||
"size:report": "node scripts/size-report.js",
|
||||
"collect-i18n": "pnpm exec playwright test --config=playwright.i18n.config.ts",
|
||||
"dev:desktop": "nx dev @comfyorg/desktop-ui",
|
||||
"dev:electron": "nx serve --config vite.electron.config.mts",
|
||||
@@ -43,8 +41,7 @@
|
||||
"test:browser": "pnpm exec nx e2e",
|
||||
"test:unit": "nx run test",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"zipdist": "node scripts/zipdist.js",
|
||||
"clean": "nx reset"
|
||||
"zipdist": "node scripts/zipdist.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "catalog:",
|
||||
@@ -56,13 +53,12 @@
|
||||
"@nx/vite": "catalog:",
|
||||
"@pinia/testing": "catalog:",
|
||||
"@playwright/test": "catalog:",
|
||||
"@prettier/plugin-oxc": "catalog:",
|
||||
"@sentry/vite-plugin": "catalog:",
|
||||
"@storybook/addon-docs": "catalog:",
|
||||
"@storybook/vue3": "catalog:",
|
||||
"@storybook/vue3-vite": "catalog:",
|
||||
"@tailwindcss/vite": "catalog:",
|
||||
"@trivago/prettier-plugin-sort-imports": "catalog:",
|
||||
"@types/eslint-plugin-tailwindcss": "catalog:",
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@types/jsdom": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
@@ -79,6 +75,7 @@
|
||||
"eslint-plugin-import-x": "catalog:",
|
||||
"eslint-plugin-prettier": "catalog:",
|
||||
"eslint-plugin-storybook": "catalog:",
|
||||
"eslint-plugin-tailwindcss": "catalog:",
|
||||
"eslint-plugin-unused-imports": "catalog:",
|
||||
"eslint-plugin-vue": "catalog:",
|
||||
"fs-extra": "^11.2.0",
|
||||
@@ -89,13 +86,9 @@
|
||||
"jsdom": "catalog:",
|
||||
"knip": "catalog:",
|
||||
"lint-staged": "catalog:",
|
||||
"markdown-table": "catalog:",
|
||||
"mixpanel-browser": "catalog:",
|
||||
"nx": "catalog:",
|
||||
"picocolors": "catalog:",
|
||||
"postcss-html": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"pretty-bytes": "catalog:",
|
||||
"rollup-plugin-visualizer": "catalog:",
|
||||
"storybook": "catalog:",
|
||||
"stylelint": "catalog:",
|
||||
|
||||