From 92839b9560931c69ba1a75f5456130623df67263 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 25 May 2023 11:57:30 -0700 Subject: [PATCH 01/20] feat: set up metadata plugin --- .../command-plugins/package.json | 1 + .../command-plugins/pnpm-lock.yaml | 2 + .../rush-metadata-plugin/command-line.json | 23 +++++ .../rush-plugin-manifest.json | 11 +++ common/config/rush/pnpm-lock.yaml | 29 ++++++ common/config/rush/rush-plugins.json | 5 + .../rush-metadata-plugin/.eslintrc.js | 14 +++ rush-plugins/rush-metadata-plugin/.gitignore | 1 + .../rush-metadata-plugin/command-line.json | 23 +++++ .../rush-metadata-plugin/package.json | 43 ++++++++ .../rush-plugin-manifest.json | 11 +++ .../rush-metadata-plugin/src/index.ts | 1 + .../rush-metadata-plugin/tsconfig.json | 97 +++++++++++++++++++ rush.json | 5 + 14 files changed, 266 insertions(+) create mode 100644 common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json create mode 100644 common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-plugin-manifest.json create mode 100644 rush-plugins/rush-metadata-plugin/.eslintrc.js create mode 100644 rush-plugins/rush-metadata-plugin/.gitignore create mode 100644 rush-plugins/rush-metadata-plugin/command-line.json create mode 100644 rush-plugins/rush-metadata-plugin/package.json create mode 100644 rush-plugins/rush-metadata-plugin/rush-plugin-manifest.json create mode 100644 rush-plugins/rush-metadata-plugin/src/index.ts create mode 100644 rush-plugins/rush-metadata-plugin/tsconfig.json diff --git a/common/autoinstallers/command-plugins/package.json b/common/autoinstallers/command-plugins/package.json index 43f204ca..d65d85da 100644 --- a/common/autoinstallers/command-plugins/package.json +++ b/common/autoinstallers/command-plugins/package.json @@ -9,6 +9,7 @@ "rush-lint-staged-plugin": "0.1.6", "rush-sort-package-json": "0.0.3", "rush-upgrade-self-plugin": "1.0.6", + "rush-metadata-plugin": "link:../../../rush-plugins/rush-metadata-plugin", "typescript": "4.4.2" } } diff --git a/common/autoinstallers/command-plugins/pnpm-lock.yaml b/common/autoinstallers/command-plugins/pnpm-lock.yaml index 2ff6a010..8d8cf0af 100644 --- a/common/autoinstallers/command-plugins/pnpm-lock.yaml +++ b/common/autoinstallers/command-plugins/pnpm-lock.yaml @@ -5,6 +5,7 @@ specifiers: rush-audit-cache-plugin: 0.0.2 rush-init-project-plugin: 0.6.0 rush-lint-staged-plugin: 0.1.6 + rush-metadata-plugin: link:../../../rush-plugins/rush-metadata-plugin rush-sort-package-json: 0.0.3 rush-upgrade-self-plugin: 1.0.6 typescript: 4.4.2 @@ -14,6 +15,7 @@ dependencies: rush-audit-cache-plugin: 0.0.2 rush-init-project-plugin: 0.6.0_typescript@4.4.2 rush-lint-staged-plugin: 0.1.6 + rush-metadata-plugin: link:../../../rush-plugins/rush-metadata-plugin rush-sort-package-json: 0.0.3 rush-upgrade-self-plugin: 1.0.6 typescript: 4.4.2 diff --git a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json new file mode 100644 index 00000000..48b48603 --- /dev/null +++ b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [ + { + "name": "meta", + "commandKind": "global", + "summary": "Create or update metadata information in the current package.", + "shellCommand": "node /lib/bin.js", + "safeForSimultaneousRushProcesses": true + } + ], + "parameters": [ + { + "parameterKind": "string", + "description": "The package name of the project", + "longName": "--project", + "shortName": "-p", + "argumentName": "PACKAGE_NAME", + "associatedCommands": ["meta"], + "required": false + } + ] +} diff --git a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-plugin-manifest.json b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000..616133b0 --- /dev/null +++ b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-plugin-manifest.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", + "plugins": [ + { + "pluginName": "rush-metadata-plugin", + "description": "A tool for managing metadata files", + "commandLineJsonFilePath": "command-line.json", + "optionsSchema": "lib/schemas/plugin-options.schema.json" + } + ] +} diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index de6dfbed..227285a1 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -212,6 +212,35 @@ importers: eslint: 7.32.0 typescript: 4.4.2 + ../../rush-plugins/rush-metadata-plugin: + specifiers: + '@rushstack/eslint-config': 2.4.5 + '@rushstack/heft': 0.43.2 + '@rushstack/heft-jest-plugin': ~0.1.53 + '@rushstack/heft-node-rig': 1.2.31 + '@rushstack/node-core-library': 3.44.1 + '@rushstack/rush-sdk': 5.62.4 + '@types/heft-jest': 1.0.1 + '@types/node': 12.20.24 + commander: ~9.4.0 + eslint: 7.32.0 + ignore: 5.1.9 + typescript: 4.4.2 + dependencies: + '@rushstack/node-core-library': 3.44.1 + '@rushstack/rush-sdk': 5.62.4 + commander: 9.4.0 + ignore: 5.1.9 + devDependencies: + '@rushstack/eslint-config': 2.4.5_eslint@7.32.0+typescript@4.4.2 + '@rushstack/heft': 0.43.2 + '@rushstack/heft-jest-plugin': 0.1.53_@rushstack+heft@0.43.2 + '@rushstack/heft-node-rig': 1.2.31_@rushstack+heft@0.43.2 + '@types/heft-jest': 1.0.1 + '@types/node': 12.20.24 + eslint: 7.32.0 + typescript: 4.4.2 + ../../rush-plugins/rush-print-log-if-error-plugin: specifiers: '@rushstack/eslint-config': 2.4.5 diff --git a/common/config/rush/rush-plugins.json b/common/config/rush/rush-plugins.json index b4d5c7e9..063b84eb 100644 --- a/common/config/rush/rush-plugins.json +++ b/common/config/rush/rush-plugins.json @@ -50,6 +50,11 @@ "packageName": "rush-upgrade-self-plugin", "pluginName": "rush-upgrade-self-plugin", "autoinstallerName": "command-plugins" // the name of autoinstaller you created before + }, + { + "packageName": "rush-metadata-plugin", + "pluginName": "rush-metadata-plugin", + "autoinstallerName": "command-plugins" // the name of autoinstaller you created before } ] } diff --git a/rush-plugins/rush-metadata-plugin/.eslintrc.js b/rush-plugins/rush-metadata-plugin/.eslintrc.js new file mode 100644 index 00000000..925d67c4 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/.eslintrc.js @@ -0,0 +1,14 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require("@rushstack/eslint-config/patch/modern-module-resolution"); + +module.exports = { + extends: [ + "@rushstack/eslint-config/profile/node-trusted-tool", + "@rushstack/eslint-config/mixins/friendly-locals", + ], + parserOptions: { tsconfigRootDir: __dirname }, + ignorePatterns: ["node_modules/", "lib/"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + }, +}; diff --git a/rush-plugins/rush-metadata-plugin/.gitignore b/rush-plugins/rush-metadata-plugin/.gitignore new file mode 100644 index 00000000..f1ff06d6 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/.gitignore @@ -0,0 +1 @@ +lib/ \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/command-line.json b/rush-plugins/rush-metadata-plugin/command-line.json new file mode 100644 index 00000000..48b48603 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/command-line.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [ + { + "name": "meta", + "commandKind": "global", + "summary": "Create or update metadata information in the current package.", + "shellCommand": "node /lib/bin.js", + "safeForSimultaneousRushProcesses": true + } + ], + "parameters": [ + { + "parameterKind": "string", + "description": "The package name of the project", + "longName": "--project", + "shortName": "-p", + "argumentName": "PACKAGE_NAME", + "associatedCommands": ["meta"], + "required": false + } + ] +} diff --git a/rush-plugins/rush-metadata-plugin/package.json b/rush-plugins/rush-metadata-plugin/package.json new file mode 100644 index 00000000..864010a6 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/package.json @@ -0,0 +1,43 @@ +{ + "name": "rush-metadata-plugin", + "version": "0.0.1", + "description": "A tool for managing metadata files", + "keywords": [ + "rush", + "plugin", + "command", + "metadata" + ], + "homepage": "https://github.com/bytemate/rush-plugins#readme", + "repository": { + "type": "git", + "url": "https://github.com/bytemate/rush-plugins", + "directory": "rush-plugins/rush-audit-cache-plugin" + }, + "license": "MIT", + "author": "william2958", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "heft build --clean", + "build:watch": "heft build --watch", + "prepublishOnly": "npm run build", + "test": "heft test" + }, + "dependencies": { + "@rushstack/node-core-library": "3.44.1", + "@rushstack/rush-sdk": "5.62.4", + "commander": "~9.4.0", + "ignore": "5.1.9" + }, + "devDependencies": { + "@rushstack/eslint-config": "2.4.5", + "@rushstack/heft": "0.43.2", + "@rushstack/heft-jest-plugin": "~0.1.53", + "@rushstack/heft-node-rig": "1.2.31", + "@types/heft-jest": "1.0.1", + "@types/node": "12.20.24", + "eslint": "7.32.0", + "typescript": "4.4.2" + } +} diff --git a/rush-plugins/rush-metadata-plugin/rush-plugin-manifest.json b/rush-plugins/rush-metadata-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000..616133b0 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/rush-plugin-manifest.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", + "plugins": [ + { + "pluginName": "rush-metadata-plugin", + "description": "A tool for managing metadata files", + "commandLineJsonFilePath": "command-line.json", + "optionsSchema": "lib/schemas/plugin-options.schema.json" + } + ] +} diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts new file mode 100644 index 00000000..6f30d50a --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -0,0 +1 @@ +console.log('hello!'); \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/tsconfig.json b/rush-plugins/rush-metadata-plugin/tsconfig.json new file mode 100644 index 00000000..82917d24 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/tsconfig.json @@ -0,0 +1,97 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + /* Projects */ + // "incremental": true, /* Enable incremental compilation */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "es5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "lib": [ + "ES2015" + ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ + // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + /* Modules */ + "module": "commonjs" /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + "types": [ + "heft-jest", + "node" + ] /* Specify type package names to be included without being referenced in a source file. */, + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "resolveJsonModule": true, /* Enable importing .json files */ + // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + /* Emit */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "declarationMap": true /* Create sourcemaps for d.ts files. */, + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true /* Create source map files for emitted JavaScript files. */, + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./lib" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + "downlevelIteration": true /* Emit more compliant, but verbose and less performant JavaScript for iteration. */, + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ + // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/rush.json b/rush.json index 3b674985..11f86c92 100644 --- a/rush.json +++ b/rush.json @@ -496,6 +496,11 @@ "packageName": "rush-git-lfs-plugin", "projectFolder": "rush-plugins/rush-git-lfs-plugin", "shouldPublish": true + }, + { + "packageName": "rush-metadata-plugin", + "projectFolder": "rush-plugins/rush-metadata-plugin", + "shouldPublish": true } ] } From 8b5291206931fa54803692b7479a84bb23be9e84 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 25 May 2023 14:39:38 -0700 Subject: [PATCH 02/20] chore: set up receiving arguments in archiver --- .../rush-metadata-plugin/command-line.json | 2 +- common/config/rush/pnpm-lock.yaml | 4 +++ .../rush-metadata-plugin/command-line.json | 2 +- .../rush-metadata-plugin/package.json | 7 +++- .../rush-metadata-plugin/src/fillMeta.ts | 3 ++ .../rush-metadata-plugin/src/index.ts | 34 ++++++++++++++++++- .../rush-metadata-plugin/tsconfig.json | 6 ++-- 7 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/src/fillMeta.ts diff --git a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json index 48b48603..22b97289 100644 --- a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json +++ b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json @@ -5,7 +5,7 @@ "name": "meta", "commandKind": "global", "summary": "Create or update metadata information in the current package.", - "shellCommand": "node /lib/bin.js", + "shellCommand": "node /lib/index.js meta", "safeForSimultaneousRushProcesses": true } ], diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 227285a1..9a6890d8 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -222,15 +222,18 @@ importers: '@rushstack/rush-sdk': 5.62.4 '@types/heft-jest': 1.0.1 '@types/node': 12.20.24 + '@types/yargs': ~17.0.7 commander: ~9.4.0 eslint: 7.32.0 ignore: 5.1.9 typescript: 4.4.2 + yargs: ~17.3.0 dependencies: '@rushstack/node-core-library': 3.44.1 '@rushstack/rush-sdk': 5.62.4 commander: 9.4.0 ignore: 5.1.9 + yargs: 17.3.0 devDependencies: '@rushstack/eslint-config': 2.4.5_eslint@7.32.0+typescript@4.4.2 '@rushstack/heft': 0.43.2 @@ -238,6 +241,7 @@ importers: '@rushstack/heft-node-rig': 1.2.31_@rushstack+heft@0.43.2 '@types/heft-jest': 1.0.1 '@types/node': 12.20.24 + '@types/yargs': 17.0.7 eslint: 7.32.0 typescript: 4.4.2 diff --git a/rush-plugins/rush-metadata-plugin/command-line.json b/rush-plugins/rush-metadata-plugin/command-line.json index 48b48603..22b97289 100644 --- a/rush-plugins/rush-metadata-plugin/command-line.json +++ b/rush-plugins/rush-metadata-plugin/command-line.json @@ -5,7 +5,7 @@ "name": "meta", "commandKind": "global", "summary": "Create or update metadata information in the current package.", - "shellCommand": "node /lib/bin.js", + "shellCommand": "node /lib/index.js meta", "safeForSimultaneousRushProcesses": true } ], diff --git a/rush-plugins/rush-metadata-plugin/package.json b/rush-plugins/rush-metadata-plugin/package.json index 864010a6..e1d41fef 100644 --- a/rush-plugins/rush-metadata-plugin/package.json +++ b/rush-plugins/rush-metadata-plugin/package.json @@ -18,6 +18,9 @@ "author": "william2958", "main": "lib/index.js", "typings": "lib/index.d.ts", + "bin": { + "rush-metadata-plugin": "lib/index.js" + }, "scripts": { "build": "heft build --clean", "build:watch": "heft build --watch", @@ -28,7 +31,8 @@ "@rushstack/node-core-library": "3.44.1", "@rushstack/rush-sdk": "5.62.4", "commander": "~9.4.0", - "ignore": "5.1.9" + "ignore": "5.1.9", + "yargs": "~17.3.0" }, "devDependencies": { "@rushstack/eslint-config": "2.4.5", @@ -37,6 +41,7 @@ "@rushstack/heft-node-rig": "1.2.31", "@types/heft-jest": "1.0.1", "@types/node": "12.20.24", + "@types/yargs": "~17.0.7", "eslint": "7.32.0", "typescript": "4.4.2" } diff --git a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts b/rush-plugins/rush-metadata-plugin/src/fillMeta.ts new file mode 100644 index 00000000..51aac23f --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/fillMeta.ts @@ -0,0 +1,3 @@ +export const fillMeta = async ({project}: {project: string}): Promise => { + console.log('creating metadata for project: ', project); +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts index 6f30d50a..465b2556 100644 --- a/rush-plugins/rush-metadata-plugin/src/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -1 +1,33 @@ -console.log('hello!'); \ No newline at end of file +#!/usr/bin/env node +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +main(); + +async function main(): Promise { + await yargs(hideBin(process.argv)) + .command( + "meta", + "Add metadata to a project", + (yargs) => { + return yargs + .option("package", { + type: "string", + describe: "The name of the package to archive", + }) + .demandOption(["project"]); + }, + async (argv) => { + const { fillMeta } = await import("./fillMeta"); + try { + await fillMeta(argv); + } catch (e: any) { + console.error('error: ', e); + process.exit(1); + } + } + ) + .demandCommand(1, "You need at least one command before moving on") + .parse(); +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/tsconfig.json b/rush-plugins/rush-metadata-plugin/tsconfig.json index 82917d24..c5ba5d71 100644 --- a/rush-plugins/rush-metadata-plugin/tsconfig.json +++ b/rush-plugins/rush-metadata-plugin/tsconfig.json @@ -10,9 +10,9 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ "target": "es5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - "lib": [ - "ES2015" - ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, + // "lib": [ + // "ES2015" + // ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ From 6ee3e13713b9edcf0a7ea8d19d59b48d5bca8af7 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 25 May 2023 15:23:12 -0700 Subject: [PATCH 03/20] chore: accept metadata file path --- .../rush-plugins/rush-metadata-plugin.json | 3 + .../rush-metadata-plugin/src/fillMeta.ts | 26 +++++++ .../rush-metadata-plugin/src/index.ts | 2 +- .../src/logic/generateMD.ts | 26 +++++++ .../rush-metadata-plugin/src/logic/git.ts | 78 +++++++++++++++++++ .../src/logic/graveyard.ts | 32 ++++++++ .../src/logic/projectMetadata.ts | 36 +++++++++ .../src/logic/promptRushUpdate.ts | 46 +++++++++++ .../src/logic/rushConfiguration.ts | 24 ++++++ 9 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 common/config/rush-plugins/rush-metadata-plugin.json create mode 100644 rush-plugins/rush-metadata-plugin/src/logic/generateMD.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/logic/git.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/logic/graveyard.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/logic/projectMetadata.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/logic/promptRushUpdate.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/logic/rushConfiguration.ts diff --git a/common/config/rush-plugins/rush-metadata-plugin.json b/common/config/rush-plugins/rush-metadata-plugin.json new file mode 100644 index 00000000..5bbf8d33 --- /dev/null +++ b/common/config/rush-plugins/rush-metadata-plugin.json @@ -0,0 +1,3 @@ +{ + "metadataFileName": "project-metadata.json" +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts b/rush-plugins/rush-metadata-plugin/src/fillMeta.ts index 51aac23f..9b3fcbfa 100644 --- a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/fillMeta.ts @@ -1,3 +1,29 @@ +import fs from 'fs'; +import path from 'path'; + +import { RushConfiguration, RushConfigurationProject } from "@rushstack/rush-sdk"; +import { loadRushConfiguration } from "./logic/rushConfiguration"; +import { log } from 'console'; + +const META_FILE_NAME: string = 'project-metadata.json'; + export const fillMeta = async ({project}: {project: string}): Promise => { console.log('creating metadata for project: ', project); + const rushConfiguration: RushConfiguration = loadRushConfiguration(); + const rushProject: RushConfigurationProject | undefined = + rushConfiguration.getProjectByName(project); + if (!rushProject) { + throw new Error(`Could not find project with package name ${project}`); + } + const { projectFolder, projectRelativeFolder } = rushProject; + console.log('projecrt folder: ', projectFolder, projectRelativeFolder); + + // Check if metadata file exists already + const metaFilePath: string = path.join(projectFolder, META_FILE_NAME); + console.log('Checking metadata file: ', metaFilePath); + if (fs.existsSync(metaFilePath)) { + log('file exists at location: ', metaFilePath); + } else { + log('Creating metadata file at location: ', metaFilePath); + } } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts index 465b2556..65b1bb7e 100644 --- a/rush-plugins/rush-metadata-plugin/src/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -12,7 +12,7 @@ async function main(): Promise { "Add metadata to a project", (yargs) => { return yargs - .option("package", { + .option("project", { type: "string", describe: "The name of the package to archive", }) diff --git a/rush-plugins/rush-metadata-plugin/src/logic/generateMD.ts b/rush-plugins/rush-metadata-plugin/src/logic/generateMD.ts new file mode 100644 index 00000000..fb3a861e --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/logic/generateMD.ts @@ -0,0 +1,26 @@ +import json2md from 'json2md'; +import { IProjectCheckpointMetadata } from './projectMetadata'; + +// Generate a md file with the corresponding json content for archived projects +export const convertProjectMetadataToMD = (archivedProjectMetadataObject: { [key in string]: IProjectCheckpointMetadata }): string => { +// Try saving a md file of this json file + const mdFileContents: any = [ + { h2: "Archived Projects" } + ]; + const tableRows: any = [] + for (const [projectName, projectMetadata] of Object.entries(archivedProjectMetadataObject)) { + tableRows.push([ + projectName, + projectMetadata.checkpointBranch, + projectMetadata.description, + projectMetadata.archivedOn + ]) + } + mdFileContents.push({ + table: { + headers: ["Project Name", "Checkpoint Branch", "Description", "Archive Date"], + rows: tableRows + } + }) + return json2md(mdFileContents); +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/logic/git.ts b/rush-plugins/rush-metadata-plugin/src/logic/git.ts new file mode 100644 index 00000000..082fdd54 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/logic/git.ts @@ -0,0 +1,78 @@ +import { Executable } from "@rushstack/node-core-library"; + +import type { SpawnSyncReturns } from "child_process"; + +let checkedGitPath: boolean = false; +let gitPath: string | undefined; + +export const getGitPath = (): string | undefined => { + if (!checkedGitPath) { + checkedGitPath = true; + gitPath = Executable.tryResolve("git"); + } + return gitPath; +}; + +export const getGitPathOrThrow = (): string => { + const gitPath: string | undefined = getGitPath(); + if (!gitPath) { + throw new Error("Unable to find git."); + } + return gitPath; +}; + +export const gitFullClean = (cwd: string): void => { + const gitPath: string = getGitPathOrThrow(); + Executable.spawnSync(gitPath, ["clean", "-fdx"], { + currentWorkingDirectory: cwd, + }); +}; + +export const gitCheckIgnored = (cwd: string, filePath: string): string => { + const gitPath: string = getGitPathOrThrow(); + let result: string = ""; + try { + const process: SpawnSyncReturns = Executable.spawnSync( + gitPath, + ["check-ignore", "-v", filePath], + { + currentWorkingDirectory: cwd, + } + ); + if (process.status === 0) { + result = process.stdout.toString(); + } + } catch (e: any) { + if (e.message.includes("The command failed with exit code 1")) { + // ignore + } else { + // rethrow + throw e; + } + } + return result; +}; + +export const getCheckpointBranch = (cwd: string, branchName: string): string => { + const gitPath: string = getGitPathOrThrow(); + const currDate: string = new Date().toISOString().substring(0, 10); + const branchNameToCreate: string = `${branchName}-checkpoint-${currDate}`; + const process: SpawnSyncReturns = Executable.spawnSync(gitPath, ["branch", branchNameToCreate], { + currentWorkingDirectory: cwd, + }); + if (process.status === 128) { + throw new Error(`The passed branch name is invalid: ${branchNameToCreate}`) + } + + return branchNameToCreate; +} + +export const pushGitBranch = (cwd: string, branchName: string): void => { + const gitPath: string = getGitPathOrThrow(); + const process: SpawnSyncReturns = Executable.spawnSync(gitPath, ["push", "origin", `${branchName}:${branchName}`], { + currentWorkingDirectory: cwd, + }); + if (process.status !== 0) { + console.error('Could not push branch to origin'); + } +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/logic/graveyard.ts b/rush-plugins/rush-metadata-plugin/src/logic/graveyard.ts new file mode 100644 index 00000000..bec08a0a --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/logic/graveyard.ts @@ -0,0 +1,32 @@ +import * as path from "path"; +import { PackageName, IParsedPackageName } from "@rushstack/node-core-library"; + +export const graveyardRelativeFolder: string = "common/_graveyard"; + +export interface IGraveyardInfo { + tarballFolder: string; + tarballRelativeFolder: string; + tarballName: string; +} + +export interface IGetGraveyardInfoParams { + monoRoot: string; + packageName: string; +} + +export const getGraveyardInfo = ({ + monoRoot, + packageName, +}: IGetGraveyardInfoParams): IGraveyardInfo => { + const parsedPackageName: IParsedPackageName = PackageName.parse(packageName); + const tarballRelativeFolder: string = path.join( + graveyardRelativeFolder, + parsedPackageName.scope + ); + const tarballFolder: string = path.join(monoRoot, tarballRelativeFolder); + return { + tarballFolder, + tarballRelativeFolder, + tarballName: `${parsedPackageName.unscopedName}.tar.gz`, + }; +}; diff --git a/rush-plugins/rush-metadata-plugin/src/logic/projectMetadata.ts b/rush-plugins/rush-metadata-plugin/src/logic/projectMetadata.ts new file mode 100644 index 00000000..4d6f927d --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/logic/projectMetadata.ts @@ -0,0 +1,36 @@ +import { JsonObject, JsonFile } from "@rushstack/node-core-library"; +export class ProjectMetadata { + private _projectConfig: JsonObject; + public static FILENAME: string = "rush-project-metadata.json"; + + public constructor(projectConfig: JsonObject) { + this._projectConfig = projectConfig; + } + + public static load(filePath: string): ProjectMetadata { + const { projectConfig } = JsonFile.load(filePath); + return new ProjectMetadata(projectConfig); + } + + public save(filePath: string): void { + JsonFile.save( + { + projectConfig: this._projectConfig, + }, + filePath, + { + ensureFolderExists: true, + } + ); + } + + public get projectConfig(): JsonObject { + return this._projectConfig; + } +} + +export interface IProjectCheckpointMetadata { + checkpointBranch: string; + archivedOn: string; + description: string; +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/logic/promptRushUpdate.ts b/rush-plugins/rush-metadata-plugin/src/logic/promptRushUpdate.ts new file mode 100644 index 00000000..8aa36e1e --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/logic/promptRushUpdate.ts @@ -0,0 +1,46 @@ +import type { RushConfiguration } from "@rushstack/rush-sdk"; +import type { ITerminal } from "@rushstack/node-core-library"; +import { Executable } from "@rushstack/node-core-library"; +import * as path from "path"; +import { loadRushConfiguration } from "./rushConfiguration"; + +export interface IPromptRushUpdateParams { + terminal: ITerminal; +} + +export async function promptRushUpdate({ + terminal, +}: IPromptRushUpdateParams): Promise { + const { prompt } = await import("inquirer"); + interface IShouldRunRushUpdateAnswer { + shouldRunRushUpdate: boolean; + } + const rushConfiguration: RushConfiguration = loadRushConfiguration(); + const runRushJSPath: string = path.join( + rushConfiguration.commonScriptsFolder, + "install-run-rush.js" + ); + + const { shouldRunRushUpdate } = await prompt([ + { + type: "confirm", + name: "shouldRunRushUpdate", + message: "Run rush update right now?", + default: true, + }, + ]); + + if (shouldRunRushUpdate) { + terminal.writeLine("Run rush update..."); + try { + Executable.spawnSync("node", [runRushJSPath, "update"], { + stdio: "inherit", + }); + } catch (e) { + terminal.writeErrorLine("Rush update failed, please run it manually."); + } + terminal.writeLine("Rush update successfully."); + } else { + terminal.writeWarningLine("Rush update skipped, please run it manually."); + } +} diff --git a/rush-plugins/rush-metadata-plugin/src/logic/rushConfiguration.ts b/rush-plugins/rush-metadata-plugin/src/logic/rushConfiguration.ts new file mode 100644 index 00000000..f8e86e12 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/logic/rushConfiguration.ts @@ -0,0 +1,24 @@ +import { RushConfiguration } from "@rushstack/rush-sdk"; + +const cwd2rushConfiguration: Record = {}; + +export const loadRushConfiguration = ( + cwd: string = process.cwd() +): RushConfiguration => { + let rushConfiguration: RushConfiguration | undefined = + cwd2rushConfiguration[cwd]; + if (!rushConfiguration) { + try { + rushConfiguration = RushConfiguration.loadFromDefaultLocation({ + startingFolder: cwd, + }); + } catch { + // no-catch + } + if (!rushConfiguration) { + throw new Error("Could not load rush configuration"); + } + cwd2rushConfiguration[cwd] = rushConfiguration; + } + return rushConfiguration; +}; From 8563f142cfa47899b7bb7e9f80cbfd1390251dc8 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 25 May 2023 16:27:01 -0700 Subject: [PATCH 04/20] feat: prompt for basic answers to populate metdata file --- common/config/rush/pnpm-lock.yaml | 18 ++++- .../rush-metadata-plugin/package.json | 9 ++- .../rush-metadata-plugin/src/fillMeta.ts | 67 ++++++++++++++++++- .../rush-metadata-plugin/src/template.ts | 7 ++ 4 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/src/template.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 9a6890d8..2503ddd0 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -221,18 +221,26 @@ importers: '@rushstack/node-core-library': 3.44.1 '@rushstack/rush-sdk': 5.62.4 '@types/heft-jest': 1.0.1 + '@types/inquirer': ~8.1.3 + '@types/json2md': ~1.5.1 '@types/node': 12.20.24 '@types/yargs': ~17.0.7 + chalk: 4.1.2 commander: ~9.4.0 eslint: 7.32.0 ignore: 5.1.9 + inquirer: ~8.2.0 + json2md: ~2.0.0 typescript: 4.4.2 yargs: ~17.3.0 dependencies: '@rushstack/node-core-library': 3.44.1 '@rushstack/rush-sdk': 5.62.4 + chalk: 4.1.2 commander: 9.4.0 ignore: 5.1.9 + inquirer: 8.2.0 + json2md: 2.0.0 yargs: 17.3.0 devDependencies: '@rushstack/eslint-config': 2.4.5_eslint@7.32.0+typescript@4.4.2 @@ -240,6 +248,8 @@ importers: '@rushstack/heft-jest-plugin': 0.1.53_@rushstack+heft@0.43.2 '@rushstack/heft-node-rig': 1.2.31_@rushstack+heft@0.43.2 '@types/heft-jest': 1.0.1 + '@types/inquirer': 8.1.3 + '@types/json2md': 1.5.1 '@types/node': 12.20.24 '@types/yargs': 17.0.7 eslint: 7.32.0 @@ -4376,7 +4386,7 @@ packages: mute-stream: 0.0.8 ora: 5.4.1 run-async: 2.4.1 - rxjs: 7.4.0 + rxjs: 7.5.2 string-width: 4.2.3 strip-ansi: 6.0.1 through: 2.3.8 @@ -6200,7 +6210,7 @@ packages: dev: false /os-tmpdir/1.0.2: - resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=} + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} dev: false @@ -6783,6 +6793,7 @@ packages: resolution: {integrity: sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==} dependencies: tslib: 2.1.0 + dev: true /rxjs/7.5.2: resolution: {integrity: sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==} @@ -7365,7 +7376,7 @@ packages: dev: true /through/2.3.8: - resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=} + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: false /through2/2.0.5: @@ -7501,6 +7512,7 @@ packages: /tslib/2.1.0: resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==} + dev: true /tslib/2.3.1: resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} diff --git a/rush-plugins/rush-metadata-plugin/package.json b/rush-plugins/rush-metadata-plugin/package.json index e1d41fef..298110ca 100644 --- a/rush-plugins/rush-metadata-plugin/package.json +++ b/rush-plugins/rush-metadata-plugin/package.json @@ -32,7 +32,10 @@ "@rushstack/rush-sdk": "5.62.4", "commander": "~9.4.0", "ignore": "5.1.9", - "yargs": "~17.3.0" + "yargs": "~17.3.0", + "inquirer": "~8.2.0", + "chalk": "4.1.2", + "json2md": "~2.0.0" }, "devDependencies": { "@rushstack/eslint-config": "2.4.5", @@ -43,6 +46,8 @@ "@types/node": "12.20.24", "@types/yargs": "~17.0.7", "eslint": "7.32.0", - "typescript": "4.4.2" + "typescript": "4.4.2", + "@types/inquirer": "~8.1.3", + "@types/json2md": "~1.5.1" } } diff --git a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts b/rush-plugins/rush-metadata-plugin/src/fillMeta.ts index 9b3fcbfa..a17853a2 100644 --- a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/fillMeta.ts @@ -4,11 +4,15 @@ import path from 'path'; import { RushConfiguration, RushConfigurationProject } from "@rushstack/rush-sdk"; import { loadRushConfiguration } from "./logic/rushConfiguration"; import { log } from 'console'; +import { JsonFile } from '@rushstack/node-core-library'; +import { ICoreMetadata } from './template'; +import chalk from 'chalk'; +import inquirer from 'inquirer'; const META_FILE_NAME: string = 'project-metadata.json'; export const fillMeta = async ({project}: {project: string}): Promise => { - console.log('creating metadata for project: ', project); + log('creating metadata for project: ', project); const rushConfiguration: RushConfiguration = loadRushConfiguration(); const rushProject: RushConfigurationProject | undefined = rushConfiguration.getProjectByName(project); @@ -16,14 +20,71 @@ export const fillMeta = async ({project}: {project: string}): Promise => { throw new Error(`Could not find project with package name ${project}`); } const { projectFolder, projectRelativeFolder } = rushProject; - console.log('projecrt folder: ', projectFolder, projectRelativeFolder); + log('projecrt folder: ', projectFolder, projectRelativeFolder); // Check if metadata file exists already const metaFilePath: string = path.join(projectFolder, META_FILE_NAME); - console.log('Checking metadata file: ', metaFilePath); + log('Checking metadata file: ', metaFilePath); if (fs.existsSync(metaFilePath)) { log('file exists at location: ', metaFilePath); + + // Read and parse the file + const loadedJsonFile: ICoreMetadata = JsonFile.load(metaFilePath); + + log('Loaded metadata file: ', loadedJsonFile.purpose); + } else { log('Creating metadata file at location: ', metaFilePath); + log(chalk.green(`Creating a new metadata file for project: ${project}...`)); + const answers: any = await inquirer.prompt([ + { + type: 'input', + name: 'purpose', + message: 'What is the purpose of this package?' + }, + { + type: 'input', + name: 'pointOfContact', + message: 'Who are the POCs of this package? (Separated by ",")' + }, + { + type: 'input', + name: 'projectGroup', + message: 'What is the project group for this package?' + }, + { + type: 'list', + name: 'targetRuntime', + choices: ['node', 'browser', 'cli'], + message: 'What is the target runtime?' + }, + { + type: 'list', + name: 'riskLevel', + choices: [ + '0 - Low Risk (Internal Library)', + '1 - Medium Risk (External Library)', + '2 - High Risk (Production Application)' + ], + message: 'What is the Risk Level of this package?' + } + ]); + console.log('The answers entered are: ', answers); + const {confirmAnswers}: any = await inquirer.prompt([ + { + type: 'confirm', + name: 'confirmAnswers', + message: 'Do you want to save the above answers to the metadata file?' + } + ]) + if (!confirmAnswers) { + console.log('Discarding answers...'); + return; + } + // const metadataContents: ICoreMetadata = { + // // description: 'test description', + // // pointOfContact: '' + // }; + // JsonFile.save(metadataContents, metaFilePath); } } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/template.ts b/rush-plugins/rush-metadata-plugin/src/template.ts new file mode 100644 index 00000000..9586664a --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/template.ts @@ -0,0 +1,7 @@ +export interface ICoreMetadata { + pointOfContact: string[] | string; + purpose: string; + projectGroup: string; + targetRuntime: string; + riskLevel: number; +} \ No newline at end of file From d52a55f34ee1a50eb492dc55beed2f5753f4b1f7 Mon Sep 17 00:00:00 2001 From: William Huang Date: Tue, 30 May 2023 13:11:54 -0700 Subject: [PATCH 05/20] feat: load custom metadata information from configuration file --- .../rush-plugins/rush-metadata-plugin.json | 2 +- .../rush-metadata-plugin/src/fillMeta.ts | 17 ++++--- .../src/logic/customMeta.ts | 48 +++++++++++++++++++ 3 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts diff --git a/common/config/rush-plugins/rush-metadata-plugin.json b/common/config/rush-plugins/rush-metadata-plugin.json index 5bbf8d33..4f6ea3d9 100644 --- a/common/config/rush-plugins/rush-metadata-plugin.json +++ b/common/config/rush-plugins/rush-metadata-plugin.json @@ -1,3 +1,3 @@ { - "metadataFileName": "project-metadata.json" + "metadataFileName": "project-metadata222.json" } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts b/rush-plugins/rush-metadata-plugin/src/fillMeta.ts index a17853a2..7b005b7c 100644 --- a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/fillMeta.ts @@ -8,23 +8,28 @@ import { JsonFile } from '@rushstack/node-core-library'; import { ICoreMetadata } from './template'; import chalk from 'chalk'; import inquirer from 'inquirer'; - -const META_FILE_NAME: string = 'project-metadata.json'; +import { getCustomMetadataInfo } from './logic/customMeta'; export const fillMeta = async ({project}: {project: string}): Promise => { - log('creating metadata for project: ', project); + const rushConfiguration: RushConfiguration = loadRushConfiguration(); + const monoRoot: string = rushConfiguration.rushJsonFolder; const rushProject: RushConfigurationProject | undefined = rushConfiguration.getProjectByName(project); if (!rushProject) { throw new Error(`Could not find project with package name ${project}`); } const { projectFolder, projectRelativeFolder } = rushProject; - log('projecrt folder: ', projectFolder, projectRelativeFolder); + + // Look for custom plugin configurations + const { metadataFileName, fields } = getCustomMetadataInfo({ + monoRoot, + packageName: project + }) // Check if metadata file exists already - const metaFilePath: string = path.join(projectFolder, META_FILE_NAME); - log('Checking metadata file: ', metaFilePath); + const metaFilePath: string = path.join(projectFolder, metadataFileName); + if (fs.existsSync(metaFilePath)) { log('file exists at location: ', metaFilePath); diff --git a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts new file mode 100644 index 00000000..6a4374fc --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts @@ -0,0 +1,48 @@ +import { RushConfiguration } from "@rushstack/rush-sdk"; +import path from 'path'; + +import { loadRushConfiguration } from "./rushConfiguration"; +import { JsonFile, FileSystem } from "@rushstack/node-core-library"; + +export const defaultMetadataRelativeFolder: string = 'incorrect-package-meta.json'; + +export interface IGetCustomMetadataInfoParams { + monoRoot: string; + packageName: string; +} + +export interface ICustomMetadata { + metadataFileName: string; + fields: any[]; +} + +export const getCustomMetadataInfo = ({ monoRoot, packageName }: IGetCustomMetadataInfoParams): ICustomMetadata => { + + const rushConfiguration: RushConfiguration = loadRushConfiguration(); + + const pluginOptionsJsonFilePath: string = path.join( + rushConfiguration.rushPluginOptionsFolder, + 'rush-metadata-plugin.json' + ) + + let metadataRelativeFolder: string = defaultMetadataRelativeFolder; + let metaConfigs: ICustomMetadata | undefined; + try { + metaConfigs = JsonFile.load(pluginOptionsJsonFilePath); + } catch (e) { + if (!FileSystem.isNotExistError(e as Error)) { + throw e; + } + } + + if (metaConfigs?.metadataFileName) { + metadataRelativeFolder = metaConfigs.metadataFileName; + } + + console.log('custom metadata folder: ', metadataRelativeFolder); + + return { + metadataFileName: metadataRelativeFolder, + fields: [] + } +} \ No newline at end of file From 0116a7590c90ad7c73fd8d588e9d41022c501263 Mon Sep 17 00:00:00 2001 From: William Huang Date: Tue, 30 May 2023 13:19:39 -0700 Subject: [PATCH 06/20] chore: change file --- .../will-rush-metadata-plugin_2023-05-30-20-19.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/rush-metadata-plugin/will-rush-metadata-plugin_2023-05-30-20-19.json diff --git a/common/changes/rush-metadata-plugin/will-rush-metadata-plugin_2023-05-30-20-19.json b/common/changes/rush-metadata-plugin/will-rush-metadata-plugin_2023-05-30-20-19.json new file mode 100644 index 00000000..c6500b6e --- /dev/null +++ b/common/changes/rush-metadata-plugin/will-rush-metadata-plugin_2023-05-30-20-19.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "rush-metadata-plugin", + "comment": "Add the basic functionality to generate and update metadata files in rush projects", + "type": "minor" + } + ], + "packageName": "rush-metadata-plugin" +} \ No newline at end of file From d654e1d7906220483f6109320947032fc61edb3a Mon Sep 17 00:00:00 2001 From: William Huang Date: Wed, 31 May 2023 16:57:40 -0700 Subject: [PATCH 07/20] feat: add default fields in seperate json --- .../rush-plugins/rush-metadata-plugin.json | 58 ++++++++++++++++++- .../customMetaSchema.json | 15 +++++ .../src/defaultMetadataFields.json | 43 ++++++++++++++ .../src/generateSchema.ts | 10 ++++ .../rush-metadata-plugin/src/index.ts | 4 +- .../src/{fillMeta.ts => initMeta/index.ts} | 14 +++-- .../src/logic/customMeta.ts | 26 +++++---- .../rush-metadata-plugin/src/syncMeta.ts | 6 ++ .../src/types/metadataField.ts | 7 +++ .../src/types/pluginConfig.ts | 8 +++ 10 files changed, 173 insertions(+), 18 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/customMetaSchema.json create mode 100644 rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json create mode 100644 rush-plugins/rush-metadata-plugin/src/generateSchema.ts rename rush-plugins/rush-metadata-plugin/src/{fillMeta.ts => initMeta/index.ts} (86%) create mode 100644 rush-plugins/rush-metadata-plugin/src/syncMeta.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/types/metadataField.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts diff --git a/common/config/rush-plugins/rush-metadata-plugin.json b/common/config/rush-plugins/rush-metadata-plugin.json index 4f6ea3d9..4f5daf40 100644 --- a/common/config/rush-plugins/rush-metadata-plugin.json +++ b/common/config/rush-plugins/rush-metadata-plugin.json @@ -1,3 +1,59 @@ +/** + +Custom field parameters: + +field: name +desc: name of the metadata field +type: string + +field: description +desc: description of the metdata field +type: string + +field: type +desc: what kind of data is it +types: +- string +- number +- string[] +- number[] + +field: isSelect +desc: whether this field will be a rush selector or not (passed into "--to" parameter, etc) +type: boolean +(note that this cannot be used for any type other than string) + +field: required +desc: whether or not this field is required +type: boolean + +*/ + + { - "metadataFileName": "project-metadata222.json" + "$schema": "../../../rush-plugins/rush-metadata-plugin/customMetaSchema.json", + "metadataFileName": "project-metadata222.json", + "fields": [ + { + "name": "pointOfContacts", + "description": "POC for a specific package, synced to CODEOWNERS", + "type": "string[]", + "isSelect": false, + "required": true + }, + { + "name": "productLine", + "description": "Which product line this project belongs to", + "type": "string", + "isSelect": true, + "required": true + }, + { + "name": "environmentVars", + "description": "A list of environment variables used in this package", + "type": "string[]", + "isSelect": false, + "required": false + } + ] } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/customMetaSchema.json b/rush-plugins/rush-metadata-plugin/customMetaSchema.json new file mode 100644 index 00000000..3277fa2a --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/customMetaSchema.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "title": "CustomMetadata", + "properties": { + "metadataFileName": { + "type": "string", + "description": "The metadata file name to be saved and edited." + }, + "fields": { + "type": "array", + "description": "" + } + }, + "required": ["metadataFileName", "fields"] +} diff --git a/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json b/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json new file mode 100644 index 00000000..8ec96d9d --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json @@ -0,0 +1,43 @@ +{ + "fields": [ + { + "name": "purpose", + "description": "The purpose of the package.", + "prompt": "What is the purpose of this package?", + "type": "string", + "required": true + }, + { + "name": "pointOfContact", + "description": "The list of package owners.", + "prompt": "Who are the POCs of this package? (Separated by ',')", + "type": "string", + "required": true + }, + { + "name": "projectGroup", + "description": "Which project group does this package belong to?", + "prompt": "What is the project group for this package?", + "type": "string", + "required": false + }, + { + "name": "targetRuntime", + "description": "The environment in which the project will run.", + "prompt": "What is the target runtime?", + "type": "list", + "choices": ["node", "browser", "cli"] + }, + { + "name": "riskLevel", + "description": "The risk level assigned to this package.", + "prompt": "What is the risk level of this package?", + "type": "list", + "choices": [ + { "label": "0 - Low Risk (Internal Library)", "value": 0 }, + { "label": "1 - Medium Risk (External Library)", "value": 1 }, + { "label": "2 - High Risk (Production Application)", "value": 2 } + ] + } + ] +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/generateSchema.ts b/rush-plugins/rush-metadata-plugin/src/generateSchema.ts new file mode 100644 index 00000000..c8d79ac3 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/generateSchema.ts @@ -0,0 +1,10 @@ +import { ICustomMetadataField } from "./types/metadataField"; + + +// This function takes in the custom fields defined in a specific monorepo and generates +// a JSON schema that can be uploaded to a custom server and referenced in the metadata files. +export const generateSchema = (fields: ICustomMetadataField): void => { + + console.log('generating schema...'); + +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts index 65b1bb7e..dc292772 100644 --- a/rush-plugins/rush-metadata-plugin/src/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -19,9 +19,9 @@ async function main(): Promise { .demandOption(["project"]); }, async (argv) => { - const { fillMeta } = await import("./fillMeta"); + const { initMeta } = await import("./initMeta"); try { - await fillMeta(argv); + await initMeta(argv); } catch (e: any) { console.error('error: ', e); process.exit(1); diff --git a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts similarity index 86% rename from rush-plugins/rush-metadata-plugin/src/fillMeta.ts rename to rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index 7b005b7c..d66a3577 100644 --- a/rush-plugins/rush-metadata-plugin/src/fillMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -2,15 +2,15 @@ import fs from 'fs'; import path from 'path'; import { RushConfiguration, RushConfigurationProject } from "@rushstack/rush-sdk"; -import { loadRushConfiguration } from "./logic/rushConfiguration"; +import { loadRushConfiguration } from "../logic/rushConfiguration"; import { log } from 'console'; import { JsonFile } from '@rushstack/node-core-library'; -import { ICoreMetadata } from './template'; +import { ICoreMetadata } from '../template'; import chalk from 'chalk'; import inquirer from 'inquirer'; -import { getCustomMetadataInfo } from './logic/customMeta'; +import { getCustomMetadataInfo } from '../logic/customMeta'; -export const fillMeta = async ({project}: {project: string}): Promise => { +export const initMeta = async ({project}: {project: string}): Promise => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); const monoRoot: string = rushConfiguration.rushJsonFolder; @@ -21,12 +21,18 @@ export const fillMeta = async ({project}: {project: string}): Promise => { } const { projectFolder, projectRelativeFolder } = rushProject; + // Load default fields + const { fields: defaultFields } = JsonFile.load(path.relative("../defaultMetadataFields.json")); + log('Default fields: ', defaultFields); + // Look for custom plugin configurations const { metadataFileName, fields } = getCustomMetadataInfo({ monoRoot, packageName: project }) + console.log('fields', fields) + // Check if metadata file exists already const metaFilePath: string = path.join(projectFolder, metadataFileName); diff --git a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts index 6a4374fc..1aad52b0 100644 --- a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts @@ -3,6 +3,8 @@ import path from 'path'; import { loadRushConfiguration } from "./rushConfiguration"; import { JsonFile, FileSystem } from "@rushstack/node-core-library"; +import { ICustomMetadataField } from "../types/metadataField"; +import { IPluginConfig } from "../types/pluginConfig"; export const defaultMetadataRelativeFolder: string = 'incorrect-package-meta.json'; @@ -11,12 +13,7 @@ export interface IGetCustomMetadataInfoParams { packageName: string; } -export interface ICustomMetadata { - metadataFileName: string; - fields: any[]; -} - -export const getCustomMetadataInfo = ({ monoRoot, packageName }: IGetCustomMetadataInfoParams): ICustomMetadata => { +export const getCustomMetadataInfo = ({ monoRoot, packageName }: IGetCustomMetadataInfoParams): IPluginConfig => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); @@ -25,8 +22,11 @@ export const getCustomMetadataInfo = ({ monoRoot, packageName }: IGetCustomMetad 'rush-metadata-plugin.json' ) + // Custom configurations for plugin let metadataRelativeFolder: string = defaultMetadataRelativeFolder; - let metaConfigs: ICustomMetadata | undefined; + let customFields: ICustomMetadataField[] = []; + + let metaConfigs: IPluginConfig | undefined; try { metaConfigs = JsonFile.load(pluginOptionsJsonFilePath); } catch (e) { @@ -34,15 +34,19 @@ export const getCustomMetadataInfo = ({ monoRoot, packageName }: IGetCustomMetad throw e; } } - - if (metaConfigs?.metadataFileName) { - metadataRelativeFolder = metaConfigs.metadataFileName; + if (metaConfigs) { + if (metaConfigs.metadataFileName) { + metadataRelativeFolder = metaConfigs.metadataFileName; + } + if (metaConfigs.fields) { + customFields = metaConfigs.fields; + } } console.log('custom metadata folder: ', metadataRelativeFolder); return { metadataFileName: metadataRelativeFolder, - fields: [] + fields: customFields } } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta.ts new file mode 100644 index 00000000..f3597aca --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta.ts @@ -0,0 +1,6 @@ +// Run by the "rush meta sync" command +// Syncs all the metadata files in a monorepo according to the metadata spec. + +export const syncMeta = () => { + +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts new file mode 100644 index 00000000..6eac2271 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts @@ -0,0 +1,7 @@ +export interface ICustomMetadataField { + name: string; + description: string; + type: "string" | "number" | "string[]" | "number[]"; + isSelect: boolean; + required: boolean; +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts b/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts new file mode 100644 index 00000000..5f5ee600 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts @@ -0,0 +1,8 @@ +import { ICustomMetadataField } from "./metadataField"; + +export interface IPluginConfig { + metadataFileName: string; + // Link to the custom schema for this metadata object (generated by this plugin) + metadataSchema?: string; + fields: ICustomMetadataField[]; +} \ No newline at end of file From ebcdf03a8f23980490dc0bba9446a64a315660ee Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 1 Jun 2023 13:36:58 -0700 Subject: [PATCH 08/20] feat: process fields and ask user for answers when initializing the metadata file --- .../rush-plugins/rush-metadata-plugin.json | 13 +-- .../src/defaultMetadataFields.json | 24 +++--- .../src/generateSchema.ts | 4 +- .../src/initMeta/index.ts | 80 +++++-------------- .../src/initMeta/queryFields.ts | 43 ++++++++++ .../src/logic/customMeta.ts | 4 +- .../src/types/metadataField.ts | 17 +++- .../src/types/pluginConfig.ts | 4 +- .../rush-metadata-plugin/tsconfig.json | 2 +- 9 files changed, 102 insertions(+), 89 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/src/initMeta/queryFields.ts diff --git a/common/config/rush-plugins/rush-metadata-plugin.json b/common/config/rush-plugins/rush-metadata-plugin.json index 4f5daf40..029550ed 100644 --- a/common/config/rush-plugins/rush-metadata-plugin.json +++ b/common/config/rush-plugins/rush-metadata-plugin.json @@ -34,25 +34,18 @@ type: boolean "$schema": "../../../rush-plugins/rush-metadata-plugin/customMetaSchema.json", "metadataFileName": "project-metadata222.json", "fields": [ - { - "name": "pointOfContacts", - "description": "POC for a specific package, synced to CODEOWNERS", - "type": "string[]", - "isSelect": false, - "required": true - }, { "name": "productLine", "description": "Which product line this project belongs to", + "prompt": "Which product line does this project belong to?", "type": "string", - "isSelect": true, "required": true }, { "name": "environmentVars", "description": "A list of environment variables used in this package", - "type": "string[]", - "isSelect": false, + "prompt": "What environment variables are required for this package?", + "type": "string", "required": false } ] diff --git a/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json b/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json index 8ec96d9d..537007ba 100644 --- a/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json +++ b/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json @@ -3,41 +3,43 @@ { "name": "purpose", "description": "The purpose of the package.", - "prompt": "What is the purpose of this package?", - "type": "string", + "prompt": "What is the purpose of this package?zzz", + "fieldType": "string", "required": true }, { "name": "pointOfContact", "description": "The list of package owners.", "prompt": "Who are the POCs of this package? (Separated by ',')", - "type": "string", + "fieldType": "string", "required": true }, { "name": "projectGroup", "description": "Which project group does this package belong to?", "prompt": "What is the project group for this package?", - "type": "string", + "fieldType": "string", "required": false }, { "name": "targetRuntime", "description": "The environment in which the project will run.", "prompt": "What is the target runtime?", - "type": "list", - "choices": ["node", "browser", "cli"] + "fieldType": "list", + "choices": ["node", "browser", "cli"], + "required": false }, { "name": "riskLevel", "description": "The risk level assigned to this package.", "prompt": "What is the risk level of this package?", - "type": "list", + "fieldType": "list", "choices": [ - { "label": "0 - Low Risk (Internal Library)", "value": 0 }, - { "label": "1 - Medium Risk (External Library)", "value": 1 }, - { "label": "2 - High Risk (Production Application)", "value": 2 } - ] + { "name": "0 - Low Risk (Internal Library)", "value": 0 }, + { "name": "1 - Medium Risk (External Library)", "value": 1 }, + { "name": "2 - High Risk (Production Application)", "value": 2 } + ], + "required": false } ] } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/generateSchema.ts b/rush-plugins/rush-metadata-plugin/src/generateSchema.ts index c8d79ac3..4a897058 100644 --- a/rush-plugins/rush-metadata-plugin/src/generateSchema.ts +++ b/rush-plugins/rush-metadata-plugin/src/generateSchema.ts @@ -1,9 +1,9 @@ -import { ICustomMetadataField } from "./types/metadataField"; +import { IMetadataField } from "./types/metadataField"; // This function takes in the custom fields defined in a specific monorepo and generates // a JSON schema that can be uploaded to a custom server and referenced in the metadata files. -export const generateSchema = (fields: ICustomMetadataField): void => { +export const generateSchema = (fields: IMetadataField): void => { console.log('generating schema...'); diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index d66a3577..11f37e90 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -9,6 +9,9 @@ import { ICoreMetadata } from '../template'; import chalk from 'chalk'; import inquirer from 'inquirer'; import { getCustomMetadataInfo } from '../logic/customMeta'; +import DefaultFields from '../defaultMetadataFields.json'; +import { IMetadataField } from '../types/metadataField'; +import { queryFields } from './queryFields'; export const initMeta = async ({project}: {project: string}): Promise => { @@ -21,81 +24,42 @@ export const initMeta = async ({project}: {project: string}): Promise => { } const { projectFolder, projectRelativeFolder } = rushProject; - // Load default fields - const { fields: defaultFields } = JsonFile.load(path.relative("../defaultMetadataFields.json")); - log('Default fields: ', defaultFields); - // Look for custom plugin configurations const { metadataFileName, fields } = getCustomMetadataInfo({ monoRoot, packageName: project }) - console.log('fields', fields) - // Check if metadata file exists already const metaFilePath: string = path.join(projectFolder, metadataFileName); if (fs.existsSync(metaFilePath)) { - log('file exists at location: ', metaFilePath); + log('file already exists at location! ', metaFilePath); // Read and parse the file - const loadedJsonFile: ICoreMetadata = JsonFile.load(metaFilePath); + // const loadedJsonFile: ICoreMetadata = JsonFile.load(metaFilePath); + + log(chalk.red("Please run rush meta update or edit the metadata file directly to make updates")); + + return; + + } + + // Load default fields + const { fields: defaultFields }: { fields: IMetadataField[] } = DefaultFields as { fields: IMetadataField[] }; + log('Default fields: ', defaultFields); - log('Loaded metadata file: ', loadedJsonFile.purpose); + console.log('fields', fields) + + // join the custom and default fields + const allFields: IMetadataField[] = [...defaultFields, ...fields]; - } else { - log('Creating metadata file at location: ', metaFilePath); - log(chalk.green(`Creating a new metadata file for project: ${project}...`)); - const answers: any = await inquirer.prompt([ - { - type: 'input', - name: 'purpose', - message: 'What is the purpose of this package?' - }, - { - type: 'input', - name: 'pointOfContact', - message: 'Who are the POCs of this package? (Separated by ",")' - }, - { - type: 'input', - name: 'projectGroup', - message: 'What is the project group for this package?' - }, - { - type: 'list', - name: 'targetRuntime', - choices: ['node', 'browser', 'cli'], - message: 'What is the target runtime?' - }, - { - type: 'list', - name: 'riskLevel', - choices: [ - '0 - Low Risk (Internal Library)', - '1 - Medium Risk (External Library)', - '2 - High Risk (Production Application)' - ], - message: 'What is the Risk Level of this package?' - } - ]); - console.log('The answers entered are: ', answers); - const {confirmAnswers}: any = await inquirer.prompt([ - { - type: 'confirm', - name: 'confirmAnswers', - message: 'Do you want to save the above answers to the metadata file?' - } - ]) - if (!confirmAnswers) { - console.log('Discarding answers...'); - return; - } + const answers: Record = await queryFields(allFields); + log('Returned answers: ', answers); // const metadataContents: ICoreMetadata = { // // description: 'test description', // // pointOfContact: '' // }; // JsonFile.save(metadataContents, metaFilePath); - } + } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/queryFields.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/queryFields.ts new file mode 100644 index 00000000..d2bcebd3 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/queryFields.ts @@ -0,0 +1,43 @@ +import inquirer from "inquirer"; +import { IMetadataField } from "../types/metadataField"; + +export const queryFields = async (fields: IMetadataField[]): Promise> => { + + const inquirerQuestions: any = []; + + for (const field of fields) { + const question: any = { + name: field.name, + message: field.prompt, + type: "input" + }; + switch(field.fieldType) { + case "string": + case "number": + question.type = "input"; + break; + case "list": + question.type = "list"; + question.choices = field.choices || []; + break; + } + inquirerQuestions.push(question); + } + + const answers: any = await inquirer.prompt(inquirerQuestions); + console.log('The answers entered are: ', answers); + const {confirmAnswers}: any = await inquirer.prompt([ + { + type: 'confirm', + name: 'confirmAnswers', + message: 'Do you want to save the above answers to the metadata file?' + } + ]) + + if (!confirmAnswers) { + return {}; + } + + return answers; + +} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts index 1aad52b0..dbe06f4e 100644 --- a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts @@ -3,7 +3,7 @@ import path from 'path'; import { loadRushConfiguration } from "./rushConfiguration"; import { JsonFile, FileSystem } from "@rushstack/node-core-library"; -import { ICustomMetadataField } from "../types/metadataField"; +import { IMetadataField } from "../types/metadataField"; import { IPluginConfig } from "../types/pluginConfig"; export const defaultMetadataRelativeFolder: string = 'incorrect-package-meta.json'; @@ -24,7 +24,7 @@ export const getCustomMetadataInfo = ({ monoRoot, packageName }: IGetCustomMetad // Custom configurations for plugin let metadataRelativeFolder: string = defaultMetadataRelativeFolder; - let customFields: ICustomMetadataField[] = []; + let customFields: IMetadataField[] = []; let metaConfigs: IPluginConfig | undefined; try { diff --git a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts index 6eac2271..274867da 100644 --- a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts +++ b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts @@ -1,7 +1,18 @@ -export interface ICustomMetadataField { +export type FieldTypes = "string" | "number" | "list" | "choice" | "selector"; + +export enum FIELD_TYPES { + STRING = "string", + NUMBER = "number", + LIST = "list", + CHOICE = "choice", + SELECTOR = "selector" +} + +export interface IMetadataField { name: string; description: string; - type: "string" | "number" | "string[]" | "number[]"; - isSelect: boolean; + prompt: string; + fieldType: FIELD_TYPES; required: boolean; + choices?: string[] | { label: string; value: string | number }[] } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts b/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts index 5f5ee600..80892f0b 100644 --- a/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts +++ b/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts @@ -1,8 +1,8 @@ -import { ICustomMetadataField } from "./metadataField"; +import { IMetadataField } from "./metadataField"; export interface IPluginConfig { metadataFileName: string; // Link to the custom schema for this metadata object (generated by this plugin) metadataSchema?: string; - fields: ICustomMetadataField[]; + fields: IMetadataField[]; } \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/tsconfig.json b/rush-plugins/rush-metadata-plugin/tsconfig.json index c5ba5d71..5f5cea08 100644 --- a/rush-plugins/rush-metadata-plugin/tsconfig.json +++ b/rush-plugins/rush-metadata-plugin/tsconfig.json @@ -35,7 +35,7 @@ "node" ] /* Specify type package names to be included without being referenced in a source file. */, // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ + "resolveJsonModule": true, /* Enable importing .json files */ // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ From f047472b917d23851d5d0f98929bc68d0b8fc938 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 1 Jun 2023 14:19:43 -0700 Subject: [PATCH 09/20] feat: handle the sync action --- .../command-plugins/pnpm-lock.yaml | 21 ++++++---- .../rush-metadata-plugin/command-line.json | 17 ++++++--- .../rush-metadata-plugin/command-line.json | 17 ++++++--- .../rush-metadata-plugin/package.json | 12 +++--- .../rush-metadata-plugin/src/index.ts | 29 +++++++++----- .../src/initMeta/index.ts | 38 +++++++++---------- .../rush-metadata-plugin/src/syncMeta.ts | 6 ++- .../src/syncMeta/index.ts | 11 ++++++ 8 files changed, 96 insertions(+), 55 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts diff --git a/common/autoinstallers/command-plugins/pnpm-lock.yaml b/common/autoinstallers/command-plugins/pnpm-lock.yaml index 8d8cf0af..8499c710 100644 --- a/common/autoinstallers/command-plugins/pnpm-lock.yaml +++ b/common/autoinstallers/command-plugins/pnpm-lock.yaml @@ -721,7 +721,7 @@ packages: '@npmcli/move-file': 2.0.1 chownr: 2.0.0 fs-minipass: 2.1.0 - glob: 8.1.0 + glob: 8.0.3 infer-owner: 1.0.4 lru-cache: 7.14.1 minipass: 3.3.6 @@ -909,6 +909,13 @@ packages: engines: {node: ^12.20.0 || >=14} dev: false + /commander/9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + requiresBuild: true + dev: false + optional: true + /component-emitter/1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} dev: false @@ -1382,14 +1389,14 @@ packages: path-is-absolute: 1.0.1 dev: false - /glob/8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + /glob/8.0.3: + resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==} engines: {node: '>=12'} dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 5.1.6 + minimatch: 5.1.2 once: 1.4.0 dev: false @@ -2316,8 +2323,8 @@ packages: brace-expansion: 1.1.11 dev: false - /minimatch/5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + /minimatch/5.1.2: + resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==} engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 @@ -3731,5 +3738,5 @@ packages: lodash.isequal: 4.5.0 validator: 13.7.0 optionalDependencies: - commander: 9.4.1 + commander: 9.5.0 dev: false diff --git a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json index 22b97289..efc9907e 100644 --- a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json +++ b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json @@ -2,11 +2,18 @@ "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", "commands": [ { - "name": "meta", + "name": "meta-init", "commandKind": "global", - "summary": "Create or update metadata information in the current package.", - "shellCommand": "node /lib/index.js meta", - "safeForSimultaneousRushProcesses": true + "summary": "Initialize a metadata file for a project", + "shellCommand": "node /lib/index.js init", + "safeForSimultaneousRushProcesses": false + }, + { + "name": "meta-sync", + "commandKind": "global", + "summary": "Sync the metadata files in the monorepo", + "shellCommand": "node /lib/index.js sync", + "safeForSimultaneousRushProcesses": false } ], "parameters": [ @@ -16,7 +23,7 @@ "longName": "--project", "shortName": "-p", "argumentName": "PACKAGE_NAME", - "associatedCommands": ["meta"], + "associatedCommands": ["meta-init"], "required": false } ] diff --git a/rush-plugins/rush-metadata-plugin/command-line.json b/rush-plugins/rush-metadata-plugin/command-line.json index 22b97289..efc9907e 100644 --- a/rush-plugins/rush-metadata-plugin/command-line.json +++ b/rush-plugins/rush-metadata-plugin/command-line.json @@ -2,11 +2,18 @@ "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", "commands": [ { - "name": "meta", + "name": "meta-init", "commandKind": "global", - "summary": "Create or update metadata information in the current package.", - "shellCommand": "node /lib/index.js meta", - "safeForSimultaneousRushProcesses": true + "summary": "Initialize a metadata file for a project", + "shellCommand": "node /lib/index.js init", + "safeForSimultaneousRushProcesses": false + }, + { + "name": "meta-sync", + "commandKind": "global", + "summary": "Sync the metadata files in the monorepo", + "shellCommand": "node /lib/index.js sync", + "safeForSimultaneousRushProcesses": false } ], "parameters": [ @@ -16,7 +23,7 @@ "longName": "--project", "shortName": "-p", "argumentName": "PACKAGE_NAME", - "associatedCommands": ["meta"], + "associatedCommands": ["meta-init"], "required": false } ] diff --git a/rush-plugins/rush-metadata-plugin/package.json b/rush-plugins/rush-metadata-plugin/package.json index 298110ca..d3b9c6d7 100644 --- a/rush-plugins/rush-metadata-plugin/package.json +++ b/rush-plugins/rush-metadata-plugin/package.json @@ -30,12 +30,12 @@ "dependencies": { "@rushstack/node-core-library": "3.44.1", "@rushstack/rush-sdk": "5.62.4", + "chalk": "4.1.2", "commander": "~9.4.0", "ignore": "5.1.9", - "yargs": "~17.3.0", "inquirer": "~8.2.0", - "chalk": "4.1.2", - "json2md": "~2.0.0" + "json2md": "~2.0.0", + "yargs": "~17.3.0" }, "devDependencies": { "@rushstack/eslint-config": "2.4.5", @@ -43,11 +43,11 @@ "@rushstack/heft-jest-plugin": "~0.1.53", "@rushstack/heft-node-rig": "1.2.31", "@types/heft-jest": "1.0.1", + "@types/inquirer": "~8.1.3", + "@types/json2md": "~1.5.1", "@types/node": "12.20.24", "@types/yargs": "~17.0.7", "eslint": "7.32.0", - "typescript": "4.4.2", - "@types/inquirer": "~8.1.3", - "@types/json2md": "~1.5.1" + "typescript": "4.4.2" } } diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts index dc292772..85594f73 100644 --- a/rush-plugins/rush-metadata-plugin/src/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -6,20 +6,22 @@ import { hideBin } from 'yargs/helpers'; main(); async function main(): Promise { + console.log('argv: ', process.argv); await yargs(hideBin(process.argv)) .command( - "meta", - "Add metadata to a project", + 'init', + 'Add metadata to a project', (yargs) => { return yargs - .option("project", { - type: "string", - describe: "The name of the package to archive", + .option('project', { + type: 'string', + describe: 'The name of the package to archive' }) - .demandOption(["project"]); + .demandOption(['project']); }, async (argv) => { - const { initMeta } = await import("./initMeta"); + console.log('ARGUMENTS: ', argv); + const { initMeta } = await import('./initMeta'); try { await initMeta(argv); } catch (e: any) { @@ -28,6 +30,15 @@ async function main(): Promise { } } ) - .demandCommand(1, "You need at least one command before moving on") + .command('sync', 'Sync the metadata in the monorepo.', async () => { + const { syncMeta } = await import('./syncMeta'); + try { + await syncMeta(); + } catch (e: any) { + console.error('error: ', e); + process.exit(1); + } + }) + .demandCommand(1, 'You need at least one command before moving on') .parse(); -} \ No newline at end of file +} diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index 11f37e90..7d43e99c 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -1,24 +1,20 @@ import fs from 'fs'; import path from 'path'; -import { RushConfiguration, RushConfigurationProject } from "@rushstack/rush-sdk"; -import { loadRushConfiguration } from "../logic/rushConfiguration"; +import { RushConfiguration, RushConfigurationProject } from '@rushstack/rush-sdk'; +import { loadRushConfiguration } from '../logic/rushConfiguration'; import { log } from 'console'; -import { JsonFile } from '@rushstack/node-core-library'; -import { ICoreMetadata } from '../template'; import chalk from 'chalk'; -import inquirer from 'inquirer'; import { getCustomMetadataInfo } from '../logic/customMeta'; import DefaultFields from '../defaultMetadataFields.json'; import { IMetadataField } from '../types/metadataField'; import { queryFields } from './queryFields'; -export const initMeta = async ({project}: {project: string}): Promise => { - +// Used to initialize a metadata file for a package +export const initMeta = async ({ project }: { project: string }): Promise => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); const monoRoot: string = rushConfiguration.rushJsonFolder; - const rushProject: RushConfigurationProject | undefined = - rushConfiguration.getProjectByName(project); + const rushProject: RushConfigurationProject | undefined = rushConfiguration.getProjectByName(project); if (!rushProject) { throw new Error(`Could not find project with package name ${project}`); } @@ -28,7 +24,7 @@ export const initMeta = async ({project}: {project: string}): Promise => { const { metadataFileName, fields } = getCustomMetadataInfo({ monoRoot, packageName: project - }) + }); // Check if metadata file exists already const metaFilePath: string = path.join(projectFolder, metadataFileName); @@ -39,27 +35,27 @@ export const initMeta = async ({project}: {project: string}): Promise => { // Read and parse the file // const loadedJsonFile: ICoreMetadata = JsonFile.load(metaFilePath); - log(chalk.red("Please run rush meta update or edit the metadata file directly to make updates")); + log(chalk.red('Please run rush meta update or edit the metadata file directly to make updates')); return; - } // Load default fields - const { fields: defaultFields }: { fields: IMetadataField[] } = DefaultFields as { fields: IMetadataField[] }; + const { fields: defaultFields }: { fields: IMetadataField[] } = DefaultFields as { + fields: IMetadataField[]; + }; log('Default fields: ', defaultFields); - console.log('fields', fields) + console.log('fields', fields); // join the custom and default fields const allFields: IMetadataField[] = [...defaultFields, ...fields]; const answers: Record = await queryFields(allFields); log('Returned answers: ', answers); - // const metadataContents: ICoreMetadata = { - // // description: 'test description', - // // pointOfContact: '' - // }; - // JsonFile.save(metadataContents, metaFilePath); - -} \ No newline at end of file + // const metadataContents: ICoreMetadata = { + // // description: 'test description', + // // pointOfContact: '' + // }; + // JsonFile.save(metadataContents, metaFilePath); +}; diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta.ts index f3597aca..3983ce7c 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta.ts @@ -1,6 +1,8 @@ // Run by the "rush meta sync" command // Syncs all the metadata files in a monorepo according to the metadata spec. -export const syncMeta = () => { +import { log } from 'console'; -} \ No newline at end of file +export const syncMeta = async (): Promise => { + log('Syncing metadata...'); +}; diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts new file mode 100644 index 00000000..be9a73d5 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts @@ -0,0 +1,11 @@ +import { RushConfiguration } from '@rushstack/rush-sdk'; +import { loadRushConfiguration } from '../logic/rushConfiguration'; +import { log } from 'console'; + +// Used to sync all the metadata files in the monorepo according to the metadata spec +export const syncMeta = async (): Promise => { + const rushConfiguration: RushConfiguration = loadRushConfiguration(); + for (const rushProject of rushConfiguration.projects) { + log('rush project: ', rushProject.projectFolder); + } +}; From 73ab033ace375a305298947c30311935b9c178c0 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 1 Jun 2023 14:41:27 -0700 Subject: [PATCH 10/20] feat: start syncing files --- .../project-metadata222.json | 9 +++++ .../rush-metadata-plugin/src/index.ts | 1 - .../src/initMeta/index.ts | 28 ++++--------- .../src/logic/customMeta.ts | 40 ++++++++++++------- .../rush-metadata-plugin/src/syncMeta.ts | 8 ---- .../src/syncMeta/index.ts | 26 +++++++++++- .../src/syncMeta/syncMetadataFile.ts | 8 ++++ 7 files changed, 74 insertions(+), 46 deletions(-) create mode 100644 rush-plugins/rush-archive-project-plugin/project-metadata222.json delete mode 100644 rush-plugins/rush-metadata-plugin/src/syncMeta.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts diff --git a/rush-plugins/rush-archive-project-plugin/project-metadata222.json b/rush-plugins/rush-archive-project-plugin/project-metadata222.json new file mode 100644 index 00000000..8453d087 --- /dev/null +++ b/rush-plugins/rush-archive-project-plugin/project-metadata222.json @@ -0,0 +1,9 @@ +{ + "purpose": "a", + "pointOfContact": "b", + "projectGroup": "c", + "targetRuntime": "node", + "riskLevel": 0, + "productLine": "f", + "environmentVars": "d" +} diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts index 85594f73..2aa6daee 100644 --- a/rush-plugins/rush-metadata-plugin/src/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -6,7 +6,6 @@ import { hideBin } from 'yargs/helpers'; main(); async function main(): Promise { - console.log('argv: ', process.argv); await yargs(hideBin(process.argv)) .command( 'init', diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index 7d43e99c..92129e5d 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -5,10 +5,11 @@ import { RushConfiguration, RushConfigurationProject } from '@rushstack/rush-sdk import { loadRushConfiguration } from '../logic/rushConfiguration'; import { log } from 'console'; import chalk from 'chalk'; -import { getCustomMetadataInfo } from '../logic/customMeta'; +import { getAllMetadataFields, getCustomMetadataInfo } from '../logic/customMeta'; import DefaultFields from '../defaultMetadataFields.json'; import { IMetadataField } from '../types/metadataField'; import { queryFields } from './queryFields'; +import { JsonFile } from '@rushstack/node-core-library'; // Used to initialize a metadata file for a package export const initMeta = async ({ project }: { project: string }): Promise => { @@ -18,13 +19,10 @@ export const initMeta = async ({ project }: { project: string }): Promise if (!rushProject) { throw new Error(`Could not find project with package name ${project}`); } - const { projectFolder, projectRelativeFolder } = rushProject; + const { projectFolder } = rushProject; // Look for custom plugin configurations - const { metadataFileName, fields } = getCustomMetadataInfo({ - monoRoot, - packageName: project - }); + const { metadataFileName, fields } = getCustomMetadataInfo(); // Check if metadata file exists already const metaFilePath: string = path.join(projectFolder, metadataFileName); @@ -40,22 +38,10 @@ export const initMeta = async ({ project }: { project: string }): Promise return; } - // Load default fields - const { fields: defaultFields }: { fields: IMetadataField[] } = DefaultFields as { - fields: IMetadataField[]; - }; - log('Default fields: ', defaultFields); - - console.log('fields', fields); - - // join the custom and default fields - const allFields: IMetadataField[] = [...defaultFields, ...fields]; + const allFields: IMetadataField[] = getAllMetadataFields(); const answers: Record = await queryFields(allFields); log('Returned answers: ', answers); - // const metadataContents: ICoreMetadata = { - // // description: 'test description', - // // pointOfContact: '' - // }; - // JsonFile.save(metadataContents, metaFilePath); + + JsonFile.save(answers, metaFilePath); }; diff --git a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts index dbe06f4e..40c0b26c 100644 --- a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts @@ -1,26 +1,21 @@ -import { RushConfiguration } from "@rushstack/rush-sdk"; +import { RushConfiguration } from '@rushstack/rush-sdk'; import path from 'path'; -import { loadRushConfiguration } from "./rushConfiguration"; -import { JsonFile, FileSystem } from "@rushstack/node-core-library"; -import { IMetadataField } from "../types/metadataField"; -import { IPluginConfig } from "../types/pluginConfig"; +import { loadRushConfiguration } from './rushConfiguration'; +import { JsonFile, FileSystem } from '@rushstack/node-core-library'; +import { IMetadataField } from '../types/metadataField'; +import { IPluginConfig } from '../types/pluginConfig'; +import DefaultFields from '../defaultMetadataFields.json'; export const defaultMetadataRelativeFolder: string = 'incorrect-package-meta.json'; -export interface IGetCustomMetadataInfoParams { - monoRoot: string; - packageName: string; -} - -export const getCustomMetadataInfo = ({ monoRoot, packageName }: IGetCustomMetadataInfoParams): IPluginConfig => { - +export const getCustomMetadataInfo = (): IPluginConfig => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); const pluginOptionsJsonFilePath: string = path.join( rushConfiguration.rushPluginOptionsFolder, 'rush-metadata-plugin.json' - ) + ); // Custom configurations for plugin let metadataRelativeFolder: string = defaultMetadataRelativeFolder; @@ -48,5 +43,20 @@ export const getCustomMetadataInfo = ({ monoRoot, packageName }: IGetCustomMetad return { metadataFileName: metadataRelativeFolder, fields: customFields - } -} \ No newline at end of file + }; +}; + +export const getAllMetadataFields = (): IMetadataField[] => { + // Look for custom plugin configurations + const { fields } = getCustomMetadataInfo(); + + // Load default fields + const { fields: defaultFields }: { fields: IMetadataField[] } = DefaultFields as { + fields: IMetadataField[]; + }; + + // join the custom and default fields + const allFields: IMetadataField[] = [...defaultFields, ...fields]; + + return allFields; +}; diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta.ts deleted file mode 100644 index 3983ce7c..00000000 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Run by the "rush meta sync" command -// Syncs all the metadata files in a monorepo according to the metadata spec. - -import { log } from 'console'; - -export const syncMeta = async (): Promise => { - log('Syncing metadata...'); -}; diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts index be9a73d5..8735aa17 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts @@ -1,11 +1,35 @@ import { RushConfiguration } from '@rushstack/rush-sdk'; -import { loadRushConfiguration } from '../logic/rushConfiguration'; +import { JsonFile } from '@rushstack/node-core-library'; import { log } from 'console'; +import chalk from 'chalk'; +import path from 'path'; +import fs from 'fs'; + +import { loadRushConfiguration } from '../logic/rushConfiguration'; +import { getCustomMetadataInfo } from '../logic/customMeta'; +import { ICoreMetadata } from '../template'; +import { syncMetadataFile } from './syncMetadataFile'; // Used to sync all the metadata files in the monorepo according to the metadata spec export const syncMeta = async (): Promise => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); for (const rushProject of rushConfiguration.projects) { log('rush project: ', rushProject.projectFolder); + + // Look for custom plugin configurations + const { metadataFileName, fields } = getCustomMetadataInfo(); + + // Check if metadata file exists already + const metaFilePath: string = path.join(rushProject.projectFolder, metadataFileName); + if (fs.existsSync(metaFilePath)) { + // Read and parse the file + const loadedJsonFile: ICoreMetadata = JsonFile.load(metaFilePath); + + log(chalk.green(`Updating metadata file at: ${metaFilePath}`)); + + syncMetadataFile(loadedJsonFile); + + return; + } } }; diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts new file mode 100644 index 00000000..600bea1b --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts @@ -0,0 +1,8 @@ +import { getAllMetadataFields } from '../logic/customMeta'; +import { ICoreMetadata } from '../template'; + +export const syncMetadataFile = (currMetadata: ICoreMetadata): void => { + const allFields = getAllMetadataFields(); + + console.log('syncing file: ', currMetadata); +}; From 2f477cbf87afa93c668798aa5cb85f147822f6b9 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 1 Jun 2023 15:01:16 -0700 Subject: [PATCH 11/20] feat: rush meta-sync can now do basic updating of all monorepo metadata files --- .../rush-plugins/rush-metadata-plugin.json | 9 +++++++-- .../project-metadata222.json | 1 - .../src/syncMeta/index.ts | 4 ++-- .../src/syncMeta/syncMetadataFile.ts | 20 +++++++++++++++++-- .../src/types/metadataField.ts | 17 ++++++++-------- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/common/config/rush-plugins/rush-metadata-plugin.json b/common/config/rush-plugins/rush-metadata-plugin.json index 029550ed..d22fe1c8 100644 --- a/common/config/rush-plugins/rush-metadata-plugin.json +++ b/common/config/rush-plugins/rush-metadata-plugin.json @@ -29,7 +29,6 @@ type: boolean */ - { "$schema": "../../../rush-plugins/rush-metadata-plugin/customMetaSchema.json", "metadataFileName": "project-metadata222.json", @@ -48,5 +47,11 @@ type: boolean "type": "string", "required": false } + // { + // "name": "testField", + // "description": "test", + // "type": "string", + // "required": true + // } ] -} \ No newline at end of file +} diff --git a/rush-plugins/rush-archive-project-plugin/project-metadata222.json b/rush-plugins/rush-archive-project-plugin/project-metadata222.json index 8453d087..5df27b0b 100644 --- a/rush-plugins/rush-archive-project-plugin/project-metadata222.json +++ b/rush-plugins/rush-archive-project-plugin/project-metadata222.json @@ -3,7 +3,6 @@ "pointOfContact": "b", "projectGroup": "c", "targetRuntime": "node", - "riskLevel": 0, "productLine": "f", "environmentVars": "d" } diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts index 8735aa17..d2e76c23 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts @@ -27,9 +27,9 @@ export const syncMeta = async (): Promise => { log(chalk.green(`Updating metadata file at: ${metaFilePath}`)); - syncMetadataFile(loadedJsonFile); + const newMetadataFile: any = syncMetadataFile(loadedJsonFile); - return; + JsonFile.save(newMetadataFile, metaFilePath, { updateExistingFile: true }); } } }; diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts index 600bea1b..8d9dc252 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts @@ -1,8 +1,24 @@ import { getAllMetadataFields } from '../logic/customMeta'; import { ICoreMetadata } from '../template'; +import { IMetadataField } from '../types/metadataField'; -export const syncMetadataFile = (currMetadata: ICoreMetadata): void => { - const allFields = getAllMetadataFields(); +export const syncMetadataFile = (currMetadata: any): ICoreMetadata => { + const allFields: IMetadataField[] = getAllMetadataFields(); + const newMetadata: any = {}; console.log('syncing file: ', currMetadata); + + for (const field of allFields) { + // @ts-ignore + if (field.required && !currMetadata[field.name]) { + // Fill in the field with a default value + newMetadata[field.name] = ''; + } else if (!field.required && !currMetadata[field.name]) { + // Don't care if the field doesn't exist on a field that is not required + } else { + newMetadata[field.name] = currMetadata[field.name]; + } + } + + return newMetadata; }; diff --git a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts index 274867da..23582a60 100644 --- a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts +++ b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts @@ -1,11 +1,11 @@ -export type FieldTypes = "string" | "number" | "list" | "choice" | "selector"; +export type FieldTypes = 'string' | 'number' | 'list' | 'choice' | 'selector'; export enum FIELD_TYPES { - STRING = "string", - NUMBER = "number", - LIST = "list", - CHOICE = "choice", - SELECTOR = "selector" + STRING = 'string', + NUMBER = 'number', + LIST = 'list', + CHOICE = 'choice', + SELECTOR = 'selector' } export interface IMetadataField { @@ -14,5 +14,6 @@ export interface IMetadataField { prompt: string; fieldType: FIELD_TYPES; required: boolean; - choices?: string[] | { label: string; value: string | number }[] -} \ No newline at end of file + choices?: string[] | { label: string; value: string | number }[]; + defaultValue?: string; +} From d1d4093db0495a6aa3df2cd99c7aaee7a286d0c9 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 1 Jun 2023 15:08:42 -0700 Subject: [PATCH 12/20] chore: clean up --- rush-plugins/rush-metadata-plugin/src/initMeta/index.ts | 4 +--- rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index 92129e5d..1fa054cd 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -6,7 +6,6 @@ import { loadRushConfiguration } from '../logic/rushConfiguration'; import { log } from 'console'; import chalk from 'chalk'; import { getAllMetadataFields, getCustomMetadataInfo } from '../logic/customMeta'; -import DefaultFields from '../defaultMetadataFields.json'; import { IMetadataField } from '../types/metadataField'; import { queryFields } from './queryFields'; import { JsonFile } from '@rushstack/node-core-library'; @@ -14,7 +13,6 @@ import { JsonFile } from '@rushstack/node-core-library'; // Used to initialize a metadata file for a package export const initMeta = async ({ project }: { project: string }): Promise => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); - const monoRoot: string = rushConfiguration.rushJsonFolder; const rushProject: RushConfigurationProject | undefined = rushConfiguration.getProjectByName(project); if (!rushProject) { throw new Error(`Could not find project with package name ${project}`); @@ -22,7 +20,7 @@ export const initMeta = async ({ project }: { project: string }): Promise const { projectFolder } = rushProject; // Look for custom plugin configurations - const { metadataFileName, fields } = getCustomMetadataInfo(); + const { metadataFileName } = getCustomMetadataInfo(); // Check if metadata file exists already const metaFilePath: string = path.join(projectFolder, metadataFileName); diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts index d2e76c23..348af884 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts @@ -17,7 +17,7 @@ export const syncMeta = async (): Promise => { log('rush project: ', rushProject.projectFolder); // Look for custom plugin configurations - const { metadataFileName, fields } = getCustomMetadataInfo(); + const { metadataFileName } = getCustomMetadataInfo(); // Check if metadata file exists already const metaFilePath: string = path.join(rushProject.projectFolder, metadataFileName); From 0fa50169aaa6249db4d1796e9da4438c62ede50d Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 1 Jun 2023 15:21:18 -0700 Subject: [PATCH 13/20] chore: try to get working build --- rush-plugins/rush-metadata-plugin/package.json | 3 --- rush-plugins/rush-metadata-plugin/src/initMeta/index.ts | 7 ------- .../rush-metadata-plugin/src/types/metadataField.ts | 6 ++---- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/rush-plugins/rush-metadata-plugin/package.json b/rush-plugins/rush-metadata-plugin/package.json index d3b9c6d7..29ea0592 100644 --- a/rush-plugins/rush-metadata-plugin/package.json +++ b/rush-plugins/rush-metadata-plugin/package.json @@ -18,9 +18,6 @@ "author": "william2958", "main": "lib/index.js", "typings": "lib/index.d.ts", - "bin": { - "rush-metadata-plugin": "lib/index.js" - }, "scripts": { "build": "heft build --clean", "build:watch": "heft build --watch", diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index 1fa054cd..35f45eea 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -26,20 +26,13 @@ export const initMeta = async ({ project }: { project: string }): Promise const metaFilePath: string = path.join(projectFolder, metadataFileName); if (fs.existsSync(metaFilePath)) { - log('file already exists at location! ', metaFilePath); - - // Read and parse the file - // const loadedJsonFile: ICoreMetadata = JsonFile.load(metaFilePath); - log(chalk.red('Please run rush meta update or edit the metadata file directly to make updates')); - return; } const allFields: IMetadataField[] = getAllMetadataFields(); const answers: Record = await queryFields(allFields); - log('Returned answers: ', answers); JsonFile.save(answers, metaFilePath); }; diff --git a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts index 23582a60..40e50d0a 100644 --- a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts +++ b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts @@ -1,6 +1,4 @@ -export type FieldTypes = 'string' | 'number' | 'list' | 'choice' | 'selector'; - -export enum FIELD_TYPES { +export enum FieldTypes { STRING = 'string', NUMBER = 'number', LIST = 'list', @@ -12,7 +10,7 @@ export interface IMetadataField { name: string; description: string; prompt: string; - fieldType: FIELD_TYPES; + fieldType: FieldTypes; required: boolean; choices?: string[] | { label: string; value: string | number }[]; defaultValue?: string; From 5bacc903eaf1679eab17ac43b0bdc013ff22b941 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 1 Jun 2023 15:22:49 -0700 Subject: [PATCH 14/20] chore: clean project metadata file --- .../rush-archive-project-plugin/project-metadata222.json | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 rush-plugins/rush-archive-project-plugin/project-metadata222.json diff --git a/rush-plugins/rush-archive-project-plugin/project-metadata222.json b/rush-plugins/rush-archive-project-plugin/project-metadata222.json deleted file mode 100644 index 5df27b0b..00000000 --- a/rush-plugins/rush-archive-project-plugin/project-metadata222.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "purpose": "a", - "pointOfContact": "b", - "projectGroup": "c", - "targetRuntime": "node", - "productLine": "f", - "environmentVars": "d" -} From 9ea45916458c6e21c363873d8e3d9e44524e64da Mon Sep 17 00:00:00 2001 From: William Huang Date: Fri, 2 Jun 2023 13:10:29 -0700 Subject: [PATCH 15/20] chore: load default metadata file name from config --- common/config/rush-plugins/rush-metadata-plugin.json | 2 +- .../rush-metadata-plugin/src/defaultMetadataFields.json | 3 ++- .../rush-metadata-plugin/src/logic/customMeta.ts | 9 +++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/common/config/rush-plugins/rush-metadata-plugin.json b/common/config/rush-plugins/rush-metadata-plugin.json index d22fe1c8..4d20b5b2 100644 --- a/common/config/rush-plugins/rush-metadata-plugin.json +++ b/common/config/rush-plugins/rush-metadata-plugin.json @@ -31,7 +31,7 @@ type: boolean { "$schema": "../../../rush-plugins/rush-metadata-plugin/customMetaSchema.json", - "metadataFileName": "project-metadata222.json", + // "metadataFileName": "config/project-metadata.json", "fields": [ { "name": "productLine", diff --git a/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json b/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json index 537007ba..35842a92 100644 --- a/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json +++ b/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json @@ -1,4 +1,5 @@ { + "metadataFileName": "config/project-metadata.json", "fields": [ { "name": "purpose", @@ -42,4 +43,4 @@ "required": false } ] -} \ No newline at end of file +} diff --git a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts index 40c0b26c..54e0fce5 100644 --- a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts @@ -7,7 +7,12 @@ import { IMetadataField } from '../types/metadataField'; import { IPluginConfig } from '../types/pluginConfig'; import DefaultFields from '../defaultMetadataFields.json'; -export const defaultMetadataRelativeFolder: string = 'incorrect-package-meta.json'; +export const getDefaultMetadataFileName = (): string => { + // Load default fields + const { metadataFileName }: { metadataFileName: string } = DefaultFields; + + return metadataFileName; +}; export const getCustomMetadataInfo = (): IPluginConfig => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); @@ -18,7 +23,7 @@ export const getCustomMetadataInfo = (): IPluginConfig => { ); // Custom configurations for plugin - let metadataRelativeFolder: string = defaultMetadataRelativeFolder; + let metadataRelativeFolder: string = getDefaultMetadataFileName(); let customFields: IMetadataField[] = []; let metaConfigs: IPluginConfig | undefined; From 76dc1953f4e92cef754c90cdc62039a50f2c9807 Mon Sep 17 00:00:00 2001 From: William Huang Date: Fri, 2 Jun 2023 13:19:46 -0700 Subject: [PATCH 16/20] chore: support file path nesting in metadata file name --- .../rush-metadata-plugin/config/project-metadata.json | 9 +++++++++ rush-plugins/rush-metadata-plugin/src/initMeta/index.ts | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 rush-plugins/rush-metadata-plugin/config/project-metadata.json diff --git a/rush-plugins/rush-metadata-plugin/config/project-metadata.json b/rush-plugins/rush-metadata-plugin/config/project-metadata.json new file mode 100644 index 00000000..28b917eb --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/config/project-metadata.json @@ -0,0 +1,9 @@ +{ + "purpose": "d", + "pointOfContact": "d", + "projectGroup": "d", + "targetRuntime": "node", + "riskLevel": 0, + "productLine": "", + "environmentVars": "d" +} diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index 35f45eea..f13bf602 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -34,5 +34,5 @@ export const initMeta = async ({ project }: { project: string }): Promise const answers: Record = await queryFields(allFields); - JsonFile.save(answers, metaFilePath); + JsonFile.save(answers, metaFilePath, { ensureFolderExists: true }); }; From 585b41f3d54c10dc09a7722565cdc28955022592 Mon Sep 17 00:00:00 2001 From: William Huang Date: Mon, 5 Jun 2023 14:33:28 -0700 Subject: [PATCH 17/20] chore: set up for codeowners transformation --- .../rush-metadata-plugin/command-line.json | 7 +++++ .../rush-metadata-plugin/command-line.json | 7 +++++ .../config/project-metadata.json | 10 +++---- .../src/defaultMetadataFields.json | 3 ++- .../rush-metadata-plugin/src/index.ts | 26 +++++++++++++------ .../src/initMeta/index.ts | 7 ++++- .../src/logic/customMeta.ts | 19 +++++++++++--- .../src/syncMeta/index.ts | 14 +++++++--- .../src/transformers/outputToCodeowners.ts | 6 +++++ .../src/types/pluginConfig.ts | 7 ++--- 10 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts diff --git a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json index efc9907e..80290e0c 100644 --- a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json +++ b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json @@ -25,6 +25,13 @@ "argumentName": "PACKAGE_NAME", "associatedCommands": ["meta-init"], "required": false + }, + { + "parameterKind": "flag", + "description": "Whether to output a codeowners file", + "longName": "--codeowners", + "associatedCommands": ["meta-init", "meta-sync"], + "required": false } ] } diff --git a/rush-plugins/rush-metadata-plugin/command-line.json b/rush-plugins/rush-metadata-plugin/command-line.json index efc9907e..80290e0c 100644 --- a/rush-plugins/rush-metadata-plugin/command-line.json +++ b/rush-plugins/rush-metadata-plugin/command-line.json @@ -25,6 +25,13 @@ "argumentName": "PACKAGE_NAME", "associatedCommands": ["meta-init"], "required": false + }, + { + "parameterKind": "flag", + "description": "Whether to output a codeowners file", + "longName": "--codeowners", + "associatedCommands": ["meta-init", "meta-sync"], + "required": false } ] } diff --git a/rush-plugins/rush-metadata-plugin/config/project-metadata.json b/rush-plugins/rush-metadata-plugin/config/project-metadata.json index 28b917eb..69f9bfd0 100644 --- a/rush-plugins/rush-metadata-plugin/config/project-metadata.json +++ b/rush-plugins/rush-metadata-plugin/config/project-metadata.json @@ -1,9 +1,7 @@ { - "purpose": "d", - "pointOfContact": "d", - "projectGroup": "d", + "purpose": "l", + "pointOfContact": ["j", "k", "l"], + "projectGroup": "k", "targetRuntime": "node", - "riskLevel": 0, - "productLine": "", - "environmentVars": "d" + "productLine": "m" } diff --git a/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json b/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json index 35842a92..76b11536 100644 --- a/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json +++ b/rush-plugins/rush-metadata-plugin/src/defaultMetadataFields.json @@ -1,10 +1,11 @@ { "metadataFileName": "config/project-metadata.json", + "codeownersFileName": "config/CODEOWNERS", "fields": [ { "name": "purpose", "description": "The purpose of the package.", - "prompt": "What is the purpose of this package?zzz", + "prompt": "What is the purpose of this package?", "fieldType": "string", "required": true }, diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts index 2aa6daee..70681d81 100644 --- a/rush-plugins/rush-metadata-plugin/src/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -29,15 +29,25 @@ async function main(): Promise { } } ) - .command('sync', 'Sync the metadata in the monorepo.', async () => { - const { syncMeta } = await import('./syncMeta'); - try { - await syncMeta(); - } catch (e: any) { - console.error('error: ', e); - process.exit(1); + .command( + 'sync', + 'Sync the metadata in the monorepo.', + (yargs) => { + return yargs.option('codeowners', { + type: 'boolean', + describe: 'option to generate codeowners file' + }); + }, + async ({ codeowners }) => { + const { syncMeta } = await import('./syncMeta'); + try { + await syncMeta({ codeowners: !!codeowners }); + } catch (e: any) { + console.error('error: ', e); + process.exit(1); + } } - }) + ) .demandCommand(1, 'You need at least one command before moving on') .parse(); } diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index f13bf602..f18d3c6c 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -32,7 +32,12 @@ export const initMeta = async ({ project }: { project: string }): Promise const allFields: IMetadataField[] = getAllMetadataFields(); - const answers: Record = await queryFields(allFields); + const answers: Record = await queryFields(allFields); + + if (answers.pointOfContact) { + const enteredPointsOfContact: string = answers.pointOfContact as string; + answers.pointOfContact = enteredPointsOfContact.split(',').map((s) => s.trim()); + } JsonFile.save(answers, metaFilePath, { ensureFolderExists: true }); }; diff --git a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts index 54e0fce5..fe59aa70 100644 --- a/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts +++ b/rush-plugins/rush-metadata-plugin/src/logic/customMeta.ts @@ -14,7 +14,17 @@ export const getDefaultMetadataFileName = (): string => { return metadataFileName; }; -export const getCustomMetadataInfo = (): IPluginConfig => { +export const getDefaultCodeownersFileName = (): string => { + const { codeownersFileName }: { codeownersFileName: string } = DefaultFields; + return codeownersFileName; +}; + +export interface ICustomMetadataInfo { + metadataFileName: string; + codeownersFileName: string; + fields: IMetadataField[]; +} +export const getCustomMetadataInfo = (): ICustomMetadataInfo => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); const pluginOptionsJsonFilePath: string = path.join( @@ -24,6 +34,7 @@ export const getCustomMetadataInfo = (): IPluginConfig => { // Custom configurations for plugin let metadataRelativeFolder: string = getDefaultMetadataFileName(); + let codeownersFileName: string = getDefaultCodeownersFileName(); let customFields: IMetadataField[] = []; let metaConfigs: IPluginConfig | undefined; @@ -38,15 +49,17 @@ export const getCustomMetadataInfo = (): IPluginConfig => { if (metaConfigs.metadataFileName) { metadataRelativeFolder = metaConfigs.metadataFileName; } + if (metaConfigs.codeownersFileName) { + codeownersFileName = metaConfigs.codeownersFileName; + } if (metaConfigs.fields) { customFields = metaConfigs.fields; } } - console.log('custom metadata folder: ', metadataRelativeFolder); - return { metadataFileName: metadataRelativeFolder, + codeownersFileName, fields: customFields }; }; diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts index 348af884..cf8f8aba 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts @@ -9,27 +9,35 @@ import { loadRushConfiguration } from '../logic/rushConfiguration'; import { getCustomMetadataInfo } from '../logic/customMeta'; import { ICoreMetadata } from '../template'; import { syncMetadataFile } from './syncMetadataFile'; +import { outputToCodeowners } from '../transformers/outputToCodeowners'; // Used to sync all the metadata files in the monorepo according to the metadata spec -export const syncMeta = async (): Promise => { +export const syncMeta = async ({ codeowners }: { codeowners: boolean }): Promise => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); for (const rushProject of rushConfiguration.projects) { log('rush project: ', rushProject.projectFolder); // Look for custom plugin configurations - const { metadataFileName } = getCustomMetadataInfo(); + const { metadataFileName, codeownersFileName } = getCustomMetadataInfo(); // Check if metadata file exists already const metaFilePath: string = path.join(rushProject.projectFolder, metadataFileName); + let newMetadataFile: any; if (fs.existsSync(metaFilePath)) { // Read and parse the file const loadedJsonFile: ICoreMetadata = JsonFile.load(metaFilePath); log(chalk.green(`Updating metadata file at: ${metaFilePath}`)); - const newMetadataFile: any = syncMetadataFile(loadedJsonFile); + newMetadataFile = syncMetadataFile(loadedJsonFile); JsonFile.save(newMetadataFile, metaFilePath, { updateExistingFile: true }); + + if (codeowners) { + // Sync this project's POCs to the project's codeowners file + const codeownersAbsoluteFilePath: string = path.join(rushProject.projectFolder, codeownersFileName); + outputToCodeowners(newMetadataFile.pointOfContact, codeownersAbsoluteFilePath); + } } } }; diff --git a/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts b/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts new file mode 100644 index 00000000..eae4e9c6 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts @@ -0,0 +1,6 @@ +export const outputToCodeowners = (pointsOfContact: string[], outputFileLocation: string): void => { + // Output to gitlab format codeowners + + console.log('outputting these POCs to the codeowners file: ', pointsOfContact); + console.log('at location: ', outputFileLocation); +}; diff --git a/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts b/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts index 80892f0b..36a69574 100644 --- a/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts +++ b/rush-plugins/rush-metadata-plugin/src/types/pluginConfig.ts @@ -1,8 +1,9 @@ -import { IMetadataField } from "./metadataField"; +import { IMetadataField } from './metadataField'; export interface IPluginConfig { - metadataFileName: string; + metadataFileName?: string; + codeownersFileName?: string; // Link to the custom schema for this metadata object (generated by this plugin) metadataSchema?: string; fields: IMetadataField[]; -} \ No newline at end of file +} From 18f22c4672c7d4e42a129fadd7302110c2da6cf9 Mon Sep 17 00:00:00 2001 From: William Huang Date: Wed, 14 Jun 2023 15:57:42 -0400 Subject: [PATCH 18/20] feat: output metadata information to readme --- rush-plugins/rush-metadata-plugin/README.md | 47 +++++++++++++++ .../config/project-metadata.json | 3 +- .../src/generateSchema.ts | 10 ---- .../src/logic/convertFieldValue.ts | 27 +++++++++ .../src/syncMeta/index.ts | 4 ++ .../src/transformers/outputToCodeowners.ts | 4 ++ .../src/transformers/outputToReadme.ts | 57 +++++++++++++++++++ .../src/types/metadataField.ts | 2 +- 8 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/README.md delete mode 100644 rush-plugins/rush-metadata-plugin/src/generateSchema.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/logic/convertFieldValue.ts create mode 100644 rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts diff --git a/rush-plugins/rush-metadata-plugin/README.md b/rush-plugins/rush-metadata-plugin/README.md new file mode 100644 index 00000000..b49f9644 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/README.md @@ -0,0 +1,47 @@ +testing test +========== This section is auto-generated by the rush metadata plugin. ========== + +## purpose + +#### The purpose of the package. + + +l + +## pointOfContact + +#### The list of package owners. + + +j,k,l + +## projectGroup + +#### Which project group does this package belong to? + + +k + +## targetRuntime + +#### The environment in which the project will run. + + +node + +## riskLevel + +#### The risk level assigned to this package. + + +2 - High Risk (Production Application) + +## productLine + +#### Which product line this project belongs to + + +m + + +========== End of auto-generated section. ========== \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/config/project-metadata.json b/rush-plugins/rush-metadata-plugin/config/project-metadata.json index 69f9bfd0..fe955498 100644 --- a/rush-plugins/rush-metadata-plugin/config/project-metadata.json +++ b/rush-plugins/rush-metadata-plugin/config/project-metadata.json @@ -3,5 +3,6 @@ "pointOfContact": ["j", "k", "l"], "projectGroup": "k", "targetRuntime": "node", - "productLine": "m" + "productLine": "m", + "riskLevel": 2 } diff --git a/rush-plugins/rush-metadata-plugin/src/generateSchema.ts b/rush-plugins/rush-metadata-plugin/src/generateSchema.ts deleted file mode 100644 index 4a897058..00000000 --- a/rush-plugins/rush-metadata-plugin/src/generateSchema.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IMetadataField } from "./types/metadataField"; - - -// This function takes in the custom fields defined in a specific monorepo and generates -// a JSON schema that can be uploaded to a custom server and referenced in the metadata files. -export const generateSchema = (fields: IMetadataField): void => { - - console.log('generating schema...'); - -} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/logic/convertFieldValue.ts b/rush-plugins/rush-metadata-plugin/src/logic/convertFieldValue.ts new file mode 100644 index 00000000..43328516 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/logic/convertFieldValue.ts @@ -0,0 +1,27 @@ +import { IMetadataField } from '../types/metadataField'; + +export const convertFieldValue = (field: IMetadataField, value: string | number | string[]): string => { + if (Array.isArray(value)) { + return value.join(','); + } + + if (field.fieldType === 'list') { + if (field.choices && typeof field.choices[0] === 'string') { + return String(value); + } + // Try to decode the field value into it's label + + if (field.choices && field.choices.length) { + for (const choice of field.choices as { + name: string; + value: string | number; + }[]) { + if (choice?.value === value) { + return choice.name; + } + } + } + return ''; + } + return String(value); +}; diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts index cf8f8aba..b3d0a192 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts @@ -10,6 +10,7 @@ import { getCustomMetadataInfo } from '../logic/customMeta'; import { ICoreMetadata } from '../template'; import { syncMetadataFile } from './syncMetadataFile'; import { outputToCodeowners } from '../transformers/outputToCodeowners'; +import { outputToReadme } from '../transformers/outputToReadme'; // Used to sync all the metadata files in the monorepo according to the metadata spec export const syncMeta = async ({ codeowners }: { codeowners: boolean }): Promise => { @@ -38,6 +39,9 @@ export const syncMeta = async ({ codeowners }: { codeowners: boolean }): Promise const codeownersAbsoluteFilePath: string = path.join(rushProject.projectFolder, codeownersFileName); outputToCodeowners(newMetadataFile.pointOfContact, codeownersAbsoluteFilePath); } + + const readmeAbsoluteFilePath: string = path.join(rushProject.projectFolder, 'README.md'); + outputToReadme(loadedJsonFile, readmeAbsoluteFilePath); } } }; diff --git a/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts b/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts index eae4e9c6..cb4649c0 100644 --- a/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts +++ b/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts @@ -1,6 +1,10 @@ +import fs from 'fs'; + export const outputToCodeowners = (pointsOfContact: string[], outputFileLocation: string): void => { // Output to gitlab format codeowners console.log('outputting these POCs to the codeowners file: ', pointsOfContact); console.log('at location: ', outputFileLocation); + + fs.writeFileSync(outputFileLocation, pointsOfContact.join(',')); }; diff --git a/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts b/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts new file mode 100644 index 00000000..8b612742 --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts @@ -0,0 +1,57 @@ +import json2md from 'json2md'; +import fs from 'fs'; +import { IMetadataField } from '../types/metadataField'; +import { getAllMetadataFields } from '../logic/customMeta'; +import { convertFieldValue } from '../logic/convertFieldValue'; + +export const MDStartKey: string = + '========== This section is auto-generated by the rush metadata plugin. =========='; +export const MDEndKey: string = '========== End of auto-generated section. =========='; + +export const outputToReadme = (currentValues: any, outputFileLocation: string): void => { + let prevMDContents: string = ''; + let postMDContents: string = ''; + + // Check if readme file already exists + if (fs.existsSync(outputFileLocation)) { + console.log('README file already exists'); + const readmeContents: string = fs.readFileSync(outputFileLocation, 'utf8'); + + // Check if the start and end key exist + if (readmeContents.indexOf(MDStartKey) !== -1 && readmeContents.indexOf(MDEndKey)) { + prevMDContents = readmeContents.slice(0, readmeContents.indexOf(MDStartKey)); + postMDContents = readmeContents.slice(readmeContents.indexOf(MDEndKey) + MDEndKey.length); + + console.log(prevMDContents); + console.log(postMDContents); + } else { + // Preserve previous content in prevMDContents + prevMDContents = readmeContents + '\n'; + } + } + + console.log('Outputting values to readme...'); + console.log('Current values: ', currentValues); + + const allFields: IMetadataField[] = getAllMetadataFields(); + // Generate md file + const mdFileContents: any = [{ p: MDStartKey }]; + + for (const field of allFields) { + let fieldValue: string = ''; + if (!currentValues[field.name]) { + console.log('No value found for field: ', field.name); + continue; + } + fieldValue = convertFieldValue(field, currentValues[field.name]); + mdFileContents.push(...[{ h2: field.name }, { h4: field.description }, { p: fieldValue }]); + } + + mdFileContents.push({ p: MDEndKey }); + + const mdContents: string = json2md(mdFileContents); + + const mdToOutput: string = prevMDContents + mdContents.trim() + postMDContents; + + fs.writeFileSync(outputFileLocation, mdToOutput); +}; diff --git a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts index 40e50d0a..b39570a0 100644 --- a/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts +++ b/rush-plugins/rush-metadata-plugin/src/types/metadataField.ts @@ -12,6 +12,6 @@ export interface IMetadataField { prompt: string; fieldType: FieldTypes; required: boolean; - choices?: string[] | { label: string; value: string | number }[]; + choices?: string[] | { name: string; value: string | number }[]; defaultValue?: string; } From 69a17a8bcd0a5dfa0692d9c72ee88d348d7ebdb3 Mon Sep 17 00:00:00 2001 From: William Huang Date: Thu, 15 Jun 2023 15:40:37 -0400 Subject: [PATCH 19/20] feat: clean up MR --- rush-plugins/rush-metadata-plugin/README.md | 18 ++--- .../rush-metadata-plugin/command-line.json | 7 -- .../config/project-metadata.json | 11 +-- .../rush-metadata-plugin/src/index.ts | 5 +- .../src/initMeta/index.ts | 4 + .../src/initMeta/queryFields.ts | 28 ++++--- .../src/logic/convertFieldValue.ts | 5 +- .../src/logic/generateMD.ts | 26 ------- .../rush-metadata-plugin/src/logic/git.ts | 78 ------------------- .../src/logic/graveyard.ts | 32 -------- .../src/logic/promptRushUpdate.ts | 46 ----------- .../src/syncMeta/index.ts | 11 +-- .../src/syncMeta/syncMetadataFile.ts | 2 - .../src/transformers/outputToCodeowners.ts | 10 --- .../src/transformers/outputToReadme.ts | 16 ++-- 15 files changed, 42 insertions(+), 257 deletions(-) delete mode 100644 rush-plugins/rush-metadata-plugin/src/logic/generateMD.ts delete mode 100644 rush-plugins/rush-metadata-plugin/src/logic/git.ts delete mode 100644 rush-plugins/rush-metadata-plugin/src/logic/graveyard.ts delete mode 100644 rush-plugins/rush-metadata-plugin/src/logic/promptRushUpdate.ts delete mode 100644 rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts diff --git a/rush-plugins/rush-metadata-plugin/README.md b/rush-plugins/rush-metadata-plugin/README.md index b49f9644..07e39c00 100644 --- a/rush-plugins/rush-metadata-plugin/README.md +++ b/rush-plugins/rush-metadata-plugin/README.md @@ -1,26 +1,29 @@ testing test ========== This section is auto-generated by the rush metadata plugin. ========== + +This section is auto-generated and will be auto-updated. Anything added manually outside this section will be preserved. + ## purpose #### The purpose of the package. -l +This package initializes and syncs metadata files in a monorepo. ## pointOfContact #### The list of package owners. -j,k,l +william2958 ## projectGroup #### Which project group does this package belong to? -k +rush-plugins ## targetRuntime @@ -29,19 +32,12 @@ k node -## riskLevel - -#### The risk level assigned to this package. - - -2 - High Risk (Production Application) - ## productLine #### Which product line this project belongs to -m +libs ========== End of auto-generated section. ========== \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/command-line.json b/rush-plugins/rush-metadata-plugin/command-line.json index 80290e0c..efc9907e 100644 --- a/rush-plugins/rush-metadata-plugin/command-line.json +++ b/rush-plugins/rush-metadata-plugin/command-line.json @@ -25,13 +25,6 @@ "argumentName": "PACKAGE_NAME", "associatedCommands": ["meta-init"], "required": false - }, - { - "parameterKind": "flag", - "description": "Whether to output a codeowners file", - "longName": "--codeowners", - "associatedCommands": ["meta-init", "meta-sync"], - "required": false } ] } diff --git a/rush-plugins/rush-metadata-plugin/config/project-metadata.json b/rush-plugins/rush-metadata-plugin/config/project-metadata.json index fe955498..9a6e70cc 100644 --- a/rush-plugins/rush-metadata-plugin/config/project-metadata.json +++ b/rush-plugins/rush-metadata-plugin/config/project-metadata.json @@ -1,8 +1,9 @@ { - "purpose": "l", - "pointOfContact": ["j", "k", "l"], - "projectGroup": "k", + "purpose": "This package initializes and syncs metadata files in a monorepo.", + "pointOfContact": ["william2958"], + "projectGroup": "rush-plugins", "targetRuntime": "node", - "productLine": "m", - "riskLevel": 2 + "riskLevel": 0, + "productLine": "libs", + "environmentVars": "" } diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts index 70681d81..cbe933c4 100644 --- a/rush-plugins/rush-metadata-plugin/src/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -19,7 +19,6 @@ async function main(): Promise { .demandOption(['project']); }, async (argv) => { - console.log('ARGUMENTS: ', argv); const { initMeta } = await import('./initMeta'); try { await initMeta(argv); @@ -38,10 +37,10 @@ async function main(): Promise { describe: 'option to generate codeowners file' }); }, - async ({ codeowners }) => { + async () => { const { syncMeta } = await import('./syncMeta'); try { - await syncMeta({ codeowners: !!codeowners }); + await syncMeta(); } catch (e: any) { console.error('error: ', e); process.exit(1); diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index f18d3c6c..7d9e69d1 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -9,6 +9,7 @@ import { getAllMetadataFields, getCustomMetadataInfo } from '../logic/customMeta import { IMetadataField } from '../types/metadataField'; import { queryFields } from './queryFields'; import { JsonFile } from '@rushstack/node-core-library'; +import { outputToReadme } from '../transformers/outputToReadme'; // Used to initialize a metadata file for a package export const initMeta = async ({ project }: { project: string }): Promise => { @@ -40,4 +41,7 @@ export const initMeta = async ({ project }: { project: string }): Promise } JsonFile.save(answers, metaFilePath, { ensureFolderExists: true }); + + const readmeAbsoluteFilePath: string = path.join(rushProject.projectFolder, 'README.md'); + outputToReadme(answers, readmeAbsoluteFilePath); }; diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/queryFields.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/queryFields.ts index d2bcebd3..8bce23d9 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/queryFields.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/queryFields.ts @@ -1,23 +1,22 @@ -import inquirer from "inquirer"; -import { IMetadataField } from "../types/metadataField"; +import inquirer from 'inquirer'; +import { IMetadataField } from '../types/metadataField'; export const queryFields = async (fields: IMetadataField[]): Promise> => { - const inquirerQuestions: any = []; for (const field of fields) { const question: any = { name: field.name, message: field.prompt, - type: "input" + type: 'input' }; - switch(field.fieldType) { - case "string": - case "number": - question.type = "input"; + switch (field.fieldType) { + case 'string': + case 'number': + question.type = 'input'; break; - case "list": - question.type = "list"; + case 'list': + question.type = 'list'; question.choices = field.choices || []; break; } @@ -25,19 +24,18 @@ export const queryFields = async (fields: IMetadataField[]): Promise { -// Try saving a md file of this json file - const mdFileContents: any = [ - { h2: "Archived Projects" } - ]; - const tableRows: any = [] - for (const [projectName, projectMetadata] of Object.entries(archivedProjectMetadataObject)) { - tableRows.push([ - projectName, - projectMetadata.checkpointBranch, - projectMetadata.description, - projectMetadata.archivedOn - ]) - } - mdFileContents.push({ - table: { - headers: ["Project Name", "Checkpoint Branch", "Description", "Archive Date"], - rows: tableRows - } - }) - return json2md(mdFileContents); -} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/logic/git.ts b/rush-plugins/rush-metadata-plugin/src/logic/git.ts deleted file mode 100644 index 082fdd54..00000000 --- a/rush-plugins/rush-metadata-plugin/src/logic/git.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Executable } from "@rushstack/node-core-library"; - -import type { SpawnSyncReturns } from "child_process"; - -let checkedGitPath: boolean = false; -let gitPath: string | undefined; - -export const getGitPath = (): string | undefined => { - if (!checkedGitPath) { - checkedGitPath = true; - gitPath = Executable.tryResolve("git"); - } - return gitPath; -}; - -export const getGitPathOrThrow = (): string => { - const gitPath: string | undefined = getGitPath(); - if (!gitPath) { - throw new Error("Unable to find git."); - } - return gitPath; -}; - -export const gitFullClean = (cwd: string): void => { - const gitPath: string = getGitPathOrThrow(); - Executable.spawnSync(gitPath, ["clean", "-fdx"], { - currentWorkingDirectory: cwd, - }); -}; - -export const gitCheckIgnored = (cwd: string, filePath: string): string => { - const gitPath: string = getGitPathOrThrow(); - let result: string = ""; - try { - const process: SpawnSyncReturns = Executable.spawnSync( - gitPath, - ["check-ignore", "-v", filePath], - { - currentWorkingDirectory: cwd, - } - ); - if (process.status === 0) { - result = process.stdout.toString(); - } - } catch (e: any) { - if (e.message.includes("The command failed with exit code 1")) { - // ignore - } else { - // rethrow - throw e; - } - } - return result; -}; - -export const getCheckpointBranch = (cwd: string, branchName: string): string => { - const gitPath: string = getGitPathOrThrow(); - const currDate: string = new Date().toISOString().substring(0, 10); - const branchNameToCreate: string = `${branchName}-checkpoint-${currDate}`; - const process: SpawnSyncReturns = Executable.spawnSync(gitPath, ["branch", branchNameToCreate], { - currentWorkingDirectory: cwd, - }); - if (process.status === 128) { - throw new Error(`The passed branch name is invalid: ${branchNameToCreate}`) - } - - return branchNameToCreate; -} - -export const pushGitBranch = (cwd: string, branchName: string): void => { - const gitPath: string = getGitPathOrThrow(); - const process: SpawnSyncReturns = Executable.spawnSync(gitPath, ["push", "origin", `${branchName}:${branchName}`], { - currentWorkingDirectory: cwd, - }); - if (process.status !== 0) { - console.error('Could not push branch to origin'); - } -} \ No newline at end of file diff --git a/rush-plugins/rush-metadata-plugin/src/logic/graveyard.ts b/rush-plugins/rush-metadata-plugin/src/logic/graveyard.ts deleted file mode 100644 index bec08a0a..00000000 --- a/rush-plugins/rush-metadata-plugin/src/logic/graveyard.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as path from "path"; -import { PackageName, IParsedPackageName } from "@rushstack/node-core-library"; - -export const graveyardRelativeFolder: string = "common/_graveyard"; - -export interface IGraveyardInfo { - tarballFolder: string; - tarballRelativeFolder: string; - tarballName: string; -} - -export interface IGetGraveyardInfoParams { - monoRoot: string; - packageName: string; -} - -export const getGraveyardInfo = ({ - monoRoot, - packageName, -}: IGetGraveyardInfoParams): IGraveyardInfo => { - const parsedPackageName: IParsedPackageName = PackageName.parse(packageName); - const tarballRelativeFolder: string = path.join( - graveyardRelativeFolder, - parsedPackageName.scope - ); - const tarballFolder: string = path.join(monoRoot, tarballRelativeFolder); - return { - tarballFolder, - tarballRelativeFolder, - tarballName: `${parsedPackageName.unscopedName}.tar.gz`, - }; -}; diff --git a/rush-plugins/rush-metadata-plugin/src/logic/promptRushUpdate.ts b/rush-plugins/rush-metadata-plugin/src/logic/promptRushUpdate.ts deleted file mode 100644 index 8aa36e1e..00000000 --- a/rush-plugins/rush-metadata-plugin/src/logic/promptRushUpdate.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { RushConfiguration } from "@rushstack/rush-sdk"; -import type { ITerminal } from "@rushstack/node-core-library"; -import { Executable } from "@rushstack/node-core-library"; -import * as path from "path"; -import { loadRushConfiguration } from "./rushConfiguration"; - -export interface IPromptRushUpdateParams { - terminal: ITerminal; -} - -export async function promptRushUpdate({ - terminal, -}: IPromptRushUpdateParams): Promise { - const { prompt } = await import("inquirer"); - interface IShouldRunRushUpdateAnswer { - shouldRunRushUpdate: boolean; - } - const rushConfiguration: RushConfiguration = loadRushConfiguration(); - const runRushJSPath: string = path.join( - rushConfiguration.commonScriptsFolder, - "install-run-rush.js" - ); - - const { shouldRunRushUpdate } = await prompt([ - { - type: "confirm", - name: "shouldRunRushUpdate", - message: "Run rush update right now?", - default: true, - }, - ]); - - if (shouldRunRushUpdate) { - terminal.writeLine("Run rush update..."); - try { - Executable.spawnSync("node", [runRushJSPath, "update"], { - stdio: "inherit", - }); - } catch (e) { - terminal.writeErrorLine("Rush update failed, please run it manually."); - } - terminal.writeLine("Rush update successfully."); - } else { - terminal.writeWarningLine("Rush update skipped, please run it manually."); - } -} diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts index b3d0a192..8074b4f6 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/index.ts @@ -9,17 +9,16 @@ import { loadRushConfiguration } from '../logic/rushConfiguration'; import { getCustomMetadataInfo } from '../logic/customMeta'; import { ICoreMetadata } from '../template'; import { syncMetadataFile } from './syncMetadataFile'; -import { outputToCodeowners } from '../transformers/outputToCodeowners'; import { outputToReadme } from '../transformers/outputToReadme'; // Used to sync all the metadata files in the monorepo according to the metadata spec -export const syncMeta = async ({ codeowners }: { codeowners: boolean }): Promise => { +export const syncMeta = async (): Promise => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); for (const rushProject of rushConfiguration.projects) { log('rush project: ', rushProject.projectFolder); // Look for custom plugin configurations - const { metadataFileName, codeownersFileName } = getCustomMetadataInfo(); + const { metadataFileName } = getCustomMetadataInfo(); // Check if metadata file exists already const metaFilePath: string = path.join(rushProject.projectFolder, metadataFileName); @@ -34,12 +33,6 @@ export const syncMeta = async ({ codeowners }: { codeowners: boolean }): Promise JsonFile.save(newMetadataFile, metaFilePath, { updateExistingFile: true }); - if (codeowners) { - // Sync this project's POCs to the project's codeowners file - const codeownersAbsoluteFilePath: string = path.join(rushProject.projectFolder, codeownersFileName); - outputToCodeowners(newMetadataFile.pointOfContact, codeownersAbsoluteFilePath); - } - const readmeAbsoluteFilePath: string = path.join(rushProject.projectFolder, 'README.md'); outputToReadme(loadedJsonFile, readmeAbsoluteFilePath); } diff --git a/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts b/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts index 8d9dc252..ec0f5d06 100644 --- a/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts +++ b/rush-plugins/rush-metadata-plugin/src/syncMeta/syncMetadataFile.ts @@ -6,8 +6,6 @@ export const syncMetadataFile = (currMetadata: any): ICoreMetadata => { const allFields: IMetadataField[] = getAllMetadataFields(); const newMetadata: any = {}; - console.log('syncing file: ', currMetadata); - for (const field of allFields) { // @ts-ignore if (field.required && !currMetadata[field.name]) { diff --git a/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts b/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts deleted file mode 100644 index cb4649c0..00000000 --- a/rush-plugins/rush-metadata-plugin/src/transformers/outputToCodeowners.ts +++ /dev/null @@ -1,10 +0,0 @@ -import fs from 'fs'; - -export const outputToCodeowners = (pointsOfContact: string[], outputFileLocation: string): void => { - // Output to gitlab format codeowners - - console.log('outputting these POCs to the codeowners file: ', pointsOfContact); - console.log('at location: ', outputFileLocation); - - fs.writeFileSync(outputFileLocation, pointsOfContact.join(',')); -}; diff --git a/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts b/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts index 8b612742..436e908f 100644 --- a/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts +++ b/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts @@ -14,33 +14,29 @@ export const outputToReadme = (currentValues: any, outputFileLocation: string): // Check if readme file already exists if (fs.existsSync(outputFileLocation)) { - console.log('README file already exists'); const readmeContents: string = fs.readFileSync(outputFileLocation, 'utf8'); - // Check if the start and end key exist if (readmeContents.indexOf(MDStartKey) !== -1 && readmeContents.indexOf(MDEndKey)) { prevMDContents = readmeContents.slice(0, readmeContents.indexOf(MDStartKey)); postMDContents = readmeContents.slice(readmeContents.indexOf(MDEndKey) + MDEndKey.length); - - console.log(prevMDContents); - console.log(postMDContents); } else { // Preserve previous content in prevMDContents prevMDContents = readmeContents + '\n'; } } - console.log('Outputting values to readme...'); - console.log('Current values: ', currentValues); - const allFields: IMetadataField[] = getAllMetadataFields(); // Generate md file - const mdFileContents: any = [{ p: MDStartKey }]; + const mdFileContents: any = [ + { p: MDStartKey }, + { + p: 'This section is auto-generated and will be auto-updated. Anything added manually outside this section will be preserved.' + } + ]; for (const field of allFields) { let fieldValue: string = ''; if (!currentValues[field.name]) { - console.log('No value found for field: ', field.name); continue; } fieldValue = convertFieldValue(field, currentValues[field.name]); From 22fe61ac6457fcaba248d9af3ea090f43f7f9852 Mon Sep 17 00:00:00 2001 From: William Huang Date: Fri, 30 Jun 2023 09:59:10 -0600 Subject: [PATCH 20/20] feat: add ability to initialize all metada ta files at once --- .../rush-metadata-plugin/command-line.json | 6 +- rush-plugins/rush-metadata-plugin/README.md | 11 ++- .../rush-metadata-plugin/command-line.json | 7 ++ .../config/project-metadata.json | 4 +- .../rush-metadata-plugin/src/index.ts | 5 +- .../src/initMeta/index.ts | 68 ++++++++----------- .../src/initMeta/initMetaForProject.ts | 50 ++++++++++++++ .../src/transformers/outputToReadme.ts | 8 ++- 8 files changed, 107 insertions(+), 52 deletions(-) create mode 100644 rush-plugins/rush-metadata-plugin/src/initMeta/initMetaForProject.ts diff --git a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json index 80290e0c..94e066c6 100644 --- a/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json +++ b/common/autoinstallers/command-plugins/rush-plugins/rush-metadata-plugin/rush-metadata-plugin/command-line.json @@ -28,9 +28,9 @@ }, { "parameterKind": "flag", - "description": "Whether to output a codeowners file", - "longName": "--codeowners", - "associatedCommands": ["meta-init", "meta-sync"], + "description": "Generate a metadata JSON for each project in the monorepo", + "longName": "--all", + "associatedCommands": ["meta-init"], "required": false } ] diff --git a/rush-plugins/rush-metadata-plugin/README.md b/rush-plugins/rush-metadata-plugin/README.md index 07e39c00..e1f92a56 100644 --- a/rush-plugins/rush-metadata-plugin/README.md +++ b/rush-plugins/rush-metadata-plugin/README.md @@ -1,4 +1,3 @@ -testing test ========== This section is auto-generated by the rush metadata plugin. ========== @@ -6,35 +5,35 @@ This section is auto-generated and will be auto-updated. Anything added manually ## purpose -#### The purpose of the package. +#### The purpose of the package. This package initializes and syncs metadata files in a monorepo. ## pointOfContact -#### The list of package owners. +#### The list of package owners. william2958 ## projectGroup -#### Which project group does this package belong to? +#### Which project group does this package belong to? rush-plugins ## targetRuntime -#### The environment in which the project will run. +#### The environment in which the project will run. node ## productLine -#### Which product line this project belongs to +#### Which product line this project belongs to libs diff --git a/rush-plugins/rush-metadata-plugin/command-line.json b/rush-plugins/rush-metadata-plugin/command-line.json index efc9907e..94e066c6 100644 --- a/rush-plugins/rush-metadata-plugin/command-line.json +++ b/rush-plugins/rush-metadata-plugin/command-line.json @@ -25,6 +25,13 @@ "argumentName": "PACKAGE_NAME", "associatedCommands": ["meta-init"], "required": false + }, + { + "parameterKind": "flag", + "description": "Generate a metadata JSON for each project in the monorepo", + "longName": "--all", + "associatedCommands": ["meta-init"], + "required": false } ] } diff --git a/rush-plugins/rush-metadata-plugin/config/project-metadata.json b/rush-plugins/rush-metadata-plugin/config/project-metadata.json index 9a6e70cc..103e5e00 100644 --- a/rush-plugins/rush-metadata-plugin/config/project-metadata.json +++ b/rush-plugins/rush-metadata-plugin/config/project-metadata.json @@ -3,7 +3,5 @@ "pointOfContact": ["william2958"], "projectGroup": "rush-plugins", "targetRuntime": "node", - "riskLevel": 0, - "productLine": "libs", - "environmentVars": "" + "productLine": "libs" } diff --git a/rush-plugins/rush-metadata-plugin/src/index.ts b/rush-plugins/rush-metadata-plugin/src/index.ts index cbe933c4..65937ccf 100644 --- a/rush-plugins/rush-metadata-plugin/src/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/index.ts @@ -16,7 +16,10 @@ async function main(): Promise { type: 'string', describe: 'The name of the package to archive' }) - .demandOption(['project']); + .option('all', { + type: 'boolean', + describe: 'Whether to initialize a basic metadata file for each project in the monorepo' + }); }, async (argv) => { const { initMeta } = await import('./initMeta'); diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts index 7d9e69d1..ab9d6560 100644 --- a/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/index.ts @@ -1,47 +1,39 @@ -import fs from 'fs'; -import path from 'path'; - import { RushConfiguration, RushConfigurationProject } from '@rushstack/rush-sdk'; import { loadRushConfiguration } from '../logic/rushConfiguration'; -import { log } from 'console'; +import { initMetaForProject } from './initMetaForProject'; import chalk from 'chalk'; -import { getAllMetadataFields, getCustomMetadataInfo } from '../logic/customMeta'; -import { IMetadataField } from '../types/metadataField'; -import { queryFields } from './queryFields'; -import { JsonFile } from '@rushstack/node-core-library'; -import { outputToReadme } from '../transformers/outputToReadme'; +import path from 'path'; +import fs from 'fs'; +import { getCustomMetadataInfo } from '../logic/customMeta'; // Used to initialize a metadata file for a package -export const initMeta = async ({ project }: { project: string }): Promise => { +export const initMeta = async ({ project, all }: { project?: string; all?: boolean }): Promise => { const rushConfiguration: RushConfiguration = loadRushConfiguration(); - const rushProject: RushConfigurationProject | undefined = rushConfiguration.getProjectByName(project); - if (!rushProject) { - throw new Error(`Could not find project with package name ${project}`); - } - const { projectFolder } = rushProject; - - // Look for custom plugin configurations - const { metadataFileName } = getCustomMetadataInfo(); - // Check if metadata file exists already - const metaFilePath: string = path.join(projectFolder, metadataFileName); - - if (fs.existsSync(metaFilePath)) { - log(chalk.red('Please run rush meta update or edit the metadata file directly to make updates')); - return; + if (project) { + const rushProject: RushConfigurationProject | undefined = rushConfiguration.getProjectByName(project); + if (!rushProject) { + throw new Error(`Could not find project with package name ${project}`); + } + await initMetaForProject(rushProject, true); + } else if (all) { + const allRushProjects: RushConfigurationProject[] = rushConfiguration.projects; + // Look for custom plugin configurations + const { metadataFileName } = getCustomMetadataInfo(); + for (const rushProject of allRushProjects) { + // Check if metadata file exists already + const { projectFolder } = rushProject; + const metaFilePath: string = path.join(projectFolder, metadataFileName); + if (!fs.existsSync(metaFilePath)) { + // Only initialize for projects that don't already have a metadata file + await initMetaForProject(rushProject, false); + } + } + } else { + console.log( + chalk.red( + 'Please specify a project with the --project parameter, or --all to initialize metadata for all packages.' + ) + ); } - - const allFields: IMetadataField[] = getAllMetadataFields(); - - const answers: Record = await queryFields(allFields); - - if (answers.pointOfContact) { - const enteredPointsOfContact: string = answers.pointOfContact as string; - answers.pointOfContact = enteredPointsOfContact.split(',').map((s) => s.trim()); - } - - JsonFile.save(answers, metaFilePath, { ensureFolderExists: true }); - - const readmeAbsoluteFilePath: string = path.join(rushProject.projectFolder, 'README.md'); - outputToReadme(answers, readmeAbsoluteFilePath); }; diff --git a/rush-plugins/rush-metadata-plugin/src/initMeta/initMetaForProject.ts b/rush-plugins/rush-metadata-plugin/src/initMeta/initMetaForProject.ts new file mode 100644 index 00000000..680e218a --- /dev/null +++ b/rush-plugins/rush-metadata-plugin/src/initMeta/initMetaForProject.ts @@ -0,0 +1,50 @@ +import fs from 'fs'; +import path from 'path'; +import chalk from 'chalk'; + +import { RushConfigurationProject } from '@rushstack/rush-sdk'; +import { getAllMetadataFields, getCustomMetadataInfo } from '../logic/customMeta'; +import { IMetadataField } from '../types/metadataField'; +import { queryFields } from './queryFields'; +import { JsonFile } from '@rushstack/node-core-library'; +import { outputToReadme } from '../transformers/outputToReadme'; + +export const initMetaForProject = async ( + rushProject: RushConfigurationProject, + queryForAnswers: boolean +): Promise => { + const { projectFolder } = rushProject; + + // Look for custom plugin configurations + const { metadataFileName } = getCustomMetadataInfo(); + + // Check if metadata file exists already + const metaFilePath: string = path.join(projectFolder, metadataFileName); + + if (fs.existsSync(metaFilePath)) { + console.log(chalk.red('Please run rush meta update or edit the metadata file directly to make updates')); + return; + } + + const allFields: IMetadataField[] = getAllMetadataFields(); + + let answers: Record; + if (queryForAnswers) { + answers = await queryFields(allFields); + + if (answers.pointOfContact) { + const enteredPointsOfContact: string = answers.pointOfContact as string; + answers.pointOfContact = enteredPointsOfContact.split(',').map((s) => s.trim()); + } + } else { + answers = {}; + for (const field of allFields) { + answers[field.name] = ''; + } + } + + JsonFile.save(answers, metaFilePath, { ensureFolderExists: true }); + + const readmeAbsoluteFilePath: string = path.join(rushProject.projectFolder, 'README.md'); + outputToReadme(answers, readmeAbsoluteFilePath); +}; diff --git a/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts b/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts index 436e908f..7e19cfe9 100644 --- a/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts +++ b/rush-plugins/rush-metadata-plugin/src/transformers/outputToReadme.ts @@ -40,7 +40,13 @@ export const outputToReadme = (currentValues: any, outputFileLocation: string): continue; } fieldValue = convertFieldValue(field, currentValues[field.name]); - mdFileContents.push(...[{ h2: field.name }, { h4: field.description }, { p: fieldValue }]); + mdFileContents.push( + ...[ + { h2: field.name }, + { h4: `${field.description}` }, + { p: fieldValue } + ] + ); } mdFileContents.push({ p: MDEndKey });