diff --git a/.github/workflows/pr-merged-tag.yml b/.github/workflows/pr-merged-tag.yml index b0df0602..b062d74a 100644 --- a/.github/workflows/pr-merged-tag.yml +++ b/.github/workflows/pr-merged-tag.yml @@ -120,6 +120,7 @@ jobs: - name: Run Release if: steps.check_version.outputs.has_changes == 'true' run: | + npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_AUTH_TOKEN }} git tag --points-at ${{ github.event.head_commit.id }} node common/scripts/install-run-rush.js release --commit ${{ github.event.head_commit.id }} env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8093119..bc4d6b8c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,6 +53,8 @@ jobs: node common/scripts/install-run-rush.js install - name: Run Release - run: node common/scripts/install-run-rush.js release --commit ${{ github.event.head_commit.id }} + run: | + npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_AUTH_TOKEN }} + 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/.gitignore b/.gitignore index 9bcea528..be12c727 100644 --- a/.gitignore +++ b/.gitignore @@ -176,3 +176,5 @@ sensitive_info_result.txt .rollup.cache lib + +log diff --git a/common/changes/@coze-arch/rush-publish-plugin/feat-support-registry-config_2026-01-07-12-54.json b/common/changes/@coze-arch/rush-publish-plugin/feat-support-registry-config_2026-01-07-12-54.json new file mode 100644 index 00000000..7c626815 --- /dev/null +++ b/common/changes/@coze-arch/rush-publish-plugin/feat-support-registry-config_2026-01-07-12-54.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@coze-arch/rush-publish-plugin", + "comment": "support registry priority configuration", + "type": "minor" + } + ], + "packageName": "@coze-arch/rush-publish-plugin", + "email": "tecvan.fe@qq.com" +} diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/action.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/action.test.ts index 09d8d98a..3771cb86 100644 --- a/packages/rush-plugins/publish/__tests__/actions/publish/action.test.ts +++ b/packages/rush-plugins/publish/__tests__/actions/publish/action.test.ts @@ -127,7 +127,7 @@ describe('publish action', () => { false, { isReleaseMode: false, - registry: 'https://registry.npmjs.org', + registry: undefined, }, ); expect(applyPublishManifest).toHaveBeenCalledWith(mockPublishManifests); @@ -326,7 +326,7 @@ describe('publish action', () => { }); expect(confirmForPublish).toHaveBeenCalledWith(mockPublishManifests, true, { isReleaseMode: false, - registry: 'https://registry.npmjs.org', + registry: undefined, }); }); @@ -416,7 +416,7 @@ describe('publish action', () => { expect(pushToRemote).toHaveBeenCalled(); }); - it('should use default registry when registry is not provided', async () => { + it('should use undefined registry when registry is not provided (use npm config)', async () => { vi.mocked(generatePublishManifest).mockResolvedValue({ manifests: mockPublishManifests, bumpPolicy: BumpType.BETA, @@ -430,7 +430,7 @@ describe('publish action', () => { expect(release).toHaveBeenCalledWith({ dryRun: false, - registry: 'https://registry.npmjs.org', + registry: undefined, packages: expect.any(Array), }); }); @@ -473,7 +473,7 @@ describe('publish action', () => { expect(release).toHaveBeenCalledWith({ dryRun: true, - registry: 'https://registry.npmjs.org', + registry: undefined, packages: expect.any(Array), }); }); diff --git a/packages/rush-plugins/publish/__tests__/actions/publish/confirm.test.ts b/packages/rush-plugins/publish/__tests__/actions/publish/confirm.test.ts index fda46681..b506365f 100644 --- a/packages/rush-plugins/publish/__tests__/actions/publish/confirm.test.ts +++ b/packages/rush-plugins/publish/__tests__/actions/publish/confirm.test.ts @@ -192,13 +192,13 @@ describe('confirm', () => { }); }); - it('should show default registry when registry is not provided in release mode', async () => { + it('should show npm configured registry when registry is not provided in release mode', async () => { await confirmForPublish(mockPublishManifests, false, { isReleaseMode: true, }); expect(logger.warn).toHaveBeenCalledWith( - expect.stringContaining('default registry'), + expect.stringContaining('npm configured registry'), false, ); }); diff --git a/packages/rush-plugins/publish/__tests__/utils/resolve-registry.test.ts b/packages/rush-plugins/publish/__tests__/utils/resolve-registry.test.ts new file mode 100644 index 00000000..7a42305b --- /dev/null +++ b/packages/rush-plugins/publish/__tests__/utils/resolve-registry.test.ts @@ -0,0 +1,95 @@ +// Copyright (c) 2025 coze-dev +// SPDX-License-Identifier: MIT + +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import type { RushConfigurationProject } from '@rushstack/rush-sdk'; + +import { resolveRegistry } from '../../src/utils/resolve-registry'; + +// Mock logger +vi.mock('@coze-arch/logger', () => ({ + logger: { + info: vi.fn(), + }, +})); + +describe('resolveRegistry', () => { + let mockProject: RushConfigurationProject; + + beforeEach(() => { + // 创建一个基础的 mock project + const baseMockProject: Partial = { + packageName: '@test/package', + packageJson: {}, + }; + mockProject = baseMockProject as RushConfigurationProject; + }); + + it('should use CLI registry with highest priority', () => { + mockProject.packageJson = { + publishConfig: { registry: 'https://package.json.registry.com' }, + }; + + const result = resolveRegistry(mockProject, 'https://cli.registry.com'); + + expect(result).toBe('https://cli.registry.com'); + }); + + it('should use package.json publishConfig.registry when CLI is not provided', () => { + mockProject.packageJson = { + publishConfig: { registry: 'https://package.json.registry.com' }, + }; + + const result = resolveRegistry(mockProject); + + expect(result).toBe('https://package.json.registry.com'); + }); + + it('should return undefined when no config is provided (use npm config)', () => { + mockProject.packageJson = {}; + + const result = resolveRegistry(mockProject); + + expect(result).toBeUndefined(); + }); + + it('should handle package.json without publishConfig', () => { + mockProject.packageJson = { + name: '@test/package', + version: '1.0.0', + }; + + const result = resolveRegistry(mockProject); + + expect(result).toBeUndefined(); + }); + + it('should handle empty publishConfig in package.json', () => { + mockProject.packageJson = { + publishConfig: {}, + }; + + const result = resolveRegistry(mockProject); + + expect(result).toBeUndefined(); + }); + + it('should handle undefined CLI parameter explicitly', () => { + mockProject.packageJson = {}; + + const result = resolveRegistry(mockProject, undefined); + + expect(result).toBeUndefined(); + }); + + it('should prioritize CLI over package.json even with empty string', () => { + mockProject.packageJson = { + publishConfig: { registry: 'https://package.json.registry.com' }, + }; + + const result = resolveRegistry(mockProject, ''); + + // Empty string is falsy, so should fall back to package.json + expect(result).toBe('https://package.json.registry.com'); + }); +}); diff --git a/packages/rush-plugins/publish/src/action/publish/action.ts b/packages/rush-plugins/publish/src/action/publish/action.ts index db5ccc34..4a7833e7 100644 --- a/packages/rush-plugins/publish/src/action/publish/action.ts +++ b/packages/rush-plugins/publish/src/action/publish/action.ts @@ -7,7 +7,6 @@ import { release } from '../release/action'; import { randomHash } from '../../utils/random'; import { ensureNotUncommittedChanges, isMainBranch } from '../../utils/git'; import { getRushConfiguration } from '../../utils/get-rush-config'; -import { DEFAULT_NPM_REGISTRY } from '../../const'; import { generatePublishManifest } from './version'; import { BumpType, type PublishOptions } from './types'; import { pushToRemote } from './push-to-remote'; @@ -21,8 +20,10 @@ import { applyPublishManifest } from './apply-new-version'; // 2. beta: 本分支直接切换版本号,并发布 // 3. 正式版本:发起MR,MR 合入 main 后,触发发布 +const SESSION_ID_LENGTH = 6; + export const publish = async (options: PublishOptions) => { - const sessionId = randomHash(6); + const sessionId = randomHash(SESSION_ID_LENGTH); const rushConfiguration = getRushConfiguration(); const rushFolder = rushConfiguration.rushJsonFolder; if ( @@ -70,7 +71,7 @@ export const publish = async (options: PublishOptions) => { !!options.dryRun, { isReleaseMode: !!options.release, - registry: options.registry || DEFAULT_NPM_REGISTRY, + registry: options.registry, }, ); @@ -106,13 +107,16 @@ export const publish = async (options: PublishOptions) => { // Release 模式:直接发布 logger.info('Running in direct release mode...'); logger.info('Starting package release...'); - const registry = options.registry || DEFAULT_NPM_REGISTRY; // 将 PublishManifest[] 转换为 PackageToPublish[] const packages = publishManifests.map(manifest => ({ packageName: manifest.project.packageName, version: manifest.newVersion, })); - await release({ dryRun: !!options.dryRun, registry, packages }); + await release({ + dryRun: !!options.dryRun, + registry: options.registry, + packages, + }); } else { // 普通模式:创建并推送发布分支 await pushToRemote({ diff --git a/packages/rush-plugins/publish/src/action/publish/confirm.ts b/packages/rush-plugins/publish/src/action/publish/confirm.ts index abb44b30..c320a1ca 100644 --- a/packages/rush-plugins/publish/src/action/publish/confirm.ts +++ b/packages/rush-plugins/publish/src/action/publish/confirm.ts @@ -28,7 +28,7 @@ export const confirmForPublish = async ( if (options?.isReleaseMode) { logger.info('', false); logger.warn(chalk.yellow.bold('⚠️ Release Mode Enabled:'), false); - const registryMsg = ` Packages will be published directly to: ${chalk.bold(options.registry || 'default registry')}`; + const registryMsg = ` Packages will be published directly to: ${chalk.bold(options.registry || 'npm configured registry')}`; logger.warn(chalk.yellow(registryMsg), false); } diff --git a/packages/rush-plugins/publish/src/action/publish/index.ts b/packages/rush-plugins/publish/src/action/publish/index.ts index 06b46f42..fadcb7a6 100644 --- a/packages/rush-plugins/publish/src/action/publish/index.ts +++ b/packages/rush-plugins/publish/src/action/publish/index.ts @@ -6,7 +6,7 @@ import { logger } from '@coze-arch/logger'; import { getCurrentOrigin } from '../../utils/git'; import { type InstallAction } from '../../types'; -import { DEFAULT_BRANCH_PREFIX, DEFAULT_NPM_REGISTRY } from '../../const'; +import { DEFAULT_BRANCH_PREFIX } from '../../const'; import { type PublishOptions } from './types'; import { GIT_REPO_URL_REGEX } from './const'; import { publish } from './action'; @@ -50,8 +50,7 @@ export const installAction: InstallAction = (program: Command) => { ) .option( '--registry ', - `NPM registry URL (default: ${DEFAULT_NPM_REGISTRY})`, - DEFAULT_NPM_REGISTRY, + 'NPM registry URL (优先级: CLI参数 > package.json publishConfig.registry > npm config)', ) .action(async (options: PublishOptions) => { try { diff --git a/packages/rush-plugins/publish/src/action/publish/types.ts b/packages/rush-plugins/publish/src/action/publish/types.ts index c7061e69..0f80b2ad 100644 --- a/packages/rush-plugins/publish/src/action/publish/types.ts +++ b/packages/rush-plugins/publish/src/action/publish/types.ts @@ -23,7 +23,7 @@ export interface PublishOptions { repoUrl: string; // Git 仓库 URL,例如:git@github.com:coze-dev/coze-js.git branchPrefix?: string; // Git 分支名前缀,默认见 DEFAULT_BRANCH_PREFIX release?: boolean; // 是否直接发布(仅支持 alpha/beta 版本) - registry?: string; // NPM registry 地址,默认见 DEFAULT_NPM_REGISTRY + registry?: string; // NPM registry 地址,优先级:CLI > package.json publishConfig > npm config } export interface PublishManifest { diff --git a/packages/rush-plugins/publish/src/action/release/index.ts b/packages/rush-plugins/publish/src/action/release/index.ts index 6198fd99..8940b2a4 100644 --- a/packages/rush-plugins/publish/src/action/release/index.ts +++ b/packages/rush-plugins/publish/src/action/release/index.ts @@ -5,7 +5,7 @@ import { type Command } from 'commander'; import { logger } from '@coze-arch/logger'; import { type InstallAction } from '../../types'; -import { DEFAULT_NPM_REGISTRY, DEFAULT_ALLOW_BRANCHES } from '../../const'; +import { DEFAULT_ALLOW_BRANCHES } from '../../const'; import { type ReleaseOptions } from './types'; import { release } from './action'; @@ -17,8 +17,7 @@ export const installAction: InstallAction = (program: Command) => { .option('--dry-run', '是否只执行不真实发布', false) .option( '-r, --registry ', - `发布到的 registry (默认: ${DEFAULT_NPM_REGISTRY})`, - DEFAULT_NPM_REGISTRY, + '发布到的 registry (优先级: CLI参数 > package.json publishConfig.registry > npm config)', ) .option( '--allow-branches ', diff --git a/packages/rush-plugins/publish/src/action/release/release.ts b/packages/rush-plugins/publish/src/action/release/release.ts index c34f79db..b5c2a3c0 100644 --- a/packages/rush-plugins/publish/src/action/release/release.ts +++ b/packages/rush-plugins/publish/src/action/release/release.ts @@ -4,8 +4,9 @@ import { type RushConfigurationProject } from '@rushstack/rush-sdk'; import { logger } from '@coze-arch/logger'; +import { resolveRegistry } from '../../utils/resolve-registry'; import { exec } from '../../utils/exec'; -import { type ReleaseOptions, type ReleaseManifest } from './types'; +import { type ReleaseManifest, type ReleaseOptions } from './types'; import { applyPublishConfig } from './package'; /** @@ -15,7 +16,7 @@ const publishPackage = async ( project: RushConfigurationProject, releaseOptions: ReleaseOptions, ): Promise => { - const { dryRun, registry } = releaseOptions; + const { dryRun } = releaseOptions; const token = process.env.NPM_AUTH_TOKEN; const { version } = project.packageJson; const tag = version.includes('alpha') @@ -23,15 +24,16 @@ const publishPackage = async ( : version.includes('beta') ? 'beta' : 'latest'; - const setToken = `npm config set //bnpm.byted.org/:_authToken ${token}`; - await exec(setToken, { - cwd: project.projectFolder, - }); + // 解析 registry:CLI 参数 > package.json publishConfig > npm 配置 + const registry = resolveRegistry(project, releaseOptions.registry); + + // 使用环境变量传递 authToken,npm 会自动应用到任何 registry const args = [`NODE_AUTH_TOKEN=${token}`, 'npm', 'publish', `--tag ${tag}`]; if (dryRun) { args.push('--dry-run'); } + // 只有明确指定了 registry 才传递参数,否则使用 npm 配置的默认值 if (registry) { args.push(`--registry=${registry}`); } diff --git a/packages/rush-plugins/publish/src/action/release/types.ts b/packages/rush-plugins/publish/src/action/release/types.ts index 0f648ae6..cbbb7ea9 100644 --- a/packages/rush-plugins/publish/src/action/release/types.ts +++ b/packages/rush-plugins/publish/src/action/release/types.ts @@ -6,7 +6,7 @@ import { type RushConfigurationProject } from '@rushstack/rush-sdk'; export interface ReleaseOptions { commit?: string; // 可选,为空时使用当前 HEAD dryRun?: boolean; - registry: string; + registry?: string; // 可选,优先级:CLI > package.json publishConfig > npm config packages?: PackageToPublish[]; // 可选,直接传入需要发布的包列表 allowBranches?: string[]; // 可选,允许发布正式版本的分支列表 } diff --git a/packages/rush-plugins/publish/src/const/index.ts b/packages/rush-plugins/publish/src/const/index.ts index 14aa20fc..8496498b 100644 --- a/packages/rush-plugins/publish/src/const/index.ts +++ b/packages/rush-plugins/publish/src/const/index.ts @@ -1,11 +1,6 @@ // Copyright (c) 2025 coze-dev // SPDX-License-Identifier: MIT -/** - * 默认的 NPM registry 地址 - */ -export const DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org'; - /** * 默认的 Git 分支名前缀 */ diff --git a/packages/rush-plugins/publish/src/utils/resolve-registry.ts b/packages/rush-plugins/publish/src/utils/resolve-registry.ts new file mode 100644 index 00000000..b4b6b1fe --- /dev/null +++ b/packages/rush-plugins/publish/src/utils/resolve-registry.ts @@ -0,0 +1,56 @@ +// Copyright (c) 2025 coze-dev +// SPDX-License-Identifier: MIT + +import { type RushConfigurationProject } from '@rushstack/rush-sdk'; +import { logger } from '@coze-arch/logger'; + +/** + * package.json 的类型定义 + */ +interface PackageJson { + publishConfig?: { + registry?: string; + [key: string]: unknown; + }; + [key: string]: unknown; +} + +/** + * 解析发布 registry 的优先级: + * 1. 命令行参数 --registry (最高优先级) + * 2. package.json 中的 publishConfig.registry + * 3. 使用 npm 配置的默认 registry (不传 --registry 参数) + * + * @param project - Rush 项目配置 + * @param registryFromCli - 命令行参数传入的 registry + * @returns 解析后的 registry URL,如果为 undefined 则使用 npm 默认配置 + */ +export function resolveRegistry( + project: RushConfigurationProject, + registryFromCli?: string, +): string | undefined { + // 1. 优先使用命令行参数 + if (registryFromCli) { + logger.info( + `Using registry from CLI: ${registryFromCli} (${project.packageName})`, + false, + ); + return registryFromCli; + } + + // 2. 尝试读取 package.json 中的 publishConfig.registry + const packageJson = project.packageJson as unknown as PackageJson; + const registryFromPackageJson = packageJson.publishConfig?.registry; + + if (registryFromPackageJson) { + logger.info( + `Using registry from publishConfig: ${registryFromPackageJson} (${project.packageName})`, + false, + ); + return registryFromPackageJson; + } + + // 3. 返回 undefined,让 npm 使用自己配置的 registry + logger.info(`Using npm configured registry (${project.packageName})`, false); + return undefined; +}