Skip to content

Commit 7ad925f

Browse files
Tecvan-feTecvan-feclaude
authored
feat(publish): support registry priority configuration (#132)
- Remove hardcoded DEFAULT_NPM_REGISTRY constant - Add resolveRegistry utility to handle registry resolution with priority: CLI parameter > package.json publishConfig.registry > npm config - Update GitHub Actions workflows to configure npm auth tokens - Add comprehensive unit tests for registry resolution - Update related type definitions to make registry optional - Fix eslint errors: extract magic number constant and fix type assertion 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Tecvan-fe <tecvan.fe@qq.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 3c66122 commit 7ad925f

16 files changed

Lines changed: 199 additions & 33 deletions

File tree

.github/workflows/pr-merged-tag.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ jobs:
120120
- name: Run Release
121121
if: steps.check_version.outputs.has_changes == 'true'
122122
run: |
123+
npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_AUTH_TOKEN }}
123124
git tag --points-at ${{ github.event.head_commit.id }}
124125
node common/scripts/install-run-rush.js release --commit ${{ github.event.head_commit.id }}
125126
env:

.github/workflows/release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ jobs:
5353
node common/scripts/install-run-rush.js install
5454
5555
- name: Run Release
56-
run: node common/scripts/install-run-rush.js release --commit ${{ github.event.head_commit.id }}
56+
run: |
57+
npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_AUTH_TOKEN }}
58+
node common/scripts/install-run-rush.js release --commit ${{ github.event.head_commit.id }}
5759
env:
5860
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,5 @@ sensitive_info_result.txt
176176

177177
.rollup.cache
178178
lib
179+
180+
log
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@coze-arch/rush-publish-plugin",
5+
"comment": "support registry priority configuration",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@coze-arch/rush-publish-plugin",
10+
"email": "tecvan.fe@qq.com"
11+
}

packages/rush-plugins/publish/__tests__/actions/publish/action.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ describe('publish action', () => {
127127
false,
128128
{
129129
isReleaseMode: false,
130-
registry: 'https://registry.npmjs.org',
130+
registry: undefined,
131131
},
132132
);
133133
expect(applyPublishManifest).toHaveBeenCalledWith(mockPublishManifests);
@@ -326,7 +326,7 @@ describe('publish action', () => {
326326
});
327327
expect(confirmForPublish).toHaveBeenCalledWith(mockPublishManifests, true, {
328328
isReleaseMode: false,
329-
registry: 'https://registry.npmjs.org',
329+
registry: undefined,
330330
});
331331
});
332332

@@ -416,7 +416,7 @@ describe('publish action', () => {
416416
expect(pushToRemote).toHaveBeenCalled();
417417
});
418418

419-
it('should use default registry when registry is not provided', async () => {
419+
it('should use undefined registry when registry is not provided (use npm config)', async () => {
420420
vi.mocked(generatePublishManifest).mockResolvedValue({
421421
manifests: mockPublishManifests,
422422
bumpPolicy: BumpType.BETA,
@@ -430,7 +430,7 @@ describe('publish action', () => {
430430

431431
expect(release).toHaveBeenCalledWith({
432432
dryRun: false,
433-
registry: 'https://registry.npmjs.org',
433+
registry: undefined,
434434
packages: expect.any(Array),
435435
});
436436
});
@@ -473,7 +473,7 @@ describe('publish action', () => {
473473

474474
expect(release).toHaveBeenCalledWith({
475475
dryRun: true,
476-
registry: 'https://registry.npmjs.org',
476+
registry: undefined,
477477
packages: expect.any(Array),
478478
});
479479
});

packages/rush-plugins/publish/__tests__/actions/publish/confirm.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,13 @@ describe('confirm', () => {
192192
});
193193
});
194194

195-
it('should show default registry when registry is not provided in release mode', async () => {
195+
it('should show npm configured registry when registry is not provided in release mode', async () => {
196196
await confirmForPublish(mockPublishManifests, false, {
197197
isReleaseMode: true,
198198
});
199199

200200
expect(logger.warn).toHaveBeenCalledWith(
201-
expect.stringContaining('default registry'),
201+
expect.stringContaining('npm configured registry'),
202202
false,
203203
);
204204
});
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) 2025 coze-dev
2+
// SPDX-License-Identifier: MIT
3+
4+
import { describe, it, expect, vi, beforeEach } from 'vitest';
5+
import type { RushConfigurationProject } from '@rushstack/rush-sdk';
6+
7+
import { resolveRegistry } from '../../src/utils/resolve-registry';
8+
9+
// Mock logger
10+
vi.mock('@coze-arch/logger', () => ({
11+
logger: {
12+
info: vi.fn(),
13+
},
14+
}));
15+
16+
describe('resolveRegistry', () => {
17+
let mockProject: RushConfigurationProject;
18+
19+
beforeEach(() => {
20+
// 创建一个基础的 mock project
21+
const baseMockProject: Partial<RushConfigurationProject> = {
22+
packageName: '@test/package',
23+
packageJson: {},
24+
};
25+
mockProject = baseMockProject as RushConfigurationProject;
26+
});
27+
28+
it('should use CLI registry with highest priority', () => {
29+
mockProject.packageJson = {
30+
publishConfig: { registry: 'https://package.json.registry.com' },
31+
};
32+
33+
const result = resolveRegistry(mockProject, 'https://cli.registry.com');
34+
35+
expect(result).toBe('https://cli.registry.com');
36+
});
37+
38+
it('should use package.json publishConfig.registry when CLI is not provided', () => {
39+
mockProject.packageJson = {
40+
publishConfig: { registry: 'https://package.json.registry.com' },
41+
};
42+
43+
const result = resolveRegistry(mockProject);
44+
45+
expect(result).toBe('https://package.json.registry.com');
46+
});
47+
48+
it('should return undefined when no config is provided (use npm config)', () => {
49+
mockProject.packageJson = {};
50+
51+
const result = resolveRegistry(mockProject);
52+
53+
expect(result).toBeUndefined();
54+
});
55+
56+
it('should handle package.json without publishConfig', () => {
57+
mockProject.packageJson = {
58+
name: '@test/package',
59+
version: '1.0.0',
60+
};
61+
62+
const result = resolveRegistry(mockProject);
63+
64+
expect(result).toBeUndefined();
65+
});
66+
67+
it('should handle empty publishConfig in package.json', () => {
68+
mockProject.packageJson = {
69+
publishConfig: {},
70+
};
71+
72+
const result = resolveRegistry(mockProject);
73+
74+
expect(result).toBeUndefined();
75+
});
76+
77+
it('should handle undefined CLI parameter explicitly', () => {
78+
mockProject.packageJson = {};
79+
80+
const result = resolveRegistry(mockProject, undefined);
81+
82+
expect(result).toBeUndefined();
83+
});
84+
85+
it('should prioritize CLI over package.json even with empty string', () => {
86+
mockProject.packageJson = {
87+
publishConfig: { registry: 'https://package.json.registry.com' },
88+
};
89+
90+
const result = resolveRegistry(mockProject, '');
91+
92+
// Empty string is falsy, so should fall back to package.json
93+
expect(result).toBe('https://package.json.registry.com');
94+
});
95+
});

packages/rush-plugins/publish/src/action/publish/action.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { release } from '../release/action';
77
import { randomHash } from '../../utils/random';
88
import { ensureNotUncommittedChanges, isMainBranch } from '../../utils/git';
99
import { getRushConfiguration } from '../../utils/get-rush-config';
10-
import { DEFAULT_NPM_REGISTRY } from '../../const';
1110
import { generatePublishManifest } from './version';
1211
import { BumpType, type PublishOptions } from './types';
1312
import { pushToRemote } from './push-to-remote';
@@ -21,8 +20,10 @@ import { applyPublishManifest } from './apply-new-version';
2120
// 2. beta: 本分支直接切换版本号,并发布
2221
// 3. 正式版本:发起MR,MR 合入 main 后,触发发布
2322

23+
const SESSION_ID_LENGTH = 6;
24+
2425
export const publish = async (options: PublishOptions) => {
25-
const sessionId = randomHash(6);
26+
const sessionId = randomHash(SESSION_ID_LENGTH);
2627
const rushConfiguration = getRushConfiguration();
2728
const rushFolder = rushConfiguration.rushJsonFolder;
2829
if (
@@ -70,7 +71,7 @@ export const publish = async (options: PublishOptions) => {
7071
!!options.dryRun,
7172
{
7273
isReleaseMode: !!options.release,
73-
registry: options.registry || DEFAULT_NPM_REGISTRY,
74+
registry: options.registry,
7475
},
7576
);
7677

@@ -106,13 +107,16 @@ export const publish = async (options: PublishOptions) => {
106107
// Release 模式:直接发布
107108
logger.info('Running in direct release mode...');
108109
logger.info('Starting package release...');
109-
const registry = options.registry || DEFAULT_NPM_REGISTRY;
110110
// 将 PublishManifest[] 转换为 PackageToPublish[]
111111
const packages = publishManifests.map(manifest => ({
112112
packageName: manifest.project.packageName,
113113
version: manifest.newVersion,
114114
}));
115-
await release({ dryRun: !!options.dryRun, registry, packages });
115+
await release({
116+
dryRun: !!options.dryRun,
117+
registry: options.registry,
118+
packages,
119+
});
116120
} else {
117121
// 普通模式:创建并推送发布分支
118122
await pushToRemote({

packages/rush-plugins/publish/src/action/publish/confirm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const confirmForPublish = async (
2828
if (options?.isReleaseMode) {
2929
logger.info('', false);
3030
logger.warn(chalk.yellow.bold('⚠️ Release Mode Enabled:'), false);
31-
const registryMsg = ` Packages will be published directly to: ${chalk.bold(options.registry || 'default registry')}`;
31+
const registryMsg = ` Packages will be published directly to: ${chalk.bold(options.registry || 'npm configured registry')}`;
3232
logger.warn(chalk.yellow(registryMsg), false);
3333
}
3434

packages/rush-plugins/publish/src/action/publish/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { logger } from '@coze-arch/logger';
66

77
import { getCurrentOrigin } from '../../utils/git';
88
import { type InstallAction } from '../../types';
9-
import { DEFAULT_BRANCH_PREFIX, DEFAULT_NPM_REGISTRY } from '../../const';
9+
import { DEFAULT_BRANCH_PREFIX } from '../../const';
1010
import { type PublishOptions } from './types';
1111
import { GIT_REPO_URL_REGEX } from './const';
1212
import { publish } from './action';
@@ -50,8 +50,7 @@ export const installAction: InstallAction = (program: Command) => {
5050
)
5151
.option(
5252
'--registry <url>',
53-
`NPM registry URL (default: ${DEFAULT_NPM_REGISTRY})`,
54-
DEFAULT_NPM_REGISTRY,
53+
'NPM registry URL (优先级: CLI参数 > package.json publishConfig.registry > npm config)',
5554
)
5655
.action(async (options: PublishOptions) => {
5756
try {

0 commit comments

Comments
 (0)