@coze-arch/rush-publish-plugin 0.0.5-alpha.0f1107 → 0.0.5-alpha.1ae01f
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 +26 -2
- package/lib/action/publish/confirm.d.ts +5 -1
- package/lib/action/publish/git.d.ts +1 -5
- package/lib/action/publish/push-to-remote.d.ts +1 -0
- package/lib/action/publish/types.d.ts +3 -0
- package/lib/action/release/plan.d.ts +1 -1
- package/lib/action/release/types.d.ts +3 -1
- package/lib/const/index.d.ts +12 -0
- package/lib/index.js +408 -99
- package/lib/utils/get-plugin-config.d.ts +8 -0
- package/lib/utils/git.d.ts +27 -0
- package/package.json +1 -1
package/command-line.json
CHANGED
|
@@ -106,6 +106,22 @@
|
|
|
106
106
|
"associatedCommands": ["pub"],
|
|
107
107
|
"required": false
|
|
108
108
|
},
|
|
109
|
+
{
|
|
110
|
+
"parameterKind": "string",
|
|
111
|
+
"longName": "--branch-prefix",
|
|
112
|
+
"description": "Git branch name prefix (default: 'release')",
|
|
113
|
+
"argumentName": "BRANCH_PREFIX",
|
|
114
|
+
"associatedCommands": ["pub"],
|
|
115
|
+
"required": false
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"parameterKind": "flag",
|
|
119
|
+
"longName": "--release",
|
|
120
|
+
"shortName": "-l",
|
|
121
|
+
"description": "Directly publish packages (only for alpha/beta versions)",
|
|
122
|
+
"associatedCommands": ["pub"],
|
|
123
|
+
"required": false
|
|
124
|
+
},
|
|
109
125
|
{
|
|
110
126
|
"parameterKind": "flag",
|
|
111
127
|
"longName": "--amend-commit",
|
|
@@ -141,8 +157,16 @@
|
|
|
141
157
|
"argumentName": "REGISTRY",
|
|
142
158
|
"longName": "--registry",
|
|
143
159
|
"shortName": "-r",
|
|
144
|
-
"description": "
|
|
145
|
-
"associatedCommands": ["release"]
|
|
160
|
+
"description": "NPM registry URL (default: https://registry.npmjs.org)",
|
|
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
|
|
146
170
|
}
|
|
147
171
|
]
|
|
148
172
|
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { type PublishManifest } from './types';
|
|
2
|
-
export
|
|
2
|
+
export interface ConfirmForPublishOptions {
|
|
3
|
+
isReleaseMode?: boolean;
|
|
4
|
+
registry?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const confirmForPublish: (publishManifest: PublishManifest[], dryRun: boolean, options?: ConfirmForPublishOptions) => Promise<boolean>;
|
|
@@ -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({
|
|
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;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type RushConfigurationProject } from '@rushstack/rush-sdk';
|
|
2
2
|
export interface ReleaseOptions {
|
|
3
|
-
commit
|
|
3
|
+
commit?: string;
|
|
4
4
|
dryRun?: boolean;
|
|
5
5
|
registry: string;
|
|
6
|
+
packages?: PackageToPublish[];
|
|
7
|
+
allowBranches?: string[];
|
|
6
8
|
}
|
|
7
9
|
export interface PackageToPublish {
|
|
8
10
|
packageName: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 默认的 NPM registry 地址
|
|
3
|
+
*/
|
|
4
|
+
export declare const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
5
|
+
/**
|
|
6
|
+
* 默认的 Git 分支名前缀
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEFAULT_BRANCH_PREFIX = "bump";
|
|
9
|
+
/**
|
|
10
|
+
* 默认允许发布正式版本的分支列表
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_ALLOW_BRANCHES: string[];
|
package/lib/index.js
CHANGED
|
@@ -148,6 +148,24 @@ 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
|
+
/**
|
|
157
|
+
* 默认的 Git 分支名前缀
|
|
158
|
+
*/
|
|
159
|
+
const DEFAULT_BRANCH_PREFIX = 'bump';
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 默认允许发布正式版本的分支列表
|
|
163
|
+
*/
|
|
164
|
+
const DEFAULT_ALLOW_BRANCHES = ['main', 'feat/auto-publish'];
|
|
165
|
+
|
|
166
|
+
// Copyright (c) 2025 coze-dev
|
|
167
|
+
// SPDX-License-Identifier: MIT
|
|
168
|
+
|
|
151
169
|
|
|
152
170
|
|
|
153
171
|
|
|
@@ -214,6 +232,15 @@ const getCurrentBranchName = async () => {
|
|
|
214
232
|
return stdout.trim();
|
|
215
233
|
};
|
|
216
234
|
|
|
235
|
+
/**
|
|
236
|
+
* 获取当前 commit hash
|
|
237
|
+
* @returns commit hash
|
|
238
|
+
*/
|
|
239
|
+
const getCurrentCommitHash = async () => {
|
|
240
|
+
const { stdout } = await exec('git rev-parse HEAD');
|
|
241
|
+
return stdout.trim();
|
|
242
|
+
};
|
|
243
|
+
|
|
217
244
|
const isMainBranch = async () => {
|
|
218
245
|
const currentBranchName = await getCurrentBranchName();
|
|
219
246
|
return currentBranchName === 'main';
|
|
@@ -255,6 +282,84 @@ const getCurrentOrigin = async (
|
|
|
255
282
|
}
|
|
256
283
|
};
|
|
257
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
|
+
|
|
258
363
|
// Copyright (c) 2025 coze-dev
|
|
259
364
|
// SPDX-License-Identifier: MIT
|
|
260
365
|
|
|
@@ -369,7 +474,12 @@ const publishPackage = async (
|
|
|
369
474
|
: version.includes('beta')
|
|
370
475
|
? 'beta'
|
|
371
476
|
: 'latest';
|
|
372
|
-
const
|
|
477
|
+
const setToken = `npm config set //bnpm.byted.org/:_authToken ${token}`;
|
|
478
|
+
await exec(setToken, {
|
|
479
|
+
cwd: project.projectFolder,
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
const args = [`NODE_AUTH_TOKEN=${token}`, 'npm', 'publish', `--tag ${tag}`];
|
|
373
483
|
if (dryRun) {
|
|
374
484
|
args.push('--dry-run');
|
|
375
485
|
}
|
|
@@ -444,13 +554,16 @@ const calReleasePlan = (releaseManifests) => {
|
|
|
444
554
|
const checkReleasePlan = (
|
|
445
555
|
releaseManifests,
|
|
446
556
|
branchName,
|
|
557
|
+
allowBranches = ['main', 'feat/auto-publish'],
|
|
447
558
|
) => {
|
|
448
559
|
const releasePlan = calReleasePlan(releaseManifests);
|
|
449
560
|
if (
|
|
450
561
|
releasePlan === ReleaseType.LATEST &&
|
|
451
|
-
!
|
|
562
|
+
!allowBranches.includes(branchName)
|
|
452
563
|
) {
|
|
453
|
-
throw new Error(
|
|
564
|
+
throw new Error(
|
|
565
|
+
`For LATEST release, should be on one of these branches: ${allowBranches.join(', ')}. Current Branch: ${branchName}`,
|
|
566
|
+
);
|
|
454
567
|
}
|
|
455
568
|
return true;
|
|
456
569
|
};
|
|
@@ -519,10 +632,32 @@ const getPackagesToPublish = async (
|
|
|
519
632
|
|
|
520
633
|
|
|
521
634
|
async function release(options) {
|
|
522
|
-
const {
|
|
635
|
+
const {
|
|
636
|
+
dryRun = false,
|
|
637
|
+
registry,
|
|
638
|
+
packages,
|
|
639
|
+
allowBranches = DEFAULT_ALLOW_BRANCHES,
|
|
640
|
+
} = options;
|
|
641
|
+
let { commit } = options;
|
|
642
|
+
const hasPassedCommit = !!options.commit;
|
|
523
643
|
|
|
524
644
|
// 1. 获取需要发布的包列表
|
|
525
|
-
|
|
645
|
+
let packagesToPublish;
|
|
646
|
+
if (packages) {
|
|
647
|
+
// 直接使用传入的包列表
|
|
648
|
+
packagesToPublish = packages;
|
|
649
|
+
logger.info('Using provided package list');
|
|
650
|
+
} else {
|
|
651
|
+
// 从 git tags 获取包列表
|
|
652
|
+
if (!hasPassedCommit) {
|
|
653
|
+
commit = await getCurrentCommitHash();
|
|
654
|
+
logger.info('Using current commit');
|
|
655
|
+
}
|
|
656
|
+
// 此时 commit 必定有值(要么传入了,要么刚获取了)
|
|
657
|
+
const commitHash = commit ;
|
|
658
|
+
packagesToPublish = await getPackagesToPublish(commitHash);
|
|
659
|
+
}
|
|
660
|
+
|
|
526
661
|
if (packagesToPublish.length === 0) {
|
|
527
662
|
logger.warn('No packages to publish');
|
|
528
663
|
return;
|
|
@@ -538,8 +673,16 @@ async function release(options) {
|
|
|
538
673
|
false,
|
|
539
674
|
);
|
|
540
675
|
const branchName = await getCurrentBranchName();
|
|
541
|
-
checkReleasePlan(releaseManifests, branchName);
|
|
542
|
-
|
|
676
|
+
checkReleasePlan(releaseManifests, branchName, allowBranches);
|
|
677
|
+
|
|
678
|
+
// 只有在指定了 commit 且与当前 HEAD 不同时才切换
|
|
679
|
+
if (hasPassedCommit) {
|
|
680
|
+
const currentHead = await getCurrentCommitHash();
|
|
681
|
+
if (currentHead !== commit) {
|
|
682
|
+
logger.info(`Checking out commit: ${commit}`);
|
|
683
|
+
await exec(`git checkout ${commit}`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
543
686
|
|
|
544
687
|
await releasePackages(releaseManifests, { dryRun, registry });
|
|
545
688
|
logger.success('All packages published successfully!');
|
|
@@ -555,18 +698,19 @@ const installAction$2 = (program) => {
|
|
|
555
698
|
program
|
|
556
699
|
.command('release')
|
|
557
700
|
.description('Release packages based on git tags.')
|
|
558
|
-
.
|
|
701
|
+
.option('--commit <string>', '需要执行发布的 commit id (默认使用当前 HEAD)')
|
|
559
702
|
.option('--dry-run', '是否只执行不真实发布', false)
|
|
560
703
|
.option(
|
|
561
704
|
'-r, --registry <string>',
|
|
562
|
-
|
|
563
|
-
|
|
705
|
+
`发布到的 registry (默认: ${DEFAULT_NPM_REGISTRY})`,
|
|
706
|
+
DEFAULT_NPM_REGISTRY,
|
|
707
|
+
)
|
|
708
|
+
.option(
|
|
709
|
+
'--allow-branches <branches...>',
|
|
710
|
+
`允许发布正式版本的分支列表 (默认: ${DEFAULT_ALLOW_BRANCHES.join(', ')})`,
|
|
564
711
|
)
|
|
565
712
|
.action(async (options) => {
|
|
566
713
|
try {
|
|
567
|
-
if (!options.commit) {
|
|
568
|
-
throw new Error('请提供需要发布的 commit id');
|
|
569
|
-
}
|
|
570
714
|
if (!process.env.NPM_AUTH_TOKEN) {
|
|
571
715
|
throw new Error('请设置 NPM_AUTH_TOKEN 环境变量');
|
|
572
716
|
}
|
|
@@ -805,31 +949,15 @@ const generatePublishManifest = async (
|
|
|
805
949
|
|
|
806
950
|
|
|
807
951
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
952
|
async function commitChanges({
|
|
812
|
-
sessionId,
|
|
813
953
|
files,
|
|
814
954
|
cwd,
|
|
815
|
-
publishManifests,
|
|
816
955
|
branchName,
|
|
817
|
-
createTags,
|
|
818
956
|
}) {
|
|
819
957
|
await exec(`git add ${files.join(' ')}`, { cwd });
|
|
820
958
|
await exec(`git commit -m "chore: Publish ${branchName}" -n`, { cwd });
|
|
821
959
|
|
|
822
|
-
|
|
823
|
-
if (createTags) {
|
|
824
|
-
tags = publishManifests.map(
|
|
825
|
-
m => `v/${m.project.packageName}@${m.newVersion}`,
|
|
826
|
-
);
|
|
827
|
-
await exec(
|
|
828
|
-
tags.map(tag => `git tag -a ${tag} -m "Bump type ${tag}"`).join(' && '),
|
|
829
|
-
{ cwd },
|
|
830
|
-
);
|
|
831
|
-
}
|
|
832
|
-
return { effects: [...tags, branchName], branchName };
|
|
960
|
+
return { effects: [branchName], branchName };
|
|
833
961
|
}
|
|
834
962
|
|
|
835
963
|
|
|
@@ -859,80 +987,189 @@ async function push({ refs, cwd, repoUrl }) {
|
|
|
859
987
|
|
|
860
988
|
|
|
861
989
|
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* 获取或创建发布分支
|
|
993
|
+
*/
|
|
994
|
+
const getOrCreateBranch = async (params
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
) => {
|
|
1000
|
+
const { bumpPolicy, sessionId, branchPrefix, cwd } = params;
|
|
1001
|
+
|
|
1002
|
+
if (bumpPolicy === BumpType.BETA) {
|
|
1003
|
+
return await getCurrentBranchName();
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const date = dayjs().format('YYYYMMDD');
|
|
1007
|
+
const branchName = `${branchPrefix}/${date}-${sessionId}`;
|
|
1008
|
+
await exec(`git checkout -b ${branchName}`, { cwd });
|
|
1009
|
+
return branchName;
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* 处理 Beta 发布流程:询问并执行 git push
|
|
1014
|
+
*/
|
|
1015
|
+
const handleBetaPublish = async (
|
|
1016
|
+
branchName,
|
|
1017
|
+
cwd,
|
|
1018
|
+
) => {
|
|
1019
|
+
logger.success(`Changes have been committed to branch "${branchName}".`);
|
|
1020
|
+
|
|
1021
|
+
const shouldPush = await prompts.confirm({
|
|
1022
|
+
message: 'Do you want to push the changes now?',
|
|
1023
|
+
default: true,
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
if (shouldPush) {
|
|
1027
|
+
logger.info('Pushing changes to remote...');
|
|
1028
|
+
await exec('git push', { cwd });
|
|
1029
|
+
logger.success('Changes pushed successfully!');
|
|
1030
|
+
} else {
|
|
1031
|
+
logger.info('Please run "git push" manually when you are ready.');
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* 显示手动创建 MR 的提示消息
|
|
1037
|
+
*/
|
|
1038
|
+
const showManualMrMessage = (
|
|
1039
|
+
branchName,
|
|
1040
|
+
repositoryUrl,
|
|
1041
|
+
) => {
|
|
1042
|
+
const baseMessage = `Please create a merge request from branch "${branchName}" to the main branch`;
|
|
1043
|
+
const suffix =
|
|
1044
|
+
'The release will be triggered after the merge request is merged.';
|
|
1045
|
+
|
|
1046
|
+
if (repositoryUrl) {
|
|
1047
|
+
logger.success(
|
|
1048
|
+
`Repository: ${repositoryUrl}\n${baseMessage}.\n${suffix}`,
|
|
1049
|
+
false,
|
|
1050
|
+
);
|
|
1051
|
+
} else {
|
|
1052
|
+
logger.success(`${baseMessage} in your repository.\n${suffix}`);
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* 显示 MR/PR 创建链接并打开浏览器
|
|
1058
|
+
*/
|
|
1059
|
+
const showMrLinkAndOpenBrowser = async (mrUrl) => {
|
|
1060
|
+
const log = [
|
|
1061
|
+
'************************************************',
|
|
1062
|
+
'*',
|
|
1063
|
+
`* Please create MR/PR: ${mrUrl}`,
|
|
1064
|
+
'*',
|
|
1065
|
+
'* The release will be triggered after the MR is merged.',
|
|
1066
|
+
'*',
|
|
1067
|
+
'************************************************',
|
|
1068
|
+
];
|
|
1069
|
+
logger.success(log.join('\n'), false);
|
|
1070
|
+
|
|
1071
|
+
const open = await import('open');
|
|
1072
|
+
await open.default(mrUrl);
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* 处理正式版本发布流程:提示创建 MR/PR
|
|
1077
|
+
*/
|
|
1078
|
+
const handleProductionPublish = async (
|
|
1079
|
+
branchName,
|
|
1080
|
+
cwd,
|
|
1081
|
+
) => {
|
|
1082
|
+
const originUrl = await getCurrentOrigin(cwd);
|
|
1083
|
+
if (!originUrl) {
|
|
1084
|
+
showManualMrMessage(branchName);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
const repoInfo = parseGitRemoteUrl(originUrl);
|
|
1089
|
+
if (!repoInfo) {
|
|
1090
|
+
showManualMrMessage(branchName);
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
const mrUrl = buildMergeRequestUrl(repoInfo, branchName);
|
|
1095
|
+
if (!mrUrl) {
|
|
1096
|
+
showManualMrMessage(branchName, repoInfo.fullUrl);
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
await showMrLinkAndOpenBrowser(mrUrl);
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* 根据发布类型处理后续提示
|
|
1105
|
+
*/
|
|
1106
|
+
const handlePostPushPrompts = async (
|
|
1107
|
+
bumpPolicy,
|
|
1108
|
+
branchName,
|
|
1109
|
+
cwd,
|
|
1110
|
+
) => {
|
|
1111
|
+
const isAlphaPublish = bumpPolicy === BumpType.ALPHA;
|
|
1112
|
+
const isBetaPublish = bumpPolicy === BumpType.BETA;
|
|
1113
|
+
|
|
1114
|
+
// Alpha 发布:不需要提示(会自动触发 CI 发布)
|
|
1115
|
+
if (isAlphaPublish) {
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// Beta 发布:询问用户是否立即执行 git push
|
|
1120
|
+
if (isBetaPublish) {
|
|
1121
|
+
await handleBetaPublish(branchName, cwd);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// 正式版本发布:提示创建 MR/PR
|
|
1126
|
+
await handleProductionPublish(branchName, cwd);
|
|
1127
|
+
};
|
|
1128
|
+
|
|
862
1129
|
const pushToRemote = async (options) => {
|
|
863
1130
|
const {
|
|
864
1131
|
sessionId,
|
|
865
1132
|
changedFiles,
|
|
866
1133
|
cwd,
|
|
867
|
-
publishManifests,
|
|
868
1134
|
bumpPolicy,
|
|
869
1135
|
skipCommit,
|
|
870
1136
|
skipPush,
|
|
871
1137
|
repoUrl,
|
|
1138
|
+
branchPrefix = 'release',
|
|
872
1139
|
} = options;
|
|
1140
|
+
|
|
873
1141
|
if (skipCommit) {
|
|
874
1142
|
return;
|
|
875
1143
|
}
|
|
876
1144
|
|
|
877
|
-
//
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
}
|
|
884
|
-
const date = dayjs().format('YYYYMMDD');
|
|
885
|
-
branchName = `release/${date}-${sessionId}`;
|
|
886
|
-
await exec(`git checkout -b ${branchName}`, { cwd });
|
|
887
|
-
}
|
|
888
|
-
const isTestPublish = [BumpType.ALPHA, BumpType.BETA].includes(
|
|
889
|
-
bumpPolicy ,
|
|
890
|
-
);
|
|
1145
|
+
// 1. 获取或创建发布分支
|
|
1146
|
+
const branchName = await getOrCreateBranch({
|
|
1147
|
+
bumpPolicy,
|
|
1148
|
+
sessionId,
|
|
1149
|
+
branchPrefix,
|
|
1150
|
+
cwd,
|
|
1151
|
+
});
|
|
891
1152
|
|
|
892
|
-
//
|
|
1153
|
+
// 2. 提交变更
|
|
893
1154
|
const { effects } = await commitChanges({
|
|
894
|
-
sessionId,
|
|
895
1155
|
files: changedFiles,
|
|
896
1156
|
cwd,
|
|
897
|
-
publishManifests,
|
|
898
1157
|
branchName,
|
|
899
|
-
// 只有 alpha、beta 需要创建 tag,正式发布会在 .github/workflows/common-pr-checks.yml 创建并发布tag
|
|
900
|
-
createTags: isTestPublish,
|
|
901
1158
|
});
|
|
1159
|
+
|
|
902
1160
|
if (skipPush) {
|
|
903
1161
|
return;
|
|
904
1162
|
}
|
|
1163
|
+
|
|
1164
|
+
// 3. 推送变更
|
|
905
1165
|
await push({
|
|
906
1166
|
refs: effects,
|
|
907
1167
|
cwd,
|
|
908
|
-
repoUrl
|
|
1168
|
+
repoUrl,
|
|
909
1169
|
});
|
|
910
1170
|
|
|
911
|
-
//
|
|
912
|
-
|
|
913
|
-
if (!repoInfoMatch) {
|
|
914
|
-
throw new Error('Invalid git repository URL');
|
|
915
|
-
}
|
|
916
|
-
const repoOwner = repoInfoMatch[1];
|
|
917
|
-
const repoName = repoInfoMatch[2];
|
|
918
|
-
|
|
919
|
-
if (isTestPublish) {
|
|
920
|
-
logger.success(
|
|
921
|
-
`Please refer to https://github.com/${repoOwner}/${repoName}/actions/workflows/release.yml for the release progress.`,
|
|
922
|
-
);
|
|
923
|
-
} else {
|
|
924
|
-
const prUrl = `https://github.com/${repoOwner}/${repoName}/compare/${branchName}?expand=1`;
|
|
925
|
-
const log = [
|
|
926
|
-
'************************************************',
|
|
927
|
-
'*',
|
|
928
|
-
`* Please create PR: ${prUrl}`,
|
|
929
|
-
'*',
|
|
930
|
-
'************************************************',
|
|
931
|
-
];
|
|
932
|
-
logger.success(log.join('\n'), false);
|
|
933
|
-
const open = await import('open');
|
|
934
|
-
await open.default(prUrl);
|
|
935
|
-
}
|
|
1171
|
+
// 4. 根据发布类型显示不同提示
|
|
1172
|
+
await handlePostPushPrompts(bumpPolicy, branchName, cwd);
|
|
936
1173
|
};
|
|
937
1174
|
|
|
938
1175
|
// Copyright (c) 2025 coze-dev
|
|
@@ -1034,7 +1271,7 @@ const lookupOnly = (packageName) => {
|
|
|
1034
1271
|
return projects[0];
|
|
1035
1272
|
};
|
|
1036
1273
|
|
|
1037
|
-
function _optionalChain$
|
|
1274
|
+
function _optionalChain$2(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; }
|
|
1038
1275
|
|
|
1039
1276
|
|
|
1040
1277
|
|
|
@@ -1088,7 +1325,7 @@ const retrievePackages = (
|
|
|
1088
1325
|
|
|
1089
1326
|
const validateAndGetPackages = (options) => {
|
|
1090
1327
|
const retrievePatterns = Object.values(RetrievePattern);
|
|
1091
|
-
if (retrievePatterns.every(pattern => (_optionalChain$
|
|
1328
|
+
if (retrievePatterns.every(pattern => (_optionalChain$2([options, 'access', _ => _[pattern], 'optionalAccess', _2 => _2.length]) || 0) <= 0)) {
|
|
1092
1329
|
throw new Error('No packages to publish');
|
|
1093
1330
|
}
|
|
1094
1331
|
const res = retrievePatterns.reduce((acc, pattern) => {
|
|
@@ -1114,8 +1351,11 @@ const validateAndGetPackages = (options) => {
|
|
|
1114
1351
|
return result;
|
|
1115
1352
|
};
|
|
1116
1353
|
|
|
1117
|
-
// Copyright (c) 2025 coze-dev
|
|
1118
|
-
|
|
1354
|
+
function _optionalChain$1(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; }// Copyright (c) 2025 coze-dev
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
|
|
1358
|
+
|
|
1119
1359
|
|
|
1120
1360
|
|
|
1121
1361
|
|
|
@@ -1123,20 +1363,33 @@ const validateAndGetPackages = (options) => {
|
|
|
1123
1363
|
const confirmForPublish = async (
|
|
1124
1364
|
publishManifest,
|
|
1125
1365
|
dryRun,
|
|
1366
|
+
options,
|
|
1126
1367
|
) => {
|
|
1127
|
-
|
|
1368
|
+
logger.info(chalk.gray('Will publish the following packages:'), false);
|
|
1128
1369
|
publishManifest.forEach(manifest => {
|
|
1129
|
-
const
|
|
1130
|
-
|
|
1370
|
+
const versionChange = `${manifest.currentVersion} -> ${chalk.bold(manifest.newVersion)}`;
|
|
1371
|
+
const msg = `${manifest.project.packageName}: ${chalk.bgGreen(versionChange)}`;
|
|
1372
|
+
logger.info(`- ${msg}`, false);
|
|
1131
1373
|
});
|
|
1374
|
+
|
|
1375
|
+
// Release 模式的额外提示
|
|
1376
|
+
if (_optionalChain$1([options, 'optionalAccess', _ => _.isReleaseMode])) {
|
|
1377
|
+
logger.info('', false);
|
|
1378
|
+
logger.warn(chalk.yellow.bold('⚠️ Release Mode Enabled:'), false);
|
|
1379
|
+
const registryMsg = ` Packages will be published directly to: ${chalk.bold(options.registry || 'default registry')}`;
|
|
1380
|
+
logger.warn(chalk.yellow(registryMsg), false);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1132
1383
|
if (dryRun) {
|
|
1133
1384
|
return false;
|
|
1134
1385
|
}
|
|
1135
1386
|
|
|
1136
|
-
|
|
1387
|
+
logger.info('', false);
|
|
1137
1388
|
try {
|
|
1138
1389
|
const result = await prompts.confirm({
|
|
1139
|
-
message: '
|
|
1390
|
+
message: _optionalChain$1([options, 'optionalAccess', _2 => _2.isReleaseMode])
|
|
1391
|
+
? 'Are you sure to publish directly?'
|
|
1392
|
+
: 'Are you sure to publish?',
|
|
1140
1393
|
default: true,
|
|
1141
1394
|
});
|
|
1142
1395
|
return result;
|
|
@@ -1500,7 +1753,10 @@ const publish = async (options) => {
|
|
|
1500
1753
|
const sessionId = randomHash(6);
|
|
1501
1754
|
const rushConfiguration = getRushConfiguration$1();
|
|
1502
1755
|
const rushFolder = rushConfiguration.rushJsonFolder;
|
|
1503
|
-
if (
|
|
1756
|
+
if (
|
|
1757
|
+
process.env.SKIP_UNCOMMITTED_CHECK !== 'true' &&
|
|
1758
|
+
options.release !== true
|
|
1759
|
+
) {
|
|
1504
1760
|
await ensureNotUncommittedChanges();
|
|
1505
1761
|
}
|
|
1506
1762
|
|
|
@@ -1525,7 +1781,11 @@ const publish = async (options) => {
|
|
|
1525
1781
|
const isBetaPublish = [BumpType.BETA, BumpType.ALPHA].includes(
|
|
1526
1782
|
bumpPolicy ,
|
|
1527
1783
|
);
|
|
1528
|
-
if (
|
|
1784
|
+
if (
|
|
1785
|
+
process.env.SKIP_BRANCH_CHECK !== 'true' &&
|
|
1786
|
+
isBetaPublish === false &&
|
|
1787
|
+
(await isMainBranch()) === false
|
|
1788
|
+
) {
|
|
1529
1789
|
// 只允许在主分支发布
|
|
1530
1790
|
logger.error(
|
|
1531
1791
|
'You are not in main branch, please switch to main branch and try again.',
|
|
@@ -1536,6 +1796,10 @@ const publish = async (options) => {
|
|
|
1536
1796
|
const continuePublish = await confirmForPublish(
|
|
1537
1797
|
publishManifests,
|
|
1538
1798
|
!!options.dryRun,
|
|
1799
|
+
{
|
|
1800
|
+
isReleaseMode: !!options.release,
|
|
1801
|
+
registry: options.registry || DEFAULT_NPM_REGISTRY,
|
|
1802
|
+
},
|
|
1539
1803
|
);
|
|
1540
1804
|
|
|
1541
1805
|
if (!continuePublish) {
|
|
@@ -1551,17 +1815,46 @@ const publish = async (options) => {
|
|
|
1551
1815
|
await Promise.all(postHandles.map(handle => handle(publishManifests)))
|
|
1552
1816
|
).flat();
|
|
1553
1817
|
|
|
1554
|
-
// 4. 创建并推送发布分支
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1818
|
+
// 4. 创建并推送发布分支 或 直接发布
|
|
1819
|
+
let shouldRelease = false;
|
|
1820
|
+
if (options.release) {
|
|
1821
|
+
// 验证 release 模式的前置条件
|
|
1822
|
+
if (isBetaPublish === false) {
|
|
1823
|
+
logger.error(
|
|
1824
|
+
'Direct release (--release) is only allowed for alpha or beta versions.',
|
|
1825
|
+
);
|
|
1826
|
+
logger.error(`Current bump type is: ${bumpPolicy}`);
|
|
1827
|
+
logger.warn('Falling back to normal publish mode...');
|
|
1828
|
+
} else {
|
|
1829
|
+
shouldRelease = true;
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
if (shouldRelease) {
|
|
1834
|
+
// Release 模式:直接发布
|
|
1835
|
+
logger.info('Running in direct release mode...');
|
|
1836
|
+
logger.info('Starting package release...');
|
|
1837
|
+
const registry = options.registry || DEFAULT_NPM_REGISTRY;
|
|
1838
|
+
// 将 PublishManifest[] 转换为 PackageToPublish[]
|
|
1839
|
+
const packages = publishManifests.map(manifest => ({
|
|
1840
|
+
packageName: manifest.project.packageName,
|
|
1841
|
+
version: manifest.newVersion,
|
|
1842
|
+
}));
|
|
1843
|
+
await release({ dryRun: !!options.dryRun, registry, packages });
|
|
1844
|
+
} else {
|
|
1845
|
+
// 普通模式:创建并推送发布分支
|
|
1846
|
+
await pushToRemote({
|
|
1847
|
+
bumpPolicy: bumpPolicy ,
|
|
1848
|
+
sessionId,
|
|
1849
|
+
changedFiles,
|
|
1850
|
+
cwd: rushFolder,
|
|
1851
|
+
skipCommit: !!options.skipCommit,
|
|
1852
|
+
skipPush: !!options.skipPush,
|
|
1853
|
+
repoUrl: options.repoUrl,
|
|
1854
|
+
branchPrefix: options.branchPrefix,
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1565
1858
|
logger.success('Publish success.');
|
|
1566
1859
|
};
|
|
1567
1860
|
|
|
@@ -1592,6 +1885,21 @@ const installAction$1 = (program) => {
|
|
|
1592
1885
|
'Git repository URL (e.g. git@github.com:coze-dev/coze-js.git)',
|
|
1593
1886
|
undefined,
|
|
1594
1887
|
)
|
|
1888
|
+
.option(
|
|
1889
|
+
'--branch-prefix <prefix>',
|
|
1890
|
+
`Git branch name prefix (default: ${DEFAULT_BRANCH_PREFIX})`,
|
|
1891
|
+
DEFAULT_BRANCH_PREFIX,
|
|
1892
|
+
)
|
|
1893
|
+
.option(
|
|
1894
|
+
'-l, --release',
|
|
1895
|
+
'Directly publish packages (only for alpha/beta versions)',
|
|
1896
|
+
false,
|
|
1897
|
+
)
|
|
1898
|
+
.option(
|
|
1899
|
+
'--registry <url>',
|
|
1900
|
+
`NPM registry URL (default: ${DEFAULT_NPM_REGISTRY})`,
|
|
1901
|
+
DEFAULT_NPM_REGISTRY,
|
|
1902
|
+
)
|
|
1595
1903
|
.action(async (options) => {
|
|
1596
1904
|
try {
|
|
1597
1905
|
const repoUrl = options.repoUrl || (await getCurrentOrigin());
|
|
@@ -1599,8 +1907,9 @@ const installAction$1 = (program) => {
|
|
|
1599
1907
|
throw new Error('Git repository URL is required');
|
|
1600
1908
|
}
|
|
1601
1909
|
if (!GIT_REPO_URL_REGEX.test(repoUrl)) {
|
|
1910
|
+
const expectedFormat = 'git@github.com:${org}/${repo}.git';
|
|
1602
1911
|
throw new Error(
|
|
1603
|
-
`Invalid git repository URL: ${repoUrl}, it should
|
|
1912
|
+
`Invalid git repository URL: ${repoUrl}, it should follow the format: ${expectedFormat}`,
|
|
1604
1913
|
);
|
|
1605
1914
|
}
|
|
1606
1915
|
const normalizeOptions = {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface IPublishPluginConfig {
|
|
2
|
+
branchPrefix?: string;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Read plugin configuration from common/config/rush-plugins/@coze-arch/rush-publish-plugin.json
|
|
6
|
+
* @returns Plugin configuration object
|
|
7
|
+
*/
|
|
8
|
+
export declare function getPluginConfig(): IPublishPluginConfig;
|
package/lib/utils/git.d.ts
CHANGED
|
@@ -4,6 +4,11 @@ export declare const getChangedFilesFromCached: () => Promise<string[]>;
|
|
|
4
4
|
* @returns string
|
|
5
5
|
*/
|
|
6
6
|
export declare const getCurrentBranchName: () => Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* 获取当前 commit hash
|
|
9
|
+
* @returns commit hash
|
|
10
|
+
*/
|
|
11
|
+
export declare const getCurrentCommitHash: () => Promise<string>;
|
|
7
12
|
export declare const isMainBranch: () => Promise<boolean>;
|
|
8
13
|
export declare const getChangedFiles: () => Promise<string[]>;
|
|
9
14
|
/**
|
|
@@ -17,3 +22,25 @@ export declare const ensureNotUncommittedChanges: () => Promise<boolean>;
|
|
|
17
22
|
*/
|
|
18
23
|
export declare const getCurrentOrigin: (cwd?: string) => Promise<string | undefined>;
|
|
19
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;
|