diff --git a/README.md b/README.md index 08ce8a4ca4f..898c549d22b 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,7 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/repo-scripts/repo-toolbox](./repo-scripts/repo-toolbox/) | Used to execute various operations specific to this repo | | [/rigs/local-node-rig](./rigs/local-node-rig/) | A rig package for Node.js projects that build using Heft inside the RushStack repository. | | [/rigs/local-web-rig](./rigs/local-web-rig/) | A rig package for Web projects that build using Heft inside the RushStack repository. | +| [/rush-plugins/rush-buildxl-graph-plugin](./rush-plugins/rush-buildxl-graph-plugin/) | Rush plugin for generating a BuildXL graph. | | [/rush-plugins/rush-litewatch-plugin](./rush-plugins/rush-litewatch-plugin/) | An experimental alternative approach for multi-project watch mode | | [/vscode-extensions/rush-vscode-command-webview](./vscode-extensions/rush-vscode-command-webview/) | Part of the Rush Stack VSCode extension, provides a UI for invoking Rush commands | | [/vscode-extensions/rush-vscode-extension](./vscode-extensions/rush-vscode-extension/) | Enhanced experience for monorepos that use the Rush Stack toolchain | diff --git a/common/changes/@microsoft/rush/rush-bxl-operation-graph-plugin_2025-02-11-00-56.json b/common/changes/@microsoft/rush/rush-bxl-operation-graph-plugin_2025-02-11-00-56.json new file mode 100644 index 00000000000..bd7ff97cb34 --- /dev/null +++ b/common/changes/@microsoft/rush/rush-bxl-operation-graph-plugin_2025-02-11-00-56.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/common/changes/@rushstack/terminal/user-ianc-rush-bxl-operation-graph-plugin_2025-02-11-00-50.json b/common/changes/@rushstack/terminal/user-ianc-rush-bxl-operation-graph-plugin_2025-02-11-00-50.json new file mode 100644 index 00000000000..5eb7a43cd6c --- /dev/null +++ b/common/changes/@rushstack/terminal/user-ianc-rush-bxl-operation-graph-plugin_2025-02-11-00-50.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/terminal", + "comment": "Introduce a NoOpTerminalProvider.", + "type": "minor" + } + ], + "packageName": "@rushstack/terminal" +} \ No newline at end of file diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 40be7db171a..7a59add0ca7 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -4062,6 +4062,31 @@ importers: specifier: workspace:* version: link:../../rigs/local-node-rig + ../../../rush-plugins/rush-buildxl-graph-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + ../../../rush-plugins/rush-http-build-cache-plugin: dependencies: '@rushstack/node-core-library': diff --git a/common/reviews/api/rush-buildxl-graph-plugin.api.md b/common/reviews/api/rush-buildxl-graph-plugin.api.md new file mode 100644 index 00000000000..a207e8511e0 --- /dev/null +++ b/common/reviews/api/rush-buildxl-graph-plugin.api.md @@ -0,0 +1,42 @@ +## API Report File for "@rushstack/rush-buildxl-graph-plugin" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { IRushPlugin } from '@rushstack/rush-sdk'; +import { RushConfiguration } from '@rushstack/rush-sdk'; +import { RushSession } from '@rushstack/rush-sdk'; + +// @public +class DropBuildGraphPlugin implements IRushPlugin { + // (undocumented) + apply(session: RushSession, rushConfiguration: RushConfiguration): void; + // (undocumented) + readonly pluginName: string; +} +export default DropBuildGraphPlugin; + +// @public +export interface IBuildXLRushGraph { + // (undocumented) + nodes: IGraphNode[]; + // (undocumented) + repoSettings: { + commonTempFolder: string; + }; +} + +// @public (undocumented) +export interface IGraphNode { + command: string; + dependencies: string[]; + id: string; + package: string; + task: string; + workingDirectory: string; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/terminal.api.md b/common/reviews/api/terminal.api.md index 17da9dd1390..741bbd33f73 100644 --- a/common/reviews/api/terminal.api.md +++ b/common/reviews/api/terminal.api.md @@ -242,6 +242,13 @@ export class MockWritable extends TerminalWritable { reset(): void; } +// @beta +export class NoOpTerminalProvider implements ITerminalProvider { + get eolCharacter(): string; + get supportsColor(): boolean; + write(data: string, severity: TerminalProviderSeverity): void; +} + // @public export class NormalizeNewlinesTextRewriter extends TextRewriter { constructor(options: INormalizeNewlinesTextRewriterOptions); diff --git a/libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts b/libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts index 6f7be6867c7..4dc68994abd 100644 --- a/libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts +++ b/libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts @@ -34,8 +34,8 @@ export class ShellOperationRunner implements IOperationRunner { public readonly silent: boolean = false; public readonly cacheable: boolean = true; public readonly warningsAreAllowed: boolean; + public readonly commandToRun: string; - private readonly _commandToRun: string; private readonly _commandForHash: string; private readonly _rushProject: RushConfigurationProject; @@ -47,7 +47,7 @@ export class ShellOperationRunner implements IOperationRunner { this.warningsAreAllowed = EnvironmentConfiguration.allowWarningsInSuccessfulBuild || phase.allowWarningsOnSuccess || false; this._rushProject = options.rushProject; - this._commandToRun = options.commandToRun; + this.commandToRun = options.commandToRun; this._commandForHash = options.commandForHash; } @@ -69,14 +69,14 @@ export class ShellOperationRunner implements IOperationRunner { let hasWarningOrError: boolean = false; // Run the operation - terminal.writeLine(`Invoking: ${this._commandToRun}`); + terminal.writeLine(`Invoking: ${this.commandToRun}`); const { rushConfiguration, projectFolder } = this._rushProject; const { environment: initialEnvironment } = context; const subProcess: child_process.ChildProcess = Utilities.executeLifecycleCommandAsync( - this._commandToRun, + this.commandToRun, { rushConfiguration: rushConfiguration, workingDirectory: projectFolder, diff --git a/libraries/terminal/src/NoOpTerminalProvider.ts b/libraries/terminal/src/NoOpTerminalProvider.ts new file mode 100644 index 00000000000..032e04909f3 --- /dev/null +++ b/libraries/terminal/src/NoOpTerminalProvider.ts @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ITerminalProvider, TerminalProviderSeverity } from './ITerminalProvider'; + +/** + * Terminal provider that stores written data in buffers separated by severity. + * This terminal provider is designed to be used when code that prints to a terminal + * is being unit tested. + * + * @beta + */ +export class NoOpTerminalProvider implements ITerminalProvider { + /** + * {@inheritDoc ITerminalProvider.write} + */ + public write(data: string, severity: TerminalProviderSeverity): void { + // no-op + } + + /** + * {@inheritDoc ITerminalProvider.eolCharacter} + */ + public get eolCharacter(): string { + return '\n'; + } + + /** + * {@inheritDoc ITerminalProvider.supportsColor} + */ + public get supportsColor(): boolean { + return false; + } +} diff --git a/libraries/terminal/src/index.ts b/libraries/terminal/src/index.ts index 6c9a2070518..c1d65046786 100644 --- a/libraries/terminal/src/index.ts +++ b/libraries/terminal/src/index.ts @@ -46,4 +46,5 @@ export { type IPrefixProxyTerminalProviderOptionsBase, type IStaticPrefixProxyTerminalProviderOptions } from './PrefixProxyTerminalProvider'; +export { NoOpTerminalProvider } from './NoOpTerminalProvider'; export { TerminalStreamWritable, type ITerminalStreamWritableOptions } from './TerminalStreamWritable'; diff --git a/rush-plugins/rush-buildxl-graph-plugin/.eslintrc.js b/rush-plugins/rush-buildxl-graph-plugin/.eslintrc.js new file mode 100644 index 00000000000..27dc0bdff95 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/.eslintrc.js @@ -0,0 +1,12 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('local-node-rig/profiles/default/includes/eslint/patch/modern-module-resolution'); +// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 +require('local-node-rig/profiles/default/includes/eslint/patch/custom-config-package-names'); + +module.exports = { + extends: [ + 'local-node-rig/profiles/default/includes/eslint/profile/node-trusted-tool', + 'local-node-rig/profiles/default/includes/eslint/mixins/friendly-locals' + ], + parserOptions: { tsconfigRootDir: __dirname } +}; diff --git a/rush-plugins/rush-buildxl-graph-plugin/.npmignore b/rush-plugins/rush-buildxl-graph-plugin/.npmignore new file mode 100644 index 00000000000..5114742642b --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/.npmignore @@ -0,0 +1,35 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- + +lib/examples/** +lib-*/examples/** diff --git a/rush-plugins/rush-buildxl-graph-plugin/LICENSE b/rush-plugins/rush-buildxl-graph-plugin/LICENSE new file mode 100644 index 00000000000..5414f40bdb0 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/rush-buildxl-graph-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/rush-plugins/rush-buildxl-graph-plugin/config/api-extractor.json b/rush-plugins/rush-buildxl-graph-plugin/config/api-extractor.json new file mode 100644 index 00000000000..74590d3c4f8 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/config/api-extractor.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "mainEntryPointFilePath": "/lib/index.d.ts", + "apiReport": { + "enabled": true, + "reportFolder": "../../../common/reviews/api" + }, + "docModel": { + "enabled": false + }, + "dtsRollup": { + "enabled": true, + "betaTrimmedFilePath": "/dist/.d.ts" + } +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/config/jest.config.json b/rush-plugins/rush-buildxl-graph-plugin/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/config/rig.json b/rush-plugins/rush-buildxl-graph-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/package.json b/rush-plugins/rush-buildxl-graph-plugin/package.json new file mode 100644 index 00000000000..ff920ee574d --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/package.json @@ -0,0 +1,31 @@ +{ + "name": "@rushstack/rush-buildxl-graph-plugin", + "version": "5.148.0", + "description": "Rush plugin for generating a BuildXL graph.", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack", + "directory": "rush-plugins/rush-buildxl-graph-plugin" + }, + "homepage": "https://rushjs.io", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "start": "heft test --clean --watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "@rushstack/node-core-library": "workspace:*", + "@rushstack/rush-sdk": "workspace:*", + "@rushstack/terminal": "workspace:*", + "@rushstack/ts-command-line": "workspace:*" + }, + "devDependencies": { + "@microsoft/rush-lib": "workspace:*", + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + } +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/rush-plugin-manifest.json b/rush-plugins/rush-buildxl-graph-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..5993e5a2728 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/rush-plugin-manifest.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", + "plugins": [ + { + "pluginName": "rush-buildxl-graph-plugin", + "description": "Plugin for providing access to Rush build graph.", + "entryPoint": "lib/index.js" + } + ] +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/DropBuildGraphPlugin.ts b/rush-plugins/rush-buildxl-graph-plugin/src/DropBuildGraphPlugin.ts new file mode 100644 index 00000000000..2cb9ca2199f --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/DropBuildGraphPlugin.ts @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + RushConstants, + type ICreateOperationsContext, + type IPhasedCommand, + type IRushPlugin, + type Operation, + type RushConfiguration, + type RushSession +} from '@rushstack/rush-sdk'; +import { CommandLineParameterKind, type CommandLineStringParameter } from '@rushstack/ts-command-line'; + +import type { IGraphNode } from './GraphProcessor'; + +const PLUGIN_NAME: 'DropBuildGraphPlugin' = 'DropBuildGraphPlugin'; + +/** + * This is the type that represents the schema of the drop file + * @public + */ +export interface IBuildXLRushGraph { + nodes: IGraphNode[]; + repoSettings: { + commonTempFolder: string; + }; +} + +const DROP_GRAPH_PARAMETER_LONG_NAME: '--drop-graph' = '--drop-graph'; + +/** + * This plugin is used to drop the build graph to a file for BuildXL to consume. + * @public + */ +export class DropBuildGraphPlugin implements IRushPlugin { + public readonly pluginName: string = PLUGIN_NAME; + + public apply(session: RushSession, rushConfiguration: RushConfiguration): void { + session.hooks.runAnyPhasedCommand.tap(PLUGIN_NAME, (command: IPhasedCommand) => { + command.hooks.createOperations.tapPromise( + { + name: PLUGIN_NAME, + stage: Number.MAX_SAFE_INTEGER // Run this after other plugins have created all operations + }, + async (operations: Set, context: ICreateOperationsContext) => { + const dropGraphParameter: CommandLineStringParameter | undefined = context.customParameters.get( + DROP_GRAPH_PARAMETER_LONG_NAME + ) as CommandLineStringParameter; + if (!dropGraphParameter) { + // TODO: Introduce an API to allow plugins to register command line options for arbitrary, existing commands + // in a repo + throw new Error( + `The ${DROP_GRAPH_PARAMETER_LONG_NAME} parameter needs to be defined in "${RushConstants.commandLineFilename}" ` + + `and associated with the action "${command.actionName}"` + ); + } else if (dropGraphParameter.kind !== CommandLineParameterKind.String) { + throw new Error(`The ${DROP_GRAPH_PARAMETER_LONG_NAME} parameter must be a string parameter`); + } + + const dropGraphPath: string | undefined = dropGraphParameter.value; + if (dropGraphPath) { + const { dropGraphAsync } = await import('./dropGraph'); + const isValid: boolean = await dropGraphAsync({ + operations, + context, + dropGraphPath, + rushConfiguration, + logger: session.getLogger(PLUGIN_NAME) + }); + + if (!isValid) { + throw new Error('Failed to validate the graph'); + } else { + // If the --drop-graph flag is present, we want to exit the process after dropping the graph + return new Set(); + } + } else { + return operations; + } + } + ); + }); + } +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/GraphProcessor.ts b/rush-plugins/rush-buildxl-graph-plugin/src/GraphProcessor.ts new file mode 100644 index 00000000000..9a77c3d87de --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/GraphProcessor.ts @@ -0,0 +1,252 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Operation, ILogger } from '@rushstack/rush-sdk'; +import type { ShellOperationRunner } from '@rushstack/rush-sdk/lib/logic/operations/ShellOperationRunner'; +import { Colorize } from '@rushstack/terminal'; + +import { filterObjectForDebug } from './debugGraphFiltering'; + +/** + * @example + * ``` + * { + * "id": "@rushstack/node-core-library#_phase:build", + * "task": "_phase:build", + * "package": "@rushstack/node-core-library", + * "dependencies": [ + * "@rushstack/eslint-patch#_phase:build", + * "@rushstack/eslint-plugin#_phase:build", + * "@rushstack/eslint-plugin-packlets#_phase:build", + * "@rushstack/eslint-plugin-security#_phase:build" + * ], + * "workingDirectory": "/repo/libraries/node-core-library", + * "command": "heft run --only build -- --clean --production --drop-graph ./src/examples/graph.json" + * } + * ``` + * + * See https://github.com/microsoft/BuildXL/blob/adf025c1b96b8106984928df3e9c30c8331bc8d6/Public/Src/Tools/JavaScript/Tool.RushGraphBuilder/src/RushBuildPluginGraph.ts + * + * @public + */ +export interface IGraphNode { + /** + * The unique id of the Pip + * + * @example + * `@rushstack/node-core-library#_phase:build` + */ + id: string; + + /** + * The command to run during the Pip + */ + command: string; + + /** + * The working directory of the Pip + */ + workingDirectory: string; + + /** + * The project name associated with the Pip + * + * @example + * `@rushstack/node-core-library` + */ + package: string; + + /** + * The task name of the Pip + * + * @example + * `_phase:build` + */ + task: string; + + /** + * The IDs of the dependencies of the Pip. These are the {@link IGraphNode.id} properties of other Pips. + */ + dependencies: string[]; +} + +interface IGraphNodeInternal extends Readonly> { + dependencies: ReadonlySet; + command: string | undefined; +} + +type NodeMap = ReadonlyMap; + +const REQUIRED_FIELDS: Array = [ + // command is absent because it is not required + 'id', + 'task', + 'package', + 'dependencies', + 'workingDirectory' +]; + +/* + * Try to get the operation id, return undefined if it fails + */ +export function tryGetOperationId(operation: Partial): string | undefined { + const task: string | undefined = operation.associatedPhase?.name; + const project: string | undefined = operation.associatedProject?.packageName; + return task && project ? `${project}#${task}` : undefined; +} + +export class GraphProcessor { + private readonly _logger: ILogger; + + public constructor(logger: ILogger) { + this._logger = logger; + } + + /* + * Convert the operationMap into an array of graph nodes with empty commands pruned + */ + public processOperations(operations: Set): IGraphNode[] { + const nodeMap: Map = new Map(); + for (const operation of operations) { + const entry: IGraphNodeInternal = this._operationAsHashedEntry(operation); + nodeMap.set(entry.id, entry); + } + + return this._pruneNoOps(nodeMap); + } + + /* + * Validate that all dependencies exist + * Validate that all nodes have a non-empty command + * Print a message to the logger, and return true if the graph is valid + */ + public validateGraph(entries: readonly Readonly[]): boolean { + const entryIDs: Set = new Set(entries.map((entry) => entry.id)); + let isValid: boolean = true; + for (const entry of entries) { + for (const depId of entry.dependencies) { + if (!entryIDs.has(depId)) { + this._logger.emitError(new Error(`${entry.id} has a dependency on ${depId} which does not exist`)); + isValid = false; + } + } + + if (!entry.command) { + this._logger.emitError(new Error(`There is an empty command in ${entry.id}`)); + isValid = false; + } + } + + if (isValid) { + this._logger.terminal.writeLine( + Colorize.green('All nodes have non-empty commands and dependencies which exist') + ); + } + + const totalEdges: number = entries.reduce((acc, entry) => acc + entry.dependencies.length, 0); + this._logger.terminal.writeLine(`Graph has ${entries.length} nodes, ${totalEdges} edges`); + return isValid; + } + + /* + * Get the operation id, throw an error if it fails + */ + public getOperationId(operation: Operation): string { + const result: string | undefined = tryGetOperationId(operation); + if (!result) { + throw new Error( + `Operation does not have a name: ${JSON.stringify(filterObjectForDebug(operation, 2), undefined, 2)}` + ); + } + + return result; + } + + /* + * remove all entries with empty commands + * if an entry has a dependency with an empty command, it should inherit the dependencies of the empty command + */ + private _pruneNoOps(inputNodeMap: NodeMap): IGraphNode[] { + // Cache for the non-empty upstream dependencies of each operation + const nonEmptyDependenciesByOperation: Map> = new Map(); + function getNonEmptyDependencies(node: IGraphNodeInternal): ReadonlySet { + // If we've already computed this, return the cached result + let nonEmptyDependencies: Set | undefined = nonEmptyDependenciesByOperation.get(node); + if (!nonEmptyDependencies) { + nonEmptyDependencies = new Set(); + nonEmptyDependenciesByOperation.set(node, nonEmptyDependencies); + for (const dependencyID of node.dependencies) { + if (!inputNodeMap.get(dependencyID)!.command) { + // If the dependency is empty, recursively inherit its non-empty dependencies + for (const deepDependency of getNonEmptyDependencies(inputNodeMap.get(dependencyID)!)) { + nonEmptyDependencies.add(deepDependency); + } + } else { + nonEmptyDependencies.add(dependencyID); + } + } + } + + return nonEmptyDependencies; + } + + const result: IGraphNode[] = []; + for (const node of inputNodeMap.values()) { + const command: string | undefined = node.command; + if (command) { + const nonEmptyDependencySet: ReadonlySet = getNonEmptyDependencies(node); + result.push({ + ...node, + dependencies: Array.from(nonEmptyDependencySet), + command: command + }); + } + } + + return result; + } + + /* + * Convert an operation into a graph node + */ + private _operationAsHashedEntry(operation: Operation): IGraphNodeInternal { + const dependencies: Set = new Set(); + for (const element of operation.dependencies.values()) { + const id: string | undefined = this.getOperationId(element); + if (id) { + dependencies.add(id); + } + } + + const node: Partial = { + id: tryGetOperationId(operation), + task: operation.associatedPhase?.name, + package: operation.associatedProject?.packageName, + dependencies, + workingDirectory: operation.associatedProject?.projectFolder, + command: (operation.runner as ShellOperationRunner | undefined)?.commandToRun + }; + + const missingFields: (keyof IGraphNodeInternal)[] = []; + for (const requiredField of REQUIRED_FIELDS) { + if (!node[requiredField]) { + missingFields.push(requiredField); + } + } + + if (missingFields.length > 0) { + this._logger.emitError( + new Error(`Operation is missing required fields ${missingFields.join(', ')}: ${JSON.stringify(node)}`) + ); + } + + // the runner is a no-op if and only if the command is empty + if (!!operation.runner?.isNoOp !== !node.command) { + this._logger.emitError( + new Error(`${node.id}: Operation runner isNoOp does not match commandToRun existence`) + ); + } + + return node as IGraphNodeInternal; + } +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/debugGraphFiltering.ts b/rush-plugins/rush-buildxl-graph-plugin/src/debugGraphFiltering.ts new file mode 100644 index 00000000000..78fd25688a2 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/debugGraphFiltering.ts @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { tryGetOperationId } from './GraphProcessor'; + +const BANNED_KEYS: ReadonlySet = new Set([ + 'packageJsonEditor', + '_packageJson', + '_subspaces', + 'subspace', + 'rushConfiguration', + '_rushConfiguration', + 'associatedParameters', + '_dependencyProjects' +]); + +const ALLOWED_KEYS: ReadonlySet = new Set([ + 'associatedPhase', + 'name', + 'associatedProject', + 'packageName', + 'projectFolder', + 'dependencies', + 'runner', + 'commandToRun', + 'isNoOp' +]); + +/* + * Filter Rush's build graph to remove repetitive and unimportant information. Useful if the schema ever changes, or needs to. + * Due to the fact that it includes all properties that are not banned, it is not guaranteed to be stable across versions. + * Should not be part of the critical path. + * @param obj - the object to filter + * @param depth - the maximum depth to recurse + * @param simplify - if true, will replace embedded operations with their operation id + */ +export function filterObjectForDebug(obj: object, depth: number = 10, simplify: boolean = false): object { + const output: Record = {}; + for (const [key, value] of Object.entries(obj)) { + if (BANNED_KEYS.has(key)) { + output[key] = `${key} truncated`; + continue; + } else if (typeof value === 'function') { + output[key] = `${key}()`; + continue; + } else if (value instanceof Set) { + output[key] = filterObjectForDebug(Array.from(value), Math.min(depth - 1, 5), true); + continue; + } else if (value instanceof Object) { + if (depth <= 0) { + output[key] = `${key} too deep`; + continue; + } + + if (simplify) { + const operationId: string | undefined = tryGetOperationId(value); + if (operationId) { + output[key] = operationId; + continue; + } + } + + output[key] = filterObjectForDebug(value, depth - 1); + continue; + } + + output[key] = value; + } + + return output; +} + +export function filterObjectForTesting(obj: object, depth: number = 10, ignoreSets: boolean = false): object { + const output: Record = {}; + for (const [key, value] of Object.entries(obj)) { + if (!ALLOWED_KEYS.has(key) && !key.match(/^\d+$/)) { + continue; + } else if (value instanceof Set) { + if (!ignoreSets) { + // Don't need sets inside sets + output[key] = Array.from(value).map((subValue) => + filterObjectForTesting(subValue, Math.min(depth - 1, 5), true) + ); + } + + continue; + } else if (value instanceof Object) { + if (depth <= 0) { + output[key] = `${key} too deep`; + continue; + } + + output[key] = filterObjectForTesting(value, depth - 1, ignoreSets); + continue; + } + + output[key] = value; + } + + return output; +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/dropGraph.ts b/rush-plugins/rush-buildxl-graph-plugin/src/dropGraph.ts new file mode 100644 index 00000000000..1200fd40b0c --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/dropGraph.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'path'; +import type { ICreateOperationsContext, ILogger, Operation, RushConfiguration } from '@rushstack/rush-sdk'; +import { JsonFile } from '@rushstack/node-core-library'; + +import type { IBuildXLRushGraph } from './DropBuildGraphPlugin'; +import { type IGraphNode, GraphProcessor } from './GraphProcessor'; +import { filterObjectForDebug, filterObjectForTesting } from './debugGraphFiltering'; + +export interface IDropGraphOptions { + operations: Set; + context: ICreateOperationsContext; + dropGraphPath: string; + rushConfiguration: RushConfiguration; + logger: ILogger; +} + +const DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME: 'DEBUG_RUSH_BUILD_GRAPH' = 'DEBUG_RUSH_BUILD_GRAPH'; + +export async function dropGraphAsync(options: IDropGraphOptions): Promise { + const { + operations, + context, + dropGraphPath, + rushConfiguration: { commonTempFolder }, + logger + } = options; + if (process.env[DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME]) { + let filterFn: ((obj: object, depth?: number) => object) | undefined; + const debugValue: string | undefined = process.env[DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME]; + switch (process.env.DEBUG_RUSH_BUILD_GRAPH) { + case 'test': { + filterFn = filterObjectForTesting; + break; + } + + case 'full': { + filterFn = filterObjectForDebug; + break; + } + + default: { + throw new Error(`Invalid value for ${DEBUG_RUSH_BUILD_GRAPH_ENV_VAR_NAME}: ${debugValue}`); + } + } + + if (filterFn) { + const graphOut: unknown[] = []; + for (const operation of operations.keys()) { + graphOut.push(filterFn(operation)); + } + + const debugOutput: unknown = { + OperationMap: graphOut, + ICreateOperationsContext: filterFn(context) + }; + const debugPathOut: string = `${path.dirname(dropGraphPath)}/debug-${path.basename(dropGraphPath)}`; + await JsonFile.saveAsync(debugOutput, debugPathOut, { ensureFolderExists: true }); + } + } + + const graphProcessor: GraphProcessor = new GraphProcessor(logger); + const nodes: IGraphNode[] = graphProcessor.processOperations(operations); + const buildXLGraph: IBuildXLRushGraph = { + nodes, + repoSettings: { + commonTempFolder + } + }; + + await JsonFile.saveAsync(buildXLGraph, dropGraphPath, { ensureFolderExists: true }); + return graphProcessor.validateGraph(buildXLGraph.nodes); +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/examples/debug-graph.json b/rush-plugins/rush-buildxl-graph-plugin/src/examples/debug-graph.json new file mode 100644 index 00000000000..702d73cbcb6 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/examples/debug-graph.json @@ -0,0 +1,2393 @@ +{ + "OperationMap": [ + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/rush-sdk (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/rush-sdk", + "projectFolder": "/repo/libraries/rush-sdk" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@rushstack/ts-command-line (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/ts-command-line", + "projectFolder": "/repo/libraries/ts-command-line" + } + }, + { + "runner": { + "name": "@microsoft/rush-lib (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/rush-lib", + "projectFolder": "/repo/libraries/rush-lib" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + } + ], + "runner": { + "name": "@rushstack/rush-buildxl-graph-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/rush-buildxl-graph-plugin", + "projectFolder": "/repo/rush-plugins/rush-buildxl-graph-plugin" + } + }, + { + "dependencies": [ + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "dependencies": [ + { + "runner": { + "isNoOp": true, + "name": "@rushstack/eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-config", + "projectFolder": "/repo/eslint/eslint-config" + } + }, + { + "runner": { + "name": "@rushstack/eslint-patch (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-patch", + "projectFolder": "/repo/eslint/eslint-patch" + } + } + ], + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/eslint-patch (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-patch", + "projectFolder": "/repo/eslint/eslint-patch" + } + }, + { + "runner": { + "name": "@rushstack/eslint-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-plugin", + "projectFolder": "/repo/eslint/eslint-plugin" + } + }, + { + "runner": { + "name": "@rushstack/eslint-plugin-packlets (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-plugin-packlets", + "projectFolder": "/repo/eslint/eslint-plugin-packlets" + } + }, + { + "runner": { + "name": "@rushstack/eslint-plugin-security (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-plugin-security", + "projectFolder": "/repo/eslint/eslint-plugin-security" + } + } + ], + "runner": { + "isNoOp": true, + "name": "@rushstack/eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-config", + "projectFolder": "/repo/eslint/eslint-config" + } + }, + { + "dependencies": [], + "runner": { + "name": "@rushstack/eslint-patch (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-patch", + "projectFolder": "/repo/eslint/eslint-patch" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/tree-pattern (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/tree-pattern", + "projectFolder": "/repo/libraries/tree-pattern" + } + } + ], + "runner": { + "name": "@rushstack/eslint-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-plugin", + "projectFolder": "/repo/eslint/eslint-plugin" + } + }, + { + "dependencies": [], + "runner": { + "name": "@rushstack/tree-pattern (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/tree-pattern", + "projectFolder": "/repo/libraries/tree-pattern" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/tree-pattern (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/tree-pattern", + "projectFolder": "/repo/libraries/tree-pattern" + } + } + ], + "runner": { + "name": "@rushstack/eslint-plugin-packlets (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-plugin-packlets", + "projectFolder": "/repo/eslint/eslint-plugin-packlets" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/tree-pattern (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/tree-pattern", + "projectFolder": "/repo/libraries/tree-pattern" + } + } + ], + "runner": { + "name": "@rushstack/eslint-plugin-security (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-plugin-security", + "projectFolder": "/repo/eslint/eslint-plugin-security" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/lookup-by-path (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/lookup-by-path", + "projectFolder": "/repo/libraries/lookup-by-path" + } + }, + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/package-deps-hash (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/package-deps-hash", + "projectFolder": "/repo/libraries/package-deps-hash" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@microsoft/rush-lib (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/rush-lib", + "projectFolder": "/repo/libraries/rush-lib" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + }, + { + "runner": { + "name": "@rushstack/heft-webpack5-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-webpack5-plugin", + "projectFolder": "/repo/heft-plugins/heft-webpack5-plugin" + } + }, + { + "runner": { + "name": "@rushstack/stream-collator (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/stream-collator", + "projectFolder": "/repo/libraries/stream-collator" + } + }, + { + "runner": { + "name": "@rushstack/ts-command-line (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/ts-command-line", + "projectFolder": "/repo/libraries/ts-command-line" + } + }, + { + "runner": { + "name": "@rushstack/webpack-preserve-dynamic-require-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/webpack-preserve-dynamic-require-plugin", + "projectFolder": "/repo/webpack/preserve-dynamic-require-plugin" + } + } + ], + "runner": { + "name": "@rushstack/rush-sdk (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/rush-sdk", + "projectFolder": "/repo/libraries/rush-sdk" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + } + ], + "runner": { + "name": "@rushstack/lookup-by-path (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/lookup-by-path", + "projectFolder": "/repo/libraries/lookup-by-path" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/heft-config-file (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-config-file", + "projectFolder": "/repo/libraries/heft-config-file" + } + }, + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/operation-graph (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/operation-graph", + "projectFolder": "/repo/libraries/operation-graph" + } + }, + { + "runner": { + "name": "@rushstack/rig-package (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/rig-package", + "projectFolder": "/repo/libraries/rig-package" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@rushstack/ts-command-line (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/ts-command-line", + "projectFolder": "/repo/libraries/ts-command-line" + } + }, + { + "runner": { + "name": "@microsoft/api-extractor (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/api-extractor", + "projectFolder": "/repo/apps/api-extractor" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/rig-package (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/rig-package", + "projectFolder": "/repo/libraries/rig-package" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@rushstack/heft-config-file (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-config-file", + "projectFolder": "/repo/libraries/heft-config-file" + } + }, + { + "dependencies": [ + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@rushstack/rig-package (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/rig-package", + "projectFolder": "/repo/libraries/rig-package" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@rushstack/operation-graph (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/operation-graph", + "projectFolder": "/repo/libraries/operation-graph" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@rushstack/ts-command-line (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/ts-command-line", + "projectFolder": "/repo/libraries/ts-command-line" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@microsoft/api-extractor-model (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/api-extractor-model", + "projectFolder": "/repo/libraries/api-extractor-model" + } + }, + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/rig-package (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/rig-package", + "projectFolder": "/repo/libraries/rig-package" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@rushstack/ts-command-line (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/ts-command-line", + "projectFolder": "/repo/libraries/ts-command-line" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@microsoft/api-extractor (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/api-extractor", + "projectFolder": "/repo/apps/api-extractor" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@microsoft/api-extractor-model (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/api-extractor-model", + "projectFolder": "/repo/libraries/api-extractor-model" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@microsoft/api-extractor (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/api-extractor", + "projectFolder": "/repo/apps/api-extractor" + } + }, + { + "runner": { + "isNoOp": true, + "name": "@rushstack/heft-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-node-rig", + "projectFolder": "/repo/rigs/heft-node-rig" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@microsoft/api-extractor (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/api-extractor", + "projectFolder": "/repo/apps/api-extractor" + } + }, + { + "runner": { + "isNoOp": true, + "name": "@rushstack/eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/eslint-config", + "projectFolder": "/repo/eslint/eslint-config" + } + }, + { + "runner": { + "name": "@rushstack/heft-api-extractor-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-api-extractor-plugin", + "projectFolder": "/repo/heft-plugins/heft-api-extractor-plugin" + } + }, + { + "runner": { + "name": "@rushstack/heft-jest-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-jest-plugin", + "projectFolder": "/repo/heft-plugins/heft-jest-plugin" + } + }, + { + "runner": { + "name": "@rushstack/heft-lint-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-lint-plugin", + "projectFolder": "/repo/heft-plugins/heft-lint-plugin" + } + }, + { + "runner": { + "name": "@rushstack/heft-typescript-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-typescript-plugin", + "projectFolder": "/repo/heft-plugins/heft-typescript-plugin" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + } + ], + "runner": { + "isNoOp": true, + "name": "@rushstack/heft-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-node-rig", + "projectFolder": "/repo/rigs/heft-node-rig" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/heft-config-file (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-config-file", + "projectFolder": "/repo/libraries/heft-config-file" + } + }, + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@microsoft/api-extractor (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/api-extractor", + "projectFolder": "/repo/apps/api-extractor" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + } + ], + "runner": { + "name": "@rushstack/heft-api-extractor-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-api-extractor-plugin", + "projectFolder": "/repo/heft-plugins/heft-api-extractor-plugin" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/heft-config-file (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-config-file", + "projectFolder": "/repo/libraries/heft-config-file" + } + }, + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + } + ], + "runner": { + "name": "@rushstack/heft-jest-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-jest-plugin", + "projectFolder": "/repo/heft-plugins/heft-jest-plugin" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "name": "@rushstack/heft-typescript-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-typescript-plugin", + "projectFolder": "/repo/heft-plugins/heft-typescript-plugin" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + } + ], + "runner": { + "name": "@rushstack/heft-lint-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-lint-plugin", + "projectFolder": "/repo/heft-plugins/heft-lint-plugin" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/heft-config-file (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-config-file", + "projectFolder": "/repo/libraries/heft-config-file" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-eslint-config (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-eslint-config", + "projectFolder": "/repo/eslint/local-eslint-config" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + } + ], + "runner": { + "name": "@rushstack/heft-typescript-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-typescript-plugin", + "projectFolder": "/repo/heft-plugins/heft-typescript-plugin" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + } + ], + "runner": { + "name": "@rushstack/package-deps-hash (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/package-deps-hash", + "projectFolder": "/repo/libraries/package-deps-hash" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/heft-config-file (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-config-file", + "projectFolder": "/repo/libraries/heft-config-file" + } + }, + { + "runner": { + "name": "@rushstack/lookup-by-path (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/lookup-by-path", + "projectFolder": "/repo/libraries/lookup-by-path" + } + }, + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/package-deps-hash (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/package-deps-hash", + "projectFolder": "/repo/libraries/package-deps-hash" + } + }, + { + "runner": { + "name": "@rushstack/package-extractor (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/package-extractor", + "projectFolder": "/repo/libraries/package-extractor" + } + }, + { + "runner": { + "name": "@rushstack/rig-package (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/rig-package", + "projectFolder": "/repo/libraries/rig-package" + } + }, + { + "runner": { + "name": "@rushstack/stream-collator (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/stream-collator", + "projectFolder": "/repo/libraries/stream-collator" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@rushstack/ts-command-line (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/ts-command-line", + "projectFolder": "/repo/libraries/ts-command-line" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + }, + { + "runner": { + "name": "@rushstack/heft-webpack5-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-webpack5-plugin", + "projectFolder": "/repo/heft-plugins/heft-webpack5-plugin" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "name": "@rushstack/operation-graph (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/operation-graph", + "projectFolder": "/repo/libraries/operation-graph" + } + }, + { + "runner": { + "name": "@rushstack/webpack-deep-imports-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/webpack-deep-imports-plugin", + "projectFolder": "/repo/webpack/webpack-deep-imports-plugin" + } + }, + { + "runner": { + "name": "@rushstack/webpack-preserve-dynamic-require-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/webpack-preserve-dynamic-require-plugin", + "projectFolder": "/repo/webpack/preserve-dynamic-require-plugin" + } + } + ], + "runner": { + "name": "@microsoft/rush-lib (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@microsoft/rush-lib", + "projectFolder": "/repo/libraries/rush-lib" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@rushstack/ts-command-line (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/ts-command-line", + "projectFolder": "/repo/libraries/ts-command-line" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + }, + { + "runner": { + "name": "@rushstack/heft-webpack5-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-webpack5-plugin", + "projectFolder": "/repo/heft-plugins/heft-webpack5-plugin" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "name": "@rushstack/webpack-preserve-dynamic-require-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/webpack-preserve-dynamic-require-plugin", + "projectFolder": "/repo/webpack/preserve-dynamic-require-plugin" + } + } + ], + "runner": { + "name": "@rushstack/package-extractor (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/package-extractor", + "projectFolder": "/repo/libraries/package-extractor" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/debug-certificate-manager (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/debug-certificate-manager", + "projectFolder": "/repo/libraries/debug-certificate-manager" + } + }, + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + } + ], + "runner": { + "name": "@rushstack/heft-webpack5-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft-webpack5-plugin", + "projectFolder": "/repo/heft-plugins/heft-webpack5-plugin" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + } + ], + "runner": { + "name": "@rushstack/debug-certificate-manager (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/debug-certificate-manager", + "projectFolder": "/repo/libraries/debug-certificate-manager" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + } + ], + "runner": { + "name": "@rushstack/webpack-preserve-dynamic-require-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/webpack-preserve-dynamic-require-plugin", + "projectFolder": "/repo/webpack/preserve-dynamic-require-plugin" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "name": "@rushstack/terminal (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/terminal", + "projectFolder": "/repo/libraries/terminal" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + } + ], + "runner": { + "name": "@rushstack/stream-collator (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/stream-collator", + "projectFolder": "/repo/libraries/stream-collator" + } + }, + { + "dependencies": [ + { + "runner": { + "name": "@rushstack/node-core-library (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/node-core-library", + "projectFolder": "/repo/libraries/node-core-library" + } + }, + { + "runner": { + "isNoOp": true, + "name": "local-node-rig (build)" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "local-node-rig", + "projectFolder": "/repo/rigs/local-node-rig" + } + }, + { + "runner": { + "name": "@rushstack/heft (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/heft", + "projectFolder": "/repo/apps/heft" + } + } + ], + "runner": { + "name": "@rushstack/webpack-deep-imports-plugin (build)", + "commandToRun": "heft run --only build -- --clean --production" + }, + "associatedPhase": { + "name": "_phase:build", + "dependencies": {} + }, + "associatedProject": { + "packageName": "@rushstack/webpack-deep-imports-plugin", + "projectFolder": "/repo/webpack/webpack-deep-imports-plugin" + } + } + ], + "ICreateOperationsContext": {} +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/examples/graph.json b/rush-plugins/rush-buildxl-graph-plugin/src/examples/graph.json new file mode 100644 index 00000000000..0c989e1ebc4 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/examples/graph.json @@ -0,0 +1,503 @@ +{ + "nodes": [ + { + "id": "@rushstack/rush-buildxl-graph-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/rush-buildxl-graph-plugin", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/rush-sdk#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/ts-command-line#_phase:build", + "@microsoft/rush-lib#_phase:build", + "@rushstack/heft#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build" + ], + "workingDirectory": "/repo/rush-plugins/rush-buildxl-graph-plugin", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/node-core-library#_phase:build", + "task": "_phase:build", + "package": "@rushstack/node-core-library", + "dependencies": [ + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/libraries/node-core-library", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/eslint-patch#_phase:build", + "task": "_phase:build", + "package": "@rushstack/eslint-patch", + "dependencies": [], + "workingDirectory": "/repo/eslint/eslint-patch", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/eslint-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/eslint-plugin", + "dependencies": ["@rushstack/tree-pattern#_phase:build"], + "workingDirectory": "/repo/eslint/eslint-plugin", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/tree-pattern#_phase:build", + "task": "_phase:build", + "package": "@rushstack/tree-pattern", + "dependencies": [], + "workingDirectory": "/repo/libraries/tree-pattern", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/eslint-plugin-packlets#_phase:build", + "task": "_phase:build", + "package": "@rushstack/eslint-plugin-packlets", + "dependencies": ["@rushstack/tree-pattern#_phase:build"], + "workingDirectory": "/repo/eslint/eslint-plugin-packlets", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/eslint-plugin-security#_phase:build", + "task": "_phase:build", + "package": "@rushstack/eslint-plugin-security", + "dependencies": ["@rushstack/tree-pattern#_phase:build"], + "workingDirectory": "/repo/eslint/eslint-plugin-security", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/rush-sdk#_phase:build", + "task": "_phase:build", + "package": "@rushstack/rush-sdk", + "dependencies": [ + "@rushstack/lookup-by-path#_phase:build", + "@rushstack/node-core-library#_phase:build", + "@rushstack/package-deps-hash#_phase:build", + "@rushstack/terminal#_phase:build", + "@microsoft/rush-lib#_phase:build", + "@rushstack/heft#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build", + "@rushstack/heft-webpack5-plugin#_phase:build", + "@rushstack/stream-collator#_phase:build", + "@rushstack/ts-command-line#_phase:build", + "@rushstack/webpack-preserve-dynamic-require-plugin#_phase:build" + ], + "workingDirectory": "/repo/libraries/rush-sdk", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/lookup-by-path#_phase:build", + "task": "_phase:build", + "package": "@rushstack/lookup-by-path", + "dependencies": [ + "@rushstack/heft#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build" + ], + "workingDirectory": "/repo/libraries/lookup-by-path", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/heft#_phase:build", + "task": "_phase:build", + "package": "@rushstack/heft", + "dependencies": [ + "@rushstack/heft-config-file#_phase:build", + "@rushstack/node-core-library#_phase:build", + "@rushstack/operation-graph#_phase:build", + "@rushstack/rig-package#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/ts-command-line#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/apps/heft", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/heft-config-file#_phase:build", + "task": "_phase:build", + "package": "@rushstack/heft-config-file", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/rig-package#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/libraries/heft-config-file", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/rig-package#_phase:build", + "task": "_phase:build", + "package": "@rushstack/rig-package", + "dependencies": [ + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/libraries/rig-package", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/terminal#_phase:build", + "task": "_phase:build", + "package": "@rushstack/terminal", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/libraries/terminal", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/operation-graph#_phase:build", + "task": "_phase:build", + "package": "@rushstack/operation-graph", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/libraries/operation-graph", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/ts-command-line#_phase:build", + "task": "_phase:build", + "package": "@rushstack/ts-command-line", + "dependencies": [ + "@rushstack/terminal#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/libraries/ts-command-line", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@microsoft/api-extractor#_phase:build", + "task": "_phase:build", + "package": "@microsoft/api-extractor", + "dependencies": [ + "@microsoft/api-extractor-model#_phase:build", + "@rushstack/node-core-library#_phase:build", + "@rushstack/rig-package#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/ts-command-line#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/apps/api-extractor", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@microsoft/api-extractor-model#_phase:build", + "task": "_phase:build", + "package": "@microsoft/api-extractor-model", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/libraries/api-extractor-model", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/heft-api-extractor-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/heft-api-extractor-plugin", + "dependencies": [ + "@rushstack/heft-config-file#_phase:build", + "@rushstack/node-core-library#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft#_phase:build", + "@rushstack/terminal#_phase:build" + ], + "workingDirectory": "/repo/heft-plugins/heft-api-extractor-plugin", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/heft-jest-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/heft-jest-plugin", + "dependencies": [ + "@rushstack/heft-config-file#_phase:build", + "@rushstack/node-core-library#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/heft#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build" + ], + "workingDirectory": "/repo/heft-plugins/heft-jest-plugin", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/heft-lint-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/heft-lint-plugin", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build", + "@rushstack/terminal#_phase:build" + ], + "workingDirectory": "/repo/heft-plugins/heft-lint-plugin", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/heft-typescript-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/heft-typescript-plugin", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/heft-config-file#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft#_phase:build", + "@rushstack/terminal#_phase:build" + ], + "workingDirectory": "/repo/heft-plugins/heft-typescript-plugin", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/package-deps-hash#_phase:build", + "task": "_phase:build", + "package": "@rushstack/package-deps-hash", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/heft#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build" + ], + "workingDirectory": "/repo/libraries/package-deps-hash", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@microsoft/rush-lib#_phase:build", + "task": "_phase:build", + "package": "@microsoft/rush-lib", + "dependencies": [ + "@rushstack/heft-config-file#_phase:build", + "@rushstack/lookup-by-path#_phase:build", + "@rushstack/node-core-library#_phase:build", + "@rushstack/package-deps-hash#_phase:build", + "@rushstack/package-extractor#_phase:build", + "@rushstack/rig-package#_phase:build", + "@rushstack/stream-collator#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/ts-command-line#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build", + "@rushstack/heft#_phase:build", + "@rushstack/heft-webpack5-plugin#_phase:build", + "@rushstack/operation-graph#_phase:build", + "@rushstack/webpack-deep-imports-plugin#_phase:build", + "@rushstack/webpack-preserve-dynamic-require-plugin#_phase:build" + ], + "workingDirectory": "/repo/libraries/rush-lib", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/package-extractor#_phase:build", + "task": "_phase:build", + "package": "@rushstack/package-extractor", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/ts-command-line#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build", + "@rushstack/heft#_phase:build", + "@rushstack/heft-webpack5-plugin#_phase:build", + "@rushstack/webpack-preserve-dynamic-require-plugin#_phase:build" + ], + "workingDirectory": "/repo/libraries/package-extractor", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/heft-webpack5-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/heft-webpack5-plugin", + "dependencies": [ + "@rushstack/debug-certificate-manager#_phase:build", + "@rushstack/node-core-library#_phase:build", + "@rushstack/heft#_phase:build", + "@rushstack/terminal#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build" + ], + "workingDirectory": "/repo/heft-plugins/heft-webpack5-plugin", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/debug-certificate-manager#_phase:build", + "task": "_phase:build", + "package": "@rushstack/debug-certificate-manager", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/heft#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build" + ], + "workingDirectory": "/repo/libraries/debug-certificate-manager", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/webpack-preserve-dynamic-require-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/webpack-preserve-dynamic-require-plugin", + "dependencies": [ + "@rushstack/heft#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build" + ], + "workingDirectory": "/repo/webpack/preserve-dynamic-require-plugin", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/stream-collator#_phase:build", + "task": "_phase:build", + "package": "@rushstack/stream-collator", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@rushstack/terminal#_phase:build", + "@rushstack/heft#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build" + ], + "workingDirectory": "/repo/libraries/stream-collator", + "command": "heft run --only build -- --clean --production" + }, + { + "id": "@rushstack/webpack-deep-imports-plugin#_phase:build", + "task": "_phase:build", + "package": "@rushstack/webpack-deep-imports-plugin", + "dependencies": [ + "@rushstack/node-core-library#_phase:build", + "@microsoft/api-extractor#_phase:build", + "@rushstack/eslint-patch#_phase:build", + "@rushstack/eslint-plugin#_phase:build", + "@rushstack/eslint-plugin-packlets#_phase:build", + "@rushstack/eslint-plugin-security#_phase:build", + "@rushstack/heft-api-extractor-plugin#_phase:build", + "@rushstack/heft-jest-plugin#_phase:build", + "@rushstack/heft-lint-plugin#_phase:build", + "@rushstack/heft-typescript-plugin#_phase:build", + "@rushstack/heft#_phase:build" + ], + "workingDirectory": "/repo/webpack/webpack-deep-imports-plugin", + "command": "heft run --only build -- --clean --production" + } + ], + "repoSettings": { + "commonTempFolder": "/repo/common/temp" + } +} diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/index.ts b/rush-plugins/rush-buildxl-graph-plugin/src/index.ts new file mode 100644 index 00000000000..281f5f7d78c --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export type { IBuildXLRushGraph } from './DropBuildGraphPlugin'; +export type { IGraphNode } from './GraphProcessor'; +import { DropBuildGraphPlugin } from './DropBuildGraphPlugin'; +export default DropBuildGraphPlugin; diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/test/GraphProcessor.test.ts b/rush-plugins/rush-buildxl-graph-plugin/src/test/GraphProcessor.test.ts new file mode 100644 index 00000000000..7a5de764199 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/test/GraphProcessor.test.ts @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IOperationRunner, Operation } from '@rushstack/rush-sdk'; +import type { ShellOperationRunner } from '@rushstack/rush-sdk/lib/logic/operations/ShellOperationRunner'; +import { Terminal, NoOpTerminalProvider } from '@rushstack/terminal'; + +import { GraphProcessor, type IGraphNode } from '../GraphProcessor'; + +// to update the examples folder, run the following command from the project root: +// export DEBUG_RUSH_BUILD_GRAPH="test" && rush build --production -t . --drop-graph ./src/examples/graph.json +import graph from '../examples/graph.json'; +import debugGraph from '../examples/debug-graph.json'; + +function sortGraphNodes(graphNodes: IGraphNode[]): IGraphNode[] { + return graphNodes.sort((a, b) => (a.id === b.id ? 0 : a.id < b.id ? -1 : 1)); +} + +describe(GraphProcessor.name, () => { + let exampleGraph: readonly IGraphNode[]; + let graphParser: GraphProcessor; + let emittedErrors: Error[]; + let emittedWarnings: Error[]; + + beforeAll(() => { + exampleGraph = sortGraphNodes(Array.from(graph.nodes)); + }); + + beforeEach(() => { + emittedErrors = []; + emittedWarnings = []; + + const terminal: Terminal = new Terminal(new NoOpTerminalProvider()); + graphParser = new GraphProcessor({ + terminal, + emitWarning: emittedWarnings.push.bind(emittedWarnings), + emitError: emittedErrors.push.bind(emittedErrors) + }); + }); + + it('should process debug-graph.json into graph.json', () => { + let prunedGraph: IGraphNode[] = graphParser.processOperations( + new Set(debugGraph.OperationMap as unknown as Operation[]) + ); + + prunedGraph = sortGraphNodes(prunedGraph); + expect(prunedGraph).toEqual(exampleGraph); + expect(emittedErrors).toEqual([]); + expect(emittedWarnings).toEqual([]); + }); + + it('should fail if the input schema is invalid', () => { + const clonedOperationMap: Operation[] = JSON.parse(JSON.stringify(debugGraph.OperationMap)); + (clonedOperationMap[0].dependencies as unknown as Operation[]).push({ + incorrectPhase: { name: 'incorrectPhase' }, + incorrectProject: { packageName: 'incorrectProject' } + } as unknown as Operation); + const operations: Set = new Set(clonedOperationMap); + expect(() => graphParser.processOperations(operations)).toThrowErrorMatchingSnapshot(); + expect(emittedErrors).toEqual([]); + expect(emittedWarnings).toEqual([]); + }); + + it('should fail if isNoOp mismatches a command', () => { + const clonedOperationMap: Operation[] = JSON.parse(JSON.stringify(debugGraph.OperationMap)); + (clonedOperationMap[0].runner as IOperationRunner & { isNoOp: boolean }).isNoOp = true; + (clonedOperationMap[0].runner as ShellOperationRunner & { commandToRun: string }).commandToRun = + 'echo "hello world"'; + const operations: Set = new Set(clonedOperationMap); + graphParser.processOperations(operations); + expect(emittedErrors).not.toEqual([]); + expect(emittedWarnings).toEqual([]); + }); + + describe(GraphProcessor.prototype.validateGraph.name, () => { + it('should validate graph.json', () => { + const isValid: boolean = graphParser.validateGraph(exampleGraph); + expect(isValid).toBe(true); + expect(emittedErrors).toEqual([]); + expect(emittedWarnings).toEqual([]); + }); + + it('should fail to validate when command is empty', () => { + const invalidGraph = JSON.parse(JSON.stringify(exampleGraph)); + invalidGraph[2].command = ''; + const isValid: boolean = graphParser.validateGraph(invalidGraph); + expect(isValid).toBe(false); + expect(emittedErrors).toMatchSnapshot(); + expect(emittedWarnings).toEqual([]); + }); + + it('should fail to validate when command is missing', () => { + const invalidGraph = JSON.parse(JSON.stringify(exampleGraph)); + delete invalidGraph[2].command; + const isValid: boolean = graphParser.validateGraph(invalidGraph); + expect(isValid).toBe(false); + expect(emittedErrors).toMatchSnapshot(); + expect(emittedWarnings).toEqual([]); + }); + + it('should fail to validate when id is missing', () => { + const invalidGraph = JSON.parse(JSON.stringify(exampleGraph)); + delete invalidGraph[2].id; + const isValid: boolean = graphParser.validateGraph(invalidGraph); + expect(isValid).toBe(false); + expect(emittedErrors).toMatchSnapshot(); + expect(emittedWarnings).toEqual([]); + }); + + it('should fail to validate when id is empty', () => { + const invalidGraph = JSON.parse(JSON.stringify(exampleGraph)); + invalidGraph[2].id = ''; + const isValid: boolean = graphParser.validateGraph(invalidGraph); + expect(isValid).toBe(false); + expect(emittedErrors).toMatchSnapshot(); + expect(emittedWarnings).toEqual([]); + }); + + it('should fail to validate when dependencies refer to nonexistent nodes', () => { + const invalidGraph = JSON.parse(JSON.stringify(exampleGraph)); + invalidGraph[2].dependencies.push('@this/is/presumably#_not:real'); + const isValid: boolean = graphParser.validateGraph(invalidGraph); + expect(isValid).toBe(false); + expect(emittedErrors).toMatchSnapshot(); + expect(emittedWarnings).toEqual([]); + }); + }); +}); diff --git a/rush-plugins/rush-buildxl-graph-plugin/src/test/__snapshots__/GraphProcessor.test.ts.snap b/rush-plugins/rush-buildxl-graph-plugin/src/test/__snapshots__/GraphProcessor.test.ts.snap new file mode 100644 index 00000000000..f7a28b0d118 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/src/test/__snapshots__/GraphProcessor.test.ts.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GraphProcessor should fail if the input schema is invalid 1`] = ` +"Operation does not have a name: { + \\"incorrectPhase\\": { + \\"name\\": \\"incorrectPhase\\" + }, + \\"incorrectProject\\": { + \\"packageName\\": \\"incorrectProject\\" + } +}" +`; + +exports[`GraphProcessor validateGraph should fail to validate when command is empty 1`] = ` +Array [ + [Error: There is an empty command in @microsoft/rush-lib#_phase:build], +] +`; + +exports[`GraphProcessor validateGraph should fail to validate when command is missing 1`] = ` +Array [ + [Error: There is an empty command in @microsoft/rush-lib#_phase:build], +] +`; + +exports[`GraphProcessor validateGraph should fail to validate when dependencies refer to nonexistent nodes 1`] = ` +Array [ + [Error: @microsoft/rush-lib#_phase:build has a dependency on @this/is/presumably#_not:real which does not exist], +] +`; + +exports[`GraphProcessor validateGraph should fail to validate when id is empty 1`] = ` +Array [ + [Error: @rushstack/rush-buildxl-graph-plugin#_phase:build has a dependency on @microsoft/rush-lib#_phase:build which does not exist], + [Error: @rushstack/rush-sdk#_phase:build has a dependency on @microsoft/rush-lib#_phase:build which does not exist], +] +`; + +exports[`GraphProcessor validateGraph should fail to validate when id is missing 1`] = ` +Array [ + [Error: @rushstack/rush-buildxl-graph-plugin#_phase:build has a dependency on @microsoft/rush-lib#_phase:build which does not exist], + [Error: @rushstack/rush-sdk#_phase:build has a dependency on @microsoft/rush-lib#_phase:build which does not exist], +] +`; diff --git a/rush-plugins/rush-buildxl-graph-plugin/tsconfig.json b/rush-plugins/rush-buildxl-graph-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/rush-plugins/rush-buildxl-graph-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/rush.json b/rush.json index 362ec856367..5c4dcca80d8 100644 --- a/rush.json +++ b/rush.json @@ -1249,6 +1249,13 @@ "reviewCategory": "libraries", "versionPolicyName": "rush" }, + { + "packageName": "@rushstack/rush-buildxl-graph-plugin", + "projectFolder": "rush-plugins/rush-buildxl-graph-plugin", + "reviewCategory": "libraries" + // For now + // "versionPolicyName": "rush" + }, { "packageName": "@rushstack/rush-http-build-cache-plugin", "projectFolder": "rush-plugins/rush-http-build-cache-plugin",