Skip to content

Commit 1d19a68

Browse files
authored
Merge branch 'main' into predict/crypto-data-plumbing
2 parents 1603e11 + b90a311 commit 1d19a68

11 files changed

Lines changed: 313 additions & 25 deletions

.github/workflows/auto-rc-ota-build-core.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,4 @@ jobs:
142142
build_commit_sha: ${{ needs.trigger-build.outputs.built_commit_sha }}
143143
build_version: ${{ needs.trigger-build.outputs.semantic_version }}
144144
build_number: ${{ needs.trigger-build.outputs.ios_version_code }}
145-
distribute_external: false
146145
secrets: inherit

.github/workflows/build-and-upload-to-testflight.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ on:
2020
type: string
2121
default: 'MetaMask BETA & Release Candidates'
2222
distribute_external:
23-
description: 'Whether to distribute to external testers. Defaults to false; nightly-build.yml relies on the script default (true) so it always distributes externally.'
23+
description: 'Whether to distribute to external testers. (default: false)'
2424
required: false
2525
type: boolean
2626
default: false
@@ -55,7 +55,7 @@ on:
5555
- 'MM Card Team'
5656
- 'Ramp Provider Testing'
5757
distribute_external:
58-
description: 'Whether to distribute to external testers'
58+
description: 'Whether to distribute to external testers (default: false)'
5959
required: false
6060
type: boolean
6161
default: false
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: Build Android
2+
3+
# Creates a temp branch, bumps version (build.yml), builds the Android APK/AAB.
4+
# Mirrors build-and-upload-to-testflight.yml but Android-only and without the upload step.
5+
# APK/AAB artifacts stay attached to the build.yml run via its existing Upload Android * steps.
6+
#
7+
on:
8+
workflow_call:
9+
inputs:
10+
source_branch:
11+
description: 'Branch, tag, or SHA to build'
12+
required: true
13+
type: string
14+
environment:
15+
description: 'Build environment / track. Must be one of: exp, beta, rc (enforced by validate-inputs).'
16+
required: true
17+
type: string
18+
upload_to_sentry:
19+
description: 'If true, enable Sentry CLI upload of JS source maps and native debug symbols during the build'
20+
required: false
21+
type: boolean
22+
default: false
23+
runner_provider:
24+
description: Runner provider forwarded from the caller
25+
required: false
26+
type: string
27+
default: current
28+
outputs:
29+
build_branch:
30+
description: 'Ephemeral build branch created from source_branch'
31+
value: ${{ jobs.prepare-build-branch.outputs.build_branch }}
32+
built_commit_sha:
33+
description: 'Resolved commit SHA at the version-bump commit after build succeeded'
34+
value: ${{ jobs.build.outputs.built_commit_sha }}
35+
semantic_version:
36+
description: 'package.json version at the built commit'
37+
value: ${{ jobs.build.outputs.semantic_version }}
38+
android_version_code:
39+
description: 'android/app/build.gradle versionCode at the built commit'
40+
value: ${{ jobs.build.outputs.android_version_code }}
41+
workflow_dispatch:
42+
inputs:
43+
source_branch:
44+
description: 'Branch, tag, or SHA to build'
45+
required: true
46+
type: string
47+
default: 'main'
48+
environment:
49+
description: 'Build environment / track'
50+
required: true
51+
type: choice
52+
options:
53+
- exp
54+
- beta
55+
- rc
56+
default: rc
57+
upload_to_sentry:
58+
description: 'Upload JS source maps and native debug symbols to Sentry during the build (requires Sentry auth in the build environment)'
59+
required: false
60+
type: boolean
61+
default: false
62+
runner_provider:
63+
description: Runner provider for this manual trial run
64+
required: false
65+
type: choice
66+
options:
67+
- current
68+
- namespace
69+
default: current
70+
71+
permissions:
72+
contents: write
73+
id-token: write
74+
75+
jobs:
76+
# workflow_call inputs cannot use `type: choice` in GitHub Actions, so we enforce
77+
# the allowed `environment` values at runtime to prevent other workflows from
78+
# invoking this wrapper with arbitrary build tracks.
79+
validate-inputs:
80+
name: Validate inputs
81+
runs-on: ubuntu-latest
82+
steps:
83+
- name: Validate environment input
84+
env:
85+
ENVIRONMENT: ${{ inputs.environment }}
86+
run: |
87+
case "$ENVIRONMENT" in
88+
exp|beta|rc) echo "✅ environment=$ENVIRONMENT is allowed" ;;
89+
*) echo "::error::Invalid environment '$ENVIRONMENT'. Must be one of: exp, beta, rc"; exit 1 ;;
90+
esac
91+
92+
prepare-build-branch:
93+
needs: [validate-inputs]
94+
uses: ./.github/workflows/create-build-branch.yml
95+
with:
96+
source_branch: ${{ inputs.source_branch }}
97+
secrets: inherit
98+
99+
build:
100+
name: Build Android (${{ inputs.environment }})
101+
needs: [prepare-build-branch]
102+
uses: ./.github/workflows/build.yml
103+
with:
104+
build_name: main-${{ inputs.environment }}
105+
platform: android
106+
skip_version_bump: false
107+
source_branch: ${{ needs.prepare-build-branch.outputs.build_branch }}
108+
upload_to_sentry: ${{ inputs.upload_to_sentry }}
109+
runner_provider: ${{ inputs.runner_provider }}
110+
secrets: inherit
111+
112+
cleanup-build-branch:
113+
name: Cleanup build branch
114+
needs: [prepare-build-branch, build]
115+
if: always()
116+
runs-on: ${{ inputs.runner_provider == 'namespace' && 'namespace-profile-metamask-ci-linux' || 'ubuntu-latest' }}
117+
steps:
118+
- uses: actions/checkout@v4
119+
with:
120+
token: ${{ secrets.PR_TOKEN || github.token }}
121+
- name: Delete temporary build branch
122+
env:
123+
BRANCH: ${{ needs.prepare-build-branch.outputs.build_branch }}
124+
run: |
125+
if [ -n "$BRANCH" ]; then
126+
git push origin --delete "$BRANCH" || true
127+
echo "🧹 Deleted build branch: $BRANCH"
128+
fi
Lines changed: 152 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,168 @@
11
name: CI (Namespace shadow)
22

3+
# Fire-and-forget dispatcher for the Namespace shadow benchmark (INFRA-3631).
4+
#
5+
# This workflow does NOT use `workflow_call`. Instead, it dispatches `ci.yml`
6+
# as a separate `workflow_dispatch` run with `runner_provider=namespace`.
7+
#
8+
# Why a dispatch instead of a call:
9+
# - `workflow_call` nests the called workflow's jobs into THIS workflow's
10+
# check suite on the PR. With the repo's merge queue ALLGREEN policy,
11+
# any shadow flake would block merges.
12+
# - `workflow_dispatch` runs live in the Actions tab only -- invisible to
13+
# PR checks. The shadow can fail freely; it never blocks the queue,
14+
# never duplicates commit statuses, never posts PR comments.
15+
#
16+
# Correlation back to the originating PR:
17+
# - The dispatched run's display name is set via `run-name` in ci.yml to
18+
# `ci [shadow PR #<num> @ <sha>]`, so it's identifiable in the Actions
19+
# tab at a glance.
20+
# - The dispatcher job posts a GitHub Actions step summary linking the PR
21+
# to the dispatched shadow run URL, reachable from the PR Checks tab.
22+
#
23+
# Authentication: Token Exchange Service (same pattern as triage-forwarder.yml
24+
# and shared-services-workflows deploy.yml — OIDC → POST /api/exchange/token).
25+
#
26+
# Prerequisites:
27+
# - Repo/org Actions variable: TOKEN_EXCHANGE_URL (already used elsewhere in
28+
# metamask-mobile, e.g. triage-forwarder).
29+
# - Rego policy in token-exchange-service: explicit policy
30+
# mm-metamask-mobile-namespace-shadow-ci-token-exchange (matches OIDC
31+
# `workflow_ref` claim for .github/workflows/ci-namespace-shadow.yml).
32+
#
33+
# Fork PRs: the job `if` below skips the entire job for fork head repos, so OIDC
34+
# exchange never runs for untrusted forks (same model as not exposing secrets).
35+
336
on:
437
pull_request:
538
types: [opened, synchronize, reopened, ready_for_review]
639
paths-ignore:
7-
- 'docs/**'
8-
- '**/*.md'
9-
- '.github/CODEOWNERS'
40+
- "docs/**"
41+
- "**/*.md"
42+
- ".github/CODEOWNERS"
1043
push:
1144
branches: [main]
1245
schedule:
13-
- cron: '0 * * * *'
46+
- cron: "0 * * * *"
1447
workflow_dispatch:
1548

1649
concurrency:
1750
group: ns-shadow-${{ github.workflow }}-${{ github.ref }}
1851
cancel-in-progress: true
1952

53+
permissions:
54+
contents: read
55+
id-token: write
56+
2057
jobs:
21-
shadow-ci:
22-
name: '[shadow] CI'
23-
uses: ./.github/workflows/ci.yml
24-
with:
25-
runner_provider: namespace
26-
secrets: inherit
58+
dispatch-shadow:
59+
name: "[shadow] Dispatch"
60+
runs-on: ubuntu-latest
61+
# Fork PRs use head.repo != github.repository — skip (no shadow, no token exchange).
62+
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
63+
steps:
64+
- name: Get OIDC token for token-exchange-service
65+
id: oidc
66+
run: |
67+
set -euo pipefail
68+
OIDC_TOKEN=$(curl -sSf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
69+
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=api://token-exchange-service" | jq -r '.value')
70+
echo "::add-mask::$OIDC_TOKEN"
71+
echo "oidc_token=$OIDC_TOKEN" >> "$GITHUB_OUTPUT"
72+
73+
- name: Exchange for installation token (scoped permissions)
74+
id: exchange
75+
env:
76+
OIDC_TOKEN: ${{ steps.oidc.outputs.oidc_token }}
77+
TOKEN_EXCHANGE_URL: ${{ vars.TOKEN_EXCHANGE_URL }}
78+
run: |
79+
set -euo pipefail
80+
if [ -z "${TOKEN_EXCHANGE_URL}" ]; then
81+
echo "::error::TOKEN_EXCHANGE_URL Actions variable is not set. Configure it at org or repo level (see triage-forwarder.yml)."
82+
exit 1
83+
fi
84+
RESPONSE=$(curl -sSf -X POST "${TOKEN_EXCHANGE_URL}/api/exchange/token" \
85+
-H "Content-Type: application/json" \
86+
-d "$(jq -cn \
87+
--arg oidcToken "$OIDC_TOKEN" \
88+
--arg targetRepo "${{ github.repository }}" \
89+
'{oidcToken: $oidcToken, targetRepo: $targetRepo, requested_permissions: {metadata: "read", contents: "read", actions: "write"}}')")
90+
STATUS=$(echo "$RESPONSE" | jq -r '.status // "ok"')
91+
if [[ "$STATUS" == "fail" ]]; then
92+
MSG=$(echo "$RESPONSE" | jq -r '.message // "unknown"')
93+
echo "::error::Token exchange failed: ${MSG}"
94+
echo "::notice::Ensure token-exchange-service policy mm-metamask-mobile-namespace-shadow-ci-token-exchange is deployed (INFRA-3631)."
95+
exit 1
96+
fi
97+
TOKEN=$(echo "$RESPONSE" | jq -r '.token // empty')
98+
if [[ -z "$TOKEN" || "$TOKEN" == "null" ]]; then
99+
echo "::error::Token exchange returned no token"
100+
exit 1
101+
fi
102+
echo "::add-mask::$TOKEN"
103+
echo "token=$TOKEN" >> "$GITHUB_OUTPUT"
104+
105+
- name: Dispatch ci.yml on Namespace
106+
id: dispatch
107+
env:
108+
GH_TOKEN: ${{ steps.exchange.outputs.token }}
109+
REPO: ${{ github.repository }}
110+
REF: ${{ github.head_ref || github.ref_name }}
111+
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
112+
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
113+
run: |
114+
set -euo pipefail
115+
echo "Dispatching ci.yml on ref='${REF}' (PR='${PR_NUMBER:-none}', sha='${HEAD_SHA}')"
116+
gh workflow run ci.yml \
117+
--repo "$REPO" \
118+
--ref "$REF" \
119+
-f runner_provider=namespace \
120+
-f pr_number="$PR_NUMBER" \
121+
-f head_sha="$HEAD_SHA"
122+
123+
# gh workflow run returns immediately with no run ID. Find the run
124+
# we just created so we can link to it from the step summary.
125+
# Best-effort: poll briefly for a queued/in_progress dispatch on this ref.
126+
RUN_URL=""
127+
for _ in $(seq 1 10); do
128+
RUN_URL=$(gh run list \
129+
--repo "$REPO" \
130+
--workflow ci.yml \
131+
--event workflow_dispatch \
132+
--branch "$REF" \
133+
--limit 1 \
134+
--json url,createdAt \
135+
--jq '.[0].url' 2>/dev/null || true)
136+
if [ -n "$RUN_URL" ] && [ "$RUN_URL" != "null" ]; then
137+
break
138+
fi
139+
sleep 2
140+
done
141+
echo "run_url=${RUN_URL}" >> "$GITHUB_OUTPUT"
142+
143+
- name: Step summary
144+
env:
145+
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
146+
PR_URL: ${{ github.event.pull_request.html_url || '' }}
147+
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
148+
REF: ${{ github.head_ref || github.ref_name }}
149+
RUN_URL: ${{ steps.dispatch.outputs.run_url }}
150+
run: |
151+
{
152+
echo "## Namespace shadow CI dispatched"
153+
echo
154+
echo "| Field | Value |"
155+
echo "|---|---|"
156+
if [ -n "$PR_NUMBER" ]; then
157+
echo "| PR | [#${PR_NUMBER}](${PR_URL}) |"
158+
fi
159+
echo "| Ref | \`${REF}\` |"
160+
echo "| Head SHA | \`${HEAD_SHA}\` |"
161+
if [ -n "${RUN_URL}" ]; then
162+
echo "| Shadow run | ${RUN_URL} |"
163+
else
164+
echo "| Shadow run | (not yet visible — check the Actions tab) |"
165+
fi
166+
echo
167+
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\`._"
168+
} >> "$GITHUB_STEP_SUMMARY"

0 commit comments

Comments
 (0)