Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ These GitHub repositories provide supplementary resources for Rush Stack:
| [/rigs/heft-web-rig](./rigs/heft-web-rig/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-web-rig.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-web-rig) | [changelog](./rigs/heft-web-rig/CHANGELOG.md) | [@rushstack/heft-web-rig](https://www.npmjs.com/package/@rushstack/heft-web-rig) |
| [/rush-plugins/rush-amazon-s3-build-cache-plugin](./rush-plugins/rush-amazon-s3-build-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-amazon-s3-build-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-amazon-s3-build-cache-plugin) | | [@rushstack/rush-amazon-s3-build-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-amazon-s3-build-cache-plugin) |
| [/rush-plugins/rush-azure-storage-build-cache-plugin](./rush-plugins/rush-azure-storage-build-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-azure-storage-build-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-azure-storage-build-cache-plugin) | | [@rushstack/rush-azure-storage-build-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-azure-storage-build-cache-plugin) |
| [/rush-plugins/rush-buildxl-graph-plugin](./rush-plugins/rush-buildxl-graph-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-buildxl-graph-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-buildxl-graph-plugin) | | [@rushstack/rush-buildxl-graph-plugin](https://www.npmjs.com/package/@rushstack/rush-buildxl-graph-plugin) |
| [/rush-plugins/rush-http-build-cache-plugin](./rush-plugins/rush-http-build-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-http-build-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-http-build-cache-plugin) | | [@rushstack/rush-http-build-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-http-build-cache-plugin) |
| [/rush-plugins/rush-redis-cobuild-plugin](./rush-plugins/rush-redis-cobuild-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-redis-cobuild-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-redis-cobuild-plugin) | | [@rushstack/rush-redis-cobuild-plugin](https://www.npmjs.com/package/@rushstack/rush-redis-cobuild-plugin) |
| [/rush-plugins/rush-resolver-cache-plugin](./rush-plugins/rush-resolver-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-resolver-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-resolver-cache-plugin) | | [@rushstack/rush-resolver-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-resolver-cache-plugin) |
Expand Down Expand Up @@ -205,7 +206,6 @@ These GitHub repositories provide supplementary resources for Rush Stack:
| [/rigs/decoupled-local-node-rig](./rigs/decoupled-local-node-rig/) | A rig package for Node.js projects that build using Heft inside the RushStack repository, but are dependencies of @rushstack/heft-node-rig or local-node-rig. |
| [/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 |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
2 changes: 1 addition & 1 deletion common/reviews/api/rush-buildxl-graph-plugin.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ export interface IDropGraphPluginOptions {

// @public (undocumented)
export interface IGraphNode {
cacheable?: false;
command: string;
dependencies: string[];
id: string;
package: string;
task: string;
uncacheable?: true;
workingDirectory: string;
}

Expand Down
2 changes: 1 addition & 1 deletion rush-plugins/rush-buildxl-graph-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rushstack/rush-buildxl-graph-plugin",
"version": "5.148.0",
"version": "5.153.0",
"description": "Rush plugin for generating a BuildXL graph.",
"repository": {
"type": "git",
Expand Down
63 changes: 31 additions & 32 deletions rush-plugins/rush-buildxl-graph-plugin/src/GraphProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ export interface IGraphNode {
dependencies: string[];

/**
* If true, the Pip is uncacheable
* If false, the Pip is uncacheable
*/
uncacheable?: true;
cacheable?: false;
}

interface IGraphNodeInternal extends Omit<IGraphNode, 'dependencies' | 'command'> {
Expand All @@ -90,12 +90,14 @@ const REQUIRED_FIELDS: Array<keyof IGraphNodeInternal> = [
];

/*
* Try to get the operation id, return undefined if it fails
* Get the operation id
*/
export function getOperationId(operation: Pick<Operation, 'associatedPhase' | 'associatedProject'>): string {
const task: string = operation.associatedPhase.name;
const project: string = operation.associatedProject.packageName;
return `${project}#${task}`;
export function getOperationId(operation: Operation): string {
const {
associatedPhase: { name: task },
associatedProject: { packageName }
} = operation;
return `${packageName}#${task}`;
}

export class GraphProcessor {
Expand Down Expand Up @@ -151,14 +153,6 @@ export class GraphProcessor {
return isValid;
}

/*
* Get the operation id
*/
public getOperationId(operation: Operation): string {
const result: string = getOperationId(operation);
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
Expand Down Expand Up @@ -209,30 +203,35 @@ export class GraphProcessor {
* Convert an operation into a graph node
*/
private _operationAsHashedEntry(operation: Operation): IGraphNodeInternal {
const dependencies: Set<string> = new Set();
for (const element of operation.dependencies.values()) {
const id: string | undefined = this.getOperationId(element);
if (id) {
dependencies.add(id);
}
}
const {
runner,
associatedPhase: { name: task },
associatedProject: {
// "package" is a reserved word
packageName,
projectFolder: workingDirectory
},
settings,
dependencies: operationDependencies
} = operation;

const { runner } = operation;
if (!runner) {
throw new Error(`Operation does not have a runner assigned`);
const dependencies: Set<string> = new Set();
for (const dependency of operationDependencies.values()) {
const id: string = getOperationId(dependency);
dependencies.add(id);
}

const node: Partial<IGraphNodeInternal> = {
id: getOperationId(operation),
task: operation.associatedPhase.name,
package: operation.associatedProject.packageName,
task,
package: packageName,
dependencies,
workingDirectory: operation.associatedProject.projectFolder,
command: (runner as Partial<Pick<ShellOperationRunner, 'commandToRun'>>).commandToRun
workingDirectory,
command: (runner as Partial<Pick<ShellOperationRunner, 'commandToRun'>>)?.commandToRun
};

if (operation.settings?.disableBuildCacheForOperation) {
node.uncacheable = true;
if (settings?.disableBuildCacheForOperation) {
node.cacheable = false;
}

const missingFields: (keyof IGraphNodeInternal)[] = [];
Expand All @@ -249,7 +248,7 @@ export class GraphProcessor {
}

// the runner is a no-op if and only if the command is empty
if (operation.isNoOp !== !node.command) {
if (!!runner?.isNoOp !== !node.command) {
this._logger.emitError(
new Error(`${node.id}: Operation runner isNoOp does not match commandToRun existence`)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// 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 { 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';
Expand All @@ -15,16 +16,6 @@ function sortGraphNodes(graphNodes: IGraphNode[]): IGraphNode[] {
return graphNodes.sort((a, b) => (a.id === b.id ? 0 : a.id < b.id ? -1 : 1));
}

function getDebugOperationMap(): Operation[] {
const cloned: typeof debugGraph.OperationMap = JSON.parse(JSON.stringify(debugGraph.OperationMap));

return cloned.map((op) => {
// Operation has getters
Object.setPrototypeOf(op, Operation.prototype);
return op as unknown as Operation;
});
}

describe(GraphProcessor.name, () => {
let exampleGraph: readonly IGraphNode[];
let graphParser: GraphProcessor;
Expand All @@ -48,7 +39,9 @@ describe(GraphProcessor.name, () => {
});

it('should process debug-graph.json into graph.json', () => {
let prunedGraph: IGraphNode[] = graphParser.processOperations(new Set<Operation>(getDebugOperationMap()));
let prunedGraph: IGraphNode[] = graphParser.processOperations(
new Set<Operation>(debugGraph.OperationMap as unknown as Operation[])
);

prunedGraph = sortGraphNodes(prunedGraph);
expect(prunedGraph).toEqual(exampleGraph);
Expand All @@ -57,7 +50,7 @@ describe(GraphProcessor.name, () => {
});

it('should fail if the input schema is invalid', () => {
const clonedOperationMap: Operation[] = getDebugOperationMap();
const clonedOperationMap: Operation[] = JSON.parse(JSON.stringify(debugGraph.OperationMap));
(clonedOperationMap[0].dependencies as unknown as Operation[]).push({
incorrectPhase: { name: 'incorrectPhase' },
incorrectProject: { packageName: 'incorrectProject' }
Expand All @@ -69,13 +62,11 @@ describe(GraphProcessor.name, () => {
});

it('should fail if isNoOp mismatches a command', () => {
const clonedOperationMap: Operation[] = getDebugOperationMap();
const runner: IOperationRunner | undefined = clonedOperationMap[0].runner;
if (!runner) {
throw new Error('runner is undefined');
}
(runner as IOperationRunner & { isNoOp: boolean }).isNoOp = true;
(runner as IOperationRunner & { commandToRun?: string }).commandToRun = 'echo "hello world"';
const clonedOperationMap: Operation[] = JSON.parse(JSON.stringify(debugGraph.OperationMap));
(clonedOperationMap[0].runner as IOperationRunner & { isNoOp: boolean }).isNoOp = true;
(
clonedOperationMap[0].runner as unknown as ShellOperationRunner & { commandToRun: string }
).commandToRun = 'echo "hello world"';
const operations: Set<Operation> = new Set(clonedOperationMap);
graphParser.processOperations(operations);
expect(emittedErrors).not.toEqual([]);
Expand Down
5 changes: 2 additions & 3 deletions rush.json
Original file line number Diff line number Diff line change
Expand Up @@ -1283,9 +1283,8 @@
{
"packageName": "@rushstack/rush-buildxl-graph-plugin",
"projectFolder": "rush-plugins/rush-buildxl-graph-plugin",
"reviewCategory": "libraries"
// For now
// "versionPolicyName": "rush"
"reviewCategory": "libraries",
"versionPolicyName": "rush"
},
{
"packageName": "@rushstack/rush-http-build-cache-plugin",
Expand Down
Loading