@taole/deploy-helper 0.5.5 → 0.6.0
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/index.mjs +47 -6
- package/lib/offlinePkg.mjs +260 -0
- package/lib/upload.js +49 -0
- package/lib/util.mjs +44 -0
- package/package.json +5 -2
package/index.mjs
CHANGED
|
@@ -6,15 +6,24 @@ import { join, basename, dirname } from "path";
|
|
|
6
6
|
import { simpleGit } from 'simple-git';
|
|
7
7
|
import { homedir } from 'os'
|
|
8
8
|
import { runPipeline, checkYunxiaoToken, triggerPipeline } from './lib/pipelineApi.mjs';
|
|
9
|
-
import { setDebug, log } from './lib/util.mjs';
|
|
9
|
+
import { setDebug, log, getUserDeployHelperConfig } from './lib/util.mjs';
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
|
+
import { checkOfflinePkg, syncApi as syncOfflinePkgApi } from './lib/offlinePkg.mjs';
|
|
12
13
|
|
|
13
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
15
|
const __dirname = path.dirname(__filename);
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
const TEST_SERVER_HOST = "192.168.0.35";
|
|
19
|
+
|
|
20
|
+
function fmtAssetsCommit(msg){
|
|
21
|
+
if(msg.startsWith("-#")){
|
|
22
|
+
return msg;
|
|
23
|
+
}
|
|
24
|
+
return `-#DH-1 ${msg}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
/**
|
|
19
28
|
* 加载配置
|
|
20
29
|
* @returns 配置对象
|
|
@@ -115,7 +124,7 @@ async function main() {
|
|
|
115
124
|
// return;
|
|
116
125
|
// other commands
|
|
117
126
|
let command = process.argv[2];
|
|
118
|
-
if (!["init", "prod", "test", "scp", "scpevt", "pipeline"].includes(command)) {
|
|
127
|
+
if (!["init", "prod", "test", "scp", "scpevt", "pipeline", "rmofflinepkg"].includes(command)) {
|
|
119
128
|
command = "help";
|
|
120
129
|
}
|
|
121
130
|
|
|
@@ -130,6 +139,7 @@ async function main() {
|
|
|
130
139
|
console.log(`command: scpevt {file} 复制文件{file}到events测试服务器{file}`);
|
|
131
140
|
console.log(`command: scpevt {file} {dest} 复制文件{file}到events测试服务器{dest}`);
|
|
132
141
|
console.log(`command: pipeline {pipelineName|pipelineId} [branch] 触发流水线{pipelineName|pipelineId}, 指定分支(可省略)`);
|
|
142
|
+
console.log(`command: rmofflinepkg {name} [mode] 删除离线包{name}, 指定模式(可省略,默认test)`);
|
|
133
143
|
process.exit(0);
|
|
134
144
|
}
|
|
135
145
|
log(`deploy-helper v${version} start`);
|
|
@@ -191,6 +201,25 @@ async function main() {
|
|
|
191
201
|
process.exit(1);
|
|
192
202
|
}
|
|
193
203
|
|
|
204
|
+
} else if (command === "rmofflinepkg") {
|
|
205
|
+
const name = process.argv[3];
|
|
206
|
+
if (!name) {
|
|
207
|
+
log(`name参数不能为空`);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
const mode = process.argv[4] || "test";
|
|
211
|
+
if (!["prod", "test"].includes(mode)) {
|
|
212
|
+
log(`mode参数只能是prod或test`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
const userDeployHelperConfig = getUserDeployHelperConfig();
|
|
216
|
+
if (!userDeployHelperConfig || !userDeployHelperConfig.offlineApi || !userDeployHelperConfig.offlineApi.get || !userDeployHelperConfig.offlineApi.set) {
|
|
217
|
+
log(`配置文件未配置离线包api接口, 请先配置`);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
await syncOfflinePkgApi(userDeployHelperConfig, mode, { name, remove: true }, null);
|
|
221
|
+
log(`${mode === 'prod' ? '线上' : '测试'}环境删除离线包${name}完成`);
|
|
222
|
+
process.exit(0);
|
|
194
223
|
}
|
|
195
224
|
try {
|
|
196
225
|
const workDir = process.cwd(); // 当前项目根目录
|
|
@@ -221,7 +250,17 @@ async function main() {
|
|
|
221
250
|
|
|
222
251
|
// 检查流水线配置
|
|
223
252
|
checkYunxiaoToken(config, mode);
|
|
224
|
-
|
|
253
|
+
const offlinePkgResult = checkOfflinePkg({
|
|
254
|
+
workDir: workDir,
|
|
255
|
+
mode,
|
|
256
|
+
});
|
|
257
|
+
if(!offlinePkgResult.canBuild && offlinePkgResult.errorMsg) {
|
|
258
|
+
log(`${offlinePkgResult.errorMsg}`);
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
if(offlinePkgResult.canBuild) {
|
|
262
|
+
await offlinePkgResult.hookPostBuild();
|
|
263
|
+
}
|
|
225
264
|
|
|
226
265
|
// 检查项目
|
|
227
266
|
// 1. 检查项目是否存在
|
|
@@ -305,10 +344,10 @@ async function main() {
|
|
|
305
344
|
log(`${entryDest} git pull done`);
|
|
306
345
|
}
|
|
307
346
|
|
|
347
|
+
let assetsFilesCount = 0;
|
|
308
348
|
// 5. 复制构建产物
|
|
309
349
|
// 5.1 复制assets
|
|
310
350
|
if (needHandleAssets) {
|
|
311
|
-
let assetsFilesCount = 0;
|
|
312
351
|
const assetsFiles = config.assets.files;
|
|
313
352
|
for (const [src, dest] of Object.entries(assetsFiles)) {
|
|
314
353
|
const srcPath = join(workDir, src);
|
|
@@ -355,7 +394,8 @@ async function main() {
|
|
|
355
394
|
if (!assetsStatus.isClean()) {
|
|
356
395
|
canPushAssets = true;
|
|
357
396
|
await assetsGit.add(".");
|
|
358
|
-
|
|
397
|
+
let assetsCommit = `${config.assets.commit || "feat:部署资源文件"} by deploy-helper`;
|
|
398
|
+
assetsCommit = fmtAssetsCommit(assetsCommit);
|
|
359
399
|
const assetsCommitResult = await assetsGit.commit(assetsCommit);
|
|
360
400
|
log(`assets commit: ${assetsCommit}`);
|
|
361
401
|
log(`assets commit: ${JSON.stringify(assetsCommitResult.summary)}`);
|
|
@@ -368,7 +408,8 @@ async function main() {
|
|
|
368
408
|
if (!entryStatus.isClean()) {
|
|
369
409
|
canPushEntry = true;
|
|
370
410
|
await entryGit.add(".");
|
|
371
|
-
|
|
411
|
+
let entryCommit = `${config.entry.commit || "feat:部署入口文件"} by deploy-helper`;
|
|
412
|
+
entryCommit = fmtAssetsCommit(entryCommit);
|
|
372
413
|
const entryCommitResult = await entryGit.commit(entryCommit);
|
|
373
414
|
log(`entry commit: ${entryCommit}`);
|
|
374
415
|
log(`entry commit: ${JSON.stringify(entryCommitResult.summary)}`);
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import archiver from 'archiver';
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { log, getOssToken, getUserDeployHelperConfig } from './util.mjs';
|
|
5
|
+
import { uploadFile } from './upload.js';
|
|
6
|
+
import md5File from 'md5-file';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function genArchive(outputPath, dir) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const output = fs.createWriteStream(outputPath);
|
|
13
|
+
const archive = archiver('zip', {
|
|
14
|
+
zlib: { level: 1 } // Sets the compression level.
|
|
15
|
+
});
|
|
16
|
+
archive.on('error', reject);
|
|
17
|
+
archive.on("finish", resolve);
|
|
18
|
+
archive.pipe(output);
|
|
19
|
+
archive.directory(dir, false);
|
|
20
|
+
archive.finalize();
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* json配置
|
|
26
|
+
* @param {string} workDir 项目工作目录
|
|
27
|
+
* @returns {Object|null} 配置
|
|
28
|
+
*/
|
|
29
|
+
export function getJsonConfig(workDir, name) {
|
|
30
|
+
const filePath = join(workDir, name);
|
|
31
|
+
if (fs.existsSync(filePath)) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
34
|
+
} catch (_error) {
|
|
35
|
+
// pass
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 同步离线包列表到api
|
|
43
|
+
* @param {{offlineApi: {get: string, set: string}}} userDeployHelperConfig 用户配置
|
|
44
|
+
* @param {"prod"|"test"} mode 部署模式 prod: 线上, test: 测试
|
|
45
|
+
* @param {{name: string, remove: boolean}} offlineConfig 离线包配置
|
|
46
|
+
* @param {any} packageDescJson 离线包描述json
|
|
47
|
+
*/
|
|
48
|
+
export async function syncApi(userDeployHelperConfig, mode, offlineConfig, packageDescJson) {
|
|
49
|
+
// 根据部署模式,决定更新调用哪个接口
|
|
50
|
+
const doAction = offlineConfig.remove ? "remove" : "add";
|
|
51
|
+
const apiDomain = mode === "prod" ? "https://yapi.tuwan.com" : "https://yapi-test.tuwan.com";
|
|
52
|
+
const getPath = userDeployHelperConfig.offlineApi.get;
|
|
53
|
+
const setPath = userDeployHelperConfig.offlineApi.set;
|
|
54
|
+
const getUrl = `${apiDomain}${getPath}`;
|
|
55
|
+
const setUrl = `${apiDomain}${setPath}`;
|
|
56
|
+
const nowRes = await fetch(getUrl);
|
|
57
|
+
const nowData = await nowRes.json();
|
|
58
|
+
let needSetData = true;
|
|
59
|
+
let finalJsonData = null;
|
|
60
|
+
if (!nowData || nowData.error !== 0) {
|
|
61
|
+
throw new Error(`获取离线包列表失败, 获取离线包当前配置失败: ${nowData.error_msg || "未知错误"}`);
|
|
62
|
+
}
|
|
63
|
+
const jsonData = JSON.parse(nowData.data.json || "{}");
|
|
64
|
+
finalJsonData = jsonData;
|
|
65
|
+
jsonData.packages = jsonData.packages || [];
|
|
66
|
+
if (doAction === "add") {
|
|
67
|
+
const idx = jsonData.packages.findIndex(item => item.name === offlineConfig.name);
|
|
68
|
+
if (idx !== -1) {
|
|
69
|
+
log(`离线包${offlineConfig.name}已存在, 更新离线包`);
|
|
70
|
+
jsonData.packages[idx] = packageDescJson;
|
|
71
|
+
} else {
|
|
72
|
+
log(`离线包${offlineConfig.name}不存在, 添加离线包`);
|
|
73
|
+
jsonData.packages.push(packageDescJson);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
const idx = jsonData.packages.findIndex(item => item.name === offlineConfig.name);
|
|
77
|
+
if (idx === -1) {
|
|
78
|
+
log(`离线包${offlineConfig.name}不存在, 无需删除`);
|
|
79
|
+
needSetData = false;
|
|
80
|
+
}
|
|
81
|
+
jsonData.packages = jsonData.packages.filter(item => item.name !== offlineConfig.name);
|
|
82
|
+
}
|
|
83
|
+
if (needSetData) {
|
|
84
|
+
// console.log('jsonData', jsonData);
|
|
85
|
+
var formdata = new FormData();
|
|
86
|
+
formdata.append("json", JSON.stringify(jsonData));
|
|
87
|
+
const setRes = await fetch(setUrl, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
body: formdata,
|
|
90
|
+
});
|
|
91
|
+
const setData = await setRes.json();
|
|
92
|
+
if (!setData || setData.error !== 0) {
|
|
93
|
+
throw new Error(`更新离线包列表失败, 更新离线包当前配置失败: ${setData.error_msg || "未知错误"}`);
|
|
94
|
+
}
|
|
95
|
+
log(`离线包列表更新完成: ${JSON.stringify(setData)}`);
|
|
96
|
+
// 再次获取列表,确保更新成功
|
|
97
|
+
const nowRes2 = await fetch(getUrl);
|
|
98
|
+
const nowData2 = await nowRes2.json();
|
|
99
|
+
if (!nowData2 || nowData2.error !== 0) {
|
|
100
|
+
throw new Error(`再次获取离线包列表失败, 获取离线包当前配置失败: ${nowData2.error_msg || "未知错误"}`);
|
|
101
|
+
}
|
|
102
|
+
finalJsonData = JSON.parse(nowData2.data.json || "{}");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const packages = finalJsonData.packages || [];
|
|
106
|
+
console.log(`${mode === "prod" ? "线上" : "测试"}环境当前离线包列表:`, packages);
|
|
107
|
+
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @typedef {Object} CheckOfflinePkgResult
|
|
113
|
+
* @property {string} workDir 工作目录
|
|
114
|
+
* @property {boolean} canBuild 是否可以构建离线包
|
|
115
|
+
* @property {string} errorMsg 错误信息
|
|
116
|
+
* @property {function} hookPostBuild 构建后钩子
|
|
117
|
+
* @property {function} hookPostDeployTest 部署到测试环境后钩子
|
|
118
|
+
* @property {function} hookPostDeployProd 部署到生产环境后钩子
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 在项目构建好之后,将项目打包成离线包,并上传到指定服务器
|
|
123
|
+
* @param {Object} config 配置
|
|
124
|
+
* @param {string} config.workDir 工作目录
|
|
125
|
+
* @param {"prod"|"test"} config.mode 部署模式
|
|
126
|
+
* @returns {CheckOfflinePkgResult} 结果
|
|
127
|
+
*/
|
|
128
|
+
export function checkOfflinePkg(config) {
|
|
129
|
+
let canBuildOfflinePkg = true;
|
|
130
|
+
let errorMsg = "";
|
|
131
|
+
const offlineConfig = getJsonConfig(config.workDir, "offline.config.json");
|
|
132
|
+
if (offlineConfig) {
|
|
133
|
+
offlineConfig.distDir = offlineConfig.distDir || "dist";
|
|
134
|
+
}
|
|
135
|
+
const packageJson = getJsonConfig(config.workDir, "package.json");
|
|
136
|
+
const userDeployHelperConfig = getUserDeployHelperConfig();
|
|
137
|
+
const distPath = join(config.workDir, offlineConfig.distDir);
|
|
138
|
+
if (!fs.existsSync(distPath)) {
|
|
139
|
+
errorMsg = `构建产物${distPath}不存在, 请先构建项目`;
|
|
140
|
+
canBuildOfflinePkg = false;
|
|
141
|
+
} else if (!offlineConfig) {
|
|
142
|
+
errorMsg = ""; // 没有离线包配置文件,跳过构建离线包
|
|
143
|
+
canBuildOfflinePkg = false;
|
|
144
|
+
} else if (!userDeployHelperConfig || !userDeployHelperConfig.offlineApi || !userDeployHelperConfig.offlineApi.get || !userDeployHelperConfig.offlineApi.set) {
|
|
145
|
+
errorMsg = "未配置离线包接口";
|
|
146
|
+
canBuildOfflinePkg = false;
|
|
147
|
+
} else if (!packageJson) {
|
|
148
|
+
errorMsg = "package.json";
|
|
149
|
+
canBuildOfflinePkg = false;
|
|
150
|
+
} else {
|
|
151
|
+
// 暂时不检查这个
|
|
152
|
+
// const allDeps = {
|
|
153
|
+
// ...packageJson.devDependencies,
|
|
154
|
+
// ...packageJson.dependencies,
|
|
155
|
+
// };
|
|
156
|
+
// if(!allDeps['@vitejs/plugin-legacy'] || !allDeps['vite-plugin-dynamic-base']){
|
|
157
|
+
// errorMsg = "检查到项目中未安装依赖@vitejs/plugin-legacy和vite-plugin-dynamic-base, 请先安装依赖并调整构建代码以支持离线化";
|
|
158
|
+
// canBuildOfflinePkg = false;
|
|
159
|
+
// }
|
|
160
|
+
}
|
|
161
|
+
if (!canBuildOfflinePkg) {
|
|
162
|
+
return {
|
|
163
|
+
canBuild: canBuildOfflinePkg,
|
|
164
|
+
errorMsg: errorMsg,
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
let ossToken = "";
|
|
168
|
+
if (canBuildOfflinePkg) {
|
|
169
|
+
ossToken = getOssToken();
|
|
170
|
+
if (!ossToken) {
|
|
171
|
+
errorMsg = "获取ossToken失败";
|
|
172
|
+
canBuildOfflinePkg = false;
|
|
173
|
+
return {
|
|
174
|
+
canBuild: canBuildOfflinePkg,
|
|
175
|
+
errorMsg: errorMsg,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const hookPostBuild = async () => {
|
|
180
|
+
// 在构建完成后,执行一些操作
|
|
181
|
+
// hack
|
|
182
|
+
// 由于vite-plugin-dynamic-base插件和legacy插件同时使用, 生存的入口文件中代码有点问题,这里直接修改有问题的代码
|
|
183
|
+
// 暂时解决加载问题
|
|
184
|
+
const entryHtmlPath = join(config.workDir, offlineConfig.distDir, "index.html");
|
|
185
|
+
if (!fs.existsSync(entryHtmlPath)) {
|
|
186
|
+
throw new Error(`构建产物${entryHtmlPath}不存在`);
|
|
187
|
+
}
|
|
188
|
+
const markLine = "} else if (item.tagName == 'script') {";
|
|
189
|
+
const replaceLine = `${markLine}
|
|
190
|
+
if(item.attrs.id=='vite-legacy-polyfill')childNode.onload=function(){System.import(window.__dynamic_base__ + document.getElementById('vite-legacy-entry').getAttribute('data-src'))};
|
|
191
|
+
`;
|
|
192
|
+
let entryHtml = fs.readFileSync(entryHtmlPath, "utf-8");
|
|
193
|
+
entryHtml = entryHtml.replace(markLine, replaceLine);
|
|
194
|
+
fs.writeFileSync(entryHtmlPath, entryHtml);
|
|
195
|
+
|
|
196
|
+
// 开始进行打包流程
|
|
197
|
+
const offlinePkgDir = join(config.workDir, "./offlinepkgDist");
|
|
198
|
+
if (fs.existsSync(offlinePkgDir)) {
|
|
199
|
+
fs.rmSync(offlinePkgDir, { recursive: true });
|
|
200
|
+
} else {
|
|
201
|
+
fs.mkdirSync(offlinePkgDir, { recursive: true });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const packageDir = join(offlinePkgDir, "./package");
|
|
205
|
+
fs.mkdirSync(packageDir, { recursive: true });
|
|
206
|
+
// cp dist到offlinepkgDist目录下
|
|
207
|
+
fs.cpSync(join(config.workDir, offlineConfig.distDir), join(packageDir, offlineConfig.distDir), { recursive: true });
|
|
208
|
+
|
|
209
|
+
const targetArchive = join(offlinePkgDir, `./${packageJson.version}.zip`);
|
|
210
|
+
await genArchive(targetArchive, packageDir);
|
|
211
|
+
log(`离线包zip包打包完成: ${targetArchive}`);
|
|
212
|
+
|
|
213
|
+
const packageDescJson = { // 包描述json,之后会通过接口更新
|
|
214
|
+
name: offlineConfig.name,
|
|
215
|
+
version: packageJson.version,
|
|
216
|
+
md5: "",
|
|
217
|
+
packageUrl: "",
|
|
218
|
+
items: [],
|
|
219
|
+
updateTime: String(Math.floor(Date.now() / 1000)),
|
|
220
|
+
cacheTime: offlineConfig.cacheTime || "7200",
|
|
221
|
+
isLazy: offlineConfig.isLazy || 0,
|
|
222
|
+
openType: offlineConfig.openType || "file",
|
|
223
|
+
};
|
|
224
|
+
Object.keys(offlineConfig.entry || {}).forEach(key => {
|
|
225
|
+
const val = offlineConfig.entry[key];
|
|
226
|
+
let vals = [];
|
|
227
|
+
if (typeof val === 'string') {
|
|
228
|
+
vals.push(val);
|
|
229
|
+
} else if (Array.isArray(val)) {
|
|
230
|
+
vals = val;
|
|
231
|
+
}
|
|
232
|
+
vals.forEach(v => {
|
|
233
|
+
packageDescJson.items.push({
|
|
234
|
+
"path": key,
|
|
235
|
+
"mimeType": "text/html",
|
|
236
|
+
"remoteUrl": v,
|
|
237
|
+
"isEntrance": 1
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
packageDescJson.md5 = await md5File(targetArchive);
|
|
242
|
+
const uploadResult = await uploadFile(ossToken, targetArchive, packageDescJson.md5);
|
|
243
|
+
packageDescJson.packageUrl = uploadResult.url;
|
|
244
|
+
fs.writeFileSync(join(offlinePkgDir, "./offline.package.json"), JSON.stringify(packageDescJson, null, 2));
|
|
245
|
+
log(`离线包描述json写入完成: ${join(offlinePkgDir, "./offline.package.json")}`);
|
|
246
|
+
|
|
247
|
+
if (config.mode !== "prod") {
|
|
248
|
+
packageDescJson.items.forEach(item => {
|
|
249
|
+
item.remoteUrl = item.remoteUrl.replace("https://y.tuwan.com", "https://y-test.tuwan.com");
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
await syncApi(userDeployHelperConfig, config.mode, offlineConfig, packageDescJson);
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
canBuild: canBuildOfflinePkg,
|
|
256
|
+
errorMsg: errorMsg,
|
|
257
|
+
hookPostBuild: hookPostBuild,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
package/lib/upload.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import OSS from "ali-oss";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function UUIDNOCA() {
|
|
5
|
+
return "xxxxxxxxxxxxxxxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
6
|
+
var r = (Math.random() * 16) | 0;
|
|
7
|
+
var v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
8
|
+
return v.toString(16);
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let keyConfig = null;
|
|
13
|
+
async function getKeyConfig(ossToken) {
|
|
14
|
+
if (keyConfig) {
|
|
15
|
+
return keyConfig;
|
|
16
|
+
}
|
|
17
|
+
const reqUrl = `https://u.tuwan.com/Oss/sts?token=${ossToken}`;
|
|
18
|
+
const res = await fetch(reqUrl);
|
|
19
|
+
const resText = await res.text();
|
|
20
|
+
let fmtJson = resText.substring(1, resText.length - 1).replace("'", '"');
|
|
21
|
+
const realData = JSON.parse(fmtJson).data;
|
|
22
|
+
keyConfig = realData;
|
|
23
|
+
return keyConfig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let ossClient = null;
|
|
27
|
+
async function getUploadConfig(ossToken) {
|
|
28
|
+
if (ossClient) {
|
|
29
|
+
return ossClient;
|
|
30
|
+
}
|
|
31
|
+
const keyConfig = await getKeyConfig(ossToken);
|
|
32
|
+
ossClient = new OSS({
|
|
33
|
+
endpoint: "oss-cn-qingdao.aliyuncs.com",
|
|
34
|
+
accessKeyId: keyConfig.AccessKeyId,
|
|
35
|
+
accessKeySecret: keyConfig.AccessKeySecret,
|
|
36
|
+
bucket: "tuwanpicshare",
|
|
37
|
+
stsToken: keyConfig.SecurityToken,
|
|
38
|
+
secure: true,
|
|
39
|
+
});
|
|
40
|
+
return ossClient;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function uploadFile(ossToken, filePath, name) {
|
|
44
|
+
const str = filePath;
|
|
45
|
+
const ext = str.substr(str.lastIndexOf(".") + 1);
|
|
46
|
+
const fileName = `offlinepkg/${name || UUIDNOCA()}.${ext}`;
|
|
47
|
+
const ossClient = await getUploadConfig(ossToken);
|
|
48
|
+
return ossClient.put(fileName, filePath);
|
|
49
|
+
}
|
package/lib/util.mjs
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { homedir } from "node:os"
|
|
4
|
+
|
|
1
5
|
let _isDebug = false;
|
|
2
6
|
export function setDebug(debug) {
|
|
3
7
|
_isDebug = debug;
|
|
@@ -14,4 +18,44 @@ export function log(...args) {
|
|
|
14
18
|
} else {
|
|
15
19
|
console.log(...args);
|
|
16
20
|
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getOssToken() {
|
|
24
|
+
let token = "";
|
|
25
|
+
token = process.env.TW_DH_OSS_TOKEN || ""
|
|
26
|
+
if (token) {
|
|
27
|
+
log(`将使用来自环境变量TW_DH_OSS_TOKEN的token`);
|
|
28
|
+
return token;
|
|
29
|
+
}
|
|
30
|
+
const userDeployHelperDir = join(homedir(), "deploy-helper.config.json");
|
|
31
|
+
if (fs.existsSync(userDeployHelperDir)) {
|
|
32
|
+
try {
|
|
33
|
+
const userDeployHelperConfig = JSON.parse(fs.readFileSync(userDeployHelperDir, "utf-8"));
|
|
34
|
+
token = userDeployHelperConfig.ossToken || "";
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(`读取${userDeployHelperDir}配置失败: ${error}`);
|
|
37
|
+
}
|
|
38
|
+
if (token) {
|
|
39
|
+
log(`将使用来自${userDeployHelperDir}的ossToken`);
|
|
40
|
+
return token;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return token;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 读取dh用户配置文件deploy-helper.config.json
|
|
48
|
+
* @returns {Object|null} 用户配置
|
|
49
|
+
*/
|
|
50
|
+
export function getUserDeployHelperConfig() {
|
|
51
|
+
const userDeployHelperDir = join(homedir(), "deploy-helper.config.json");
|
|
52
|
+
if (fs.existsSync(userDeployHelperDir)) {
|
|
53
|
+
try {
|
|
54
|
+
const userDeployHelperConfig = JSON.parse(fs.readFileSync(userDeployHelperDir, "utf-8"));
|
|
55
|
+
return userDeployHelperConfig;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(`读取${userDeployHelperDir}配置失败: ${error}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
17
61
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taole/deploy-helper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "脚本部署工具,用于将项目部署到测试环境或生产环境",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"test": "node
|
|
8
|
+
"test": "node test.mjs"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
11
|
"deploy-helper": "index.mjs",
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"license": "ISC",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"alibabacloud-devops-mcp-server": "*",
|
|
28
|
+
"ali-oss": "^6.23.0",
|
|
29
|
+
"archiver": "^7.0.1",
|
|
30
|
+
"md5-file": "^5.0.0",
|
|
28
31
|
"node-scp": "^0.0.25",
|
|
29
32
|
"simple-git": "^3.28.0"
|
|
30
33
|
}
|