diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..c1b161af --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,74 @@ +name: Release Pipeline + +on: + push: + branches: + - '**/*' + +jobs: + publish: + strategy: + matrix: + include: + - NodeVersion: 20.14.x + NodeVersionDisplayName: 20 + OS: ubuntu-latest + name: Node.js v${{ matrix.NodeVersionDisplayName }} (${{ matrix.OS }}) + runs-on: ${{ matrix.OS }} + if: github.repository == 'coze-dev/rush-arch' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check Release Tag + id: check_tag + run: | + HAS_VALID_TAG=$(git tag --points-at HEAD | grep -E "^v/.+/.+$" || true) + if [ ! -z "$HAS_VALID_TAG" ]; then + echo "has_valid_tag=true" >> $GITHUB_OUTPUT + echo "Found valid tag: $HAS_VALID_TAG" + else + echo "has_valid_tag=false" >> $GITHUB_OUTPUT + echo "No valid tag found" + fi + + - name: Config Git User + if: steps.check_tag.outputs.has_valid_tag == 'true' + run: | + git config --local user.name "tecvan" + git config --local user.email "tecvan.fe@gmail.com" + + - uses: actions/setup-node@v3 + if: steps.check_tag.outputs.has_valid_tag == 'true' + with: + node-version: ${{ matrix.NodeVersion }} + registry-url: 'https://registry.npmjs.org' + node-version-file: '.nvmrc' + + - name: Cache + if: steps.check_tag.outputs.has_valid_tag == 'true' + uses: actions/cache@v4 + with: + path: | + common/temp/pnpm-local + common/temp/pnpm-store + common/temp/install-run + key: ${{ runner.os }}-rush-store-${{ hashFiles('common/config/subspaces/**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-rush-store-main + ${{ runner.os }}-rush-store + + - name: Install Dependencies + if: steps.check_tag.outputs.has_valid_tag == 'true' + run: | + npm i -g @microsoft/rush@5.150.0 + sudo apt-get update + sudo apt-get install -y libasound2-dev + node common/scripts/install-run-rush.js install + + - name: Run Release + if: steps.check_tag.outputs.has_valid_tag == 'true' + run: node common/scripts/install-run-rush.js release --commit ${{ github.event.head_commit.id }} + env: + NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} diff --git a/.vscode/settings.json b/.vscode/settings.json index 4d768eae..7e17a770 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -33,12 +33,15 @@ "**/.git": true, "**/.DS_Store": true, "**/.swc": true, + "**/rush-logs": true, "**/.rush": true }, "files.watcherExclude": { + "**/rush-logs": true, "**/.git/objects/**": true, "**/.git/subtree-cache/**": true, - "**/node_modules/*/**": true + "**/node_modules/*/**": true, + "common/temp": true }, "files.defaultLanguage": "plaintext", "files.associations": { @@ -53,7 +56,9 @@ "CODEOWNERS": "ini", "**/coverage/**/*.*": "plaintext", "**/pnpm-lock.yaml": "plaintext", - "**/*.yml": "yaml" + "**/*.yml": "yaml", + "**/.npmrc": "ini", + "**/.npmrc-*": "ini" }, "search.useIgnoreFiles": true, "files.eol": "\n", diff --git a/README.md b/README.md index 1659f728..3610f986 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Rush Architecture is a monorepo template for building and managing Model Context You can directly use the NPM MCP Server without cloning this repository: ```sh -npx @coze-infra/npm-mcp-server@latest serve +npx @coze-arch/npm-mcp-server@latest serve ``` This will start the MCP server on port 3000 by default. You can access it at `http://localhost:3000/sse`. @@ -74,7 +74,7 @@ The repository includes an MCP server for npm. You can run it in two modes: #### HTTP Server Mode Using npx: ```sh -npx @coze-infra/npm-mcp-server@latest serve +npx @coze-arch/npm-mcp-server@latest serve # Access the server at http://localhost:3000/sse ``` @@ -87,7 +87,7 @@ npm run serve #### Terminal Mode Using npx: ```sh -npx @coze-infra/npm-mcp-server@latest start +npx @coze-arch/npm-mcp-server@latest start ``` Or if developing locally: diff --git a/common/_templates/component-core/eslint.config.js b/common/_templates/component-core/eslint.config.js index d97961e1..c272da55 100644 --- a/common/_templates/component-core/eslint.config.js +++ b/common/_templates/component-core/eslint.config.js @@ -1,4 +1,4 @@ -const { defineConfig } = require('@coze-infra/eslint-config'); +const { defineConfig } = require('@coze-arch/eslint-config'); module.exports = defineConfig({ packageRoot: __dirname, diff --git a/common/_templates/component-core/package.json b/common/_templates/component-core/package.json index 0c35f85a..59f14791 100644 --- a/common/_templates/component-core/package.json +++ b/common/_templates/component-core/package.json @@ -15,9 +15,9 @@ "dependencies": { }, "devDependencies": { - "@coze-infra/eslint-config": "workspace:*", - "@coze-infra/ts-config": "workspace:*", - "@coze-infra/vitest-config": "workspace:*", + "@coze-arch/eslint-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", + "@coze-arch/vitest-config": "workspace:*", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@testing-library/react-hooks": "^8.0.1", diff --git a/common/_templates/component-core/tsconfig.build.json b/common/_templates/component-core/tsconfig.build.json index 5e8c4e39..ef439f79 100644 --- a/common/_templates/component-core/tsconfig.build.json +++ b/common/_templates/component-core/tsconfig.build.json @@ -1,5 +1,5 @@ { - "extends": "@coze-infra/ts-config/tsconfig.web.json", + "extends": "@coze-arch/ts-config/tsconfig.web.json", "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "outDir": "dist", diff --git a/common/_templates/component-core/vitest.config.ts b/common/_templates/component-core/vitest.config.ts index f6a2ce13..c8378dcd 100644 --- a/common/_templates/component-core/vitest.config.ts +++ b/common/_templates/component-core/vitest.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from '@coze-infra/vitest-config'; +import { defineConfig } from '@coze-arch/vitest-config'; export default defineConfig({ dirname: __dirname, diff --git a/common/_templates/node-core/eslint.config.js b/common/_templates/node-core/eslint.config.js index eb14595e..8a3bd050 100644 --- a/common/_templates/node-core/eslint.config.js +++ b/common/_templates/node-core/eslint.config.js @@ -1,4 +1,4 @@ -const { defineConfig } = require('@coze-infra/eslint-config'); +const { defineConfig } = require('@coze-arch/eslint-config'); module.exports = defineConfig({ packageRoot: __dirname, diff --git a/common/_templates/node-core/package.json b/common/_templates/node-core/package.json index 9794a782..8b6c4b65 100644 --- a/common/_templates/node-core/package.json +++ b/common/_templates/node-core/package.json @@ -14,12 +14,12 @@ }, "dependencies": {}, "devDependencies": { - "@coze-infra/eslint-config": "workspace:*", - "@coze-infra/ts-config": "workspace:*", - "@coze-infra/vitest-config": "workspace:*", + "@coze-arch/eslint-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", + "@coze-arch/vitest-config": "workspace:*", "@types/node": "^22.13.13", "@vitest/coverage-v8": "^3.0.9", - "tsx": "^4.19.2", + "tsx": "^4.19.3", "vitest": "^3.0.9" } } diff --git a/common/_templates/node-core/tsconfig.build.json b/common/_templates/node-core/tsconfig.build.json index 98c73517..6086f3cd 100644 --- a/common/_templates/node-core/tsconfig.build.json +++ b/common/_templates/node-core/tsconfig.build.json @@ -1,5 +1,5 @@ { - "extends": "@coze-infra/ts-config/tsconfig.node.json", + "extends": "@coze-arch/ts-config/tsconfig.node.json", "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "outDir": "dist", diff --git a/common/_templates/node-core/tsconfig.misc.json b/common/_templates/node-core/tsconfig.misc.json index 19f2c65d..553ee729 100644 --- a/common/_templates/node-core/tsconfig.misc.json +++ b/common/_templates/node-core/tsconfig.misc.json @@ -1,5 +1,5 @@ { - "extends": "@coze-infra/ts-config/tsconfig.node.json", + "extends": "@coze-arch/ts-config/tsconfig.node.json", "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "rootDir": "./", diff --git a/common/_templates/node-core/vitest.config.ts b/common/_templates/node-core/vitest.config.ts index 310bb227..5c7b91ac 100644 --- a/common/_templates/node-core/vitest.config.ts +++ b/common/_templates/node-core/vitest.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from '@coze-infra/vitest-config'; +import { defineConfig } from '@coze-arch/vitest-config'; export default defineConfig({ dirname: __dirname, diff --git a/common/_templates/rush-plugin/README.md b/common/_templates/rush-plugin/README.md new file mode 100644 index 00000000..4cc7fab1 --- /dev/null +++ b/common/_templates/rush-plugin/README.md @@ -0,0 +1 @@ +# {{ packageName }} \ No newline at end of file diff --git a/common/_templates/rush-plugin/__tests__/.gitkeep b/common/_templates/rush-plugin/__tests__/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/common/_templates/rush-plugin/command-line.json b/common/_templates/rush-plugin/command-line.json new file mode 100644 index 00000000..3a9ada9c --- /dev/null +++ b/common/_templates/rush-plugin/command-line.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [ + ], + "parameters": [] +} diff --git a/common/_templates/rush-plugin/config/rush-project.json b/common/_templates/rush-plugin/config/rush-project.json new file mode 100644 index 00000000..1c23b14b --- /dev/null +++ b/common/_templates/rush-plugin/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "test:cov", + "outputFolderNames": ["coverage"] + }, + { + "operationName": "ts-check", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/common/_templates/rush-plugin/eslint.config.js b/common/_templates/rush-plugin/eslint.config.js new file mode 100644 index 00000000..8a3bd050 --- /dev/null +++ b/common/_templates/rush-plugin/eslint.config.js @@ -0,0 +1,7 @@ +const { defineConfig } = require('@coze-arch/eslint-config'); + +module.exports = defineConfig({ + packageRoot: __dirname, + preset: 'node', + rules: {}, +}); diff --git a/common/_templates/rush-plugin/init.config.ts b/common/_templates/rush-plugin/init.config.ts new file mode 100644 index 00000000..75aca4c8 --- /dev/null +++ b/common/_templates/rush-plugin/init.config.ts @@ -0,0 +1,11 @@ +import type { IConfig } from '../../autoinstallers/plugins/node_modules/rush-init-project-plugin'; +import SetDefaultAuthorPlugin from '../_plugins/SetDefaultAuthorPlugin'; + +const config: IConfig = { + plugins: [new SetDefaultAuthorPlugin()], + defaultProjectConfiguration: { + tags:['level-3'] + } +}; + +export default config; diff --git a/common/_templates/rush-plugin/package.json b/common/_templates/rush-plugin/package.json new file mode 100644 index 00000000..e98efd58 --- /dev/null +++ b/common/_templates/rush-plugin/package.json @@ -0,0 +1,32 @@ +{ + "name": "{{ packageName }}", + "version": "0.0.1", + "description": "{{ description }}", + "license": "ISC", + "author": "{{ authorName }}", + "maintainers": [], + "main": "src/index.ts", + "files": [ + "dist", + "!**/*.tsbuildinfo", + "!**/*.map", + "command-line.json", + "rush-plugin-manifest.json" + ], + "scripts": { + "build": "exit 0", + "lint": "eslint ./ --cache", + "test": "vitest --run --passWithNoTests", + "test:cov": "npm run test -- --coverage" + }, + "dependencies": {}, + "devDependencies": { + "@coze-arch/eslint-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", + "@coze-arch/vitest-config": "workspace:*", + "@types/node": "^22.13.13", + "@vitest/coverage-v8": "^3.0.9", + "tsx": "^4.19.3", + "vitest": "^3.0.9" + } +} diff --git a/common/_templates/rush-plugin/rush-plugin-manifest.json b/common/_templates/rush-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000..642c1abc --- /dev/null +++ b/common/_templates/rush-plugin/rush-plugin-manifest.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", + "plugins": [ + { + "pluginName": "{{ packageName }}", + "description": "{{ description }}", + "commandLineJsonFilePath": "./command-line.json" + } + ] +} diff --git a/common/_templates/rush-plugin/src/index.ts b/common/_templates/rush-plugin/src/index.ts new file mode 100644 index 00000000..c155820b --- /dev/null +++ b/common/_templates/rush-plugin/src/index.ts @@ -0,0 +1 @@ +export const foo = 'bar'; diff --git a/common/_templates/rush-plugin/tsconfig.build.json b/common/_templates/rush-plugin/tsconfig.build.json new file mode 100644 index 00000000..6086f3cd --- /dev/null +++ b/common/_templates/rush-plugin/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "@coze-arch/ts-config/tsconfig.node.json", + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "module": "CommonJS", + "target": "ES2020", + "moduleResolution": "node" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/common/_templates/rush-plugin/tsconfig.json b/common/_templates/rush-plugin/tsconfig.json new file mode 100644 index 00000000..b3951a30 --- /dev/null +++ b/common/_templates/rush-plugin/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "exclude": ["**/*"], + "compilerOptions": { + "composite": true + }, + "references": [ + { + "path": "./tsconfig.build.json" + }, + { + "path": "./tsconfig.misc.json" + } + ] +} diff --git a/common/_templates/rush-plugin/tsconfig.misc.json b/common/_templates/rush-plugin/tsconfig.misc.json new file mode 100644 index 00000000..553ee729 --- /dev/null +++ b/common/_templates/rush-plugin/tsconfig.misc.json @@ -0,0 +1,18 @@ +{ + "extends": "@coze-arch/ts-config/tsconfig.node.json", + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "rootDir": "./", + "outDir": "./dist", + "module": "CommonJS", + "target": "ES2020", + "moduleResolution": "node" + }, + "include": ["__tests__", "vitest.config.ts"], + "exclude": ["./dist"], + "references": [ + { + "path": "./tsconfig.build.json" + } + ] +} diff --git a/common/_templates/rush-plugin/vitest.config.ts b/common/_templates/rush-plugin/vitest.config.ts new file mode 100644 index 00000000..5c7b91ac --- /dev/null +++ b/common/_templates/rush-plugin/vitest.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from '@coze-arch/vitest-config'; + +export default defineConfig({ + dirname: __dirname, + preset: 'node', +}); diff --git a/common/autoinstallers/plugins/package.json b/common/autoinstallers/plugins/package.json index 188f7fa6..c6da06ec 100644 --- a/common/autoinstallers/plugins/package.json +++ b/common/autoinstallers/plugins/package.json @@ -7,7 +7,8 @@ "fanwenjie.fe@bytedance.com" ], "dependencies": { - "@coze-infra/rush-run-tsc-plugin": "../../../packages/rush-plugins/run-tsc-plugin", + "@coze-arch/rush-publish-plugin": "../../../packages/rush-plugins/publish", + "@coze-arch/rush-run-tsc-plugin": "../../../packages/rush-plugins/run-tsc-plugin", "json5": "^2.2.3", "rush-init-project-plugin": "^0.11.0", "typescript": "~5.7.2" diff --git a/common/autoinstallers/plugins/pnpm-lock.yaml b/common/autoinstallers/plugins/pnpm-lock.yaml index 30902e54..58de0b7f 100644 --- a/common/autoinstallers/plugins/pnpm-lock.yaml +++ b/common/autoinstallers/plugins/pnpm-lock.yaml @@ -8,7 +8,10 @@ importers: .: dependencies: - '@coze-infra/rush-run-tsc-plugin': + '@coze-arch/rush-publish-plugin': + specifier: ../../../packages/rush-plugins/publish + version: link:../../../packages/rush-plugins/publish + '@coze-arch/rush-run-tsc-plugin': specifier: ../../../packages/rush-plugins/run-tsc-plugin version: link:../../../packages/rush-plugins/run-tsc-plugin json5: diff --git a/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-publish-plugin/@coze-arch/rush-publish-plugin/command-line.json b/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-publish-plugin/@coze-arch/rush-publish-plugin/command-line.json new file mode 100644 index 00000000..c6acc22e --- /dev/null +++ b/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-publish-plugin/@coze-arch/rush-publish-plugin/command-line.json @@ -0,0 +1,140 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [ + { + "name": "pub", + "commandKind": "global", + "summary": "⭐️️ Publish packages to npm", + "shellCommand": "rush-publish pub", + "safeForSimultaneousRushProcesses": true + }, + { + "name": "change-x", + "commandKind": "global", + "summary": "⭐️️ generate change log", + "shellCommand": "rush-publish change", + "safeForSimultaneousRushProcesses": true + }, + { + "name": "release", + "commandKind": "global", + "summary": "⭐️️ release packages", + "shellCommand": "rush-publish release", + "safeForSimultaneousRushProcesses": true + } + ], + "parameters": [ + { + "parameterKind": "string", + "description": "Enable dry run mode", + "shortName": "-d", + "longName": "--dry-run", + "argumentName": "DRY_RUN", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "string", + "shortName": "-t", + "longName": "--to", + "description": "Publish specified packages and their downstream dependencies", + "argumentName": "TO", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "string", + "shortName": "-f", + "longName": "--from", + "description": "Publish specified packages and their upstream/downstream dependencies", + "argumentName": "FROM", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "string", + "shortName": "-o", + "longName": "--only", + "description": "Only publish specified packages", + "argumentName": "ONLY", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "flag", + "shortName": "-s", + "longName": "--skip-commit", + "description": "Skip git commit", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "flag", + "shortName": "-p", + "longName": "--skip-push", + "description": "Skip git push", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "choice", + "alternatives": [ + { + "name": "alpha", + "description": "Alpha version" + }, + { + "name": "beta", + "description": "Beta version" + }, + { + "name": "patch", + "description": "Patch version" + }, + { + "name": "minor", + "description": "Minor version" + }, + { + "name": "major", + "description": "Major version" + } + ], + "shortName": "-b", + "longName": "--bump-type", + "description": "Version bump type (alpha/beta/patch/minor/major)", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "flag", + "longName": "--amend-commit", + "shortName": "-a", + "description": "是否 amend commit 阶段", + "associatedCommands": ["change-x"] + }, + { + "parameterKind": "flag", + "longName": "--ci", + "shortName": "-i", + "description": "是否在 CI 环境", + "associatedCommands": ["change-x"] + }, + { + "parameterKind": "string", + "argumentName": "COMMIT_MSG", + "longName": "--commit-msg", + "shortName": "-c", + "description": "本次提交信息,默认读取 .git/COMMIT_EDITMSG", + "associatedCommands": ["change-x"] + }, + { + "parameterKind": "string", + "argumentName": "COMMIT", + "longName": "--commit", + "shortName": "-c", + "description": "Git commit hash", + "associatedCommands": ["release"] + } + ] +} diff --git a/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-publish-plugin/rush-plugin-manifest.json b/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-publish-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000..592d0287 --- /dev/null +++ b/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-publish-plugin/rush-plugin-manifest.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", + "plugins": [ + { + "pluginName": "@coze-arch/rush-publish-plugin", + "description": "rush plugin to generate change log and publish packages", + "commandLineJsonFilePath": "./command-line.json" + } + ] +} diff --git a/common/autoinstallers/plugins/rush-plugins/@coze-infra/rush-run-tsc-plugin/@coze-infra/rush-run-tsc-plugin/command-line.json b/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-run-tsc-plugin/@coze-arch/rush-run-tsc-plugin/command-line.json similarity index 100% rename from common/autoinstallers/plugins/rush-plugins/@coze-infra/rush-run-tsc-plugin/@coze-infra/rush-run-tsc-plugin/command-line.json rename to common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-run-tsc-plugin/@coze-arch/rush-run-tsc-plugin/command-line.json diff --git a/common/autoinstallers/plugins/rush-plugins/@coze-infra/rush-run-tsc-plugin/rush-plugin-manifest.json b/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-run-tsc-plugin/rush-plugin-manifest.json similarity index 81% rename from common/autoinstallers/plugins/rush-plugins/@coze-infra/rush-run-tsc-plugin/rush-plugin-manifest.json rename to common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-run-tsc-plugin/rush-plugin-manifest.json index c09f33f5..6af51fd3 100644 --- a/common/autoinstallers/plugins/rush-plugins/@coze-infra/rush-run-tsc-plugin/rush-plugin-manifest.json +++ b/common/autoinstallers/plugins/rush-plugins/@coze-arch/rush-run-tsc-plugin/rush-plugin-manifest.json @@ -2,7 +2,7 @@ "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", "plugins": [ { - "pluginName": "@coze-infra/rush-run-tsc-plugin", + "pluginName": "@coze-arch/rush-run-tsc-plugin", "description": "Rush plugin for run tsc", "commandLineJsonFilePath": "./command-line.json" } diff --git a/common/changes/@coze-arch/npm-mcp-server/feat-rush-change-plugin_2025-03-25-14-54.json b/common/changes/@coze-arch/npm-mcp-server/feat-rush-change-plugin_2025-03-25-14-54.json new file mode 100644 index 00000000..9a7d0c4c --- /dev/null +++ b/common/changes/@coze-arch/npm-mcp-server/feat-rush-change-plugin_2025-03-25-14-54.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/npm-mcp-server", + "comment": "change scope", + "type": "patch" + } + ], + "packageName": "@coze-arch/npm-mcp-server", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/npm-mcp-server/feat-rush-change-plugin_2025-03-25-15-10.json b/common/changes/@coze-arch/npm-mcp-server/feat-rush-change-plugin_2025-03-25-15-10.json new file mode 100644 index 00000000..9a7d0c4c --- /dev/null +++ b/common/changes/@coze-arch/npm-mcp-server/feat-rush-change-plugin_2025-03-25-15-10.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/npm-mcp-server", + "comment": "change scope", + "type": "patch" + } + ], + "packageName": "@coze-arch/npm-mcp-server", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-54.json b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-54.json new file mode 100644 index 00000000..a5948551 --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-54.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "change scope", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-22.json b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-22.json new file mode 100644 index 00000000..9618f997 --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-22.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "update lock", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-30.json b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-30.json new file mode 100644 index 00000000..9618f997 --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-30.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "update lock", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-35.json b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-35.json new file mode 100644 index 00000000..9618f997 --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-35.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "update lock", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-40.json b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-40.json new file mode 100644 index 00000000..9618f997 --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-40.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "update lock", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-41.json b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-41.json new file mode 100644 index 00000000..9618f997 --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-26-06-41.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "update lock", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-27-03-48.json b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-27-03-48.json new file mode 100644 index 00000000..217b2fba --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-03-27-03-48.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "use outside infra", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-05-16-03-16.json b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-05-16-03-16.json new file mode 100644 index 00000000..a5b89978 --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-rush-change-plugin_2025-05-16-03-16.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "fix test error", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-14-54.json b/common/changes/@coze-arch/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-14-54.json new file mode 100644 index 00000000..d18cbc0d --- /dev/null +++ b/common/changes/@coze-arch/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-14-54.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-run-tsc-plugin", + "comment": "change scope", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-run-tsc-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-arch/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-15-05.json b/common/changes/@coze-arch/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-15-05.json new file mode 100644 index 00000000..d18cbc0d --- /dev/null +++ b/common/changes/@coze-arch/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-15-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-run-tsc-plugin", + "comment": "change scope", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-run-tsc-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/npm-mcp-server/feat-rush-change-plugin_2025-03-25-09-24.json b/common/changes/@coze-infra/npm-mcp-server/feat-rush-change-plugin_2025-03-25-09-24.json new file mode 100644 index 00000000..c1cc8fa4 --- /dev/null +++ b/common/changes/@coze-infra/npm-mcp-server/feat-rush-change-plugin_2025-03-25-09-24.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/npm-mcp-server", + "comment": "rush plugin for generate change logs", + "type": "patch" + } + ], + "packageName": "@coze-arch/npm-mcp-server", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-09-44.json b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-09-44.json new file mode 100644 index 00000000..ea1ec671 --- /dev/null +++ b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-09-44.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "use rush change in pre-commit hook", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-05.json b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-05.json new file mode 100644 index 00000000..9b1c7998 --- /dev/null +++ b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "publish & release command", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-07.json b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-07.json new file mode 100644 index 00000000..9b1c7998 --- /dev/null +++ b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-07.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "publish & release command", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-08.json b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-08.json new file mode 100644 index 00000000..9b1c7998 --- /dev/null +++ b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-10-08.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "publish & release command", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-13-52.json b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-13-52.json new file mode 100644 index 00000000..8a3b2b32 --- /dev/null +++ b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-13-52.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "publish & release pipeline", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-00.json b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-00.json new file mode 100644 index 00000000..8a3b2b32 --- /dev/null +++ b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-00.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "publish & release pipeline", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-01.json b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-01.json new file mode 100644 index 00000000..8a3b2b32 --- /dev/null +++ b/common/changes/@coze-infra/rush-publish-plugin/feat-rush-change-plugin_2025-03-25-14-01.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "publish & release pipeline", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/changes/@coze-infra/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-10-05.json b/common/changes/@coze-infra/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-10-05.json new file mode 100644 index 00000000..3f7f7995 --- /dev/null +++ b/common/changes/@coze-infra/rush-run-tsc-plugin/feat-rush-change-plugin_2025-03-25-10-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-run-tsc-plugin", + "comment": "publish & release command", + "type": "patch" + } + ], + "packageName": "@coze-arch/rush-run-tsc-plugin", + "email": "tecvan.fe@gmail.com" +} diff --git a/common/config/rush/.npmrc-publish b/common/config/rush/.npmrc-publish index 7ab44c18..f6477899 100644 --- a/common/config/rush/.npmrc-publish +++ b/common/config/rush/.npmrc-publish @@ -15,6 +15,6 @@ # other unrelated processes may be able to read the file. Also, the file may persist indefinitely, # for example if the machine loses power. A safer practice is to pass the token via an # environment variable, which can be referenced from .npmrc using ${} expansion. For example: -# -# //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} -# + +//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} +always-auth=false diff --git a/common/config/rush/rush-plugins.json b/common/config/rush/rush-plugins.json index 7dc68042..f5c929d9 100644 --- a/common/config/rush/rush-plugins.json +++ b/common/config/rush/rush-plugins.json @@ -31,8 +31,13 @@ "autoinstallerName": "plugins" }, { - "packageName": "@coze-infra/rush-run-tsc-plugin", - "pluginName": "@coze-infra/rush-run-tsc-plugin", + "packageName": "@coze-arch/rush-run-tsc-plugin", + "pluginName": "@coze-arch/rush-run-tsc-plugin", + "autoinstallerName": "plugins" + }, + { + "packageName": "@coze-arch/rush-publish-plugin", + "pluginName": "@coze-arch/rush-publish-plugin", "autoinstallerName": "plugins" } ] diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 4697949b..c63b6c8b 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -66,7 +66,7 @@ importers: '@babel/preset-react': specifier: ~7.13.13 version: 7.13.13(@babel/core@7.26.10) - '@coze-infra/ts-config': + '@coze-arch/ts-config': specifier: workspace:* version: link:../ts-config '@eslint/compat': @@ -185,10 +185,10 @@ importers: specifier: ^4.2.1 version: 4.3.2(typescript@5.8.2)(vite@5.4.15(@types/node@22.13.13)) devDependencies: - '@coze-infra/eslint-config': + '@coze-arch/eslint-config': specifier: workspace:* version: link:../eslint-config - '@coze-infra/ts-config': + '@coze-arch/ts-config': specifier: workspace:* version: link:../ts-config '@types/node': @@ -243,16 +243,13 @@ importers: specifier: ^3.24.2 version: 3.24.2 devDependencies: - '@commitlint/types': - specifier: ^17.4.0 - version: 17.8.1 - '@coze-infra/eslint-config': + '@coze-arch/eslint-config': specifier: workspace:* version: link:../../../config/eslint-config - '@coze-infra/ts-config': + '@coze-arch/ts-config': specifier: workspace:* version: link:../../../config/ts-config - '@coze-infra/vitest-config': + '@coze-arch/vitest-config': specifier: workspace:* version: link:../../../config/vitest-config '@rollup/plugin-commonjs': @@ -289,11 +286,132 @@ importers: specifier: ^3.0.9 version: 3.0.9(@types/node@22.13.13)(happy-dom@17.4.4) + ../../../packages/rush-plugins/publish: + dependencies: + '@coze-arch/fs-enhance': + specifier: workspace:* + version: link:../../utils/fs-enhance + '@coze-arch/logger': + specifier: workspace:* + version: link:../../utils/logger + '@inquirer/prompts': + specifier: ^3.2.0 + version: 3.3.2 + '@rushstack/rush-sdk': + specifier: ^5.150.0 + version: 5.150.0(@types/node@22.13.13) + chalk: + specifier: ^4.1.2 + version: 4.1.2 + conventional-changelog-angular: + specifier: ^5.0.13 + version: 5.0.13 + conventional-commits-parser: + specifier: ^3.2.4 + version: 3.2.4 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 + open: + specifier: ~10.1.0 + version: 10.1.0 + semver: + specifier: ^7.7.1 + version: 7.7.1 + shelljs: + specifier: ^0.9.2 + version: 0.9.2 + devDependencies: + '@commitlint/types': + specifier: ^17.4.0 + version: 17.8.1 + '@coze-arch/eslint-config': + specifier: workspace:* + version: link:../../../config/eslint-config + '@coze-arch/ts-config': + specifier: workspace:* + version: link:../../../config/ts-config + '@coze-arch/vitest-config': + specifier: workspace:* + version: link:../../../config/vitest-config + '@types/node': + specifier: ^22.13.13 + version: 22.13.13 + '@types/semver': + specifier: ^7.5.8 + version: 7.5.8 + '@types/shelljs': + specifier: ^0.8.15 + version: 0.8.15 + '@vitest/coverage-v8': + specifier: ^3.0.9 + version: 3.0.9(vitest@3.0.9(@types/node@22.13.13)(happy-dom@17.4.4)) + commander: + specifier: ^13.1.0 + version: 13.1.0 + sucrase: + specifier: ^3.32.0 + version: 3.35.0 + tsx: + specifier: ^4.19.3 + version: 4.19.3 + vitest: + specifier: ^3.0.9 + version: 3.0.9(@types/node@22.13.13)(happy-dom@17.4.4) + ../../../packages/rush-plugins/run-tsc-plugin: devDependencies: - '@coze-infra/ts-config': + '@coze-arch/ts-config': + specifier: workspace:* + version: link:../../../config/ts-config + + ../../../packages/utils/fs-enhance: + devDependencies: + '@coze-arch/eslint-config': + specifier: workspace:* + version: link:../../../config/eslint-config + '@coze-arch/ts-config': + specifier: workspace:* + version: link:../../../config/ts-config + '@coze-arch/vitest-config': + specifier: workspace:* + version: link:../../../config/vitest-config + '@types/node': + specifier: ^22.13.13 + version: 22.13.13 + '@vitest/coverage-v8': + specifier: ^3.0.9 + version: 3.0.9(vitest@3.0.9(@types/node@22.13.13)(happy-dom@17.4.4)) + tsx: + specifier: ^4.19.3 + version: 4.19.3 + vitest: + specifier: ^3.0.9 + version: 3.0.9(@types/node@22.13.13)(happy-dom@17.4.4) + + ../../../packages/utils/logger: + devDependencies: + '@coze-arch/eslint-config': + specifier: workspace:* + version: link:../../../config/eslint-config + '@coze-arch/ts-config': specifier: workspace:* version: link:../../../config/ts-config + '@coze-arch/vitest-config': + specifier: workspace:* + version: link:../../../config/vitest-config + '@types/node': + specifier: ^22.13.13 + version: 22.13.13 + '@vitest/coverage-v8': + specifier: ^3.0.9 + version: 3.0.9(vitest@3.0.9(@types/node@22.13.13)(happy-dom@17.4.4)) + tsx: + specifier: ^4.19.3 + version: 4.19.3 + vitest: + specifier: ^3.0.9 + version: 3.0.9(@types/node@22.13.13)(happy-dom@17.4.4) packages: @@ -1376,6 +1494,50 @@ packages: resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} engines: {node: '>=18.18'} + '@inquirer/checkbox@1.5.2': + resolution: {integrity: sha512-CifrkgQjDkUkWexmgYYNyB5603HhTHI91vLFeQXh6qrTKiCMVASol01Rs1cv6LP/A2WccZSRlJKZhbaBIs/9ZA==} + engines: {node: '>=14.18.0'} + + '@inquirer/confirm@2.0.17': + resolution: {integrity: sha512-EqzhGryzmGpy2aJf6LxJVhndxYmFs+m8cxXzf8nejb1DE3sabf6mUgBcp4J0jAUEiAcYzqmkqRr7LPFh/WdnXA==} + engines: {node: '>=14.18.0'} + + '@inquirer/core@6.0.0': + resolution: {integrity: sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==} + engines: {node: '>=14.18.0'} + + '@inquirer/editor@1.2.15': + resolution: {integrity: sha512-gQ77Ls09x5vKLVNMH9q/7xvYPT6sIs5f7URksw+a2iJZ0j48tVS6crLqm2ugG33tgXHIwiEqkytY60Zyh5GkJQ==} + engines: {node: '>=14.18.0'} + + '@inquirer/expand@1.1.16': + resolution: {integrity: sha512-TGLU9egcuo+s7PxphKUCnJnpCIVY32/EwPCLLuu+gTvYiD8hZgx8Z2niNQD36sa6xcfpdLY6xXDBiL/+g1r2XQ==} + engines: {node: '>=14.18.0'} + + '@inquirer/input@1.2.16': + resolution: {integrity: sha512-Ou0LaSWvj1ni+egnyQ+NBtfM1885UwhRCMtsRt2bBO47DoC1dwtCa+ZUNgrxlnCHHF0IXsbQHYtIIjFGAavI4g==} + engines: {node: '>=14.18.0'} + + '@inquirer/password@1.1.16': + resolution: {integrity: sha512-aZYZVHLUXZ2gbBot+i+zOJrks1WaiI95lvZCn1sKfcw6MtSSlYC8uDX8sTzQvAsQ8epHoP84UNvAIT0KVGOGqw==} + engines: {node: '>=14.18.0'} + + '@inquirer/prompts@3.3.2': + resolution: {integrity: sha512-k52mOMRvTUejrqyF1h8Z07chC+sbaoaUYzzr1KrJXyj7yaX7Nrh0a9vktv8TuocRwIJOQMaj5oZEmkspEcJFYQ==} + engines: {node: '>=14.18.0'} + + '@inquirer/rawlist@1.2.16': + resolution: {integrity: sha512-pZ6TRg2qMwZAOZAV6TvghCtkr53dGnK29GMNQ3vMZXSNguvGqtOVc4j/h1T8kqGJFagjyfBZhUPGwNS55O5qPQ==} + engines: {node: '>=14.18.0'} + + '@inquirer/select@1.3.3': + resolution: {integrity: sha512-RzlRISXWqIKEf83FDC9ZtJ3JvuK1l7aGpretf41BCWYrvla2wU8W8MTRNMiPrPJ+1SIqrRC1nZdZ60hD9hRXLg==} + engines: {node: '>=14.18.0'} + + '@inquirer/type@1.5.5': + resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==} + engines: {node: '>=18'} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1446,6 +1608,18 @@ packages: resolution: {integrity: sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@pnpm/lockfile.types@1.0.3': + resolution: {integrity: sha512-A7vUWktnhDkrIs+WmXm7AdffJVyVYJpQUEouya/DYhB+Y+tQ3BXjZ6CV0KybqLgI/8AZErgCJqFxA0GJH6QDjA==} + engines: {node: '>=18.12'} + + '@pnpm/patching.types@1.0.0': + resolution: {integrity: sha512-juCdQCC1USqLcOhVPl1tYReoTO9YH4fTullMnFXXcmpsDM7Dkn3tzuOQKC3oPoJ2ozv+0EeWWMtMGqn2+IM3pQ==} + engines: {node: '>=18.12'} + + '@pnpm/types@12.2.0': + resolution: {integrity: sha512-5RtwWhX39j89/Tmyv2QSlpiNjErA357T/8r1Dkg+2lD3P7RuS7Xi2tChvmOC3VlezEFNcWnEGCOeKoGRkDuqFA==} + engines: {node: '>=18.12'} + '@rollup/plugin-commonjs@24.1.0': resolution: {integrity: sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==} engines: {node: '>=14.0.0'} @@ -1621,6 +1795,22 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@rushstack/lookup-by-path@0.5.9': + resolution: {integrity: sha512-xGdjy6Mj2FDccpzo+wGDWmQoTzZQDjlEFNWbiKeIdA/TkIcOoK+6t0Ppka4XE+od0vggZD6Hqk9kZoXuY8lu+A==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/node-core-library@5.11.0': + resolution: {integrity: sha512-I8+VzG9A0F3nH2rLpPd7hF8F7l5Xb7D+ldrWVZYegXM6CsKkvWc670RlgK3WX8/AseZfXA/vVrh0bpXe2Y2UDQ==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + '@rushstack/node-core-library@5.12.0': resolution: {integrity: sha512-QSwwzgzWoil1SCQse+yCHwlhRxNv2dX9siPnAb9zR/UmMhac4mjMrlMZpk64BlCeOFi1kJKgXRkihSwRMbboAQ==} peerDependencies: @@ -1629,6 +1819,20 @@ packages: '@types/node': optional: true + '@rushstack/package-deps-hash@4.3.10': + resolution: {integrity: sha512-jqKuD39WnG7E82Y7D5c2yAJLQRC/kv/C6PkPeGcegDl7owmt1pxfiRsoMqK+8W3z2eg4wa4VtuT/DtiLYTDGew==} + + '@rushstack/rush-sdk@5.150.0': + resolution: {integrity: sha512-ReSRRDgtBIwD0uJFzNOp9ahAFOAi5gxhtVYgoQNcgdzOOLxqKdTFOo4saKKuSfxH74GEz8ZaDdkuj/0nQDBWZg==} + + '@rushstack/terminal@0.15.0': + resolution: {integrity: sha512-vXQPRQ+vJJn4GVqxkwRe+UGgzNxdV8xuJZY2zem46Y0p3tlahucH9/hPmLGj2i9dQnUBFiRnoM9/KW7PYw8F4Q==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + '@rushstack/tree-pattern@0.2.4': resolution: {integrity: sha512-H8i0OinWsdKM1TKEKPeRRTw85e+/7AIFpxm7q1blceZJhuxRBjCGAUZvQXZK4CMLx75xPqh/h1t5WHwFmElAPA==} @@ -1665,6 +1869,9 @@ packages: '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/glob@7.2.0': + resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1674,6 +1881,18 @@ packages: '@types/lodash@4.17.16': resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==} + '@types/minimatch@5.1.2': + resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} + + '@types/minimist@1.2.5': + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + + '@types/mute-stream@0.0.4': + resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + + '@types/node@20.17.27': + resolution: {integrity: sha512-U58sbKhDrthHlxHRJw7ZLiLDZGmAUOZUbpw0S6nL27sYUdhvgBLCRu/keSd6qcTsfArd1sRFCCBxzWATGr/0UA==} + '@types/node@22.13.13': resolution: {integrity: sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==} @@ -1689,6 +1908,12 @@ packages: '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + '@types/shelljs@0.8.15': + resolution: {integrity: sha512-vzmnCHl6hViPu9GNLQJ+DZFd6BQI2DBTUeOvYHqkWQLMfKAAQYMb/xAmZkTogZI/vqXHCWkqDRymDI5p0QTi5Q==} + + '@types/wrap-ansi@3.0.0': + resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} + '@typescript-eslint/eslint-plugin@5.38.1': resolution: {integrity: sha512-ky7EFzPhqz3XlhS7vPOoMDaQnQMn+9o5ICR9CPr/6bw8HrFkzhMSxuA3gRfiJVvs7geYrSeawGJjZoZQKCOglQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1963,6 +2188,10 @@ packages: '@vitest/utils@3.0.9': resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==} + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -1998,6 +2227,10 @@ packages: ajv@8.13.0: resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -2034,6 +2267,9 @@ packages: array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + array-includes@3.1.8: resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} engines: {node: '>= 0.4'} @@ -2066,6 +2302,10 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -2169,6 +2409,10 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -2193,6 +2437,14 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase-keys@6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001707: resolution: {integrity: sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==} @@ -2204,6 +2456,9 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -2216,6 +2471,14 @@ packages: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -2238,6 +2501,9 @@ packages: commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2256,6 +2522,15 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + conventional-changelog-angular@5.0.13: + resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==} + engines: {node: '>=10'} + + conventional-commits-parser@3.2.4: + resolution: {integrity: sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==} + engines: {node: '>=10'} + hasBin: true + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2281,6 +2556,10 @@ packages: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} + cross-spawn@6.0.6: + resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} + engines: {node: '>=4.8'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2300,6 +2579,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} @@ -2324,6 +2606,14 @@ packages: supports-color: optional: true + decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -2335,10 +2625,22 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + default-browser-id@5.0.0: + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + engines: {node: '>=18'} + + default-browser@5.2.1: + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + engines: {node: '>=18'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -2375,6 +2677,10 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -2402,6 +2708,9 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2750,6 +3059,10 @@ packages: resolution: {integrity: sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==} engines: {node: '>=18.0.0'} + execa@1.0.0: + resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} + engines: {node: '>=6'} + expect-type@1.2.0: resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==} engines: {node: '>=12.0.0'} @@ -2768,6 +3081,10 @@ packages: resolution: {integrity: sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==} engines: {node: '>= 18'} + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2795,6 +3112,10 @@ packages: picomatch: optional: true + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -2897,6 +3218,10 @@ packages: resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} engines: {node: '>=12'} + get-stream@4.1.0: + resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + engines: {node: '>=6'} + get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -2965,6 +3290,10 @@ packages: resolution: {integrity: sha512-/Pb0ctk3HTZ5xEL3BZ0hK1AqDSAUuRQitOmROPHhfUYEWpmTImwfD8vFDGADmMAX0JYgbcgxWoLFKtsWhcpuVA==} engines: {node: '>=18.0.0'} + hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -2995,6 +3324,10 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -3049,6 +3382,10 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -3095,6 +3432,11 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -3115,6 +3457,11 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -3130,6 +3477,14 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -3152,6 +3507,10 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} + is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -3160,6 +3519,10 @@ packages: resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} + is-text-path@1.0.1: + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} + is-typed-array@1.1.15: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} @@ -3176,6 +3539,10 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -3260,6 +3627,10 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -3267,6 +3638,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -3342,6 +3717,14 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + + map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + markdown-it-meta@0.0.1: resolution: {integrity: sha512-sCQG7mHJM3i4l6MztgzxE8t3aTQB5CSCO0wq8k6CEaqud40eryWXqPesq5AyztbYgwnxJcNIsmFsKDRkrl6Zuw==} @@ -3364,6 +3747,10 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + meow@8.1.2: + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} + merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -3423,6 +3810,10 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -3443,6 +3834,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -3465,12 +3860,23 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + + npm-run-path@2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3522,14 +3928,26 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + open@10.1.0: + resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -3569,6 +3987,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -3660,6 +4082,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + pump@3.0.2: + resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} @@ -3668,6 +4093,14 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + qs@6.13.0: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} @@ -3679,6 +4112,10 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-lru@4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -3706,6 +4143,18 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -3789,6 +4238,14 @@ packages: rspack-resolver@1.2.2: resolution: {integrity: sha512-Fwc19jMBA3g+fxDJH2B4WxwZjE0VaaOL7OX/A4Wn5Zv7bOD/vyPZhzXfaO73Xc2GAlfi96g5fGUa378WbIGfFw==} + run-applescript@7.0.0: + resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + engines: {node: '>=18'} + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -3865,14 +4322,27 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shelljs@0.9.2: + resolution: {integrity: sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==} + engines: {node: '>=18'} + hasBin: true + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -3892,6 +4362,9 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -3923,6 +4396,9 @@ packages: spdx-license-ids@3.0.21: resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} + split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -3973,6 +4449,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3985,6 +4464,10 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-eof@1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -4002,6 +4485,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -4014,10 +4501,18 @@ packages: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + test-exclude@7.0.1: resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} engines: {node: '>=18'} + text-extensions@1.9.0: + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -4028,6 +4523,12 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -4050,6 +4551,10 @@ packages: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4058,6 +4563,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + trim-newlines@3.0.1: + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -4105,10 +4614,18 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@0.18.1: + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} + type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -4153,6 +4670,9 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} @@ -4189,6 +4709,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -4296,6 +4819,10 @@ packages: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -4310,6 +4837,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4331,6 +4862,10 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -5472,6 +6007,94 @@ snapshots: '@humanwhocodes/retry@0.4.2': {} + '@inquirer/checkbox@1.5.2': + dependencies: + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + figures: 3.2.0 + + '@inquirer/confirm@2.0.17': + dependencies: + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + chalk: 4.1.2 + + '@inquirer/core@6.0.0': + dependencies: + '@inquirer/type': 1.5.5 + '@types/mute-stream': 0.0.4 + '@types/node': 20.17.27 + '@types/wrap-ansi': 3.0.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-spinners: 2.9.2 + cli-width: 4.1.0 + figures: 3.2.0 + mute-stream: 1.0.0 + run-async: 3.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + '@inquirer/editor@1.2.15': + dependencies: + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + chalk: 4.1.2 + external-editor: 3.1.0 + + '@inquirer/expand@1.1.16': + dependencies: + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + chalk: 4.1.2 + figures: 3.2.0 + + '@inquirer/input@1.2.16': + dependencies: + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + chalk: 4.1.2 + + '@inquirer/password@1.1.16': + dependencies: + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + + '@inquirer/prompts@3.3.2': + dependencies: + '@inquirer/checkbox': 1.5.2 + '@inquirer/confirm': 2.0.17 + '@inquirer/core': 6.0.0 + '@inquirer/editor': 1.2.15 + '@inquirer/expand': 1.1.16 + '@inquirer/input': 1.2.16 + '@inquirer/password': 1.1.16 + '@inquirer/rawlist': 1.2.16 + '@inquirer/select': 1.3.3 + + '@inquirer/rawlist@1.2.16': + dependencies: + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + chalk: 4.1.2 + + '@inquirer/select@1.3.3': + dependencies: + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + figures: 3.2.0 + + '@inquirer/type@1.5.5': + dependencies: + mute-stream: 1.0.0 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -5555,6 +6178,15 @@ snapshots: '@pkgr/core@0.2.0': {} + '@pnpm/lockfile.types@1.0.3': + dependencies: + '@pnpm/patching.types': 1.0.0 + '@pnpm/types': 12.2.0 + + '@pnpm/patching.types@1.0.0': {} + + '@pnpm/types@12.2.0': {} + '@rollup/plugin-commonjs@24.1.0(rollup@4.37.0)': dependencies: '@rollup/pluginutils': 5.1.4(rollup@4.37.0) @@ -5709,6 +6341,23 @@ snapshots: - supports-color - typescript + '@rushstack/lookup-by-path@0.5.9(@types/node@22.13.13)': + optionalDependencies: + '@types/node': 22.13.13 + + '@rushstack/node-core-library@5.11.0(@types/node@22.13.13)': + dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1 + fs-extra: 11.3.0 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.10 + semver: 7.5.4 + optionalDependencies: + '@types/node': 22.13.13 + '@rushstack/node-core-library@5.12.0(@types/node@22.13.13)': dependencies: ajv: 8.13.0 @@ -5722,6 +6371,30 @@ snapshots: optionalDependencies: '@types/node': 22.13.13 + '@rushstack/package-deps-hash@4.3.10(@types/node@22.13.13)': + dependencies: + '@rushstack/node-core-library': 5.11.0(@types/node@22.13.13) + transitivePeerDependencies: + - '@types/node' + + '@rushstack/rush-sdk@5.150.0(@types/node@22.13.13)': + dependencies: + '@pnpm/lockfile.types': 1.0.3 + '@rushstack/lookup-by-path': 0.5.9(@types/node@22.13.13) + '@rushstack/node-core-library': 5.11.0(@types/node@22.13.13) + '@rushstack/package-deps-hash': 4.3.10(@types/node@22.13.13) + '@rushstack/terminal': 0.15.0(@types/node@22.13.13) + tapable: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/terminal@0.15.0(@types/node@22.13.13)': + dependencies: + '@rushstack/node-core-library': 5.11.0(@types/node@22.13.13) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 22.13.13 + '@rushstack/tree-pattern@0.2.4': {} '@stylistic/eslint-plugin-ts@2.13.0(eslint@9.14.0)(typescript@5.8.2)': @@ -5771,12 +6444,29 @@ snapshots: '@types/estree@1.0.7': {} + '@types/glob@7.2.0': + dependencies: + '@types/minimatch': 5.1.2 + '@types/node': 22.13.13 + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} '@types/lodash@4.17.16': {} + '@types/minimatch@5.1.2': {} + + '@types/minimist@1.2.5': {} + + '@types/mute-stream@0.0.4': + dependencies: + '@types/node': 22.13.13 + + '@types/node@20.17.27': + dependencies: + undici-types: 6.19.8 + '@types/node@22.13.13': dependencies: undici-types: 6.20.0 @@ -5789,6 +6479,13 @@ snapshots: '@types/semver@7.5.8': {} + '@types/shelljs@0.8.15': + dependencies: + '@types/glob': 7.2.0 + '@types/node': 22.13.13 + + '@types/wrap-ansi@3.0.0': {} + '@typescript-eslint/eslint-plugin@5.38.1(@typescript-eslint/parser@5.38.1(eslint@9.14.0)(typescript@5.8.2))(eslint@9.14.0)(typescript@5.8.2)': dependencies: '@typescript-eslint/parser': 5.38.1(eslint@9.14.0)(typescript@5.8.2) @@ -6150,6 +6847,11 @@ snapshots: loupe: 3.1.3 tinyrainbow: 2.0.0 + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -6188,6 +6890,10 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -6215,6 +6921,8 @@ snapshots: array-flatten@1.1.1: {} + array-ify@1.0.0: {} + array-includes@3.1.8: dependencies: call-bind: 1.0.8 @@ -6277,6 +6985,8 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + arrify@1.0.1: {} + assertion-error@2.0.1: {} ast-types-flow@0.0.8: {} @@ -6436,6 +7146,10 @@ snapshots: builtin-modules@3.3.0: {} + bundle-name@4.1.0: + dependencies: + run-applescript: 7.0.0 + bytes@3.1.2: {} cac@6.7.14: {} @@ -6459,6 +7173,14 @@ snapshots: callsites@3.1.0: {} + camelcase-keys@6.2.2: + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + + camelcase@5.3.1: {} + caniuse-lite@1.0.30001707: {} chai@5.2.0: @@ -6474,6 +7196,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chardet@0.7.0: {} + check-error@2.1.1: {} ci-info@3.9.0: {} @@ -6482,6 +7206,10 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 + cli-spinners@2.9.2: {} + + cli-width@4.1.0: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -6498,6 +7226,11 @@ snapshots: commondir@1.0.1: {} + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + concat-map@0.0.1: {} confusing-browser-globals@1.0.11: {} @@ -6512,6 +7245,20 @@ snapshots: content-type@1.0.5: {} + conventional-changelog-angular@5.0.13: + dependencies: + compare-func: 2.0.0 + q: 1.5.1 + + conventional-commits-parser@3.2.4: + dependencies: + JSONStream: 1.3.5 + is-text-path: 1.0.1 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + convert-source-map@2.0.0: {} cookie-signature@1.0.6: {} @@ -6537,6 +7284,14 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 + cross-spawn@6.0.6: + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -6563,6 +7318,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + dayjs@1.11.13: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -6579,18 +7336,34 @@ snapshots: dependencies: ms: 2.1.3 + decamelize-keys@1.1.1: + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + + decamelize@1.2.0: {} + deep-eql@5.0.2: {} deep-is@0.1.4: {} deepmerge@4.3.1: {} + default-browser-id@5.0.0: {} + + default-browser@5.2.1: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.0 + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 + define-lazy-prop@3.0.0: {} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -6619,6 +7392,10 @@ snapshots: dependencies: esutils: 2.0.3 + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -6639,6 +7416,10 @@ snapshots: encodeurl@2.0.0: {} + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + entities@4.5.0: {} error-ex@1.3.2: @@ -7191,6 +7972,16 @@ snapshots: dependencies: eventsource-parser: 3.0.0 + execa@1.0.0: + dependencies: + cross-spawn: 6.0.6 + get-stream: 4.1.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + expect-type@1.2.0: {} express-rate-limit@7.5.0(express@5.0.1): @@ -7268,6 +8059,12 @@ snapshots: transitivePeerDependencies: - supports-color + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -7292,6 +8089,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -7408,6 +8209,10 @@ snapshots: get-stdin@9.0.0: {} + get-stream@4.1.0: + dependencies: + pump: 3.0.2 + get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 @@ -7482,6 +8287,8 @@ snapshots: webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 + hard-rejection@2.1.0: {} + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -7506,6 +8313,10 @@ snapshots: hosted-git-info@2.8.9: {} + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + html-escaper@2.0.2: {} http-errors@2.0.0: @@ -7556,6 +8367,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + interpret@1.4.0: {} + ipaddr.js@1.9.1: {} is-array-buffer@3.0.5: @@ -7608,6 +8421,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-docker@3.0.0: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -7627,6 +8442,10 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + is-map@2.0.3: {} is-module@1.0.0: {} @@ -7638,6 +8457,10 @@ snapshots: is-number@7.0.0: {} + is-obj@2.0.0: {} + + is-plain-obj@1.1.0: {} + is-plain-obj@4.1.0: {} is-promise@4.0.0: {} @@ -7659,6 +8482,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-stream@1.1.0: {} + is-string@1.1.1: dependencies: call-bound: 1.0.4 @@ -7670,6 +8495,10 @@ snapshots: has-symbols: 1.1.0 safe-regex-test: 1.1.0 + is-text-path@1.0.1: + dependencies: + text-extensions: 1.9.0 + is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.19 @@ -7685,6 +8514,10 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + isarray@2.0.5: {} isexe@2.0.0: {} @@ -7766,6 +8599,8 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonparse@1.3.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -7777,6 +8612,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kind-of@6.0.3: {} + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -7848,6 +8685,10 @@ snapshots: dependencies: semver: 7.7.1 + map-obj@1.0.1: {} + + map-obj@4.3.0: {} + markdown-it-meta@0.0.1: dependencies: js-yaml: 3.14.1 @@ -7869,6 +8710,20 @@ snapshots: media-typer@1.1.0: {} + meow@8.1.2: + dependencies: + '@types/minimist': 1.2.5 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + merge-descriptors@1.0.3: {} merge-descriptors@2.0.0: {} @@ -7914,6 +8769,12 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimist-options@4.1.0: + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + minimist@1.2.8: {} minipass@7.1.2: {} @@ -7932,6 +8793,8 @@ snapshots: ms@2.1.3: {} + mute-stream@1.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -7948,6 +8811,8 @@ snapshots: negotiator@1.0.0: {} + nice-try@1.0.5: {} + node-releases@2.0.19: {} normalize-package-data@2.5.0: @@ -7957,6 +8822,17 @@ snapshots: semver: 5.7.2 validate-npm-package-license: 3.0.4 + normalize-package-data@3.0.3: + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.16.1 + semver: 7.7.1 + validate-npm-package-license: 3.0.4 + + npm-run-path@2.0.2: + dependencies: + path-key: 2.0.1 + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -8019,6 +8895,13 @@ snapshots: dependencies: wrappy: 1.0.2 + open@10.1.0: + dependencies: + default-browser: 5.2.1 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 3.1.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -8028,12 +8911,16 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + os-tmpdir@1.0.2: {} + own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 + p-finally@1.0.0: {} + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -8069,6 +8956,8 @@ snapshots: path-exists@4.0.0: {} + path-key@2.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -8136,10 +9025,17 @@ snapshots: proxy-from-env@1.1.0: {} + pump@3.0.2: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + punycode.js@2.3.1: {} punycode@2.3.1: {} + q@1.5.1: {} + qs@6.13.0: dependencies: side-channel: 1.1.0 @@ -8150,6 +9046,8 @@ snapshots: queue-microtask@1.2.3: {} + quick-lru@4.0.1: {} + range-parser@1.2.1: {} raw-body@2.5.2: @@ -8183,6 +9081,21 @@ snapshots: parse-json: 5.2.0 type-fest: 0.6.0 + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + rechoir@0.6.2: + dependencies: + resolve: 1.22.10 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -8309,6 +9222,10 @@ snapshots: '@unrs/rspack-resolver-binding-win32-arm64-msvc': 1.2.2 '@unrs/rspack-resolver-binding-win32-x64-msvc': 1.2.2 + run-applescript@7.0.0: {} + + run-async@3.0.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -8425,12 +9342,25 @@ snapshots: setprototypeof@1.2.0: {} + shebang-command@1.2.0: + dependencies: + shebang-regex: 1.0.0 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 + shebang-regex@1.0.0: {} + shebang-regex@3.0.0: {} + shelljs@0.9.2: + dependencies: + execa: 1.0.0 + fast-glob: 3.3.3 + interpret: 1.4.0 + rechoir: 0.6.2 + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -8461,6 +9391,8 @@ snapshots: siginfo@2.0.0: {} + signal-exit@3.0.7: {} + signal-exit@4.1.0: {} slash@3.0.0: {} @@ -8494,6 +9426,10 @@ snapshots: spdx-license-ids@3.0.21: {} + split2@3.2.2: + dependencies: + readable-stream: 3.6.2 + sprintf-js@1.0.3: {} stable-hash@0.0.5: {} @@ -8568,6 +9504,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -8578,6 +9518,8 @@ snapshots: strip-bom@3.0.0: {} + strip-eof@1.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 @@ -8598,6 +9540,10 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} synckit@0.10.3: @@ -8610,12 +9556,16 @@ snapshots: '@pkgr/core': 0.1.2 tslib: 2.8.1 + tapable@2.2.1: {} + test-exclude@7.0.1: dependencies: '@istanbuljs/schema': 0.1.3 glob: 10.4.5 minimatch: 9.0.5 + text-extensions@1.9.0: {} + text-table@0.2.0: {} thenify-all@1.6.0: @@ -8626,6 +9576,12 @@ snapshots: dependencies: any-promise: 1.3.0 + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + + through@2.3.8: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -8641,12 +9597,18 @@ snapshots: tinyspy@3.0.2: {} + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 toidentifier@1.0.1: {} + trim-newlines@3.0.1: {} + ts-api-utils@2.1.0(typescript@5.8.2): dependencies: typescript: 5.8.2 @@ -8690,8 +9652,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@0.18.1: {} + type-fest@0.20.2: {} + type-fest@0.21.3: {} + type-fest@0.6.0: {} type-fest@0.8.1: {} @@ -8751,6 +9717,8 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + undici-types@6.19.8: {} + undici-types@6.20.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -8778,6 +9746,8 @@ snapshots: dependencies: punycode: 2.3.1 + util-deprecate@1.0.2: {} + utils-merge@1.0.1: {} validate-npm-package-license@3.0.4: @@ -8906,6 +9876,10 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -8917,6 +9891,12 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -8937,6 +9917,8 @@ snapshots: yaml@1.10.2: {} + yargs-parser@20.2.9: {} + yocto-queue@0.1.0: {} zod-to-json-schema@3.24.5(zod@3.24.2): diff --git a/common/git-hooks/commit-msg b/common/git-hooks/commit-msg index 08c413eb..e5756368 100755 --- a/common/git-hooks/commit-msg +++ b/common/git-hooks/commit-msg @@ -34,4 +34,6 @@ else node common/scripts/install-run-rush.js -q commitlint \ --config common/autoinstallers/rush-commitlint/commitlint.config.js \ --edit "$1" || exit 1 + + node common/scripts/install-run-rush.js change-x || exit 1 fi diff --git a/common/git-hooks/post-commit b/common/git-hooks/post-commit new file mode 100755 index 00000000..48d78121 --- /dev/null +++ b/common/git-hooks/post-commit @@ -0,0 +1,11 @@ +#!/bin/bash + +# rebase 过程中分支名格式为 (no branch, rebasing chore/replace-rushtool) +# 正常提交时分支名格式为 chore/replace-rushtool +BRANCH_NAME=$(git branch | grep '*' | sed 's/* //') +# 如果匹配到 rebase 格式的输出,认为是在rebase ,则跳过自动推送 +if [[ "X${BRANCH_NAME}" == "X(no branch"* ]]; then + exit +else + node common/scripts/install-run-rush.js change-x -a || exit 1 +fi diff --git a/config/eslint-config/package.json b/config/eslint-config/package.json index c08eeecb..8f378156 100644 --- a/config/eslint-config/package.json +++ b/config/eslint-config/package.json @@ -1,5 +1,5 @@ { - "name": "@coze-infra/eslint-config", + "name": "@coze-arch/eslint-config", "version": "0.0.1", "author": "fanwenjie.fe@bytedance.com", "maintainers": [], @@ -35,7 +35,7 @@ "@babel/plugin-syntax-jsx": "~7.23.3", "@babel/preset-env": "~7.20.2", "@babel/preset-react": "~7.13.13", - "@coze-infra/ts-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", "@eslint/compat": "~1.2.0", "@eslint/eslintrc": "~3.1.0", "@rushstack/eslint-config": "~3.1.1", @@ -68,7 +68,7 @@ "sucrase": "^3.32.0" }, "devDependencies": { - "@coze-infra/ts-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", "@types/eslint": "~9.6.1", "@types/node": "^22.13.13", "typescript": "^5.8.2" diff --git a/config/eslint-config/tsconfig.build.json b/config/eslint-config/tsconfig.build.json index cba15b1f..b954665a 100644 --- a/config/eslint-config/tsconfig.build.json +++ b/config/eslint-config/tsconfig.build.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/tsconfig", - "extends": "@coze-infra/ts-config/tsconfig.node.json", + "extends": "@coze-arch/ts-config/tsconfig.node.json", "compilerOptions": { "sourceMap": false, "lib": ["ES2021"], diff --git a/config/eslint-config/tsconfig.misc.json b/config/eslint-config/tsconfig.misc.json index 9ae59d3a..e77b97a1 100644 --- a/config/eslint-config/tsconfig.misc.json +++ b/config/eslint-config/tsconfig.misc.json @@ -1,5 +1,5 @@ { - "extends": "@coze-infra/ts-config/tsconfig.node.json", + "extends": "@coze-arch/ts-config/tsconfig.node.json", "$schema": "https://json.schemastore.org/tsconfig", "include": ["__tests__", "vitest.config.ts"], "exclude": ["./dist"], diff --git a/config/ts-config/package.json b/config/ts-config/package.json index 87446abb..e1cae318 100644 --- a/config/ts-config/package.json +++ b/config/ts-config/package.json @@ -1,5 +1,5 @@ { - "name": "@coze-infra/ts-config", + "name": "@coze-arch/ts-config", "version": "1.0.0", "description": "", "keywords": [], diff --git a/config/vitest-config/eslint.config.js b/config/vitest-config/eslint.config.js index 37c7b079..609e4faa 100644 --- a/config/vitest-config/eslint.config.js +++ b/config/vitest-config/eslint.config.js @@ -1,4 +1,4 @@ -const { defineConfig } = require('@coze-infra/eslint-config'); +const { defineConfig } = require('@coze-arch/eslint-config'); module.exports = defineConfig({ preset: 'node', diff --git a/config/vitest-config/package.json b/config/vitest-config/package.json index 06ddf57e..4ee02c6a 100644 --- a/config/vitest-config/package.json +++ b/config/vitest-config/package.json @@ -1,5 +1,5 @@ { - "name": "@coze-infra/vitest-config", + "name": "@coze-arch/vitest-config", "version": "0.0.1", "author": "fanwenjie.fe@bytedance.com", "maintainers": [], @@ -13,8 +13,8 @@ "vite-tsconfig-paths": "^4.2.1" }, "devDependencies": { - "@coze-infra/eslint-config": "workspace:*", - "@coze-infra/ts-config": "workspace:*", + "@coze-arch/eslint-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", "@types/node": "^22.13.13", "@vitejs/plugin-react": "^4.3.4", "@vitest/coverage-v8": "^3.0.9", diff --git a/config/vitest-config/tsconfig.json b/config/vitest-config/tsconfig.json index b4c9ffd1..3ad10769 100644 --- a/config/vitest-config/tsconfig.json +++ b/config/vitest-config/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@coze-infra/ts-config/tsconfig.node.json", + "extends": "@coze-arch/ts-config/tsconfig.node.json", "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", diff --git a/packages/mcp/npm/.npmrc b/packages/mcp/npm/.npmrc new file mode 100644 index 00000000..fb7f2353 --- /dev/null +++ b/packages/mcp/npm/.npmrc @@ -0,0 +1 @@ +//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} diff --git a/packages/mcp/npm/README.md b/packages/mcp/npm/README.md index df1e6fe3..a4edcac3 100644 --- a/packages/mcp/npm/README.md +++ b/packages/mcp/npm/README.md @@ -26,7 +26,7 @@ NPM MCP Server is a Model Context Protocol server implementation for NPM. It pro You can directly use the NPM MCP Server without installing it locally via `npx`: ```sh -npx @coze-infra/npm-mcp-server@latest serve +npx @coze-arch/npm-mcp-server@latest serve ``` This will start the MCP server on port 3000 by default. You can access it at `http://localhost:3000/sse`. @@ -66,13 +66,13 @@ The NPM MCP Server can be run in two different modes: Start the server to listen on HTTP requests using npx: ```sh -npx @coze-infra/npm-mcp-server@latest serve +npx @coze-arch/npm-mcp-server@latest serve ``` You can specify a different port: ```sh -npx @coze-infra/npm-mcp-server@latest serve --port 4000 +npx @coze-arch/npm-mcp-server@latest serve --port 4000 ``` Access the server at: `http://localhost:/sse` @@ -82,7 +82,7 @@ Access the server at: `http://localhost:/sse` Run the server directly in the terminal: ```sh -npx @coze-infra/npm-mcp-server@latest start +npx @coze-arch/npm-mcp-server@latest start ``` ### Development diff --git a/packages/mcp/npm/eslint.config.js b/packages/mcp/npm/eslint.config.js index 37c7b079..609e4faa 100644 --- a/packages/mcp/npm/eslint.config.js +++ b/packages/mcp/npm/eslint.config.js @@ -1,4 +1,4 @@ -const { defineConfig } = require('@coze-infra/eslint-config'); +const { defineConfig } = require('@coze-arch/eslint-config'); module.exports = defineConfig({ preset: 'node', diff --git a/packages/mcp/npm/package.json b/packages/mcp/npm/package.json index 58df384a..d7c62dc6 100644 --- a/packages/mcp/npm/package.json +++ b/packages/mcp/npm/package.json @@ -1,5 +1,5 @@ { - "name": "@coze-infra/npm-mcp-server", + "name": "@coze-arch/npm-mcp-server", "version": "0.0.1-alpha.1", "description": "NPM MCP Server", "repository": { @@ -40,10 +40,9 @@ "zod": "^3.24.2" }, "devDependencies": { - "@commitlint/types": "^17.4.0", - "@coze-infra/eslint-config": "workspace:*", - "@coze-infra/ts-config": "workspace:*", - "@coze-infra/vitest-config": "workspace:*", + "@coze-arch/eslint-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", + "@coze-arch/vitest-config": "workspace:*", "@rollup/plugin-commonjs": "^24.0.0", "@rollup/plugin-json": "~6.0.0", "@rollup/plugin-node-resolve": "~15.0.1", diff --git a/packages/mcp/npm/tsconfig.build.json b/packages/mcp/npm/tsconfig.build.json index af42917d..9e12ddbd 100644 --- a/packages/mcp/npm/tsconfig.build.json +++ b/packages/mcp/npm/tsconfig.build.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/tsconfig", - "extends": "@coze-infra/ts-config/tsconfig.node.json", + "extends": "@coze-arch/ts-config/tsconfig.node.json", "compilerOptions": { "outDir": "dist", "rootDir": "src", diff --git a/packages/mcp/npm/tsconfig.misc.json b/packages/mcp/npm/tsconfig.misc.json index 503ec508..4090d8cf 100644 --- a/packages/mcp/npm/tsconfig.misc.json +++ b/packages/mcp/npm/tsconfig.misc.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/tsconfig", - "extends": "@coze-infra/ts-config/tsconfig.node.json", + "extends": "@coze-arch/ts-config/tsconfig.node.json", "compilerOptions": { "rootDir": "./", "outDir": "./dist", diff --git a/packages/rush-plugins/publish/.npmrc b/packages/rush-plugins/publish/.npmrc new file mode 100644 index 00000000..fb7f2353 --- /dev/null +++ b/packages/rush-plugins/publish/.npmrc @@ -0,0 +1 @@ +//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} diff --git a/packages/rush-plugins/publish/README.md b/packages/rush-plugins/publish/README.md new file mode 100644 index 00000000..ab3d5e80 --- /dev/null +++ b/packages/rush-plugins/publish/README.md @@ -0,0 +1 @@ +# @coze-arch/rush-change-plugin diff --git a/packages/rush-plugins/publish/__tests__/actions/change/action.test.ts b/packages/rush-plugins/publish/__tests__/actions/change/action.test.ts new file mode 100644 index 00000000..01771c2b --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/change/action.test.ts @@ -0,0 +1,105 @@ +import path from 'path'; +import fs from 'fs/promises'; + +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +import * as projectAnalyzer from '@/utils/get-rush-config'; +import * as helper from '@/action/change/helper'; +import * as amendCommit from '@/action/change/amend-commit'; +import { generateChangeFiles } from '@/action/change/action'; + +// Mock all dependencies +vi.mock('fs/promises'); +vi.mock('path'); +vi.mock('@coze-arch/logger'); +vi.mock('@/action/change/helper'); +vi.mock('@/action/change/amend-commit'); +vi.mock('@/utils/get-rush-config'); + +describe('generateChangeFiles', () => { + beforeEach(() => { + vi.clearAllMocks(); + // Reset environment variables + delete process.env.CI; + }); + + it('should do nothing in CI environment', async () => { + process.env.CI = 'true'; + await generateChangeFiles({ ci: false }); + expect(helper.analysisCommitMsg).not.toHaveBeenCalled(); + }); + + it('should do nothing when ci option is true', async () => { + await generateChangeFiles({ ci: true }); + expect(helper.analysisCommitMsg).not.toHaveBeenCalled(); + }); + + it('should handle amend commit option', async () => { + await generateChangeFiles({ amendCommit: true }); + expect(amendCommit.amendCommit).toHaveBeenCalled(); + expect(helper.analysisCommitMsg).not.toHaveBeenCalled(); + }); + + it('should read commit message from file when not provided', async () => { + const mockCommitMsg = 'feat: test commit'; + const mockRushConfig = { + rushJsonFolder: '/test/path', + }; + + vi.mocked(projectAnalyzer.getRushConfiguration).mockReturnValue( + mockRushConfig as any, + ); + vi.mocked(fs.readFile).mockResolvedValue(mockCommitMsg); + vi.mocked(helper.analysisCommitMsg).mockResolvedValue({ + content: 'test commit', + type: 'minor', + }); + + await generateChangeFiles({}); + + expect(fs.readFile).toHaveBeenCalledWith( + path.resolve('/test/path', '.git/COMMIT_EDITMSG'), + 'utf-8', + ); + expect(helper.analysisCommitMsg).toHaveBeenCalledWith(mockCommitMsg); + expect(helper.generateAllChangesFile).toHaveBeenCalledWith( + 'test commit', + 'minor', + ); + }); + + it('should use provided commit message', async () => { + const mockCommitMsg = 'feat: direct commit'; + vi.mocked(helper.analysisCommitMsg).mockResolvedValue({ + content: 'direct commit', + type: 'minor', + }); + + await generateChangeFiles({ commitMsg: mockCommitMsg }); + + expect(fs.readFile).not.toHaveBeenCalled(); + expect(helper.analysisCommitMsg).toHaveBeenCalledWith(mockCommitMsg); + expect(helper.generateAllChangesFile).toHaveBeenCalledWith( + 'direct commit', + 'minor', + ); + }); + + it('should handle invalid commit message', async () => { + vi.mocked(helper.analysisCommitMsg).mockResolvedValue({ + content: '', + type: 'minor', + }); + + await generateChangeFiles({ commitMsg: 'invalid commit' }); + + expect(helper.generateAllChangesFile).not.toHaveBeenCalled(); + }); + + it('should handle errors', async () => { + const error = new Error('Test error'); + vi.mocked(helper.analysisCommitMsg).mockRejectedValue(error); + + await generateChangeFiles({ commitMsg: 'test commit' }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/change/amend-commit.test.ts b/packages/rush-plugins/publish/__tests__/actions/change/amend-commit.test.ts new file mode 100644 index 00000000..5152a06d --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/change/amend-commit.test.ts @@ -0,0 +1,112 @@ +import path from 'path'; + +import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest'; + +import * as gitCommand from '@/utils/git'; +import * as projectAnalyzer from '@/utils/get-rush-config'; +import { exec } from '@/utils/exec'; +import { amendCommit } from '@/action/change/amend-commit'; + +// Mock all dependencies +vi.mock('path', () => ({ default: { relative: vi.fn() } })); +vi.mock('@/utils/git'); +vi.mock('@/utils/get-rush-config'); +vi.mock('@/utils/exec', () => ({ + exec: vi.fn(), +})); + +describe('amendCommit', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should amend commit when changes file is detected', async () => { + // Mock rush configuration + const mockRushConfig = { + rushJsonFolder: '/root/path', + changesFolder: '/root/path/common/changes', + }; + vi.mocked(projectAnalyzer.getRushConfiguration).mockReturnValue( + mockRushConfig as any, + ); + + // Mock path.relative to return the changes folder path + (path.relative as Mock).mockReturnValue('common/changes'); + + // Mock git changed files to include a changes file + vi.mocked(gitCommand.getChangedFilesFromCached).mockResolvedValue([ + 'common/changes/my-package.json', + 'some/other/file.js', + ]); + + await amendCommit(); + + // Verify the correct paths were used + expect(path.relative).toHaveBeenCalledWith( + '/root/path', + '/root/path/common/changes', + ); + + // Verify git command was executed + expect(exec).toHaveBeenCalledWith('git commit --amend --no-edit -n'); + }); + + it('should not amend commit when no changes file is detected', async () => { + // Mock rush configuration + const mockRushConfig = { + rushJsonFolder: '/root/path', + changesFolder: '/root/path/common/changes', + }; + vi.mocked(projectAnalyzer.getRushConfiguration).mockReturnValue( + mockRushConfig as any, + ); + + // Mock path.relative to return the changes folder path + vi.mocked(path.relative).mockReturnValue('common/changes'); + + // Mock git changed files to not include any changes file + vi.mocked(gitCommand.getChangedFilesFromCached).mockResolvedValue([ + 'src/index.ts', + 'package.json', + ]); + + await amendCommit(); + + // Verify path.relative was still called + expect(path.relative).toHaveBeenCalledWith( + '/root/path', + '/root/path/common/changes', + ); + + // Verify git command was not executed + expect(exec).not.toHaveBeenCalled(); + }); + + it('should handle empty changed files array', async () => { + // Mock rush configuration + const mockRushConfig = { + rushJsonFolder: '/root/path', + changesFolder: '/root/path/common/changes', + }; + vi.mocked(projectAnalyzer.getRushConfiguration).mockReturnValue( + mockRushConfig as any, + ); + + // Mock path.relative + vi.mocked(path.relative).mockReturnValue('common/changes'); + + // Mock git changed files to return empty array + vi.mocked(gitCommand.getChangedFilesFromCached).mockResolvedValue([]); + + await amendCommit(); + + // Verify path.relative was called + expect(path.relative).toHaveBeenCalledWith( + '/root/path', + '/root/path/common/changes', + ); + + // Verify git command was not executed + expect(exec).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/change/helper.test.ts b/packages/rush-plugins/publish/__tests__/actions/change/helper.test.ts new file mode 100644 index 00000000..f2367d96 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/change/helper.test.ts @@ -0,0 +1,170 @@ +import path from 'path'; + +import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest'; + +import * as gitCommand from '@/utils/git'; +import * as projectAnalyzer from '@/utils/get-rush-config'; +import * as helper from '@/action/change/helper'; + +// Mock all dependencies +vi.mock('path', () => ({ default: { relative: vi.fn(), dirname: vi.fn() } })); +vi.mock('@rushstack/rush-sdk/lib/api/ChangeFile'); +vi.mock('@coze-arch/logger'); +vi.mock('@/utils/get-rush-config'); +vi.mock('@/utils/git'); +vi.mock('@/utils/exec'); +vi.mock('@/utils/env'); + +describe('helper functions', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('collectShouldUpdateChangesProjects', () => { + it('should collect projects that need changes update', async () => { + const mockRushConfig = { + rushJsonFolder: '/root/path', + changesFolder: '/root/path/common/changes', + getProjectLookupForRoot: vi.fn(), + }; + + const mockProject = { + packageName: 'test-package', + shouldPublish: true, + }; + + const mockLookup = { + findChildPath: vi.fn(), + }; + + vi.mocked(projectAnalyzer.getRushConfiguration).mockReturnValue( + mockRushConfig as any, + ); + mockRushConfig.getProjectLookupForRoot.mockReturnValue(mockLookup); + mockLookup.findChildPath.mockReturnValue(mockProject); + + (path.relative as Mock) + .mockReturnValueOnce('common/changes') + .mockReturnValueOnce('test-package'); + + vi.mocked(gitCommand.getChangedFilesFromCached).mockResolvedValue([ + 'packages/test-package/src/index.ts', + 'common/changes/other-package/change.json', + ]); + + const result = await helper.collectShouldUpdateChangesProjects(); + + expect(result).toEqual([mockProject]); + }); + + it('should handle empty changed files', async () => { + const mockRushConfig = { + rushJsonFolder: '/root/path', + changesFolder: '/root/path/common/changes', + getProjectLookupForRoot: vi.fn().mockReturnValue({ + findChildPath: vi.fn(), + }), + }; + + vi.mocked(projectAnalyzer.getRushConfiguration).mockReturnValue( + mockRushConfig as any, + ); + vi.mocked(gitCommand.getChangedFilesFromCached).mockResolvedValue([]); + + const result = await helper.collectShouldUpdateChangesProjects(); + expect(result).toEqual([]); + }); + }); + + describe('parseCommit', () => { + it('should parse commit message correctly', async () => { + const message = 'feat(scope): test commit message'; + const result = await helper.parseCommit(message); + + expect(result).toMatchObject({ + type: 'feat', + scope: 'scope', + subject: 'test commit message', + raw: message, + }); + }); + + it('should handle commit message without scope', async () => { + const message = 'fix: test commit'; + const result = await helper.parseCommit(message); + + expect(result).toMatchObject({ + type: 'fix', + subject: 'test commit', + raw: message, + }); + }); + }); + + describe('whatBump', () => { + it('should determine major version bump for breaking changes', () => { + const commits = [ + { + notes: [ + { title: 'BREAKING CHANGE', text: 'breaking change description' }, + ], + type: 'feat', + }, + ]; + + const result = helper.whatBump(commits as any); + + expect(result).toEqual({ + level: 0, + releaseType: 'major', + reason: 'There is 1 BREAKING CHANGE and 0 features', + }); + }); + + it('should determine minor version bump for features', () => { + const commits = [ + { + notes: [], + type: 'feat', + }, + ]; + + const result = helper.whatBump(commits as any); + + expect(result).toEqual({ + level: 1, + releaseType: 'minor', + reason: 'There are 0 BREAKING CHANGES and 1 features', + }); + }); + + it('should determine patch version bump for other changes', () => { + const commits = [ + { + notes: [], + type: 'fix', + }, + ]; + + const result = helper.whatBump(commits as any); + + expect(result).toEqual({ + level: 2, + releaseType: 'patch', + reason: 'There are 0 BREAKING CHANGES and 0 features', + }); + }); + }); + + describe('analysisCommitMsg', () => { + it('should analyze commit message and return type and content', async () => { + const message = 'feat(scope): test feature'; + const result = await helper.analysisCommitMsg(message); + + expect(result).toEqual({ + type: 'minor', + content: 'test feature', + }); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/action.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/action.test.ts new file mode 100644 index 00000000..49617aab --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/action.test.ts @@ -0,0 +1,256 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { + type RushConfigurationProject, + type VersionPolicy, +} from '@rushstack/rush-sdk'; +import { logger } from '@coze-arch/logger'; + +import { randomHash } from '@/utils/random'; +import { ensureNotUncommittedChanges, isMainBranch } from '@/utils/git'; +import { getRushConfiguration } from '@/utils/get-rush-config'; +import { generatePublishManifest } from '@/action/publish/version'; +import { BumpType, type PublishOptions } from '@/action/publish/types'; +import { pushToRemote } from '@/action/publish/push-to-remote'; +import { validateAndGetPackages } from '@/action/publish/packages'; +import { confirmForPublish } from '@/action/publish/confirm'; +import { generateChangelog } from '@/action/publish/changelog'; +import { applyPublishManifest } from '@/action/publish/apply-new-version'; +import { publish } from '@/action/publish/action'; + +// Mock dependencies +vi.mock('@coze-arch/logger'); +vi.mock('@/utils/random'); +vi.mock('@/utils/get-rush-config'); +vi.mock('@/utils/git'); +vi.mock('@/action/publish/version'); +vi.mock('@/action/publish/packages'); +vi.mock('@/action/publish/confirm'); +vi.mock('@/action/publish/changelog'); +vi.mock('@/action/publish/apply-new-version'); +vi.mock('@/action/publish/push-to-remote'); + +describe('publish action', () => { + const mockRushFolder = '/mock/rush'; + const mockSessionId = 'abc123'; + + const createMockProject = (name: string): RushConfigurationProject => { + const project = { + _shouldPublish: true, + _versionPolicy: null as VersionPolicy | null, + _dependencyProjects: new Set(), + _consumingProjects: new Set(), + _packageJson: {}, + projectRelativeFolder: `packages/${name}`, + projectRushConfigFolder: '/mock/rush/config', + projectRushTempFolder: '/mock/rush/temp', + tempProjectName: name, + isMainProject: false, + dependencyProjects: new Set(), + consumingProjects: new Set(), + shouldPublish: true, + versionPolicyName: undefined as string | undefined, + decoupledLocalDependencies: [] as string[], + packageName: name, + projectFolder: `/mock/project/${name}`, + rushConfiguration: {} as any, + reviewCategory: undefined as string | undefined, + packageJson: {} as any, + packageJsonEditor: {} as any, + isPublished: true, + statusValue: 0, + statusMessage: '', + cyclicDependencyProjects: new Set(), + localDependencyProjects: new Set(), + }; + return project as unknown as RushConfigurationProject; + }; + + const mockProject1 = createMockProject('package-1'); + const mockProject2 = createMockProject('package-2'); + + const mockPackages = new Set([mockProject1, mockProject2]); + const mockPublishManifests = [ + { + project: mockProject1, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }, + { + project: mockProject2, + currentVersion: '2.0.0', + newVersion: '2.1.0', + }, + ]; + const mockChangedFiles = ['package.json', 'CHANGELOG.md']; + + beforeEach(() => { + vi.clearAllMocks(); + process.env.SKIP_UNCOMMITTED_CHECK = 'false'; + + // Setup default mocks + vi.mocked(randomHash).mockReturnValue(mockSessionId); + vi.mocked(getRushConfiguration).mockReturnValue({ + rushJsonFolder: mockRushFolder, + } as any); + vi.mocked(validateAndGetPackages).mockReturnValue(mockPackages); + vi.mocked(generatePublishManifest).mockResolvedValue({ + manifests: mockPublishManifests, + bumpPolicy: BumpType.PATCH, + }); + vi.mocked(confirmForPublish).mockResolvedValue(true); + vi.mocked(applyPublishManifest).mockResolvedValue(['package.json']); + vi.mocked(generateChangelog).mockResolvedValue(['CHANGELOG.md']); + vi.mocked(isMainBranch).mockResolvedValue(true); + }); + + it('should publish packages successfully', async () => { + const options: PublishOptions = { + to: ['package-1'], + }; + + await publish(options); + + // 验证流程 + expect(validateAndGetPackages).toHaveBeenCalledWith(options); + expect(generatePublishManifest).toHaveBeenCalledWith(mockPackages, { + ...options, + sessionId: mockSessionId, + }); + expect(confirmForPublish).toHaveBeenCalledWith(mockPublishManifests, false); + expect(applyPublishManifest).toHaveBeenCalledWith(mockPublishManifests); + expect(generateChangelog).toHaveBeenCalledWith(mockPublishManifests); + expect(pushToRemote).toHaveBeenCalledWith({ + publishManifests: mockPublishManifests, + bumpPolicy: BumpType.PATCH, + sessionId: mockSessionId, + changedFiles: mockChangedFiles, + cwd: mockRushFolder, + skipCommit: false, + skipPush: false, + }); + expect(logger.success).toHaveBeenCalledWith('Publish success.'); + }); + + it('should skip uncommitted changes check when SKIP_UNCOMMITTED_CHECK is true', async () => { + process.env.SKIP_UNCOMMITTED_CHECK = 'true'; + await publish({ to: ['package-1'] }); + expect(ensureNotUncommittedChanges).not.toHaveBeenCalled(); + }); + + it('should perform uncommitted changes check by default', async () => { + process.env.SKIP_UNCOMMITTED_CHECK = 'false'; + await publish({ to: ['package-1'] }); + expect(ensureNotUncommittedChanges).toHaveBeenCalled(); + }); + + it('should stop if no packages to publish', async () => { + vi.mocked(validateAndGetPackages).mockReturnValue(new Set()); + await publish({ to: ['package-1'] }); + expect(logger.error).toHaveBeenCalledWith( + 'No packages to publish, should specify some package by `--to` or `--from` or `--only`', + ); + expect(generatePublishManifest).not.toHaveBeenCalled(); + }); + + it('should stop if not in main branch for production release', async () => { + vi.mocked(isMainBranch).mockResolvedValue(false); + await publish({ to: ['package-1'] }); + expect(logger.error).toHaveBeenCalledWith( + 'You are not in main branch, please switch to main branch and try again.', + ); + expect(applyPublishManifest).not.toHaveBeenCalled(); + }); + + it('should allow non-main branch for beta releases', async () => { + vi.mocked(isMainBranch).mockResolvedValue(false); + vi.mocked(generatePublishManifest).mockResolvedValue({ + manifests: mockPublishManifests, + bumpPolicy: BumpType.BETA, + }); + + await publish({ to: ['package-1'] }); + + expect(logger.error).not.toHaveBeenCalled(); + expect(applyPublishManifest).toHaveBeenCalled(); + }); + + it('should allow non-main branch for alpha releases', async () => { + vi.mocked(isMainBranch).mockResolvedValue(false); + vi.mocked(generatePublishManifest).mockResolvedValue({ + manifests: mockPublishManifests, + bumpPolicy: BumpType.ALPHA, + }); + + await publish({ to: ['package-1'] }); + + expect(logger.error).not.toHaveBeenCalled(); + expect(applyPublishManifest).toHaveBeenCalled(); + }); + + it('should stop if user does not confirm', async () => { + vi.mocked(confirmForPublish).mockResolvedValue(false); + await publish({ to: ['package-1'] }); + expect(applyPublishManifest).not.toHaveBeenCalled(); + expect(generateChangelog).not.toHaveBeenCalled(); + expect(pushToRemote).not.toHaveBeenCalled(); + }); + + it('should skip changelog generation for beta releases', async () => { + vi.mocked(generatePublishManifest).mockResolvedValue({ + manifests: mockPublishManifests, + bumpPolicy: BumpType.BETA, + }); + + await publish({ to: ['package-1'] }); + + expect(applyPublishManifest).toHaveBeenCalled(); + expect(generateChangelog).not.toHaveBeenCalled(); + expect(pushToRemote).toHaveBeenCalledWith( + expect.objectContaining({ + bumpPolicy: BumpType.BETA, + changedFiles: ['package.json'], // 只有 package.json 被修改 + }), + ); + }); + + it('should skip changelog generation for alpha releases', async () => { + vi.mocked(generatePublishManifest).mockResolvedValue({ + manifests: mockPublishManifests, + bumpPolicy: BumpType.ALPHA, + }); + + await publish({ to: ['package-1'] }); + + expect(applyPublishManifest).toHaveBeenCalled(); + expect(generateChangelog).not.toHaveBeenCalled(); + expect(pushToRemote).toHaveBeenCalledWith( + expect.objectContaining({ + bumpPolicy: BumpType.ALPHA, + changedFiles: ['package.json'], // 只有 package.json 被修改 + }), + ); + }); + + it('should respect skipCommit option', async () => { + await publish({ to: ['package-1'], skipCommit: true }); + expect(pushToRemote).toHaveBeenCalledWith( + expect.objectContaining({ + skipCommit: true, + }), + ); + }); + + it('should respect skipPush option', async () => { + await publish({ to: ['package-1'], skipPush: true }); + expect(pushToRemote).toHaveBeenCalledWith( + expect.objectContaining({ + skipPush: true, + }), + ); + }); + + it('should respect dryRun option', async () => { + await publish({ to: ['package-1'], dryRun: true }); + expect(confirmForPublish).toHaveBeenCalledWith(mockPublishManifests, true); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/apply-new-version.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/apply-new-version.test.ts new file mode 100644 index 00000000..701d5b4a --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/apply-new-version.test.ts @@ -0,0 +1,205 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; +import { logger } from '@coze-arch/logger'; +import { readJsonFile, writeJsonFile } from '@coze-arch/fs-enhance'; + +import { type PublishManifest } from '@/action/publish/types'; +import { applyPublishManifest } from '@/action/publish/apply-new-version'; + +// Mock dependencies +vi.mock('@coze-arch/logger'); +vi.mock('@coze-arch/fs-enhance'); +vi.mock('path', () => ({ + default: { resolve: (...args: string[]) => args.join('/') }, +})); + +describe('apply-new-version', () => { + const mockProjectFolder = '/mock/project'; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('applyPublishManifest', () => { + it('should update version in package.json for a single package', async () => { + const mockPackageJson = { + name: 'test-package', + version: '1.0.0', + }; + + const mockProject: RushConfigurationProject = { + packageName: 'test-package', + projectFolder: mockProjectFolder, + } as any; + + const manifest: PublishManifest = { + project: mockProject, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }; + + vi.mocked(readJsonFile).mockResolvedValue(mockPackageJson); + + const result = await applyPublishManifest([manifest]); + + // 验证文件读写操作 + expect(readJsonFile).toHaveBeenCalledWith( + `${mockProjectFolder}/package.json`, + ); + expect(writeJsonFile).toHaveBeenCalledWith( + `${mockProjectFolder}/package.json`, + { + ...mockPackageJson, + version: '1.1.0', + }, + ); + + // 验证日志输出 + expect(logger.info).toHaveBeenCalledWith( + 'Updated version for packages: test-package', + ); + + // 验证返回的修改文件列表 + expect(result).toEqual([`${mockProjectFolder}/package.json`]); + }); + + it('should update version in package.json for multiple packages', async () => { + const mockPackageJson1 = { + name: 'package-1', + version: '1.0.0', + }; + + const mockPackageJson2 = { + name: 'package-2', + version: '2.0.0', + }; + + const mockProject1: RushConfigurationProject = { + packageName: 'package-1', + projectFolder: `${mockProjectFolder}/package-1`, + } as any; + + const mockProject2: RushConfigurationProject = { + packageName: 'package-2', + projectFolder: `${mockProjectFolder}/package-2`, + } as any; + + const manifests: PublishManifest[] = [ + { + project: mockProject1, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }, + { + project: mockProject2, + currentVersion: '2.0.0', + newVersion: '2.1.0', + }, + ]; + + vi.mocked(readJsonFile).mockImplementation((filePath: string) => { + if (filePath.includes('package-1')) { + return Promise.resolve(mockPackageJson1); + } + if (filePath.includes('package-2')) { + return Promise.resolve(mockPackageJson2); + } + return Promise.reject(new Error('Unexpected file read')); + }); + + const result = await applyPublishManifest(manifests); + + // 验证文件读写操作 + expect(readJsonFile).toHaveBeenCalledWith( + `${mockProjectFolder}/package-1/package.json`, + ); + expect(readJsonFile).toHaveBeenCalledWith( + `${mockProjectFolder}/package-2/package.json`, + ); + + expect(writeJsonFile).toHaveBeenCalledWith( + `${mockProjectFolder}/package-1/package.json`, + { + ...mockPackageJson1, + version: '1.1.0', + }, + ); + expect(writeJsonFile).toHaveBeenCalledWith( + `${mockProjectFolder}/package-2/package.json`, + { + ...mockPackageJson2, + version: '2.1.0', + }, + ); + + // 验证日志输出 + expect(logger.info).toHaveBeenCalledWith( + 'Updated version for packages: package-1, package-2', + ); + + // 验证返回的修改文件列表 + expect(result).toEqual([ + `${mockProjectFolder}/package-1/package.json`, + `${mockProjectFolder}/package-2/package.json`, + ]); + }); + + it('should handle read error gracefully', async () => { + const mockProject: RushConfigurationProject = { + packageName: 'test-package', + projectFolder: mockProjectFolder, + } as any; + + const manifest: PublishManifest = { + project: mockProject, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }; + + vi.mocked(readJsonFile).mockRejectedValue(new Error('File read error')); + + await expect(applyPublishManifest([manifest])).rejects.toThrow( + 'File read error', + ); + expect(writeJsonFile).not.toHaveBeenCalled(); + expect(logger.info).not.toHaveBeenCalled(); + }); + + it('should handle write error gracefully', async () => { + const mockPackageJson = { + name: 'test-package', + version: '1.0.0', + }; + + const mockProject: RushConfigurationProject = { + packageName: 'test-package', + projectFolder: mockProjectFolder, + } as any; + + const manifest: PublishManifest = { + project: mockProject, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }; + + vi.mocked(readJsonFile).mockResolvedValue(mockPackageJson); + vi.mocked(writeJsonFile).mockRejectedValue(new Error('File write error')); + + await expect(applyPublishManifest([manifest])).rejects.toThrow( + 'File write error', + ); + expect(logger.info).not.toHaveBeenCalled(); + }); + + it('should handle empty manifest list', async () => { + const result = await applyPublishManifest([]); + + expect(readJsonFile).not.toHaveBeenCalled(); + expect(writeJsonFile).not.toHaveBeenCalled(); + expect(logger.info).toHaveBeenCalledWith( + 'Updated version for packages: ', + ); + expect(result).toEqual([]); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/changelog.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/changelog.test.ts new file mode 100644 index 00000000..aff4ebc5 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/changelog.test.ts @@ -0,0 +1,289 @@ +import fs from 'fs/promises'; + +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { + readJsonFile, + writeJsonFile, + isFileExists, + isDirExists, +} from '@coze-arch/fs-enhance'; + +import { getRushConfiguration } from '@/utils/get-rush-config'; +import { + generateChangelog as core, + type ChangeFile, + type ChangeLog, +} from '@/generate-changelog/generate-changelog'; +import { type PublishManifest } from '@/action/publish/types'; +import { generateChangelog } from '@/action/publish/changelog'; + +// Mock dependencies +vi.mock('fs/promises'); +vi.mock('@coze-arch/fs-enhance'); +vi.mock('@/utils/get-rush-config'); +vi.mock('@/generate-changelog/generate-changelog'); +vi.mock('path', () => ({ + default: { + resolve: (...args: string[]) => args.join('/'), + }, +})); + +describe('changelog', () => { + const mockChangesFolder = '/mock/changes'; + const mockProjectFolder = '/mock/project'; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(getRushConfiguration).mockReturnValue({ + changesFolder: mockChangesFolder, + } as any); + }); + + describe('generateChangelog', () => { + const mockChangeFile: ChangeFile = { + changes: [ + { + type: 'minor', + comment: 'Test change', + packageName: 'test-package', + }, + ], + packageName: 'test-package', + email: 'test@example.com', + }; + + const mockPreviousChangelog: ChangeLog = { + name: 'test-package', + entries: [ + { + version: '1.0.0', + tag: 'test-package_v1.0.0', + date: '2024-01-01', + comments: { + minor: [{ comment: 'Previous change' }], + }, + }, + ], + }; + + const mockNewChangelog: ChangeLog = { + name: 'test-package', + entries: [ + { + version: '1.1.0', + tag: 'test-package_v1.1.0', + date: '2024-01-02', + comments: { + minor: [{ comment: 'Test change' }], + }, + }, + ...mockPreviousChangelog.entries, + ], + }; + + const mockChangelogMarkdown = '# Changelog\n\n## 1.1.0\n\n- Test change'; + + it('should generate changelog for a package with changes', async () => { + // Mock file system operations + vi.mocked(isDirExists).mockResolvedValue(true); + vi.mocked(fs.readdir).mockResolvedValue(['change1.json'] as any); + vi.mocked(readJsonFile).mockImplementation((filePath: string) => { + if (filePath.endsWith('change1.json')) { + return Promise.resolve(mockChangeFile); + } + if (filePath.endsWith('CHANGELOG.json')) { + return Promise.resolve(mockPreviousChangelog); + } + return Promise.reject(new Error('Unexpected file read')); + }); + vi.mocked(isFileExists).mockResolvedValue(true); + vi.mocked(core).mockResolvedValue({ + changelog: mockNewChangelog, + report: mockChangelogMarkdown, + }); + + const manifest: PublishManifest = { + project: { + packageName: 'test-package', + projectFolder: mockProjectFolder, + } as any, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }; + + const result = await generateChangelog([manifest]); + + // Verify file operations + expect(writeJsonFile).toHaveBeenCalledWith( + `${mockProjectFolder}/CHANGELOG.json`, + mockNewChangelog, + ); + expect(fs.writeFile).toHaveBeenCalledWith( + `${mockProjectFolder}/CHANGELOG.md`, + mockChangelogMarkdown, + ); + expect(fs.unlink).toHaveBeenCalledWith( + `${mockChangesFolder}/test-package/change1.json`, + ); + + // Verify returned file paths + expect(result).toEqual([ + `${mockProjectFolder}/CHANGELOG.json`, + `${mockProjectFolder}/CHANGELOG.md`, + `${mockChangesFolder}/test-package/change1.json`, + ]); + }); + + it('should handle package without changes directory', async () => { + vi.mocked(isDirExists).mockResolvedValue(false); + vi.mocked(isFileExists).mockResolvedValue(false); + vi.mocked(core).mockResolvedValue({ + changelog: { name: 'test-package', entries: [] }, + report: '# Changelog\n', + }); + + const manifest: PublishManifest = { + project: { + packageName: 'test-package', + projectFolder: mockProjectFolder, + } as any, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }; + + const result = await generateChangelog([manifest]); + + expect(fs.readdir).not.toHaveBeenCalled(); + expect(result).toEqual([ + `${mockProjectFolder}/CHANGELOG.json`, + `${mockProjectFolder}/CHANGELOG.md`, + ]); + }); + + it('should handle package without existing changelog', async () => { + vi.mocked(isDirExists).mockResolvedValue(true); + vi.mocked(fs.readdir).mockResolvedValue(['change1.json'] as any); + vi.mocked(readJsonFile).mockImplementation((filePath: string) => { + if (filePath.endsWith('change1.json')) { + return Promise.resolve(mockChangeFile); + } + return Promise.reject(new Error('File not found')); + }); + vi.mocked(isFileExists).mockResolvedValue(false); + vi.mocked(core).mockResolvedValue({ + changelog: mockNewChangelog, + report: mockChangelogMarkdown, + }); + + const manifest: PublishManifest = { + project: { + packageName: 'test-package', + projectFolder: mockProjectFolder, + } as any, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }; + + const result = await generateChangelog([manifest]); + + expect(writeJsonFile).toHaveBeenCalledWith( + `${mockProjectFolder}/CHANGELOG.json`, + mockNewChangelog, + ); + expect(result).toContain(`${mockProjectFolder}/CHANGELOG.json`); + }); + + it('should handle invalid change files', async () => { + vi.mocked(isDirExists).mockResolvedValue(true); + vi.mocked(fs.readdir).mockResolvedValue([ + 'invalid.json', + 'valid.json', + ] as any); + vi.mocked(readJsonFile).mockImplementation((filePath: string) => { + if (filePath.endsWith('invalid.json')) { + return Promise.reject(new Error('Invalid JSON')); + } + if (filePath.endsWith('valid.json')) { + return Promise.resolve(mockChangeFile); + } + if (filePath.endsWith('CHANGELOG.json')) { + return Promise.resolve(mockPreviousChangelog); + } + return Promise.reject(new Error('Unexpected file read')); + }); + vi.mocked(isFileExists).mockResolvedValue(true); + vi.mocked(core).mockResolvedValue({ + changelog: mockNewChangelog, + report: mockChangelogMarkdown, + }); + + const manifest: PublishManifest = { + project: { + packageName: 'test-package', + projectFolder: mockProjectFolder, + } as any, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }; + + const result = await generateChangelog([manifest]); + + // Should still process valid change file + expect(core).toHaveBeenCalledWith( + expect.objectContaining({ + commingChanges: [mockChangeFile], + }), + ); + expect(result).toContain( + `${mockChangesFolder}/test-package/invalid.json`, + ); + expect(result).toContain(`${mockChangesFolder}/test-package/valid.json`); + }); + + it('should handle multiple packages', async () => { + vi.mocked(isDirExists).mockResolvedValue(true); + vi.mocked(fs.readdir).mockResolvedValue(['change1.json'] as any); + vi.mocked(readJsonFile).mockImplementation((filePath: string) => { + if (filePath.endsWith('change1.json')) { + return Promise.resolve(mockChangeFile); + } + if (filePath.endsWith('CHANGELOG.json')) { + return Promise.resolve(mockPreviousChangelog); + } + return Promise.reject(new Error('Unexpected file read')); + }); + vi.mocked(isFileExists).mockResolvedValue(true); + vi.mocked(core).mockResolvedValue({ + changelog: mockNewChangelog, + report: mockChangelogMarkdown, + }); + + const manifests: PublishManifest[] = [ + { + project: { + packageName: 'package-1', + projectFolder: `${mockProjectFolder}/package-1`, + } as any, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }, + { + project: { + packageName: 'package-2', + projectFolder: `${mockProjectFolder}/package-2`, + } as any, + currentVersion: '2.0.0', + newVersion: '2.1.0', + }, + ]; + + const result = await generateChangelog(manifests); + + // Should process both packages + expect(result).toContain(`${mockProjectFolder}/package-1/CHANGELOG.json`); + expect(result).toContain(`${mockProjectFolder}/package-1/CHANGELOG.md`); + expect(result).toContain(`${mockProjectFolder}/package-2/CHANGELOG.json`); + expect(result).toContain(`${mockProjectFolder}/package-2/CHANGELOG.md`); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/confirm.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/confirm.test.ts new file mode 100644 index 00000000..c42d06a7 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/confirm.test.ts @@ -0,0 +1,142 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { confirm } from '@inquirer/prompts'; + +import { type PublishManifest } from '@/action/publish/types'; +import { confirmForPublish } from '@/action/publish/confirm'; + +// Mock dependencies +vi.mock('@inquirer/prompts'); +vi.mock('chalk', () => ({ + default: { + gray: vi.fn((str: string) => `gray(${str})`), + bgGreen: vi.fn((str: string) => `bgGreen(${str})`), + bold: vi.fn((str: string) => `bold(${str})`), + }, +})); + +describe('confirm', () => { + const mockPublishManifests: PublishManifest[] = [ + { + project: { + packageName: 'test-package-1', + } as any, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }, + { + project: { + packageName: 'test-package-2', + } as any, + currentVersion: '2.0.0', + newVersion: '2.1.0', + }, + ]; + + beforeEach(() => { + vi.clearAllMocks(); + vi.spyOn(console, 'log').mockImplementation(vi.fn()); + vi.mocked(confirm).mockResolvedValue(true); + }); + + describe('confirmForPublish', () => { + it('should display package information correctly', async () => { + await confirmForPublish(mockPublishManifests, false); + + expect(console.log).toHaveBeenCalledWith( + 'gray(Will publish the following packages:)', + ); + expect(console.log).toHaveBeenCalledWith( + expect.stringContaining( + 'test-package-1: bgGreen(1.0.0 -> bold(1.1.0))', + ), + ); + expect(console.log).toHaveBeenCalledWith( + expect.stringContaining( + 'test-package-2: bgGreen(2.0.0 -> bold(2.1.0))', + ), + ); + }); + + it('should display empty line before confirmation prompt', async () => { + await confirmForPublish(mockPublishManifests, false); + expect(console.log).toHaveBeenCalledWith('\n'); + }); + + it('should return false when dry run is true', async () => { + const result = await confirmForPublish(mockPublishManifests, true); + expect(result).toBe(false); + expect(confirm).not.toHaveBeenCalled(); + }); + + it('should return true when user confirms', async () => { + vi.mocked(confirm).mockResolvedValueOnce(true); + const result = await confirmForPublish(mockPublishManifests, false); + expect(result).toBe(true); + expect(confirm).toHaveBeenCalledWith({ + message: 'Are you sure to publish?', + default: true, + }); + }); + + it('should return false when user cancels', async () => { + vi.mocked(confirm).mockResolvedValueOnce(false); + const result = await confirmForPublish(mockPublishManifests, false); + expect(result).toBe(false); + }); + + it('should return false when confirmation throws error', async () => { + vi.mocked(confirm).mockRejectedValueOnce(new Error('User cancelled')); + const result = await confirmForPublish(mockPublishManifests, false); + expect(result).toBe(false); + }); + + it('should handle empty manifest list', async () => { + await confirmForPublish([], false); + expect(console.log).toHaveBeenCalledWith( + 'gray(Will publish the following packages:)', + ); + expect(console.log).toHaveBeenCalledTimes(2); // 标题 + 空行 + }); + + it('should format version changes with correct styling', async () => { + const singleManifest: PublishManifest[] = [ + { + project: { + packageName: 'test-package', + } as any, + currentVersion: '1.0.0', + newVersion: '2.0.0', + }, + ]; + + await confirmForPublish(singleManifest, false); + + // 验证 chalk 样式的正确应用顺序 + expect(console.log).toHaveBeenCalledWith( + expect.stringContaining( + '- test-package: bgGreen(1.0.0 -> bold(2.0.0))', + ), + ); + }); + + it('should handle scoped package names', async () => { + const scopedManifest: PublishManifest[] = [ + { + project: { + packageName: '@scope/package', + } as any, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }, + ]; + + await confirmForPublish(scopedManifest, false); + + expect(console.log).toHaveBeenCalledWith( + expect.stringContaining( + '- @scope/package: bgGreen(1.0.0 -> bold(1.1.0))', + ), + ); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/git.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/git.test.ts new file mode 100644 index 00000000..172f4805 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/git.test.ts @@ -0,0 +1,122 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { logger } from '@coze-arch/logger'; + +import { exec } from '@/utils/exec'; +import { type PublishManifest } from '@/action/publish/types'; +import { createAndPushBranch, commitChanges, push } from '@/action/publish/git'; + +// Mock dependencies +vi.mock('@coze-arch/logger'); +vi.mock('@/utils/exec'); + +describe('git operations', () => { + const mockCwd = '/mock/cwd'; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(exec).mockResolvedValue({ stdout: '', stderr: '', code: 0 }); + }); + + describe('createAndPushBranch', () => { + it('should create and push a new branch', async () => { + const branchName = 'feature/test-branch'; + + await createAndPushBranch(branchName, mockCwd); + + expect(exec).toHaveBeenCalledTimes(2); + expect(exec).toHaveBeenNthCalledWith(1, `git checkout -b ${branchName}`, { + cwd: mockCwd, + }); + expect(exec).toHaveBeenNthCalledWith( + 2, + `git push -u origin ${branchName}`, + { + cwd: mockCwd, + }, + ); + expect(logger.info).toHaveBeenCalledWith( + `Created new branch: ${branchName}`, + ); + expect(logger.info).toHaveBeenCalledWith( + `Pushed branch to remote: ${branchName}`, + ); + }); + + it('should throw error when git command fails', async () => { + const branchName = 'feature/test-branch'; + const mockError = new Error('Git command failed'); + vi.mocked(exec).mockRejectedValueOnce(mockError); + + await expect(createAndPushBranch(branchName, mockCwd)).rejects.toThrow( + 'Failed to create/push branch', + ); + }); + }); + + describe('commitChanges', () => { + const mockPublishManifests: PublishManifest[] = [ + { + project: { + packageName: 'test-package', + } as any, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }, + ]; + + it('should commit changes and create tags', async () => { + const options = { + sessionId: 'test-session', + files: ['package.json', 'CHANGELOG.md'], + cwd: mockCwd, + publishManifests: mockPublishManifests, + branchName: 'release/test', + }; + + const result = await commitChanges(options); + + expect(exec).toHaveBeenCalledTimes(3); + // 验证 git add + expect(exec).toHaveBeenNthCalledWith( + 1, + 'git add package.json CHANGELOG.md', + { + cwd: mockCwd, + }, + ); + // 验证 git commit + expect(exec).toHaveBeenNthCalledWith( + 2, + 'git commit -m "chore: Publish release/test" -n', + { cwd: mockCwd }, + ); + // 验证 git tag + expect(exec).toHaveBeenNthCalledWith( + 3, + 'git tag -a v/test-package@1.1.0 -m "Bump type v/test-package@1.1.0"', + { cwd: mockCwd }, + ); + + expect(result).toEqual({ + effects: ['v/test-package@1.1.0', 'release/test'], + branchName: 'release/test', + }); + }); + }); + + describe('push', () => { + it('should push refs to remote', async () => { + const refs = ['main', 'v1.0.0', 'feature/test']; + await push({ + refs, + cwd: mockCwd, + repoUrl: 'git@github.com:coze-dev/coze-js.git', + }); + + expect(exec).toHaveBeenCalledWith( + 'git push git@github.com:coze-dev/coze-js.git main v1.0.0 feature/test --no-verify', + { cwd: mockCwd }, + ); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/packages.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/packages.test.ts new file mode 100644 index 00000000..f6a27b1c --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/packages.test.ts @@ -0,0 +1,227 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +import { getRushConfiguration } from '@/utils/get-rush-config'; +import { type PublishOptions } from '@/action/publish/types'; +import { validateAndGetPackages } from '@/action/publish/packages'; + +// Mock dependencies +vi.mock('@/utils/get-rush-config'); + +describe('packages', () => { + // 创建模拟项目 + const createMockProject = ({ + name, + shouldPublish = true, + dependencyProjects = new Set(), + consumingProjects = new Set(), + }: { + name: string; + shouldPublish?: boolean; + dependencyProjects?: Set; + consumingProjects?: Set; + }): RushConfigurationProject => { + const project = { + packageName: name, + shouldPublish, + dependencyProjects, + consumingProjects, + _shouldPublish: shouldPublish, + _versionPolicy: null, + _dependencyProjects: dependencyProjects, + _consumingProjects: consumingProjects, + } as unknown as RushConfigurationProject; + return project; + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('validateAndGetPackages', () => { + it('should throw error when no packages are specified', () => { + const options: PublishOptions = {}; + expect(() => validateAndGetPackages(options)).toThrow( + 'No packages to publish', + ); + }); + + it('should throw error when package is not found', () => { + const options: PublishOptions = { + to: ['non-existent-package'], + }; + + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: () => null, + } as any); + + expect(() => validateAndGetPackages(options)).toThrow( + 'Package "non-existent-package" not found in rush configuration', + ); + }); + + it('should throw error when package is not set to publish', () => { + const options: PublishOptions = { + to: ['non-publishable-package'], + }; + + const mockProject = createMockProject({ + name: 'non-publishable-package', + shouldPublish: false, + }); + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: () => mockProject, + } as any); + + expect(() => validateAndGetPackages(options)).toThrow( + 'Package "non-publishable-package" is not set to publish', + ); + }); + + it('should handle "to" pattern correctly', () => { + const depProject1 = createMockProject({ name: 'dep1' }); + const depProject2 = createMockProject({ name: 'dep2' }); + const nonPublishableDepProject = createMockProject({ + name: 'dep3', + shouldPublish: false, + }); + const mainProject = createMockProject({ + name: 'main', + dependencyProjects: new Set([ + depProject1, + depProject2, + nonPublishableDepProject, + ]), + }); + + const mockProjects = { + main: mainProject, + dep1: depProject1, + dep2: depProject2, + dep3: nonPublishableDepProject, + } as const; + + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: (name: string) => + mockProjects[name as keyof typeof mockProjects], + } as any); + + const options: PublishOptions = { + to: ['main'], + }; + + const result = validateAndGetPackages(options); + expect(result.size).toBe(3); // main + 2 publishable deps + expect(result.has(mainProject)).toBe(true); + expect(result.has(depProject1)).toBe(true); + expect(result.has(depProject2)).toBe(true); + expect(result.has(nonPublishableDepProject)).toBe(false); + }); + + it('should handle "from" pattern correctly', () => { + const depProject = createMockProject({ name: 'dep' }); + const consumingProject1 = createMockProject({ name: 'consuming1' }); + const consumingProject2 = createMockProject({ name: 'consuming2' }); + const nonPublishableConsumer = createMockProject({ + name: 'consuming3', + shouldPublish: false, + }); + + const mainProject = createMockProject({ + name: 'main', + dependencyProjects: new Set([depProject]), + consumingProjects: new Set([ + consumingProject1, + consumingProject2, + nonPublishableConsumer, + ]), + }); + + const mockProjects = { + main: mainProject, + dep: depProject, + consuming1: consumingProject1, + consuming2: consumingProject2, + consuming3: nonPublishableConsumer, + } as const; + + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: (name: string) => + mockProjects[name as keyof typeof mockProjects], + } as any); + + const options: PublishOptions = { + from: ['main'], + }; + + const result = validateAndGetPackages(options); + expect(result.size).toBe(4); // main + dep + 2 publishable consumers + expect(result.has(mainProject)).toBe(true); + expect(result.has(depProject)).toBe(true); + expect(result.has(consumingProject1)).toBe(true); + expect(result.has(consumingProject2)).toBe(true); + expect(result.has(nonPublishableConsumer)).toBe(false); + }); + + it('should handle "only" pattern correctly', () => { + const project1 = createMockProject({ name: 'project1' }); + const project2 = createMockProject({ name: 'project2' }); + + const mockProjects = { + project1, + project2, + } as const; + + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: (name: string) => + mockProjects[name as keyof typeof mockProjects], + } as any); + + const options: PublishOptions = { + only: ['project1', 'project2'], + }; + + const result = validateAndGetPackages(options); + expect(result.size).toBe(2); + expect(result.has(project1)).toBe(true); + expect(result.has(project2)).toBe(true); + }); + + it('should combine multiple patterns correctly', () => { + const depProject = createMockProject({ name: 'dep' }); + const consumingProject = createMockProject({ name: 'consuming' }); + const onlyProject = createMockProject({ name: 'only' }); + + const mainProject = createMockProject({ + name: 'main', + dependencyProjects: new Set([depProject]), + consumingProjects: new Set([consumingProject]), + }); + + const mockProjects = { + main: mainProject, + dep: depProject, + consuming: consumingProject, + only: onlyProject, + } as const; + + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: (name: string) => + mockProjects[name as keyof typeof mockProjects], + } as any); + + const options: PublishOptions = { + to: ['main'], + from: ['main'], + only: ['only'], + }; + + const result = validateAndGetPackages(options); + expect(result.size).toBe(4); // main + dep + consuming + only + expect(result.has(mainProject)).toBe(true); + expect(result.has(depProject)).toBe(true); + expect(result.has(consumingProject)).toBe(true); + expect(result.has(onlyProject)).toBe(true); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/push-to-remote.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/push-to-remote.test.ts new file mode 100644 index 00000000..0f78cc10 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/push-to-remote.test.ts @@ -0,0 +1,183 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import dayjs from 'dayjs'; +import { logger } from '@coze-arch/logger'; + +import { getCurrentBranchName } from '@/utils/git'; +import { exec } from '@/utils/exec'; +import { BumpType } from '@/action/publish/types'; +import { pushToRemote } from '@/action/publish/push-to-remote'; +import { commitChanges, push } from '@/action/publish/git'; + +// Mock dependencies +vi.mock('dayjs'); +vi.mock('@coze-arch/logger'); +vi.mock('@/utils/git'); +vi.mock('@/utils/exec'); +vi.mock('@/action/publish/git'); +vi.mock('open', () => ({ + default: vi.fn(), +})); + +const REPO_URL = 'git@github.com:test-owner/test-repo.git'; + +describe('push-to-remote', () => { + const mockCwd = '/mock/cwd'; + const mockSessionId = 'test-session'; + const mockDate = '20240101'; + const mockBranchName = 'feature/test'; + const mockChangedFiles = ['package.json', 'CHANGELOG.md']; + const mockPublishManifests = [ + { + project: { + packageName: 'test-package', + }, + currentVersion: '1.0.0', + newVersion: '1.1.0', + }, + ]; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(dayjs).mockReturnValue({ format: () => mockDate } as any); + vi.mocked(getCurrentBranchName).mockResolvedValue(mockBranchName); + vi.mocked(exec).mockResolvedValue({ stdout: '', stderr: '', code: 0 }); + vi.mocked(commitChanges).mockResolvedValue({ + effects: ['tag1', 'branch1'], + branchName: mockBranchName, + }); + }); + + describe('pushToRemote', () => { + it('should skip commit and push when skipCommit is true', async () => { + await pushToRemote({ + sessionId: mockSessionId, + changedFiles: mockChangedFiles, + cwd: mockCwd, + publishManifests: mockPublishManifests, + bumpPolicy: BumpType.PATCH, + skipCommit: true, + skipPush: false, + }); + + expect(exec).not.toHaveBeenCalled(); + expect(commitChanges).not.toHaveBeenCalled(); + expect(push).not.toHaveBeenCalled(); + }); + + it('should use existing branch for beta releases', async () => { + await pushToRemote({ + sessionId: mockSessionId, + changedFiles: mockChangedFiles, + cwd: mockCwd, + publishManifests: mockPublishManifests, + bumpPolicy: BumpType.BETA, + skipCommit: false, + repoUrl: REPO_URL, + skipPush: false, + }); + + expect(getCurrentBranchName).toHaveBeenCalled(); + expect(exec).not.toHaveBeenCalled(); // 不应该创建新分支 + expect(commitChanges).toHaveBeenCalledWith({ + sessionId: mockSessionId, + files: mockChangedFiles, + cwd: mockCwd, + publishManifests: mockPublishManifests, + branchName: mockBranchName, + }); + expect(push).toHaveBeenCalledWith({ + refs: ['tag1', 'branch1'], + cwd: mockCwd, + repoUrl: REPO_URL, + }); + }); + + it('should create new branch for non-beta releases', async () => { + await pushToRemote({ + sessionId: mockSessionId, + changedFiles: mockChangedFiles, + cwd: mockCwd, + publishManifests: mockPublishManifests, + bumpPolicy: BumpType.PATCH, + skipCommit: false, + repoUrl: REPO_URL, + skipPush: false, + }); + + const expectedBranchName = `release/${mockDate}-${mockSessionId}`; + expect(exec).toHaveBeenCalledWith( + `git checkout -b ${expectedBranchName}`, + { + cwd: mockCwd, + }, + ); + expect(commitChanges).toHaveBeenCalledWith({ + sessionId: mockSessionId, + files: mockChangedFiles, + cwd: mockCwd, + publishManifests: mockPublishManifests, + branchName: expectedBranchName, + }); + expect(push).toHaveBeenCalledWith({ + refs: ['tag1', 'branch1'], + cwd: mockCwd, + repoUrl: REPO_URL, + }); + }); + + it('should skip push when skipPush is true', async () => { + await pushToRemote({ + sessionId: mockSessionId, + changedFiles: mockChangedFiles, + cwd: mockCwd, + publishManifests: mockPublishManifests, + bumpPolicy: BumpType.PATCH, + skipCommit: false, + skipPush: true, + }); + + expect(commitChanges).toHaveBeenCalled(); + expect(push).not.toHaveBeenCalled(); + }); + + it('should show GitHub Actions link for test releases', async () => { + await pushToRemote({ + sessionId: mockSessionId, + changedFiles: mockChangedFiles, + cwd: mockCwd, + publishManifests: mockPublishManifests, + bumpPolicy: BumpType.ALPHA, + skipCommit: false, + skipPush: false, + repoUrl: REPO_URL, + }); + + expect(logger.success).toHaveBeenCalledWith( + 'Please refer to https://github.com/test-owner/test-repo/actions/workflows/release.yml for the release progress.', + ); + }); + + it('should show PR link and open browser for production releases', async () => { + const open = await import('open'); + const expectedBranchName = `release/${mockDate}-${mockSessionId}`; + const expectedPrUrl = `https://github.com/test-owner/test-repo/compare/${expectedBranchName}?expand=1`; + + await pushToRemote({ + sessionId: mockSessionId, + changedFiles: mockChangedFiles, + cwd: mockCwd, + publishManifests: mockPublishManifests, + bumpPolicy: BumpType.PATCH, + skipCommit: false, + skipPush: false, + repoUrl: REPO_URL, + }); + + expect(logger.success).toHaveBeenCalledWith( + expect.stringContaining(expectedPrUrl), + false, + ); + expect(open.default).toHaveBeenCalledWith(expectedPrUrl); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/request-bump-type.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/request-bump-type.test.ts new file mode 100644 index 00000000..7c5e6964 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/request-bump-type.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { select } from '@inquirer/prompts'; + +import { BumpType } from '@/action/publish/types'; +import { requestBumpType } from '@/action/publish/request-bump-type'; + +// Mock dependencies +vi.mock('@inquirer/prompts'); +vi.mock('chalk', () => ({ + default: { + green: vi.fn((str: string) => str), + bold: vi.fn((str: string) => str), + }, +})); + +describe('request-bump-type', () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(select).mockResolvedValue(BumpType.PATCH); + }); + + describe('requstBumpType', () => { + it('should present correct choices with descriptions', async () => { + await requestBumpType(); + + // 验证选择列表的格式和内容 + expect(select).toHaveBeenCalledWith({ + message: 'Select version bump type:', + choices: [ + { + name: 'ALPHA: Alpha pre-release version, for example: 1.1.1-alpha.2597f3', + value: BumpType.ALPHA, + }, + { + name: 'BETA: Beta pre-release version, for example: 1.1.1-beta.1', + value: BumpType.BETA, + }, + { + name: 'PATCH: Patch update, backwards-compatible bug fixes, for example: 1.1.1 -> 1.1.2', + value: BumpType.PATCH, + }, + { + name: 'MINOR: Minor update, backwards-compatible features, for example: 1.1.1 -> 1.2.0', + value: BumpType.MINOR, + }, + { + name: 'MAJOR: Major update, incompatible API changes, for example: 1.1.1 -> 2.0.0', + value: BumpType.MAJOR, + }, + ], + }); + }); + + it('should return selected bump type', async () => { + vi.mocked(select).mockResolvedValueOnce(BumpType.MINOR); + const result = await requestBumpType(); + expect(result).toBe(BumpType.MINOR); + }); + + it('should return null when selection is cancelled', async () => { + vi.mocked(select).mockRejectedValueOnce(new Error('Selection cancelled')); + const result = await requestBumpType(); + expect(result).toBeNull(); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/version.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/version.test.ts new file mode 100644 index 00000000..857704a6 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/publish/version.test.ts @@ -0,0 +1,218 @@ +import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest'; +import semver from 'semver'; +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +import * as randomModule from '@/utils/random'; +import { generatePublishManifest } from '@/action/publish/version'; +import { BumpType } from '@/action/publish/types'; +import { requestBumpType } from '@/action/publish/request-bump-type'; + +// Mock 所有依赖 +vi.mock('semver'); +vi.mock('@/utils/random'); +vi.mock('@/action/publish/request-bump-type', () => ({ + requestBumpType: vi.fn(), +})); + +describe('version management', () => { + beforeEach(() => { + vi.clearAllMocks(); + // 设置默认的 semver mock + vi.mocked(semver.parse).mockImplementation( + version => + ({ + major: 1, + minor: 0, + patch: 0, + prerelease: [], + version, + }) as any, + ); + vi.mocked(semver.inc).mockImplementation(version => `${version}-mocked`); + vi.mocked(semver.valid).mockImplementation(version => + version === 'invalid' ? null : '1.0.0', + ); + + // Mock random hash + vi.mocked(randomModule.randomHash).mockReturnValue('abc123'); + + // Mock request bump type + (requestBumpType as Mock).mockResolvedValue(BumpType.PATCH); + }); + + describe('generatePublishManifest', () => { + const mockProject: RushConfigurationProject = { + packageJson: { + version: '1.0.0', + }, + } as any; + + it('should handle specified version', async () => { + const packages = new Set([mockProject]); + const options = { version: '2.0.0' }; + + const result = await generatePublishManifest(packages, options); + + expect(result).toEqual({ + manifests: [ + { + project: mockProject, + currentVersion: '1.0.0', + newVersion: '2.0.0', + }, + ], + bumpPolicy: '2.0.0', + }); + }); + + it('should handle specified bump type', async () => { + const packages = new Set([mockProject]); + const options = { bumpType: BumpType.PATCH }; + vi.mocked(semver.inc).mockReturnValueOnce('1.0.1'); + + const result = await generatePublishManifest(packages, options); + + expect(result).toEqual({ + manifests: [ + { + project: mockProject, + currentVersion: '1.0.0', + newVersion: '1.0.1', + }, + ], + bumpPolicy: BumpType.PATCH, + }); + }); + + it('should handle interactive bump type selection', async () => { + const packages = new Set([mockProject]); + const options = {}; + vi.mocked(semver.inc).mockReturnValueOnce('1.0.1'); + + const result = await generatePublishManifest(packages, options); + + expect(requestBumpType).toHaveBeenCalled(); + expect(result).toEqual({ + manifests: [ + { + project: mockProject, + currentVersion: '1.0.0', + newVersion: '1.0.1', + }, + ], + bumpPolicy: BumpType.PATCH, + }); + }); + + it('should throw error for invalid version', async () => { + const packages = new Set([mockProject]); + const options = { version: 'invalid' }; + vi.mocked(semver.valid).mockReturnValueOnce(null); + + await expect(generatePublishManifest(packages, options)).rejects.toThrow( + 'Invalid version specified', + ); + }); + + it('should throw error when version selection is cancelled', async () => { + const packages = new Set([mockProject]); + const options = {}; + (requestBumpType as Mock).mockResolvedValueOnce(null); + + await expect(generatePublishManifest(packages, options)).rejects.toThrow( + 'Version selection was cancelled', + ); + }); + }); + + describe('version calculation', () => { + const testCases = [ + { + type: BumpType.PATCH, + currentVersion: '1.0.0', + expected: '1.0.1', + prerelease: [], + }, + { + type: BumpType.MINOR, + currentVersion: '1.0.0', + expected: '1.1.0', + prerelease: [], + }, + { + type: BumpType.MAJOR, + currentVersion: '1.0.0', + expected: '2.0.0', + prerelease: [], + }, + { + type: BumpType.BETA, + currentVersion: '1.0.0', + expected: '1.0.0-beta.1', + prerelease: [], + }, + { + type: BumpType.BETA, + currentVersion: '1.0.0-beta.1', + expected: '1.0.0-beta.2', + prerelease: ['beta', '1'], + }, + { + type: BumpType.ALPHA, + currentVersion: '1.0.0', + expected: '1.0.0-alpha.abc123', + prerelease: [], + }, + { + type: BumpType.ALPHA, + currentVersion: '1.0.0-alpha.1', + expected: '1.0.0-alpha.abc123', + prerelease: ['alpha', '1'], + }, + ]; + + testCases.forEach(({ type, currentVersion, expected, prerelease }) => { + it(`should handle ${type} bump for version ${currentVersion}`, async () => { + const packages = new Set([ + { + packageJson: { version: currentVersion }, + } as any, + ]); + + vi.mocked(semver.parse).mockReturnValueOnce({ + major: 1, + minor: 0, + patch: 0, + prerelease, + version: currentVersion, + } as any); + + (semver.inc as Mock).mockReset(); + if (type === BumpType.BETA && prerelease[0] === 'beta') { + vi.mocked(semver.inc).mockReturnValueOnce('1.0.0-beta.2'); + } else { + vi.mocked(semver.inc).mockReturnValueOnce(expected); + } + + const result = await generatePublishManifest(packages, { + bumpType: type, + }); + expect(result.manifests[0].newVersion).toBe(expected); + }); + }); + + it('should throw error for invalid version format', async () => { + const packages = new Set([ + { + packageJson: { version: 'invalid' }, + } as any, + ]); + + vi.mocked(semver.parse).mockReturnValueOnce(null); + + await expect( + generatePublishManifest(packages, { bumpType: BumpType.PATCH }), + ).rejects.toThrow('Invalid current version'); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/release/action.test.ts b/packages/rush-plugins/publish/__tests__/actions/release/action.test.ts new file mode 100644 index 00000000..ffa68c85 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/release/action.test.ts @@ -0,0 +1,174 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +import { getCurrentBranchName } from '@/utils/git'; +import { exec } from '@/utils/exec'; +import { + type ReleaseOptions, + type PackageToPublish, +} from '@/action/release/types'; +import { releasePackages } from '@/action/release/release'; +import { checkReleasePlan } from '@/action/release/plan'; +import { buildReleaseManifest } from '@/action/release/manifest'; +import { getPackagesToPublish } from '@/action/release/git'; +import { release } from '@/action/release/action'; + +// Mock dependencies +vi.mock('@/utils/git'); +vi.mock('@/utils/exec'); +vi.mock('@/action/release/release'); +vi.mock('@/action/release/plan'); +vi.mock('@/action/release/manifest'); +vi.mock('@/action/release/git'); + +describe('release action', () => { + const mockCommit = 'abc123'; + const mockBranchName = 'main'; + const mockRegistry = 'https://registry.npmjs.org/'; + + const createMockProject = (name: string): RushConfigurationProject => { + const project = { + _shouldPublish: true, + _versionPolicy: null, + _dependencyProjects: new Set(), + _consumingProjects: new Set(), + _packageJson: {}, + projectRelativeFolder: `packages/${name}`, + projectRushConfigFolder: '/mock/rush/config', + projectRushTempFolder: '/mock/rush/temp', + tempProjectName: name, + unscopedTempProjectName: name, + skipRushCheck: false, + publishFolder: `/mock/project/${name}/dist`, + isMainProject: false, + dependencyProjects: new Set(), + consumingProjects: new Set(), + shouldPublish: true, + versionPolicyName: undefined, + decoupledLocalDependencies: [], + packageName: name, + projectFolder: `/mock/project/${name}`, + rushConfiguration: {} as any, + reviewCategory: undefined, + packageJson: {} as any, + packageJsonEditor: {} as any, + isPublished: true, + statusValue: 0, + statusMessage: '', + cyclicDependencyProjects: new Set(), + localDependencyProjects: new Set(), + tags: [], + } as unknown as RushConfigurationProject; + return project; + }; + + const mockProject1 = createMockProject('package-1'); + const mockProject2 = createMockProject('package-2'); + + const mockPackagesToPublish: PackageToPublish[] = [ + { + packageName: 'package-1', + version: '1.1.0', + }, + { + packageName: 'package-2', + version: '2.1.0', + }, + ]; + + const mockReleaseManifests = [ + { + project: mockProject1, + version: '1.1.0', + }, + { + project: mockProject2, + version: '2.1.0', + }, + ]; + + beforeEach(() => { + vi.clearAllMocks(); + + // Setup default mocks + vi.mocked(exec).mockResolvedValue({ stdout: '', stderr: '', code: 0 }); + vi.mocked(getCurrentBranchName).mockResolvedValue(mockBranchName); + vi.mocked(getPackagesToPublish).mockResolvedValue(mockPackagesToPublish); + vi.mocked(buildReleaseManifest).mockReturnValue(mockReleaseManifests); + vi.mocked(checkReleasePlan).mockReturnValue(undefined); + vi.mocked(releasePackages).mockResolvedValue(undefined); + }); + + it('should release packages successfully', async () => { + const options: ReleaseOptions = { + commit: mockCommit, + registry: mockRegistry, + }; + + await release(options); + + // 验证流程 + expect(exec).toHaveBeenCalledWith(`git checkout ${mockCommit}`); + expect(getPackagesToPublish).toHaveBeenCalledWith(mockCommit); + expect(buildReleaseManifest).toHaveBeenCalledWith(mockPackagesToPublish); + expect(getCurrentBranchName).toHaveBeenCalled(); + expect(checkReleasePlan).toHaveBeenCalledWith( + mockReleaseManifests, + mockBranchName, + ); + expect(releasePackages).toHaveBeenCalledWith(mockReleaseManifests, { + commit: mockCommit, + dryRun: false, + registry: mockRegistry, + }); + }); + + it('should handle no packages to publish', async () => { + vi.mocked(getPackagesToPublish).mockResolvedValue([]); + + await release({ commit: mockCommit, registry: mockRegistry }); + + expect(buildReleaseManifest).not.toHaveBeenCalled(); + expect(checkReleasePlan).not.toHaveBeenCalled(); + expect(releasePackages).not.toHaveBeenCalled(); + }); + + it('should respect dryRun option', async () => { + await release({ commit: mockCommit, dryRun: true, registry: mockRegistry }); + + expect(releasePackages).toHaveBeenCalledWith(mockReleaseManifests, { + commit: mockCommit, + dryRun: true, + registry: mockRegistry, + }); + }); + + it('should respect registry option', async () => { + await release({ commit: mockCommit, registry: mockRegistry }); + + expect(releasePackages).toHaveBeenCalledWith(mockReleaseManifests, { + commit: mockCommit, + dryRun: false, + registry: mockRegistry, + }); + }); + + it('should handle release plan check error', async () => { + vi.mocked(checkReleasePlan).mockImplementation(() => { + throw new Error('Invalid release plan'); + }); + + await expect( + release({ commit: mockCommit, registry: mockRegistry }), + ).rejects.toThrow('Invalid release plan'); + expect(releasePackages).not.toHaveBeenCalled(); + }); + + it('should handle release packages error', async () => { + vi.mocked(releasePackages).mockRejectedValue(new Error('Release failed')); + + await expect( + release({ commit: mockCommit, registry: mockRegistry }), + ).rejects.toThrow('Release failed'); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/release/git.test.ts b/packages/rush-plugins/publish/__tests__/actions/release/git.test.ts new file mode 100644 index 00000000..9ba4bfcc --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/release/git.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +import { exec } from '@/utils/exec'; +import { getPackagesToPublish } from '@/action/release/git'; + +// Mock dependencies +vi.mock('@/utils/exec'); + +describe('git', () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(exec).mockResolvedValue({ stdout: '', stderr: '', code: 0 }); + }); + + describe('getPackagesToPublish', () => { + it('should parse package tags correctly', async () => { + const mockTags = [ + 'v/package-a@1.0.0', + 'v/package-b@2.0.0', + 'v/@scope/package-c@3.0.0', + 'some-other-tag', + '', + ].join('\n'); + + vi.mocked(exec).mockResolvedValueOnce({ + stdout: mockTags, + stderr: '', + code: 0, + }); + + const result = await getPackagesToPublish('abc123'); + + expect(exec).toHaveBeenCalledWith('git tag --points-at abc123'); + expect(result).toEqual([ + { packageName: 'package-a', version: '1.0.0' }, + { packageName: 'package-b', version: '2.0.0' }, + { packageName: '@scope/package-c', version: '3.0.0' }, + ]); + }); + + it('should handle empty tag list', async () => { + vi.mocked(exec).mockResolvedValueOnce({ + stdout: '', + stderr: '', + code: 0, + }); + + const result = await getPackagesToPublish('abc123'); + + expect(result).toEqual([]); + }); + + it('should handle tags without version pattern', async () => { + const mockTags = ['tag1', 'tag2', 'v/invalid-tag', ''].join('\n'); + + vi.mocked(exec).mockResolvedValueOnce({ + stdout: mockTags, + stderr: '', + code: 0, + }); + + const result = await getPackagesToPublish('abc123'); + + expect(result).toEqual([]); + }); + + it('should handle mixed valid and invalid tags', async () => { + const mockTags = [ + 'v/package-a@1.0.0', + 'invalid-tag', + 'v/package-b@2.0.0', + 'v/invalid-format', + '', + ].join('\n'); + + vi.mocked(exec).mockResolvedValueOnce({ + stdout: mockTags, + stderr: '', + code: 0, + }); + + const result = await getPackagesToPublish('abc123'); + + expect(result).toEqual([ + { packageName: 'package-a', version: '1.0.0' }, + { packageName: 'package-b', version: '2.0.0' }, + ]); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/release/manifest.test.ts b/packages/rush-plugins/publish/__tests__/actions/release/manifest.test.ts new file mode 100644 index 00000000..cd1f2fa7 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/release/manifest.test.ts @@ -0,0 +1,100 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +import { getRushConfiguration } from '@/utils/get-rush-config'; +import { type PackageToPublish } from '@/action/release/types'; +import { buildReleaseManifest } from '@/action/release/manifest'; + +// Mock dependencies +vi.mock('@/utils/get-rush-config'); + +describe('manifest', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('buildReleaseManifest', () => { + it('should build release manifest for valid packages', () => { + const mockPackages: PackageToPublish[] = [ + { packageName: 'package-a', version: '1.0.0' }, + { packageName: 'package-b', version: '2.0.0' }, + ]; + + const mockProjects = { + 'package-a': { + packageName: 'package-a', + packageJson: { version: '1.0.0' }, + }, + 'package-b': { + packageName: 'package-b', + packageJson: { version: '2.0.0' }, + }, + }; + + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: vi.fn( + (name: string) => mockProjects[name as keyof typeof mockProjects], + ), + } as any); + + const result = buildReleaseManifest(mockPackages); + + expect(result).toEqual([ + { + project: mockProjects['package-a'], + version: '1.0.0', + }, + { + project: mockProjects['package-b'], + version: '2.0.0', + }, + ]); + }); + + it('should throw error for non-existent package', () => { + const mockPackages: PackageToPublish[] = [ + { packageName: 'non-existent-package', version: '1.0.0' }, + ]; + + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: vi.fn(() => null), + } as any); + + expect(() => buildReleaseManifest(mockPackages)).toThrow( + 'Cannot find project: non-existent-package', + ); + }); + + it('should handle empty package list', () => { + const result = buildReleaseManifest([]); + expect(result).toEqual([]); + }); + + it('should handle scoped packages', () => { + const mockPackages: PackageToPublish[] = [ + { packageName: '@scope/package-a', version: '1.0.0' }, + ]; + + const mockProjects = { + '@scope/package-a': { + packageName: '@scope/package-a', + packageJson: { version: '1.0.0' }, + }, + } as const; + + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: vi.fn( + (name: string) => mockProjects[name as keyof typeof mockProjects], + ), + } as any); + + const result = buildReleaseManifest(mockPackages); + + expect(result).toEqual([ + { + project: mockProjects['@scope/package-a'], + version: '1.0.0', + }, + ]); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/release/package.test.ts b/packages/rush-plugins/publish/__tests__/actions/release/package.test.ts new file mode 100644 index 00000000..bbd08dad --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/release/package.test.ts @@ -0,0 +1,133 @@ +import path from 'path'; + +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; +import { readJsonFile, writeJsonFile } from '@coze-arch/fs-enhance'; + +import { getRushConfiguration } from '@/utils/get-rush-config'; +import { applyPublishConfig } from '@/action/release/package'; + +// Mock dependencies +vi.mock('path'); +vi.mock('@coze-arch/fs-enhance'); +vi.mock('@/utils/get-rush-config'); + +describe('package', () => { + const mockProjectFolder = '/mock/project'; + const mockPackageJsonPath = '/mock/project/package.json'; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(path.join).mockReturnValue(mockPackageJsonPath); + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: vi.fn(), + } as any); + }); + + describe('applyPublishConfig', () => { + const mockProject: RushConfigurationProject = { + projectFolder: mockProjectFolder, + packageName: 'test-package', + } as any; + + it('should update workspace dependencies versions', async () => { + const mockPackageJson = { + dependencies: { + 'dep-1': 'workspace:^1.0.0', + 'dep-2': '^2.0.0', + 'dep-3': 'workspace:^3.0.0', + }, + }; + + const mockDep1Project = { + packageJson: { version: '1.1.0' }, + }; + + const mockDep3Project = { + packageJson: { version: '3.1.0' }, + }; + + const mockProjects: Record = { + 'dep-1': mockDep1Project, + 'dep-3': mockDep3Project, + }; + + vi.mocked(readJsonFile).mockResolvedValue(mockPackageJson); + vi.mocked(getRushConfiguration).mockReturnValue({ + getProjectByName: vi.fn((name: string) => mockProjects[name] as any), + } as any); + + await applyPublishConfig(mockProject); + + expect(writeJsonFile).toHaveBeenCalledWith(mockPackageJsonPath, { + dependencies: { + 'dep-1': '1.1.0', + 'dep-2': '^2.0.0', + 'dep-3': '3.1.0', + }, + }); + }); + + it('should apply cozePublishConfig', async () => { + const mockPackageJson = { + name: 'test-package', + version: '1.0.0', + cozePublishConfig: { + main: './dist/index.js', + types: './dist/index.d.ts', + files: ['dist'], + }, + }; + + vi.mocked(readJsonFile).mockResolvedValue(mockPackageJson); + + await applyPublishConfig(mockProject); + + expect(writeJsonFile).toHaveBeenCalledWith(mockPackageJsonPath, { + name: 'test-package', + version: '1.0.0', + main: './dist/index.js', + types: './dist/index.d.ts', + files: ['dist'], + cozePublishConfig: { + main: './dist/index.js', + types: './dist/index.d.ts', + files: ['dist'], + }, + }); + }); + + it('should handle package without dependencies or cozePublishConfig', async () => { + const mockPackageJson = { + name: 'test-package', + version: '1.0.0', + }; + + vi.mocked(readJsonFile).mockResolvedValue(mockPackageJson); + + await applyPublishConfig(mockProject); + + expect(writeJsonFile).toHaveBeenCalledWith( + mockPackageJsonPath, + mockPackageJson, + ); + }); + + it('should handle empty dependencies', async () => { + const mockPackageJson = { + name: 'test-package', + version: '1.0.0', + dependencies: {}, + }; + + vi.mocked(readJsonFile).mockResolvedValue(mockPackageJson); + + await applyPublishConfig(mockProject); + + expect(writeJsonFile).toHaveBeenCalledWith( + mockPackageJsonPath, + mockPackageJson, + ); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/release/plan.test.ts b/packages/rush-plugins/publish/__tests__/actions/release/plan.test.ts new file mode 100644 index 00000000..1da2191c --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/release/plan.test.ts @@ -0,0 +1,186 @@ +import { describe, it, expect } from 'vitest'; +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +import { type ReleaseManifest } from '@/action/release/types'; +import { + checkReleasePlan, + calReleaseType, + ReleaseType, +} from '@/action/release/plan'; + +describe('plan', () => { + const createMockProject = (name: string): RushConfigurationProject => { + const project = { + packageName: name, + projectFolder: `/mock/project/${name}`, + _shouldPublish: true, + _versionPolicy: null, + _dependencyProjects: new Set(), + _consumingProjects: new Set(), + _packageJson: {}, + projectRelativeFolder: `packages/${name}`, + projectRushConfigFolder: '/mock/rush/config', + projectRushTempFolder: '/mock/rush/temp', + tempProjectName: name, + unscopedTempProjectName: name, + skipRushCheck: false, + publishFolder: `/mock/project/${name}/dist`, + isMainProject: false, + dependencyProjects: new Set(), + consumingProjects: new Set(), + shouldPublish: true, + versionPolicyName: undefined, + decoupledLocalDependencies: [], + rushConfiguration: {} as any, + reviewCategory: undefined, + packageJsonEditor: {} as any, + isPublished: true, + statusValue: 0, + statusMessage: '', + cyclicDependencyProjects: new Set(), + localDependencyProjects: new Set(), + tags: [], + } as unknown as RushConfigurationProject; + return project; + }; + + describe('calReleaseType', () => { + it('should identify alpha versions', () => { + expect(calReleaseType('1.0.0-alpha.1')).toBe(ReleaseType.ALPHA); + expect(calReleaseType('2.0.0-alpha.0')).toBe(ReleaseType.ALPHA); + expect(calReleaseType('0.1.0-alpha.5')).toBe(ReleaseType.ALPHA); + }); + + it('should identify beta versions', () => { + expect(calReleaseType('1.0.0-beta.1')).toBe(ReleaseType.BETA); + expect(calReleaseType('2.0.0-beta.0')).toBe(ReleaseType.BETA); + expect(calReleaseType('0.1.0-beta.5')).toBe(ReleaseType.BETA); + }); + + it('should identify latest versions', () => { + expect(calReleaseType('1.0.0')).toBe(ReleaseType.LATEST); + expect(calReleaseType('2.0.0')).toBe(ReleaseType.LATEST); + expect(calReleaseType('0.1.0')).toBe(ReleaseType.LATEST); + }); + }); + + describe('checkReleasePlan', () => { + const mockProject1 = createMockProject('package-1'); + const mockProject2 = createMockProject('package-2'); + + it('should allow alpha releases on any branch', () => { + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject1, + version: '1.0.0-alpha.1', + }, + { + project: mockProject2, + version: '2.0.0-alpha.1', + }, + ]; + + expect(() => + checkReleasePlan(releaseManifests, 'feature/test'), + ).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'main')).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'develop')).not.toThrow(); + }); + + it('should allow beta releases on any branch', () => { + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject1, + version: '1.0.0-beta.1', + }, + { + project: mockProject2, + version: '2.0.0-beta.1', + }, + ]; + + expect(() => + checkReleasePlan(releaseManifests, 'feature/test'), + ).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'main')).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'develop')).not.toThrow(); + }); + + it('should only allow latest releases on main branch', () => { + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject1, + version: '1.0.0', + }, + { + project: mockProject2, + version: '2.0.0', + }, + ]; + + expect(() => checkReleasePlan(releaseManifests, 'main')).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'feature/test')).toThrow( + 'For LATEST release, should be on main branch only.', + ); + expect(() => checkReleasePlan(releaseManifests, 'develop')).toThrow( + 'For LATEST release, should be on main branch only.', + ); + }); + + it('should treat mixed versions as latest release', () => { + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject1, + version: '1.0.0', // latest + }, + { + project: mockProject2, + version: '2.0.0-beta.1', // beta + }, + ]; + + expect(() => checkReleasePlan(releaseManifests, 'main')).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'feature/test')).toThrow( + 'For LATEST release, should be on main branch only.', + ); + }); + + it('should treat mixed alpha/beta versions as beta release', () => { + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject1, + version: '1.0.0-alpha.1', // alpha + }, + { + project: mockProject2, + version: '2.0.0-beta.1', // beta + }, + ]; + + expect(() => + checkReleasePlan(releaseManifests, 'feature/test'), + ).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'main')).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'develop')).not.toThrow(); + }); + + it('should handle empty release manifests', () => { + expect(() => checkReleasePlan([], 'main')).not.toThrow(); + expect(() => checkReleasePlan([], 'feature/test')).not.toThrow(); + }); + + it('should handle single package release', () => { + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject1, + version: '1.0.0', + }, + ]; + + expect(() => checkReleasePlan(releaseManifests, 'main')).not.toThrow(); + expect(() => checkReleasePlan(releaseManifests, 'feature/test')).toThrow( + 'For LATEST release, should be on main branch only.', + ); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/actions/release/release.test.ts b/packages/rush-plugins/publish/__tests__/actions/release/release.test.ts new file mode 100644 index 00000000..baaa442c --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/actions/release/release.test.ts @@ -0,0 +1,279 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +import { exec } from '@/utils/exec'; +import { + type ReleaseOptions, + type ReleaseManifest, +} from '@/action/release/types'; +import { releasePackages } from '@/action/release/release'; +import { applyPublishConfig } from '@/action/release/package'; + +// Mock dependencies +vi.mock('@/utils/exec'); +vi.mock('@/action/release/package'); + +describe('release', () => { + const mockRegistry = 'https://registry.npmjs.org/'; + const mockToken = 'mock-token'; + + const createMockProject = ( + name: string, + version: string, + ): RushConfigurationProject => { + const project = { + packageName: name, + projectFolder: `/mock/project/${name}`, + packageJson: { + version, + }, + _shouldPublish: true, + _versionPolicy: null, + _dependencyProjects: new Set(), + _consumingProjects: new Set(), + _packageJson: {}, + projectRelativeFolder: `packages/${name}`, + projectRushConfigFolder: '/mock/rush/config', + projectRushTempFolder: '/mock/rush/temp', + tempProjectName: name, + unscopedTempProjectName: name, + skipRushCheck: false, + publishFolder: `/mock/project/${name}/dist`, + isMainProject: false, + dependencyProjects: new Set(), + consumingProjects: new Set(), + shouldPublish: true, + versionPolicyName: undefined, + decoupledLocalDependencies: [], + rushConfiguration: {} as any, + reviewCategory: undefined, + packageJsonEditor: {} as any, + isPublished: true, + statusValue: 0, + statusMessage: '', + cyclicDependencyProjects: new Set(), + localDependencyProjects: new Set(), + tags: [], + } as unknown as RushConfigurationProject; + return project; + }; + + beforeEach(() => { + vi.clearAllMocks(); + process.env.NODE_AUTH_TOKEN = mockToken; + }); + + afterEach(() => { + delete process.env.NODE_AUTH_TOKEN; + }); + + describe('releasePackages', () => { + it('should build and publish packages successfully', async () => { + const mockProject1 = createMockProject('package-1', '1.1.0'); + const mockProject2 = createMockProject('package-2', '2.1.0'); + + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject1, + version: '1.1.0', + }, + { + project: mockProject2, + version: '2.1.0', + }, + ]; + + const options: ReleaseOptions = { + commit: 'abc123', + registry: mockRegistry, + }; + + await releasePackages(releaseManifests, options); + + // 验证构建过程 + expect(exec).toHaveBeenCalledWith( + 'rush build --to package-1 --to package-2', + ); + + // 验证发布过程 + expect(applyPublishConfig).toHaveBeenCalledWith(mockProject1); + expect(applyPublishConfig).toHaveBeenCalledWith(mockProject2); + + expect(exec).toHaveBeenCalledWith( + `NODE_AUTH_TOKEN=${mockToken} npm publish --tag latest --registry=${mockRegistry}`, + { + cwd: mockProject1.projectFolder, + }, + ); + expect(exec).toHaveBeenCalledWith( + `NODE_AUTH_TOKEN=${mockToken} npm publish --tag latest --registry=${mockRegistry}`, + { + cwd: mockProject2.projectFolder, + }, + ); + }); + + it('should handle alpha versions correctly', async () => { + const mockProject = createMockProject('package-alpha', '1.0.0-alpha.1'); + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject, + version: '1.0.0-alpha.1', + }, + ]; + + await releasePackages(releaseManifests, { + commit: 'abc123', + registry: mockRegistry, + }); + + expect(exec).toHaveBeenCalledWith( + `NODE_AUTH_TOKEN=${mockToken} npm publish --tag alpha --registry=${mockRegistry}`, + { + cwd: mockProject.projectFolder, + }, + ); + }); + + it('should handle beta versions correctly', async () => { + const mockProject = createMockProject('package-beta', '1.0.0-beta.1'); + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject, + version: '1.0.0-beta.1', + }, + ]; + + await releasePackages(releaseManifests, { + commit: 'abc123', + registry: mockRegistry, + }); + + expect(exec).toHaveBeenCalledWith( + `NODE_AUTH_TOKEN=${mockToken} npm publish --tag beta --registry=${mockRegistry}`, + { + cwd: mockProject.projectFolder, + }, + ); + }); + + it('should respect dryRun option', async () => { + const mockProject = createMockProject('package-1', '1.1.0'); + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject, + version: '1.1.0', + }, + ]; + + await releasePackages(releaseManifests, { + commit: 'abc123', + registry: mockRegistry, + dryRun: true, + }); + + expect(exec).toHaveBeenCalledWith( + `NODE_AUTH_TOKEN=${mockToken} npm publish --tag latest --dry-run --registry=${mockRegistry}`, + { + cwd: mockProject.projectFolder, + }, + ); + }); + + it('should handle build errors', async () => { + const mockProject = createMockProject('package-1', '1.1.0'); + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject, + version: '1.1.0', + }, + ]; + + vi.mocked(exec).mockRejectedValueOnce(new Error('Build failed')); + + await expect( + releasePackages(releaseManifests, { + commit: 'abc123', + registry: mockRegistry, + }), + ).rejects.toThrow('Build failed'); + + expect(applyPublishConfig).not.toHaveBeenCalled(); + }); + + it('should handle publish errors', async () => { + const mockProject = createMockProject('package-1', '1.1.0'); + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject, + version: '1.1.0', + }, + ]; + + // 模拟构建成功但发布失败 + vi.mocked(exec) + .mockResolvedValueOnce({ stdout: '', stderr: '', code: 0 }) // build 成功 + .mockRejectedValueOnce(new Error('Publish failed')); // publish 失败 + + await expect( + releasePackages(releaseManifests, { + commit: 'abc123', + registry: mockRegistry, + }), + ).rejects.toThrow('Publish failed'); + }); + + it('should handle missing auth token', async () => { + const mockProject = createMockProject('package-1', '1.1.0'); + const releaseManifests: ReleaseManifest[] = [ + { + project: mockProject, + version: '1.1.0', + }, + ]; + + delete process.env.NODE_AUTH_TOKEN; + + await releasePackages(releaseManifests, { + commit: 'abc123', + registry: mockRegistry, + }); + + expect(exec).toHaveBeenCalledWith( + 'NODE_AUTH_TOKEN=undefined npm publish --tag latest --registry=https://registry.npmjs.org/', + { + cwd: mockProject.projectFolder, + }, + ); + }); + + it('should handle multiple packages in parallel', async () => { + const projects = Array.from({ length: 5 }, (_, i) => + createMockProject(`package-${i + 1}`, `1.0.${i}`), + ); + const releaseManifests: ReleaseManifest[] = projects.map(project => ({ + project, + version: project.packageJson.version, + })); + + await releasePackages(releaseManifests, { + commit: 'abc123', + registry: mockRegistry, + }); + + expect(exec).toHaveBeenCalledWith( + `rush build ${projects.map(project => `--to ${project.packageName}`).join(' ')}`, + ); + + // 验证所有包都被构建和发布 + projects.forEach(project => { + expect(exec).toHaveBeenCalledWith( + `NODE_AUTH_TOKEN=${mockToken} npm publish --tag latest --registry=${mockRegistry}`, + { + cwd: project.projectFolder, + }, + ); + }); + }); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/generate-changelog/generate-changelog.test.ts b/packages/rush-plugins/publish/__tests__/generate-changelog/generate-changelog.test.ts new file mode 100644 index 00000000..9c18c345 --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/generate-changelog/generate-changelog.test.ts @@ -0,0 +1,244 @@ +import { describe, it, expect, vi } from 'vitest'; +import semver from 'semver'; +import dayjs from 'dayjs'; + +import { + generateChangelog, + type ChangeLog, +} from '@/generate-changelog/generate-changelog'; + +// Mock dependencies +vi.mock('semver'); +vi.mock('dayjs'); + +// Mock the dayjs().format() and toISOString() calls +const mockDate = '2024-01-01'; +const mockISOString = '2024-01-01T00:00:00.000Z'; + +vi.mocked(dayjs).mockReturnValue({ + format: () => mockDate, + toISOString: () => mockISOString, +} as any); + +// Mock semver functions +vi.mocked(semver.valid).mockImplementation(version => version as string); +vi.mocked(semver.rcompare).mockImplementation((a, b) => { + if (a > b) { + return -1; + } + if (a < b) { + return 1; + } + return 0; +}); + +describe('generateChangelog', () => { + it('should generate changelog with no previous entries', () => { + const result = generateChangelog({ + packageName: 'test-package', + version: '1.0.0', + commingChanges: [ + { + packageName: 'test-package', + changes: [ + { + packageName: 'test-package', + comment: 'Added new feature', + type: 'minor', + }, + ], + }, + ], + }); + + expect(result.changelog.name).toBe('test-package'); + expect(result.changelog.entries).toHaveLength(1); + expect(result.changelog.entries[0].version).toBe('1.0.0'); + expect(result.changelog.entries[0].comments.minor).toHaveLength(1); + expect(result.changelog.entries[0].comments.minor[0].comment).toBe( + 'Added new feature', + ); + }); + + it('should merge with previous changelog entries', () => { + const previousChangelog: ChangeLog = { + name: 'test-package', + entries: [ + { + version: '0.9.0', + tag: 'v0.9.0', + date: '2023-12-31', + comments: { + patch: [{ comment: 'Old fix' }], + }, + }, + ], + }; + + const result = generateChangelog({ + packageName: 'test-package', + version: '1.0.0', + commingChanges: [ + { + packageName: 'test-package', + changes: [ + { + packageName: 'test-package', + comment: 'Breaking change', + type: 'major', + }, + ], + }, + ], + previousChangelog, + }); + + expect(result.changelog.entries).toHaveLength(2); + expect(result.changelog.entries[0].version).toBe('1.0.0'); + expect(result.changelog.entries[1].version).toBe('0.9.0'); + }); + + it('should handle empty changes with default changelog', () => { + const result = generateChangelog({ + packageName: 'test-package', + version: '1.0.0', + commingChanges: [], + defaultChangelog: 'Default message', + }); + + expect(result.changelog.entries[0].comments.none[0].comment).toBe( + 'Default message', + ); + }); + + it('should handle custom fields in changes', () => { + const result = generateChangelog({ + packageName: 'test-package', + version: '1.0.0', + commingChanges: [ + { + packageName: 'test-package', + changes: [ + { + packageName: 'test-package', + comment: 'Change with custom fields', + type: 'patch', + customFields: { + PR: '#123', + Author: 'test-author', + }, + }, + ], + }, + ], + }); + + expect(result.changelog.entries[0].comments.patch[0].customFields).toEqual({ + PR: '#123', + Author: 'test-author', + }); + }); + + it('should deduplicate identical comments', () => { + const result = generateChangelog({ + packageName: 'test-package', + version: '1.0.0', + commingChanges: [ + { + packageName: 'test-package', + changes: [ + { + packageName: 'test-package', + comment: 'Duplicate comment', + type: 'patch', + }, + { + packageName: 'test-package', + comment: 'Duplicate comment', + type: 'patch', + }, + ], + }, + ], + }); + + expect(result.changelog.entries[0].comments.patch).toHaveLength(1); + }); + + it('should generate correct markdown format', () => { + const result = generateChangelog({ + packageName: 'test-package', + version: '1.0.0', + commingChanges: [ + { + packageName: 'test-package', + changes: [ + { + packageName: 'test-package', + comment: 'Breaking change', + type: 'major', + }, + { + packageName: 'test-package', + comment: 'New feature', + type: 'minor', + }, + { + packageName: 'test-package', + comment: 'Bug fix', + type: 'patch', + }, + ], + }, + ], + }); + + expect(result.report).toContain('test-package'); + }); + + it('should handle all change types', () => { + const result = generateChangelog({ + packageName: 'test-package', + version: '1.0.0', + commingChanges: [ + { + packageName: 'test-package', + changes: [ + { + packageName: 'test-package', + comment: 'Major change', + type: 'major', + }, + { + packageName: 'test-package', + comment: 'Minor change', + type: 'minor', + }, + { + packageName: 'test-package', + comment: 'Patch change', + type: 'patch', + }, + { + packageName: 'test-package', + comment: 'Dependency change', + type: 'dependency', + }, + { + packageName: 'test-package', + comment: 'None change', + type: 'none', + }, + ], + }, + ], + }); + + const entry = result.changelog.entries[0]; + expect(entry.comments.major).toBeDefined(); + expect(entry.comments.minor).toBeDefined(); + expect(entry.comments.patch).toBeDefined(); + expect(entry.comments.dependency).toBeDefined(); + expect(entry.comments.none).toBeDefined(); + }); +}); diff --git a/packages/rush-plugins/publish/__tests__/utils/git.test.ts b/packages/rush-plugins/publish/__tests__/utils/git.test.ts new file mode 100644 index 00000000..f88a724d --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/utils/git.test.ts @@ -0,0 +1,41 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +import { getCurrentOrigin } from '@/utils/git'; +import { exec } from '@/utils/exec'; + +// Mock dependencies +vi.mock('@/utils/exec'); + +describe('git utils', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('getCurrentOrigin', () => { + it('should return the git origin url', async () => { + const mockCwd = '/mock/cwd'; + const mockOriginUrl = 'git@github.com:test-owner/test-repo.git'; + + vi.mocked(exec).mockResolvedValueOnce({ + stdout: `${mockOriginUrl}\n`, + stderr: '', + code: 0, + }); + + const result = await getCurrentOrigin(mockCwd); + + expect(exec).toHaveBeenCalledWith('git remote get-url origin', { + cwd: mockCwd, + }); + expect(result).toBe(mockOriginUrl); + }); + + it('should throw error when git command fails', async () => { + const mockError = new Error('Git command failed'); + vi.mocked(exec).mockRejectedValueOnce(mockError); + + const result = await getCurrentOrigin(); + expect(result).toBeUndefined(); + }); + }); +}); diff --git a/packages/rush-plugins/publish/command-line.json b/packages/rush-plugins/publish/command-line.json new file mode 100644 index 00000000..c6acc22e --- /dev/null +++ b/packages/rush-plugins/publish/command-line.json @@ -0,0 +1,140 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [ + { + "name": "pub", + "commandKind": "global", + "summary": "⭐️️ Publish packages to npm", + "shellCommand": "rush-publish pub", + "safeForSimultaneousRushProcesses": true + }, + { + "name": "change-x", + "commandKind": "global", + "summary": "⭐️️ generate change log", + "shellCommand": "rush-publish change", + "safeForSimultaneousRushProcesses": true + }, + { + "name": "release", + "commandKind": "global", + "summary": "⭐️️ release packages", + "shellCommand": "rush-publish release", + "safeForSimultaneousRushProcesses": true + } + ], + "parameters": [ + { + "parameterKind": "string", + "description": "Enable dry run mode", + "shortName": "-d", + "longName": "--dry-run", + "argumentName": "DRY_RUN", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "string", + "shortName": "-t", + "longName": "--to", + "description": "Publish specified packages and their downstream dependencies", + "argumentName": "TO", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "string", + "shortName": "-f", + "longName": "--from", + "description": "Publish specified packages and their upstream/downstream dependencies", + "argumentName": "FROM", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "string", + "shortName": "-o", + "longName": "--only", + "description": "Only publish specified packages", + "argumentName": "ONLY", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "flag", + "shortName": "-s", + "longName": "--skip-commit", + "description": "Skip git commit", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "flag", + "shortName": "-p", + "longName": "--skip-push", + "description": "Skip git push", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "choice", + "alternatives": [ + { + "name": "alpha", + "description": "Alpha version" + }, + { + "name": "beta", + "description": "Beta version" + }, + { + "name": "patch", + "description": "Patch version" + }, + { + "name": "minor", + "description": "Minor version" + }, + { + "name": "major", + "description": "Major version" + } + ], + "shortName": "-b", + "longName": "--bump-type", + "description": "Version bump type (alpha/beta/patch/minor/major)", + "associatedCommands": ["pub"], + "required": false + }, + { + "parameterKind": "flag", + "longName": "--amend-commit", + "shortName": "-a", + "description": "是否 amend commit 阶段", + "associatedCommands": ["change-x"] + }, + { + "parameterKind": "flag", + "longName": "--ci", + "shortName": "-i", + "description": "是否在 CI 环境", + "associatedCommands": ["change-x"] + }, + { + "parameterKind": "string", + "argumentName": "COMMIT_MSG", + "longName": "--commit-msg", + "shortName": "-c", + "description": "本次提交信息,默认读取 .git/COMMIT_EDITMSG", + "associatedCommands": ["change-x"] + }, + { + "parameterKind": "string", + "argumentName": "COMMIT", + "longName": "--commit", + "shortName": "-c", + "description": "Git commit hash", + "associatedCommands": ["release"] + } + ] +} diff --git a/packages/rush-plugins/publish/config/rush-project.json b/packages/rush-plugins/publish/config/rush-project.json new file mode 100644 index 00000000..72ff0d3c --- /dev/null +++ b/packages/rush-plugins/publish/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "test:cov", + "outputFolderNames": ["coverage"] + }, + { + "operationName": "ts-check", + "outputFolderNames": ["lib"] + } + ] +} diff --git a/packages/rush-plugins/publish/eslint.config.js b/packages/rush-plugins/publish/eslint.config.js new file mode 100644 index 00000000..8a3bd050 --- /dev/null +++ b/packages/rush-plugins/publish/eslint.config.js @@ -0,0 +1,7 @@ +const { defineConfig } = require('@coze-arch/eslint-config'); + +module.exports = defineConfig({ + packageRoot: __dirname, + preset: 'node', + rules: {}, +}); diff --git a/packages/rush-plugins/publish/package.json b/packages/rush-plugins/publish/package.json new file mode 100644 index 00000000..5735553c --- /dev/null +++ b/packages/rush-plugins/publish/package.json @@ -0,0 +1,71 @@ +{ + "name": "@coze-arch/rush-publish-plugin", + "version": "0.0.1", + "description": "rush plugin to generate change log and publish packages", + "keywords": [ + "rush", + "plugin", + "command", + "release", + "publish" + ], + "license": "ISC", + "author": "tecvan.fe@gmail.com", + "maintainers": [], + "main": "src/index.ts", + "bin": { + "rush-publish": "./src/run.js" + }, + "files": [ + "lib", + "!**/*.tsbuildinfo", + "!**/*.map", + "command-line.json", + "rush-plugin-manifest.json" + ], + "scripts": { + "build": "tsc -b tsconfig.build.json", + "lint": "eslint ./ --cache", + "test": "vitest --run --passWithNoTests", + "test:cov": "npm run test -- --coverage" + }, + "dependencies": { + "@coze-arch/fs-enhance": "workspace:*", + "@coze-arch/logger": "workspace:*", + "@inquirer/prompts": "^3.2.0", + "@rushstack/rush-sdk": "^5.150.0", + "chalk": "^4.1.2", + "conventional-changelog-angular": "^5.0.13", + "conventional-commits-parser": "^3.2.4", + "dayjs": "^1.11.13", + "open": "~10.1.0", + "semver": "^7.7.1", + "shelljs": "^0.9.2" + }, + "devDependencies": { + "@commitlint/types": "^17.4.0", + "@coze-arch/eslint-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", + "@coze-arch/vitest-config": "workspace:*", + "@types/node": "^22.13.13", + "@types/semver": "^7.5.8", + "@types/shelljs": "^0.8.15", + "@vitest/coverage-v8": "^3.0.9", + "commander": "^13.1.0", + "sucrase": "^3.32.0", + "tsx": "^4.19.3", + "vitest": "^3.0.9" + }, + "peerDependencies": { + "commander": "^13.1.0" + }, + "publishConfig": { + "access": "public" + }, + "cozePublishConfig": { + "bin": { + "rush-publish": "./lib/index.js" + }, + "main": "./lib/index.js" + } +} diff --git a/packages/rush-plugins/publish/rush-plugin-manifest.json b/packages/rush-plugins/publish/rush-plugin-manifest.json new file mode 100644 index 00000000..592d0287 --- /dev/null +++ b/packages/rush-plugins/publish/rush-plugin-manifest.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", + "plugins": [ + { + "pluginName": "@coze-arch/rush-publish-plugin", + "description": "rush plugin to generate change log and publish packages", + "commandLineJsonFilePath": "./command-line.json" + } + ] +} diff --git a/packages/rush-plugins/publish/src/action/change/action.ts b/packages/rush-plugins/publish/src/action/change/action.ts new file mode 100644 index 00000000..5c212f4e --- /dev/null +++ b/packages/rush-plugins/publish/src/action/change/action.ts @@ -0,0 +1,40 @@ +import path from 'path'; +import fs from 'fs/promises'; + +import { type RushConfiguration } from '@rushstack/rush-sdk'; +import { logger } from '@coze-arch/logger'; + +import { getRushConfiguration } from '../../utils/get-rush-config'; +import { type ChangeOptions } from './types'; +import { generateAllChangesFile, analysisCommitMsg } from './helper'; +import { amendCommit } from './amend-commit'; + +export const generateChangeFiles = async (options: ChangeOptions) => { + // CI 环境的提交不做处理 + if (options.ci || process.env.CI === 'true') { + return; + } + if (options.amendCommit) { + amendCommit(); + return; + } + try { + let { commitMsg } = options; + if (!commitMsg) { + const rushConfiguration: RushConfiguration = getRushConfiguration(); + commitMsg = await fs.readFile( + path.resolve(rushConfiguration.rushJsonFolder, '.git/COMMIT_EDITMSG'), + 'utf-8', + ); + } + + const { content, type } = await analysisCommitMsg(commitMsg); + if (!content) { + logger.warn('Invalid subject'); + return; + } + generateAllChangesFile(content, type); + } catch (e) { + logger.error(`Generate changes file fail \n ${e}`); + } +}; diff --git a/packages/rush-plugins/publish/src/action/change/amend-commit.ts b/packages/rush-plugins/publish/src/action/change/amend-commit.ts new file mode 100644 index 00000000..85293d98 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/change/amend-commit.ts @@ -0,0 +1,24 @@ +import path from 'path'; + +import { type RushConfiguration } from '@rushstack/rush-sdk'; + +import { getChangedFilesFromCached } from '../../utils/git'; +import { getRushConfiguration } from '../../utils/get-rush-config'; +import { exec } from '../../utils/exec'; + +export const amendCommit = async (): Promise => { + const changedFiles = await getChangedFilesFromCached(); + const rushConfiguration: RushConfiguration = getRushConfiguration(); + + const relativeChangesFolder = path.relative( + rushConfiguration.rushJsonFolder, + rushConfiguration.changesFolder, + ); + + for (const file of changedFiles) { + if (file.startsWith(relativeChangesFolder)) { + await exec('git commit --amend --no-edit -n'); + break; + } + } +}; diff --git a/packages/rush-plugins/publish/src/action/change/helper.ts b/packages/rush-plugins/publish/src/action/change/helper.ts new file mode 100644 index 00000000..dba19d5c --- /dev/null +++ b/packages/rush-plugins/publish/src/action/change/helper.ts @@ -0,0 +1,167 @@ +import path from 'path'; + +import { type RushConfiguration as RushConfigurationType } from '@rushstack/rush-sdk/lib/api/RushConfiguration'; +import { ChangeFile } from '@rushstack/rush-sdk/lib/api/ChangeFile'; +import { + type RushConfigurationProject, + type RushConfiguration, +} from '@rushstack/rush-sdk'; +import { logger } from '@coze-arch/logger'; +import type { Commit, Parser, ParserOptions } from '@commitlint/types'; + +import { whoAmI } from '../../utils/whoami'; +import { getChangedFilesFromCached } from '../../utils/git'; +import { getRushConfiguration } from '../../utils/get-rush-config'; +import { exec } from '../../utils/exec'; + +// 这两个包没有 module 导出 +// eslint-disable-next-line @typescript-eslint/no-require-imports +const { sync } = require('conventional-commits-parser'); +// eslint-disable-next-line @typescript-eslint/no-require-imports +const defaultChangelogOpts = require('conventional-changelog-angular'); + +const VERSIONS = ['major', 'minor', 'patch']; + +/** + * 收集需要更新 changes 的包 + * @returns RushConfigurationProject[] + */ +export const collectShouldUpdateChangesProjects = async (): Promise< + RushConfigurationProject[] +> => { + const changedFiles = await getChangedFilesFromCached(); + const rushConfiguration: RushConfiguration = getRushConfiguration(); + const lookup = rushConfiguration.getProjectLookupForRoot( + rushConfiguration.rushJsonFolder, + ); + + const relativeChangesFolder = path.relative( + rushConfiguration.rushJsonFolder, + rushConfiguration.changesFolder, + ); + const ignorePackage: string[] = []; + const shouldUpdateChangesProjects: Set = new Set(); + + for (const file of changedFiles) { + // 收集有 common/changes 下变更的包 + if (file.startsWith(relativeChangesFolder)) { + const packageName = path.relative( + relativeChangesFolder, + path.dirname(file), + ); + ignorePackage.push(packageName); + } + + const project = lookup.findChildPath(file); + // 按是否发布提取相关包信息,同时过滤掉changes包含在本次变更内的文件(该策略是手动更改优先级大于自动生成) + if (project && !ignorePackage.includes(project.packageName)) { + if (!shouldUpdateChangesProjects.has(project) && project.shouldPublish) { + shouldUpdateChangesProjects.add(project); + } + } + } + + return [...shouldUpdateChangesProjects]; +}; + +/** + * 解析 commit-mag + * @param message + * @param parser + * @param parserOpts + * @returns + */ +// https://github.com/conventional-changelog/commitlint/blob/8b8a6e62f57511c0be05346d14959247851cdfeb/%40commitlint/parse/src/index.ts#L6 +export async function parseCommit( + message: string, + parser: Parser = sync, + parserOpts?: ParserOptions, +): Promise { + const defaultOpts = (await defaultChangelogOpts).parserOpts; + const opts = { + ...defaultOpts, + ...(parserOpts || {}), + fieldPattern: null, + }; + const parsed = parser(message, opts) as Commit; + parsed.raw = message; + return parsed; +} + +/** + * 生成可能更新的版本类型 + * @param commits + * @returns + */ +// https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-recommended-bump/index.js +export function whatBump(commits: Commit[]): { + level: number; + releaseType: string; + reason: string; +} { + const DEFAULT_LEVEL = 2; + let level = DEFAULT_LEVEL; + let breakings = 0; + let features = 0; + + commits.forEach(commit => { + if (commit.notes.length > 0) { + breakings += commit.notes.length; + level = 0; + } else if (commit.type === 'feat') { + features += 1; + if (level === DEFAULT_LEVEL) { + level = 1; + } + } + }); + + return { + level, + releaseType: VERSIONS[level], + reason: + breakings === 1 + ? `There is ${breakings} BREAKING CHANGE and ${features} features` + : `There are ${breakings} BREAKING CHANGES and ${features} features`, + }; +} + +export async function analysisCommitMsg( + msg: string, +): Promise<{ type: string; content: string }> { + const parsedCommit = await parseCommit(msg); + const bumpInfo = whatBump([parsedCommit]); + + return { type: bumpInfo.releaseType, content: parsedCommit.subject || '' }; +} + +/** + * 生成 changes + */ +export async function generateAllChangesFile( + comment: string, + patchType: string, +): Promise { + const rushConfiguration: RushConfiguration = getRushConfiguration(); + const needUpdateProjects = await collectShouldUpdateChangesProjects(); + const { email } = await whoAmI(); + + // 重新组织已有 change 文件和待新增的 change + for (const project of needUpdateProjects) { + const { packageName } = project; + // TODO: ChangeFile 需要的 IChangeInfo 类型和当前规范存在属性差异,暂时先忽略 email + const changeFile = new ChangeFile( + { changes: [], packageName, email }, + rushConfiguration as unknown as RushConfigurationType, + ); + changeFile.addChange({ packageName, comment, type: patchType }); + changeFile.writeSync(); + + const updateChangesPath = path.resolve( + rushConfiguration.changesFolder, + packageName, + ); + await exec(`git add ${updateChangesPath}`); + logger.success(`Success update ${packageName} changes file`); + } +} diff --git a/packages/rush-plugins/publish/src/action/change/index.ts b/packages/rush-plugins/publish/src/action/change/index.ts new file mode 100644 index 00000000..fbaa96ff --- /dev/null +++ b/packages/rush-plugins/publish/src/action/change/index.ts @@ -0,0 +1,20 @@ +import { type Command } from 'commander'; + +import { type InstallAction } from '../../types'; +import { type ChangeOptions } from './types'; +import { generateChangeFiles } from './action'; + +export const installAction: InstallAction = (program: Command) => { + program + .command('change') + .description('Generate changes in a simple way.') + .option( + '-c, --commit-msg ', + '本次提交信息,默认读取 .git/COMMIT_EDITMSG', + ) + .option('-a, --amend-commit', '是否 amend commit 阶段') + .option('-i, --ci', '是否在 CI 环境') + .action(async (options: ChangeOptions) => { + await generateChangeFiles({ ...options }); + }); +}; diff --git a/packages/rush-plugins/publish/src/action/change/types.ts b/packages/rush-plugins/publish/src/action/change/types.ts new file mode 100644 index 00000000..374946cd --- /dev/null +++ b/packages/rush-plugins/publish/src/action/change/types.ts @@ -0,0 +1,5 @@ +export interface ChangeOptions { + commitMsg: string; + amendCommit: boolean; + ci: boolean; +} diff --git a/packages/rush-plugins/publish/src/action/publish/action.ts b/packages/rush-plugins/publish/src/action/publish/action.ts new file mode 100644 index 00000000..1b09783f --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/action.ts @@ -0,0 +1,87 @@ +import { logger } from '@coze-arch/logger'; + +import { randomHash } from '../../utils/random'; +import { ensureNotUncommittedChanges, isMainBranch } from '../../utils/git'; +import { getRushConfiguration } from '../../utils/get-rush-config'; +import { generatePublishManifest } from './version'; +import { type PublishOptions } from './types'; +import { BumpType } from './types'; +import { pushToRemote } from './push-to-remote'; +import { validateAndGetPackages } from './packages'; +import { confirmForPublish } from './confirm'; +import { generateChangelog } from './changelog'; +import { applyPublishManifest } from './apply-new-version'; + +// 针对不同类型的发布,对应不同 sideEffects: +// 1. alpha: 直接创建并push 分支,触发 CI,执行发布; +// 2. beta: 本分支直接切换版本号,并发布 +// 3. 正式版本:发起MR,MR 合入 main 后,触发发布 + +export const publish = async (options: PublishOptions) => { + const sessionId = randomHash(6); + const rushConfiguration = getRushConfiguration(); + const rushFolder = rushConfiguration.rushJsonFolder; + if (process.env.SKIP_UNCOMMITTED_CHECK !== 'true') { + await ensureNotUncommittedChanges(); + } + + // 1. 验证并获取需要发布的包列表 + const packagesToPublish = validateAndGetPackages(options); + if (packagesToPublish.size === 0) { + logger.error( + 'No packages to publish, should specify some package by `--to` or `--from` or `--only`', + ); + return; + } + logger.debug( + `Will publish the following packages:\n ${[...packagesToPublish].map(pkg => pkg.packageName).join('\n')}`, + ); + + // 2. 生成发布清单 + const { manifests: publishManifests, bumpPolicy } = + await generatePublishManifest(packagesToPublish, { + ...options, + sessionId, + }); + const isBetaPublish = [BumpType.BETA, BumpType.ALPHA].includes( + bumpPolicy as BumpType, + ); + if (isBetaPublish === false && (await isMainBranch()) === false) { + // 只允许在主分支发布 + logger.error( + 'You are not in main branch, please switch to main branch and try again.', + ); + return; + } + + const continuePublish = await confirmForPublish( + publishManifests, + !!options.dryRun, + ); + + if (!continuePublish) { + return; + } + + // 3. 应用更新,注意这里会修改文件,产生 sideEffect + const postHandles = [applyPublishManifest]; + if (isBetaPublish === false) { + postHandles.push(generateChangelog); + } + const changedFiles = ( + await Promise.all(postHandles.map(handle => handle(publishManifests))) + ).flat(); + + // 4. 创建并推送发布分支 + await pushToRemote({ + publishManifests, + bumpPolicy: bumpPolicy as BumpType, + sessionId, + changedFiles, + cwd: rushFolder, + skipCommit: !!options.skipCommit, + skipPush: !!options.skipPush, + repoUrl: options.repoUrl, + }); + logger.success('Publish success.'); +}; diff --git a/packages/rush-plugins/publish/src/action/publish/apply-new-version.ts b/packages/rush-plugins/publish/src/action/publish/apply-new-version.ts new file mode 100644 index 00000000..94075bce --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/apply-new-version.ts @@ -0,0 +1,35 @@ +import path from 'path'; + +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; +import { logger } from '@coze-arch/logger'; +import { readJsonFile, writeJsonFile } from '@coze-arch/fs-enhance'; + +import { type PublishManifest, type ApplyPublishManifest } from './types'; + +const updatePackageVersion = async ( + project: RushConfigurationProject, + newVersion: string, +): Promise => { + const packageJsonPath = path.resolve(project.projectFolder, 'package.json'); + const packageJson = await readJsonFile<{ version: string }>(packageJsonPath); + packageJson.version = newVersion; + await writeJsonFile(packageJsonPath, packageJson); + return packageJsonPath; +}; + +export const applyPublishManifest: ApplyPublishManifest = async ( + manifests: PublishManifest[], +): Promise => { + const modifiedFiles: string[] = await Promise.all( + manifests.map(async manifest => { + const { project, newVersion } = manifest; + + const modifiedFile = await updatePackageVersion(project, newVersion); + return modifiedFile; + }), + ); + logger.info( + `Updated version for packages: ${manifests.map(m => m.project.packageName).join(', ')}`, + ); + return modifiedFiles; +}; diff --git a/packages/rush-plugins/publish/src/action/publish/changelog.ts b/packages/rush-plugins/publish/src/action/publish/changelog.ts new file mode 100644 index 00000000..bc77b079 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/changelog.ts @@ -0,0 +1,114 @@ +import path from 'path'; +import fs from 'fs/promises'; + +import { + readJsonFile, + writeJsonFile, + isFileExists, + isDirExists, +} from '@coze-arch/fs-enhance'; + +import { getRushConfiguration } from '../../utils/get-rush-config'; +import { + generateChangelog as core, + type ChangeFile, + type ChangeLog, +} from '../../generate-changelog/generate-changelog'; +import { type PublishManifest, type ApplyPublishManifest } from './types'; + +const deleteFiles = async (files: string[]): Promise => { + await Promise.all( + files.map(async file => { + await fs.unlink(file); + }), + ); +}; + +const readChangeFiles = async (changedFolderOfPkg: string) => { + if (!(await isDirExists(changedFolderOfPkg))) { + return []; + } + const changeFiles = (await fs.readdir(changedFolderOfPkg)).filter(r => + r.endsWith('.json'), + ); + return changeFiles.map(r => path.resolve(changedFolderOfPkg, r)); +}; + +const readChanges = async (changeFiles: string[]) => { + const changes = ( + await Promise.all( + changeFiles.map(async r => { + try { + const res = await readJsonFile(r); + return res; + } catch (e) { + return null; + } + }), + ) + ).filter(r => r !== null); + return changes; +}; + +const readPreviousChangelog = async ( + changelogJsonPath: string, + packageName: string, +) => { + const defaultValue = { + name: packageName, + entries: [] as ChangeLog['entries'], + }; + if (!(await isFileExists(changelogJsonPath))) { + return defaultValue; + } + try { + const changelog = await readJsonFile(changelogJsonPath); + return changelog; + } catch (e) { + return defaultValue; + } +}; + +const generateChangelogForProject = async (manifest: PublishManifest) => { + const { project, newVersion } = manifest; + const rushConfiguration = getRushConfiguration(); + const { changesFolder } = rushConfiguration; + const changedFolderOfPkg = path.resolve(changesFolder, project.packageName); + const changelogJsonPath = path.resolve( + project.projectFolder, + 'CHANGELOG.json', + ); + const changelogMarkdownPath = path.resolve( + project.projectFolder, + 'CHANGELOG.md', + ); + + const changeFiles = await readChangeFiles(changedFolderOfPkg); + const changes = await readChanges(changeFiles); + + const previousChangelog = await readPreviousChangelog( + changelogJsonPath, + project.packageName, + ); + const { changelog, report: changelogMarkdown } = await core({ + packageName: project.packageName, + version: newVersion, + commingChanges: changes, + previousChangelog, + }); + + // side effects + await writeJsonFile(changelogJsonPath, changelog); + await fs.writeFile(changelogMarkdownPath, changelogMarkdown); + await deleteFiles(changeFiles); + return [changelogJsonPath, changelogMarkdownPath, ...changeFiles]; +}; + +export const generateChangelog: ApplyPublishManifest = async ( + manifests: PublishManifest[], +) => { + const modifiedFiles = await Promise.all( + manifests.map(manifest => generateChangelogForProject(manifest)), + ); + return modifiedFiles.flat(); +}; diff --git a/packages/rush-plugins/publish/src/action/publish/confirm.ts b/packages/rush-plugins/publish/src/action/publish/confirm.ts new file mode 100644 index 00000000..e2ad529a --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/confirm.ts @@ -0,0 +1,29 @@ +import chalk from 'chalk'; +import { confirm } from '@inquirer/prompts'; + +import { type PublishManifest } from './types'; + +export const confirmForPublish = async ( + publishManifest: PublishManifest[], + dryRun: boolean, +): Promise => { + console.log(chalk.gray('Will publish the following packages:')); + publishManifest.forEach(manifest => { + const msg = `${manifest.project.packageName}: ${chalk.bgGreen(`${manifest.currentVersion} -> ${chalk.bold(manifest.newVersion)}`)}`; + console.log(`- ${msg}`); + }); + if (dryRun) { + return false; + } + + console.log('\n'); + try { + const result = await confirm({ + message: 'Are you sure to publish?', + default: true, + }); + return result; + } catch (error) { + return false; + } +}; diff --git a/packages/rush-plugins/publish/src/action/publish/const.ts b/packages/rush-plugins/publish/src/action/publish/const.ts new file mode 100644 index 00000000..e6b559a8 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/const.ts @@ -0,0 +1 @@ +export const GIT_REPO_URL_REGEX = /git@github\.com:([^\/]+)\/([^\.]+)\.git/; diff --git a/packages/rush-plugins/publish/src/action/publish/git.ts b/packages/rush-plugins/publish/src/action/publish/git.ts new file mode 100644 index 00000000..d11014bf --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/git.ts @@ -0,0 +1,60 @@ +import { logger } from '@coze-arch/logger'; + +import { exec } from '../../utils/exec'; +import { type PublishManifest } from './types'; + +export async function createAndPushBranch( + branchName: string, + cwd: string, +): Promise { + try { + // 创建新分支 + await exec(`git checkout -b ${branchName}`, { cwd }); + logger.info(`Created new branch: ${branchName}`); + + // 推送到远程 + await exec(`git push -u origin ${branchName}`, { cwd }); + logger.info(`Pushed branch to remote: ${branchName}`); + } catch (error) { + throw new Error(`Failed to create/push branch: ${error}`); + } +} + +interface CommitChangesOptions { + sessionId: string; + files: string[]; + cwd: string; + publishManifests: PublishManifest[]; + branchName: string; +} +export async function commitChanges({ + sessionId, + files, + cwd, + publishManifests, + branchName, +}: CommitChangesOptions): Promise<{ effects: string[]; branchName: string }> { + await exec(`git add ${files.join(' ')}`, { cwd }); + await exec(`git commit -m "chore: Publish ${branchName}" -n`, { cwd }); + + const tags = publishManifests.map( + m => `v/${m.project.packageName}@${m.newVersion}`, + ); + await exec( + tags.map(tag => `git tag -a ${tag} -m "Bump type ${tag}"`).join(' && '), + { cwd }, + ); + return { effects: [...tags, branchName], branchName }; +} + +export interface PushOptions { + refs: string[]; + cwd: string; + repoUrl: string; +} + +export async function push({ refs, cwd, repoUrl }: PushOptions): Promise { + await exec(`git push ${repoUrl} ${refs.join(' ')} --no-verify`, { + cwd, + }); +} diff --git a/packages/rush-plugins/publish/src/action/publish/index.ts b/packages/rush-plugins/publish/src/action/publish/index.ts new file mode 100644 index 00000000..65acf855 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/index.ts @@ -0,0 +1,58 @@ +import { type Command } from 'commander'; +import { logger } from '@coze-arch/logger'; + +import { getCurrentOrigin } from '../../utils/git'; +import { type InstallAction } from '../../types'; +import { type PublishOptions } from './types'; +import { GIT_REPO_URL_REGEX } from './const'; +import { publish } from './action'; + +export const installAction: InstallAction = (program: Command) => { + program + .command('pub') + .description('Generate new version and create release branch.') + .option('-v, --version ', 'Specify publish version') + .option('-d, --dry-run', 'Enable dry run mode', false) + .option( + '-t, --to ', + 'Publish specified packages and their downstream dependencies', + ) + .option( + '-f, --from ', + 'Publish specified packages and their upstream/downstream dependencies', + ) + .option('-o, --only ', 'Only publish specified packages') + .option('-s, --skip-commit', 'Skip git commit', false) + .option('-p, --skip-push', 'Skip git push', false) + .option( + '-b, --bump-type ', + 'Version bump type (alpha/beta/patch/minor/major)', + /^(alpha|beta|patch|minor|major)$/, + ) + .option( + '--repo-url ', + 'Git repository URL (e.g. git@github.com:coze-dev/coze-js.git)', + undefined, + ) + .action(async (options: PublishOptions) => { + try { + const repoUrl = options.repoUrl || (await getCurrentOrigin()); + if (!repoUrl) { + throw new Error('Git repository URL is required'); + } + if (!GIT_REPO_URL_REGEX.test(repoUrl)) { + throw new Error( + `Invalid git repository URL: ${repoUrl}, it should be follow the format: git@github.com:\${org}/\${repo}.git`, + ); + } + const normalizeOptions = { + ...options, + repoUrl, + }; + await publish(normalizeOptions); + } catch (error) { + logger.error('Publish failed!'); + logger.error((error as Error).message); + } + }); +}; diff --git a/packages/rush-plugins/publish/src/action/publish/packages.ts b/packages/rush-plugins/publish/src/action/publish/packages.ts new file mode 100644 index 00000000..67c1c559 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/packages.ts @@ -0,0 +1,73 @@ +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +import { getRushConfiguration } from '../../utils/get-rush-config'; +import { type PublishOptions } from './types'; + +enum RetrievePattern { + TO = 'to', + FROM = 'from', + ONLY = 'only', +} + +const retrievePackages = ( + pattern: RetrievePattern, + packages: string[], +): Set => { + const rushConfiguration = getRushConfiguration(); + const matchedPackages = new Set(); + packages.forEach(pkg => { + const project = rushConfiguration.getProjectByName(pkg); + if (!project) { + throw new Error(`Package "${pkg}" not found in rush configuration`); + } + if (!project.shouldPublish) { + throw new Error( + `Package "${pkg}" is not set to publish. if you want to publish it, please set the "shouldPublish" property to true in the \`rush.json\` file.`, + ); + } + const matched: RushConfigurationProject['dependencyProjects'][] = []; + switch (pattern) { + case 'to': { + matched.push(project.dependencyProjects); + break; + } + case 'from': { + matched.push(project.consumingProjects); + matched.push(project.dependencyProjects); + break; + } + case 'only': { + // do nothing + break; + } + default: { + throw new Error('Unexpected package selection state'); + } + } + + for (const matchedSet of matched) { + for (const p of matchedSet) { + if (p.shouldPublish) { + matchedPackages.add(p); + } + } + } + matchedPackages.add(project); + }); + return matchedPackages; +}; + +export const validateAndGetPackages = (options: PublishOptions) => { + const retrievePatterns = Object.values(RetrievePattern); + if (retrievePatterns.every(pattern => (options[pattern]?.length || 0) <= 0)) { + throw new Error('No packages to publish'); + } + return retrievePatterns.reduce((acc, pattern) => { + const packages = options[pattern]; + if (!packages || packages.length <= 0) { + return acc; + } + const placeholders = retrievePackages(pattern as RetrievePattern, packages); + return new Set([...acc, ...placeholders]); + }, new Set()); +}; diff --git a/packages/rush-plugins/publish/src/action/publish/push-to-remote.ts b/packages/rush-plugins/publish/src/action/publish/push-to-remote.ts new file mode 100644 index 00000000..4ff53721 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/push-to-remote.ts @@ -0,0 +1,94 @@ +import dayjs from 'dayjs'; +import { logger } from '@coze-arch/logger'; + +import { getCurrentBranchName } from '../../utils/git'; +import { exec } from '../../utils/exec'; +import { type PublishManifest, BumpType } from './types'; +import { commitChanges, push } from './git'; +import { GIT_REPO_URL_REGEX } from './const'; + +interface PushToRemoteOptions { + sessionId: string; + changedFiles: string[]; + cwd: string; + publishManifests: PublishManifest[]; + bumpPolicy: BumpType | string; + skipCommit: boolean; + skipPush: boolean; + repoUrl: string; +} + +export const pushToRemote = async (options: PushToRemoteOptions) => { + const { + sessionId, + changedFiles, + cwd, + publishManifests, + bumpPolicy, + skipCommit, + skipPush, + repoUrl, + } = options; + if (skipCommit) { + return; + } + + // 获取仓库 URL + const actualRepoUrl = repoUrl; + + let branchName: string; + if (bumpPolicy === BumpType.BETA) { + branchName = await getCurrentBranchName(); + } else { + const date = dayjs().format('YYYYMMDD'); + branchName = `release/${date}-${sessionId}`; + await exec(`git checkout -b ${branchName}`, { cwd }); + } + + // 4. 创建并推送发布分支 + const { effects } = await commitChanges({ + sessionId, + files: changedFiles, + cwd, + publishManifests, + branchName, + }); + if (skipPush) { + return; + } + await push({ + refs: effects, + cwd, + repoUrl: actualRepoUrl, + }); + + const isTestPublish = [BumpType.ALPHA, BumpType.BETA].includes( + bumpPolicy as BumpType, + ); + + // 从 git URL 提取组织和仓库名称,用于构建 GitHub 链接 + const repoInfoMatch = actualRepoUrl.match(GIT_REPO_URL_REGEX); + if (!repoInfoMatch) { + throw new Error('Invalid git repository URL'); + } + const repoOwner = repoInfoMatch[1]; + const repoName = repoInfoMatch[2]; + + if (isTestPublish) { + logger.success( + `Please refer to https://github.com/${repoOwner}/${repoName}/actions/workflows/release.yml for the release progress.`, + ); + } else { + const prUrl = `https://github.com/${repoOwner}/${repoName}/compare/${branchName}?expand=1`; + const log = [ + '************************************************', + '*', + `* Please create PR: ${prUrl}`, + '*', + '************************************************', + ]; + logger.success(log.join('\n'), false); + const open = await import('open'); + await open.default(prUrl); + } +}; diff --git a/packages/rush-plugins/publish/src/action/publish/request-bump-type.ts b/packages/rush-plugins/publish/src/action/publish/request-bump-type.ts new file mode 100644 index 00000000..72bb39af --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/request-bump-type.ts @@ -0,0 +1,52 @@ +import chalk from 'chalk'; +import { select } from '@inquirer/prompts'; + +import { BumpType } from './types'; + +/** + * 获取更新类型的描述 + */ +const getTypeDescription = (type: BumpType): string => { + switch (type) { + case BumpType.MAJOR: + return `Major update, incompatible API changes, for example: ${chalk.green('1.1.1 -> 2.0.0')}`; + case BumpType.MINOR: + return `Minor update, backwards-compatible features, for example: ${chalk.green('1.1.1 -> 1.2.0')}`; + case BumpType.PATCH: + return `Patch update, backwards-compatible bug fixes, for example: ${chalk.green('1.1.1 -> 1.1.2')}`; + case BumpType.BETA: + return `Beta pre-release version, for example: ${chalk.green('1.1.1-beta.1')}`; + case BumpType.ALPHA: + return `Alpha pre-release version, for example: ${chalk.green('1.1.1-alpha.2597f3')}`; + default: + return ''; + } +}; + +/** + * 让用户选择版本更新类型 + */ +export const requestBumpType = async (): Promise => { + const bumpTypesChoices = [ + BumpType.ALPHA, + BumpType.BETA, + BumpType.PATCH, + BumpType.MINOR, + BumpType.MAJOR, + ]; + const choices = bumpTypesChoices.map(type => ({ + name: `${chalk.bold(type.toUpperCase())}: ${getTypeDescription(type)}`, + value: type, + })); + + try { + const selected = await select({ + message: 'Select version bump type:', + choices, + }); + + return selected; + } catch (error) { + return null; + } +}; diff --git a/packages/rush-plugins/publish/src/action/publish/types.ts b/packages/rush-plugins/publish/src/action/publish/types.ts new file mode 100644 index 00000000..bd3561f8 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/types.ts @@ -0,0 +1,31 @@ +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +export enum BumpType { + ALPHA = 'alpha', + BETA = 'beta', + PATCH = 'patch', + MINOR = 'minor', + MAJOR = 'major', +} + +export interface PublishOptions { + version?: string; + dryRun?: boolean; + to?: string[]; // 发布指定包及其下游依赖 + from?: string[]; // 发布指定包及其上下游依赖 + only?: string[]; // 仅发布指定包 + bumpType?: BumpType; // 版本升级类型 + skipCommit?: boolean; // 是否跳过提交 + skipPush?: boolean; // 是否跳过推送 + repoUrl: string; // Git 仓库 URL,例如:git@github.com:coze-dev/coze-js.git +} + +export interface PublishManifest { + project: RushConfigurationProject; + currentVersion: string; + newVersion: string; +} + +export type ApplyPublishManifest = ( + manifests: PublishManifest[], +) => Promise; diff --git a/packages/rush-plugins/publish/src/action/publish/version.ts b/packages/rush-plugins/publish/src/action/publish/version.ts new file mode 100644 index 00000000..254e5c23 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/publish/version.ts @@ -0,0 +1,139 @@ +import semver from 'semver'; +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +import { randomHash } from '../../utils/random'; +import { BumpType } from './types'; +import { type PublishManifest } from './types'; +import { requestBumpType } from './request-bump-type'; + +interface VersionOptions { + version?: string; + bumpType?: BumpType; + sessionId: string; +} + +/** + * 根据当前版本和发布类型计算新版本号 + */ +const calculateNewVersion = ( + currentVersion: string, + bumpType: BumpType, + sessionId?: string, +): string => { + // 解析当前版本 + const parsed = semver.parse(currentVersion); + if (!parsed) { + throw new Error(`Invalid current version: ${currentVersion}`); + } + + const { major, minor, patch, prerelease } = parsed; + + switch (bumpType) { + case BumpType.PATCH: + case BumpType.MINOR: + case BumpType.MAJOR: { + // 如果当前是预发布版本,去掉预发布标识 + const cc = + prerelease.length > 0 ? `${major}.${minor}.${patch}` : currentVersion; + // 否则增加 patch 版本 + return semver.inc(cc, bumpType) || currentVersion; + } + case BumpType.BETA: { + // 如果当前已经是 beta 版本,增加 beta 版本号 + if (prerelease[0] === 'beta') { + const nextVersion = semver.inc(currentVersion, 'prerelease', 'beta'); + return nextVersion || currentVersion; + } + // 否则基于当前版本创建新的 beta 版本 + const baseVersion = `${major}.${minor}.${patch}`; + return `${baseVersion}-beta.1`; + } + case BumpType.ALPHA: { + // 否则基于当前版本创建新的 alpha 版本 + const baseVersion = `${major}.${minor}.${patch}`; + // 生成随机哈希值 + return `${baseVersion}-alpha.${sessionId || randomHash(6)}`; + } + default: { + throw new Error( + `Invalid bump type: ${bumpType}, should be one of ${Object.values(BumpType).join(', ')}`, + ); + } + } +}; + +/** + * 生成新版本号 + * 策略优先级: + * 1. 指定版本号 + * 2. 指定发布类型 + * 3. 交互式选择 + */ +const generateNewVersionForPackage = ( + project: RushConfigurationProject, + options: VersionOptions, +): string => { + const currentVersion = project.packageJson.version; + // 1. 如果指定了版本号,直接使用 + if (options.version) { + return options.version; + } + + // 1. 如果指定了发布类型,计算新版本号 + const { bumpType } = options; + if (!bumpType) { + throw new Error('Version selection was cancelled'); + } + const newVersion = calculateNewVersion( + currentVersion, + bumpType, + options.sessionId, + ); + + return newVersion; +}; + +const calBumpPolicy = (options: VersionOptions) => { + const { version, bumpType } = options; + if (version) { + return version; + } + return bumpType; +}; + +/** + * 生成发布清单 + */ +export const generatePublishManifest = async ( + packages: Set, + options: VersionOptions, +): Promise<{ + manifests: PublishManifest[]; + bumpPolicy: BumpType | string | undefined; +}> => { + const manifests: PublishManifest[] = []; + const { version, bumpType } = options; + if (version && !semver.valid(version)) { + throw new Error(`Invalid version specified: ${version}`); + } else if (!bumpType) { + const newBumpType = await requestBumpType(); + if (!newBumpType) { + throw new Error('Version selection was cancelled!'); + } + options.bumpType = newBumpType; + } + for (const pkg of packages) { + const currentVersion = pkg.packageJson.version; + const newVersion = await generateNewVersionForPackage(pkg, options); + manifests.push({ + project: pkg, + newVersion, + currentVersion, + }); + } + const bumpPolicy = calBumpPolicy(options); + return { + manifests, + bumpPolicy, + }; +}; diff --git a/packages/rush-plugins/publish/src/action/release/action.ts b/packages/rush-plugins/publish/src/action/release/action.ts new file mode 100644 index 00000000..489181a5 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/release/action.ts @@ -0,0 +1,42 @@ +import { logger } from '@coze-arch/logger'; + +import { getCurrentBranchName } from '../../utils/git'; +import { exec } from '../../utils/exec'; +import { type ReleaseOptions } from './types'; +import { releasePackages } from './release'; +import { checkReleasePlan } from './plan'; +import { buildReleaseManifest } from './manifest'; +import { getPackagesToPublish } from './git'; + +export async function release(options: ReleaseOptions): Promise { + const { commit, dryRun = false, registry } = options; + + // 1. 获取需要发布的包列表 + const packagesToPublish = await getPackagesToPublish(commit); + if (packagesToPublish.length === 0) { + logger.warn('No packages to publish'); + return; + } + + // 2. 构建发布依赖树 + const releaseManifests = buildReleaseManifest(packagesToPublish); + logger.info('Release manifests:'); + logger.info( + releaseManifests + .map(manifest => `${manifest.project.packageName}@${manifest.version}`) + .join(', '), + false, + ); + const branchName = await getCurrentBranchName(); + checkReleasePlan(releaseManifests, branchName); + await exec(`git checkout ${commit}`); + + await releasePackages(releaseManifests, { commit, dryRun, registry }); + logger.success('All packages published successfully!'); + logger.success( + releaseManifests + .map(manifest => `- ${manifest.project.packageName}@${manifest.version}`) + .join('\n'), + false, + ); +} diff --git a/packages/rush-plugins/publish/src/action/release/git.ts b/packages/rush-plugins/publish/src/action/release/git.ts new file mode 100644 index 00000000..11f86fdc --- /dev/null +++ b/packages/rush-plugins/publish/src/action/release/git.ts @@ -0,0 +1,30 @@ +import { exec } from '../../utils/exec'; +import { type PackageToPublish } from './types'; + +/** + * 从 git tag 中解析需要发布的包信息 + */ +export const getPackagesToPublish = async ( + commit: string, +): Promise => { + // 获取指定 commit 的所有 tag + const { stdout } = await exec(`git tag --points-at ${commit}`); + const tags = stdout.split('\n').filter(Boolean); + + // 解析符合 v/{packagename}@{version} 格式的 tag + const packagePattern = /^v\/(.+)@(.+)$/; + const packages: PackageToPublish[] = []; + + for (const tag of tags) { + const match = tag.match(packagePattern); + if (match) { + const [, packageName, version] = match; + packages.push({ + packageName, + version, + }); + } + } + + return packages; +}; diff --git a/packages/rush-plugins/publish/src/action/release/index.ts b/packages/rush-plugins/publish/src/action/release/index.ts new file mode 100644 index 00000000..f6c94d47 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/release/index.ts @@ -0,0 +1,34 @@ +import { type Command } from 'commander'; +import { logger } from '@coze-arch/logger'; + +import { type InstallAction } from '../../types'; +import { type ReleaseOptions } from './types'; +import { release } from './action'; + +export const installAction: InstallAction = (program: Command) => { + program + .command('release') + .description('Release packages based on git tags.') + .requiredOption('--commit ', '需要执行发布的 commit id') + .option('--dry-run', '是否只执行不真实发布', false) + .option( + '-r, --registry ', + '发布到的 registry', + 'https://registry.npmjs.org', + ) + .action(async (options: ReleaseOptions) => { + try { + if (!options.commit) { + throw new Error('请提供需要发布的 commit id'); + } + if (!process.env.NODE_AUTH_TOKEN) { + throw new Error('请设置 NODE_AUTH_TOKEN 环境变量'); + } + await release(options); + } catch (error) { + logger.error('Release failed!'); + logger.error((error as Error).message); + process.exit(1); + } + }); +}; diff --git a/packages/rush-plugins/publish/src/action/release/manifest.ts b/packages/rush-plugins/publish/src/action/release/manifest.ts new file mode 100644 index 00000000..6eff6bb1 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/release/manifest.ts @@ -0,0 +1,18 @@ +import { getRushConfiguration } from '../../utils/get-rush-config'; +import { type ReleaseManifest, type PackageToPublish } from './types'; + +/** + * 构建发布依赖树 + */ +export function buildReleaseManifest( + packages: PackageToPublish[], +): ReleaseManifest[] { + const rushConfiguration = getRushConfiguration(); + return packages.map(pkg => { + const project = rushConfiguration.getProjectByName(pkg.packageName); + if (!project) { + throw new Error(`Cannot find project: ${pkg.packageName}`); + } + return { project, version: project.packageJson.version }; + }); +} diff --git a/packages/rush-plugins/publish/src/action/release/package.ts b/packages/rush-plugins/publish/src/action/release/package.ts new file mode 100644 index 00000000..f2d47274 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/release/package.ts @@ -0,0 +1,57 @@ +import path from 'path'; + +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; +import { readJsonFile, writeJsonFile } from '@coze-arch/fs-enhance'; + +import { getRushConfiguration } from '../../utils/get-rush-config'; + +type PackageJson = Record & { + cozePublishConfig?: Record; + dependencies?: Record; +}; +type PrehandleJob = (packageJson: PackageJson) => Promise; + +/** + * 更新依赖版本 + */ +const updateDependencyVersions: PrehandleJob = async ( + packageJson: PackageJson, +) => { + const rushConfiguration = getRushConfiguration(); + const { dependencies } = packageJson; + if (dependencies) { + for (const [dep, ver] of Object.entries(dependencies)) { + const project = rushConfiguration.getProjectByName(dep); + if (/^workspace:/.test(ver) && project) { + dependencies[dep] = project.packageJson.version; + } + } + } + return Promise.resolve(packageJson); +}; + +const applyCozePublishConfig: PrehandleJob = async ( + packageJson: PackageJson, +) => { + const { cozePublishConfig } = packageJson; + if (cozePublishConfig) { + const keys = Object.keys(cozePublishConfig); + for (const key of keys) { + packageJson[key] = cozePublishConfig[key]; + } + } + return Promise.resolve(packageJson); +}; + +export const applyPublishConfig = async (project: RushConfigurationProject) => { + const jobs: PrehandleJob[] = [ + updateDependencyVersions, + applyCozePublishConfig, + ]; + const packageJsonPath = path.join(project.projectFolder, 'package.json'); + let packageJson = await readJsonFile(packageJsonPath); + for (const job of jobs) { + packageJson = await job(packageJson); + } + await writeJsonFile(packageJsonPath, packageJson); +}; diff --git a/packages/rush-plugins/publish/src/action/release/plan.ts b/packages/rush-plugins/publish/src/action/release/plan.ts new file mode 100644 index 00000000..a457d66b --- /dev/null +++ b/packages/rush-plugins/publish/src/action/release/plan.ts @@ -0,0 +1,38 @@ +import { type ReleaseManifest } from './types'; + +export enum ReleaseType { + ALPHA = 'alpha', + BETA = 'beta', + LATEST = 'latest', +} + +export const calReleaseType = (version: string) => { + const tag = version.includes('alpha') + ? ReleaseType.ALPHA + : version.includes('beta') + ? ReleaseType.BETA + : ReleaseType.LATEST; + return tag; +}; + +const calReleasePlan = (releaseManifests: ReleaseManifest[]) => { + const plan = releaseManifests.map(r => calReleaseType(r.version)); + if (plan.some(p => p === ReleaseType.LATEST)) { + return ReleaseType.LATEST; + } + if (plan.some(p => p === ReleaseType.BETA)) { + return ReleaseType.BETA; + } + return ReleaseType.ALPHA; +}; + +export const checkReleasePlan = ( + releaseManifests: ReleaseManifest[], + branchName: string, +) => { + const releasePlan = calReleasePlan(releaseManifests); + if (releasePlan === ReleaseType.LATEST && branchName !== 'main') { + throw new Error('For LATEST release, should be on main branch only.'); + } + return true; +}; diff --git a/packages/rush-plugins/publish/src/action/release/release.ts b/packages/rush-plugins/publish/src/action/release/release.ts new file mode 100644 index 00000000..e999dca4 --- /dev/null +++ b/packages/rush-plugins/publish/src/action/release/release.ts @@ -0,0 +1,67 @@ +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; +import { logger } from '@coze-arch/logger'; + +import { exec } from '../../utils/exec'; +import { type ReleaseOptions, type ReleaseManifest } from './types'; +import { applyPublishConfig } from './package'; + +/** + * 发布包 + */ +const publishPackage = async ( + project: RushConfigurationProject, + releaseOptions: ReleaseOptions, +): Promise => { + const { dryRun, registry } = releaseOptions; + const token = process.env.NODE_AUTH_TOKEN; + const { version } = project.packageJson; + const tag = version.includes('alpha') + ? 'alpha' + : version.includes('beta') + ? 'beta' + : 'latest'; + const args = [`NODE_AUTH_TOKEN=${token}`, 'npm', 'publish', `--tag ${tag}`]; + if (dryRun) { + args.push('--dry-run'); + } + if (registry) { + args.push(`--registry=${registry}`); + } + + await exec(args.join(' '), { + cwd: project.projectFolder, + }); + + logger.success(`- Published ${project.packageName}@${version}`); +}; + +const releasePackage = async ( + releaseManifest: ReleaseManifest, + releaseOptions: ReleaseOptions, +) => { + const { project } = releaseManifest; + const { packageName } = project; + logger.info(`Preparing release for package: ${packageName}`); + await applyPublishConfig(project); + await publishPackage(project, releaseOptions); +}; + +const buildProjects = async (releaseManifests: ReleaseManifest[]) => { + const packageNames = releaseManifests.map( + manifest => manifest.project.packageName, + ); + const buildCommands = `rush build ${packageNames.map(name => `--to ${name}`).join(' ')}`; + await exec(buildCommands); +}; + +export const releasePackages = async ( + releaseManifests: ReleaseManifest[], + releaseOptions: ReleaseOptions, +) => { + await buildProjects(releaseManifests); + await Promise.all( + releaseManifests.map(async manifest => { + await releasePackage(manifest, releaseOptions); + }), + ); +}; diff --git a/packages/rush-plugins/publish/src/action/release/types.ts b/packages/rush-plugins/publish/src/action/release/types.ts new file mode 100644 index 00000000..b323310b --- /dev/null +++ b/packages/rush-plugins/publish/src/action/release/types.ts @@ -0,0 +1,17 @@ +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; + +export interface ReleaseOptions { + commit: string; + dryRun?: boolean; + registry: string; +} + +export interface PackageToPublish { + packageName: string; + version: string; +} + +export interface ReleaseManifest { + project: RushConfigurationProject; + version: string; +} diff --git a/packages/rush-plugins/publish/src/generate-changelog/README.md b/packages/rush-plugins/publish/src/generate-changelog/README.md new file mode 100644 index 00000000..30ce3ab9 --- /dev/null +++ b/packages/rush-plugins/publish/src/generate-changelog/README.md @@ -0,0 +1 @@ +这部分代码,以及 change 命令对应的代码,未来可以单独抽出一个package diff --git a/packages/rush-plugins/publish/src/generate-changelog/change-file.schema.json b/packages/rush-plugins/publish/src/generate-changelog/change-file.schema.json new file mode 100644 index 00000000..c7adec7e --- /dev/null +++ b/packages/rush-plugins/publish/src/generate-changelog/change-file.schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Generated changefiles", + "description": "For use with the coze/rush-x tool, this file tracks changes that are made to individual packages.", + + "type": "object", + "properties": { + "$schema": { + "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", + "type": "string" + }, + "changes": { + "description": "A list of changes that apply to the specified package. These changes will cause the specified package and all dependent packages ", + "type": "array", + "items": { + "type": "object", + "required": ["packageName", "comment", "type"], + "properties": { + "packageName": { + "type": "string", + "description": "The name of the package that the change applies to." + }, + "comment": { + "type": "string", + "description": "A comment that describes the change being made." + }, + "type": { + "type": "string", + "description": "The change type associated with the change.", + "enum": ["none", "dependency", "hotfix", "patch", "minor", "major"] + }, + "customFields": { + "type": "object", + "description": "An optional dictionary of custom string fields.", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + } + } + } + }, + "packageName": { + "description": "The name of the package that the change file applies to.", + "type": "string" + }, + "email": { + "description": "The email address for the author of the change.", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/packages/rush-plugins/publish/src/generate-changelog/changelog.schema.json b/packages/rush-plugins/publish/src/generate-changelog/changelog.schema.json new file mode 100644 index 00000000..86b0f7f7 --- /dev/null +++ b/packages/rush-plugins/publish/src/generate-changelog/changelog.schema.json @@ -0,0 +1,118 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Changelog file for packages in coze/rush-x which should be published", + "description": "This file represents a JSON format of changelog of a package", + "additionalProperties": false, + "properties": { + "entries": { + "description": "Entries within the changelog corresponding to each published version.", + "items": { + "$ref": "#/definitions/IChangeLogEntry" + }, + "type": "array" + }, + "name": { + "description": "Name of the project", + "type": "string" + } + }, + "required": ["name", "entries"], + "type": "object", + "definitions": { + "IChangeLogComment": { + "additionalProperties": false, + "description": "Interface representing a single changelog comment within an entry.", + "properties": { + "author": { + "description": "The author, if applicable, that created the change request.", + "type": "string" + }, + "comment": { + "description": "The given comment. (supports markdown.)", + "type": "string" + }, + "commit": { + "description": "The commit, if applicable, including the change request.", + "type": "string" + }, + "customFields": { + "type": "object", + "description": "An optional dictionary of custom string fields.", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + } + }, + "required": ["comment"], + "type": "object" + }, + "IChangeLogEntry": { + "additionalProperties": false, + "description": "Interface representing a single published entry in the changelog.", + "properties": { + "comments": { + "$ref": "#/definitions/IChangeLogEntryComments", + "description": "Comments for the entry, where key represents the ChangeType string (Example: major)" + }, + "date": { + "description": "The UTC date when the publish was applied. (Example: Fri, 02 Dec 2016 22:27:16 GMT)", + "type": "string" + }, + "tag": { + "description": "Git tag used to identify the published commit. (Example: b7f55611e54910327a206476b185265498c66acf)", + "type": "string" + }, + "version": { + "description": "Published version for the entry. (Example: 1.0.0)", + "type": "string" + } + }, + "required": ["version", "tag", "comments"], + "type": "object" + }, + "IChangeLogEntryComments": { + "additionalProperties": false, + "description": "Interface representing a single published entry in the changelog.", + "properties": { + "dependency": { + "description": "Describes changes to the package's dependencies", + "items": { + "$ref": "#/definitions/IChangeLogComment" + }, + "type": "array" + }, + "major": { + "description": "Describes changes which cause a major-level SemVer bump", + "items": { + "$ref": "#/definitions/IChangeLogComment" + }, + "type": "array" + }, + "minor": { + "description": "Describes changes which cause a minor-level SemVer bump", + "items": { + "$ref": "#/definitions/IChangeLogComment" + }, + "type": "array" + }, + "none": { + "description": "Describe changes that do not have version information", + "items": { + "$ref": "#/definitions/IChangeLogComment" + }, + "type": "array" + }, + "patch": { + "description": "Describes changes which cause a patch-level SemVer bump", + "items": { + "$ref": "#/definitions/IChangeLogComment" + }, + "type": "array" + } + }, + "type": "object" + } + } +} diff --git a/packages/rush-plugins/publish/src/generate-changelog/generate-changelog.ts b/packages/rush-plugins/publish/src/generate-changelog/generate-changelog.ts new file mode 100644 index 00000000..484117c5 --- /dev/null +++ b/packages/rush-plugins/publish/src/generate-changelog/generate-changelog.ts @@ -0,0 +1,206 @@ +import semver from 'semver'; +import dayjs from 'dayjs'; + +interface ChangeLogComment { + comment: string; + author?: string; + commit?: string; + customFields?: Record; +} + +interface ChangeLogEntry { + version: string; + tag: string; + date: string; + comments: { + major?: ChangeLogComment[]; + minor?: ChangeLogComment[]; + patch?: ChangeLogComment[]; + dependency?: ChangeLogComment[]; + none?: ChangeLogComment[]; + }; +} + +export interface ChangeLog { + name: string; + entries: ChangeLogEntry[]; +} + +interface Change { + packageName: string; + comment: string; + type: 'none' | 'dependency' | 'patch' | 'minor' | 'major'; + customFields?: Record; +} + +export interface ChangeFile { + changes: Change[]; + packageName: string; + email?: string; +} + +/** + * Convert change type to corresponding title + */ +const getChangeTypeTitle = (type: Change['type']): string => { + switch (type) { + case 'major': + return '### Breaking Changes'; + case 'minor': + return '### New Features'; + case 'patch': + return '### Bug Fixes'; + case 'dependency': + return '### Dependencies'; + case 'none': + return '### Other Changes'; + default: + return ''; + } +}; + +/** + * Generate changelog for a single version + */ +const generateVersionChangelog = ({ + version, + changes, + tag, + defaultChangelog, +}: { + version: string; + changes: Change[]; + tag: string; + defaultChangelog?: string; +}): ChangeLogEntry => { + // Group changes by type + const groupedChanges = + changes.length > 0 + ? changes.reduce( + (acc, change) => { + const { type, comment, customFields } = change; + if (!acc[type]) { + acc[type] = []; + } + const node = acc[type]; + if (node.some(existing => existing.comment === comment) === false) { + node.push({ + comment, + customFields, + }); + } + return acc; + }, + {} as unknown as ChangeLogEntry['comments'], + ) + : { none: [{ comment: defaultChangelog }] }; + + return { + version, + tag, + date: dayjs().toISOString(), + comments: groupedChanges as ChangeLogEntry['comments'], + }; +}; + +/** + * Convert changelog to Markdown format + */ +const changelogToMarkdown = (changelog: ChangeLog): string => { + const lines: string[] = []; + lines.push(`# ${changelog.name}`); + lines.push(''); + + changelog.entries.forEach(entry => { + lines.push( + `## ${entry.version} - ${dayjs(entry.date).format('YYYY-MM-DD')}`, + ); + lines.push(''); + + // Output different types of changes in fixed order + const typeOrder: Change['type'][] = [ + 'major', + 'minor', + 'patch', + 'dependency', + 'none', + ]; + + typeOrder.forEach(type => { + const changes = entry.comments[type]; + if (changes?.length) { + lines.push(getChangeTypeTitle(type)); + lines.push(''); + changes.forEach(change => { + lines.push(`- ${change.comment}`); + // Add custom fields to changelog if they exist + if (change.customFields) { + Object.entries(change.customFields).forEach(([key, value]) => { + lines.push(` - ${key}: ${value}`); + }); + } + }); + lines.push(''); + } + }); + + lines.push(''); + }); + + return lines.join('\n'); +}; + +interface GenerateChangelogOptions { + packageName: string; + version: string; + commingChanges: ChangeFile[]; + previousChangelog?: ChangeLog; + defaultChangelog?: string; + tag?: string; +} +/** + * Merge and generate changelog + */ +export const generateChangelog = ({ + commingChanges, + previousChangelog, + version, + packageName, + tag, + defaultChangelog = 'Publish for noop', +}: GenerateChangelogOptions): { + changelog: ChangeLog; + report: string; +} => { + // Create new changelog entry + const newEntry = generateVersionChangelog({ + version, + changes: commingChanges.flatMap(r => r.changes), + tag: tag || 'HEAD', + defaultChangelog, + }); + + const allEntries = ( + previousChangelog ? [newEntry, ...previousChangelog.entries] : [newEntry] + ).sort((a, b) => { + // Handle invalid version numbers + if (!semver.valid(a.version) || !semver.valid(b.version)) { + return 0; + } + // Use semver.rcompare for descending sort (newer versions first) + return semver.rcompare(a.version, b.version); + }); + + // Merge with existing changelog + const changelog: ChangeLog = { + name: packageName, + entries: allEntries, + }; + + // Convert to markdown + const markdown = changelogToMarkdown(changelog); + return { + changelog, + report: markdown, + }; +}; diff --git a/packages/rush-plugins/publish/src/index.ts b/packages/rush-plugins/publish/src/index.ts new file mode 100644 index 00000000..c34d1ef3 --- /dev/null +++ b/packages/rush-plugins/publish/src/index.ts @@ -0,0 +1,32 @@ +import path from 'path'; +import fs from 'fs'; + +import { Command } from 'commander'; + +import { installAction as releaseAction } from './action/release'; +import { installAction as publishAction } from './action/publish'; +import { installAction as generateChangeAction } from './action/change'; + +const main = () => { + const packageJson = JSON.parse( + fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8'), + ); + const program = new Command(); + + program + .name(packageJson.name) + .description(packageJson.description) + .version(packageJson.version) + .showSuggestionAfterError(true) + .showHelpAfterError(true); + + const actions = [generateChangeAction, releaseAction, publishAction]; + + actions.forEach(a => { + a(program); + }); + + program.parse(); +}; + +main(); diff --git a/packages/rush-plugins/publish/src/run.js b/packages/rush-plugins/publish/src/run.js new file mode 100755 index 00000000..56c23923 --- /dev/null +++ b/packages/rush-plugins/publish/src/run.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +process.env.SUCRASE_OPTIONS = '{"preserveDynamicImport":true}'; +require('sucrase/register/ts'); +const path = require('path'); + +require(path.resolve(__dirname, '../src/index.ts')); diff --git a/packages/rush-plugins/publish/src/types.ts b/packages/rush-plugins/publish/src/types.ts new file mode 100644 index 00000000..32f23587 --- /dev/null +++ b/packages/rush-plugins/publish/src/types.ts @@ -0,0 +1,3 @@ +import { type Command } from 'commander'; + +export type InstallAction = (program: Command) => void; diff --git a/packages/rush-plugins/publish/src/utils/exec.ts b/packages/rush-plugins/publish/src/utils/exec.ts new file mode 100644 index 00000000..9ed02e35 --- /dev/null +++ b/packages/rush-plugins/publish/src/utils/exec.ts @@ -0,0 +1,33 @@ +import { exec as _exec, type ExecOptions } from 'shelljs'; + +export interface ExecResult { + code: number; + stdout: string; + stderr: string; +} + +class ExecError extends Error { + code: number; + stderr: string; + stdout: string; + constructor(result: ExecResult) { + super(result.stderr || result.stdout); + this.code = result.code; + this.stderr = result.stderr; + this.stdout = result.stdout; + } +} + +export const exec = ( + cmd: string, + options: ExecOptions = { silent: true }, +): Promise => + new Promise((r, j) => { + _exec(cmd, options, (code, stdout, stderr) => { + if (code === 0) { + r({ code, stdout, stderr }); + } else { + j(new ExecError({ code, stderr, stdout })); + } + }); + }); diff --git a/packages/rush-plugins/publish/src/utils/get-rush-config.ts b/packages/rush-plugins/publish/src/utils/get-rush-config.ts new file mode 100644 index 00000000..fc65f846 --- /dev/null +++ b/packages/rush-plugins/publish/src/utils/get-rush-config.ts @@ -0,0 +1,13 @@ +import { RushConfiguration } from '@rushstack/rush-sdk'; + +export const getRushConfiguration = (() => { + let rushConfiguration: RushConfiguration | null = null; + return (useCache = true) => { + if (!useCache) { + rushConfiguration = null; + } + return (rushConfiguration ||= RushConfiguration.loadFromDefaultLocation({ + startingFolder: process.cwd(), + })); + }; +})(); diff --git a/packages/rush-plugins/publish/src/utils/git.ts b/packages/rush-plugins/publish/src/utils/git.ts new file mode 100644 index 00000000..0cb5e14b --- /dev/null +++ b/packages/rush-plugins/publish/src/utils/git.ts @@ -0,0 +1,74 @@ +import { exec } from './exec'; + +const serializeFilesName = (output: string): string[] => + output + .split('\n') + .map(line => { + if (line) { + const trimmedLine = line.trim(); + return trimmedLine; + } + return ''; + }) + .filter(line => line && line.length > 0); + +export const getChangedFilesFromCached = async (): Promise => { + const output = await exec('git diff --name-only --diff-filter=ACMR --cached'); + if (!output) { + return []; + } + return serializeFilesName(output.stdout); +}; + +/** + * 获取当前分支名称 + * @returns string + */ +export const getCurrentBranchName = async () => { + const { stdout } = await exec('git rev-parse --abbrev-ref HEAD'); + return stdout.trim(); +}; + +export const isMainBranch = async () => { + const currentBranchName = await getCurrentBranchName(); + return currentBranchName === 'main'; +}; + +export const getChangedFiles = async (): Promise => { + const output = await exec('git diff --name-only --diff-filter=ACMR'); + return serializeFilesName(output.stdout); +}; + +/** + * 确保没有未提交的变更 + */ +export const ensureNotUncommittedChanges = async () => { + const changedFiles = ( + await Promise.all([getChangedFilesFromCached(), getChangedFiles()]) + ).flat(); + if (changedFiles.length > 0) { + throw new Error( + 'There are uncommitted changes in the working tree, please commit them first.', + ); + } + return true; +}; + +/** + * 获取当前 Git 仓库设置的 origin 远程源地址 + * @param cwd 当前工作目录 + * @returns origin 远程源地址 + */ +export const getCurrentOrigin = async ( + cwd: string = process.cwd(), +): Promise => { + try { + const { stdout } = await exec('git remote get-url origin', { cwd }); + return stdout.trim(); + } catch (error) { + return undefined; + } +}; + +export const convertGitSchemaToHttp = (gitUrl: string) => + gitUrl.replace('git@', 'https://').replace(':', '/').replace('.git', ''); diff --git a/packages/rush-plugins/publish/src/utils/random.ts b/packages/rush-plugins/publish/src/utils/random.ts new file mode 100644 index 00000000..ddc796c3 --- /dev/null +++ b/packages/rush-plugins/publish/src/utils/random.ts @@ -0,0 +1,13 @@ +import crypto from 'crypto'; + +/** + * 生成指定长度的随机字符串(使用 crypto 模块) + * @param digit 字符串长度 + * @returns 随机字符串 + */ +export function randomHash(digit: number): string { + return crypto + .randomBytes(Math.ceil(digit / 2)) + .toString('hex') + .slice(0, digit); +} diff --git a/packages/rush-plugins/publish/src/utils/whoami.ts b/packages/rush-plugins/publish/src/utils/whoami.ts new file mode 100644 index 00000000..e6c65a18 --- /dev/null +++ b/packages/rush-plugins/publish/src/utils/whoami.ts @@ -0,0 +1,12 @@ +import { exec } from './exec'; + +export const whoAmI = async (): Promise<{ name: string; email: string }> => { + const [name, email] = await Promise.all([ + exec('git config user.name', { cwd: __dirname, silent: true }), + exec('git config user.email', { cwd: __dirname, silent: true }), + ]); + return { + name: name.stdout.toString().replace('\n', ''), + email: email.stdout.toString().replace('\n', ''), + }; +}; diff --git a/packages/rush-plugins/publish/tsconfig.build.json b/packages/rush-plugins/publish/tsconfig.build.json new file mode 100644 index 00000000..d291dc0b --- /dev/null +++ b/packages/rush-plugins/publish/tsconfig.build.json @@ -0,0 +1,17 @@ +{ + "extends": "@coze-arch/ts-config/tsconfig.node.json", + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src", + "tsBuildInfoFile": "lib/tsconfig.build.tsbuildinfo", + "module": "CommonJS", + "target": "ES2020", + "moduleResolution": "node", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"], + "exclude": ["node_modules", "lib"] +} diff --git a/packages/rush-plugins/publish/tsconfig.json b/packages/rush-plugins/publish/tsconfig.json new file mode 100644 index 00000000..b3951a30 --- /dev/null +++ b/packages/rush-plugins/publish/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "exclude": ["**/*"], + "compilerOptions": { + "composite": true + }, + "references": [ + { + "path": "./tsconfig.build.json" + }, + { + "path": "./tsconfig.misc.json" + } + ] +} diff --git a/packages/rush-plugins/publish/tsconfig.misc.json b/packages/rush-plugins/publish/tsconfig.misc.json new file mode 100644 index 00000000..69a68b09 --- /dev/null +++ b/packages/rush-plugins/publish/tsconfig.misc.json @@ -0,0 +1,21 @@ +{ + "extends": "@coze-arch/ts-config/tsconfig.node.json", + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "rootDir": "./", + "outDir": "./dist", + "module": "CommonJS", + "target": "ES2020", + "moduleResolution": "node", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["__tests__", "vitest.config.ts"], + "exclude": ["./dist"], + "references": [ + { + "path": "./tsconfig.build.json" + } + ] +} diff --git a/packages/rush-plugins/publish/vitest.config.ts b/packages/rush-plugins/publish/vitest.config.ts new file mode 100644 index 00000000..5c7b91ac --- /dev/null +++ b/packages/rush-plugins/publish/vitest.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from '@coze-arch/vitest-config'; + +export default defineConfig({ + dirname: __dirname, + preset: 'node', +}); diff --git a/packages/rush-plugins/run-tsc-plugin/.npmrc b/packages/rush-plugins/run-tsc-plugin/.npmrc new file mode 100644 index 00000000..fb7f2353 --- /dev/null +++ b/packages/rush-plugins/run-tsc-plugin/.npmrc @@ -0,0 +1 @@ +//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} diff --git a/packages/rush-plugins/run-tsc-plugin/README.md b/packages/rush-plugins/run-tsc-plugin/README.md index ecf62697..9597231c 100644 --- a/packages/rush-plugins/run-tsc-plugin/README.md +++ b/packages/rush-plugins/run-tsc-plugin/README.md @@ -1 +1 @@ -# @coze-infra/run-tsc-plugin \ No newline at end of file +# @coze-arch/run-tsc-plugin diff --git a/packages/rush-plugins/run-tsc-plugin/package.json b/packages/rush-plugins/run-tsc-plugin/package.json index 4bc7b8d4..01f5bd0d 100644 --- a/packages/rush-plugins/run-tsc-plugin/package.json +++ b/packages/rush-plugins/run-tsc-plugin/package.json @@ -1,11 +1,19 @@ { - "name": "@coze-infra/rush-run-tsc-plugin", - "version": "0.0.1", + "name": "@coze-arch/rush-run-tsc-plugin", + "version": "0.0.1-beta.1", "description": "rush plugin to run tsc ", "license": "ISC", "author": "tecvan.fe@gmail.com", "maintainers": [], + "main": "./command-line.json", + "files": [ + "./command-line.json", + "./rush-plugin-manifest.json" + ], "devDependencies": { - "@coze-infra/ts-config": "workspace:*" + "@coze-arch/ts-config": "workspace:*" + }, + "publishConfig": { + "access": "public" } -} +} \ No newline at end of file diff --git a/packages/rush-plugins/run-tsc-plugin/rush-plugin-manifest.json b/packages/rush-plugins/run-tsc-plugin/rush-plugin-manifest.json index c09f33f5..6af51fd3 100644 --- a/packages/rush-plugins/run-tsc-plugin/rush-plugin-manifest.json +++ b/packages/rush-plugins/run-tsc-plugin/rush-plugin-manifest.json @@ -2,7 +2,7 @@ "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", "plugins": [ { - "pluginName": "@coze-infra/rush-run-tsc-plugin", + "pluginName": "@coze-arch/rush-run-tsc-plugin", "description": "Rush plugin for run tsc", "commandLineJsonFilePath": "./command-line.json" } diff --git a/packages/utils/fs-enhance/README.md b/packages/utils/fs-enhance/README.md new file mode 100644 index 00000000..805833a1 --- /dev/null +++ b/packages/utils/fs-enhance/README.md @@ -0,0 +1 @@ +# @coze-arch/fs-enhance \ No newline at end of file diff --git a/packages/utils/fs-enhance/__tests__/.gitkeep b/packages/utils/fs-enhance/__tests__/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/utils/fs-enhance/config/rush-project.json b/packages/utils/fs-enhance/config/rush-project.json new file mode 100644 index 00000000..1c23b14b --- /dev/null +++ b/packages/utils/fs-enhance/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "test:cov", + "outputFolderNames": ["coverage"] + }, + { + "operationName": "ts-check", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/packages/utils/fs-enhance/eslint.config.js b/packages/utils/fs-enhance/eslint.config.js new file mode 100644 index 00000000..8a3bd050 --- /dev/null +++ b/packages/utils/fs-enhance/eslint.config.js @@ -0,0 +1,7 @@ +const { defineConfig } = require('@coze-arch/eslint-config'); + +module.exports = defineConfig({ + packageRoot: __dirname, + preset: 'node', + rules: {}, +}); diff --git a/packages/utils/fs-enhance/package.json b/packages/utils/fs-enhance/package.json new file mode 100644 index 00000000..bbbf2830 --- /dev/null +++ b/packages/utils/fs-enhance/package.json @@ -0,0 +1,25 @@ +{ + "name": "@coze-arch/fs-enhance", + "version": "0.0.1", + "description": "fs enhance ", + "license": "ISC", + "author": "tecvan.fe@gmail.com", + "maintainers": [], + "main": "src/index.ts", + "scripts": { + "build": "exit 0", + "lint": "eslint ./ --cache", + "test": "vitest --run --passWithNoTests", + "test:cov": "npm run test -- --coverage" + }, + "dependencies": {}, + "devDependencies": { + "@coze-arch/eslint-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", + "@coze-arch/vitest-config": "workspace:*", + "@types/node": "^22.13.13", + "@vitest/coverage-v8": "^3.0.9", + "tsx": "^4.19.3", + "vitest": "^3.0.9" + } +} diff --git a/packages/utils/fs-enhance/src/index.ts b/packages/utils/fs-enhance/src/index.ts new file mode 100644 index 00000000..80bbd222 --- /dev/null +++ b/packages/utils/fs-enhance/src/index.ts @@ -0,0 +1,22 @@ +import fs from 'fs/promises'; + +export const readJsonFile = async (path: string) => { + const content = await fs.readFile(path, 'utf8'); + return JSON.parse(content) as T; +}; + +export const writeJsonFile = async (path: string, data: unknown) => { + await fs.writeFile(path, JSON.stringify(data, null, 2), 'utf8'); +}; + +export const isFileExists = async (path: string) => + fs + .access(path) + .then(() => true) + .catch(() => false); + +export const isDirExists = async (path: string) => + fs + .access(path) + .then(() => true) + .catch(() => false); diff --git a/packages/utils/fs-enhance/tsconfig.build.json b/packages/utils/fs-enhance/tsconfig.build.json new file mode 100644 index 00000000..6086f3cd --- /dev/null +++ b/packages/utils/fs-enhance/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "@coze-arch/ts-config/tsconfig.node.json", + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "module": "CommonJS", + "target": "ES2020", + "moduleResolution": "node" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/utils/fs-enhance/tsconfig.json b/packages/utils/fs-enhance/tsconfig.json new file mode 100644 index 00000000..b3951a30 --- /dev/null +++ b/packages/utils/fs-enhance/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "exclude": ["**/*"], + "compilerOptions": { + "composite": true + }, + "references": [ + { + "path": "./tsconfig.build.json" + }, + { + "path": "./tsconfig.misc.json" + } + ] +} diff --git a/packages/utils/fs-enhance/tsconfig.misc.json b/packages/utils/fs-enhance/tsconfig.misc.json new file mode 100644 index 00000000..553ee729 --- /dev/null +++ b/packages/utils/fs-enhance/tsconfig.misc.json @@ -0,0 +1,18 @@ +{ + "extends": "@coze-arch/ts-config/tsconfig.node.json", + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "rootDir": "./", + "outDir": "./dist", + "module": "CommonJS", + "target": "ES2020", + "moduleResolution": "node" + }, + "include": ["__tests__", "vitest.config.ts"], + "exclude": ["./dist"], + "references": [ + { + "path": "./tsconfig.build.json" + } + ] +} diff --git a/packages/utils/fs-enhance/vitest.config.ts b/packages/utils/fs-enhance/vitest.config.ts new file mode 100644 index 00000000..5c7b91ac --- /dev/null +++ b/packages/utils/fs-enhance/vitest.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from '@coze-arch/vitest-config'; + +export default defineConfig({ + dirname: __dirname, + preset: 'node', +}); diff --git a/packages/utils/logger/README.md b/packages/utils/logger/README.md new file mode 100644 index 00000000..a3cfc0cd --- /dev/null +++ b/packages/utils/logger/README.md @@ -0,0 +1 @@ +# @coze-arch/logger \ No newline at end of file diff --git a/packages/utils/logger/__tests__/.gitkeep b/packages/utils/logger/__tests__/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/utils/logger/config/rush-project.json b/packages/utils/logger/config/rush-project.json new file mode 100644 index 00000000..1c23b14b --- /dev/null +++ b/packages/utils/logger/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "test:cov", + "outputFolderNames": ["coverage"] + }, + { + "operationName": "ts-check", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/packages/utils/logger/eslint.config.js b/packages/utils/logger/eslint.config.js new file mode 100644 index 00000000..8a3bd050 --- /dev/null +++ b/packages/utils/logger/eslint.config.js @@ -0,0 +1,7 @@ +const { defineConfig } = require('@coze-arch/eslint-config'); + +module.exports = defineConfig({ + packageRoot: __dirname, + preset: 'node', + rules: {}, +}); diff --git a/packages/utils/logger/package.json b/packages/utils/logger/package.json new file mode 100644 index 00000000..046bc57b --- /dev/null +++ b/packages/utils/logger/package.json @@ -0,0 +1,25 @@ +{ + "name": "@coze-arch/logger", + "version": "0.0.1", + "description": "logger ", + "license": "ISC", + "author": "tecvan.fe@gmail.com", + "maintainers": [], + "main": "src/index.ts", + "scripts": { + "build": "exit 0", + "lint": "eslint ./ --cache", + "test": "vitest --run --passWithNoTests", + "test:cov": "npm run test -- --coverage" + }, + "dependencies": {}, + "devDependencies": { + "@coze-arch/eslint-config": "workspace:*", + "@coze-arch/ts-config": "workspace:*", + "@coze-arch/vitest-config": "workspace:*", + "@types/node": "^22.13.13", + "@vitest/coverage-v8": "^3.0.9", + "tsx": "^4.19.3", + "vitest": "^3.0.9" + } +} diff --git a/packages/utils/logger/src/index.ts b/packages/utils/logger/src/index.ts new file mode 100644 index 00000000..710986fc --- /dev/null +++ b/packages/utils/logger/src/index.ts @@ -0,0 +1,144 @@ +/** + * 日志工具 + * + * 提供统一的日志输出接口,支持不同级别的日志,包括彩色输出 + */ + +// 日志级别枚举 +export enum LogLevel { + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, + SUCCESS = 4, + NONE = 5, // 用于完全禁用日志 +} + +// ANSI 颜色代码 +const colors = { + reset: '\x1b[0m', + debug: '\x1b[36m', // 青色 + info: '\x1b[34m', // 蓝色 + warn: '\x1b[33m', // 黄色 + error: '\x1b[31m', // 红色 + success: '\x1b[32m', // 绿色 +}; + +// 默认日志级别 +let currentLogLevel = LogLevel.INFO; + +/** + * 设置全局日志级别 + * @param level 日志级别 + */ +export function setLogLevel(level: LogLevel): void { + currentLogLevel = level; +} + +/** + * 获取当前日志级别 + * @returns 当前日志级别 + */ +export function getLogLevel(): LogLevel { + return currentLogLevel; +} + +/** + * 基础日志函数 + * @param level 日志级别 + * @param message 日志消息 + * @param showPrefix 是否显示前缀,默认为 true + */ +function log(level: LogLevel, message: string, showPrefix = true): void { + if (level < currentLogLevel) { + return; + } + + let prefix = ''; + let color = ''; + + switch (level) { + case LogLevel.DEBUG: { + prefix = '[DEBUG]'; + color = colors.debug; + break; + } + case LogLevel.WARN: { + prefix = '[WARN]'; + color = colors.warn; + break; + } + case LogLevel.ERROR: { + prefix = '[ERROR]'; + color = colors.error; + break; + } + case LogLevel.SUCCESS: { + prefix = '[SUCCESS]'; + color = colors.success; + break; + } + case LogLevel.INFO: + default: { + prefix = '[INFO]'; + color = colors.info; + break; + } + } + + // 格式化日志前缀 + const formattedPrefix = showPrefix ? `${color}${prefix}${colors.reset} ` : ''; + + // 输出日志 + console.log(`${formattedPrefix}${message}`); +} + +/** + * 导出的日志工具 + */ +export const logger = { + /** + * 调试日志 + * @param message 日志消息 + * @param showPrefix 是否显示前缀,默认为 true + */ + debug(message: string, showPrefix = true): void { + log(LogLevel.DEBUG, message, showPrefix); + }, + + /** + * 信息日志 + * @param message 日志消息 + * @param showPrefix 是否显示前缀,默认为 true + */ + info(message: string, showPrefix = true): void { + log(LogLevel.INFO, message, showPrefix); + }, + + /** + * 警告日志 + * @param message 日志消息 + * @param showPrefix 是否显示前缀,默认为 true + */ + warn(message: string, showPrefix = true): void { + log(LogLevel.WARN, message, showPrefix); + }, + + /** + * 错误日志 + * @param message 日志消息 + * @param showPrefix 是否显示前缀,默认为 true + */ + error(message: string, showPrefix = true): void { + log(LogLevel.ERROR, message, showPrefix); + }, + + /** + * 成功日志 + * @param message 日志消息 + * @param showPrefix 是否显示前缀,默认为 true + */ + success(message: string, showPrefix = true): void { + log(LogLevel.SUCCESS, message, showPrefix); + }, +}; diff --git a/packages/utils/logger/tsconfig.build.json b/packages/utils/logger/tsconfig.build.json new file mode 100644 index 00000000..6086f3cd --- /dev/null +++ b/packages/utils/logger/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "@coze-arch/ts-config/tsconfig.node.json", + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "module": "CommonJS", + "target": "ES2020", + "moduleResolution": "node" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/utils/logger/tsconfig.json b/packages/utils/logger/tsconfig.json new file mode 100644 index 00000000..b3951a30 --- /dev/null +++ b/packages/utils/logger/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "exclude": ["**/*"], + "compilerOptions": { + "composite": true + }, + "references": [ + { + "path": "./tsconfig.build.json" + }, + { + "path": "./tsconfig.misc.json" + } + ] +} diff --git a/packages/utils/logger/tsconfig.misc.json b/packages/utils/logger/tsconfig.misc.json new file mode 100644 index 00000000..553ee729 --- /dev/null +++ b/packages/utils/logger/tsconfig.misc.json @@ -0,0 +1,18 @@ +{ + "extends": "@coze-arch/ts-config/tsconfig.node.json", + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "rootDir": "./", + "outDir": "./dist", + "module": "CommonJS", + "target": "ES2020", + "moduleResolution": "node" + }, + "include": ["__tests__", "vitest.config.ts"], + "exclude": ["./dist"], + "references": [ + { + "path": "./tsconfig.build.json" + } + ] +} diff --git a/packages/utils/logger/vitest.config.ts b/packages/utils/logger/vitest.config.ts new file mode 100644 index 00000000..5c7b91ac --- /dev/null +++ b/packages/utils/logger/vitest.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from '@coze-arch/vitest-config'; + +export default defineConfig({ + dirname: __dirname, + preset: 'node', +}); diff --git a/rush.json b/rush.json index 88ea1265..76013105 100644 --- a/rush.json +++ b/rush.json @@ -435,28 +435,46 @@ // "tags": [ "tools" ] // } { - "packageName": "@coze-infra/eslint-config", + "packageName": "@coze-arch/eslint-config", "projectFolder": "config/eslint-config", "tags": ["config"] }, { - "packageName": "@coze-infra/ts-config", + "packageName": "@coze-arch/ts-config", "projectFolder": "config/ts-config", "tags": ["config"] }, { - "packageName": "@coze-infra/vitest-config", + "packageName": "@coze-arch/vitest-config", "projectFolder": "config/vitest-config", "tags": ["config"] }, { - "packageName": "@coze-infra/npm-mcp-server", "projectFolder": "packages/mcp/npm", + "packageName": "@coze-arch/npm-mcp-server", + "shouldPublish": true, "tags": ["mcp"] }, { - "packageName": "@coze-infra/rush-run-tsc-plugin", + "packageName": "@coze-arch/rush-run-tsc-plugin", "projectFolder": "packages/rush-plugins/run-tsc-plugin", + "shouldPublish": true, + "tags": ["level-3"] + }, + { + "packageName": "@coze-arch/rush-publish-plugin", + "projectFolder": "packages/rush-plugins/publish", + "shouldPublish": true, + "tags": ["level-3"] + }, + { + "packageName": "@coze-arch/logger", + "projectFolder": "packages/utils/logger", + "tags": ["level-3"] + }, + { + "packageName": "@coze-arch/fs-enhance", + "projectFolder": "packages/utils/fs-enhance", "tags": ["level-3"] } ]