test: migrate MMConnect multichain e2e from performance BrowserStack to Appium smoke CI #7375
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: CI (Namespace shadow) | |
| # Dispatcher for Namespace shadow runs (INFRA-3631 / INFRA-3678). | |
| # | |
| # It dispatches `ci.yml` as a separate `workflow_dispatch` run with | |
| # `runner_provider=namespace` (not `workflow_call`), so shadow runs live in the | |
| # Actions tab only and never appear in PR checks or the merge queue. The OIDC, | |
| # token-exchange, and dispatch steps use continue-on-error so this job's own | |
| # check always concludes success even when the dispatch cannot run. | |
| # | |
| # Shadow runs are iOS-only: ci.yml skips Android and Linux-only jobs when | |
| # runner_provider is namespace (see the BITRISE/NAMESPACE-SHADOW markers there), | |
| # mirroring the Bitrise shadow benchmark scope. | |
| # | |
| # Authentication: Token Exchange Service (same pattern as triage-forwarder.yml — | |
| # OIDC → POST /api/exchange/token). Prerequisites: | |
| # - Actions variable TOKEN_EXCHANGE_URL (already used elsewhere, e.g. triage-forwarder). | |
| # - Rego policy mm-metamask-mobile-namespace-shadow-ci-token-exchange in | |
| # token-exchange-service (matches the OIDC `workflow_ref` claim). | |
| # | |
| # Auto-dispatch gate: set repo Actions variable NAMESPACE_SHADOW_AUTO_DISPATCH=true | |
| # to dispatch on PR/push. Default (unset/false) = manual workflow_dispatch only. | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| paths-ignore: | |
| - "docs/**" | |
| - "**/*.md" | |
| - ".github/CODEOWNERS" | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ns-shadow-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| id-token: write | |
| jobs: | |
| dispatch-shadow: | |
| name: "[shadow] Dispatch" | |
| runs-on: ubuntu-latest | |
| # Fork PRs use head.repo != github.repository — skip (no shadow, no token exchange). | |
| # PR/push auto-dispatch requires NAMESPACE_SHADOW_AUTO_DISPATCH=true; workflow_dispatch always runs. | |
| if: >- | |
| ${{ | |
| (github.event_name == 'workflow_dispatch' || vars.NAMESPACE_SHADOW_AUTO_DISPATCH == 'true') && | |
| (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) | |
| }} | |
| steps: | |
| - name: Get OIDC token for token-exchange-service | |
| id: oidc | |
| continue-on-error: true | |
| run: | | |
| set -euo pipefail | |
| OIDC_TOKEN=$(curl -sSf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ | |
| "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=api://token-exchange-service" | jq -r '.value') | |
| echo "::add-mask::$OIDC_TOKEN" | |
| echo "oidc_token=$OIDC_TOKEN" >> "$GITHUB_OUTPUT" | |
| - name: Exchange for installation token (scoped permissions) | |
| id: exchange | |
| continue-on-error: true | |
| env: | |
| OIDC_TOKEN: ${{ steps.oidc.outputs.oidc_token }} | |
| TOKEN_EXCHANGE_URL: ${{ vars.TOKEN_EXCHANGE_URL }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${TOKEN_EXCHANGE_URL}" ]; then | |
| echo "::error::TOKEN_EXCHANGE_URL Actions variable is not set. Configure it at org or repo level (see triage-forwarder.yml)." | |
| exit 1 | |
| fi | |
| RESPONSE=$(curl -sSf -X POST "${TOKEN_EXCHANGE_URL}/api/exchange/token" \ | |
| -H "Content-Type: application/json" \ | |
| -d "$(jq -cn \ | |
| --arg oidcToken "$OIDC_TOKEN" \ | |
| --arg targetRepo "${{ github.repository }}" \ | |
| '{oidcToken: $oidcToken, targetRepo: $targetRepo, requested_permissions: {metadata: "read", contents: "read", actions: "write"}}')") | |
| STATUS=$(echo "$RESPONSE" | jq -r '.status // "ok"') | |
| if [[ "$STATUS" == "fail" ]]; then | |
| MSG=$(echo "$RESPONSE" | jq -r '.message // "unknown"') | |
| echo "::error::Token exchange failed: ${MSG}" | |
| echo "::notice::Ensure token-exchange-service policy mm-metamask-mobile-namespace-shadow-ci-token-exchange is deployed (INFRA-3631)." | |
| exit 1 | |
| fi | |
| TOKEN=$(echo "$RESPONSE" | jq -r '.token // empty') | |
| if [[ -z "$TOKEN" || "$TOKEN" == "null" ]]; then | |
| echo "::error::Token exchange returned no token" | |
| exit 1 | |
| fi | |
| echo "::add-mask::$TOKEN" | |
| echo "token=$TOKEN" >> "$GITHUB_OUTPUT" | |
| - name: Dispatch ci.yml on Namespace (best effort) | |
| id: dispatch | |
| continue-on-error: true | |
| env: | |
| GH_TOKEN: ${{ steps.exchange.outputs.token }} | |
| REPO: ${{ github.repository }} | |
| REF: ${{ github.head_ref || github.ref_name }} | |
| PR_NUMBER: ${{ github.event.pull_request.number || '' }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| run: | | |
| set -euo pipefail | |
| echo "Dispatching ci.yml on ref='${REF}' (PR='${PR_NUMBER:-none}', sha='${HEAD_SHA}')" | |
| # gh prints the created workflow run URL to stdout (see `gh workflow run --help`). | |
| # Capture it here — do not use `gh run list` polling, which can return the wrong run. | |
| # This step never fails the job: log ::error:: and leave run_url empty on failure. | |
| # | |
| # Local checks: parse only — DISPATCH_LOG='https://github.com/org/repo/actions/runs/123'; printf '%s\n' "$DISPATCH_LOG" | grep -oE 'https://[^[:space:]]+/actions/runs/[0-9]+' | tail -1 | |
| # Or run gh against any workflow_dispatch workflow on your branch that needs no inputs; | |
| # reuse the same DISPATCH_LOG capture + grep pipeline (omit -f flags when not needed). | |
| set +e | |
| DISPATCH_LOG="$(gh workflow run ci.yml \ | |
| --repo "$REPO" \ | |
| --ref "$REF" \ | |
| -f runner_provider=namespace \ | |
| -f pr_number="$PR_NUMBER" \ | |
| -f head_sha="$HEAD_SHA" 2>&1)" | |
| gh_status=$? | |
| set -e | |
| RUN_URL="" | |
| if [[ "$gh_status" -ne 0 ]]; then | |
| echo "::error::gh workflow run failed (exit ${gh_status}); shadow dispatch skipped — this job still succeeds." | |
| echo "$DISPATCH_LOG" | |
| else | |
| echo "$DISPATCH_LOG" | |
| RUN_URL="$(printf '%s\n' "$DISPATCH_LOG" | grep -oE 'https://[^[:space:]]+/actions/runs/[0-9]+' | tail -1 || true)" | |
| if [[ -z "$RUN_URL" ]]; then | |
| echo "::warning::Could not parse workflow run URL from gh output; open the Actions tab for ci.yml on ref ${REF}." | |
| fi | |
| fi | |
| echo "run_url=${RUN_URL}" >> "$GITHUB_OUTPUT" | |
| - name: Step summary | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number || '' }} | |
| PR_URL: ${{ github.event.pull_request.html_url || '' }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| REF: ${{ github.head_ref || github.ref_name }} | |
| RUN_URL: ${{ steps.dispatch.outputs.run_url }} | |
| run: | | |
| { | |
| echo "## Namespace shadow CI dispatched" | |
| echo | |
| echo "| Field | Value |" | |
| echo "|---|---|" | |
| if [ -n "$PR_NUMBER" ]; then | |
| echo "| PR | [#${PR_NUMBER}](${PR_URL}) |" | |
| fi | |
| echo "| Ref | \`${REF}\` |" | |
| echo "| Head SHA | \`${HEAD_SHA}\` |" | |
| if [ -n "${RUN_URL}" ]; then | |
| echo "| Shadow run | ${RUN_URL} |" | |
| else | |
| echo "| Shadow run | (not yet visible — check the Actions tab) |" | |
| fi | |
| echo | |
| echo "_Shadow CI runs **fire-and-forget**: it does not appear in this PR's checks and never blocks the merge queue. Benchmark data is collected by \`scripts/namespace-benchmark.sh\`._" | |
| } >> "$GITHUB_STEP_SUMMARY" |