Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/actions/setup-ci-js-deps/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: 'Setup CI JS dependencies'
description: >
Sets up node_modules + project setup outputs for CI test jobs. On Namespace
runners it mounts a persistent cache volume and skips install when the volume
is already warm (yarn.lock unchanged across runs in the pool). On any other
runner provider it falls back to a plain `yarn install --immutable` +
`yarn setup:github-ci --node`, matching the behaviour on main. We deliberately
do not use `actions/cache` here: the 10 GB repo-wide pool is too small/volatile
for a ~1.3 GB node_modules entry, and self-hosted/Namespace cache is the
established pattern across this repo's CI.

inputs:
runner_provider:
description: 'Runner provider. Only `namespace` benefits from cache reuse; anything else falls through to a fresh install + setup.'
required: false
default: 'current'

runs:
using: 'composite'
steps:
# Namespace mounts node_modules / .yarn/cache as a persistent volume scoped
# to the runner pool, so a warm volume makes the file check below find
# everything and skip install entirely. Cold (e.g. yarn.lock change in the
# pool) falls through to install, which then warms the volume for everyone.
- name: Configure Namespace cache
if: ${{ inputs.runner_provider == 'namespace' }}
uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1
with:
path: |
~/.cache/yarn
.metamask
node_modules
.yarn/cache

- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: ${{ inputs.runner_provider != 'namespace' && 'yarn' || '' }}

# File-based check is the source of truth: a partial restore (e.g.
# node_modules present but termsOfUseContent.ts missing) still falls
# through to install + setup. On non-namespace runners no cache is
# mounted, so this always evaluates to "needs install".
- name: Determine if install is needed
id: check-deps
shell: bash
run: |
if [ -d node_modules ] && [ -f app/util/termsOfUse/termsOfUseContent.ts ]; then
echo "needs-install=false" >> "$GITHUB_OUTPUT"
echo "✅ node_modules + setup outputs present; skipping install"
else
echo "needs-install=true" >> "$GITHUB_OUTPUT"
echo "⚠️ node_modules or setup outputs missing; will install + setup"
fi
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.

- name: Install Yarn dependencies with retry
if: ${{ steps.check-deps.outputs.needs-install == 'true' }}
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: yarn install --immutable

- name: Run project setup
if: ${{ steps.check-deps.outputs.needs-install == 'true' }}
shell: bash
run: yarn setup:github-ci --node
118 changes: 34 additions & 84 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -527,39 +527,45 @@ jobs:
run: ${{ steps.download-actionlint.outputs.executable }} -color -config-file .github/actionlint.yaml
shell: bash

# Producer: warms the Namespace cache volume once per run so downstream
# test shards skip the 2-min `yarn install` + setup. On a warm volume
# (yarn.lock unchanged across the pool) this job is near-instant; on a
# cold volume it pays the install cost once, on behalf of all consumers.
# On non-namespace runners there is no shared cache, so this job is a
# no-op (consumers will install themselves, matching main).
prepare-ci-js-deps:
name: Prepare CI JS dependencies
runs-on: ${{ inputs.runner_provider == 'namespace' && 'namespace-profile-metamask-ci-linux' || 'ubuntu-latest' }}
if: ${{ needs.get_requirements.outputs.skip_everything != 'true' }}
needs:
- get_requirements
steps:
- uses: actions/checkout@v6
if: ${{ inputs.runner_provider == 'namespace' }}
- uses: ./.github/actions/setup-ci-js-deps
if: ${{ inputs.runner_provider == 'namespace' }}
with:
runner_provider: ${{ inputs.runner_provider }}
- name: Skip cache warm-up (no shared cache available)
if: ${{ inputs.runner_provider != 'namespace' }}
run: |
echo "Skipping cache warm-up. runner_provider=${{ inputs.runner_provider }} has no cross-job cache; consumers will install themselves."

unit-tests:
name: Unit tests (${{ matrix.shard }})
runs-on: ${{ inputs.runner_provider == 'namespace' && 'namespace-profile-metamask-ci-linux' || 'ubuntu-latest' }}
if: ${{ needs.get_requirements.outputs.skip_everything != 'true' }}
needs:
- get_requirements
- prepare-ci-js-deps
Comment thread
cursor[bot] marked this conversation as resolved.
strategy:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
steps:
- uses: actions/checkout@v6
- name: Configure Namespace cache
if: ${{ inputs.runner_provider == 'namespace' }}
uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1
with:
path: |
~/.cache/yarn
.metamask
node_modules
.yarn/cache
- uses: actions/setup-node@v6
- uses: ./.github/actions/setup-ci-js-deps
with:
node-version-file: '.nvmrc'
cache: ${{ inputs.runner_provider != 'namespace' && 'yarn' || '' }}
- name: Install Yarn dependencies with retry
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: yarn install --immutable
- name: Clean state and following up dependencies installation
run: yarn setup:github-ci --node
runner_provider: ${{ inputs.runner_provider }}
- name: Prepare results directory
run: mkdir -p tests/results
# The "10" in this command is the total number of shards. It must be kept
Expand Down Expand Up @@ -594,42 +600,13 @@ jobs:
# threshold calculation is accurate.
merge-unit-and-component-view-tests:
runs-on: ${{ inputs.runner_provider == 'namespace' && 'namespace-profile-metamask-ci-linux' || 'ubuntu-latest' }}
needs: [unit-tests, component-view-tests]
needs: [prepare-ci-js-deps, unit-tests, component-view-tests]
if: ${{ !cancelled() && github.event_name != 'merge_group' }}
steps:
- uses: actions/checkout@v6
- name: Configure Namespace cache
if: ${{ inputs.runner_provider == 'namespace' }}
uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1
- uses: ./.github/actions/setup-ci-js-deps
with:
path: |
~/.cache/yarn
.metamask
node_modules
.yarn/cache
- name: Restore node_modules cache
if: ${{ inputs.runner_provider != 'namespace' }}
id: cache-node-modules
uses: actions/cache@v4
with:
path: |
node_modules
.yarn/install-state.gz
key: ${{ runner.os }}-node-modules-${{ hashFiles('yarn.lock') }}
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: ${{ inputs.runner_provider != 'namespace' && 'yarn' || '' }}
- name: Install Yarn dependencies with retry
if: ${{ inputs.runner_provider == 'namespace' || steps.cache-node-modules.outputs.cache-hit != 'true' }}
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: yarn install --immutable
- name: Clean state and following up dependencies installation
run: yarn setup:github-ci --node
runner_provider: ${{ inputs.runner_provider }}
- uses: actions/download-artifact@v4
with:
pattern: coverage-*
Expand Down Expand Up @@ -724,43 +701,15 @@ jobs:
if: ${{ needs.get_requirements.outputs.skip_everything != 'true' }}
needs:
- get_requirements
- prepare-ci-js-deps
strategy:
matrix:
shard: [1, 2]
steps:
- uses: actions/checkout@v6
- name: Configure Namespace cache
if: ${{ inputs.runner_provider == 'namespace' }}
uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1
with:
path: |
~/.cache/yarn
.metamask
node_modules
.yarn/cache
- name: Restore node_modules cache
if: ${{ inputs.runner_provider != 'namespace' }}
id: cache-node-modules
uses: actions/cache@v4
- uses: ./.github/actions/setup-ci-js-deps
with:
path: |
node_modules
.yarn/install-state.gz
key: ${{ runner.os }}-node-modules-${{ hashFiles('yarn.lock') }}
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: ${{ inputs.runner_provider != 'namespace' && 'yarn' || '' }}
- name: Install Yarn dependencies with retry
if: ${{ inputs.runner_provider == 'namespace' || steps.cache-node-modules.outputs.cache-hit != 'true' }}
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 #v3.0.2
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: yarn install --immutable
- name: Clean state and following up dependencies installation
run: yarn setup:github-ci --node
runner_provider: ${{ inputs.runner_provider }}
- name: Prepare results directory
run: mkdir -p tests/results
- run: |
Expand Down Expand Up @@ -1080,6 +1029,7 @@ jobs:
- unit-tests
- component-view-tests
- check-workflows
- prepare-ci-js-deps
- js-bundle-size-check
- sonar-cloud-quality-gate-status
- build-android-apks
Expand Down
Loading