@coze-arch/rush-publish-plugin 0.0.5-beta.1 → 0.0.5-beta.2

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.
package/command-line.json CHANGED
@@ -159,6 +159,14 @@
159
159
  "shortName": "-r",
160
160
  "description": "NPM registry URL (default: https://registry.npmjs.org)",
161
161
  "associatedCommands": ["release", "pub"]
162
+ },
163
+ {
164
+ "parameterKind": "stringList",
165
+ "argumentName": "ALLOW_BRANCHES",
166
+ "longName": "--allow-branches",
167
+ "description": "Branches allowed for LATEST release (default: main, feat/auto-publish)",
168
+ "associatedCommands": ["release"],
169
+ "required": false
162
170
  }
163
171
  ]
164
172
  }
@@ -1,14 +1,10 @@
1
- import { type PublishManifest } from './types';
2
1
  export declare function createAndPushBranch(branchName: string, cwd: string): Promise<void>;
3
2
  interface CommitChangesOptions {
4
- sessionId: string;
5
3
  files: string[];
6
4
  cwd: string;
7
- publishManifests: PublishManifest[];
8
5
  branchName: string;
9
- createTags: boolean;
10
6
  }
11
- export declare function commitChanges({ sessionId, files, cwd, publishManifests, branchName, createTags, }: CommitChangesOptions): Promise<{
7
+ export declare function commitChanges({ files, cwd, branchName, }: CommitChangesOptions): Promise<{
12
8
  effects: string[];
13
9
  branchName: string;
14
10
  }>;
@@ -5,4 +5,4 @@ export declare enum ReleaseType {
5
5
  LATEST = "latest"
6
6
  }
7
7
  export declare const calReleaseType: (version: string) => ReleaseType;
8
- export declare const checkReleasePlan: (releaseManifests: ReleaseManifest[], branchName: string) => boolean;
8
+ export declare const checkReleasePlan: (releaseManifests: ReleaseManifest[], branchName: string, allowBranches?: string[]) => boolean;
@@ -4,6 +4,7 @@ export interface ReleaseOptions {
4
4
  dryRun?: boolean;
5
5
  registry: string;
6
6
  packages?: PackageToPublish[];
7
+ allowBranches?: string[];
7
8
  }
8
9
  export interface PackageToPublish {
9
10
  packageName: string;
@@ -6,3 +6,7 @@ export declare const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
6
6
  * 默认的 Git 分支名前缀
7
7
  */
8
8
  export declare const DEFAULT_BRANCH_PREFIX = "bump";
9
+ /**
10
+ * 默认允许发布正式版本的分支列表
11
+ */
12
+ export declare const DEFAULT_ALLOW_BRANCHES: string[];
package/lib/index.js CHANGED
@@ -158,6 +158,11 @@ const DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org';
158
158
  */
159
159
  const DEFAULT_BRANCH_PREFIX = 'bump';
160
160
 
161
+ /**
162
+ * 默认允许发布正式版本的分支列表
163
+ */
164
+ const DEFAULT_ALLOW_BRANCHES = ['main', 'feat/auto-publish'];
165
+
161
166
  // Copyright (c) 2025 coze-dev
162
167
  // SPDX-License-Identifier: MIT
163
168
 
@@ -277,6 +282,84 @@ const getCurrentOrigin = async (
277
282
  }
278
283
  };
279
284
 
285
+ /**
286
+ * 解析 Git 远程仓库 URL,提取主机和仓库路径
287
+ * 支持 HTTP/HTTPS 和 SSH 格式
288
+ * @param gitUrl Git 仓库 URL
289
+ * @returns 包含主机和仓库路径的对象,解析失败返回 null
290
+ */
291
+ const parseGitRemoteUrl = (
292
+ gitUrl,
293
+ ) => {
294
+ if (!gitUrl) {
295
+ return null;
296
+ }
297
+
298
+ const trimmedUrl = gitUrl.trim().replace(/\.git$/, '');
299
+
300
+ // 匹配 SSH 格式: git@github.com:owner/repo.git
301
+ const sshMatch = trimmedUrl.match(/^git@([^:]+):(.+)$/);
302
+ if (sshMatch) {
303
+ const host = sshMatch[1];
304
+ const path = sshMatch[2];
305
+ return {
306
+ host,
307
+ path,
308
+ fullUrl: `https://${host}/${path}`,
309
+ };
310
+ }
311
+
312
+ // 匹配 HTTP/HTTPS 格式: https://github.com/owner/repo.git
313
+ const httpMatch = trimmedUrl.match(/^https?:\/\/([^/]+)\/(.+)$/);
314
+ if (httpMatch) {
315
+ const host = httpMatch[1];
316
+ const path = httpMatch[2];
317
+ return {
318
+ host,
319
+ path,
320
+ fullUrl: `https://${host}/${path}`,
321
+ };
322
+ }
323
+
324
+ return null;
325
+ };
326
+
327
+ /**
328
+ * 根据仓库信息构建 MR/PR 创建链接
329
+ * @param repoInfo 仓库信息
330
+ * @param branchName 分支名称
331
+ * @returns MR/PR 创建链接,如果无法构建则返回 null
332
+ */
333
+ const buildMergeRequestUrl = (
334
+ repoInfo,
335
+ branchName,
336
+ ) => {
337
+ const { host, fullUrl } = repoInfo;
338
+
339
+ // GitHub
340
+ if (host.includes('github.com')) {
341
+ return `${fullUrl}/compare/${branchName}?expand=1`;
342
+ }
343
+
344
+ // GitLab
345
+ if (host.includes('gitlab')) {
346
+ return `${fullUrl}/-/merge_requests/new?merge_request[source_branch]=${branchName}`;
347
+ }
348
+
349
+ // Gitee
350
+ if (host.includes('gitee.com')) {
351
+ return `${fullUrl}/compare/${branchName}...master`;
352
+ }
353
+
354
+ // Bitbucket
355
+ if (host.includes('bitbucket')) {
356
+ return `${fullUrl}/pull-requests/new?source=${branchName}`;
357
+ }
358
+
359
+ // 无法识别的平台
360
+ return null;
361
+ };
362
+
280
363
  // Copyright (c) 2025 coze-dev
281
364
  // SPDX-License-Identifier: MIT
282
365
 
@@ -318,8 +401,7 @@ const getRushConfiguration$1 = (() => {
318
401
  };
319
402
  })();
320
403
 
321
- // Copyright (c) 2025 coze-dev
322
- // SPDX-License-Identifier: MIT
404
+ function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// Copyright (c) 2025 coze-dev
323
405
 
324
406
 
325
407
 
@@ -350,14 +432,21 @@ const updateDependencyVersions = async (
350
432
  return Promise.resolve(packageJson);
351
433
  };
352
434
 
353
- const applyCozePublishConfig = async (
435
+ /**
436
+ * 应用发布配置
437
+ * 优先使用 botPublishConfig,如果不存在则使用 cozePublishConfig
438
+ */
439
+ const applyPublishConfigSettings = async (
354
440
  packageJson,
355
441
  ) => {
356
- const { cozePublishConfig } = packageJson;
357
- if (cozePublishConfig) {
358
- const keys = Object.keys(cozePublishConfig);
442
+ // 优先使用 botPublishConfig,如果不存在则回退到 cozePublishConfig
443
+ const publishConfig =
444
+ _nullishCoalesce(packageJson.botPublishConfig, () => ( packageJson.cozePublishConfig));
445
+
446
+ if (publishConfig) {
447
+ const keys = Object.keys(publishConfig);
359
448
  for (const key of keys) {
360
- packageJson[key] = cozePublishConfig[key];
449
+ packageJson[key] = publishConfig[key];
361
450
  }
362
451
  }
363
452
  return Promise.resolve(packageJson);
@@ -366,7 +455,7 @@ const applyCozePublishConfig = async (
366
455
  const applyPublishConfig = async (project) => {
367
456
  const jobs = [
368
457
  updateDependencyVersions,
369
- applyCozePublishConfig,
458
+ applyPublishConfigSettings,
370
459
  ];
371
460
  const packageJsonPath = path.join(project.projectFolder, 'package.json');
372
461
  let packageJson = await readJsonFile(packageJsonPath);
@@ -471,13 +560,16 @@ const calReleasePlan = (releaseManifests) => {
471
560
  const checkReleasePlan = (
472
561
  releaseManifests,
473
562
  branchName,
563
+ allowBranches = ['main', 'feat/auto-publish'],
474
564
  ) => {
475
565
  const releasePlan = calReleasePlan(releaseManifests);
476
566
  if (
477
567
  releasePlan === ReleaseType.LATEST &&
478
- !['main', 'feat/auto-publish'].includes(branchName)
568
+ !allowBranches.includes(branchName)
479
569
  ) {
480
- throw new Error('For LATEST release, should be on main branch only.');
570
+ throw new Error(
571
+ `For LATEST release, should be on one of these branches: ${allowBranches.join(', ')}. Current Branch: ${branchName}`,
572
+ );
481
573
  }
482
574
  return true;
483
575
  };
@@ -546,7 +638,12 @@ const getPackagesToPublish = async (
546
638
 
547
639
 
548
640
  async function release(options) {
549
- const { dryRun = false, registry, packages } = options;
641
+ const {
642
+ dryRun = false,
643
+ registry,
644
+ packages,
645
+ allowBranches = DEFAULT_ALLOW_BRANCHES,
646
+ } = options;
550
647
  let { commit } = options;
551
648
  const hasPassedCommit = !!options.commit;
552
649
 
@@ -582,7 +679,7 @@ async function release(options) {
582
679
  false,
583
680
  );
584
681
  const branchName = await getCurrentBranchName();
585
- checkReleasePlan(releaseManifests, branchName);
682
+ checkReleasePlan(releaseManifests, branchName, allowBranches);
586
683
 
587
684
  // 只有在指定了 commit 且与当前 HEAD 不同时才切换
588
685
  if (hasPassedCommit) {
@@ -614,6 +711,10 @@ const installAction$2 = (program) => {
614
711
  `发布到的 registry (默认: ${DEFAULT_NPM_REGISTRY})`,
615
712
  DEFAULT_NPM_REGISTRY,
616
713
  )
714
+ .option(
715
+ '--allow-branches <branches...>',
716
+ `允许发布正式版本的分支列表 (默认: ${DEFAULT_ALLOW_BRANCHES.join(', ')})`,
717
+ )
617
718
  .action(async (options) => {
618
719
  try {
619
720
  if (!process.env.NPM_AUTH_TOKEN) {
@@ -854,31 +955,15 @@ const generatePublishManifest = async (
854
955
 
855
956
 
856
957
 
857
-
858
-
859
-
860
958
  async function commitChanges({
861
- sessionId,
862
959
  files,
863
960
  cwd,
864
- publishManifests,
865
961
  branchName,
866
- createTags,
867
962
  }) {
868
963
  await exec(`git add ${files.join(' ')}`, { cwd });
869
964
  await exec(`git commit -m "chore: Publish ${branchName}" -n`, { cwd });
870
965
 
871
- let tags = [];
872
- if (createTags) {
873
- tags = publishManifests.map(
874
- m => `v/${m.project.packageName}@${m.newVersion}`,
875
- );
876
- await exec(
877
- tags.map(tag => `git tag -a ${tag} -m "Bump type ${tag}"`).join(' && '),
878
- { cwd },
879
- );
880
- }
881
- return { effects: [...tags, branchName], branchName };
966
+ return { effects: [branchName], branchName };
882
967
  }
883
968
 
884
969
 
@@ -909,81 +994,188 @@ async function push({ refs, cwd, repoUrl }) {
909
994
 
910
995
 
911
996
 
997
+ /**
998
+ * 获取或创建发布分支
999
+ */
1000
+ const getOrCreateBranch = async (params
1001
+
1002
+
1003
+
1004
+
1005
+ ) => {
1006
+ const { bumpPolicy, sessionId, branchPrefix, cwd } = params;
1007
+
1008
+ if (bumpPolicy === BumpType.BETA) {
1009
+ return await getCurrentBranchName();
1010
+ }
1011
+
1012
+ const date = dayjs().format('YYYYMMDD');
1013
+ const branchName = `${branchPrefix}/${date}-${sessionId}`;
1014
+ await exec(`git checkout -b ${branchName}`, { cwd });
1015
+ return branchName;
1016
+ };
1017
+
1018
+ /**
1019
+ * 处理 Beta 发布流程:询问并执行 git push
1020
+ */
1021
+ const handleBetaPublish = async (
1022
+ branchName,
1023
+ cwd,
1024
+ ) => {
1025
+ logger.success(`Changes have been committed to branch "${branchName}".`);
1026
+
1027
+ const shouldPush = await prompts.confirm({
1028
+ message: 'Do you want to push the changes now?',
1029
+ default: true,
1030
+ });
1031
+
1032
+ if (shouldPush) {
1033
+ logger.info('Pushing changes to remote...');
1034
+ await exec('git push', { cwd });
1035
+ logger.success('Changes pushed successfully!');
1036
+ } else {
1037
+ logger.info('Please run "git push" manually when you are ready.');
1038
+ }
1039
+ };
1040
+
1041
+ /**
1042
+ * 显示手动创建 MR 的提示消息
1043
+ */
1044
+ const showManualMrMessage = (
1045
+ branchName,
1046
+ repositoryUrl,
1047
+ ) => {
1048
+ const baseMessage = `Please create a merge request from branch "${branchName}" to the main branch`;
1049
+ const suffix =
1050
+ 'The release will be triggered after the merge request is merged.';
1051
+
1052
+ if (repositoryUrl) {
1053
+ logger.success(
1054
+ `Repository: ${repositoryUrl}\n${baseMessage}.\n${suffix}`,
1055
+ false,
1056
+ );
1057
+ } else {
1058
+ logger.success(`${baseMessage} in your repository.\n${suffix}`);
1059
+ }
1060
+ };
1061
+
1062
+ /**
1063
+ * 显示 MR/PR 创建链接并打开浏览器
1064
+ */
1065
+ const showMrLinkAndOpenBrowser = async (mrUrl) => {
1066
+ const log = [
1067
+ '************************************************',
1068
+ '*',
1069
+ `* Please create MR/PR: ${mrUrl}`,
1070
+ '*',
1071
+ '* The release will be triggered after the MR is merged.',
1072
+ '*',
1073
+ '************************************************',
1074
+ ];
1075
+ logger.success(log.join('\n'), false);
1076
+
1077
+ const open = await import('open');
1078
+ await open.default(mrUrl);
1079
+ };
1080
+
1081
+ /**
1082
+ * 处理正式版本发布流程:提示创建 MR/PR
1083
+ */
1084
+ const handleProductionPublish = async (
1085
+ branchName,
1086
+ cwd,
1087
+ ) => {
1088
+ const originUrl = await getCurrentOrigin(cwd);
1089
+ if (!originUrl) {
1090
+ showManualMrMessage(branchName);
1091
+ return;
1092
+ }
1093
+
1094
+ const repoInfo = parseGitRemoteUrl(originUrl);
1095
+ if (!repoInfo) {
1096
+ showManualMrMessage(branchName);
1097
+ return;
1098
+ }
1099
+
1100
+ const mrUrl = buildMergeRequestUrl(repoInfo, branchName);
1101
+ if (!mrUrl) {
1102
+ showManualMrMessage(branchName, repoInfo.fullUrl);
1103
+ return;
1104
+ }
1105
+
1106
+ await showMrLinkAndOpenBrowser(mrUrl);
1107
+ };
1108
+
1109
+ /**
1110
+ * 根据发布类型处理后续提示
1111
+ */
1112
+ const handlePostPushPrompts = async (
1113
+ bumpPolicy,
1114
+ branchName,
1115
+ cwd,
1116
+ ) => {
1117
+ const isAlphaPublish = bumpPolicy === BumpType.ALPHA;
1118
+ const isBetaPublish = bumpPolicy === BumpType.BETA;
1119
+
1120
+ // Alpha 发布:不需要提示(会自动触发 CI 发布)
1121
+ if (isAlphaPublish) {
1122
+ return;
1123
+ }
1124
+
1125
+ // Beta 发布:询问用户是否立即执行 git push
1126
+ if (isBetaPublish) {
1127
+ await handleBetaPublish(branchName, cwd);
1128
+ return;
1129
+ }
1130
+
1131
+ // 正式版本发布:提示创建 MR/PR
1132
+ await handleProductionPublish(branchName, cwd);
1133
+ };
1134
+
912
1135
  const pushToRemote = async (options) => {
913
1136
  const {
914
1137
  sessionId,
915
1138
  changedFiles,
916
1139
  cwd,
917
- publishManifests,
918
1140
  bumpPolicy,
919
1141
  skipCommit,
920
1142
  skipPush,
921
1143
  repoUrl,
922
1144
  branchPrefix = 'release',
923
1145
  } = options;
1146
+
924
1147
  if (skipCommit) {
925
1148
  return;
926
1149
  }
927
1150
 
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
- );
1151
+ // 1. 获取或创建发布分支
1152
+ const branchName = await getOrCreateBranch({
1153
+ bumpPolicy,
1154
+ sessionId,
1155
+ branchPrefix,
1156
+ cwd,
1157
+ });
942
1158
 
943
- // 4. 创建并推送发布分支
1159
+ // 2. 提交变更
944
1160
  const { effects } = await commitChanges({
945
- sessionId,
946
1161
  files: changedFiles,
947
1162
  cwd,
948
- publishManifests,
949
1163
  branchName,
950
- // 只有 alpha、beta 需要创建 tag,正式发布会在 .github/workflows/common-pr-checks.yml 创建并发布tag
951
- createTags: isTestPublish,
952
1164
  });
1165
+
953
1166
  if (skipPush) {
954
1167
  return;
955
1168
  }
1169
+
1170
+ // 3. 推送变更
956
1171
  await push({
957
1172
  refs: effects,
958
1173
  cwd,
959
- repoUrl: actualRepoUrl,
1174
+ repoUrl,
960
1175
  });
961
1176
 
962
- // git URL 提取组织和仓库名称,用于构建 GitHub 链接
963
- const repoInfoMatch = actualRepoUrl.match(GIT_REPO_URL_REGEX);
964
- if (!repoInfoMatch) {
965
- throw new Error('Invalid git repository URL');
966
- }
967
- const repoOwner = repoInfoMatch[1];
968
- const repoName = repoInfoMatch[2];
969
-
970
- if (isTestPublish) {
971
- logger.success(
972
- `Please refer to https://github.com/${repoOwner}/${repoName}/actions/workflows/release.yml for the release progress.`,
973
- );
974
- } else {
975
- const prUrl = `https://github.com/${repoOwner}/${repoName}/compare/${branchName}?expand=1`;
976
- const log = [
977
- '************************************************',
978
- '*',
979
- `* Please create PR: ${prUrl}`,
980
- '*',
981
- '************************************************',
982
- ];
983
- logger.success(log.join('\n'), false);
984
- const open = await import('open');
985
- await open.default(prUrl);
986
- }
1177
+ // 4. 根据发布类型显示不同提示
1178
+ await handlePostPushPrompts(bumpPolicy, branchName, cwd);
987
1179
  };
988
1180
 
989
1181
  // Copyright (c) 2025 coze-dev
@@ -1595,7 +1787,11 @@ const publish = async (options) => {
1595
1787
  const isBetaPublish = [BumpType.BETA, BumpType.ALPHA].includes(
1596
1788
  bumpPolicy ,
1597
1789
  );
1598
- if (isBetaPublish === false && (await isMainBranch()) === false) {
1790
+ if (
1791
+ process.env.SKIP_BRANCH_CHECK !== 'true' &&
1792
+ isBetaPublish === false &&
1793
+ (await isMainBranch()) === false
1794
+ ) {
1599
1795
  // 只允许在主分支发布
1600
1796
  logger.error(
1601
1797
  'You are not in main branch, please switch to main branch and try again.',
@@ -1654,7 +1850,6 @@ const publish = async (options) => {
1654
1850
  } else {
1655
1851
  // 普通模式:创建并推送发布分支
1656
1852
  await pushToRemote({
1657
- publishManifests,
1658
1853
  bumpPolicy: bumpPolicy ,
1659
1854
  sessionId,
1660
1855
  changedFiles,
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coze-arch/rush-publish-plugin",
3
- "version": "0.0.5-beta.1",
3
+ "version": "0.0.5-beta.2",
4
4
  "description": "rush plugin to generate change log and publish packages",
5
5
  "keywords": [
6
6
  "rush",