Skip to content

Commit 8760212

Browse files
authored
Ensure that our CLI lib tests use Yarn v4 (#16405)
* Ensure that our CLI lib tests use Yarn v4 * Change files * Update the feed warmer
1 parent f4a5ad4 commit 8760212

28 files changed

Lines changed: 2102 additions & 474 deletions

.ado/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,14 @@ branches, all driven from YAML. Keep the branch lists in `ci-pipeline.yml`,
3939
`0.82-stable` is deprecated and removed from all pipelines. `0.80-stable` and
4040
earlier are not built.
4141

42-
Feed warm-up (`warm-feed-pipeline.yml`) is branch-agnostic (scheduled on `main`).
43-
Branch *creation* is governed separately by the ruleset in
42+
Feed warm-up (`warm-feed-pipeline.yml`, scheduled on `main`) has two passes: the
43+
latest-patch enumeration is branch-agnostic (feed-centric), while the closure pass
44+
reproduces the `create-react-native-library` CLI-init graph **per built release
45+
branch**. That branch list lives in
46+
[`warm-feed.config.json`](../packages/@rnw-scripts/warm-feed/warm-feed.config.json)
47+
(`closure.modules.create-react-native-library.branches`) — keep it in sync with the
48+
built branches above (it omits the not-yet-integrated ones). Branch *creation* is
49+
governed separately by the ruleset in
4450
[`repo-rules/`](./repo-rules/README.md).
4551

4652
## Template-repository wiring (per-entry `PipelineTemplates` alias)

.ado/warm-feed-pipeline.yml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
#
33
# Enumerates the ms/react-native-public feed and re-pulls, with the pipeline's
44
# managed identity, the latest patch of every npm/NuGet major.minor line already
5-
# in use, so anonymous network-isolated PR/CI builds can restore them.
5+
# in use, so anonymous network-isolated PR/CI builds can restore them. The
6+
# scheduled run also resolves and warms the configured closure modules (see
7+
# warm-feed.config.json), e.g. the create-react-native-library CLI-init closure
8+
# across every release branch, which enumeration alone can't discover.
69
#
710
# Runs out of band (never in a PR build) because saving into the feed needs the
811
# managed identity. A maintainer can also queue it with the `packages` parameter
9-
# to warm a specific set of versions on demand.
12+
# (exact versions) or `closureModules` parameter (a graph) to warm on demand.
1013

1114
name: $(Date:yyyyMMdd).$(Rev:r)
1215

@@ -18,6 +21,10 @@ parameters:
1821
displayName: 'One-off warm (space-separated): npm:foo@1.2.3 nuget:Bar@4.0.0'
1922
type: string
2023
default: ' '
24+
- name: closureModules
25+
displayName: "One-off closure warm (space-separated module names, or 'all')"
26+
type: string
27+
default: ' '
2128

2229
schedules:
2330
- cron: '0 0,6,12,18 * * *'
@@ -60,7 +67,9 @@ extends:
6067
jobs:
6168
- job: WarmFeed
6269
displayName: Warm npm and NuGet feed cache
63-
timeoutInMinutes: 60
70+
# Longer than a plain sync: the scheduled run also scaffolds and
71+
# resolves the create-react-native-library closure per release branch.
72+
timeoutInMinutes: 120
6473
steps:
6574
- checkout: self
6675
fetchDepth: 1
@@ -91,6 +100,7 @@ extends:
91100
# under the feed managed identity).
92101
env:
93102
WARM_FEED_PACKAGES: ${{ parameters.packages }}
103+
WARM_FEED_CLOSURE_MODULES: ${{ parameters.closureModules }}
94104
inputs:
95105
azureSubscription: Office-Hermes-Windows-Bot
96106
scriptType: pscore
@@ -100,6 +110,8 @@ extends:
100110
$env:WARM_FEED_TOKEN = az account get-access-token `
101111
--resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv
102112
$pkgs = "$env:WARM_FEED_PACKAGES".Trim()
113+
$mods = "$env:WARM_FEED_CLOSURE_MODULES".Trim()
103114
$warmArgs = @()
104115
if ($pkgs) { foreach ($p in ($pkgs -split '\s+')) { $warmArgs += @('--packages', $p) } }
116+
if ($mods) { foreach ($m in ($mods -split '\s+')) { $warmArgs += @('--closure-module', $m) } }
105117
npx warm-feed @warmArgs
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Ensure that our CLI lib tests use Yarn v4",
4+
"packageName": "react-native-windows",
5+
"email": "vmorozov@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

packages/@rnw-scripts/warm-feed/README.md

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,71 @@ yarn warm-feed
4949
npm|nuget`, `--dry-run`, `--verify` (warm even already-cached targets),
5050
`--concurrency <n>`, `-v` / `--verbose`. See `--help` for the full list.
5151

52+
## Closure (graph) warming
53+
54+
Enumeration keeps **known** package lines fresh but can't introduce a **brand-new**
55+
name the feed has never seen — e.g. the packages a bumped
56+
`create-react-native-library` pulls into a generated project. Closure warming
57+
closes that gap: it resolves a dependency **graph** and warms every version in it.
58+
59+
The graph is resolved with `npm install --package-lock-only` (metadata only; the
60+
feed save still happens per version). The lockfile covers the **resolve host's own
61+
platform** (Windows x64 in CI). It can also list other-OS/CPU optional variants, but
62+
not dependably against an Azure Artifacts upstream proxy: npm 11+ writes
63+
not-yet-cached, non-current-platform optional deps without a `version`
64+
(npm/cli#9342) and the parser drops version-less entries. Warming every platform's
65+
optional deps would need a per-OS/CPU resolve.
66+
67+
Three ways to feed it a graph:
68+
69+
```powershell
70+
# 1. Roots — warm the full npm graph of one or more packages:
71+
yarn warm-feed --closure npm:create-react-native-library@0.63.0
72+
73+
# 2. Manifest — warm the external-dependency graph of a package.json:
74+
yarn warm-feed --closure-manifest ./some/package.json
75+
76+
# 3. Special module — a registered reproducer for a case enumeration can't see:
77+
yarn warm-feed --closure-module create-react-native-library
78+
yarn warm-feed --closure-module all # every enabled module
79+
```
80+
81+
### Special modules (pluggable)
82+
83+
A **special module** reproduces a specific install closure and turns it into a
84+
dependency graph to warm. They live in `src/specialModules/` and are registered
85+
in `src/specialModules/index.ts`; add a module there and it's available to config
86+
(`closure.modules.<name>`) and `--closure-module <name>`.
87+
88+
The first module, **`create-react-native-library`**, reproduces the CLI-init lib
89+
test: the test scaffolds a library (+ vanilla example app) and installs *that
90+
generated project*, so its closure — not cRNL's own dependencies — is what the
91+
feed needs. warm-feed runs only from `main`, but the test runs on every release
92+
branch pinned to a different React Native, so the module's config manifest lists
93+
all branches and, per branch, derives the RN/CLI versions (nightly for `main`
94+
from the working-tree `vnext/package.json`; for `0.NN-stable`, the exact
95+
`react-native` that branch pins in its own `vnext/package.json`, read via git),
96+
scaffolds, and reads the generated manifests. Mirrors
97+
`vnext/Scripts/creaternwlib.cmd`.
98+
99+
The scheduled pipeline run warms every **enabled** configured module in addition
100+
to the latest-patch sync, so brand-new closures stay warm automatically.
101+
102+
### NuGet lock closure
103+
104+
The special modules above are npm-only. NuGet has the same gap — enumeration only
105+
refreshes lines already in the feed — for which the repo's committed
106+
`packages.lock.json` files are the source of truth: they pin the full resolved
107+
NuGet closure (incl. transitives) every project restores. warm-feed scans them and
108+
warms every `name@resolved` they list, so a brand-new NuGet package (or an exact
109+
non-latest pinned version) restores under isolation.
110+
111+
The scheduled pass does this automatically (unless `closure.nugetLocks.enabled` is
112+
`false`, or `--only npm`); `--nuget-locks` runs just this pass one-off. Scanning is
113+
local (no feed access), so it is included in a `--dry-run` plan. By default it
114+
scans the repo root (`--repo-root`, default cwd), pruning `node_modules`; narrow it
115+
with `closure.nugetLocks.roots`.
116+
52117
## Pipeline usage
53118

54119
`.ado/warm-feed-pipeline.yml` runs the tool on a schedule (and on manual queue)
@@ -76,13 +141,19 @@ maintainer queues the pipeline with the `packages` parameter
76141
| `expand.maxMajorsBack` | Limit to the N most-recent majors already in use (0 = no limit). |
77142
| `concurrency` | Parallel requests. |
78143
| `ignore` | `id`, `id@version`, or `eco:id@version` entries to skip. |
144+
| `closure.registry` | npm registry for closure resolution (defaults to `feeds.npm.registry`). |
145+
| `closure.modules` | Per-module config blocks (e.g. `create-react-native-library`), keyed by module name; each may set `enabled: false`. |
146+
| `closure.nugetLocks` | NuGet `packages.lock.json` closure: `enabled` (default true) and `roots` (repo-relative dirs to scan; default repo root). |
79147

80148
## Scope and limitations
81149

82-
- Warms **latest patch per in-use line**, not a specific build's exact
83-
lockfile-pinned closure. A build pinning an older patch, or a version whose
84-
transitive graph differs, is not guaranteed by this pass alone.
85-
- Cannot introduce a **brand-new package name** the feed has never seen (that name
86-
is not in the feed's list). First use is covered by the authenticated CI build
87-
that restores it, or by a one-off `--packages` warm.
88-
- Does not resolve transitive closures (each warmed version is fetched on its own).
150+
- The enumeration pass warms **latest patch per in-use line**, not a specific
151+
build's exact lockfile-pinned closure. A build pinning an older patch is not
152+
guaranteed by that pass alone — use closure warming for exact graphs.
153+
- Enumeration cannot introduce a **brand-new package name** the feed has never
154+
seen, nor an exact non-latest pinned version. Covered instead by closure warming:
155+
for **npm** a special module, `--closure`, or `--closure-manifest`; for **NuGet**
156+
the `packages.lock.json` closure (above). The authenticated CI build that first
157+
restores a package, or a one-off `--packages` warm, also cover it.
158+
- Closure resolution needs `npm` on `PATH` (bundled with Node) and, for special
159+
modules that scaffold, network access to the feed for the generator.

packages/@rnw-scripts/warm-feed/src/auth.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export function resolveAuth(log: Logger, pat?: string): Auth {
4242
async header() {
4343
return {Authorization: `Bearer ${bearer}`};
4444
},
45+
async token() {
46+
return bearer;
47+
},
4548
};
4649
}
4750

@@ -54,6 +57,9 @@ export function resolveAuth(log: Logger, pat?: string): Auth {
5457
async header() {
5558
return {Authorization: `Basic ${basic}`};
5659
},
60+
async token() {
61+
return explicit;
62+
},
5763
};
5864
}
5965

@@ -75,5 +81,8 @@ export function resolveAuth(log: Logger, pat?: string): Auth {
7581
async header() {
7682
return {Authorization: `Bearer ${get()}`};
7783
},
84+
async token() {
85+
return get();
86+
},
7887
};
7988
}

0 commit comments

Comments
 (0)