Skip to content

[rush] Add a RUSH_BUILD_CACHE_OVERRIDE_JSON env var to override the build cache configuration. #5154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Add support for a `RUSH_BUILD_CACHE_OVERRIDE_JSON` environment variable that takes a JSON string with the same format as the `common/config/build-cache.json` file that can be used to override the build cache configuration that is normally provided by that file.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
2 changes: 2 additions & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ export class EnvironmentConfiguration {
static get allowWarningsInSuccessfulBuild(): boolean;
static get buildCacheCredential(): string | undefined;
static get buildCacheEnabled(): boolean | undefined;
static get buildCacheOverrideJson(): string | undefined;
static get buildCacheWriteAllowed(): boolean | undefined;
static get cobuildContextId(): string | undefined;
static get cobuildLeafProjectLogOnlyAllowed(): boolean | undefined;
Expand Down Expand Up @@ -272,6 +273,7 @@ export const EnvironmentVariableNames: {
readonly RUSH_BUILD_CACHE_CREDENTIAL: "RUSH_BUILD_CACHE_CREDENTIAL";
readonly RUSH_BUILD_CACHE_ENABLED: "RUSH_BUILD_CACHE_ENABLED";
readonly RUSH_BUILD_CACHE_WRITE_ALLOWED: "RUSH_BUILD_CACHE_WRITE_ALLOWED";
readonly RUSH_BUILD_CACHE_OVERRIDE_JSON: "RUSH_BUILD_CACHE_OVERRIDE_JSON";
readonly RUSH_COBUILD_CONTEXT_ID: "RUSH_COBUILD_CONTEXT_ID";
readonly RUSH_COBUILD_RUNNER_ID: "RUSH_COBUILD_RUNNER_ID";
readonly RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED: "RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED";
Expand Down
75 changes: 50 additions & 25 deletions libraries/rush-lib/src/api/BuildCacheConfiguration.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import * as path from 'path';
import {
JsonFile,
JsonSchema,
Expand All @@ -16,7 +15,7 @@ import { FileSystemBuildCacheProvider } from '../logic/buildCache/FileSystemBuil
import { RushConstants } from '../logic/RushConstants';
import type { ICloudBuildCacheProvider } from '../logic/buildCache/ICloudBuildCacheProvider';
import { RushUserConfiguration } from './RushUserConfiguration';
import { EnvironmentConfiguration } from './EnvironmentConfiguration';
import { EnvironmentConfiguration, EnvironmentVariableNames } from './EnvironmentConfiguration';
import { CacheEntryId, type GetCacheEntryIdFunction } from '../logic/buildCache/CacheEntryId';
import type { CloudBuildCacheProviderFactory, RushSession } from '../pluginFramework/RushSession';
import schemaJson from '../schemas/build-cache.schema.json';
Expand Down Expand Up @@ -77,14 +76,14 @@ interface IBuildCacheConfigurationOptions {
cloudCacheProvider: ICloudBuildCacheProvider | undefined;
}

const BUILD_CACHE_JSON_SCHEMA: JsonSchema = JsonSchema.fromLoadedObject(schemaJson);

/**
* Use this class to load and save the "common/config/rush/build-cache.json" config file.
* This file provides configuration options for cached project build output.
* @beta
*/
export class BuildCacheConfiguration {
private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson);

/**
* Indicates whether the build cache feature is enabled.
* Typically it is enabled in the build-cache.json config file.
Expand Down Expand Up @@ -140,11 +139,12 @@ export class BuildCacheConfiguration {
rushConfiguration: RushConfiguration,
rushSession: RushSession
): Promise<BuildCacheConfiguration | undefined> {
const jsonFilePath: string = BuildCacheConfiguration.getBuildCacheConfigFilePath(rushConfiguration);
if (!FileSystem.exists(jsonFilePath)) {
return undefined;
}
return await BuildCacheConfiguration._loadAsync(jsonFilePath, terminal, rushConfiguration, rushSession);
const { buildCacheConfiguration } = await BuildCacheConfiguration._tryLoadInternalAsync(
terminal,
rushConfiguration,
rushSession
);
return buildCacheConfiguration;
}

/**
Expand All @@ -156,51 +156,76 @@ export class BuildCacheConfiguration {
rushConfiguration: RushConfiguration,
rushSession: RushSession
): Promise<BuildCacheConfiguration> {
const jsonFilePath: string = BuildCacheConfiguration.getBuildCacheConfigFilePath(rushConfiguration);
if (!FileSystem.exists(jsonFilePath)) {
const { buildCacheConfiguration, jsonFilePath } = await BuildCacheConfiguration._tryLoadInternalAsync(
terminal,
rushConfiguration,
rushSession
);

if (!buildCacheConfiguration) {
terminal.writeErrorLine(
`The build cache feature is not enabled. This config file is missing:\n` + jsonFilePath
);
terminal.writeLine(`\nThe Rush website documentation has instructions for enabling the build cache.`);
throw new AlreadyReportedError();
}

const buildCacheConfiguration: BuildCacheConfiguration = await BuildCacheConfiguration._loadAsync(
jsonFilePath,
terminal,
rushConfiguration,
rushSession
);

if (!buildCacheConfiguration.buildCacheEnabled) {
terminal.writeErrorLine(
`The build cache feature is not enabled. You can enable it by editing this config file:\n` +
jsonFilePath
);
throw new AlreadyReportedError();
}

return buildCacheConfiguration;
}

/**
* Gets the absolute path to the build-cache.json file in the specified rush workspace.
*/
public static getBuildCacheConfigFilePath(rushConfiguration: RushConfiguration): string {
return path.resolve(rushConfiguration.commonRushConfigFolder, RushConstants.buildCacheFilename);
return `${rushConfiguration.commonRushConfigFolder}/${RushConstants.buildCacheFilename}`;
}

private static async _tryLoadInternalAsync(
terminal: ITerminal,
rushConfiguration: RushConfiguration,
rushSession: RushSession
): Promise<{ buildCacheConfiguration: BuildCacheConfiguration | undefined; jsonFilePath: string }> {
const jsonFilePath: string = BuildCacheConfiguration.getBuildCacheConfigFilePath(rushConfiguration);
const buildCacheConfiguration: BuildCacheConfiguration | undefined =
await BuildCacheConfiguration._loadAsync(jsonFilePath, terminal, rushConfiguration, rushSession);
return { buildCacheConfiguration, jsonFilePath };
}

private static async _loadAsync(
jsonFilePath: string,
terminal: ITerminal,
rushConfiguration: RushConfiguration,
rushSession: RushSession
): Promise<BuildCacheConfiguration> {
const buildCacheJson: IBuildCacheJson = await JsonFile.loadAndValidateAsync(
jsonFilePath,
BuildCacheConfiguration._jsonSchema
);
const rushUserConfiguration: RushUserConfiguration = await RushUserConfiguration.initializeAsync();
): Promise<BuildCacheConfiguration | undefined> {
let buildCacheJson: IBuildCacheJson;
const buildCacheOverrideJson: string | undefined = EnvironmentConfiguration.buildCacheOverrideJson;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it's raw JSON in the environment variable. Should we add support for a file path to do the same thing?

if (buildCacheOverrideJson) {
buildCacheJson = JsonFile.parseString(buildCacheOverrideJson);
BUILD_CACHE_JSON_SCHEMA.validateObject(
buildCacheJson,
`${EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON} environment variable`
);
} else {
try {
buildCacheJson = await JsonFile.loadAndValidateAsync(jsonFilePath, BUILD_CACHE_JSON_SCHEMA);
} catch (e) {
if (!FileSystem.isNotExistError(e)) {
throw e;
} else {
return undefined;
}
}
}

const rushUserConfiguration: RushUserConfiguration = await RushUserConfiguration.initializeAsync();
let getCacheEntryId: GetCacheEntryIdFunction;
try {
getCacheEntryId = CacheEntryId.parsePattern(buildCacheJson.cacheEntryNamePattern);
Expand Down
25 changes: 25 additions & 0 deletions libraries/rush-lib/src/api/EnvironmentConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,15 @@ export const EnvironmentVariableNames = {
*/
RUSH_BUILD_CACHE_WRITE_ALLOWED: 'RUSH_BUILD_CACHE_WRITE_ALLOWED',

/**
* Set this environment variable to a JSON string to override the build cache configuration that normally lives
* at `common/config/rush/build-cache.json`.
*
* This is useful for testing purposes, or for OSS repos that are have a local-only cache, but can have
* a different cache configuration in CI/CD pipelines.
*/
RUSH_BUILD_CACHE_OVERRIDE_JSON: 'RUSH_BUILD_CACHE_OVERRIDE_JSON',

/**
* Setting this environment variable opts into running with cobuilds. The context id should be the same across
* multiple VMs, but changed when it is a new round of cobuilds.
Expand Down Expand Up @@ -250,6 +259,8 @@ export class EnvironmentConfiguration {

private static _buildCacheWriteAllowed: boolean | undefined;

private static _buildCacheOverrideJson: string | undefined;

private static _cobuildContextId: string | undefined;

private static _cobuildRunnerId: string | undefined;
Expand Down Expand Up @@ -353,6 +364,15 @@ export class EnvironmentConfiguration {
return EnvironmentConfiguration._buildCacheWriteAllowed;
}

/**
* If set, overrides the build cache configuration that normally lives at `common/config/rush/build-cache.json`.
* See {@link EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON}
*/
public static get buildCacheOverrideJson(): string | undefined {
EnvironmentConfiguration._ensureValidated();
return EnvironmentConfiguration._buildCacheOverrideJson;
}

/**
* Provides a determined cobuild context id if configured
* See {@link EnvironmentVariableNames.RUSH_COBUILD_CONTEXT_ID}
Expand Down Expand Up @@ -510,6 +530,11 @@ export class EnvironmentConfiguration {
break;
}

case EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON: {
EnvironmentConfiguration._buildCacheOverrideJson = value;
break;
}

case EnvironmentVariableNames.RUSH_COBUILD_CONTEXT_ID: {
EnvironmentConfiguration._cobuildContextId = value;
break;
Expand Down