@wjwjq/release-helper 0.1.5 → 0.1.7
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/dist/.release/release.conf.yaml +2 -0
- package/dist/cli.js +1 -1
- package/dist/deploy/script/common.sh +36 -29
- package/dist/deploy/script/prompt.sh +6 -0
- package/dist/pack.js +22 -21
- package/dist/prepare.js +20 -10
- package/dist/publish.js +7 -9
- package/dist/release.js +4 -5
- package/dist/start_prepare.js +3 -3
- package/package.json +1 -1
- package/pnpm-lock.yaml +0 -2443
- package/src/.release/README.md +0 -73
- package/src/.release/doc//351/203/250/347/275/262/346/211/213/345/206/214.md +0 -418
- package/src/.release/nginx/ca/ca.crt +0 -32
- package/src/.release/nginx/ca/ca.key +0 -54
- package/src/.release/nginx/ca/client.crt +0 -100
- package/src/.release/nginx/ca/client.csr +0 -16
- package/src/.release/nginx/ca/client.p12 +0 -0
- package/src/.release/nginx/ca/client.pem +0 -30
- package/src/.release/nginx/ca/server.crt +0 -101
- package/src/.release/nginx/ca/server.csr +0 -17
- package/src/.release/nginx/ca/server.key +0 -27
- package/src/.release/nginx/ca/server.pem +0 -30
- package/src/.release/nginx/nginx.conf +0 -179
- package/src/.release/release.conf.yaml +0 -15
- package/src/cli.ts +0 -99
- package/src/deploy/pkg/nginx/nginx.logrotate.tpl +0 -14
- package/src/deploy/pkg/nginx/nginx.service.tpl +0 -32
- package/src/deploy/pkg/nginx_binary/compile.sh +0 -39
- package/src/deploy/pkg/nginx_binary/nginx-arm-ssl1.1.1.tar.gz +0 -0
- package/src/deploy/pkg/nginx_binary/nginx-x86_64-ssl1.0.2.tar.gz +0 -0
- package/src/deploy/pkg/nginx_binary/nginx-x86_64-ssl1.1.1.tar.gz +0 -0
- package/src/deploy/pkg/nginx_binary/nginx-x86_64-ssl3.0.7.tar.gz +0 -0
- package/src/deploy/script/common.sh +0 -196
- package/src/deploy/script/install.sh +0 -7
- package/src/deploy/script/nginx.sh +0 -265
- package/src/deploy/script/prompt.sh +0 -110
- package/src/deploy/script/readme.md +0 -10
- package/src/deploy/script/upgrade.sh +0 -7
- package/src/logger.ts +0 -18
- package/src/pack.ts +0 -152
- package/src/prepare.ts +0 -120
- package/src/publish.ts +0 -308
- package/src/release.ts +0 -292
- package/src/start_prepare.ts +0 -13
package/src/pack.ts
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import fs from "fs"
|
|
2
|
-
import path from "path"
|
|
3
|
-
|
|
4
|
-
import * as inquirer from "@inquirer/prompts"
|
|
5
|
-
import pc from "picocolors"
|
|
6
|
-
import * as tar from 'tar'
|
|
7
|
-
|
|
8
|
-
import { __work_dir, __releaseDir, __dirname, releaseConf, checkEnvInfo } from './prepare'
|
|
9
|
-
import { logger } from "./logger"
|
|
10
|
-
|
|
11
|
-
export async function pack(version?: string) {
|
|
12
|
-
try {
|
|
13
|
-
await checkEnvInfo();
|
|
14
|
-
|
|
15
|
-
if (version === undefined) {
|
|
16
|
-
version = await inquirer.input(
|
|
17
|
-
{
|
|
18
|
-
message: '请输入版本号:',
|
|
19
|
-
},
|
|
20
|
-
)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (version == '') {
|
|
24
|
-
logger.error('版本号不能为空!')
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const packageJson = JSON.parse(fs.readFileSync(path.resolve(__work_dir, 'package.json'), { 'encoding': 'utf-8' }))
|
|
29
|
-
const projectName = packageJson.name as string;
|
|
30
|
-
|
|
31
|
-
if (!projectName) {
|
|
32
|
-
logger.error('未在package.json找到name定义!')
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const zipFileName = `${projectName}_${version}.tar.gz`
|
|
37
|
-
|
|
38
|
-
logger.tip(`start to pack`)
|
|
39
|
-
|
|
40
|
-
const projectDir = path.resolve(__releaseDir, projectName); //eg .release/posidon-frontend
|
|
41
|
-
const assetsDir = path.resolve(projectDir, 'pkg/assets');
|
|
42
|
-
const nginxDir = path.resolve(projectDir, 'pkg/nginx')
|
|
43
|
-
|
|
44
|
-
// 复制整个安装脚本和nginx二进制包
|
|
45
|
-
fs.cpSync(path.resolve(__dirname, 'deploy'), projectDir, { recursive: true })
|
|
46
|
-
// 复制打包后的静态资源
|
|
47
|
-
fs.cpSync(path.resolve(__work_dir, releaseConf.assetsDir), assetsDir, { recursive: true })
|
|
48
|
-
|
|
49
|
-
// 添加版本信息
|
|
50
|
-
fs.writeFileSync(path.resolve(projectDir, 'pkg', 'version'), version, { encoding: 'utf-8' })
|
|
51
|
-
fs.writeFileSync(path.resolve(assetsDir, 'version'), version, { encoding: 'utf-8' })
|
|
52
|
-
fs.writeFileSync(path.resolve(nginxDir, 'version'), version, { encoding: 'utf-8' })
|
|
53
|
-
|
|
54
|
-
logger.info(pc.green(`version: ${version} added`))
|
|
55
|
-
|
|
56
|
-
const assetInstallPath = `/opt/${projectName}/`
|
|
57
|
-
logger.info(pc.green(`start to replace variants in common.sh`))
|
|
58
|
-
|
|
59
|
-
// 替换安装脚本 与nginx匹配的名称
|
|
60
|
-
replaceFileContent({
|
|
61
|
-
filePath: path.resolve(projectDir, 'script', 'common.sh'),
|
|
62
|
-
replaced: [
|
|
63
|
-
['__APP_NAME__', `"${projectName}"`],
|
|
64
|
-
['__INSTALL_PATH__', assetInstallPath],
|
|
65
|
-
['__USER__', releaseConf.user],
|
|
66
|
-
['__USER_GROUP__', releaseConf.userGroup],
|
|
67
|
-
]
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
// 替换安装模式
|
|
71
|
-
replaceFileContent({
|
|
72
|
-
filePath: path.resolve(projectDir, 'script', 'prompt.sh'),
|
|
73
|
-
replaced: [
|
|
74
|
-
['__INSTALL_MODE__', releaseConf.installMode],
|
|
75
|
-
]
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
logger.info(pc.green(`start to replace version: ${version} in index.html`))
|
|
79
|
-
|
|
80
|
-
replaceFileContent({
|
|
81
|
-
filePath: path.resolve(assetsDir, 'index.html'),
|
|
82
|
-
replaced: [['__version__', version]]
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
// 生成nginx.conf文件
|
|
86
|
-
logger.info(pc.green(`start to generate nginx.conf`))
|
|
87
|
-
|
|
88
|
-
//复制用户指定的nginx配置信息
|
|
89
|
-
fs.cpSync(path.resolve(__releaseDir, 'nginx'), nginxDir, { recursive: true, force: true })
|
|
90
|
-
|
|
91
|
-
replaceFileContent({
|
|
92
|
-
filePath: path.resolve(nginxDir, 'nginx.conf'),
|
|
93
|
-
replaced: [['__root__', `${assetInstallPath}assets/`]],
|
|
94
|
-
// writeToNewFilePath: path.resolve(nginxDir, `${projectName}.conf`)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
logger.info(pc.green(`start to generate ${zipFileName}`))
|
|
98
|
-
|
|
99
|
-
const zipFilePath = path.resolve(__releaseDir, zipFileName)
|
|
100
|
-
await genTarPkg(__releaseDir, projectName, zipFilePath);
|
|
101
|
-
|
|
102
|
-
logger.success(`done to pack ${pc.green(`${zipFileName} at: ${__releaseDir}`)}`);
|
|
103
|
-
|
|
104
|
-
if (fs.existsSync(projectDir)) {
|
|
105
|
-
fs.rmSync(projectDir, { recursive: true, force: true })
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
version,
|
|
110
|
-
pkgName: zipFileName,
|
|
111
|
-
appName: projectName,
|
|
112
|
-
pkgFilePath: zipFilePath
|
|
113
|
-
}
|
|
114
|
-
} catch (error) {
|
|
115
|
-
console.error('Error:', error);
|
|
116
|
-
return Promise.reject(error);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function genTarPkg(releaseDir: string, projectName: string, zipFileName: string) {
|
|
121
|
-
return new Promise((resolve, reject) => {
|
|
122
|
-
const zipStream = fs.createWriteStream(zipFileName);
|
|
123
|
-
zipStream.on('close', () => {
|
|
124
|
-
resolve(true);
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
tar.create(
|
|
129
|
-
{
|
|
130
|
-
z: true,
|
|
131
|
-
cwd: releaseDir,
|
|
132
|
-
},
|
|
133
|
-
[projectName],
|
|
134
|
-
).pipe(zipStream)
|
|
135
|
-
} catch (error) {
|
|
136
|
-
reject(error);
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function replaceFileContent(params: {filePath: string; writeToNewFilePath?: string; replaced: [string, string][] }) {
|
|
142
|
-
const { filePath, writeToNewFilePath, replaced = [] } = params
|
|
143
|
-
if (fs.existsSync(filePath)) {
|
|
144
|
-
let content = fs.readFileSync(filePath, { encoding: 'utf-8' })
|
|
145
|
-
|
|
146
|
-
replaced.forEach(([regExp, replaceContent]) => {
|
|
147
|
-
content = content.toString().replace(regExp, replaceContent);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
fs.writeFileSync(writeToNewFilePath ? writeToNewFilePath : filePath, content, { encoding: 'utf-8' })
|
|
151
|
-
}
|
|
152
|
-
}
|
package/src/prepare.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import path, { dirname } from "path";
|
|
2
|
-
import fs from "fs"
|
|
3
|
-
import YAML from 'yaml';
|
|
4
|
-
import * as inquirer from "@inquirer/prompts"
|
|
5
|
-
|
|
6
|
-
import { execa } from 'execa';
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
-
|
|
9
|
-
import { logger } from './logger';
|
|
10
|
-
|
|
11
|
-
const installMode = ['standalone', 'cluster', 'both'] as const;
|
|
12
|
-
|
|
13
|
-
export type ReleaseConf = {
|
|
14
|
-
/** gitlab 仓库地址 */
|
|
15
|
-
host: string
|
|
16
|
-
/** gitlab rest api token */
|
|
17
|
-
token: string
|
|
18
|
-
/** 打包脚本 默认npm run build */
|
|
19
|
-
buildCmd?: string
|
|
20
|
-
/** 打包后资源 相对工作区所在路径 默认dist/*/
|
|
21
|
-
assetsDir?: string
|
|
22
|
-
/** nginx和assets 所属用户和用户组 默认: root*/
|
|
23
|
-
user: string
|
|
24
|
-
userGroup: string
|
|
25
|
-
/* 指定安装模式 standalone(supervisor) 、 cluster(agent\systemd) 、 both */
|
|
26
|
-
installMode: (typeof installMode)[number]
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// 获取项目根目录
|
|
30
|
-
export const __work_dir = process.env.INIT_CWD || process.cwd();
|
|
31
|
-
export const __releaseDir = path.resolve(__work_dir, '.release')
|
|
32
|
-
|
|
33
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
34
|
-
export const __dirname = dirname(__filename);
|
|
35
|
-
|
|
36
|
-
export const releaseConfFileName = "release.conf.yaml"
|
|
37
|
-
export const __releaseConfPath = path.resolve(__releaseDir, releaseConfFileName);
|
|
38
|
-
|
|
39
|
-
function parseConf(file: string) {
|
|
40
|
-
try {
|
|
41
|
-
const conf = fs.readFileSync(file, "utf-8");
|
|
42
|
-
return YAML.parse(conf) as ReleaseConf
|
|
43
|
-
} catch (error) {
|
|
44
|
-
return {} as ReleaseConf;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const releaseConf: Required<ReleaseConf> = Object.assign({
|
|
49
|
-
host: '',
|
|
50
|
-
token: '',
|
|
51
|
-
/** 打包脚本 默认npm run build */
|
|
52
|
-
buildCmd: 'npm run build',
|
|
53
|
-
/** 打包后资源 相对工作区所在路径 默认dist/*/
|
|
54
|
-
assetsDir: 'dist',
|
|
55
|
-
user: 'root',
|
|
56
|
-
userGroup: 'root',
|
|
57
|
-
installMode: 'both'
|
|
58
|
-
}, parseConf(__releaseConfPath));
|
|
59
|
-
|
|
60
|
-
export const $ = execa({ encoding: 'utf8' });
|
|
61
|
-
|
|
62
|
-
export async function prepare() {
|
|
63
|
-
const copyFiles = () => {
|
|
64
|
-
// 将.release file copy 到__work_dir 目录下
|
|
65
|
-
fs.cpSync(path.resolve(__dirname, '.release'), __releaseDir, { recursive: true })
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!fs.existsSync(__releaseDir)) {
|
|
69
|
-
|
|
70
|
-
fs.mkdirSync(__releaseDir)
|
|
71
|
-
copyFiles();
|
|
72
|
-
logger.tip(`请注意修改.release/${releaseConfFileName} 配置信息`)
|
|
73
|
-
}else {
|
|
74
|
-
if(!process.env.INIT_CWD){ // Npm环境下 无法交互 会阻塞install
|
|
75
|
-
const shouldRegenerate = await inquirer.confirm(
|
|
76
|
-
{
|
|
77
|
-
message: '.release配置已存在,是否重新生成?',
|
|
78
|
-
},
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
if(shouldRegenerate){
|
|
82
|
-
copyFiles()
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export async function checkEnvInfo() {
|
|
89
|
-
// 检查是否在 git 仓库中
|
|
90
|
-
const { stdout: isGitRepo } = await $(`git rev-parse --is-inside-work-tree`)
|
|
91
|
-
if (isGitRepo !== 'true') {
|
|
92
|
-
logger.error('当前项目不是git仓库')
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// 检查.release下 nginx.conf, release.conf.yaml 等文件是否存在
|
|
97
|
-
|
|
98
|
-
if (!fs.existsSync(path.resolve(__releaseDir, 'nginx/nginx.conf'))) {
|
|
99
|
-
logger.error('.release目录下不存在nginx/nginx.conf 文件, 请使用 release-helper init 初始化生成')
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (!fs.existsSync(__releaseConfPath)) {
|
|
104
|
-
logger.error(`.release目录下不存在${releaseConfFileName}文件,请使用 release-helper init 初始化生成`)
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
for(let [field, value] of Object.entries(releaseConf)){
|
|
109
|
-
if (!value) {
|
|
110
|
-
logger.error(`The field: [${field}] is required in .release/release.conf.yaml!`);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if(field === 'installMode' && !installMode.includes(value as (typeof installMode)[number])){
|
|
115
|
-
logger.error(`The field: [${field}]'s value should be one of ${installMode.join(' | ')}`);
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
package/src/publish.ts
DELETED
|
@@ -1,308 +0,0 @@
|
|
|
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
|
-
}
|