mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-05 21:54:50 +00:00
Compare commits
1 Commits
v1.44.17
...
bl/e2e-req
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f67bb9022c |
379
.github/workflows/ci-tests-e2e.yaml
vendored
379
.github/workflows/ci-tests-e2e.yaml
vendored
@@ -4,10 +4,8 @@ name: 'CI: Tests E2E'
|
||||
on:
|
||||
push:
|
||||
branches: [main, master, core/*, desktop/*]
|
||||
paths-ignore: ['**/*.md']
|
||||
pull_request:
|
||||
branches-ignore: [wip/*, draft/*, temp/*]
|
||||
paths-ignore: ['**/*.md']
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -16,7 +14,70 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
run-app-e2e: ${{ steps.route.outputs.run-app-e2e }}
|
||||
run-website-e2e: ${{ steps.route.outputs.run-website-e2e }}
|
||||
steps:
|
||||
- name: Route e2e suites
|
||||
id: route
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const runAll = context.eventName !== 'pull_request'
|
||||
if (runAll) {
|
||||
core.info(`${context.eventName} runs all e2e suites`)
|
||||
core.setOutput('run-app-e2e', 'true')
|
||||
core.setOutput('run-website-e2e', 'true')
|
||||
return
|
||||
}
|
||||
|
||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
per_page: 100
|
||||
})
|
||||
const changedFiles = files.map((file) => file.filename)
|
||||
|
||||
const sharedWebsiteFiles = new Set([
|
||||
'package.json',
|
||||
'pnpm-lock.yaml',
|
||||
'pnpm-workspace.yaml',
|
||||
'.github/workflows/ci-tests-e2e.yaml',
|
||||
'.github/workflows/ci-website-e2e.yaml'
|
||||
])
|
||||
const affectsWebsite = (file) =>
|
||||
file.startsWith('apps/website/') ||
|
||||
file.startsWith('packages/design-system/') ||
|
||||
file.startsWith('packages/tailwind-utils/') ||
|
||||
file.startsWith('.github/actions/upsert-comment-section/') ||
|
||||
sharedWebsiteFiles.has(file)
|
||||
|
||||
const isAppE2eExcluded = (file) =>
|
||||
file.endsWith('.md') ||
|
||||
file.startsWith('apps/desktop-ui/') ||
|
||||
file.startsWith('apps/website/') ||
|
||||
file.startsWith('docs/') ||
|
||||
file.startsWith('.storybook/')
|
||||
|
||||
const runWebsiteE2e = changedFiles.some(affectsWebsite)
|
||||
const runAppE2e = changedFiles.some((file) => !isAppE2eExcluded(file))
|
||||
|
||||
core.info(`Changed files: ${changedFiles.join(', ') || '(none)'}`)
|
||||
core.info(`run-app-e2e=${runAppE2e}`)
|
||||
core.info(`run-website-e2e=${runWebsiteE2e}`)
|
||||
|
||||
core.setOutput('run-app-e2e', String(runAppE2e))
|
||||
core.setOutput('run-website-e2e', String(runWebsiteE2e))
|
||||
|
||||
setup:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.run-app-e2e == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -50,7 +111,8 @@ jobs:
|
||||
|
||||
# Sharded chromium tests
|
||||
playwright-tests-chromium-sharded:
|
||||
needs: setup
|
||||
needs: [detect-changes, setup]
|
||||
if: needs.detect-changes.outputs.run-app-e2e == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
container:
|
||||
@@ -109,7 +171,8 @@ jobs:
|
||||
playwright-tests:
|
||||
# Ideally, each shard runs test in 6 minutes, but allow up to 15 minutes
|
||||
timeout-minutes: 15
|
||||
needs: setup
|
||||
needs: [detect-changes, setup]
|
||||
if: needs.detect-changes.outputs.run-app-e2e == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.16
|
||||
@@ -164,9 +227,9 @@ jobs:
|
||||
|
||||
# Merge sharded test reports (no container needed - only runs CLI)
|
||||
merge-reports:
|
||||
needs: [playwright-tests-chromium-sharded]
|
||||
needs: [detect-changes, playwright-tests-chromium-sharded]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !cancelled() }}
|
||||
if: ${{ !cancelled() && needs.detect-changes.outputs.run-app-e2e == 'true' }}
|
||||
steps:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
|
||||
@@ -195,14 +258,302 @@ jobs:
|
||||
path: ./playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
website-e2e:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.run-website-e2e == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.1-noble
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
test-outcome: ${{ steps.tests.outcome }}
|
||||
report-url: ${{ steps.deploy.outputs.url }}
|
||||
screenshot-failures: ${{ steps.failures.outputs.screenshot }}
|
||||
other-failures: ${{ steps.failures.outputs.other }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install pnpm
|
||||
run: corepack enable && corepack prepare
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build website
|
||||
run: pnpm --filter @comfyorg/website build
|
||||
|
||||
- name: Run Playwright tests
|
||||
id: tests
|
||||
run: pnpm --filter @comfyorg/website test:e2e
|
||||
|
||||
- name: Upload test report
|
||||
uses: actions/upload-artifact@v6
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: website-playwright-report
|
||||
path: apps/website/playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
- name: Deploy report to Cloudflare
|
||||
id: deploy
|
||||
if: always() && !cancelled()
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
HEAD_REF: ${{ github.head_ref || github.ref_name }}
|
||||
run: |
|
||||
BRANCH=$(echo "$HEAD_REF" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g;s/--*/-/g;s/^-\|-$//g')
|
||||
DEPLOY_OK=false
|
||||
for i in 1 2 3; do
|
||||
echo "Deployment attempt $i of 3..."
|
||||
OUTPUT=$(npx wrangler@^4.0.0 pages deploy apps/website/playwright-report \
|
||||
--project-name=comfyui-website-e2e \
|
||||
--branch="$BRANCH" 2>&1) && { DEPLOY_OK=true; break; } || echo "$OUTPUT"
|
||||
[ $i -lt 3 ] && sleep 10
|
||||
done
|
||||
echo "$OUTPUT"
|
||||
if [ "$DEPLOY_OK" != "true" ]; then
|
||||
echo "::error::All 3 deployment attempts failed"
|
||||
exit 1
|
||||
fi
|
||||
URL=$(echo "$OUTPUT" | grep -oE 'https://[a-zA-Z0-9.-]+\.pages\.dev\S*' | head -1)
|
||||
echo "url=${URL}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Categorize failures
|
||||
id: failures
|
||||
if: always() && !cancelled() && steps.tests.outcome != 'success'
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs')
|
||||
const report = JSON.parse(fs.readFileSync('apps/website/results.json', 'utf8'))
|
||||
|
||||
function isFailed(t) { return t.status === 'unexpected' || t.status === 'flaky' }
|
||||
function isVisual(spec) {
|
||||
return spec.file?.includes('visual') ||
|
||||
spec.tests?.some(t => t.results?.some(r => r.error?.message?.includes('toHaveScreenshot')))
|
||||
}
|
||||
function specsOf(suite) {
|
||||
return [
|
||||
...(suite.specs || []),
|
||||
...(suite.suites || []).flatMap(specsOf)
|
||||
]
|
||||
}
|
||||
|
||||
// True: Visual
|
||||
// False: Other
|
||||
const failed = specsOf(report)
|
||||
.flatMap(spec => (spec.tests || [])
|
||||
.filter(isFailed)
|
||||
.map(() => isVisual(spec)))
|
||||
|
||||
const screenshotFailures = failed.filter(Boolean).length
|
||||
core.setOutput('screenshot', screenshotFailures)
|
||||
core.setOutput('other', failed.length - screenshotFailures)
|
||||
|
||||
- name: Write job summary
|
||||
if: always() && !cancelled()
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
TEST_OUTCOME: ${{ steps.tests.outcome }}
|
||||
REPORT_URL: ${{ steps.deploy.outputs.url }}
|
||||
SCREENSHOT_FAILURES: ${{ steps.failures.outputs.screenshot }}
|
||||
OTHER_FAILURES: ${{ steps.failures.outputs.other }}
|
||||
with:
|
||||
script: |
|
||||
const passed = process.env.TEST_OUTCOME === 'success'
|
||||
const reportUrl = process.env.REPORT_URL
|
||||
const screenshotFailures = parseInt(process.env.SCREENSHOT_FAILURES) || 0
|
||||
const otherFailures = parseInt(process.env.OTHER_FAILURES) || 0
|
||||
|
||||
const lines = ['## 🌐 Website E2E', '']
|
||||
|
||||
if (passed) {
|
||||
lines.push('> [!TIP]', '> All tests passed.')
|
||||
} else {
|
||||
lines.push('> [!CAUTION]', '> Some tests failed.')
|
||||
}
|
||||
|
||||
const rows = [
|
||||
['Status', passed ? '✅ Passed' : '❌ Failed'],
|
||||
['Report', reportUrl ? `[View Report](${reportUrl})` : '_unavailable_']
|
||||
]
|
||||
if (!passed) {
|
||||
rows.push(
|
||||
['Screenshot diffs', String(screenshotFailures)],
|
||||
['Other failures', String(otherFailures)]
|
||||
)
|
||||
}
|
||||
lines.push(
|
||||
'',
|
||||
'| | |',
|
||||
'|---|---|',
|
||||
...rows.map(([k, v]) => `| **${k}** | ${v} |`)
|
||||
)
|
||||
|
||||
await core.summary.addRaw(lines.join('\n')).write()
|
||||
|
||||
post-starting-website-comment:
|
||||
needs: detect-changes
|
||||
# Safe to comment from pull_request trigger: fork PRs are excluded by the guard below.
|
||||
if: |
|
||||
needs.detect-changes.outputs.run-website-e2e == 'true' &&
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.fork == false
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
concurrency:
|
||||
group: website-pr-comment-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/upsert-comment-section
|
||||
with:
|
||||
pr-number: ${{ github.event.pull_request.number }}
|
||||
section-name: e2e
|
||||
comment-marker: '<!-- WEBSITE_CI_REPORT -->'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
section-content: |-
|
||||
## 🌐 Website E2E
|
||||
<!-- WEBSITE_E2E_STATUS -->
|
||||
|
||||
> [!NOTE]
|
||||
> Tests are running… [View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
post-website-result-comment:
|
||||
needs: [detect-changes, website-e2e]
|
||||
if: |
|
||||
always() &&
|
||||
needs.detect-changes.outputs.run-website-e2e == 'true' &&
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.fork == false
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
concurrency:
|
||||
group: website-pr-comment-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Build e2e section content
|
||||
id: content
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
TEST_OUTCOME: ${{ needs.website-e2e.outputs.test-outcome }}
|
||||
REPORT_URL: ${{ needs.website-e2e.outputs.report-url }}
|
||||
SCREENSHOT_FAILURES: ${{ needs.website-e2e.outputs.screenshot-failures }}
|
||||
OTHER_FAILURES: ${{ needs.website-e2e.outputs.other-failures }}
|
||||
with:
|
||||
script: |
|
||||
const passed = process.env.TEST_OUTCOME === 'success'
|
||||
const reportUrl = process.env.REPORT_URL
|
||||
const screenshotFailures = parseInt(process.env.SCREENSHOT_FAILURES) || 0
|
||||
const otherFailures = parseInt(process.env.OTHER_FAILURES) || 0
|
||||
|
||||
const lines = ['## 🌐 Website E2E', '<!-- WEBSITE_E2E_STATUS -->', '']
|
||||
|
||||
if (passed) {
|
||||
lines.push('> [!TIP]', '> All tests passed.')
|
||||
} else {
|
||||
lines.push('> [!CAUTION]', '> Some tests failed.')
|
||||
}
|
||||
|
||||
const rows = [
|
||||
['Status', passed ? '✅ Passed' : '❌ Failed'],
|
||||
['Report', reportUrl ? `[View Report](${reportUrl})` : '_unavailable_']
|
||||
]
|
||||
if (!passed) {
|
||||
rows.push(
|
||||
['Screenshot diffs', String(screenshotFailures)],
|
||||
['Other failures', String(otherFailures)]
|
||||
)
|
||||
}
|
||||
lines.push(
|
||||
'',
|
||||
'| | |',
|
||||
'|---|---|',
|
||||
...rows.map(([k, v]) => `| **${k}** | ${v} |`)
|
||||
)
|
||||
|
||||
if (screenshotFailures > 0) {
|
||||
const s = screenshotFailures === 1 ? '' : 's'
|
||||
lines.push('', `- [ ] Update website screenshots (${screenshotFailures} screenshot diff${s})`)
|
||||
}
|
||||
if (otherFailures > 0) {
|
||||
lines.push(
|
||||
'',
|
||||
'> [!WARNING]',
|
||||
`> ${otherFailures} non-screenshot failure${otherFailures === 1 ? '' : 's'} — these require manual review.`
|
||||
)
|
||||
}
|
||||
|
||||
core.setOutput('section-content', lines.join('\n'))
|
||||
|
||||
- uses: ./.github/actions/upsert-comment-section
|
||||
with:
|
||||
pr-number: ${{ github.event.pull_request.number }}
|
||||
section-name: e2e
|
||||
comment-marker: '<!-- WEBSITE_CI_REPORT -->'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
section-content: ${{ steps.content.outputs.section-content }}
|
||||
|
||||
e2e-required:
|
||||
needs:
|
||||
- detect-changes
|
||||
- playwright-tests
|
||||
- playwright-tests-chromium-sharded
|
||||
- website-e2e
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ always() }}
|
||||
steps:
|
||||
- name: Verify required e2e jobs passed
|
||||
env:
|
||||
RUN_APP_E2E: ${{ needs.detect-changes.outputs.run-app-e2e }}
|
||||
RUN_WEBSITE_E2E: ${{ needs.detect-changes.outputs.run-website-e2e }}
|
||||
PLAYWRIGHT_TESTS_RESULT: ${{ needs.playwright-tests.result }}
|
||||
SHARDED_TESTS_RESULT: ${{ needs.playwright-tests-chromium-sharded.result }}
|
||||
WEBSITE_E2E_RESULT: ${{ needs.website-e2e.result }}
|
||||
run: |
|
||||
if [ "$RUN_APP_E2E" = "true" ] && [ "$PLAYWRIGHT_TESTS_RESULT" != "success" ]; then
|
||||
echo "playwright-tests result: $PLAYWRIGHT_TESTS_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$RUN_APP_E2E" = "true" ] && [ "$SHARDED_TESTS_RESULT" != "success" ]; then
|
||||
echo "playwright-tests-chromium-sharded result: $SHARDED_TESTS_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$RUN_WEBSITE_E2E" = "true" ] && [ "$WEBSITE_E2E_RESULT" != "success" ]; then
|
||||
echo "website-e2e result: $WEBSITE_E2E_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$RUN_APP_E2E" != "true" ] && [ "$RUN_WEBSITE_E2E" != "true" ]; then
|
||||
echo "No e2e suites required for these changes."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "All required e2e suites passed."
|
||||
|
||||
#### BEGIN Deployment and commenting (non-forked PRs only)
|
||||
# when using pull_request event, we have permission to comment directly
|
||||
# if its a forked repo, we need to use workflow_run event in a separate workflow (pr-playwright-deploy.yaml)
|
||||
|
||||
# Post starting comment for non-forked PRs
|
||||
comment-on-pr-start:
|
||||
needs: detect-changes
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
|
||||
if: |
|
||||
needs.detect-changes.outputs.run-app-e2e == 'true' &&
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.fork == false
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
@@ -212,18 +563,23 @@ jobs:
|
||||
- name: Post starting comment
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
run: |
|
||||
chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh
|
||||
./scripts/cicd/pr-playwright-deploy-and-comment.sh \
|
||||
"${{ github.event.pull_request.number }}" \
|
||||
"${{ github.head_ref }}" \
|
||||
"$HEAD_REF" \
|
||||
"starting"
|
||||
|
||||
# Deploy and comment for non-forked PRs only
|
||||
deploy-and-comment:
|
||||
needs: [playwright-tests, merge-reports]
|
||||
needs: [detect-changes, playwright-tests, merge-reports]
|
||||
runs-on: ubuntu-latest
|
||||
if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
|
||||
if: |
|
||||
always() &&
|
||||
needs.detect-changes.outputs.run-app-e2e == 'true' &&
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.head.repo.fork == false
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
@@ -243,9 +599,10 @@ jobs:
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
GITHUB_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
run: |
|
||||
bash ./scripts/cicd/pr-playwright-deploy-and-comment.sh \
|
||||
"${{ github.event.pull_request.number }}" \
|
||||
"${{ github.head_ref }}" \
|
||||
"$HEAD_REF" \
|
||||
"completed"
|
||||
#### END Deployment and commenting (non-forked PRs only)
|
||||
|
||||
260
.github/workflows/ci-website-e2e.yaml
vendored
260
.github/workflows/ci-website-e2e.yaml
vendored
@@ -1,260 +0,0 @@
|
||||
name: 'CI: Website E2E'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, website/*]
|
||||
paths:
|
||||
- 'apps/website/**'
|
||||
- 'packages/design-system/**'
|
||||
- 'packages/tailwind-utils/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
pull_request:
|
||||
branches-ignore: [wip/*, draft/*, temp/*]
|
||||
paths:
|
||||
- 'apps/website/**'
|
||||
- 'packages/design-system/**'
|
||||
- 'packages/tailwind-utils/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
website-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.1-noble
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
test-outcome: ${{ steps.tests.outcome }}
|
||||
report-url: ${{ steps.deploy.outputs.url }}
|
||||
screenshot-failures: ${{ steps.failures.outputs.screenshot }}
|
||||
other-failures: ${{ steps.failures.outputs.other }}
|
||||
# Evaluated at job level (not from a step) — static expression.
|
||||
is-pr: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install pnpm
|
||||
run: corepack enable && corepack prepare
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build website
|
||||
run: pnpm --filter @comfyorg/website build
|
||||
|
||||
- name: Run Playwright tests
|
||||
id: tests
|
||||
run: pnpm --filter @comfyorg/website test:e2e
|
||||
|
||||
- name: Upload test report
|
||||
uses: actions/upload-artifact@v6
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: website-playwright-report
|
||||
path: apps/website/playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
- name: Deploy report to Cloudflare
|
||||
id: deploy
|
||||
if: always() && !cancelled()
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
HEAD_REF: ${{ github.head_ref || github.ref_name }}
|
||||
run: |
|
||||
BRANCH=$(echo "$HEAD_REF" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g;s/--*/-/g;s/^-\|-$//g')
|
||||
DEPLOY_OK=false
|
||||
for i in 1 2 3; do
|
||||
echo "Deployment attempt $i of 3..."
|
||||
OUTPUT=$(npx wrangler@^4.0.0 pages deploy apps/website/playwright-report \
|
||||
--project-name=comfyui-website-e2e \
|
||||
--branch="$BRANCH" 2>&1) && { DEPLOY_OK=true; break; } || echo "$OUTPUT"
|
||||
[ $i -lt 3 ] && sleep 10
|
||||
done
|
||||
echo "$OUTPUT"
|
||||
if [ "$DEPLOY_OK" != "true" ]; then
|
||||
echo "::error::All 3 deployment attempts failed"
|
||||
exit 1
|
||||
fi
|
||||
URL=$(echo "$OUTPUT" | grep -oE 'https://[a-zA-Z0-9.-]+\.pages\.dev\S*' | head -1)
|
||||
echo "url=${URL}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Categorize failures
|
||||
id: failures
|
||||
if: always() && !cancelled() && steps.tests.outcome != 'success'
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs')
|
||||
const report = JSON.parse(fs.readFileSync('apps/website/results.json', 'utf8'))
|
||||
|
||||
function isFailed(t) { return t.status === 'unexpected' || t.status === 'flaky' }
|
||||
function isVisual(spec) {
|
||||
return spec.file?.includes('visual') ||
|
||||
spec.tests?.some(t => t.results?.some(r => r.error?.message?.includes('toHaveScreenshot')))
|
||||
}
|
||||
function specsOf(suite) {
|
||||
return [
|
||||
...(suite.specs || []),
|
||||
...(suite.suites || []).flatMap(specsOf)
|
||||
]
|
||||
}
|
||||
|
||||
// True: Visual
|
||||
// False: Other
|
||||
const failed = specsOf(report)
|
||||
.flatMap(spec => (spec.tests || [])
|
||||
.filter(isFailed)
|
||||
.map(() => isVisual(spec)))
|
||||
|
||||
const screenshotFailures = failed.filter(Boolean).length
|
||||
core.setOutput('screenshot', screenshotFailures)
|
||||
core.setOutput('other', failed.length - screenshotFailures)
|
||||
|
||||
- name: Write job summary
|
||||
if: always() && !cancelled()
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
TEST_OUTCOME: ${{ steps.tests.outcome }}
|
||||
REPORT_URL: ${{ steps.deploy.outputs.url }}
|
||||
SCREENSHOT_FAILURES: ${{ steps.failures.outputs.screenshot }}
|
||||
OTHER_FAILURES: ${{ steps.failures.outputs.other }}
|
||||
with:
|
||||
script: |
|
||||
const passed = process.env.TEST_OUTCOME === 'success'
|
||||
const reportUrl = process.env.REPORT_URL
|
||||
const screenshotFailures = parseInt(process.env.SCREENSHOT_FAILURES) || 0
|
||||
const otherFailures = parseInt(process.env.OTHER_FAILURES) || 0
|
||||
|
||||
const lines = ['## 🌐 Website E2E', '']
|
||||
|
||||
if (passed) {
|
||||
lines.push('> [!TIP]', '> All tests passed.')
|
||||
} else {
|
||||
lines.push('> [!CAUTION]', '> Some tests failed.')
|
||||
}
|
||||
|
||||
const rows = [
|
||||
['Status', passed ? '✅ Passed' : '❌ Failed'],
|
||||
['Report', reportUrl ? `[View Report](${reportUrl})` : '_unavailable_']
|
||||
]
|
||||
if (!passed) {
|
||||
rows.push(
|
||||
['Screenshot diffs', String(screenshotFailures)],
|
||||
['Other failures', String(otherFailures)]
|
||||
)
|
||||
}
|
||||
lines.push(
|
||||
'',
|
||||
'| | |',
|
||||
'|---|---|',
|
||||
...rows.map(([k, v]) => `| **${k}** | ${v} |`)
|
||||
)
|
||||
|
||||
await core.summary.addRaw(lines.join('\n')).write()
|
||||
|
||||
post-starting-comment:
|
||||
# Safe to comment from pull_request trigger: fork PRs are excluded by the guard below.
|
||||
# This avoids a ci-*/pr-* workflow_run split for a comment that must appear immediately.
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
concurrency:
|
||||
group: website-pr-comment-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/upsert-comment-section
|
||||
with:
|
||||
pr-number: ${{ github.event.pull_request.number }}
|
||||
section-name: e2e
|
||||
comment-marker: '<!-- WEBSITE_CI_REPORT -->'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
section-content: |-
|
||||
## 🌐 Website E2E
|
||||
<!-- WEBSITE_E2E_STATUS -->
|
||||
|
||||
> [!NOTE]
|
||||
> Tests are running… [View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
post-result-comment:
|
||||
needs: website-e2e
|
||||
if: always() && !cancelled() && needs.website-e2e.outputs.is-pr == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
concurrency:
|
||||
group: website-pr-comment-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Build e2e section content
|
||||
id: content
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
TEST_OUTCOME: ${{ needs.website-e2e.outputs.test-outcome }}
|
||||
REPORT_URL: ${{ needs.website-e2e.outputs.report-url }}
|
||||
SCREENSHOT_FAILURES: ${{ needs.website-e2e.outputs.screenshot-failures }}
|
||||
OTHER_FAILURES: ${{ needs.website-e2e.outputs.other-failures }}
|
||||
with:
|
||||
script: |
|
||||
const passed = process.env.TEST_OUTCOME === 'success'
|
||||
const reportUrl = process.env.REPORT_URL
|
||||
const screenshotFailures = parseInt(process.env.SCREENSHOT_FAILURES) || 0
|
||||
const otherFailures = parseInt(process.env.OTHER_FAILURES) || 0
|
||||
|
||||
const lines = ['## 🌐 Website E2E', '<!-- WEBSITE_E2E_STATUS -->', '']
|
||||
|
||||
if (passed) {
|
||||
lines.push('> [!TIP]', '> All tests passed.')
|
||||
} else {
|
||||
lines.push('> [!CAUTION]', '> Some tests failed.')
|
||||
}
|
||||
|
||||
const rows = [
|
||||
['Status', passed ? '✅ Passed' : '❌ Failed'],
|
||||
['Report', reportUrl ? `[View Report](${reportUrl})` : '_unavailable_']
|
||||
]
|
||||
if (!passed) {
|
||||
rows.push(
|
||||
['Screenshot diffs', String(screenshotFailures)],
|
||||
['Other failures', String(otherFailures)]
|
||||
)
|
||||
}
|
||||
lines.push(
|
||||
'',
|
||||
'| | |',
|
||||
'|---|---|',
|
||||
...rows.map(([k, v]) => `| **${k}** | ${v} |`)
|
||||
)
|
||||
|
||||
if (screenshotFailures > 0) {
|
||||
const s = screenshotFailures === 1 ? '' : 's'
|
||||
lines.push('', `- [ ] Update website screenshots (${screenshotFailures} screenshot diff${s})`)
|
||||
}
|
||||
if (otherFailures > 0) {
|
||||
lines.push(
|
||||
'',
|
||||
'> [!WARNING]',
|
||||
`> ${otherFailures} non-screenshot failure${otherFailures === 1 ? '' : 's'} — these require manual review.`
|
||||
)
|
||||
}
|
||||
|
||||
core.setOutput('section-content', lines.join('\n'))
|
||||
|
||||
- uses: ./.github/actions/upsert-comment-section
|
||||
with:
|
||||
pr-number: ${{ github.event.pull_request.number }}
|
||||
section-name: e2e
|
||||
comment-marker: '<!-- WEBSITE_CI_REPORT -->'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
section-content: ${{ steps.content.outputs.section-content }}
|
||||
Reference in New Issue
Block a user