Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion .github/workflows/ci-host.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -938,18 +938,77 @@ jobs:
needs: [host-test, check-percy]
runs-on: ubuntu-latest
timeout-minutes: 15
# Job-level so the reject step can test for their presence in its `if:` —
# a step's own `env:` block is not in scope for that step's condition.
env:
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/init

- name: Finalise Percy
run: npx percy build:finalize
id: finalize
run: |
set -eo pipefail
npx percy build:finalize 2>&1 | tee /tmp/percy-finalize.log
# Percy build URLs are https://percy.io/<org>/<product>/<project>/builds/<id>.
# Allow 1-3 segments before /builds so a change to that structure
# doesn't break the parse, and tolerate no match — the reject step
# is gated on a non-empty build id anyway.
BUILD_URL=$(grep -oE 'https://percy\.io/[A-Za-z0-9_-]+(/[A-Za-z0-9_-]+){1,3}/builds/[0-9]+' /tmp/percy-finalize.log | tail -1) || true
echo "build_id=${BUILD_URL##*/}" >> "$GITHUB_OUTPUT"
echo "build_url=$BUILD_URL" >> "$GITHUB_OUTPUT"
if [ -z "$BUILD_URL" ]; then
echo "::warning::Could not parse the Percy build id from finalize output; the reject step will be skipped."
fi
working-directory: packages/host
env:
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN_HOST }}
PERCY_PARALLEL_NONCE: ${{ github.run_id }}-${{ github.run_attempt }}

# A shard that dies contributes no snapshots, but finalize still seals
# the build. On `main`, where the project's auto-approve-branch-filter
# promotes builds without review, that partial set becomes the baseline
# every later branch is compared against — and the snapshots the dead
# shard would have produced resurface as diffs on unrelated PRs.
# Rejecting the build keeps it from becoming a baseline.
#
# `percy build:reject` authenticates with BrowserStack account
# credentials rather than the project token: @percy/cli-build reads
# BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY (see
# fetchCredentials in @percy/cli-build). Both are required — the CLI
# exits non-zero when either is missing — so the step runs only when
# both are present and otherwise skips itself rather than failing the
# job.
- name: Reject the Percy build when a host shard failed
if: >-
needs.host-test.result != 'success'
&& steps.finalize.outputs.build_id != ''
&& env.BROWSERSTACK_USERNAME != ''

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate rejection on both BrowserStack credentials

When BROWSERSTACK_USERNAME is configured but BROWSERSTACK_ACCESS_KEY is missing or misnamed, this condition still runs percy build:reject without the required credential. The command then fails the finalize job instead of following the documented behavior of skipping rejection and emitting the configuration warning; require both environment variables before running the reject step, and treat either missing value as unrejected.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in 2955534

&& env.BROWSERSTACK_ACCESS_KEY != ''
run: |
set -eo pipefail
npx percy build:reject "${{ steps.finalize.outputs.build_id }}"
echo "::warning::Rejected Percy build ${{ steps.finalize.outputs.build_url }} — host-test finished as '${{ needs.host-test.result }}', so its snapshot set is incomplete and must not become a baseline."
working-directory: packages/host
env:
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN_HOST }}

# The exact complement of the reject step's condition, so an incomplete
# build never passes through both silently.
- name: Note that an incomplete build was left unrejected
if: >-
needs.host-test.result != 'success'
&& (
steps.finalize.outputs.build_id == ''
|| env.BROWSERSTACK_USERNAME == ''
|| env.BROWSERSTACK_ACCESS_KEY == ''
)
run: |
echo "::warning::host-test finished as '${{ needs.host-test.result }}', so this Percy build's snapshot set is incomplete, but it could not be rejected and may be auto-approved into the baseline. Percy build id: '${{ steps.finalize.outputs.build_id }}' (empty means the finalize output could not be parsed); BROWSERSTACK_USERNAME set: ${{ env.BROWSERSTACK_USERNAME != '' }}; BROWSERSTACK_ACCESS_KEY set: ${{ env.BROWSERSTACK_ACCESS_KEY != '' }}."

host-merge-reports-and-publish:
name: Merge Host reports and publish
if: ${{ !cancelled() && (needs.host-test.result == 'success' || needs.host-test.result == 'failure') }}
Expand Down
Loading