Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(rush-lib): setup Rush Custom Tips #4208

Merged
merged 38 commits into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1b488b9
feat(rush-lib): setup Rush Custom Tips
theJiawen Jun 15, 2023
534365b
chore: remove a useless file
theJiawen Jun 15, 2023
a14ad42
chore: add the change log file
theJiawen Jun 15, 2023
dd42c72
fix: lint
theJiawen Jun 15, 2023
cb12138
Refactor Rush Custom Tips based on the comments
theJiawen Jun 15, 2023
4c9c39b
wip: refactoring
theJiawen Jun 15, 2023
b091151
Fix some tests
theJiawen Jun 16, 2023
84a3b7c
fix an error
theJiawen Jun 16, 2023
c497c64
Refactor the code based on comments
theJiawen Jun 20, 2023
ca9a428
Remove terminal from the CustomTipsConfiguration
theJiawen Jun 20, 2023
ac0590e
Move "rush-custom-tips.json" to the test repo
theJiawen Jun 21, 2023
dedd332
Update rush-sdk test snapshot
theJiawen Jun 21, 2023
d32abf7
chore: update lockfile
theJiawen Jun 21, 2023
0b8ddc8
Merge branch 'main' into feat/rush-custom-tips
theJiawen Jun 21, 2023
c897538
Fix a test due to interface change
theJiawen Jun 22, 2023
feb4d19
Refactor: renaming, adding test
theJiawen Jun 22, 2023
881808d
Change a variable name to make it consistent
theJiawen Jun 26, 2023
61d492f
chore: renaming to make the github check happy.
theJiawen Jun 28, 2023
888e2d2
chore: update rush-lib.api.md
theJiawen Jun 28, 2023
1d44256
Apply suggestions from code review
theJiawen Jul 21, 2023
fba6a8e
Merge remote-tracking branch 'remotes/origin/main' into feat/rush-cus…
octogonz Aug 11, 2023
4796d09
Revise change file
octogonz Aug 11, 2023
3dcf246
Rename "RushCustomTips" -> "CustomTips" because the name is unique en…
octogonz Aug 11, 2023
083bbf1
Rename "RushCustomTips" -> "CustomTips" because the name is unique en…
octogonz Aug 11, 2023
2f187d2
Rename rush-custom-tips.json to custom-tips.json
octogonz Aug 11, 2023
552b0bd
Rename rush-custom-tips.json to custom-tips.json
octogonz Aug 11, 2023
7694f97
Move custom-tips.json to common/config/rush folder and add it to the …
octogonz Aug 11, 2023
1e6429e
Remove unused "ANOTHER_ID_THAT_IS_EXPOSED_TO_CUSTOM" identifier
octogonz Aug 11, 2023
9c4e16c
rush rebuild
octogonz Aug 11, 2023
c9ef086
Merge remote-tracking branch 'remotes/origin/main' into feat/rush-cus…
octogonz Aug 11, 2023
9385b42
heft test -u
octogonz Aug 11, 2023
c01a191
Report an error for duplicate ids
octogonz Aug 11, 2023
191eeba
Improved some config file field names based on a discussion with @the…
octogonz Aug 12, 2023
d81844c
Add a "rush init" template
octogonz Aug 12, 2023
90b53f5
Rename PNPM_MISMATCH_DEPENDENCY to TIP_RUSH_INCONSISTENT_VERSIONS, an…
octogonz Aug 12, 2023
c0a86f4
Add docs for "// (undocumented)" items in API report
octogonz Aug 12, 2023
1e365f4
Fix comparison logic
octogonz Aug 12, 2023
06fb2ba
Fix spacing
octogonz Aug 12, 2023
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
247 changes: 118 additions & 129 deletions build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ console.log('Calling an internal API...');

// Use a path-based import to access an internal API (do so at your own risk!)
import { VersionMismatchFinder } from '@microsoft/rush-lib/lib/logic/versionMismatch/VersionMismatchFinder';
import { ConsoleTerminalProvider, Terminal } from '@rushstack/node-core-library';

VersionMismatchFinder.ensureConsistentVersions(config);
const terminal = new Terminal(new ConsoleTerminalProvider());
VersionMismatchFinder.ensureConsistentVersions(config, terminal);
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Add a new config file \"custom-tips.json\" for customizing Rush messages (GitHub #4207)",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
49 changes: 49 additions & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,30 @@ export class CredentialCache {
static usingAsync(options: ICredentialCacheOptions, doActionAsync: (credentialCache: CredentialCache) => Promise<void> | void): Promise<void>;
}

// @beta (undocumented)
export type CustomTipId = 'PNPM_MISMATCH_DEPENDENCY' | string;

// @beta (undocumented)
export class CustomTipsConfiguration {
constructor(configFilename: string);
// (undocumented)
readonly configuration: Readonly<ICustomTipsJson>;
// (undocumented)
static readonly knownTipIds: ReadonlySet<string>;
// (undocumented)
log(tipId: CustomTipId, terminal: ITerminal): void;
}

// @beta (undocumented)
export enum CustomTipSeverity {
// (undocumented)
error = 2,
// (undocumented)
log = 0,
// (undocumented)
warning = 1
}

// @public (undocumented)
export enum DependencyType {
// (undocumented)
Expand Down Expand Up @@ -285,6 +309,26 @@ export interface ICredentialCacheOptions {
supportEditing: boolean;
}

// @beta (undocumented)
export interface ICustomTipItemJson {
// (undocumented)
id: CustomTipId;
// (undocumented)
prefix?: string;
// (undocumented)
severity?: CustomTipSeverity;
// (undocumented)
tip: string;
}

// @beta (undocumented)
export interface ICustomTipsJson {
// (undocumented)
customTips?: ICustomTipItemJson[];
// (undocumented)
prefix?: string;
}

// @beta (undocumented)
export interface IEnvironmentConfigurationInitializeOptions {
// (undocumented)
Expand Down Expand Up @@ -844,6 +888,10 @@ export class RushConfiguration {
get commonVersions(): CommonVersionsConfiguration;
get currentInstalledVariant(): string | undefined;
readonly currentVariantJsonFilename: string;
// @beta (undocumented)
readonly customTipsConfiguration: CustomTipsConfiguration;
// (undocumented)
readonly customTipsConfigurationFilePath: string;
readonly ensureConsistentVersions: boolean;
// @beta
readonly eventHooks: EventHooks;
Expand Down Expand Up @@ -977,6 +1025,7 @@ export class RushConstants {
static readonly commandLineFilename: string;
static readonly commonFolderName: string;
static readonly commonVersionsFilename: string;
static readonly customTipsFilename: string;
static readonly defaultMaxInstallAttempts: number;
static readonly defaultWatchDebounceMs: number;
static readonly experimentsFilename: string;
Expand Down
105 changes: 105 additions & 0 deletions libraries/rush-lib/src/api/CustomTipsConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import * as path from 'path';
import { FileSystem, ITerminal, JsonFile, JsonSchema } from '@rushstack/node-core-library';

import schemaJson from '../schemas/custom-tips.schema.json';

/**
* @beta
*/
export interface ICustomTipsJson {
prefix?: string;
customTips?: ICustomTipItemJson[];
}
/**
* @beta
*/
export interface ICustomTipItemJson {
id: CustomTipId;
tip: string;
prefix?: string;
severity?: CustomTipSeverity;
}

/**
* @beta
*/
export enum CustomTipSeverity {
log = 0,
warning = 1,
error = 2
}

/**
* @beta
*/
export type CustomTipId = 'PNPM_MISMATCH_DEPENDENCY' | string;

/**
* @beta
*/
export class CustomTipsConfiguration {
private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson);

private readonly _tipMap: Map<CustomTipId, ICustomTipItemJson>;
private readonly _jsonFileName: string;

public readonly configuration: Readonly<ICustomTipsJson>;

public static readonly knownTipIds: ReadonlySet<string> = new Set<string>(['PNPM_MISMATCH_DEPENDENCY']);

public constructor(configFilename: string) {
this._jsonFileName = configFilename;
this._tipMap = new Map();

if (!FileSystem.exists(this._jsonFileName)) {
this.configuration = {};
} else {
this.configuration = JsonFile.loadAndValidate(this._jsonFileName, CustomTipsConfiguration._jsonSchema);

const customTips: ICustomTipItemJson[] | undefined = this.configuration?.customTips;
if (customTips) {
for (const tipItem of customTips) {
if (!CustomTipsConfiguration.knownTipIds.has(tipItem.id)) {
throw new Error(
`The ${path.basename(this._jsonFileName)} configuration` +
` references an unknown ID "${tipItem.id}"`
);
}
if (this._tipMap.has(tipItem.id)) {
throw new Error(
`The ${path.basename(this._jsonFileName)} configuration` +
` specifies a duplicate definition for "${tipItem.id}"`
);
}
this._tipMap.set(tipItem.id, tipItem);
}
}
}
}

/**
*
* @param tipId - All the `tipId` options are in this doc: TODO: add link to doc
*/
public log(tipId: CustomTipId, terminal: ITerminal): void {
const customTipItem: ICustomTipItemJson | undefined = this._tipMap.get(tipId);

if (!customTipItem) {
return;
}

const prefix: string | undefined = customTipItem.prefix ?? this.configuration.prefix;
const tipString: string = `${prefix ?? ''}${customTipItem.tip}`;
switch (customTipItem.severity) {
case CustomTipSeverity.log:
terminal.writeLine(tipString);
break;
case CustomTipSeverity.warning:
terminal.writeWarningLine(tipString);
break;
default:
terminal.writeErrorLine(tipString);
break;
}
}
}
18 changes: 18 additions & 0 deletions libraries/rush-lib/src/api/RushConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import schemaJson from '../schemas/rush.schema.json';

import type * as DependencyAnalyzerModuleType from '../logic/DependencyAnalyzer';
import { PackageManagerOptionsConfigurationBase } from '../logic/base/BasePackageManagerOptionsConfiguration';
import { CustomTipsConfiguration } from './CustomTipsConfiguration';

const MINIMUM_SUPPORTED_RUSH_JSON_VERSION: string = '0.0.0';
const DEFAULT_BRANCH: string = 'main';
Expand All @@ -59,6 +60,7 @@ const knownRushConfigFilenames: string[] = [
RushConstants.buildCacheFilename,
RushConstants.commandLineFilename,
RushConstants.commonVersionsFilename,
RushConstants.customTipsFilename,
RushConstants.experimentsFilename,
RushConstants.nonbrowserApprovedPackagesFilename,
RushConstants.pinnedVersionsFilename,
Expand Down Expand Up @@ -535,6 +537,16 @@ export class RushConfiguration {
*/
public readonly versionPolicyConfigurationFilePath: string;

/**
* @beta
*/
public readonly customTipsConfiguration: CustomTipsConfiguration;

/**
*
*/
public readonly customTipsConfigurationFilePath: string;

/**
* This configuration object contains settings repo maintainers have specified to enable
* and disable experimental Rush features.
Expand Down Expand Up @@ -805,6 +817,12 @@ export class RushConfiguration {
);
this.versionPolicyConfiguration = new VersionPolicyConfiguration(this.versionPolicyConfigurationFilePath);

this.customTipsConfigurationFilePath = path.join(
this.commonRushConfigFolder,
RushConstants.customTipsFilename
);
this.customTipsConfiguration = new CustomTipsConfiguration(this.customTipsConfigurationFilePath);

this._variants = new Set<string>();

if (rushConfigurationJson.variants) {
Expand Down
16 changes: 16 additions & 0 deletions libraries/rush-lib/src/api/test/CustomTipsConfiguration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { CustomTipsConfiguration } from '../CustomTipsConfiguration';
import { RushConfiguration } from '../RushConfiguration';

describe(CustomTipsConfiguration.name, () => {
it('loads the config file (custom-tips.json)', () => {
const rushFilename: string = `${__dirname}/repo/rush-npm.json`;
const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename);
expect(rushConfiguration.customTipsConfiguration.configuration.customTips?.length).toBe(1);
});

it('reports an error for duplicate tips', () => {
expect(() => {
new CustomTipsConfiguration(`${__dirname}/jsonFiles/custom-tips.error.json`);
}).toThrowError('PNPM_MISMATCH_DEPENDENCY');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"customTips": [
{
"id": "PNPM_MISMATCH_DEPENDENCY",
"tip": "definition 1"
},
{
"id": "PNPM_MISMATCH_DEPENDENCY",
"tip": "definition 2"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
// "$schema": todo need a link
// The prefix will be prepended to all the custom message.
"prefix": "⭐️ [Monorepo Infra team tip]: ",

"customTips": [
{
"id": "PNPM_MISMATCH_DEPENDENCY",
"tip": "This is so wrong my friend. Please read this doc for more information: google.com"
// "prefix": "" // override the global prefix.
// "severity": "error" // default to error for more visibility
}
]
}
6 changes: 5 additions & 1 deletion libraries/rush-lib/src/cli/actions/BaseInstallAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import { VersionMismatchFinder } from '../../logic/versionMismatch/VersionMismat
import { Variants } from '../../api/Variants';
import { RushConstants } from '../../logic/RushConstants';
import { SelectionParameterSet } from '../parsing/SelectionParameterSet';
import { ConsoleTerminalProvider, ITerminal, Terminal } from '@rushstack/node-core-library';

/**
* This is the common base class for InstallAction and UpdateAction.
*/
export abstract class BaseInstallAction extends BaseRushAction {
protected readonly _terminal: ITerminal;
protected readonly _variant: CommandLineStringParameter;
protected readonly _purgeParameter: CommandLineFlagParameter;
protected readonly _bypassPolicyParameter: CommandLineFlagParameter;
Expand All @@ -43,6 +45,8 @@ export abstract class BaseInstallAction extends BaseRushAction {
public constructor(options: IBaseRushActionOptions) {
super(options);

this._terminal = new Terminal(new ConsoleTerminalProvider({ verboseEnabled: options.parser.isDebug }));

this._purgeParameter = this.defineFlagParameter({
parameterLongName: '--purge',
parameterShortName: '-p',
Expand Down Expand Up @@ -90,7 +94,7 @@ export abstract class BaseInstallAction extends BaseRushAction {
protected abstract buildInstallOptionsAsync(): Promise<IInstallManagerOptions>;

protected async runAsync(): Promise<void> {
VersionMismatchFinder.ensureConsistentVersions(this.rushConfiguration, {
VersionMismatchFinder.ensureConsistentVersions(this.rushConfiguration, this._terminal, {
variant: this._variant.value
});

Expand Down
5 changes: 4 additions & 1 deletion libraries/rush-lib/src/cli/actions/CheckAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { RushCommandLineParser } from '../RushCommandLineParser';
import { BaseRushAction } from './BaseRushAction';
import { VersionMismatchFinder } from '../../logic/versionMismatch/VersionMismatchFinder';
import { Variants } from '../../api/Variants';
import { ConsoleTerminalProvider, ITerminal, Terminal } from '@rushstack/node-core-library';

export class CheckAction extends BaseRushAction {
private readonly _terminal: ITerminal;
private readonly _variant: CommandLineStringParameter;
private readonly _jsonFlag: CommandLineFlagParameter;
private readonly _verboseFlag: CommandLineFlagParameter;
Expand All @@ -27,6 +29,7 @@ export class CheckAction extends BaseRushAction {
parser
});

this._terminal = new Terminal(new ConsoleTerminalProvider({ verboseEnabled: parser.isDebug }));
this._variant = this.defineStringParameter(Variants.VARIANT_PARAMETER);
this._jsonFlag = this.defineFlagParameter({
parameterLongName: '--json',
Expand All @@ -52,7 +55,7 @@ export class CheckAction extends BaseRushAction {
);
}

VersionMismatchFinder.rushCheck(this.rushConfiguration, {
VersionMismatchFinder.rushCheck(this.rushConfiguration, this._terminal, {
variant: this._variant.value,
printAsJson: this._jsonFlag.value,
truncateLongPackageNameLists: !this._verboseFlag.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
);

await doBasicInstallAsync({
terminal: this._terminal,
rushConfiguration: this.rushConfiguration,
rushGlobalFolder: this.rushGlobalFolder,
isDebug: this.parser.isDebug
Expand Down
7 changes: 7 additions & 0 deletions libraries/rush-lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ export { ILaunchOptions, Rush } from './api/Rush';
export { RushInternals as _RushInternals } from './api/RushInternals';

export { ExperimentsConfiguration, IExperimentsJson } from './api/ExperimentsConfiguration';
export {
CustomTipsConfiguration,
CustomTipId,
CustomTipSeverity,
ICustomTipsJson,
ICustomTipItemJson
} from './api/CustomTipsConfiguration';

export { ProjectChangeAnalyzer, IGetChangedProjectsOptions } from './logic/ProjectChangeAnalyzer';

Expand Down
7 changes: 7 additions & 0 deletions libraries/rush-lib/src/logic/RushConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ export class RushConstants {
*/
public static readonly repoStateFilename: string = 'repo-state.json';

/**
* The filename ("custom-tips.json") for the file used by Rush to
* print user-customized messages.
* This configuration file should go in the "common/config/rush" folder.
*/
public static readonly customTipsFilename: string = 'custom-tips.json';

/**
* The name of the per-project folder where project-specific Rush files are stored. For example,
* the package-deps files, which are used by commands to determine if a particular project needs to be rebuilt.
Expand Down
Loading