mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 10:42:44 +00:00
Address review comments and improve workflow
- Add workflow documentation explaining selective update strategy - Improve logging with clear output formatting (no emojis) - Add GitHub Actions workflow summary with file change details - Fix command injection vulnerability by validating test paths with regex - Add error handling for JSON.parse with descriptive messages - Replace non-null assertion with safer null checking pattern - Add explicit error handling for TypeScript script execution
This commit is contained in:
6
.github/workflows/tests-ci.yaml
vendored
6
.github/workflows/tests-ci.yaml
vendored
@@ -256,7 +256,11 @@ jobs:
|
|||||||
if: ${{ needs.playwright-tests-chromium-sharded.result == 'failure' }}
|
if: ${{ needs.playwright-tests-chromium-sharded.result == 'failure' }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
pnpm tsx scripts/cicd/build-failed-screenshot-manifest.ts
|
if ! pnpm tsx scripts/cicd/build-failed-screenshot-manifest.ts; then
|
||||||
|
echo "ERROR: Failed to generate screenshot manifest"
|
||||||
|
echo "This may indicate an issue with the Playwright JSON report or the manifest script"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
|
|
||||||
- name: Upload failed screenshot manifest
|
- name: Upload failed screenshot manifest
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
# Setting test expectation screenshots for Playwright
|
# Setting test expectation screenshots for Playwright
|
||||||
|
#
|
||||||
|
# This workflow uses a selective snapshot update strategy:
|
||||||
|
# 1. When tests fail in CI, they generate a manifest of failed test locations (file:line)
|
||||||
|
# 2. This workflow downloads that manifest from the failed test run artifacts
|
||||||
|
# 3. Only the failed tests are re-run with --update-snapshots (much faster than running all tests)
|
||||||
|
# 4. Updated snapshots are committed back to the PR branch
|
||||||
|
#
|
||||||
|
# Trigger: Add label "New Browser Test Expectations" OR comment "/update-playwright" on PR
|
||||||
name: Update Playwright Expectations
|
name: Update Playwright Expectations
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -97,39 +105,80 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Selective Snapshot Update"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if manifest exists
|
||||||
if [ ! -d ci-rerun ]; then
|
if [ ! -d ci-rerun ]; then
|
||||||
echo "No manifest found; running full suite as fallback"
|
echo "ERROR: No manifest found in ci-rerun/ directory"
|
||||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
echo " This means no failed screenshot tests were detected in the latest CI run."
|
||||||
pnpm exec playwright test --update-snapshots \
|
echo " Please ensure tests have been run and failures were recorded."
|
||||||
--reporter=line --reporter=html
|
exit 1
|
||||||
exit 0
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
files=(ci-rerun/*.txt)
|
files=(ci-rerun/*.txt)
|
||||||
|
|
||||||
if [ ${#files[@]} -eq 0 ]; then
|
if [ ${#files[@]} -eq 0 ]; then
|
||||||
echo "Manifest is empty; running full suite as fallback"
|
echo "ERROR: No manifest files found in ci-rerun/"
|
||||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
echo " Expected files like: chromium.txt, chromium-2x.txt, mobile-chrome.txt"
|
||||||
pnpm exec playwright test --update-snapshots \
|
exit 1
|
||||||
--reporter=line --reporter=html
|
|
||||||
exit 0
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Found ${#files[@]} project manifest(s):"
|
||||||
|
for f in "${files[@]}"; do
|
||||||
|
project="$(basename "$f" .txt)"
|
||||||
|
count=$(grep -c . "$f" 2>/dev/null || echo "0")
|
||||||
|
echo " - $project: $count failed test(s)"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Re-run tests per project
|
||||||
|
total_tests=0
|
||||||
for f in "${files[@]}"; do
|
for f in "${files[@]}"; do
|
||||||
project="$(basename "$f" .txt)"
|
project="$(basename "$f" .txt)"
|
||||||
mapfile -t lines < "$f"
|
mapfile -t lines < "$f"
|
||||||
filtered=( )
|
filtered=( )
|
||||||
|
|
||||||
|
# Validate and sanitize test paths to prevent command injection
|
||||||
for l in "${lines[@]}"; do
|
for l in "${lines[@]}"; do
|
||||||
[ -n "$l" ] && filtered+=("$l")
|
# Skip empty lines
|
||||||
|
[ -z "$l" ] && continue
|
||||||
|
|
||||||
|
# Validate format: must be browser_tests/...spec.ts:number
|
||||||
|
if [[ "$l" =~ ^browser_tests/.+\.spec\.ts:[0-9]+$ ]]; then
|
||||||
|
filtered+=("$l")
|
||||||
|
else
|
||||||
|
echo "WARNING: Skipping invalid test path: $l"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ ${#filtered[@]} -eq 0 ]; then
|
if [ ${#filtered[@]} -eq 0 ]; then
|
||||||
|
echo "WARNING: Skipping $project (no valid tests in manifest)"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
echo "Re-running ${#filtered[@]} tests for project $project"
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Updating snapshots for project: $project"
|
||||||
|
echo " Re-running ${#filtered[@]} failed test(s)..."
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
||||||
pnpm exec playwright test --project="$project" --update-snapshots \
|
pnpm exec playwright test --project="$project" --update-snapshots \
|
||||||
--reporter=line --reporter=html \
|
--reporter=line --reporter=html \
|
||||||
"${filtered[@]}"
|
"${filtered[@]}"
|
||||||
|
|
||||||
|
total_tests=$((total_tests + ${#filtered[@]}))
|
||||||
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Completed snapshot updates for $total_tests test(s)"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
@@ -144,6 +193,7 @@ jobs:
|
|||||||
git status
|
git status
|
||||||
|
|
||||||
- name: Commit updated expectations
|
- name: Commit updated expectations
|
||||||
|
id: commit
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name 'github-actions'
|
git config --global user.name 'github-actions'
|
||||||
@@ -157,7 +207,13 @@ jobs:
|
|||||||
git add browser_tests
|
git add browser_tests
|
||||||
if git diff --cached --quiet; then
|
if git diff --cached --quiet; then
|
||||||
echo "No expectation updates detected; skipping commit."
|
echo "No expectation updates detected; skipping commit."
|
||||||
|
echo "changed=false" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
|
# Count changed snapshots
|
||||||
|
changed_count=$(git diff --cached --name-only browser_tests | wc -l)
|
||||||
|
echo "changed=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "count=$changed_count" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
git commit -m "[automated] Update test expectations"
|
git commit -m "[automated] Update test expectations"
|
||||||
if [ "${{ github.event_name }}" = "issue_comment" ]; then
|
if [ "${{ github.event_name }}" = "issue_comment" ]; then
|
||||||
git push
|
git push
|
||||||
@@ -165,3 +221,30 @@ jobs:
|
|||||||
git push origin HEAD:${{ github.head_ref }}
|
git push origin HEAD:${{ github.head_ref }}
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Generate workflow summary
|
||||||
|
if: always()
|
||||||
|
working-directory: ComfyUI_frontend
|
||||||
|
run: |
|
||||||
|
echo "## Snapshot Update Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
if [ "${{ steps.commit.outputs.changed }}" = "true" ]; then
|
||||||
|
echo "**${{ steps.commit.outputs.count }} snapshot(s) updated**" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "<details>" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "<summary>View updated files</summary>" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
git diff HEAD~1 --name-only browser_tests 2>/dev/null || echo "No git history available" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "</details>" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [ "${{ steps.commit.outputs.changed }}" = "false" ]; then
|
||||||
|
echo "No snapshot changes detected" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "WARNING: Snapshot update may have failed - check logs above" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Strategy:** Selective snapshot update (only failed tests re-run)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
@@ -28,7 +28,17 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const raw = await fsp.readFile(reportPath, 'utf8')
|
const raw = await fsp.readFile(reportPath, 'utf8')
|
||||||
const data = JSON.parse(raw)
|
|
||||||
|
let data: JSONReport
|
||||||
|
try {
|
||||||
|
data = JSON.parse(raw)
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to parse Playwright JSON report at ${reportPath}. ` +
|
||||||
|
`The report file may be corrupted or incomplete. ` +
|
||||||
|
`Error: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const hasScreenshotSignal = (r: JSONReportTestResult) => {
|
const hasScreenshotSignal = (r: JSONReportTestResult) => {
|
||||||
return r.attachments.some((att) => att?.contentType?.startsWith('image/'))
|
return r.attachments.some((att) => att?.contentType?.startsWith('image/'))
|
||||||
@@ -52,7 +62,10 @@ async function main() {
|
|||||||
last && last.status === 'failed' && hasScreenshotSignal(last)
|
last && last.status === 'failed' && hasScreenshotSignal(last)
|
||||||
if (!failedScreenshot) continue
|
if (!failedScreenshot) continue
|
||||||
if (!out.has(project)) out.set(project, new Set())
|
if (!out.has(project)) out.set(project, new Set())
|
||||||
out.get(project)!.add(loc)
|
const projectSet = out.get(project)
|
||||||
|
if (projectSet) {
|
||||||
|
projectSet.add(loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user