diff --git a/.github/workflows/pr-playwright-deploy.yaml b/.github/workflows/pr-playwright-deploy.yaml index 12051fa99..19bb28253 100644 --- a/.github/workflows/pr-playwright-deploy.yaml +++ b/.github/workflows/pr-playwright-deploy.yaml @@ -1,4 +1,4 @@ -name: PR Playwright Deploy and Comment +name: PR Playwright Deploy (Forks) on: workflow_run: @@ -9,272 +9,84 @@ env: DATE_FORMAT: '+%m/%d/%Y, %I:%M:%S %p' jobs: - deploy-reports: + deploy-and-comment-forked-pr: runs-on: ubuntu-latest - if: github.repository == 'Comfy-Org/ComfyUI_frontend' && github.event.workflow_run.event == 'pull_request' && github.event.action == 'completed' + if: | + github.repository == 'Comfy-Org/ComfyUI_frontend' && + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.head_repository != null && + github.event.workflow_run.repository != null && + github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name permissions: + pull-requests: write actions: read - strategy: - fail-fast: false - matrix: - browser: [chromium, chromium-2x, chromium-0.5x, mobile-chrome] steps: - - name: Get PR info - id: pr-info + - name: Log workflow trigger info + run: | + echo "Repository: ${{ github.repository }}" + echo "Event: ${{ github.event.workflow_run.event }}" + echo "Head repo: ${{ github.event.workflow_run.head_repository.full_name || 'null' }}" + echo "Base repo: ${{ github.event.workflow_run.repository.full_name || 'null' }}" + echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}" + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get PR Number + id: pr uses: actions/github-script@v7 with: script: | - const { data: pullRequests } = await github.rest.pulls.list({ + const { data: prs } = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, state: 'open', - head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`, }); - if (pullRequests.length === 0) { - console.log('No open PR found for this branch'); - return { number: null, sanitized_branch: null }; + const pr = prs.find(p => p.head.sha === context.payload.workflow_run.head_sha); + + if (!pr) { + console.log('No PR found for SHA:', context.payload.workflow_run.head_sha); + return null; } - const pr = pullRequests[0]; - const branchName = context.payload.workflow_run.head_branch; - const sanitizedBranch = branchName.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/--+/g, '-').replace(/^-|-$/g, ''); - - return { - number: pr.number, - sanitized_branch: sanitizedBranch - }; + console.log(`Found PR #${pr.number} from fork: ${context.payload.workflow_run.head_repository.full_name}`); + return pr.number; - - name: Set project name - if: fromJSON(steps.pr-info.outputs.result).number != null - id: project-name + - name: Handle Test Start + if: steps.pr.outputs.result != 'null' && github.event.action == 'requested' + env: + GITHUB_TOKEN: ${{ github.token }} run: | - if [ "${{ matrix.browser }}" = "chromium-0.5x" ]; then - echo "name=comfyui-playwright-chromium-0-5x" >> $GITHUB_OUTPUT - else - echo "name=comfyui-playwright-${{ matrix.browser }}" >> $GITHUB_OUTPUT - fi - echo "branch=${{ fromJSON(steps.pr-info.outputs.result).sanitized_branch }}" >> $GITHUB_OUTPUT + chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh + ./scripts/cicd/pr-playwright-deploy-and-comment.sh \ + "${{ steps.pr.outputs.result }}" \ + "${{ github.event.workflow_run.head_branch }}" \ + "starting" \ + "$(date -u '${{ env.DATE_FORMAT }}')" - - name: Download playwright report - if: fromJSON(steps.pr-info.outputs.result).number != null + - name: Download and Deploy Reports + if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' uses: actions/download-artifact@v4 with: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - name: playwright-report-${{ matrix.browser }} - path: playwright-report - - - name: Install Wrangler - if: fromJSON(steps.pr-info.outputs.result).number != null - run: npm install -g wrangler - - - name: Deploy to Cloudflare Pages (${{ matrix.browser }}) - if: fromJSON(steps.pr-info.outputs.result).number != null - id: cloudflare-deploy - continue-on-error: true - run: | - # Retry logic for wrangler deploy (3 attempts) - RETRY_COUNT=0 - MAX_RETRIES=3 - SUCCESS=false + pattern: playwright-report-* + path: reports - while [ $RETRY_COUNT -lt $MAX_RETRIES ] && [ $SUCCESS = false ]; do - RETRY_COUNT=$((RETRY_COUNT + 1)) - echo "Deployment attempt $RETRY_COUNT of $MAX_RETRIES..." - - if npx wrangler pages deploy playwright-report --project-name=${{ steps.project-name.outputs.name }} --branch=${{ steps.project-name.outputs.branch }}; then - SUCCESS=true - echo "Deployment successful on attempt $RETRY_COUNT" - else - echo "Deployment failed on attempt $RETRY_COUNT" - if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then - echo "Retrying in 10 seconds..." - sleep 10 - fi - fi - done - - if [ $SUCCESS = false ]; then - echo "All deployment attempts failed" - exit 1 - fi + - name: Handle Test Completion + if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - - comment-tests-starting: - runs-on: ubuntu-latest - if: github.repository == 'Comfy-Org/ComfyUI_frontend' && github.event.workflow_run.event == 'pull_request' && github.event.action == 'requested' - permissions: - pull-requests: write - actions: read - steps: - - name: Get PR number - id: pr - uses: actions/github-script@v7 - with: - script: | - const { data: pullRequests } = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`, - }); - - if (pullRequests.length === 0) { - console.log('No open PR found for this branch'); - return null; - } - - return pullRequests[0].number; - - - name: Get completion time - id: completion-time - run: echo "time=$(date -u '${{ env.DATE_FORMAT }}')" >> $GITHUB_OUTPUT - - - name: Generate comment body for start - if: steps.pr.outputs.result != 'null' - id: comment-body-start + GITHUB_TOKEN: ${{ github.token }} run: | - echo "" > comment.md - echo "## ๐ŸŽญ Playwright Test Results" >> comment.md - echo "" >> comment.md - echo "comfy-loading-gif **Tests are starting...** " >> comment.md - echo "" >> comment.md - echo "โฐ Started at: ${{ steps.completion-time.outputs.time }} UTC" >> comment.md - echo "" >> comment.md - echo "### ๐Ÿš€ Running Tests" >> comment.md - echo "- ๐Ÿงช **chromium**: Running tests..." >> comment.md - echo "- ๐Ÿงช **chromium-0.5x**: Running tests..." >> comment.md - echo "- ๐Ÿงช **chromium-2x**: Running tests..." >> comment.md - echo "- ๐Ÿงช **mobile-chrome**: Running tests..." >> comment.md - echo "" >> comment.md - echo "---" >> comment.md - echo "โฑ๏ธ Please wait while tests are running across all browsers..." >> comment.md - - - name: Comment PR - Tests Started - if: steps.pr.outputs.result != 'null' - uses: edumserrano/find-create-or-update-comment@82880b65c8a3a6e4c70aa05a204995b6c9696f53 # v3.0.0 - with: - issue-number: ${{ steps.pr.outputs.result }} - body-includes: '' - comment-author: 'github-actions[bot]' - edit-mode: replace - body-path: comment.md - - comment-tests-completed: - runs-on: ubuntu-latest - needs: deploy-reports - if: github.repository == 'Comfy-Org/ComfyUI_frontend' && github.event.workflow_run.event == 'pull_request' && github.event.action == 'completed' && always() - permissions: - pull-requests: write - actions: read - steps: - - name: Get PR number - id: pr - uses: actions/github-script@v7 - with: - script: | - const { data: pullRequests } = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`, - }); - - if (pullRequests.length === 0) { - console.log('No open PR found for this branch'); - return null; - } - - return pullRequests[0].number; - - - name: Download all deployment info - if: steps.pr.outputs.result != 'null' - uses: actions/download-artifact@v4 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} - pattern: deployment-info-* - merge-multiple: true - path: deployment-info - - - name: Get completion time - id: completion-time - run: echo "time=$(date -u '${{ env.DATE_FORMAT }}')" >> $GITHUB_OUTPUT - - - name: Generate comment body for completion - if: steps.pr.outputs.result != 'null' - id: comment-body-completed - run: | - echo "" > comment.md - echo "## ๐ŸŽญ Playwright Test Results" >> comment.md - echo "" >> comment.md - - # Check if all tests passed - ALL_PASSED=true - for file in deployment-info/*.txt; do - if [ -f "$file" ]; then - browser=$(basename "$file" .txt) - info=$(cat "$file") - exit_code=$(echo "$info" | cut -d'|' -f2) - if [ "$exit_code" != "0" ]; then - ALL_PASSED=false - break - fi - fi - done - - if [ "$ALL_PASSED" = "true" ]; then - echo "โœ… **All tests passed across all browsers!**" >> comment.md - else - echo "โŒ **Some tests failed!**" >> comment.md - fi - - echo "" >> comment.md - echo "โฐ Completed at: ${{ steps.completion-time.outputs.time }} UTC" >> comment.md - echo "" >> comment.md - echo "### ๐Ÿ“Š Test Reports by Browser" >> comment.md - - for file in deployment-info/*.txt; do - if [ -f "$file" ]; then - browser=$(basename "$file" .txt) - info=$(cat "$file") - exit_code=$(echo "$info" | cut -d'|' -f2) - url=$(echo "$info" | cut -d'|' -f3) - - # Validate URLs before using them in comments - sanitized_url=$(echo "$url" | grep -E '^https://[a-z0-9.-]+\.pages\.dev(/.*)?$' || echo "INVALID_URL") - if [ "$sanitized_url" = "INVALID_URL" ]; then - echo "Invalid deployment URL detected: $url" - url="#" # Use safe fallback - fi - - if [ "$exit_code" = "0" ]; then - status="โœ…" - else - status="โŒ" - fi - - echo "- $status **$browser**: [View Report]($url)" >> comment.md - fi - done - - echo "" >> comment.md - echo "---" >> comment.md - if [ "$ALL_PASSED" = "true" ]; then - echo "๐ŸŽ‰ Your tests are passing across all browsers!" >> comment.md - else - echo "โš ๏ธ Please check the test reports for details on failures." >> comment.md - fi - - - name: Comment PR - Tests Complete - if: steps.pr.outputs.result != 'null' - uses: edumserrano/find-create-or-update-comment@82880b65c8a3a6e4c70aa05a204995b6c9696f53 # v3.0.0 - with: - issue-number: ${{ steps.pr.outputs.result }} - body-includes: '' - comment-author: 'github-actions[bot]' - edit-mode: replace - body-path: comment.md \ No newline at end of file + # Rename merged report if exists + [ -d "reports/playwright-report-chromium-merged" ] && \ + mv reports/playwright-report-chromium-merged reports/playwright-report-chromium + + chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh + ./scripts/cicd/pr-playwright-deploy-and-comment.sh \ + "${{ steps.pr.outputs.result }}" \ + "${{ github.event.workflow_run.head_branch }}" \ + "completed" \ No newline at end of file diff --git a/.github/workflows/test-ui.yaml b/.github/workflows/test-ui.yaml index 451c4b903..f8f6cf955 100644 --- a/.github/workflows/test-ui.yaml +++ b/.github/workflows/test-ui.yaml @@ -284,3 +284,65 @@ jobs: name: playwright-report-chromium path: ComfyUI_frontend/playwright-report/ retention-days: 30 + + #### 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: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false + permissions: + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get start time + id: start-time + run: echo "time=$(date -u '+%m/%d/%Y, %I:%M:%S %p')" >> $GITHUB_OUTPUT + + - name: Post starting comment + env: + GITHUB_TOKEN: ${{ github.token }} + 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 }}" \ + "starting" \ + "${{ steps.start-time.outputs.time }}" + + # Deploy and comment for non-forked PRs only + deploy-and-comment: + needs: [playwright-tests, merge-reports] + runs-on: ubuntu-latest + if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false + permissions: + pull-requests: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download all playwright reports + uses: actions/download-artifact@v4 + with: + pattern: playwright-report-* + path: reports + + - name: Make deployment script executable + run: chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh + + - name: Deploy reports and comment on PR + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + GITHUB_TOKEN: ${{ github.token }} + run: | + ./scripts/cicd/pr-playwright-deploy-and-comment.sh \ + "${{ github.event.pull_request.number }}" \ + "${{ github.head_ref }}" \ + "completed" + #### END Deployment and commenting (non-forked PRs only) \ No newline at end of file diff --git a/scripts/cicd/pr-playwright-deploy-and-comment.sh b/scripts/cicd/pr-playwright-deploy-and-comment.sh new file mode 100755 index 000000000..767a7f514 --- /dev/null +++ b/scripts/cicd/pr-playwright-deploy-and-comment.sh @@ -0,0 +1,241 @@ +#!/bin/bash +set -e + +# Deploy Playwright test reports to Cloudflare Pages and comment on PR +# Usage: ./pr-playwright-deploy-and-comment.sh [start_time] + +# Input validation +# Validate PR number is numeric +case "$1" in + ''|*[!0-9]*) + echo "Error: PR_NUMBER must be numeric" >&2 + exit 1 + ;; +esac +PR_NUMBER="$1" + +# Sanitize and validate branch name (allow alphanumeric, dots, dashes, underscores, slashes) +BRANCH_NAME=$(echo "$2" | sed 's/[^a-zA-Z0-9._/-]//g') +if [ -z "$BRANCH_NAME" ]; then + echo "Error: Invalid or empty branch name" >&2 + exit 1 +fi + +# Validate status parameter +STATUS="${3:-completed}" +case "$STATUS" in + starting|completed) ;; + *) + echo "Error: STATUS must be 'starting' or 'completed'" >&2 + exit 1 + ;; +esac + +START_TIME="${4:-$(date -u '+%m/%d/%Y, %I:%M:%S %p')}" + +# Required environment variables +: "${GITHUB_TOKEN:?GITHUB_TOKEN is required}" +: "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}" + +# Cloudflare variables only required for deployment +if [ "$STATUS" = "completed" ]; then + : "${CLOUDFLARE_API_TOKEN:?CLOUDFLARE_API_TOKEN is required for deployment}" + : "${CLOUDFLARE_ACCOUNT_ID:?CLOUDFLARE_ACCOUNT_ID is required for deployment}" +fi + +# Configuration +COMMENT_MARKER="" +# Use dot notation for artifact names (as Playwright creates them) +BROWSERS="chromium chromium-2x chromium-0.5x mobile-chrome" + +# Install wrangler if not available (output to stderr for debugging) +if ! command -v wrangler > /dev/null 2>&1; then + echo "Installing wrangler v4..." >&2 + npm install -g wrangler@^4.0.0 >&2 || { + echo "Failed to install wrangler" >&2 + echo "failed" + return + } +fi + +# Deploy a single browser report, WARN: ensure inputs are sanitized before calling this function +deploy_report() { + dir="$1" + browser="$2" + branch="$3" + + [ ! -d "$dir" ] && echo "failed" && return + + + # Project name with dots converted to dashes for Cloudflare + sanitized_browser=$(echo "$browser" | sed 's/\./-/g') + project="comfyui-playwright-${sanitized_browser}" + + echo "Deploying $browser to project $project on branch $branch..." >&2 + + # Try deployment up to 3 times + i=1 + while [ $i -le 3 ]; do + echo "Deployment attempt $i of 3..." >&2 + # Branch and project are already sanitized, use them directly + # Branch was sanitized at script start, project uses sanitized_browser + if output=$(wrangler pages deploy "$dir" \ + --project-name="$project" \ + --branch="$branch" 2>&1); then + + # Extract URL from output (improved regex for valid URL characters) + url=$(echo "$output" | grep -oE 'https://[a-zA-Z0-9.-]+\.pages\.dev\S*' | head -1) + result="${url:-https://${branch}.${project}.pages.dev}" + echo "Success! URL: $result" >&2 + echo "$result" # Only this goes to stdout for capture + return + else + echo "Deployment failed on attempt $i: $output" >&2 + fi + [ $i -lt 3 ] && sleep 10 + i=$((i + 1)) + done + + echo "failed" +} + +# Post or update GitHub comment +post_comment() { + body="$1" + temp_file=$(mktemp) + echo "$body" > "$temp_file" + + if command -v gh > /dev/null 2>&1; then + # Find existing comment ID + existing=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \ + --jq ".[] | select(.body | contains(\"$COMMENT_MARKER\")) | .id" | head -1) + + if [ -n "$existing" ]; then + # Update specific comment by ID + gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$existing" \ + --field body="$(cat "$temp_file")" + else + # Create new comment + gh pr comment "$PR_NUMBER" --body-file "$temp_file" + fi + else + echo "GitHub CLI not available, outputting comment:" + cat "$temp_file" + fi + + rm -f "$temp_file" +} + +# Main execution +if [ "$STATUS" = "starting" ]; then + # Post starting comment + comment=$(cat < **Tests are starting...** + +โฐ Started at: $START_TIME UTC + +### ๐Ÿš€ Running Tests +- ๐Ÿงช **chromium**: Running tests... +- ๐Ÿงช **chromium-0.5x**: Running tests... +- ๐Ÿงช **chromium-2x**: Running tests... +- ๐Ÿงช **mobile-chrome**: Running tests... + +--- +โฑ๏ธ Please wait while tests are running... +EOF +) + post_comment "$comment" + +else + # Deploy and post completion comment + # Convert branch name to Cloudflare-compatible format (lowercase, only alphanumeric and dashes) + cloudflare_branch=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]' | \ + sed 's/[^a-z0-9-]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g') + + echo "Looking for reports in: $(pwd)/reports" + echo "Available reports:" + ls -la reports/ 2>/dev/null || echo "Reports directory not found" + + # Deploy all reports in parallel and collect URLs + temp_dir=$(mktemp -d) + pids="" + i=0 + + # Start parallel deployments + for browser in $BROWSERS; do + if [ -d "reports/playwright-report-$browser" ]; then + echo "Found report for $browser, deploying in parallel..." + ( + url=$(deploy_report "reports/playwright-report-$browser" "$browser" "$cloudflare_branch") + echo "$url" > "$temp_dir/$i.url" + echo "Deployment result for $browser: $url" + ) & + pids="$pids $!" + else + echo "Report not found for $browser at reports/playwright-report-$browser" + echo "failed" > "$temp_dir/$i.url" + fi + i=$((i + 1)) + done + + # Wait for all deployments to complete + for pid in $pids; do + wait $pid + done + + # Collect URLs in order + urls="" + i=0 + for browser in $BROWSERS; do + if [ -f "$temp_dir/$i.url" ]; then + url=$(cat "$temp_dir/$i.url") + else + url="failed" + fi + if [ -z "$urls" ]; then + urls="$url" + else + urls="$urls $url" + fi + i=$((i + 1)) + done + + # Clean up temp directory + rm -rf "$temp_dir" + + # Generate completion comment + comment="$COMMENT_MARKER +## ๐ŸŽญ Playwright Test Results + +โœ… **Tests completed successfully!** + +โฐ Completed at: $(date -u '+%m/%d/%Y, %I:%M:%S %p') UTC + +### ๐Ÿ“Š Test Reports by Browser" + + # Add browser results + i=0 + for browser in $BROWSERS; do + # Get URL at position i + url=$(echo "$urls" | cut -d' ' -f$((i + 1))) + + if [ "$url" != "failed" ] && [ -n "$url" ]; then + comment="$comment +- โœ… **${browser}**: [View Report](${url})" + else + comment="$comment +- โŒ **${browser}**: Deployment failed" + fi + i=$((i + 1)) + done + + comment="$comment + +--- +๐ŸŽ‰ Click on the links above to view detailed test results for each browser configuration." + + post_comment "$comment" +fi \ No newline at end of file