diff --git a/common/changes/@microsoft/rush/copilot-update-filter-properties-to-exclude_2026-02-17-01-37.json b/common/changes/@microsoft/rush/copilot-update-filter-properties-to-exclude_2026-02-17-01-37.json new file mode 100644 index 0000000000..efcd84c45f --- /dev/null +++ b/common/changes/@microsoft/rush/copilot-update-filter-properties-to-exclude_2026-02-17-01-37.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@microsoft/rush" + } + ], + "packageName": "@microsoft/rush", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/rush/omit-apple-doubles-from-cache_2026-02-16-05-38.json b/common/changes/@microsoft/rush/omit-apple-doubles-from-cache_2026-02-16-05-38.json index ba599debc3..1a4970354d 100644 --- a/common/changes/@microsoft/rush/omit-apple-doubles-from-cache_2026-02-16-05-38.json +++ b/common/changes/@microsoft/rush/omit-apple-doubles-from-cache_2026-02-16-05-38.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@microsoft/rush", - "comment": "Add a new \"omitAppleDoubleFilesFromBuildCache\" experiment. When enabled, the Rush build cache will omit macOS AppleDouble metadata files (._*) from cache archives when a companion file exists in the same directory. This prevents platform-specific metadata files from polluting the shared build cache. The filtering only applies when running on macOS.", + "comment": "Add a new \"omitAppleDoubleFilesFromBuildCache\" experiment. When enabled, the Rush build cache will omit macOS AppleDouble metadata files (._*) from cache archives when a companion file exists in the same directory. This prevents platform-specific metadata files from polluting the shared build cache. The exclusion only applies when running on macOS.", "type": "none" } ], diff --git a/common/reviews/api/rush-lib.api.md b/common/reviews/api/rush-lib.api.md index e761e6c29a..6254bc8cab 100644 --- a/common/reviews/api/rush-lib.api.md +++ b/common/reviews/api/rush-lib.api.md @@ -584,7 +584,7 @@ export interface _INpmOptionsJson extends IPackageManagerOptionsJsonBase { // @internal (undocumented) export interface _IOperationBuildCacheOptions { buildCacheConfiguration: BuildCacheConfiguration; - filterAppleDoubleFiles: boolean; + excludeAppleDoubleFiles: boolean; terminal: ITerminal; } diff --git a/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts b/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts index 76e5415fba..e75efcbeaf 100644 --- a/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts +++ b/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts @@ -509,7 +509,7 @@ export class PhasedScriptAction extends BaseScriptAction i buildCacheConfiguration, cobuildConfiguration, terminal, - filterAppleDoubleFiles: + excludeAppleDoubleFiles: !!this.rushConfiguration.experimentsConfiguration.configuration .omitAppleDoubleFilesFromBuildCache }).apply(this.hooks); diff --git a/libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts b/libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts index 7c52597a76..0abf76c221 100644 --- a/libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts +++ b/libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts @@ -31,7 +31,7 @@ export interface IOperationBuildCacheOptions { * If true, omit AppleDouble (`._*`) files from cache archives when running on macOS * and a companion file exists in the same directory. */ - filterAppleDoubleFiles: boolean; + excludeAppleDoubleFiles: boolean; } /** @@ -74,7 +74,7 @@ export class OperationBuildCache { private readonly _cacheWriteEnabled: boolean; private readonly _projectOutputFolderNames: ReadonlyArray; private readonly _cacheId: string | undefined; - private readonly _filterAppleDoubleFiles: boolean; + private readonly _excludeAppleDoubleFiles: boolean; private constructor(cacheId: string | undefined, options: IProjectBuildCacheOptions) { const { @@ -86,7 +86,7 @@ export class OperationBuildCache { }, project, projectOutputFolderNames, - filterAppleDoubleFiles + excludeAppleDoubleFiles } = options; this._project = project; this._localBuildCacheProvider = localCacheProvider; @@ -95,7 +95,7 @@ export class OperationBuildCache { this._cacheWriteEnabled = cacheWriteEnabled; this._projectOutputFolderNames = projectOutputFolderNames || []; this._cacheId = cacheId; - this._filterAppleDoubleFiles = filterAppleDoubleFiles && process.platform === 'darwin'; + this._excludeAppleDoubleFiles = excludeAppleDoubleFiles && process.platform === 'darwin'; } private static _tryGetTarUtility(terminal: ITerminal): Promise { @@ -119,7 +119,7 @@ export class OperationBuildCache { executionResult: IOperationExecutionResult, options: IOperationBuildCacheOptions ): OperationBuildCache { - const { buildCacheConfiguration, terminal, filterAppleDoubleFiles } = options; + const { buildCacheConfiguration, terminal, excludeAppleDoubleFiles } = options; const outputFolders: string[] = [...(executionResult.operation.settings?.outputFolderNames ?? [])]; if (executionResult.metadataFolderPath) { outputFolders.push(executionResult.metadataFolderPath); @@ -132,7 +132,7 @@ export class OperationBuildCache { phaseName: executionResult.operation.associatedPhase.name, projectOutputFolderNames: outputFolders, operationStateHash: executionResult.getStateHash(), - filterAppleDoubleFiles + excludeAppleDoubleFiles }; const cacheId: string | undefined = OperationBuildCache._getCacheId(buildCacheOptions); return new OperationBuildCache(cacheId, buildCacheOptions); @@ -352,14 +352,14 @@ export class OperationBuildCache { const filteredOutputFolderNames: string[] = []; let hasSymbolicLinks: boolean = false; - const filterAppleDoubleFiles: boolean = this._filterAppleDoubleFiles; + const excludeAppleDoubleFiles: boolean = this._excludeAppleDoubleFiles; // Adds child directories to the queue, files to the path list, and bails on symlinks function processChildren(relativePath: string, diskPath: string, children: FolderItem[]): void { - // When filtering AppleDouble files, build a set of sibling names so we can check + // When excluding AppleDouble files, build a set of sibling names so we can check // whether a companion file exists for each ._X file. let childNameSet: Set | undefined; - if (filterAppleDoubleFiles) { + if (excludeAppleDoubleFiles) { childNameSet = new Set(children.map(({ name }) => name)); } diff --git a/libraries/rush-lib/src/logic/buildCache/test/OperationBuildCache.test.ts b/libraries/rush-lib/src/logic/buildCache/test/OperationBuildCache.test.ts index 319f487538..08a35a8542 100644 --- a/libraries/rush-lib/src/logic/buildCache/test/OperationBuildCache.test.ts +++ b/libraries/rush-lib/src/logic/buildCache/test/OperationBuildCache.test.ts @@ -15,7 +15,7 @@ interface ITestOptions { enabled: boolean; writeAllowed: boolean; trackedProjectFiles: string[] | undefined; - filterAppleDoubleFiles: boolean; + excludeAppleDoubleFiles: boolean; } function createFolderItem(name: string, type: 'file' | 'directory' | 'symlink'): FolderItem { @@ -59,7 +59,7 @@ describe(OperationBuildCache.name, () => { operationStateHash: '1926f30e8ed24cb47be89aea39e7efd70fcda075', terminal, phaseName: 'build', - filterAppleDoubleFiles: !!options.filterAppleDoubleFiles + excludeAppleDoubleFiles: !!options.excludeAppleDoubleFiles }); return subject; @@ -74,7 +74,7 @@ describe(OperationBuildCache.name, () => { }); }); - describe('AppleDouble file filtering', () => { + describe('AppleDouble file exclusion', () => { const originalPlatform: NodeJS.Platform = process.platform; afterEach(() => { @@ -85,7 +85,7 @@ describe(OperationBuildCache.name, () => { it('omits AppleDouble files with companions when enabled on macOS', async () => { Object.defineProperty(process, 'platform', { value: 'darwin' }); - const subject: OperationBuildCache = prepareSubject({ filterAppleDoubleFiles: true }); + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); jest .spyOn(FileSystem, 'readFolderItemsAsync') @@ -111,7 +111,7 @@ describe(OperationBuildCache.name, () => { it('keeps AppleDouble files without companion files', async () => { Object.defineProperty(process, 'platform', { value: 'darwin' }); - const subject: OperationBuildCache = prepareSubject({ filterAppleDoubleFiles: true }); + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); jest .spyOn(FileSystem, 'readFolderItemsAsync') @@ -127,10 +127,10 @@ describe(OperationBuildCache.name, () => { expect(result!.outputFilePaths).toEqual(['dist/._orphan.txt', 'dist/other.js']); }); - it('does not filter AppleDouble files when the experiment is disabled', async () => { + it('does not exclude AppleDouble files when the experiment is disabled', async () => { Object.defineProperty(process, 'platform', { value: 'darwin' }); - const subject: OperationBuildCache = prepareSubject({ filterAppleDoubleFiles: false }); + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: false }); jest .spyOn(FileSystem, 'readFolderItemsAsync') @@ -146,10 +146,10 @@ describe(OperationBuildCache.name, () => { expect(result!.outputFilePaths).toEqual(['dist/._foo.txt', 'dist/foo.txt']); }); - it('does not filter AppleDouble files on non-macOS platforms', async () => { + it('does not exclude AppleDouble files on non-macOS platforms', async () => { Object.defineProperty(process, 'platform', { value: 'win32' }); - const subject: OperationBuildCache = prepareSubject({ filterAppleDoubleFiles: true }); + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); jest .spyOn(FileSystem, 'readFolderItemsAsync') @@ -165,10 +165,10 @@ describe(OperationBuildCache.name, () => { expect(result!.outputFilePaths).toEqual(['dist/._foo.txt', 'dist/foo.txt']); }); - it('does not filter files named exactly "._"', async () => { + it('does not exclude files named exactly "._"', async () => { Object.defineProperty(process, 'platform', { value: 'darwin' }); - const subject: OperationBuildCache = prepareSubject({ filterAppleDoubleFiles: true }); + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); jest .spyOn(FileSystem, 'readFolderItemsAsync') @@ -184,10 +184,10 @@ describe(OperationBuildCache.name, () => { expect(result!.outputFilePaths).toEqual(['dist/._', 'dist/other.txt']); }); - it('filters AppleDouble files in nested directories', async () => { + it('excludes AppleDouble files in nested directories', async () => { Object.defineProperty(process, 'platform', { value: 'darwin' }); - const subject: OperationBuildCache = prepareSubject({ filterAppleDoubleFiles: true }); + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); // First call returns the top-level dist/ contents with a subdirectory // Second call returns the subdirectory contents diff --git a/libraries/rush-lib/src/logic/operations/CacheableOperationPlugin.ts b/libraries/rush-lib/src/logic/operations/CacheableOperationPlugin.ts index 32cc5913ba..90af287b9d 100644 --- a/libraries/rush-lib/src/logic/operations/CacheableOperationPlugin.ts +++ b/libraries/rush-lib/src/logic/operations/CacheableOperationPlugin.ts @@ -76,14 +76,14 @@ export interface ICacheableOperationPluginOptions { buildCacheConfiguration: BuildCacheConfiguration; cobuildConfiguration: CobuildConfiguration | undefined; terminal: ITerminal; - filterAppleDoubleFiles: boolean; + excludeAppleDoubleFiles: boolean; } interface ITryGetOperationBuildCacheOptionsBase { buildCacheContext: IOperationBuildCacheContext; buildCacheConfiguration: BuildCacheConfiguration | undefined; terminal: ITerminal; - filterAppleDoubleFiles: boolean; + excludeAppleDoubleFiles: boolean; record: TRecord; } @@ -108,7 +108,7 @@ export class CacheableOperationPlugin implements IPhasedCommandPlugin { allowWarningsInSuccessfulBuild, buildCacheConfiguration, cobuildConfiguration, - filterAppleDoubleFiles + excludeAppleDoubleFiles } = this._options; hooks.beforeExecuteOperations.tap( @@ -272,7 +272,7 @@ export class CacheableOperationPlugin implements IPhasedCommandPlugin { buildCacheConfiguration, terminal: buildCacheTerminal, record, - filterAppleDoubleFiles + excludeAppleDoubleFiles }); // Try to acquire the cobuild lock @@ -291,7 +291,7 @@ export class CacheableOperationPlugin implements IPhasedCommandPlugin { buildCacheContext, record, terminal: buildCacheTerminal, - filterAppleDoubleFiles + excludeAppleDoubleFiles }); if (operationBuildCache) { buildCacheTerminal.writeVerboseLine( @@ -585,7 +585,7 @@ export class CacheableOperationPlugin implements IPhasedCommandPlugin { private _tryGetOperationBuildCache( options: ITryGetOperationBuildCacheOptions ): OperationBuildCache | undefined { - const { buildCacheConfiguration, buildCacheContext, terminal, record, filterAppleDoubleFiles } = options; + const { buildCacheConfiguration, buildCacheContext, terminal, record, excludeAppleDoubleFiles } = options; if (!buildCacheContext.operationBuildCache) { const { cacheDisabledReason } = buildCacheContext; if (cacheDisabledReason && !record.operation.settings?.allowCobuildWithoutCache) { @@ -601,7 +601,7 @@ export class CacheableOperationPlugin implements IPhasedCommandPlugin { buildCacheContext.operationBuildCache = OperationBuildCache.forOperation(record, { buildCacheConfiguration, terminal, - filterAppleDoubleFiles + excludeAppleDoubleFiles }); } @@ -618,7 +618,7 @@ export class CacheableOperationPlugin implements IPhasedCommandPlugin { cobuildConfiguration, record, terminal, - filterAppleDoubleFiles + excludeAppleDoubleFiles } = options; if (!buildCacheConfiguration?.buildCacheEnabled) { @@ -649,7 +649,7 @@ export class CacheableOperationPlugin implements IPhasedCommandPlugin { terminal, operationStateHash, phaseName: associatedPhase.name, - filterAppleDoubleFiles + excludeAppleDoubleFiles }); buildCacheContext.operationBuildCache = operationBuildCache; diff --git a/rush-plugins/rush-bridge-cache-plugin/src/BridgeCachePlugin.ts b/rush-plugins/rush-bridge-cache-plugin/src/BridgeCachePlugin.ts index 30303dae46..f0cd91e4d5 100644 --- a/rush-plugins/rush-bridge-cache-plugin/src/BridgeCachePlugin.ts +++ b/rush-plugins/rush-bridge-cache-plugin/src/BridgeCachePlugin.ts @@ -119,7 +119,7 @@ export class BridgeCachePlugin implements IRushPlugin { { buildCacheConfiguration, terminal, - filterAppleDoubleFiles: !!omitAppleDoubleFilesFromBuildCache + excludeAppleDoubleFiles: !!omitAppleDoubleFilesFromBuildCache } );