-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
163 lines (156 loc) · 7.41 KB
/
Copy pathci-namespace-shadow.yml
File metadata and controls
163 lines (156 loc) · 7.41 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
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"