@coze-arch/rush-publish-plugin 0.0.5-alpha.8b5f6f → 0.0.5-alpha.bba657

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import { type ReleaseOptions, type ReleaseManifest } from './types';
1
+ import { type ReleaseManifest, type ReleaseOptions } from './types';
2
2
  export declare const releasePackages: (releaseManifests: ReleaseManifest[], releaseOptions: ReleaseOptions) => Promise<void>;
@@ -2,7 +2,7 @@ import { type RushConfigurationProject } from '@rushstack/rush-sdk';
2
2
  export interface ReleaseOptions {
3
3
  commit?: string;
4
4
  dryRun?: boolean;
5
- registry: string;
5
+ registry?: string;
6
6
  packages?: PackageToPublish[];
7
7
  allowBranches?: string[];
8
8
  }
@@ -1,7 +1,3 @@
1
- /**
2
- * 默认的 NPM registry 地址
3
- */
4
- export declare const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
5
1
  /**
6
2
  * 默认的 Git 分支名前缀
7
3
  */
package/lib/index.js CHANGED
@@ -148,11 +148,6 @@ const logger = {
148
148
  // Copyright (c) 2025 coze-dev
149
149
  // SPDX-License-Identifier: MIT
150
150
 
151
- /**
152
- * 默认的 NPM registry 地址
153
- */
154
- const DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org';
155
-
156
151
  /**
157
152
  * 默认的 Git 分支名前缀
158
153
  */
@@ -282,6 +277,140 @@ const getCurrentOrigin = async (
282
277
  }
283
278
  };
284
279
 
280
+ /**
281
+ * 解析 Git 远程仓库 URL,提取主机和仓库路径
282
+ * 支持 HTTP/HTTPS 和 SSH 格式
283
+ * @param gitUrl Git 仓库 URL
284
+ * @returns 包含主机和仓库路径的对象,解析失败返回 null
285
+ */
286
+ const parseGitRemoteUrl = (
287
+ gitUrl,
288
+ ) => {
289
+ if (!gitUrl) {
290
+ return null;
291
+ }
292
+
293
+ const trimmedUrl = gitUrl.trim().replace(/\.git$/, '');
294
+
295
+ // 匹配 SSH 格式: git@github.com:owner/repo.git
296
+ const sshMatch = trimmedUrl.match(/^git@([^:]+):(.+)$/);
297
+ if (sshMatch) {
298
+ const host = sshMatch[1];
299
+ const path = sshMatch[2];
300
+ return {
301
+ host,
302
+ path,
303
+ fullUrl: `https://${host}/${path}`,
304
+ };
305
+ }
306
+
307
+ // 匹配 HTTP/HTTPS 格式: https://github.com/owner/repo.git
308
+ const httpMatch = trimmedUrl.match(/^https?:\/\/([^/]+)\/(.+)$/);
309
+ if (httpMatch) {
310
+ const host = httpMatch[1];
311
+ const path = httpMatch[2];
312
+ return {
313
+ host,
314
+ path,
315
+ fullUrl: `https://${host}/${path}`,
316
+ };
317
+ }
318
+
319
+ return null;
320
+ };
321
+
322
+ /**
323
+ * 根据仓库信息构建 MR/PR 创建链接
324
+ * @param repoInfo 仓库信息
325
+ * @param branchName 分支名称
326
+ * @returns MR/PR 创建链接,如果无法构建则返回 null
327
+ */
328
+ const buildMergeRequestUrl = (
329
+ repoInfo,
330
+ branchName,
331
+ ) => {
332
+ const { host, fullUrl } = repoInfo;
333
+
334
+ // GitHub
335
+ if (host.includes('github.com')) {
336
+ return `${fullUrl}/compare/${branchName}?expand=1`;
337
+ }
338
+
339
+ // GitLab
340
+ if (host.includes('gitlab')) {
341
+ return `${fullUrl}/-/merge_requests/new?merge_request[source_branch]=${branchName}`;
342
+ }
343
+
344
+ // Gitee
345
+ if (host.includes('gitee.com')) {
346
+ return `${fullUrl}/compare/${branchName}...master`;
347
+ }
348
+
349
+ // Bitbucket
350
+ if (host.includes('bitbucket')) {
351
+ return `${fullUrl}/pull-requests/new?source=${branchName}`;
352
+ }
353
+
354
+ // 无法识别的平台
355
+ return null;
356
+ };
357
+
358
+ function _optionalChain$3(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
359
+
360
+ /**
361
+ * package.json 的类型定义
362
+ */
363
+
364
+
365
+
366
+
367
+
368
+
369
+
370
+
371
+ /**
372
+ * 解析发布 registry 的优先级:
373
+ * 1. 命令行参数 --registry (最高优先级)
374
+ * 2. package.json 中的 publishConfig.registry
375
+ * 3. 使用 npm 配置的默认 registry (不传 --registry 参数)
376
+ *
377
+ * @param project - Rush 项目配置
378
+ * @param registryFromCli - 命令行参数传入的 registry
379
+ * @returns 解析后的 registry URL,如果为 undefined 则使用 npm 默认配置
380
+ */
381
+ function resolveRegistry(
382
+ project,
383
+ registryFromCli,
384
+ ) {
385
+ // 1. 优先使用命令行参数
386
+ if (registryFromCli) {
387
+ logger.info(
388
+ `Using registry from CLI: ${registryFromCli} (${project.packageName})`,
389
+ false,
390
+ );
391
+ return registryFromCli;
392
+ }
393
+
394
+ // 2. 尝试读取 package.json 中的 publishConfig.registry
395
+ const packageJson = project.packageJson ;
396
+ const registryFromPackageJson = _optionalChain$3([packageJson, 'access', _ => _.publishConfig, 'optionalAccess', _2 => _2.registry]);
397
+
398
+ if (registryFromPackageJson) {
399
+ logger.info(
400
+ `Using registry from publishConfig: ${registryFromPackageJson} (${project.packageName})`,
401
+ false,
402
+ );
403
+ return registryFromPackageJson;
404
+ }
405
+
406
+ // 3. 返回 undefined,让 npm 使用自己配置的 registry
407
+ logger.info(
408
+ `Using npm configured registry (${project.packageName})`,
409
+ false,
410
+ );
411
+ return undefined;
412
+ }
413
+
285
414
  // Copyright (c) 2025 coze-dev
286
415
  // SPDX-License-Identifier: MIT
287
416
 
@@ -323,8 +452,7 @@ const getRushConfiguration$1 = (() => {
323
452
  };
324
453
  })();
325
454
 
326
- // Copyright (c) 2025 coze-dev
327
- // SPDX-License-Identifier: MIT
455
+ function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// Copyright (c) 2025 coze-dev
328
456
 
329
457
 
330
458
 
@@ -355,14 +483,21 @@ const updateDependencyVersions = async (
355
483
  return Promise.resolve(packageJson);
356
484
  };
357
485
 
358
- const applyCozePublishConfig = async (
486
+ /**
487
+ * 应用发布配置
488
+ * 优先使用 botPublishConfig,如果不存在则使用 cozePublishConfig
489
+ */
490
+ const applyPublishConfigSettings = async (
359
491
  packageJson,
360
492
  ) => {
361
- const { cozePublishConfig } = packageJson;
362
- if (cozePublishConfig) {
363
- const keys = Object.keys(cozePublishConfig);
493
+ // 优先使用 botPublishConfig,如果不存在则回退到 cozePublishConfig
494
+ const publishConfig =
495
+ _nullishCoalesce(packageJson.botPublishConfig, () => ( packageJson.cozePublishConfig));
496
+
497
+ if (publishConfig) {
498
+ const keys = Object.keys(publishConfig);
364
499
  for (const key of keys) {
365
- packageJson[key] = cozePublishConfig[key];
500
+ packageJson[key] = publishConfig[key];
366
501
  }
367
502
  }
368
503
  return Promise.resolve(packageJson);
@@ -371,7 +506,7 @@ const applyCozePublishConfig = async (
371
506
  const applyPublishConfig = async (project) => {
372
507
  const jobs = [
373
508
  updateDependencyVersions,
374
- applyCozePublishConfig,
509
+ applyPublishConfigSettings,
375
510
  ];
376
511
  const packageJsonPath = path.join(project.projectFolder, 'package.json');
377
512
  let packageJson = await readJsonFile(packageJsonPath);
@@ -388,7 +523,7 @@ const publishPackage = async (
388
523
  project,
389
524
  releaseOptions,
390
525
  ) => {
391
- const { dryRun, registry } = releaseOptions;
526
+ const { dryRun } = releaseOptions;
392
527
  const token = process.env.NPM_AUTH_TOKEN;
393
528
  const { version } = project.packageJson;
394
529
  const tag = version.includes('alpha')
@@ -396,6 +531,10 @@ const publishPackage = async (
396
531
  : version.includes('beta')
397
532
  ? 'beta'
398
533
  : 'latest';
534
+
535
+ // 解析 registry:CLI 参数 > package.json publishConfig > npm 配置
536
+ const registry = resolveRegistry(project, releaseOptions.registry);
537
+
399
538
  const setToken = `npm config set //bnpm.byted.org/:_authToken ${token}`;
400
539
  await exec(setToken, {
401
540
  cwd: project.projectFolder,
@@ -405,6 +544,7 @@ const publishPackage = async (
405
544
  if (dryRun) {
406
545
  args.push('--dry-run');
407
546
  }
547
+ // 只有明确指定了 registry 才传递参数,否则使用 npm 配置的默认值
408
548
  if (registry) {
409
549
  args.push(`--registry=${registry}`);
410
550
  }
@@ -484,7 +624,7 @@ const checkReleasePlan = (
484
624
  !allowBranches.includes(branchName)
485
625
  ) {
486
626
  throw new Error(
487
- `For LATEST release, should be on one of these branches: ${allowBranches.join(', ')}.`,
627
+ `For LATEST release, should be on one of these branches: ${allowBranches.join(', ')}. Current Branch: ${branchName}`,
488
628
  );
489
629
  }
490
630
  return true;
@@ -624,8 +764,7 @@ const installAction$2 = (program) => {
624
764
  .option('--dry-run', '是否只执行不真实发布', false)
625
765
  .option(
626
766
  '-r, --registry <string>',
627
- `发布到的 registry (默认: ${DEFAULT_NPM_REGISTRY})`,
628
- DEFAULT_NPM_REGISTRY,
767
+ '发布到的 registry (优先级: CLI参数 > package.json publishConfig.registry > npm config)',
629
768
  )
630
769
  .option(
631
770
  '--allow-branches <branches...>',
@@ -910,6 +1049,144 @@ async function push({ refs, cwd, repoUrl }) {
910
1049
 
911
1050
 
912
1051
 
1052
+ /**
1053
+ * 获取或创建发布分支
1054
+ */
1055
+ const getOrCreateBranch = async (params
1056
+
1057
+
1058
+
1059
+
1060
+ ) => {
1061
+ const { bumpPolicy, sessionId, branchPrefix, cwd } = params;
1062
+
1063
+ if (bumpPolicy === BumpType.BETA) {
1064
+ return await getCurrentBranchName();
1065
+ }
1066
+
1067
+ const date = dayjs().format('YYYYMMDD');
1068
+ const branchName = `${branchPrefix}/${date}-${sessionId}`;
1069
+ await exec(`git checkout -b ${branchName}`, { cwd });
1070
+ return branchName;
1071
+ };
1072
+
1073
+ /**
1074
+ * 处理 Beta 发布流程:询问并执行 git push
1075
+ */
1076
+ const handleBetaPublish = async (
1077
+ branchName,
1078
+ cwd,
1079
+ ) => {
1080
+ logger.success(`Changes have been committed to branch "${branchName}".`);
1081
+
1082
+ const shouldPush = await prompts.confirm({
1083
+ message: 'Do you want to push the changes now?',
1084
+ default: true,
1085
+ });
1086
+
1087
+ if (shouldPush) {
1088
+ logger.info('Pushing changes to remote...');
1089
+ await exec('git push', { cwd });
1090
+ logger.success('Changes pushed successfully!');
1091
+ } else {
1092
+ logger.info('Please run "git push" manually when you are ready.');
1093
+ }
1094
+ };
1095
+
1096
+ /**
1097
+ * 显示手动创建 MR 的提示消息
1098
+ */
1099
+ const showManualMrMessage = (
1100
+ branchName,
1101
+ repositoryUrl,
1102
+ ) => {
1103
+ const baseMessage = `Please create a merge request from branch "${branchName}" to the main branch`;
1104
+ const suffix =
1105
+ 'The release will be triggered after the merge request is merged.';
1106
+
1107
+ if (repositoryUrl) {
1108
+ logger.success(
1109
+ `Repository: ${repositoryUrl}\n${baseMessage}.\n${suffix}`,
1110
+ false,
1111
+ );
1112
+ } else {
1113
+ logger.success(`${baseMessage} in your repository.\n${suffix}`);
1114
+ }
1115
+ };
1116
+
1117
+ /**
1118
+ * 显示 MR/PR 创建链接并打开浏览器
1119
+ */
1120
+ const showMrLinkAndOpenBrowser = async (mrUrl) => {
1121
+ const log = [
1122
+ '************************************************',
1123
+ '*',
1124
+ `* Please create MR/PR: ${mrUrl}`,
1125
+ '*',
1126
+ '* The release will be triggered after the MR is merged.',
1127
+ '*',
1128
+ '************************************************',
1129
+ ];
1130
+ logger.success(log.join('\n'), false);
1131
+
1132
+ const open = await import('open');
1133
+ await open.default(mrUrl);
1134
+ };
1135
+
1136
+ /**
1137
+ * 处理正式版本发布流程:提示创建 MR/PR
1138
+ */
1139
+ const handleProductionPublish = async (
1140
+ branchName,
1141
+ cwd,
1142
+ ) => {
1143
+ const originUrl = await getCurrentOrigin(cwd);
1144
+ if (!originUrl) {
1145
+ showManualMrMessage(branchName);
1146
+ return;
1147
+ }
1148
+
1149
+ const repoInfo = parseGitRemoteUrl(originUrl);
1150
+ if (!repoInfo) {
1151
+ showManualMrMessage(branchName);
1152
+ return;
1153
+ }
1154
+
1155
+ const mrUrl = buildMergeRequestUrl(repoInfo, branchName);
1156
+ if (!mrUrl) {
1157
+ showManualMrMessage(branchName, repoInfo.fullUrl);
1158
+ return;
1159
+ }
1160
+
1161
+ await showMrLinkAndOpenBrowser(mrUrl);
1162
+ };
1163
+
1164
+ /**
1165
+ * 根据发布类型处理后续提示
1166
+ */
1167
+ const handlePostPushPrompts = async (
1168
+ bumpPolicy,
1169
+ branchName,
1170
+ cwd,
1171
+ ) => {
1172
+ const isAlphaPublish = bumpPolicy === BumpType.ALPHA;
1173
+ const isBetaPublish = bumpPolicy === BumpType.BETA;
1174
+
1175
+ // Alpha 发布:不需要提示(会自动触发 CI 发布)
1176
+ if (isAlphaPublish) {
1177
+ return;
1178
+ }
1179
+
1180
+ // Beta 发布:询问用户是否立即执行 git push
1181
+ if (isBetaPublish) {
1182
+ await handleBetaPublish(branchName, cwd);
1183
+ return;
1184
+ }
1185
+
1186
+ // 正式版本发布:提示创建 MR/PR
1187
+ await handleProductionPublish(branchName, cwd);
1188
+ };
1189
+
913
1190
  const pushToRemote = async (options) => {
914
1191
  const {
915
1192
  sessionId,
@@ -921,65 +1198,39 @@ const pushToRemote = async (options) => {
921
1198
  repoUrl,
922
1199
  branchPrefix = 'release',
923
1200
  } = options;
1201
+
924
1202
  if (skipCommit) {
925
1203
  return;
926
1204
  }
927
1205
 
928
- // 获取仓库 URL
929
- const actualRepoUrl = repoUrl;
930
-
931
- let branchName;
932
- if (bumpPolicy === BumpType.BETA) {
933
- branchName = await getCurrentBranchName();
934
- } else {
935
- const date = dayjs().format('YYYYMMDD');
936
- branchName = `${branchPrefix}/${date}-${sessionId}`;
937
- await exec(`git checkout -b ${branchName}`, { cwd });
938
- }
939
- const isTestPublish = [BumpType.ALPHA, BumpType.BETA].includes(
940
- bumpPolicy ,
941
- );
1206
+ // 1. 获取或创建发布分支
1207
+ const branchName = await getOrCreateBranch({
1208
+ bumpPolicy,
1209
+ sessionId,
1210
+ branchPrefix,
1211
+ cwd,
1212
+ });
942
1213
 
943
- // 4. 创建并推送发布分支
1214
+ // 2. 提交变更
944
1215
  const { effects } = await commitChanges({
945
1216
  files: changedFiles,
946
1217
  cwd,
947
1218
  branchName,
948
1219
  });
1220
+
949
1221
  if (skipPush) {
950
1222
  return;
951
1223
  }
1224
+
1225
+ // 3. 推送变更
952
1226
  await push({
953
1227
  refs: effects,
954
1228
  cwd,
955
- repoUrl: actualRepoUrl,
1229
+ repoUrl,
956
1230
  });
957
1231
 
958
- // git URL 提取组织和仓库名称,用于构建 GitHub 链接
959
- const repoInfoMatch = actualRepoUrl.match(GIT_REPO_URL_REGEX);
960
- if (!repoInfoMatch) {
961
- throw new Error('Invalid git repository URL');
962
- }
963
- const repoOwner = repoInfoMatch[1];
964
- const repoName = repoInfoMatch[2];
965
-
966
- if (isTestPublish) {
967
- logger.success(
968
- `Please refer to https://github.com/${repoOwner}/${repoName}/actions/workflows/release.yml for the release progress.`,
969
- );
970
- } else {
971
- const prUrl = `https://github.com/${repoOwner}/${repoName}/compare/${branchName}?expand=1`;
972
- const log = [
973
- '************************************************',
974
- '*',
975
- `* Please create PR: ${prUrl}`,
976
- '*',
977
- '************************************************',
978
- ];
979
- logger.success(log.join('\n'), false);
980
- const open = await import('open');
981
- await open.default(prUrl);
982
- }
1232
+ // 4. 根据发布类型显示不同提示
1233
+ await handlePostPushPrompts(bumpPolicy, branchName, cwd);
983
1234
  };
984
1235
 
985
1236
  // Copyright (c) 2025 coze-dev
@@ -1186,7 +1437,7 @@ const confirmForPublish = async (
1186
1437
  if (_optionalChain$1([options, 'optionalAccess', _ => _.isReleaseMode])) {
1187
1438
  logger.info('', false);
1188
1439
  logger.warn(chalk.yellow.bold('⚠️ Release Mode Enabled:'), false);
1189
- const registryMsg = ` Packages will be published directly to: ${chalk.bold(options.registry || 'default registry')}`;
1440
+ const registryMsg = ` Packages will be published directly to: ${chalk.bold(options.registry || 'npm configured registry')}`;
1190
1441
  logger.warn(chalk.yellow(registryMsg), false);
1191
1442
  }
1192
1443
 
@@ -1591,7 +1842,11 @@ const publish = async (options) => {
1591
1842
  const isBetaPublish = [BumpType.BETA, BumpType.ALPHA].includes(
1592
1843
  bumpPolicy ,
1593
1844
  );
1594
- if (isBetaPublish === false && (await isMainBranch()) === false) {
1845
+ if (
1846
+ process.env.SKIP_BRANCH_CHECK !== 'true' &&
1847
+ isBetaPublish === false &&
1848
+ (await isMainBranch()) === false
1849
+ ) {
1595
1850
  // 只允许在主分支发布
1596
1851
  logger.error(
1597
1852
  'You are not in main branch, please switch to main branch and try again.',
@@ -1604,7 +1859,7 @@ const publish = async (options) => {
1604
1859
  !!options.dryRun,
1605
1860
  {
1606
1861
  isReleaseMode: !!options.release,
1607
- registry: options.registry || DEFAULT_NPM_REGISTRY,
1862
+ registry: options.registry,
1608
1863
  },
1609
1864
  );
1610
1865
 
@@ -1640,13 +1895,16 @@ const publish = async (options) => {
1640
1895
  // Release 模式:直接发布
1641
1896
  logger.info('Running in direct release mode...');
1642
1897
  logger.info('Starting package release...');
1643
- const registry = options.registry || DEFAULT_NPM_REGISTRY;
1644
1898
  // 将 PublishManifest[] 转换为 PackageToPublish[]
1645
1899
  const packages = publishManifests.map(manifest => ({
1646
1900
  packageName: manifest.project.packageName,
1647
1901
  version: manifest.newVersion,
1648
1902
  }));
1649
- await release({ dryRun: !!options.dryRun, registry, packages });
1903
+ await release({
1904
+ dryRun: !!options.dryRun,
1905
+ registry: options.registry,
1906
+ packages,
1907
+ });
1650
1908
  } else {
1651
1909
  // 普通模式:创建并推送发布分支
1652
1910
  await pushToRemote({
@@ -1703,8 +1961,7 @@ const installAction$1 = (program) => {
1703
1961
  )
1704
1962
  .option(
1705
1963
  '--registry <url>',
1706
- `NPM registry URL (default: ${DEFAULT_NPM_REGISTRY})`,
1707
- DEFAULT_NPM_REGISTRY,
1964
+ 'NPM registry URL (优先级: CLI参数 > package.json publishConfig.registry > npm config)',
1708
1965
  )
1709
1966
  .action(async (options) => {
1710
1967
  try {
@@ -22,3 +22,25 @@ export declare const ensureNotUncommittedChanges: () => Promise<boolean>;
22
22
  */
23
23
  export declare const getCurrentOrigin: (cwd?: string) => Promise<string | undefined>;
24
24
  export declare const convertGitSchemaToHttp: (gitUrl: string) => string;
25
+ /**
26
+ * 解析 Git 远程仓库 URL,提取主机和仓库路径
27
+ * 支持 HTTP/HTTPS 和 SSH 格式
28
+ * @param gitUrl Git 仓库 URL
29
+ * @returns 包含主机和仓库路径的对象,解析失败返回 null
30
+ */
31
+ export declare const parseGitRemoteUrl: (gitUrl: string) => {
32
+ host: string;
33
+ path: string;
34
+ fullUrl: string;
35
+ } | null;
36
+ /**
37
+ * 根据仓库信息构建 MR/PR 创建链接
38
+ * @param repoInfo 仓库信息
39
+ * @param branchName 分支名称
40
+ * @returns MR/PR 创建链接,如果无法构建则返回 null
41
+ */
42
+ export declare const buildMergeRequestUrl: (repoInfo: {
43
+ host: string;
44
+ path: string;
45
+ fullUrl: string;
46
+ }, branchName: string) => string | null;
@@ -0,0 +1,12 @@
1
+ import { type RushConfigurationProject } from '@rushstack/rush-sdk';
2
+ /**
3
+ * 解析发布 registry 的优先级:
4
+ * 1. 命令行参数 --registry (最高优先级)
5
+ * 2. package.json 中的 publishConfig.registry
6
+ * 3. 使用 npm 配置的默认 registry (不传 --registry 参数)
7
+ *
8
+ * @param project - Rush 项目配置
9
+ * @param registryFromCli - 命令行参数传入的 registry
10
+ * @returns 解析后的 registry URL,如果为 undefined 则使用 npm 默认配置
11
+ */
12
+ export declare function resolveRegistry(project: RushConfigurationProject, registryFromCli?: string): string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coze-arch/rush-publish-plugin",
3
- "version": "0.0.5-alpha.8b5f6f",
3
+ "version": "0.0.5-alpha.bba657",
4
4
  "description": "rush plugin to generate change log and publish packages",
5
5
  "keywords": [
6
6
  "rush",