Skip to content

Commit 0b33167

Browse files
alucardzomcursoragentbsgrigorov
authored
ci(INFRA-3631): Phase 5d — Shadow CI benchmark workflow for Namespace evaluation (#30158)
## **Description** INFRA-3631 Phase 5d — Adds a shadow CI workflow that runs the full CI pipeline on Namespace runners via `workflow_dispatch`, enabling side-by-side performance and reliability comparison with the current Cirrus/GitHub runner path. **Changes:** ### Shadow CI Workflow - **`ci-namespace-shadow.yml`**: Calls `ci.yml` with `runner_provider: namespace` via `workflow_call`. Automatic triggers (PR, push, hourly cron) are commented out for initial validation — only `workflow_dispatch` is active. Ready to enable after review. - **`ci.yml`**: Added `workflow_call:` trigger with `runner_provider` input (string, default `current`). The 44 existing `inputs.runner_provider` references already handle undefined gracefully, so this is backward-compatible. ### Benchmark Script - **`scripts/namespace-benchmark.sh`**: Collects per-job p50/p95 wall-clock durations comparing `ci.yml` vs `ci-namespace-shadow.yml` runs over a configurable time window. ### Rollback Safety - All Namespace-specific logic gated on `inputs.runner_provider == 'namespace'` - Shadow workflow concurrency group uses caller's workflow name, so it never cancels normal CI runs - `runner_provider=current` path unchanged and validated ### Includes Phase 5 (INFRA-3597) cache architecture This branch is based on `phase5/cache-and-artifacts` (PR #29886) rebased on latest `main`, so it includes the full cache and artifact architecture work. The shadow-specific changes are in the last commit. ## **Validation Runs** | Run | Provider | Result | |-----|----------|--------| | [25826373445](https://github.com/MetaMask/metamask-mobile/actions/runs/25826373445) | namespace | 81 success, Android 27/27 E2E pass, iOS build pass, 2 iOS E2E flakes (pre-existing) | | [25828982183](https://github.com/MetaMask/metamask-mobile/actions/runs/25828982183) | current | Rollback validation (in progress) | ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: INFRA-3631 (parent epic INFRA-3511) ## **Manual testing steps** ```gherkin Feature: INFRA-3631 Shadow CI benchmark workflow Scenario: Current runner path is unchanged Given ci.yml runs with runner_provider current (or default) When all jobs execute Then the same runner selection and caching behavior runs as before And no shadow or namespace-specific logic is triggered Scenario: Namespace runner path works via workflow_call Given ci.yml is called with runner_provider namespace (via workflow_dispatch or workflow_call) When all jobs execute Then Namespace runner profiles are selected for all jobs And nscloud-cache-action is used for caching And all Android E2E shards pass Scenario: Shadow workflow calls ci.yml correctly Given ci-namespace-shadow.yml is dispatched (requires merge to main) When the shadow-ci job invokes ci.yml with runner_provider namespace Then the full CI pipeline runs on Namespace runners And the shadow run does not cancel or interfere with normal CI runs Scenario: Benchmark script produces comparison data Given at least one successful ci.yml and ci-namespace-shadow.yml run exist When scripts/namespace-benchmark.sh is executed Then per-job p50 and p95 durations are printed for both workflows ``` ## **Screenshots/Recordings** N/A — CI infrastructure PR. ## **Pre-merge author checklist** - [x] I've followed MetaMask Contributor Docs and Coding Standards. - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using JSDoc format if applicable - [x] I've applied the right labels on the PR ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR - [ ] I confirm that this PR addresses all acceptance criteria Made with [Cursor](https://cursor.com) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches GitHub Actions orchestration by introducing a new scheduled/PR-triggered shadow workflow and making `ci.yml` reusable via `workflow_call`, which could affect CI load or execution paths if misconfigured, though it remains advisory and defaults preserve existing behavior. > > **Overview** > Adds a new **advisory** GitHub Actions workflow, `ci-namespace-shadow.yml`, that runs the full CI pipeline by calling `ci.yml` with `runner_provider: namespace` (including PR/push/hourly schedule triggers) to benchmark Namespace runners without gating merges. > > Updates `ci.yml` to support `workflow_call` with a `runner_provider` input (defaulting to `current`) so it can be invoked by the shadow workflow, and adds `scripts/namespace-benchmark.sh` to compute per-job p50/p95 wall-clock times across `ci.yml` vs `ci-namespace-shadow.yml` runs. Also documents `[shadow]` CI jobs in `CONTRIBUTING.md`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 0a3a5cc. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Borislav Grigorov <11405770+bsgrigorov@users.noreply.github.com>
1 parent afc5b0e commit 0b33167

4 files changed

Lines changed: 93 additions & 0 deletions

File tree

.github/CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,8 @@ When you're done with your project / bugfix / feature and ready to submit a PR,
2323
- [ ] **Get the PR reviewed by code owners**: At least two code owner approvals are mandatory before merging any PR.
2424
- [ ] **Ensure the PR is correctly labeled.**: More detail about labels definitions can be found [here](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md).
2525

26+
### Shadow CI jobs
27+
28+
CI jobs prefixed with `[shadow]` (e.g., from `ci-namespace-shadow.yml`) are **advisory only** and never gate merge. They run the same test suite on Namespace runners for performance benchmarking. If a shadow job fails, it does not indicate a problem with your PR -- it reflects the state of the Namespace runner migration trial.
29+
2630
And that's it! Thanks for helping out.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CI (Namespace shadow)
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, ready_for_review]
6+
paths-ignore:
7+
- 'docs/**'
8+
- '**/*.md'
9+
- '.github/CODEOWNERS'
10+
push:
11+
branches: [main]
12+
schedule:
13+
- cron: '0 * * * *'
14+
workflow_dispatch:
15+
16+
concurrency:
17+
group: ns-shadow-${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
19+
20+
permissions:
21+
contents: read
22+
actions: read
23+
id-token: write
24+
25+
jobs:
26+
shadow-ci:
27+
name: '[shadow] CI'
28+
uses: ./.github/workflows/ci.yml
29+
with:
30+
runner_provider: namespace
31+
secrets: inherit

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ on:
1313
# Run the full suite "overnight," once every hour from 2:00am UTC until 6:00am UTC.
1414
# This helps to identy the flaky and failed tests on main branch
1515
- cron: '0 2-6 * * *'
16+
workflow_call:
17+
inputs:
18+
runner_provider:
19+
type: string
20+
required: false
21+
default: current
1622
workflow_dispatch:
1723
inputs:
1824
runner_provider:

scripts/namespace-benchmark.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env bash
2+
# Usage: scripts/namespace-benchmark.sh [hours-ago]
3+
# Prints median + p95 wall-clock per job for current vs namespace shadow
4+
# over the last N hours (default 24).
5+
6+
set -euo pipefail
7+
8+
HOURS=${1:-24}
9+
REPO=MetaMask/metamask-mobile
10+
11+
# macOS date vs GNU date
12+
if date -v-1H +%s >/dev/null 2>&1; then
13+
SINCE=$(date -u -v-${HOURS}H +%Y-%m-%dT%H:%M:%SZ)
14+
else
15+
SINCE=$(date -u -d "${HOURS} hours ago" +%Y-%m-%dT%H:%M:%SZ)
16+
fi
17+
18+
echo "Comparing runs since ${SINCE} (last ${HOURS}h)"
19+
echo
20+
21+
for WF in ci.yml ci-namespace-shadow.yml; do
22+
echo "=== ${WF} ==="
23+
24+
gh run list --repo "${REPO}" --workflow "${WF}" --status success --limit 100 \
25+
--json databaseId,createdAt \
26+
--jq "[.[] | select(.createdAt >= \"${SINCE}\")] | length" \
27+
| xargs -I{} echo " Successful runs in window: {}"
28+
29+
gh run list --repo "${REPO}" --workflow "${WF}" --status success --limit 100 \
30+
--json databaseId,createdAt \
31+
--jq "[.[] | select(.createdAt >= \"${SINCE}\") | .databaseId][]" \
32+
| while read -r RUN_ID; do
33+
gh run view "${RUN_ID}" --repo "${REPO}" --json jobs \
34+
--jq '.jobs[] | select(.conclusion == "success" and .startedAt != "0001-01-01T00:00:00Z" and .completedAt != "0001-01-01T00:00:00Z") | [.name, ((.completedAt[:19] | strptime("%Y-%m-%dT%H:%M:%S") | mktime) - (.startedAt[:19] | strptime("%Y-%m-%dT%H:%M:%S") | mktime))] | @tsv' 2>/dev/null
35+
done \
36+
| awk -F'\t' '
37+
{ sum[$1]+=$2; n[$1]++; data[$1]=data[$1]" "$2 }
38+
END {
39+
for (j in sum) {
40+
split(data[j], arr, " ")
41+
asort(arr)
42+
k=n[j]
43+
p50=arr[int(k*0.5)+1]
44+
p95=arr[int(k*0.95)+1]
45+
printf " %-55s n=%-4d p50=%6.0fs p95=%6.0fs\n", j, k, p50, p95
46+
}
47+
}
48+
' \
49+
| sort
50+
51+
echo
52+
done

0 commit comments

Comments
 (0)