@wjwjq/release-helper 0.0.1

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.
Files changed (71) hide show
  1. package/.prettierignore +13 -0
  2. package/.prettierrc.json +19 -0
  3. package/README.md +29 -0
  4. package/bin/index.js +7 -0
  5. package/dist/cli.js +56 -0
  6. package/dist/deploy/nginx/README.md +1 -0
  7. package/dist/deploy/nginx/pkg/nginx-arm-ssl1.1.1.tar.gz +0 -0
  8. package/dist/deploy/nginx/pkg/nginx-x86_64-ssl1.0.2.tar.gz +0 -0
  9. package/dist/deploy/nginx/pkg/nginx-x86_64-ssl1.1.1.tar.gz +0 -0
  10. package/dist/deploy/nginx/pkg/nginx-x86_64-ssl3.0.7.tar.gz +0 -0
  11. package/dist/deploy/nginx/script/common.sh +178 -0
  12. package/dist/deploy/nginx/script/compile.sh +11 -0
  13. package/dist/deploy/nginx/script/fix.sh +24 -0
  14. package/dist/deploy/nginx/script/https.sh +79 -0
  15. package/dist/deploy/nginx/script/install.sh +99 -0
  16. package/dist/deploy/nginx/script/prompt.sh +10 -0
  17. package/dist/deploy/nginx/script/upgrade.sh +7 -0
  18. package/dist/deploy/pkg/nginx/nginx.service.tpl +33 -0
  19. package/dist/deploy/script/common.sh +183 -0
  20. package/dist/deploy/script/install.sh +51 -0
  21. package/dist/deploy/script/nginx.sh +156 -0
  22. package/dist/deploy/script/prompt.sh +98 -0
  23. package/dist/deploy/script/readme.md +10 -0
  24. package/dist/deploy/script/upgrade.sh +50 -0
  25. package/dist/logger.js +20 -0
  26. package/dist/pack.js +111 -0
  27. package/dist/prepare.js +75 -0
  28. package/dist/publish.js +238 -0
  29. package/dist/release.js +200 -0
  30. package/package.json +71 -0
  31. package/rollup.config.js +78 -0
  32. package/src/.release/README.md +23 -0
  33. package/src/.release/doc//351/203/250/347/275/262/346/211/213/345/206/214.md +426 -0
  34. package/src/.release/nginx/ca/ca.crt +32 -0
  35. package/src/.release/nginx/ca/ca.key +54 -0
  36. package/src/.release/nginx/ca/client.crt +100 -0
  37. package/src/.release/nginx/ca/client.csr +16 -0
  38. package/src/.release/nginx/ca/client.p12 +0 -0
  39. package/src/.release/nginx/ca/client.pem +30 -0
  40. package/src/.release/nginx/ca/server.crt +101 -0
  41. package/src/.release/nginx/ca/server.csr +17 -0
  42. package/src/.release/nginx/ca/server.key +27 -0
  43. package/src/.release/nginx/ca/server.pem +30 -0
  44. package/src/.release/nginx/nginx.conf +179 -0
  45. package/src/.release/release.conf.yaml +9 -0
  46. package/src/cli.ts +99 -0
  47. package/src/deploy/nginx/README.md +1 -0
  48. package/src/deploy/nginx/pkg/nginx-arm-ssl1.1.1.tar.gz +0 -0
  49. package/src/deploy/nginx/pkg/nginx-x86_64-ssl1.0.2.tar.gz +0 -0
  50. package/src/deploy/nginx/pkg/nginx-x86_64-ssl1.1.1.tar.gz +0 -0
  51. package/src/deploy/nginx/pkg/nginx-x86_64-ssl3.0.7.tar.gz +0 -0
  52. package/src/deploy/nginx/script/common.sh +178 -0
  53. package/src/deploy/nginx/script/compile.sh +11 -0
  54. package/src/deploy/nginx/script/fix.sh +24 -0
  55. package/src/deploy/nginx/script/https.sh +79 -0
  56. package/src/deploy/nginx/script/install.sh +99 -0
  57. package/src/deploy/nginx/script/prompt.sh +10 -0
  58. package/src/deploy/nginx/script/upgrade.sh +7 -0
  59. package/src/deploy/pkg/nginx/nginx.service.tpl +33 -0
  60. package/src/deploy/script/common.sh +183 -0
  61. package/src/deploy/script/install.sh +51 -0
  62. package/src/deploy/script/nginx.sh +156 -0
  63. package/src/deploy/script/prompt.sh +98 -0
  64. package/src/deploy/script/readme.md +10 -0
  65. package/src/deploy/script/upgrade.sh +50 -0
  66. package/src/logger.ts +18 -0
  67. package/src/pack.ts +141 -0
  68. package/src/prepare.ts +105 -0
  69. package/src/publish.ts +308 -0
  70. package/src/release.ts +292 -0
  71. package/tsconfig.json +14 -0
package/src/publish.ts ADDED
@@ -0,0 +1,308 @@
1
+ import fs from "fs"
2
+ import path from "path"
3
+ import { URL } from "url"
4
+ import crypto from "crypto"
5
+ import mime from 'mime'
6
+ import * as inquirer from "@inquirer/prompts"
7
+ import { setTimeout } from 'node:timers/promises';
8
+
9
+ import { Gitlab } from "@gitbeaker/rest"
10
+
11
+ import { __releaseDir, releaseConf, $ } from './prepare'
12
+ import { logger } from "./logger"
13
+
14
+ export async function isGitFlowRepo() {
15
+ try {
16
+ let { stdout } = await $('git config', ['--get-regexp', `^gitflow\.`])
17
+ return stdout.toString().length > 0;
18
+ } catch (error) {
19
+ console.error("🚀 ~ doGitFlowRelease ~ error:", error)
20
+ }
21
+
22
+ return false;
23
+ }
24
+
25
+ async function getRepo() {
26
+ const res = await $`git remote -v `.pipe` sed -n '1p'`.pipe`awk -F ' ' '{print $2}'`.pipe`sed -e 's/.git//'`;
27
+ const repo = res.stdout.replace("\n", "");
28
+
29
+ if (repo.startsWith("git@")) {
30
+ const ps = repo.split("/");
31
+ return {
32
+ isHttpRepo: false,
33
+ repo: repo.split(":")[1],
34
+ repoName: ps[ps.length - 1].replace(".git", "")
35
+ };
36
+ }
37
+
38
+ const url = new URL(repo);
39
+ const ps = url.pathname.split("/");
40
+
41
+ return {
42
+ isHttpRepo: true,
43
+ repo: url.pathname,
44
+ origin: url.origin,
45
+ repoName: ps[ps.length - 1]
46
+ };
47
+ }
48
+
49
+ async function getChangeLog(host: string, newVersion: string, userSelectedLatestTag: string, markdownUrls: { tar: string, md5: string, doc: string }) {
50
+ async function getLog() {
51
+ let output = "";
52
+ try {
53
+ /**
54
+ * 判断是否为gitflow
55
+ *
56
+ * --> 是 获取最近两次tag 之间的commit记录, 若不足两次,则获取最新一次往前的所有记录
57
+ * --> 否 获取最近一次tag 之后的commit记录
58
+ */
59
+
60
+ const isGitFlow = await isGitFlowRepo();
61
+
62
+ const { stdout } = await $`git tag --sort=-creatordate`;
63
+ const tags = stdout.split('\n');
64
+ const latestTag = tags[0]
65
+ let tagRange = '';
66
+ if (isGitFlow) {
67
+ if (tags.length >= 2) {
68
+ const secondLastTag = userSelectedLatestTag !== '' ? userSelectedLatestTag : tags[1]
69
+ tagRange = `${secondLastTag}..${latestTag}`
70
+ } else if (tags.length === 1) {
71
+ tagRange = latestTag;
72
+ }
73
+ } else {
74
+ if (tags.length >= 1) {
75
+ tagRange = `${latestTag}..HEAD`;
76
+ }
77
+ }
78
+
79
+ let logCmd = `git log ${tagRange} --format=%B%H----DELIMITER---- --abbrev-commit`;
80
+ const { stdout: r } = await $(logCmd);
81
+ output = r;
82
+ } catch (error) {
83
+ console.error("🚀 ~ getLog ~ error:", error)
84
+ const { stdout: r } = await $`git log --format=%B%H----DELIMITER----`;
85
+ output = r;
86
+ }
87
+
88
+ return output;
89
+ }
90
+
91
+ const output = await getLog();
92
+
93
+ const commitsArray = output
94
+ .split("----DELIMITER----\n")
95
+ .map(commit => {
96
+ const [message, sha] = commit.split("\n").filter(msg => msg != "" && !msg.startsWith("Merge"));
97
+ return { sha, message };
98
+ })
99
+ .filter(commit => Boolean(commit.sha));
100
+
101
+ let currentChangelog = "";
102
+
103
+ // if (fs.existsSync(path.resolve(__dirname, "../CHANGELOG.md"))) {
104
+ // currentChangelog = fs.readFileSync(path.resolve(__dirname, "../CHANGELOG.md"), "utf-8");
105
+ // }
106
+
107
+ const { repo } = await getRepo();
108
+
109
+ // 用version和时间作为release 标记
110
+ let newChangelog = ``;// `# ${newVersion} (${new Date().toISOString().split("T")[0]})\n\n`;
111
+
112
+ let features: string[] = [];
113
+ let Bugfixes: string[] = [];
114
+
115
+ // 分别维护features和bugfixes的内容,并将message和commit的链接进行绑定
116
+ commitsArray.forEach(commit => {
117
+ const commitSha = `([${commit.sha.substring(0, 6)}](${host}/${repo}/-/commit/${commit.sha}))`;
118
+
119
+ if (commit.message.startsWith("feat:") || commit.message.startsWith("feat:")) {
120
+ const feats = commit.message.replace(/feat[:|:]/, "").replace(/;|;/g, "$").split("$");
121
+ features = features.concat(feats.map(f => `* ${f.replace(/^\s+/, "")} ${commitSha}\n`,));
122
+
123
+ }
124
+ if (commit.message.startsWith("fix:") || commit.message.startsWith("fix:")) {
125
+ const fixes = commit.message.replace(/fix[:|:]/, "").replace(/;|;/g, "$").split("$");
126
+ Bugfixes = Bugfixes.concat(fixes.map(f => `* ${f.replace(/^\s+/, "")} ${commitSha}\n`,));
127
+ }
128
+ });
129
+
130
+ if (features.length) {
131
+ newChangelog += `# Features\n`;
132
+ features.forEach(feature => {
133
+ newChangelog += feature;
134
+ });
135
+ newChangelog += "\n";
136
+ }
137
+
138
+ if (Bugfixes.length) {
139
+ newChangelog += `# Bugfixes\n`;
140
+ Bugfixes.forEach(bugfix => {
141
+ newChangelog += bugfix;
142
+ });
143
+ newChangelog += "\n";
144
+ }
145
+ console.log("🚀 ~ getChangeLog ~features & Bugfixes:", features, Bugfixes)
146
+
147
+ // add attach
148
+ newChangelog += `# Artifacts\n`;
149
+ newChangelog += `## tar包\n`;
150
+ newChangelog += `${markdownUrls.tar}\n`;
151
+
152
+ newChangelog += `## md5\n`;
153
+ newChangelog += `${markdownUrls.md5}\n`;
154
+
155
+ newChangelog += `## 文档\n`;
156
+ newChangelog += `${markdownUrls.doc}\n`;
157
+
158
+
159
+ // prepend the newChangelog to the current one
160
+ return `${newChangelog}${currentChangelog}`;
161
+ }
162
+
163
+ export async function getGitTags(){
164
+ const { stdout } = await $`git tag --sort=-creatordate`;
165
+ return stdout.split('\n');
166
+ }
167
+
168
+ export async function selectLatestTag(targetVersion: string){
169
+ let latestTag = '';
170
+ const tags = await getGitTags();
171
+
172
+ if(tags.length >= 2){
173
+ const ac = new AbortController();
174
+ const prompt = inquirer
175
+ .select(
176
+ {
177
+ message: '请选择发布信息从哪次tag开始生成,(默认: 最近一次,特别针对某主版本之间有小版本过渡的情况)(timing out in 10 seconds):',
178
+ default: tags[0],
179
+ choices: tags.filter(item => item !== targetVersion).map(tag => ({ value: tag})),
180
+ },
181
+ )
182
+ prompt
183
+ .finally(() => {
184
+ ac.abort();
185
+ })
186
+ // Silencing the cancellation error.
187
+ .catch(() => {});
188
+
189
+ const defaultValue = setTimeout(10000, 'timeout', { signal: ac.signal }).then(() => {
190
+ prompt.cancel();
191
+ return tags[0];
192
+ });
193
+
194
+ latestTag = await Promise.race([defaultValue, prompt]);
195
+ }
196
+
197
+ return latestTag
198
+ }
199
+
200
+ export async function publish(params: {
201
+ version: string;
202
+ pkgName: string;
203
+ appName: string;
204
+ remoteBranch: string;
205
+ userSelectedLatestTag: string
206
+ }) {
207
+ const {
208
+ version,
209
+ pkgName,
210
+ appName,
211
+ remoteBranch,
212
+ userSelectedLatestTag = ''
213
+ } = params || {};
214
+
215
+ try {
216
+ const { repo } = await getRepo();
217
+
218
+ const { host, token } = releaseConf;
219
+
220
+ logger.tip('start to publish');
221
+
222
+ const api = new Gitlab({
223
+ host,
224
+ token,
225
+ rejectUnauthorized: false
226
+ });
227
+
228
+ const projects = await api.Projects.all();
229
+
230
+ const proj = projects.find(item => item.path_with_namespace === repo);
231
+
232
+ if (proj === undefined) {
233
+ throw Error(`The target repo not founded!`);
234
+ }
235
+
236
+ const id = proj.id;
237
+
238
+ const assetFile = path.resolve(__releaseDir, pkgName);
239
+ const file = fs.readFileSync(assetFile);
240
+
241
+ const hash = crypto.createHash("md5");
242
+ hash.update(file);
243
+ const md5 = hash.digest("hex");
244
+ const md5FileName = `${appName}_${version}.md5.txt`;
245
+
246
+ // 读取doc目录下所有文档并上传
247
+ const docPath = path.join(__releaseDir, "doc")
248
+ // 替换文件档中的变量
249
+ const replacedConstants = [["\\$APP_NAME\\$", appName], ["\\$VERSION\\$", version], ["\\$TAR_PKG\\$", pkgName]];
250
+
251
+ const docUploadsPromises = fs.readdirSync(docPath)
252
+ .map(docFIleName => {
253
+ const docFilePath = path.resolve(docPath, docFIleName)
254
+ const stats = fs.statSync(docFilePath)
255
+ if (stats.isFile()) {
256
+ let content = fs.readFileSync(docFilePath).toString();
257
+
258
+ const doc = replacedConstants.reduce((prev, [reg, value]) => {
259
+ return prev.replace(new RegExp(reg, 'g'), value);
260
+ }, content)
261
+
262
+ return api.Projects.uploadForReference(id, { content: new Blob([doc], { type: mime.getType(docFIleName)! }), filename: docFIleName })
263
+ }
264
+ }).filter(item => item)
265
+
266
+ logger.info('start to upload resources')
267
+ const [tarUploads, md5Uploads, ...docUploads] = await Promise.all([
268
+ api.Projects.uploadForReference(id, { content: new Blob([file], { type: 'application/gzip' }), filename: pkgName }),
269
+ api.Projects.uploadForReference(id, { content: new Blob([md5], { type: 'text/plain' }), filename: md5FileName }),
270
+ ...docUploadsPromises,
271
+ ]);
272
+
273
+ const description = await getChangeLog(host, version, userSelectedLatestTag, { tar: tarUploads.markdown, md5: md5Uploads.markdown, doc: docUploads.map(item => item!.markdown).join('\n') });
274
+
275
+ logger.tip("start tag and release to git repo")
276
+
277
+ const tags = await api.Tags.all(id);
278
+ if (tags.find(r => r.name === version) === undefined) {
279
+ await api.Tags.create(id, version, remoteBranch);
280
+ }
281
+
282
+ const releases = await api.ProjectReleases.all(id);
283
+ let res;
284
+ if (releases.find(r => r.name === version)) {
285
+ res = await api.ProjectReleases.edit(id, version, {
286
+ description
287
+ });
288
+ } else {
289
+ res = await api.ProjectReleases.create(id, {
290
+ tag_name: version,
291
+ description
292
+ });
293
+ }
294
+
295
+ // 通过assets api 发布 artifacts
296
+ await api.ReleaseLinks.create(id, version, pkgName, `${host}${tarUploads.full_path}`, { linkType: "package" });
297
+ await api.ReleaseLinks.create(id, version, md5FileName, `${host}${md5Uploads.full_path}`, { linkType: "other" });
298
+ await Promise.all(docUploads.map(doc => api.ReleaseLinks.create(id, version, doc!.alt, `${host}${doc!.full_path}`, { linkType: "other" })));
299
+
300
+ fs.unlinkSync(assetFile);
301
+
302
+ logger.success(`Successfully released at: ${res._links.self} `)
303
+ } catch (error) {
304
+ logger.error("🚀 ~ file: publish.js ~ error:")
305
+ console.error(error);
306
+ return Promise.reject(error)
307
+ }
308
+ }
package/src/release.ts ADDED
@@ -0,0 +1,292 @@
1
+ import * as inquirer from "@inquirer/prompts"
2
+ import process from "process"
3
+ import pc from "picocolors"
4
+
5
+ import { pack } from './pack'
6
+ import { getGitTags, isGitFlowRepo, publish, selectLatestTag } from './publish'
7
+
8
+ import { __releaseDir, releaseConf, $, checkEnvInfo } from './prepare'
9
+ import { ResultPromise } from "execa"
10
+
11
+ import { logger } from './logger';
12
+
13
+ export async function release(fromPublishCmd = false) {
14
+ try {
15
+ await checkEnvInfo();
16
+
17
+ await hasUncommittedChanges();
18
+
19
+ // 保存当前分支信息,打包发布完成后切回当前分支
20
+ let { stdout: originalBranch } = await $(`git branch --show-current`)
21
+
22
+ /**
23
+ *
24
+ * a. 指定版本号tag
25
+ *
26
+ * b. 是否遵循git flow 发布流程,
27
+ *
28
+ * 1. 是 --> 测是否是develop分支,
29
+ *
30
+ * --> 是 同步develop,走git flow release到master, 并从远程master 切tag 打包并上传
31
+ * --> 否 检测是否为release 分支,
32
+ * --> 是, 走git flow release 到master, 并从远程master 切tag 打包并上传
33
+ * --> 提示,分支错误
34
+ * 2. 否 --> 选择指定的远程分支,
35
+ * c. 选择从哪个分支生成release msgs
36
+ * --> 默认最新发布分支
37
+ * --> 自定义
38
+ * d. 从指定分支或master 并打tag 和打包并上传
39
+ */
40
+
41
+ // 获取版本号
42
+ let version = await inquirer
43
+ .input({
44
+ message: '请输入版本号:',
45
+ },)
46
+
47
+ if (version == '') {
48
+ logger.error('版本号不能为空!')
49
+ return
50
+ }
51
+ // 补齐v开头 如果是数字开头
52
+ if (/^\d/.test(version)) {
53
+ version = `v${version}`
54
+ }
55
+
56
+ // 版本号检测
57
+ const tags = await getGitTags();
58
+ if (tags.some(tag => tag === version)) {
59
+ logger.error('版本号已存在,请更换版本号!')
60
+ return;
61
+ }
62
+
63
+
64
+ let remoteTargetBranch = 'master';
65
+ if (fromPublishCmd) {
66
+ remoteTargetBranch = await doCustomSpecifyBranchRelease()
67
+ } else {
68
+ const releaseWay = await inquirer
69
+ .select(
70
+ {
71
+ message: '请选择发布方式:',
72
+ default: '1',
73
+ choices: [
74
+ {
75
+ name: 'Git flow流程发布',
76
+ value: '1',
77
+ description: '遵循Git flow流程,需保证仓库已初始化为git flow模式, 以master为远程分支,打Tag',
78
+ },
79
+ {
80
+ name: '自定义分支打包发布',
81
+ value: '2',
82
+ description: '自定义指定远程分支,打Tag的方式',
83
+ },
84
+ ]
85
+ },
86
+ )
87
+
88
+ if (releaseWay === '1') {
89
+ remoteTargetBranch = await doGitFlowRelease(version);
90
+ } else if (releaseWay === '2') {
91
+ remoteTargetBranch = await doCustomSpecifyBranchRelease()
92
+ } else {
93
+ logger.error(`发布方式不能为空`)
94
+ return
95
+ }
96
+
97
+ if (remoteTargetBranch == null) {
98
+ logger.error(`目标发布分支不能为空`)
99
+ return
100
+ }
101
+ }
102
+
103
+ logger.tip(`start build and pack`)
104
+
105
+ // 切换到指定分支 同步remote后 打包
106
+ await execGitCheckout(remoteTargetBranch);
107
+
108
+ await execGitPullOrigin(remoteTargetBranch);
109
+
110
+ // 选择从哪个tag开始生成release 信息
111
+ const userSelectedLatestTag = await selectLatestTag(version);
112
+
113
+ await execWithPipe(releaseConf.buildCmd)
114
+
115
+ const source = await pack(version);
116
+
117
+ if (source === undefined) {
118
+ return
119
+ }
120
+
121
+
122
+ await publish({ ...source, remoteBranch: remoteTargetBranch, userSelectedLatestTag });
123
+
124
+ logger.info(pc.green('done'))
125
+
126
+ // 切换回起始分支
127
+ await execGitCheckout(originalBranch)
128
+ } catch (error) {
129
+ console.error('Error:', error);
130
+ }
131
+ }
132
+
133
+
134
+ async function doGitFlowRelease(version: string) {
135
+ // 检查 .git/config 中是否存在 gitflow 配置
136
+ const isGitFlow = await isGitFlowRepo();
137
+ if (!isGitFlow) {
138
+ logger.error('当前仓库 非git flow仓库,请使用git flow init 初始化后,再执行此命令')
139
+ process.exit(1);
140
+ }
141
+
142
+ // 获取当前所属分支
143
+ let { stdout: currentBranch } = await $(`git branch --show-current`)
144
+
145
+ const releaseBranch = `release/${version}`
146
+ if (currentBranch !== 'develop' && currentBranch !== releaseBranch) {
147
+ // await hasUncommittedFiles();
148
+ /**
149
+ * 判断是否已存在 release/version 分支
150
+ * 是--> 如果本地是否此分支
151
+ * -->否 checkout -b 指定分支
152
+ * -->是 checkout 指定分支
153
+ * 否--> 切换到develop分支
154
+ */
155
+ const { stdout } = await $('git branch -a');
156
+ const branches = stdout.split('\n').map(branch => branch.trim().replace(/^\* /, ''));
157
+ const preReleaseBranch = branches.includes(releaseBranch) ? releaseBranch : 'develop'
158
+ await execGitCheckout(preReleaseBranch)
159
+ }
160
+
161
+ if (currentBranch === 'develop') {
162
+ await execGitPullOrigin(`develop`)
163
+
164
+ const flowReleaseRes = await execWithPipe(`git flow release start ${version}`)
165
+ if (!(flowReleaseRes.stdout.indexOf(`A new branch '${releaseBranch}' was created`) > -1)) {
166
+ logger.error('执行 git flow release 失败')
167
+ process.exit(1);
168
+ }
169
+ } else {
170
+ try {
171
+ await execGitPullOrigin(releaseBranch)
172
+ } catch (error) {
173
+ console.error("🚀 ~ doGitFlowRelease ~ error:", error)
174
+ if (!(error && error.toString().indexOf(`fatal: couldn't find remote ref ${releaseBranch}`) > -1)) {
175
+ logger.error(`同步分支: ${releaseBranch} 失败`)
176
+ process.exit(1);
177
+ }
178
+ }
179
+ }
180
+
181
+ // 添加版本到package.json
182
+ try {
183
+ const npmVersionRes = await $(`npm version ${version} --no-git-tag-version`)
184
+ if (npmVersionRes.stderr) {
185
+ logger.error('添加版本信息到package.json失败')
186
+ process.exit(1);
187
+ }
188
+
189
+ logger.info(pc.green(`添加 ${version} 版本信息到package.json 成功`))
190
+ } catch (error) {
191
+ if (error && error.toString().indexOf('npm error Version not changed') == -1) {
192
+ logger.error('添加版本信息到package.json失败')
193
+ process.exit(1);
194
+ }
195
+ }
196
+ const hasUncommitted = await hasUncommittedChanges(true);
197
+
198
+ if (hasUncommitted) {
199
+ await $(`git add -A`)
200
+ await $('git commit', ['-m', `Release version: ${version}`, '--no-verify'])
201
+ }
202
+
203
+ await execWithPipe(`git flow release finish`, [version, '-m', `Release version: ${version}`]);
204
+ // if (releaseFRes.stderr) {
205
+ // logger.error('执行 git flow release finish 失败')
206
+ // process.exit(1);
207
+ // }
208
+
209
+ // 同步到远端
210
+ await $(`git push --all`)
211
+ await $(`git push --tags`)
212
+
213
+ return 'master'
214
+ }
215
+
216
+ async function doCustomSpecifyBranchRelease() {
217
+
218
+ const { stdout } = await $('git branch -r');
219
+ const branches = stdout.split('\n').map(branch => branch.trim()).filter(item => !item.startsWith('origin/HEAD')).map(branch => ({ value: branch.replace('origin/', '') }));
220
+
221
+ const selectedBranch = await inquirer.select({
222
+ message: '请选择目标分支, 分支未找到,请先将本地分支推到远端,再执行此命令',
223
+ choices: branches
224
+ })
225
+
226
+ return selectedBranch;
227
+ }
228
+
229
+
230
+ async function hasUncommittedChanges(boolReturned = false) {
231
+ // 检查当前分支是否存在未提交文件
232
+ const { stdout: uncommitted } = await $`git status --porcelain`
233
+
234
+ if (boolReturned) {
235
+ return !!uncommitted.toString().length
236
+ }
237
+
238
+ if (uncommitted.toString().length) {
239
+ logger.error('当前分支存在尚未提交的文件,请提交后再运行此命令')
240
+ return Promise.reject(true)
241
+ }
242
+
243
+ return Promise.resolve(false);
244
+ }
245
+
246
+
247
+ async function execGitPullOrigin(remoteTargetBranch: string) {
248
+ logger.tip(`${ remoteTargetBranch} syncing...`)
249
+
250
+ const syncReleaseBranchRes = $('git', ['pull', 'origin', remoteTargetBranch])
251
+ syncReleaseBranchRes.stdout.pipe(process.stdout)
252
+ syncReleaseBranchRes.stderr.pipe(process.stderr)
253
+ const res = await syncReleaseBranchRes;
254
+ // fatal: unable to access 'https://github.com/user/repo.git/': Could not resolve host: xxx
255
+ // fatal: unable to access 'https://github.com/user/repo.git/':
256
+ // fatal: Couldn't find remote ref chore/releset_test ...
257
+ if (res.stdout.indexOf('fatal:') > -1 || res.stderr.indexOf('fatal:') > -1) {
258
+ logger.error(`同步${remoteTargetBranch} 失败`)
259
+ process.exit(1);
260
+ }
261
+ }
262
+
263
+ async function execWithPipe(...args: any[]) {
264
+ const execRes = $(...args as Parameters<typeof $>) as unknown as ResultPromise<{
265
+ encoding: "utf8";
266
+ }>
267
+ execRes.stdout.pipe(process.stdout);
268
+ execRes.stderr.pipe(process.stderr);
269
+ const result = await execRes
270
+ return result
271
+ }
272
+
273
+
274
+ async function execGitCheckout(remoteTargetBranch: string) {
275
+ await hasUncommittedChanges();
276
+
277
+ const { stdout: prevCurrentBranch } = await $(`git branch --show-current`)
278
+
279
+ if (prevCurrentBranch !== remoteTargetBranch) {
280
+ const execRes = $(`git checkout ${remoteTargetBranch}`)
281
+ execRes.stdout.pipe(process.stdout);
282
+ execRes.stderr.pipe(process.stderr);
283
+ const result = await execRes;
284
+
285
+ const { stdout: currentBranch } = await $(`git branch --show-current`)
286
+
287
+ if (currentBranch !== remoteTargetBranch) {
288
+ logger.error(`目标发布分支[${remoteTargetBranch}] checkout 失败`)
289
+ return Promise.reject(result);
290
+ }
291
+ }
292
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "include": ["src"],
4
+ "exclude": ["node_modules"],
5
+ "compilerOptions": {
6
+ "module": "ES2022",
7
+ "target": "ES2022",
8
+ "moduleResolution": "Node",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "noUnusedLocals": true
13
+ }
14
+ }