yj-deploy 0.0.7 → 0.0.8
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/README.md +12 -1
- package/dist/yj-deploy.mjs +130 -114
- package/dist/yj-deploy.umd.js +7 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- ✔ 支持灵活配置dockerfile,upload.sh等脚本内容
|
|
12
12
|
- ✔ 支持多种方式配置 `镜像仓库` `镜像环境`等参数
|
|
13
13
|
- ✔ 支持懒上传,仅上传新增部分文件,提升上传效率
|
|
14
|
+
- ✔ 支持失败自动重新上传,支持配置次数,防止网络不稳定可能导致上传失败
|
|
14
15
|
> 全程不需要连接跳板机做任何操作即可完成镜像推送
|
|
15
16
|
|
|
16
17
|
## 运行情况
|
|
@@ -74,7 +75,12 @@ const deploy = new yjDeploy({
|
|
|
74
75
|
'echo "- 镜像环境: $2"',
|
|
75
76
|
'docker build -t harbor.yunjingtech.cn:30002/$1/$tag .',
|
|
76
77
|
'docker push harbor.yunjingtech.cn:30002/$1/$tag',
|
|
78
|
+
'if [ $? -eq 0 ];then',
|
|
79
|
+
'echo - 推送成功',
|
|
77
80
|
'echo - 镜像地址: harbor.yunjingtech.cn:30002/$1/$tag',
|
|
81
|
+
'else',
|
|
82
|
+
'echo - 镜像推送失败,请重试或联系运维人员,如需查看全部docker日志,请在命令后添加 --allLog查看更多信息',
|
|
83
|
+
'fi',
|
|
78
84
|
]
|
|
79
85
|
},
|
|
80
86
|
|
|
@@ -89,7 +95,9 @@ const deploy = new yjDeploy({
|
|
|
89
95
|
|
|
90
96
|
allLog: false, // 是否打印所有log信息,主要是包含推送镜像阶段的日志,如果报错可以打开此开关
|
|
91
97
|
|
|
92
|
-
lazyUpload: false // 是否开启懒上传,针对小程序图片等仅需要部署新增部分文件的项目有奇效
|
|
98
|
+
lazyUpload: false, // 是否开启懒上传,针对小程序图片等仅需要部署新增部分文件的项目有奇效
|
|
99
|
+
|
|
100
|
+
rePushNum: 3, // 重新推送次数,为0代表不重新推送
|
|
93
101
|
}
|
|
94
102
|
```
|
|
95
103
|
|
|
@@ -260,6 +268,9 @@ node uploader.js --tmpName=test
|
|
|
260
268
|
|
|
261
269
|
## 升级日志
|
|
262
270
|
|
|
271
|
+
### v0.0.8
|
|
272
|
+
支持判断是否推送成功,如果推送失败,则自动重试三次,可以手动配置重试次数
|
|
273
|
+
|
|
263
274
|
### v0.0.6
|
|
264
275
|
支持懒上传文件,大幅提升上传速度
|
|
265
276
|
|
package/dist/yj-deploy.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var $ = (f, e,
|
|
4
|
-
const { stdout:
|
|
1
|
+
var L = Object.defineProperty;
|
|
2
|
+
var v = (f, e, o) => e in f ? L(f, e, { enumerable: !0, configurable: !0, writable: !0, value: o }) : f[e] = o;
|
|
3
|
+
var $ = (f, e, o) => (v(f, typeof e != "symbol" ? e + "" : e, o), o);
|
|
4
|
+
const { stdout: F } = require("single-line-log"), A = require("path"), C = require("fs"), { Client: N } = require("ssh2");
|
|
5
5
|
module.exports = class {
|
|
6
6
|
constructor(e) {
|
|
7
7
|
$(this, "config", {
|
|
@@ -40,7 +40,12 @@ module.exports = class {
|
|
|
40
40
|
'echo "- 镜像环境: $2"',
|
|
41
41
|
"docker build -t harbor.yunjingtech.cn:30002/$1/$tag .",
|
|
42
42
|
"docker push harbor.yunjingtech.cn:30002/$1/$tag",
|
|
43
|
-
"
|
|
43
|
+
"if [ $? -eq 0 ];then",
|
|
44
|
+
"echo - 推送成功",
|
|
45
|
+
"echo - 镜像地址: harbor.yunjingtech.cn:30002/$1/$tag",
|
|
46
|
+
"else",
|
|
47
|
+
"echo - 镜像推送失败,请重试或联系运维人员,如需查看全部docker日志,请在命令后添加 --allLog查看更多信息",
|
|
48
|
+
"fi"
|
|
44
49
|
]
|
|
45
50
|
},
|
|
46
51
|
// 上传dist目录信息
|
|
@@ -53,8 +58,10 @@ module.exports = class {
|
|
|
53
58
|
// 文件并行上传数量,如果报错,可以减少此配置
|
|
54
59
|
allLog: !1,
|
|
55
60
|
// 是否显示全部日志
|
|
56
|
-
lazyUpload: !1
|
|
61
|
+
lazyUpload: !1,
|
|
57
62
|
// 是否开启懒上传
|
|
63
|
+
rePushNum: 3
|
|
64
|
+
// 重新推送次数,为0代表不重新推送
|
|
58
65
|
});
|
|
59
66
|
$(this, "ssh2Conn", null);
|
|
60
67
|
// 上传状态
|
|
@@ -107,9 +114,9 @@ module.exports = class {
|
|
|
107
114
|
console.log("- 请配置跳板机密码 password");
|
|
108
115
|
return;
|
|
109
116
|
}
|
|
110
|
-
this.uploading || (this.ssh2Conn = new
|
|
111
|
-
console.log("- 跳板机连接成功!"), this.ssh2Conn.sftp(async (e,
|
|
112
|
-
e && (console.log("- sftp连接失败"), this.breakConnect()), this.config.namespace || (console.log("- 请配置项目命名空间 namespace"), this.breakConnect()), this.uploading = !0, this.getArgv("--reset") !== void 0 && (console.log("- 重置项目"), await this.onShell(`rm -r ${this.config.rootDir}/${this.config.namespace}`)), this.upLoadProject(
|
|
117
|
+
this.uploading || (this.ssh2Conn = new N(), this.ssh2Conn.on("ready", () => {
|
|
118
|
+
console.log("- 跳板机连接成功!"), this.ssh2Conn.sftp(async (e, o) => {
|
|
119
|
+
e && (console.log("- sftp连接失败"), this.breakConnect()), this.config.namespace || (console.log("- 请配置项目命名空间 namespace"), this.breakConnect()), this.uploading = !0, this.getArgv("--reset") !== void 0 && (console.log("- 重置项目"), await this.onShell(`rm -r ${this.config.rootDir}/${this.config.namespace}`)), this.upLoadProject(o, `${this.config.rootDir}/${this.config.namespace}`);
|
|
113
120
|
});
|
|
114
121
|
}).connect({
|
|
115
122
|
host: this.config.host,
|
|
@@ -134,24 +141,24 @@ module.exports = class {
|
|
|
134
141
|
* @returns Promise
|
|
135
142
|
*/
|
|
136
143
|
onShell(e) {
|
|
137
|
-
const
|
|
138
|
-
return new Promise((
|
|
139
|
-
this.ssh2Conn.shell((
|
|
140
|
-
if (
|
|
141
|
-
console.log("- 远程命令错误:" +
|
|
144
|
+
const o = this.getArgv("--allLog") !== void 0;
|
|
145
|
+
return new Promise((t, r) => {
|
|
146
|
+
this.ssh2Conn.shell((l, n) => {
|
|
147
|
+
if (l) {
|
|
148
|
+
console.log("- 远程命令错误:" + l, e), this.breakConnect(), r(l);
|
|
142
149
|
return;
|
|
143
150
|
}
|
|
144
|
-
let
|
|
145
|
-
|
|
146
|
-
i
|
|
151
|
+
let i = [];
|
|
152
|
+
n.on("close", () => {
|
|
153
|
+
t(i.toString());
|
|
147
154
|
}).on("data", (c) => {
|
|
148
155
|
let a = c.toString();
|
|
149
|
-
(
|
|
156
|
+
(o || this.config.allLog || a.startsWith("- ")) && i.push(`
|
|
150
157
|
` + a);
|
|
151
158
|
}).stderr.on("data", (c) => {
|
|
152
159
|
console.log(`- 远程命令错误:
|
|
153
|
-
` + c), this.breakConnect(),
|
|
154
|
-
}),
|
|
160
|
+
` + c), this.breakConnect(), r(c);
|
|
161
|
+
}), n.end(e + `
|
|
155
162
|
exit
|
|
156
163
|
`);
|
|
157
164
|
});
|
|
@@ -162,19 +169,19 @@ exit
|
|
|
162
169
|
* @param {*} sftp
|
|
163
170
|
* @param {*} dir
|
|
164
171
|
*/
|
|
165
|
-
projectInit(e,
|
|
166
|
-
return new Promise(async (
|
|
167
|
-
e.mkdir(
|
|
168
|
-
if (
|
|
169
|
-
|
|
172
|
+
projectInit(e, o) {
|
|
173
|
+
return new Promise(async (t, r) => {
|
|
174
|
+
e.mkdir(o, async (l) => {
|
|
175
|
+
if (l)
|
|
176
|
+
r(l);
|
|
170
177
|
else {
|
|
171
178
|
console.log("- 正在创建dockerfile");
|
|
172
|
-
const
|
|
173
|
-
await this.onShell(`touch ${
|
|
174
|
-
`)}' > ${
|
|
175
|
-
const
|
|
176
|
-
await this.onShell(`touch ${
|
|
177
|
-
`)}' > ${
|
|
179
|
+
const n = `${o}/${this.config.dockerfile.name}`;
|
|
180
|
+
await this.onShell(`touch ${n}`), await this.onShell(`echo '${this.config.dockerfile.content.join(`
|
|
181
|
+
`)}' > ${n}`), console.log("- 正在创建upload.sh");
|
|
182
|
+
const i = `${o}/${this.config.upload.name}`;
|
|
183
|
+
await this.onShell(`touch ${i}`), await this.onShell(`echo '${this.config.upload.content.join(`
|
|
184
|
+
`)}' > ${i}`), t();
|
|
178
185
|
}
|
|
179
186
|
});
|
|
180
187
|
});
|
|
@@ -185,10 +192,10 @@ exit
|
|
|
185
192
|
* @param {*} remotePath
|
|
186
193
|
* @returns
|
|
187
194
|
*/
|
|
188
|
-
checkRemoteFile(e,
|
|
189
|
-
return new Promise((
|
|
190
|
-
e.stat(
|
|
191
|
-
|
|
195
|
+
checkRemoteFile(e, o) {
|
|
196
|
+
return new Promise((t) => {
|
|
197
|
+
e.stat(o, (r) => {
|
|
198
|
+
t(!r);
|
|
192
199
|
});
|
|
193
200
|
});
|
|
194
201
|
}
|
|
@@ -197,89 +204,98 @@ exit
|
|
|
197
204
|
* @param {*} sftp
|
|
198
205
|
* @param {*} dir
|
|
199
206
|
*/
|
|
200
|
-
upLoadProject(e,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
+
upLoadProject(e, o) {
|
|
208
|
+
let t = 0;
|
|
209
|
+
typeof this.config.rePushNum == "number" && this.config.rePushNum > 0 && (t = this.config.rePushNum), e.readdir(o, async (r) => {
|
|
210
|
+
if (r) {
|
|
211
|
+
console.log("- 跳板机不存在项目, 开始创建项目"), this.projectInit(e, o).then(() => {
|
|
212
|
+
console.log(`- ${this.config.namespace}项目创建成功`), this.upLoadProject(e, o);
|
|
213
|
+
}).catch((s) => {
|
|
214
|
+
console.log(`- 创建项目失败: ${s}`), this.breakConnect();
|
|
207
215
|
});
|
|
208
216
|
return;
|
|
209
217
|
}
|
|
210
|
-
const
|
|
211
|
-
if (
|
|
218
|
+
const l = this.getArgv("--lazyUpload") !== void 0 || this.config.lazyUpload, n = `${o}/${this.config.dist.name}`;
|
|
219
|
+
if (l || await this.onShell(`rm -rf ${n}`), await this.onShell(`mkdir ${n}`), !this.config.fileDir) {
|
|
212
220
|
console.log("- 请配置待上传文件目录 fileDir"), this.breakConnect();
|
|
213
221
|
return;
|
|
214
222
|
}
|
|
215
|
-
const { localFileList:
|
|
216
|
-
if (
|
|
223
|
+
const { localFileList: i, dirList: c } = await this.getFilesInDirectory(this.config.fileDir);
|
|
224
|
+
if (i.length === 0) {
|
|
217
225
|
console.log("- 待上传目录没有获取到文件,请检查"), this.breakConnect();
|
|
218
226
|
return;
|
|
219
227
|
}
|
|
220
|
-
let
|
|
221
|
-
const
|
|
228
|
+
let a = 0, p = i.length;
|
|
229
|
+
const y = (s) => new Promise(async (u, g) => {
|
|
222
230
|
try {
|
|
223
|
-
if (
|
|
231
|
+
if (l && await this.checkRemoteFile(e, `${n}${s.remotePath}`))
|
|
224
232
|
return u();
|
|
225
|
-
const h =
|
|
226
|
-
|
|
227
|
-
this.progressBar(++
|
|
228
|
-
}),
|
|
229
|
-
console.log(`- 文件 ${
|
|
230
|
-
}), h.pipe(
|
|
233
|
+
const h = C.createReadStream(s.localPath), b = e.createWriteStream(`${n}${s.remotePath}`);
|
|
234
|
+
b.on("close", () => {
|
|
235
|
+
this.progressBar(++a, p), u();
|
|
236
|
+
}), b.on("error", (k) => {
|
|
237
|
+
console.log(`- 文件 ${s.remotePath} 上传失败:${k}`), g(k), this.breakConnect();
|
|
238
|
+
}), h.pipe(b);
|
|
231
239
|
} catch (h) {
|
|
232
240
|
g(h);
|
|
233
241
|
}
|
|
234
|
-
}),
|
|
242
|
+
}), d = (s, u) => {
|
|
235
243
|
let g = [];
|
|
236
|
-
for (let h = 0; h <
|
|
237
|
-
g.push(
|
|
244
|
+
for (let h = 0; h < s.length; h += u)
|
|
245
|
+
g.push(s.slice(h, h + u));
|
|
238
246
|
return g;
|
|
239
|
-
},
|
|
240
|
-
if (
|
|
247
|
+
}, m = (s) => {
|
|
248
|
+
if (s.length === 0)
|
|
241
249
|
return Promise.resolve();
|
|
242
|
-
const u =
|
|
243
|
-
return Promise.all(u.map((g) =>
|
|
244
|
-
},
|
|
245
|
-
if (
|
|
250
|
+
const u = s.shift();
|
|
251
|
+
return Promise.all(u.map((g) => y(g))).then(() => m(s));
|
|
252
|
+
}, P = (s) => {
|
|
253
|
+
if (s.length === 0)
|
|
246
254
|
return Promise.resolve();
|
|
247
|
-
let g =
|
|
248
|
-
return this.onShell(`mkdir -p ${g}`).then(() =>
|
|
255
|
+
let g = s.shift().map((h) => `${n}${h}`).join(" ");
|
|
256
|
+
return this.onShell(`mkdir -p ${g}`).then(() => P(s));
|
|
249
257
|
};
|
|
250
|
-
let
|
|
251
|
-
await
|
|
258
|
+
let w = [...d(c, this.config.parallelDir || 20)];
|
|
259
|
+
await P(w), console.log("- 创建目录完成"), await new Promise((s) => {
|
|
252
260
|
setTimeout(() => {
|
|
253
|
-
|
|
261
|
+
s();
|
|
254
262
|
}, 500);
|
|
255
263
|
});
|
|
256
|
-
let
|
|
257
|
-
await
|
|
258
|
-
let
|
|
259
|
-
if (await this.isNewShell(e,
|
|
260
|
-
const
|
|
261
|
-
|
|
264
|
+
let D = [...d(i, this.config.parallelFile || 50)];
|
|
265
|
+
await m(D), l && (this.progressBar(p, p), console.log("\x1B[32m%s\x1B[0m", `- 已开启懒上传,本次共上传 ${a} 个文件`)), console.log("- 文件上传成功"), console.log("- 开始推送镜像");
|
|
266
|
+
let S = `cd ${o} && sh ${this.config.upload.name}`;
|
|
267
|
+
if (await this.isNewShell(e, o)) {
|
|
268
|
+
const s = this.getArgv("--imageStore") || this.config.imageStore;
|
|
269
|
+
S += ` ${s}`;
|
|
262
270
|
} else
|
|
263
271
|
console.log(`
|
|
264
272
|
- warning 检测到当前脚本为手动创建,不支持镜像仓库配置,imageStore将失效`), console.log("- warning 如需支持imageStore参数,请在命令行后添加 --reset 重新生成脚本");
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
273
|
+
const x = this.getArgv("--tmpName") || this.config.tmpName;
|
|
274
|
+
S += ` ${x}`;
|
|
275
|
+
const j = Array.from(new Array(t + 1)).map(() => this.onShell(S));
|
|
276
|
+
await this.pushMirrorImage(j, j.length), this.breakConnect();
|
|
269
277
|
});
|
|
270
278
|
}
|
|
279
|
+
// 推送镜像
|
|
280
|
+
async pushMirrorImage(e, o) {
|
|
281
|
+
if (e.length === 0)
|
|
282
|
+
return console.log("- 镜像推送失败,请添加 --allLog 查看全部日志"), null;
|
|
283
|
+
const t = await e[0];
|
|
284
|
+
return t.includes("- 镜像推送失败") ? (console.log(t), e.length > 1 && console.log(`- 正在尝试第 ${o - e.length + 1} 次推送...`), this.pushMirrorImage(e.slice(1), o)) : (console.log(t), console.log("- 镜像推送结束"), t);
|
|
285
|
+
}
|
|
271
286
|
/**
|
|
272
287
|
* 获取脚本内容是不是自动创建的脚本
|
|
273
288
|
* 兼容用户手动根据运维文档创建的推送脚本,因参数不同可能报错的问题
|
|
274
289
|
* 有 $2代表是自动创建的版本,否则老版本
|
|
275
290
|
*/
|
|
276
|
-
async isNewShell(e,
|
|
277
|
-
return new Promise((
|
|
278
|
-
e.readFile(`${
|
|
279
|
-
if (
|
|
280
|
-
return
|
|
281
|
-
const
|
|
282
|
-
i
|
|
291
|
+
async isNewShell(e, o) {
|
|
292
|
+
return new Promise((t, r) => {
|
|
293
|
+
e.readFile(`${o}/${this.config.upload.name}`, (l, n) => {
|
|
294
|
+
if (l)
|
|
295
|
+
return r(!1);
|
|
296
|
+
const i = n.toString();
|
|
297
|
+
i.includes("- 镜像推送失败") || console.log(`
|
|
298
|
+
- warning 检测到当前脚本可能不是最新版本,推送失败可能不会提示,且没有失败自动重试功能,请在命令后添加 --reset 重新生成脚本`), t(i.includes("$2"));
|
|
283
299
|
});
|
|
284
300
|
});
|
|
285
301
|
}
|
|
@@ -288,41 +304,41 @@ exit
|
|
|
288
304
|
* @param description 命令行开头的文字信息
|
|
289
305
|
* @param bar_length 进度条的长度(单位:字符),默认设为 25
|
|
290
306
|
*/
|
|
291
|
-
progressBar(e,
|
|
292
|
-
let
|
|
293
|
-
for (let a = 0; a <
|
|
294
|
-
|
|
295
|
-
let
|
|
296
|
-
for (let a = 0; a <
|
|
297
|
-
|
|
298
|
-
let c = `- 文件上传进度: ${
|
|
299
|
-
|
|
307
|
+
progressBar(e, o, t = 25) {
|
|
308
|
+
let r = (e / o).toFixed(4), l = Math.floor(r * t), n = "";
|
|
309
|
+
for (let a = 0; a < l; a++)
|
|
310
|
+
n += "█";
|
|
311
|
+
let i = "";
|
|
312
|
+
for (let a = 0; a < t - l; a++)
|
|
313
|
+
i += "░";
|
|
314
|
+
let c = `- 文件上传进度: ${n}${i} (${e}/${o}) ${(100 * r).toFixed(2)}%`;
|
|
315
|
+
F(c), e == o && console.log("");
|
|
300
316
|
}
|
|
301
317
|
/**
|
|
302
318
|
* 获取目录下所有文件并分类
|
|
303
319
|
* @param {*} dir
|
|
304
320
|
*/
|
|
305
321
|
getFilesInDirectory(e) {
|
|
306
|
-
const
|
|
307
|
-
return new Promise((
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
let
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
localPath:
|
|
322
|
+
const o = [], t = [];
|
|
323
|
+
return new Promise((r, l) => {
|
|
324
|
+
const n = (i, c, a) => {
|
|
325
|
+
const p = C.readdirSync(i);
|
|
326
|
+
let y = !1;
|
|
327
|
+
p.forEach((d) => {
|
|
328
|
+
const m = A.join(i, d), w = C.statSync(m).isDirectory();
|
|
329
|
+
w ? y = !0 : o.push({
|
|
330
|
+
localPath: m,
|
|
315
331
|
// 本地路径
|
|
316
332
|
remotePath: `${c}${d}`,
|
|
317
333
|
// 远程路径
|
|
318
334
|
level: a
|
|
319
335
|
// 文件或目录层级
|
|
320
|
-
}),
|
|
321
|
-
}),
|
|
336
|
+
}), w && n(m, `${c}${d}/`, a + 1);
|
|
337
|
+
}), y || t.push(c);
|
|
322
338
|
};
|
|
323
|
-
|
|
324
|
-
localFileList:
|
|
325
|
-
dirList:
|
|
339
|
+
n(e, "/", 0), r({
|
|
340
|
+
localFileList: o,
|
|
341
|
+
dirList: t
|
|
326
342
|
});
|
|
327
343
|
});
|
|
328
344
|
}
|
|
@@ -331,10 +347,10 @@ exit
|
|
|
331
347
|
* @param { string } name
|
|
332
348
|
*/
|
|
333
349
|
getArgv(e) {
|
|
334
|
-
const
|
|
335
|
-
let
|
|
336
|
-
return
|
|
337
|
-
|
|
338
|
-
}),
|
|
350
|
+
const o = process.argv;
|
|
351
|
+
let t;
|
|
352
|
+
return o.forEach((r) => {
|
|
353
|
+
r.indexOf(e) > -1 && (t = r.split("=")[1] || "");
|
|
354
|
+
}), t;
|
|
339
355
|
}
|
|
340
356
|
};
|
package/dist/yj-deploy.umd.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
(function(h){typeof define=="function"&&define.amd?define(h):h()})(function(){"use strict";var
|
|
1
|
+
(function(h){typeof define=="function"&&define.amd?define(h):h()})(function(){"use strict";var F=Object.defineProperty;var A=(h,u,f)=>u in h?F(h,u,{enumerable:!0,configurable:!0,writable:!0,value:f}):h[u]=f;var w=(h,u,f)=>(A(h,typeof u!="symbol"?u+"":u,f),f);const{stdout:h}=require("single-line-log"),u=require("path"),f=require("fs"),{Client:x}=require("ssh2");module.exports=class{constructor(e){w(this,"config",{host:"",port:"",username:"",password:"",namespace:"",imageStore:"dev-images",tmpName:"dev",delay:0,fileDir:"",rootDir:"/home/yjweb",dockerfile:{name:"Dockerfile",content:["FROM harbor.yunjingtech.cn:30002/yj-base/nginx:latest","RUN rm -rf /usr/share/nginx/html/*","COPY dist /usr/share/nginx/html/"]},upload:{name:"upload.sh",content:["#!/bin/sh","tag=`basename \\`pwd\\``:$2-`date +%Y%m%d`",'echo "- 镜像仓库: $1"','echo "- 镜像环境: $2"',"docker build -t harbor.yunjingtech.cn:30002/$1/$tag .","docker push harbor.yunjingtech.cn:30002/$1/$tag","if [ $? -eq 0 ];then","echo - 推送成功","echo - 镜像地址: harbor.yunjingtech.cn:30002/$1/$tag","else","echo - 镜像推送失败,请重试或联系运维人员,如需查看全部docker日志,请在命令后添加 --allLog查看更多信息","fi"]},dist:{name:"dist"},parallelDir:20,parallelFile:50,allLog:!1,lazyUpload:!1,rePushNum:3});w(this,"ssh2Conn",null);w(this,"uploading",!1);w(this,"trim",null);return this.config=Object.assign(this.config,e),{name:"yj-deploy",isPut:this.isPut.bind(this),upload:this.upload.bind(this),apply:this.apply.bind(this),closeBundle:this.isPut.bind(this)}}apply(e){return e&&e.hooks&&e.hooks.done&&e.hooks.done.tap("yj-deploy",()=>{this.isPut()}),"build"}isPut(){this.getArgv("--deploy")!=null&&(clearTimeout(this.trim),this.trim=setTimeout(()=>{this.upload()},this.config.delay||0))}upload(){if(console.log("-------------- deploy-start --------------"),!this.config.host){console.log("- 请配置跳板机地址 host");return}if(!this.config.port){console.log("- 请配置跳板机端口 port");return}if(!this.config.username){console.log("- 请配置跳板机账号 username");return}if(!this.config.password){console.log("- 请配置跳板机密码 password");return}this.uploading||(this.ssh2Conn=new x,this.ssh2Conn.on("ready",()=>{console.log("- 跳板机连接成功!"),this.ssh2Conn.sftp(async(e,t)=>{e&&(console.log("- sftp连接失败"),this.breakConnect()),this.config.namespace||(console.log("- 请配置项目命名空间 namespace"),this.breakConnect()),this.uploading=!0,this.getArgv("--reset")!==void 0&&(console.log("- 重置项目"),await this.onShell(`rm -r ${this.config.rootDir}/${this.config.namespace}`)),this.upLoadProject(t,`${this.config.rootDir}/${this.config.namespace}`)})}).connect({host:this.config.host,port:this.config.port,username:this.config.username,password:this.config.password}),this.ssh2Conn.on("error",e=>{console.log(`- 连接失败: ${e}`),this.breakConnect()}),this.ssh2Conn.on("end",()=>{this.uploading=!1}))}breakConnect(){this.uploading=!1,console.log("- 已断开连接"),console.log("-------------- deploy-end --------------"),this.ssh2Conn.end()}onShell(e){const t=this.getArgv("--allLog")!==void 0;return new Promise((o,l)=>{this.ssh2Conn.shell((r,s)=>{if(r){console.log("- 远程命令错误:"+r,e),this.breakConnect(),l(r);return}let i=[];s.on("close",()=>{o(i.toString())}).on("data",c=>{let a=c.toString();(t||this.config.allLog||a.startsWith("- "))&&i.push(`
|
|
2
2
|
`+a)}).stderr.on("data",c=>{console.log(`- 远程命令错误:
|
|
3
|
-
`+c),this.breakConnect(),
|
|
3
|
+
`+c),this.breakConnect(),l(c)}),s.end(e+`
|
|
4
4
|
exit
|
|
5
|
-
`)})})}projectInit(e,t){return new Promise(async(
|
|
6
|
-
`)}' > ${
|
|
7
|
-
`)}' > ${
|
|
8
|
-
- warning 检测到当前脚本为手动创建,不支持镜像仓库配置,imageStore将失效`),console.log("- warning 如需支持imageStore参数,请在命令行后添加 --reset 重新生成脚本");const
|
|
5
|
+
`)})})}projectInit(e,t){return new Promise(async(o,l)=>{e.mkdir(t,async r=>{if(r)l(r);else{console.log("- 正在创建dockerfile");const s=`${t}/${this.config.dockerfile.name}`;await this.onShell(`touch ${s}`),await this.onShell(`echo '${this.config.dockerfile.content.join(`
|
|
6
|
+
`)}' > ${s}`),console.log("- 正在创建upload.sh");const i=`${t}/${this.config.upload.name}`;await this.onShell(`touch ${i}`),await this.onShell(`echo '${this.config.upload.content.join(`
|
|
7
|
+
`)}' > ${i}`),o()}})})}checkRemoteFile(e,t){return new Promise(o=>{e.stat(t,l=>{o(!l)})})}upLoadProject(e,t){let o=0;typeof this.config.rePushNum=="number"&&this.config.rePushNum>0&&(o=this.config.rePushNum),e.readdir(t,async l=>{if(l){console.log("- 跳板机不存在项目, 开始创建项目"),this.projectInit(e,t).then(()=>{console.log(`- ${this.config.namespace}项目创建成功`),this.upLoadProject(e,t)}).catch(n=>{console.log(`- 创建项目失败: ${n}`),this.breakConnect()});return}const r=this.getArgv("--lazyUpload")!==void 0||this.config.lazyUpload,s=`${t}/${this.config.dist.name}`;if(r||await this.onShell(`rm -rf ${s}`),await this.onShell(`mkdir ${s}`),!this.config.fileDir){console.log("- 请配置待上传文件目录 fileDir"),this.breakConnect();return}const{localFileList:i,dirList:c}=await this.getFilesInDirectory(this.config.fileDir);if(i.length===0){console.log("- 待上传目录没有获取到文件,请检查"),this.breakConnect();return}let a=0,y=i.length;const P=n=>new Promise(async(m,d)=>{try{if(r&&await this.checkRemoteFile(e,`${s}${n.remotePath}`))return m();const g=f.createReadStream(n.localPath),C=e.createWriteStream(`${s}${n.remotePath}`);C.on("close",()=>{this.progressBar(++a,y),m()}),C.on("error",j=>{console.log(`- 文件 ${n.remotePath} 上传失败:${j}`),d(j),this.breakConnect()}),g.pipe(C)}catch(g){d(g)}}),p=(n,m)=>{let d=[];for(let g=0;g<n.length;g+=m)d.push(n.slice(g,g+m));return d},$=n=>{if(n.length===0)return Promise.resolve();const m=n.shift();return Promise.all(m.map(d=>P(d))).then(()=>$(n))},b=n=>{if(n.length===0)return Promise.resolve();let d=n.shift().map(g=>`${s}${g}`).join(" ");return this.onShell(`mkdir -p ${d}`).then(()=>b(n))};let S=[...p(c,this.config.parallelDir||20)];await b(S),console.log("- 创建目录完成"),await new Promise(n=>{setTimeout(()=>{n()},500)});let L=[...p(i,this.config.parallelFile||50)];await $(L),r&&(this.progressBar(y,y),console.log("\x1B[32m%s\x1B[0m",`- 已开启懒上传,本次共上传 ${a} 个文件`)),console.log("- 文件上传成功"),console.log("- 开始推送镜像");let k=`cd ${t} && sh ${this.config.upload.name}`;if(await this.isNewShell(e,t)){const n=this.getArgv("--imageStore")||this.config.imageStore;k+=` ${n}`}else console.log(`
|
|
8
|
+
- warning 检测到当前脚本为手动创建,不支持镜像仓库配置,imageStore将失效`),console.log("- warning 如需支持imageStore参数,请在命令行后添加 --reset 重新生成脚本");const v=this.getArgv("--tmpName")||this.config.tmpName;k+=` ${v}`;const D=Array.from(new Array(o+1)).map(()=>this.onShell(k));await this.pushMirrorImage(D,D.length),this.breakConnect()})}async pushMirrorImage(e,t){if(e.length===0)return console.log("- 镜像推送失败,请添加 --allLog 查看全部日志"),null;const o=await e[0];return o.includes("- 镜像推送失败")?(console.log(o),e.length>1&&console.log(`- 正在尝试第 ${t-e.length+1} 次推送...`),this.pushMirrorImage(e.slice(1),t)):(console.log(o),console.log("- 镜像推送结束"),o)}async isNewShell(e,t){return new Promise((o,l)=>{e.readFile(`${t}/${this.config.upload.name}`,(r,s)=>{if(r)return l(!1);const i=s.toString();i.includes("- 镜像推送失败")||console.log(`
|
|
9
|
+
- warning 检测到当前脚本可能不是最新版本,推送失败可能不会提示,且没有失败自动重试功能,请在命令后添加 --reset 重新生成脚本`),o(i.includes("$2"))})})}progressBar(e,t,o=25){let l=(e/t).toFixed(4),r=Math.floor(l*o),s="";for(let a=0;a<r;a++)s+="█";let i="";for(let a=0;a<o-r;a++)i+="░";let c=`- 文件上传进度: ${s}${i} (${e}/${t}) ${(100*l).toFixed(2)}%`;h(c),e==t&&console.log("")}getFilesInDirectory(e){const t=[],o=[];return new Promise((l,r)=>{const s=(i,c,a)=>{const y=f.readdirSync(i);let P=!1;y.forEach(p=>{const $=u.join(i,p),S=f.statSync($).isDirectory();S?P=!0:t.push({localPath:$,remotePath:`${c}${p}`,level:a}),S&&s($,`${c}${p}/`,a+1)}),P||o.push(c)};s(e,"/",0),l({localFileList:t,dirList:o})})}getArgv(e){const t=process.argv;let o;return t.forEach(l=>{l.indexOf(e)>-1&&(o=l.split("=")[1]||"")}),o}}});
|