Skip to content
Merged
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 `rush-pnpm approve-builds` command to persist `globalOnlyBuiltDependencies` in pnpm-config.json",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
1 change: 1 addition & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,7 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration
readonly resolutionMode: PnpmResolutionMode | undefined;
readonly strictPeerDependencies: boolean;
readonly unsupportedPackageJsonSettings: unknown | undefined;
updateGlobalOnlyBuiltDependencies(onlyBuiltDependencies: string[] | undefined): void;
updateGlobalPatchedDependencies(patchedDependencies: Record<string, string> | undefined): void;
readonly useWorkspaces: boolean;
}
Expand Down
63 changes: 63 additions & 0 deletions libraries/rush-lib/src/cli/RushPnpmCommandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,36 @@ export class RushPnpmCommandLineParser {
}
break;
}
case 'approve-builds': {
const semver: typeof import('semver') = await import('semver');
/**
* The "approve-builds" command was introduced in pnpm version 10.1.0
* to approve packages for running build scripts when onlyBuiltDependencies is used
*/
if (semver.lt(this._rushConfiguration.packageManagerToolVersion, '10.1.0')) {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm approve-builds" command is added after pnpm@10.1.0.` +
` Please update "pnpmVersion" >= 10.1.0 in ${RushConstants.rushJsonFilename} file and run "rush update" to use this command.`
) + '\n'
);
throw new AlreadyReportedError();
}
const pnpmOptionsJsonFilename: string = path.join(
this._rushConfiguration.commonRushConfigFolder,
RushConstants.pnpmConfigFilename
);
if (this._rushConfiguration.rushConfigurationJson.pnpmOptions) {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm approve-builds" command is incompatible with specifying "pnpmOptions" in ${RushConstants.rushJsonFilename} file.` +
` Please move the content of "pnpmOptions" in ${RushConstants.rushJsonFilename} file to ${pnpmOptionsJsonFilename}`
) + '\n'
);
throw new AlreadyReportedError();
}
break;
}

// Known safe
case 'audit':
Expand Down Expand Up @@ -532,6 +562,39 @@ export class RushPnpmCommandLineParser {
}
break;
}
case 'approve-builds': {
if (this._subspace.getPnpmOptions() === undefined) {
const subspaceConfigFolder: string = this._subspace.getSubspaceConfigFolderPath();
this._terminal.writeErrorLine(
`The "rush-pnpm approve-builds" command cannot proceed without a pnpm-config.json file.` +
` Create one in this folder: ${subspaceConfigFolder}`
);
break;
}

// Example: "C:\MyRepo\common\temp\package.json"
const commonPackageJsonFilename: string = `${subspaceTempFolder}/${FileConstants.PackageJson}`;
const commonPackageJson: JsonObject = await JsonFile.loadAsync(commonPackageJsonFilename);
const newGlobalOnlyBuiltDependencies: string[] | undefined =
commonPackageJson?.pnpm?.onlyBuiltDependencies;
const pnpmOptions: PnpmOptionsConfiguration | undefined = this._subspace.getPnpmOptions();
const currentGlobalOnlyBuiltDependencies: string[] | undefined =
pnpmOptions?.globalOnlyBuiltDependencies;

if (!Objects.areDeepEqual(currentGlobalOnlyBuiltDependencies, newGlobalOnlyBuiltDependencies)) {
// Update onlyBuiltDependencies to pnpm configuration file
pnpmOptions?.updateGlobalOnlyBuiltDependencies(newGlobalOnlyBuiltDependencies);

// Rerun installation to update
await this._doRushUpdateAsync();

this._terminal.writeWarningLine(
`Rush refreshed the ${RushConstants.pnpmConfigFilename} and shrinkwrap file.\n` +
' Please commit this change to Git.'
);
}
break;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,17 +600,28 @@ export class WorkspaceInstallManager extends BaseInstallManager {
}
}

const onPnpmStdoutChunk: ((chunk: string) => void) | undefined =
pnpmTips.length > 0
? (chunk: string): void => {
// Iterate over the supported custom tip metadata and try to match the chunk.
for (const { isMatch, tipId } of pnpmTips) {
if (isMatch?.(chunk)) {
tipIDsToBePrinted.add(tipId);
}
}
const onPnpmStdoutChunk: ((chunk: string) => string | void) | undefined = (
chunk: string
): string | void => {
// Iterate over the supported custom tip metadata and try to match the chunk.
if (pnpmTips.length > 0) {
for (const { isMatch, tipId } of pnpmTips) {
if (isMatch?.(chunk)) {
tipIDsToBePrinted.add(tipId);
}
: undefined;
}
}

// Replace `pnpm approve-builds` with `rush-pnpm approve-builds` when running
// `rush install` or `rush update` to instruct users to use the correct command
const modifiedChunk: string = chunk.replace(
/pnpm approve-builds/g,
`rush-pnpm --subspace ${subspace.subspaceName} approve-builds`
);

// Return modified chunk if it was changed, otherwise return void to keep original
return modifiedChunk !== chunk ? modifiedChunk : undefined;
};
try {
await Utilities.executeCommandWithRetryAsync(
{
Expand Down
10 changes: 10 additions & 0 deletions libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,4 +539,14 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration
JsonFile.save(this._json, this.jsonFilename, { updateExistingFile: true });
}
}

/**
* Updates globalOnlyBuiltDependencies field of the PNPM options in the common/config/rush/pnpm-config.json file.
*/
public updateGlobalOnlyBuiltDependencies(onlyBuiltDependencies: string[] | undefined): void {
this._json.globalOnlyBuiltDependencies = onlyBuiltDependencies;
if (this.jsonFilename) {
JsonFile.save(this._json, this.jsonFilename, { updateExistingFile: true });
}
}
}