Commit f29d18d
Add file-based transfer APIs to build cache provider interface and all cache plugins (#5746)
* feat: add streaming support to build cache provider interface and HTTP plugin
Add optional tryGetCacheEntryStreamByIdAsync and trySetCacheEntryStreamAsync
methods to ICloudBuildCacheProvider. Implement streaming in HttpBuildCacheProvider
and update OperationBuildCache to prefer streaming when available. Add
fetchStreamAsync to WebClient and stream write support to
FileSystemBuildCacheProvider.
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/050e10a7-3cad-4da4-93e5-9941453283b9
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* fix lint warnings, add change files and API report update
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/050e10a7-3cad-4da4-93e5-9941453283b9
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* address code review feedback: improve type annotations and JSDoc
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/050e10a7-3cad-4da4-93e5-9941453283b9
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Replace manual change files with rush change --bulk generated change file
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/798065bf-040f-462e-8ea9-b9ca5659196d
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Use createReadStream/createWriteStream from FileSystem instead of fs directly
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/5ad20d4d-c9a4-4855-bb13-8dd9e2c1350b
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Use ensureFolderExists option in FileSystem.createWriteStream
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/199d4b3e-1f3f-44e1-9fc6-7b4a0e027c7e
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Add streaming support to Amazon S3 and Azure Storage build cache plugins
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/6212baeb-266c-4823-94df-251c69a8f74c
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Address code review: add documentation for type assertion and no-retry streaming upload
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/6212baeb-266c-4823-94df-251c69a8f74c
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Gate streaming build cache behind useStreamingBuildCache experiment flag
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/63fb5abe-e500-4a9c-bd82-3ed613989ef4
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Update changelogs.
Co-authored-by: Ian Clanton-Thuon <iclanton@users.noreply.github.com>
* Expand FileSystem.createWriteStream.
* Use destructuring on rushConfiguration in PhasedScriptAction.
* Clean up some duplicated types.
* Fix CI: add missing useStreamingBuildCache to bridge plugin, fix WebClient private member type mismatch
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/1b32730c-9ce0-4e75-a2e1-45f06a628960
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* DRY up duplicated code across WebClient, AmazonS3Client, and HttpBuildCacheProvider
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/c103fb17-b8b1-4d02-88ba-9db236e9f48f
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Address code review: use Buffer.isBuffer for clearer body length check
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/c103fb17-b8b1-4d02-88ba-9db236e9f48f
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Change streaming cache APIs from Readable to NodeJS.ReadableStream
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/66d5d3e1-e14b-41bb-9382-46fc4176f977
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* DRY up Azure/HTTP cache providers, fix bridge plugin experiment config
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/b57084f4-36d7-42f3-8876-7298756b887e
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Clean up FileSystemBuildCacheProvider.
* fixup! Fix CI: add missing useStreamingBuildCache to bridge plugin, fix WebClient private member type mismatch
* Clean up WebClient.
* Handle Content-Encoding decompression in streaming WebClient path
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/13978d72-ea9a-4463-a6a2-51ecfc72f3ff
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Fix error message to show specific unsupported encoding value
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/13978d72-ea9a-4463-a6a2-51ecfc72f3ff
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Fix CI: Move WebClient private members to module-level for rush-sdk type compatibility
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/d8f5e137-3cb0-4ed9-8391-50d7a33d804e
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Address review comments: fix response.resume race, stream retry bug, error logging, stream cleanup, and nits
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/d0190bf7-a346-4a71-93bf-d5375d98b552
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Add unit tests for streaming cache APIs and fill buffer-based test gaps
Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/86050f65-dd6c-45f4-ac41-95fdb860c053
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
* Fix buffer path error message to show specific unsupported encoding value
The streaming path was fixed in 8becd08 but the buffer path still
printed the entire encodings array instead of the individual value.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Extract _getObjectName and _validateWriteAllowed helpers in S3 provider
Deduplicate the S3 prefix logic (repeated 4 times) into a helper,
and extract the write-permission guard (repeated in buffer and stream
set methods) to match the HTTP provider's pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Extract _getContentEncodings helper to deduplicate encoding parsing
Both the buffer and streaming response paths duplicated the same
Content-Encoding header parsing logic. Extract into a shared helper
that returns a parsed string array or undefined.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Scope cacheEntryBuffer to its branch, use cloudCacheHit flag
Replace the outer-scoped cacheEntryBuffer (which was only used as
a boolean flag at the cache-miss check) with an explicit cloudCacheHit
boolean. This scopes the buffer into the else branch and makes the
streaming path set the flag consistently too.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Reuse a single WebClient instance in HttpBuildCacheProvider
A new WebClient was being constructed on every call to
_makeHttpCoreRequestAsync. Since the provider never configures any
WebClient instance state, a single class member suffices.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Use property shorthand in HttpBuildCacheProvider
Replace verbose `terminal: terminal`, `headers: headers`, `body: body`,
and `credential: credential` with ES6 shorthand property syntax.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix unit test issues in streaming cache tests
- Fix credential fallback test: mock CredentialCache so cached credentials
are available, making the test actually validate the stream-body guard
(previously, the test passed trivially because _tryGetCredentialsAsync
would throw before making a second request)
- Fix 504 statusText from 'BadGateway' to 'Gateway Timeout'
- Replace fragile S3 upload snapshot that captured Readable internals
(breaks on Node.js upgrades) with targeted assertions on URL, verb,
headers, and body identity
- Replace fail() + try/catch with expect().rejects.toThrow()
- Move Readable import to top of S3 test file instead of per-test
dynamic import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Rework streaming cache APIs to file-based APIs with S3 payload signing
Replace stream-based ICloudBuildCacheProvider methods with file-path-based
alternatives that give providers full control over I/O:
- tryGetCacheEntryStreamByIdAsync → tryGetCacheEntryToFileAsync
- trySetCacheEntryStreamAsync → trySetCacheEntryFromFileAsync
Key improvements:
- S3 uploads now hash the tarball on disk before streaming, restoring
AWS Signature V4 payload signing (removes UNSIGNED_PAYLOAD)
- Azure provider uses SDK-native uploadFile/downloadToFile
- HTTP provider uses FileSystem.createReadStream/createWriteStreamAsync
- Providers that don't need pre-upload computation (HTTP, Azure) don't
pay the cost of hashing
- Experiment renamed to useDirectFileTransfersForBuildCache
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Rename file-based cache APIs to tryDownload/tryUpload for clarity
- tryGetCacheEntryToFileAsync → tryDownloadCacheEntryToFileAsync
- trySetCacheEntryFromFileAsync → tryUploadCacheEntryFromFileAsync
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Replace jest.mock('node:fs') with FileSystem spies in cache tests
Mock FileSystem.createReadStream, createWriteStreamAsync, and
ensureFolderAsync via jest.spyOn instead of module-level fs mocks.
This is more targeted, less brittle, and consistent with the
production code's use of the FileSystem abstraction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Clean up partial file on failed cache download
If tryDownloadCacheEntryToFileAsync throws mid-download, a corrupt
partial file could be left at the target path. On the next build,
tryGetCacheEntryPathByIdAsync would find it and try to untar it.
Delete the file in the catch block to prevent this.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Ensure parent directory exists before Azure downloadToFile
The HTTP and S3 providers both ensure the parent directory exists
before writing the cache entry file. The Azure provider was missing
this, which would cause failures on a fresh machine where the build
cache folder hasn't been created yet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fixup! Ensure parent directory exists before Azure downloadToFile
* Update change file descriptions, Azure JSDoc, and test names
- Rush change file: replace "streaming APIs" with file-based transfer
API names and mention the experiment flag
- node-core-library change file: fix broken backtick formatting
- Azure provider: update JSDoc from "stream" to "file-based"
- HTTP test names: replace "stream consumed" / "stream bodies" with
file-based language
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix S3 retry delay log unit from seconds to milliseconds
The delay variable is in milliseconds but the log message said "s".
This produced misleading output like "Will retry request in 4000s...".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fixup! Rework streaming cache APIs to file-based APIs with S3 payload signing
Remove unused onBlobAlreadyExists parameter from _trySetBlobDataAsync
This callback was previously used to drain incoming streams when the
blob already existed. With the switch to file-based APIs, no callers
pass this parameter anymore.
* Add missing test coverage for file-based cache APIs
- HTTP: add 404 cache miss test for tryDownloadCacheEntryToFileAsync
- HTTP: add pipeline assertion in download success test
- S3: add retry test for downloadObjectToFileAsync on transient 5xx
- S3: add pipeline assertions in download success/miss tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix "unknown bytes" debug log wording in HttpBuildCacheProvider
For stream-body requests, the log read "unknown bytes" which is
awkward. Change to "unknown length" for clarity.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address dmichon-msft review comments
- Add clarifying comment on maxAttempts: 1 for uploads explaining why
the parameter exists (shared between download with retries and upload
without)
- Replace S3 download snapshot containing auth headers with explicit
field assertions, avoiding credential-looking strings in snapshots
- Update inline snapshot for "unknown length" wording change
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: clean up partial files when tryDownloadCacheEntryToFileAsync returns false
Providers may catch errors internally and return false instead of throwing,
leaving a partially written file on disk. Move the cleanup logic outside
the catch block so it runs for any non-success case, preventing corrupted
partial files from being treated as valid cache entries on subsequent builds.
* fix: merge main and update test to use jest.mocked()
* Restructure S3 response handling and pre-resolve credentials for stream uploads
* Destructure response variable in _handleGetResponseAsync
* docs: clarify build cache direct file transfer experiment
* Refine streaming cache upload handling
* Fix unresolved streaming cache review comments
* Fix atomic cloud cache download writes
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
Co-authored-by: Ian Clanton-Thuon <iclanton@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 6bb6a2b commit f29d18d
25 files changed
Lines changed: 1993 additions & 423 deletions
File tree
- common
- changes/@microsoft/rush
- reviews/api
- libraries/rush-lib/src
- api
- cli
- scriptActions
- logic
- buildCache
- test
- operations
- test
- schemas
- utilities
- test
- rush-plugins
- rush-amazon-s3-build-cache-plugin/src
- test
- __snapshots__
- rush-azure-storage-build-cache-plugin/src
- rush-bridge-cache-plugin/src
- rush-http-build-cache-plugin/src
- test
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| 29 | + | |
28 | 30 | | |
29 | 31 | | |
30 | 32 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
313 | 313 | | |
314 | 314 | | |
315 | 315 | | |
316 | | - | |
| 316 | + | |
317 | 317 | | |
318 | 318 | | |
319 | 319 | | |
| |||
354 | 354 | | |
355 | 355 | | |
356 | 356 | | |
| 357 | + | |
357 | 358 | | |
358 | 359 | | |
359 | 360 | | |
360 | 361 | | |
| 362 | + | |
361 | 363 | | |
362 | 364 | | |
363 | 365 | | |
| |||
487 | 489 | | |
488 | 490 | | |
489 | 491 | | |
| 492 | + | |
490 | 493 | | |
491 | 494 | | |
492 | 495 | | |
| |||
600 | 603 | | |
601 | 604 | | |
602 | 605 | | |
| 606 | + | |
603 | 607 | | |
604 | 608 | | |
605 | 609 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
144 | 144 | | |
145 | 145 | | |
146 | 146 | | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
147 | 156 | | |
148 | 157 | | |
149 | 158 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
221 | 221 | | |
222 | 222 | | |
223 | 223 | | |
224 | | - | |
225 | | - | |
| 224 | + | |
226 | 225 | | |
227 | 226 | | |
228 | 227 | | |
| |||
Lines changed: 26 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
308 | 308 | | |
309 | 309 | | |
310 | 310 | | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
311 | 316 | | |
312 | 317 | | |
313 | 318 | | |
| |||
331 | 336 | | |
332 | 337 | | |
333 | 338 | | |
334 | | - | |
| 339 | + | |
335 | 340 | | |
336 | 341 | | |
337 | 342 | | |
| |||
340 | 345 | | |
341 | 346 | | |
342 | 347 | | |
343 | | - | |
| 348 | + | |
344 | 349 | | |
345 | 350 | | |
346 | 351 | | |
347 | 352 | | |
348 | | - | |
349 | | - | |
350 | | - | |
| 353 | + | |
351 | 354 | | |
352 | 355 | | |
353 | 356 | | |
| |||
473 | 476 | | |
474 | 477 | | |
475 | 478 | | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
476 | 491 | | |
477 | 492 | | |
478 | 493 | | |
479 | | - | |
480 | | - | |
481 | | - | |
| 494 | + | |
482 | 495 | | |
483 | 496 | | |
484 | 497 | | |
485 | | - | |
486 | | - | |
487 | | - | |
| 498 | + | |
| 499 | + | |
488 | 500 | | |
489 | 501 | | |
490 | 502 | | |
| |||
494 | 506 | | |
495 | 507 | | |
496 | 508 | | |
497 | | - | |
498 | | - | |
499 | | - | |
| 509 | + | |
500 | 510 | | |
501 | 511 | | |
502 | 512 | | |
| |||
511 | 521 | | |
512 | 522 | | |
513 | 523 | | |
| 524 | + | |
514 | 525 | | |
515 | 526 | | |
516 | 527 | | |
517 | 528 | | |
518 | | - | |
519 | | - | |
| 529 | + | |
520 | 530 | | |
521 | 531 | | |
522 | 532 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
390 | 390 | | |
391 | 391 | | |
392 | 392 | | |
393 | | - | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
394 | 398 | | |
395 | 399 | | |
396 | 400 | | |
| |||
Lines changed: 10 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | | - | |
6 | 4 | | |
7 | 5 | | |
8 | 6 | | |
| |||
32 | 30 | | |
33 | 31 | | |
34 | 32 | | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | 33 | | |
44 | 34 | | |
45 | 35 | | |
46 | | - | |
47 | | - | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
48 | 44 | | |
49 | 45 | | |
50 | 46 | | |
| |||
55 | 51 | | |
56 | 52 | | |
57 | 53 | | |
58 | | - | |
| 54 | + | |
| 55 | + | |
59 | 56 | | |
60 | 57 | | |
61 | 58 | | |
| |||
Lines changed: 30 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 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 | + | |
14 | 44 | | |
15 | 45 | | |
16 | 46 | | |
| |||
0 commit comments