-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
168 lines (159 loc) · 7 KB
/
ci-namespace-shadow.yml
File metadata and controls
168 lines (159 loc) · 7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
name: CI (Namespace shadow)
# Fire-and-forget dispatcher for the Namespace shadow benchmark (INFRA-3631).
#
# This workflow does NOT use `workflow_call`. Instead, it dispatches `ci.yml`
# as a separate `workflow_dispatch` run with `runner_provider=namespace`.
#
# Why a dispatch instead of a call:
# - `workflow_call` nests the called workflow's jobs into THIS workflow's
# check suite on the PR. With the repo's merge queue ALLGREEN policy,
# any shadow flake would block merges.
# - `workflow_dispatch` runs live in the Actions tab only -- invisible to
# PR checks. The shadow can fail freely; it never blocks the queue,
# never duplicates commit statuses, never posts PR comments.
#
# Correlation back to the originating PR:
# - The dispatched run's display name is set via `run-name` in ci.yml to
# `ci [shadow PR #<num> @ <sha>]`, so it's identifiable in the Actions
# tab at a glance.
# - The dispatcher job posts a GitHub Actions step summary linking the PR
# to the dispatched shadow run URL, reachable from the PR Checks tab.
#
# Authentication: Token Exchange Service (same pattern as triage-forwarder.yml
# and shared-services-workflows deploy.yml — OIDC → POST /api/exchange/token).
#
# Prerequisites:
# - Repo/org Actions variable: TOKEN_EXCHANGE_URL (already used elsewhere in
# metamask-mobile, e.g. triage-forwarder).
# - Rego policy in token-exchange-service: explicit policy
# mm-metamask-mobile-namespace-shadow-ci-token-exchange (matches OIDC
# `workflow_ref` claim for .github/workflows/ci-namespace-shadow.yml).
#
# Fork PRs: the job `if` below skips the entire job for fork head repos, so OIDC
# exchange never runs for untrusted forks (same model as not exposing secrets).
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- "docs/**"
- "**/*.md"
- ".github/CODEOWNERS"
push:
branches: [main]
schedule:
- cron: "0 * * * *"
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).
if: ${{ 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
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
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
id: dispatch
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 workflow run ci.yml \
--repo "$REPO" \
--ref "$REF" \
-f runner_provider=namespace \
-f pr_number="$PR_NUMBER" \
-f head_sha="$HEAD_SHA"
# gh workflow run returns immediately with no run ID. Find the run
# we just created so we can link to it from the step summary.
# Best-effort: poll briefly for a queued/in_progress dispatch on this ref.
RUN_URL=""
for _ in $(seq 1 10); do
RUN_URL=$(gh run list \
--repo "$REPO" \
--workflow ci.yml \
--event workflow_dispatch \
--branch "$REF" \
--limit 1 \
--json url,createdAt \
--jq '.[0].url' 2>/dev/null || true)
if [ -n "$RUN_URL" ] && [ "$RUN_URL" != "null" ]; then
break
fi
sleep 2
done
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"