chore: gate release and hotfix PRs on the InWorld suite #1422
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Visual Regression | |
| # Slash-command-driven visual regression runner. The actual mechanics live in | |
| # decentraland/explorer-automation's `run-visual-suite.yml` reusable workflow; | |
| # this file only handles trigger validation, branch matching, and dispatching | |
| # to that workflow. | |
| # | |
| # Trigger: | |
| # Comment "/visual-tests" on a PR. Restricted to OWNER / MEMBER / | |
| # COLLABORATOR author associations — anyone else's comment is silently | |
| # ignored to keep this from becoming a free-CPU buffet for drive-by PRs. | |
| # | |
| # Branch matching: | |
| # We look up the PR's head branch in explorer-automation. If that branch | |
| # exists, we pass it as `tests_ref` so visual baselines and test fixtures | |
| # from the matching feature branch are used. Otherwise we fall back to | |
| # explorer-automation's default branch (metaforge handles the empty case | |
| # internally). | |
| # | |
| # Required secrets / vars (configure once on the repo): | |
| # | |
| # Used directly by this dispatcher: | |
| # - vars.EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL Constructs the Explorer build download URL | |
| # | |
| # Inherited by the reusable workflow via `secrets: inherit` — must exist | |
| # on this repo even though the dispatcher itself doesn't reference them: | |
| # - secrets.ALTTESTER_LICENSE | |
| # - secrets.REPOS_READ_ONLY_TOKEN | |
| # - secrets.DEV_EXPLORER_TEAM_S3_BUCKET | |
| # - secrets.DEV_EXPLORER_TEAM_AWS_DEFAULT_REGION | |
| # - secrets.DEV_EXPLORER_TEAM_AWS_ACCESS_KEY_ID | |
| # - secrets.DEV_EXPLORER_TEAM_AWS_SECRET_ACCESS_KEY | |
| on: | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| concurrency: | |
| group: visual-regression-${{ github.event.issue.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| resolve: | |
| name: Resolve trigger | |
| if: | | |
| github.event.issue.pull_request && | |
| startsWith(github.event.comment.body, '/visual-tests') | |
| runs-on: ubuntu-latest | |
| outputs: | |
| authorized: ${{ steps.gate.outputs.authorized }} | |
| pr_number: ${{ steps.pr.outputs.number }} | |
| build_url: ${{ steps.pr.outputs.build_url }} | |
| tests_ref: ${{ steps.match.outputs.tests_ref }} | |
| head_sha: ${{ steps.pr.outputs.head_sha }} | |
| head_short_sha: ${{ steps.pr.outputs.head_short_sha }} | |
| head_ref: ${{ steps.pr.outputs.head_ref }} | |
| steps: | |
| - name: Gate by author association | |
| id: gate | |
| env: | |
| ASSOC: ${{ github.event.comment.author_association }} | |
| run: | | |
| set -euo pipefail | |
| case "$ASSOC" in | |
| OWNER|MEMBER|COLLABORATOR) | |
| echo "authorized=true" >> "$GITHUB_OUTPUT" | |
| ;; | |
| *) | |
| echo "::notice::Ignoring /visual-tests from $ASSOC ${{ github.event.comment.user.login }} — write access required." | |
| echo "authorized=false" >> "$GITHUB_OUTPUT" | |
| ;; | |
| esac | |
| - name: React to the trigger comment | |
| if: steps.gate.outputs.authorized == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh api -X POST \ | |
| "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ | |
| -f content=eyes >/dev/null | |
| - name: Resolve PR head + build URL | |
| id: pr | |
| if: steps.gate.outputs.authorized == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_NUMBER: ${{ github.event.issue.number }} | |
| PUBLIC_URL_PREFIX: ${{ vars.EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL }} | |
| run: | | |
| set -euo pipefail | |
| # 1. Fetch PR head — issue_comment events don't carry it in the payload. | |
| PR_JSON=$(gh api "repos/${{ github.repository }}/pulls/${PR_NUMBER}") | |
| HEAD_SHA=$(echo "$PR_JSON" | jq -r '.head.sha') | |
| HEAD_REF=$(echo "$PR_JSON" | jq -r '.head.ref') | |
| SHORT_SHA="${HEAD_SHA:0:7}" | |
| # 2. Find the most recent successful Unity Cloud Build run for this | |
| # SHA, scoped to the build workflow file — a repo-wide head_sha | |
| # listing gets crowded out by bot runs sharing the SHA (90+ on a | |
| # typical release commit) and the build falls past the first page. | |
| # Only pull_request/push builds qualify — dispatch builds can | |
| # carry non-default options (e.g. profiling) that break the | |
| # determinism visual tests rely on. | |
| RUN_JSON=$(gh api "repos/${{ github.repository }}/actions/workflows/build-unitycloud.yml/runs?head_sha=${HEAD_SHA}&status=success&per_page=20" \ | |
| --jq '[.workflow_runs[] | select(.event == "pull_request" or .event == "push")] | .[0]') | |
| if [ -z "$RUN_JSON" ] || [ "$RUN_JSON" = "null" ]; then | |
| echo "::error::No successful Unity Cloud Build run found for SHA ${SHORT_SHA}. Is the build still in progress, or did it fail?" | |
| exit 1 | |
| fi | |
| RUN_NUMBER=$(echo "$RUN_JSON" | jq -r '.run_number') | |
| ORIGINAL_EVENT=$(echo "$RUN_JSON" | jq -r '.event') | |
| RUN_BRANCH=$(echo "$RUN_JSON" | jq -r '.head_branch') | |
| # The S3 prefix and branch segment depend on the event and branch of | |
| # the run that produced the build (see build-unitycloud.yml), not on | |
| # this PR. Derive both from the run so /visual-tests also works on | |
| # PRs whose only build for the SHA ran on another trigger — e.g. | |
| # bot-created release PRs, whose tip is built by the push to dev. | |
| case "$ORIGINAL_EVENT" in | |
| pull_request) BUILD_PREFIX="pr" ;; | |
| push) BUILD_PREFIX="pu" ;; | |
| merge_group) BUILD_PREFIX="mg" ;; | |
| workflow_dispatch) BUILD_PREFIX="wd" ;; | |
| workflow_call) BUILD_PREFIX="wc" ;; | |
| schedule) BUILD_PREFIX="sc" ;; | |
| *) BUILD_PREFIX="gn" ;; | |
| esac | |
| ARTIFACT_PATH="@dcl/${{ github.event.repository.name }}/branch/${RUN_BRANCH}/${BUILD_PREFIX}-${RUN_NUMBER}-${SHORT_SHA}" | |
| BUILD_URL="${PUBLIC_URL_PREFIX}/${ARTIFACT_PATH}/Decentraland_macos.zip" | |
| { | |
| echo "number=${PR_NUMBER}" | |
| echo "head_sha=${HEAD_SHA}" | |
| echo "head_short_sha=${SHORT_SHA}" | |
| echo "head_ref=${HEAD_REF}" | |
| echo "build_url=${BUILD_URL}" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Look up matching explorer-automation branch | |
| id: match | |
| if: steps.gate.outputs.authorized == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.REPOS_READ_ONLY_TOKEN }} | |
| HEAD_REF: ${{ steps.pr.outputs.head_ref }} | |
| run: | | |
| set -euo pipefail | |
| # 200 = branch exists on explorer-automation, use it as tests_ref. | |
| # 404 = no matching branch, fall back to default (empty string => | |
| # metaforge resolves to main). | |
| if gh api "repos/decentraland/explorer-automation/branches/${HEAD_REF}" --silent >/dev/null 2>&1; then | |
| echo "tests_ref=${HEAD_REF}" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Using matching explorer-automation branch '${HEAD_REF}'." | |
| else | |
| echo "tests_ref=" >> "$GITHUB_OUTPUT" | |
| echo "::notice::No matching explorer-automation branch '${HEAD_REF}' — using default." | |
| fi | |
| run-suite: | |
| name: Run visual suite | |
| needs: resolve | |
| if: needs.resolve.outputs.authorized == 'true' | |
| # @main pins us to the merged version of the reusable workflow so PRs to | |
| # explorer-automation that touch run-visual-suite.yml don't accidentally | |
| # affect every unity-explorer PR's visual run. | |
| uses: decentraland/explorer-automation/.github/workflows/run-visual-suite.yml@main | |
| with: | |
| mode: test | |
| pr_number: ${{ needs.resolve.outputs.pr_number }} | |
| build_url: ${{ needs.resolve.outputs.build_url }} | |
| tests_ref: ${{ needs.resolve.outputs.tests_ref }} | |
| commit_sha: ${{ needs.resolve.outputs.head_short_sha }} | |
| branch_label: ${{ needs.resolve.outputs.head_ref }} | |
| secrets: inherit |