BangSo/.github/workflows/publish-playwright-report.yml

179 lines
8.2 KiB
YAML

name: publish Playwright report
on:
workflow_run:
workflows: [ci]
types: [completed]
permissions:
actions: read
contents: read
issues: write
pull-requests: write
concurrency:
group: playwright-publication
cancel-in-progress: false
jobs:
publish:
if: >-
(github.event.workflow_run.conclusion == 'success' ||
github.event.workflow_run.conclusion == 'failure' ||
github.event.workflow_run.conclusion == 'timed_out') &&
(github.event.workflow_run.event == 'pull_request' ||
(github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'))
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
# workflow_run has repository secrets. Always use trusted default-branch code here;
# never check out or execute the contributor's pull-request revision.
- uses: actions/checkout@v5
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Read Playwright job result
id: playwright
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RUN_ID: ${{ github.event.workflow_run.id }}
shell: bash
run: |
result="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}/jobs?per_page=100" \
--jq '[.jobs[] | select(.name == "Web E2E / Web E2E") | .conclusion] | unique | if length == 1 then .[0] else error("expected exactly one Playwright job") end')"
case "$result" in
success|failure) ;;
timed_out) ;;
*) echo "Unexpected Playwright job conclusion: $result" >&2; exit 1 ;;
esac
echo "job_result=$result" >> "$GITHUB_OUTPUT"
if [[ "$result" == "timed_out" ]]; then result="failure"; fi
echo "result=$result" >> "$GITHUB_OUTPUT"
- name: Resolve pull request
if: github.event.workflow_run.event == 'pull_request'
id: pull_request
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
HEAD_OWNER: ${{ github.event.workflow_run.head_repository.owner.login }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
shell: bash
run: |
pulls="$(gh api --method GET \
"repos/${GITHUB_REPOSITORY}/pulls?state=all&head=${HEAD_OWNER}:${HEAD_BRANCH}&per_page=100")"
number="$(jq -r --arg sha "$HEAD_SHA" \
'[.[] | select(.head.sha == $sha)] | if length == 1 then .[0].number else empty end' \
<<<"$pulls")"
if [[ -z "$number" ]]; then
echo "::warning::Could not resolve a pull request for ${HEAD_OWNER}:${HEAD_BRANCH} at ${HEAD_SHA}."
fi
echo "number=$number" >> "$GITHUB_OUTPUT"
- name: Check for Playwright artifact
id: artifact
env:
ARTIFACT_NAME: playwright-artifacts-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PLAYWRIGHT_JOB_RESULT: ${{ steps.playwright.outputs.job_result }}
RUN_ID: ${{ github.event.workflow_run.id }}
shell: bash
run: |
artifacts="$(gh api --method GET \
"repos/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}/artifacts?name=${ARTIFACT_NAME}")"
count="$(jq --arg name "$ARTIFACT_NAME" \
'[.artifacts[] | select(.name == $name and .expired == false)] | length' \
<<<"$artifacts")"
if [[ "$count" == "1" ]]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
elif [[ "$count" == "0" && "$PLAYWRIGHT_JOB_RESULT" == "timed_out" ]]; then
echo "::warning::The timed-out Playwright job ended before it uploaded an artifact. Publishing an empty gallery."
echo "exists=false" >> "$GITHUB_OUTPUT"
else
echo "Expected exactly one unexpired Playwright artifact; found $count." >&2
exit 1
fi
- name: Download Playwright artifacts
if: steps.artifact.outputs.exists == 'true'
uses: actions/download-artifact@v4
with:
name: playwright-artifacts-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
path: .
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- name: Publish trusted Playwright screenshot gallery
id: publish
env:
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ vars.S3_REGION }}
AWS_EC2_METADATA_DISABLED: "true"
S3_BUCKET: ${{ vars.S3_BUCKET }}
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
PLAYWRIGHT_PUBLIC_BASE_URL: ${{ vars.PLAYWRIGHT_PUBLIC_BASE_URL }}
PLAYWRIGHT_RESULT: ${{ steps.playwright.outputs.result }}
PLAYWRIGHT_RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
PLAYWRIGHT_RUN_ID: ${{ github.event.workflow_run.id }}
PLAYWRIGHT_RUN_NUMBER: ${{ github.event.workflow_run.run_number }}
PLAYWRIGHT_RUN_URL: ${{ github.event.workflow_run.html_url }}
PLAYWRIGHT_SHA: ${{ github.event.workflow_run.head_sha }}
PLAYWRIGHT_EVENT: ${{ github.event.workflow_run.event }}
PLAYWRIGHT_BRANCH: ${{ github.event.workflow_run.head_branch }}
PLAYWRIGHT_PR_NUMBER: ${{ steps.pull_request.outputs.number }}
PLAYWRIGHT_REPOSITORY_URL: ${{ github.server_url }}/${{ github.repository }}
PLAYWRIGHT_PUBLISH_REPORT: ${{ github.event.workflow_run.event == 'push' }}
run: bash scripts/publish-playwright-report.sh
- name: Link screenshot gallery from pull request
if: >-
github.event.workflow_run.event == 'pull_request' &&
steps.pull_request.outputs.number != '' &&
steps.publish.outputs.latest_pr_run == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PLAYWRIGHT_PUBLIC_BASE_URL: ${{ vars.PLAYWRIGHT_PUBLIC_BASE_URL }}
PR_NUMBER: ${{ steps.pull_request.outputs.number }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
SHA: ${{ github.event.workflow_run.head_sha }}
shell: bash
run: |
if [[ ! "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then
echo "PR_NUMBER must be a positive integer." >&2
exit 1
fi
public_base_url="${PLAYWRIGHT_PUBLIC_BASE_URL%/}"
gallery_url="$public_base_url/prs/$PR_NUMBER/index.html"
review_path="$GITHUB_WORKSPACE/.tmp/playwright-dashboard/screenshots/review.json"
if [[ ! -f "$review_path" ]]; then
echo "Missing Playwright review manifest at $review_path." >&2
exit 1
fi
changed_paths_file="$(mktemp)"
gh api --paginate \
"repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files?per_page=100" \
--jq '.[].filename' > "$changed_paths_file"
body="$(
PLAYWRIGHT_GALLERY_URL="$gallery_url" \
PLAYWRIGHT_DASHBOARD_URL="$public_base_url/index.html" \
PLAYWRIGHT_RUN_URL="$RUN_URL" \
PLAYWRIGHT_SHA="$SHA" \
pnpm exec tsx packages/testkit/src/cli/build-playwright-pr-screenshot-comment.ts \
"$review_path" \
"$changed_paths_file"
)"
marker="<!-- rakazo-playwright-screenshots -->"
comment_id="$(gh api --paginate --slurp \
"repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments?per_page=100" | \
jq -r --arg marker "$marker" \
'[.[][] | select(.user.login == "github-actions[bot]" and (.body | contains($marker)))] | last | .id // empty')"
payload="$(jq -n --arg body "$body" '{body: $body}')"
if [[ -n "$comment_id" ]]; then
gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" --input - <<<"$payload"
else
gh api --method POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --input - <<<"$payload"
fi