yj-deploy 0.0.5 → 0.0.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/README.md CHANGED
@@ -10,6 +10,7 @@
10
10
  - ✔ 支持手动上传其他任何类型项目
11
11
  - ✔ 支持灵活配置dockerfile,upload.sh等脚本内容
12
12
  - ✔ 支持多种方式配置 `镜像仓库` `镜像环境`等参数
13
+ - ✔ 支持懒上传,仅上传新增部分文件,提升上传效率
13
14
  > 全程不需要连接跳板机做任何操作即可完成镜像推送
14
15
 
15
16
  ## 运行情况
@@ -86,7 +87,9 @@ const deploy = new yjDeploy({
86
87
 
87
88
  parallelFile: 50, // 文件并行上传数量,如果在上传文件阶段报错,可以减少此配置
88
89
 
89
- allLog: false // 是否打印所有log信息,主要是包含推送镜像阶段的日志,如果报错可以打开此开关
90
+ allLog: false, // 是否打印所有log信息,主要是包含推送镜像阶段的日志,如果报错可以打开此开关
91
+
92
+ lazyUpload: false // 是否开启懒上传,针对小程序图片等仅需要部署新增部分文件的项目有奇效
90
93
  }
91
94
  ```
92
95
 
@@ -236,6 +239,16 @@ node uploader.js --tmpName=test
236
239
  ### --allLog
237
240
  `--allLog` :是否打印所有log信息,主要是包含推送镜像阶段的日志,如果报错可以打开此开关
238
241
 
242
+ <br />
243
+
244
+ ### --lazyUpload
245
+ `--lazyUpload` :是否开启懒上传,如果开启,则不会上传全部文件,仅上传新增的文件。针对小程序图片等仅需要部署新增部分文件的项目有奇效
246
+
247
+ ::: warning
248
+ 如果小程序修改了相同路径和文件名的文件,会存在无法判断的情况(使用hash值能判断,但是成本过高),这种情况请关掉此选项,或使用 --reset 重置项目
249
+ :::
250
+
251
+
239
252
  <br />
240
253
 
241
254
  # 最后
@@ -247,6 +260,9 @@ node uploader.js --tmpName=test
247
260
 
248
261
  ## 升级日志
249
262
 
263
+ ### v0.0.6
264
+ 支持懒上传文件,大幅提升上传速度
265
+
250
266
  ### v0.0.5
251
267
  兼容根据运维文档手动创建的脚本推送镜像
252
268
 
@@ -1,10 +1,10 @@
1
- var L = Object.defineProperty;
2
- var v = (f, e, t) => e in f ? L(f, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : f[e] = t;
3
- var y = (f, e, t) => (v(f, typeof e != "symbol" ? e + "" : e, t), t);
4
- const { stdout: x } = require("single-line-log"), F = require("path"), b = require("fs"), { Client: A } = require("ssh2");
1
+ var x = Object.defineProperty;
2
+ var L = (f, e, t) => e in f ? x(f, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : f[e] = t;
3
+ var $ = (f, e, t) => (L(f, typeof e != "symbol" ? e + "" : e, t), t);
4
+ const { stdout: v } = require("single-line-log"), F = require("path"), k = require("fs"), { Client: A } = require("ssh2");
5
5
  module.exports = class {
6
6
  constructor(e) {
7
- y(this, "config", {
7
+ $(this, "config", {
8
8
  host: "",
9
9
  port: "",
10
10
  username: "",
@@ -51,14 +51,16 @@ module.exports = class {
51
51
  // 文件夹并行创建数量,如果报错,可以减少此配置
52
52
  parallelFile: 50,
53
53
  // 文件并行上传数量,如果报错,可以减少此配置
54
- allLog: !1
54
+ allLog: !1,
55
55
  // 是否显示全部日志
56
+ lazyUpload: !1
57
+ // 是否开启懒上传
56
58
  });
57
- y(this, "ssh2Conn", null);
59
+ $(this, "ssh2Conn", null);
58
60
  // 上传状态
59
- y(this, "uploading", !1);
61
+ $(this, "uploading", !1);
60
62
  // 定时器
61
- y(this, "trim", null);
63
+ $(this, "trim", null);
62
64
  return this.config = Object.assign(this.config, e), {
63
65
  name: "yj-deploy",
64
66
  isPut: this.isPut.bind(this),
@@ -133,23 +135,23 @@ module.exports = class {
133
135
  */
134
136
  onShell(e) {
135
137
  const t = this.getArgv("--allLog") !== void 0;
136
- return new Promise((n, s) => {
137
- this.ssh2Conn.shell((i, a) => {
138
- if (i) {
139
- console.log("- 远程命令错误:" + i, e), this.breakConnect(), s(i);
138
+ return new Promise((i, s) => {
139
+ this.ssh2Conn.shell((n, l) => {
140
+ if (n) {
141
+ console.log("- 远程命令错误:" + n, e), this.breakConnect(), s(n);
140
142
  return;
141
143
  }
142
- let l = [];
143
- a.on("close", () => {
144
- n(l.toString());
144
+ let r = [];
145
+ l.on("close", () => {
146
+ i(r.toString());
145
147
  }).on("data", (c) => {
146
- let r = c.toString();
147
- (t || this.config.allLog || r.startsWith("- ")) && l.push(`
148
- ` + r);
148
+ let a = c.toString();
149
+ (t || this.config.allLog || a.startsWith("- ")) && r.push(`
150
+ ` + a);
149
151
  }).stderr.on("data", (c) => {
150
152
  console.log(`- 远程命令错误:
151
153
  ` + c), this.breakConnect(), s(c);
152
- }), a.end(e + `
154
+ }), l.end(e + `
153
155
  exit
154
156
  `);
155
157
  });
@@ -161,30 +163,43 @@ exit
161
163
  * @param {*} dir
162
164
  */
163
165
  projectInit(e, t) {
164
- return new Promise(async (n, s) => {
165
- e.mkdir(t, async (i) => {
166
- if (i)
167
- s(i);
166
+ return new Promise(async (i, s) => {
167
+ e.mkdir(t, async (n) => {
168
+ if (n)
169
+ s(n);
168
170
  else {
169
171
  console.log("- 正在创建dockerfile");
170
- const a = `${t}/${this.config.dockerfile.name}`;
171
- await this.onShell(`touch ${a}`), await this.onShell(`echo '${this.config.dockerfile.content.join(`
172
- `)}' > ${a}`), console.log("- 正在创建upload.sh");
173
- const l = `${t}/${this.config.upload.name}`;
174
- await this.onShell(`touch ${l}`), await this.onShell(`echo '${this.config.upload.content.join(`
175
- `)}' > ${l}`), n();
172
+ const l = `${t}/${this.config.dockerfile.name}`;
173
+ await this.onShell(`touch ${l}`), await this.onShell(`echo '${this.config.dockerfile.content.join(`
174
+ `)}' > ${l}`), console.log("- 正在创建upload.sh");
175
+ const r = `${t}/${this.config.upload.name}`;
176
+ await this.onShell(`touch ${r}`), await this.onShell(`echo '${this.config.upload.content.join(`
177
+ `)}' > ${r}`), i();
176
178
  }
177
179
  });
178
180
  });
179
181
  }
182
+ /**
183
+ * 判断文件是否存在
184
+ * @param {*} sftp
185
+ * @param {*} remotePath
186
+ * @returns
187
+ */
188
+ checkRemoteFile(e, t) {
189
+ return new Promise((i) => {
190
+ e.stat(t, (s) => {
191
+ i(!s);
192
+ });
193
+ });
194
+ }
180
195
  /**
181
196
  * 上传项目
182
197
  * @param {*} sftp
183
198
  * @param {*} dir
184
199
  */
185
200
  upLoadProject(e, t) {
186
- e.readdir(t, async (n) => {
187
- if (n) {
201
+ e.readdir(t, async (i) => {
202
+ if (i) {
188
203
  console.log("- 跳板机不存在项目, 开始创建项目"), this.projectInit(e, t).then(() => {
189
204
  console.log(`- ${this.config.namespace}项目创建成功`), this.upLoadProject(e, t);
190
205
  }).catch((o) => {
@@ -192,101 +207,122 @@ exit
192
207
  });
193
208
  return;
194
209
  }
195
- const s = `${t}/${this.config.dist.name}`;
196
- if (await this.onShell(`rm -rf ${s}`), await this.onShell(`mkdir ${s}`), !this.config.fileDir) {
210
+ const s = this.getArgv("--lazyUpload") !== void 0 || this.config.lazyUpload, n = `${t}/${this.config.dist.name}`;
211
+ if (s || await this.onShell(`rm -rf ${n}`), await this.onShell(`mkdir ${n}`), !this.config.fileDir) {
197
212
  console.log("- 请配置待上传文件目录 fileDir"), this.breakConnect();
198
213
  return;
199
214
  }
200
- const { localFileList: i, dirList: a } = await this.getFilesInDirectory(this.config.fileDir);
201
- if (i.length === 0) {
215
+ const { localFileList: l, dirList: r } = await this.getFilesInDirectory(this.config.fileDir);
216
+ if (l.length === 0) {
202
217
  console.log("- 待上传目录没有获取到文件,请检查"), this.breakConnect();
203
218
  return;
204
219
  }
205
- let l = 0, c = i.length;
206
- const r = (o) => new Promise((u, g) => {
220
+ let c = 0, a = l.length;
221
+ const w = (o) => new Promise(async (u, g) => {
207
222
  try {
208
- const h = b.createReadStream(o.localPath), w = e.createWriteStream(`${s}${o.remotePath}`);
209
- w.on("close", () => {
210
- this.progressBar(++l, c), u();
211
- }), w.on("error", (S) => {
212
- console.log(`- 文件 ${o.remotePath} 上传失败:${S}`), g(S), this.breakConnect();
213
- }), h.pipe(w);
223
+ if (s && await this.checkRemoteFile(e, `${n}${o.remotePath}`))
224
+ return u();
225
+ const h = k.createReadStream(o.localPath), S = e.createWriteStream(`${n}${o.remotePath}`);
226
+ S.on("close", () => {
227
+ this.progressBar(++c, a), u();
228
+ }), S.on("error", (b) => {
229
+ console.log(`- 文件 ${o.remotePath} 上传失败:${b}`), g(b), this.breakConnect();
230
+ }), h.pipe(S);
214
231
  } catch (h) {
215
232
  g(h);
216
233
  }
217
- }), P = (o, u) => {
234
+ }), m = (o, u) => {
218
235
  let g = [];
219
236
  for (let h = 0; h < o.length; h += u)
220
237
  g.push(o.slice(h, h + u));
221
238
  return g;
222
- }, m = (o) => {
239
+ }, d = (o) => {
223
240
  if (o.length === 0)
224
241
  return Promise.resolve();
225
242
  const u = o.shift();
226
- return Promise.all(u.map((g) => r(g))).then(() => m(o));
227
- }, d = (o) => {
243
+ return Promise.all(u.map((g) => w(g))).then(() => d(o));
244
+ }, p = (o) => {
228
245
  if (o.length === 0)
229
246
  return Promise.resolve();
230
- let g = o.shift().map((h) => `${s}${h}`).join(" ");
231
- return this.onShell(`mkdir -p ${g}`).then(() => d(o));
247
+ let g = o.shift().map((h) => `${n}${h}`).join(" ");
248
+ return this.onShell(`mkdir -p ${g}`).then(() => p(o));
232
249
  };
233
- let $ = [...P(a, this.config.parallelDir || 20)];
234
- console.log("- 开始创建目录"), await d($), console.log("- 创建目录完成"), await new Promise((o) => {
250
+ let C = [...m(r, this.config.parallelDir || 20)];
251
+ await p(C), console.log("- 创建目录完成"), await new Promise((o) => {
235
252
  setTimeout(() => {
236
253
  o();
237
254
  }, 500);
238
255
  });
239
- let k = [...P(i, this.config.parallelFile || 50)];
240
- console.log("- 开始上传文件"), await m(k), console.log("- 文件上传成功"), console.log("- 开始推送镜像");
241
- let p = `cd ${t} && sh ${this.config.upload.name}`;
242
- const C = this.getArgv("--imageStore") || this.config.imageStore;
243
- p += ` ${C}`;
256
+ let y = [...m(l, this.config.parallelFile || 50)];
257
+ await d(y), s && (this.progressBar(a, a), console.log("\x1B[32m%s\x1B[0m", `- 已开启懒上传,本次共上传 ${c} 个文件`)), console.log("- 文件上传成功"), console.log("- 开始推送镜像");
258
+ let P = `cd ${t} && sh ${this.config.upload.name}`;
259
+ if (await this.isNewShell(e, t)) {
260
+ const o = this.getArgv("--imageStore") || this.config.imageStore;
261
+ P += ` ${o}`;
262
+ } else
263
+ console.log(`
264
+ - warning 检测到当前脚本为手动创建,不支持镜像仓库配置,imageStore将失效`), console.log("- warning 如需支持imageStore参数,请在命令行后添加 --reset 重新生成脚本");
244
265
  const j = this.getArgv("--tmpName") || this.config.tmpName;
245
- p += ` ${j}`;
246
- const D = await this.onShell(p);
266
+ P += ` ${j}`;
267
+ const D = await this.onShell(P);
247
268
  console.log(D), console.log("- 镜像推送完成"), this.breakConnect();
248
269
  });
249
270
  }
271
+ /**
272
+ * 获取脚本内容是不是自动创建的脚本
273
+ * 兼容用户手动根据运维文档创建的推送脚本,因参数不同可能报错的问题
274
+ * 有 $2代表是自动创建的版本,否则老版本
275
+ */
276
+ async isNewShell(e, t) {
277
+ return new Promise((i, s) => {
278
+ e.readFile(`${t}/${this.config.upload.name}`, (n, l) => {
279
+ if (n)
280
+ return s(!1);
281
+ const r = l.toString();
282
+ i(r.includes("$2"));
283
+ });
284
+ });
285
+ }
250
286
  /**
251
287
  * 进度条
252
288
  * @param description 命令行开头的文字信息
253
289
  * @param bar_length 进度条的长度(单位:字符),默认设为 25
254
290
  */
255
- progressBar(e, t, n = 25) {
256
- let s = (e / t).toFixed(4), i = Math.floor(s * n), a = "";
257
- for (let r = 0; r < i; r++)
258
- a += "█";
259
- let l = "";
260
- for (let r = 0; r < n - i; r++)
261
- l += "░";
262
- let c = `- 文件上传进度: ${a}${l} (${e}/${t}) ${(100 * s).toFixed(2)}%`;
263
- x(c), e == t && console.log("");
291
+ progressBar(e, t, i = 25) {
292
+ let s = (e / t).toFixed(4), n = Math.floor(s * i), l = "";
293
+ for (let a = 0; a < n; a++)
294
+ l += "█";
295
+ let r = "";
296
+ for (let a = 0; a < i - n; a++)
297
+ r += "░";
298
+ let c = `- 文件上传进度: ${l}${r} (${e}/${t}) ${(100 * s).toFixed(2)}%`;
299
+ v(c), e == t && console.log("");
264
300
  }
265
301
  /**
266
302
  * 获取目录下所有文件并分类
267
303
  * @param {*} dir
268
304
  */
269
305
  getFilesInDirectory(e) {
270
- const t = [], n = [];
271
- return new Promise((s, i) => {
272
- const a = (l, c, r) => {
273
- const P = b.readdirSync(l);
306
+ const t = [], i = [];
307
+ return new Promise((s, n) => {
308
+ const l = (r, c, a) => {
309
+ const w = k.readdirSync(r);
274
310
  let m = !1;
275
- P.forEach((d) => {
276
- const $ = F.join(l, d), p = b.statSync($).isDirectory();
277
- p ? m = !0 : t.push({
278
- localPath: $,
311
+ w.forEach((d) => {
312
+ const p = F.join(r, d), y = k.statSync(p).isDirectory();
313
+ y ? m = !0 : t.push({
314
+ localPath: p,
279
315
  // 本地路径
280
316
  remotePath: `${c}${d}`,
281
317
  // 远程路径
282
- level: r
318
+ level: a
283
319
  // 文件或目录层级
284
- }), p && a($, `${c}${d}/`, r + 1);
285
- }), m || n.push(c);
320
+ }), y && l(p, `${c}${d}/`, a + 1);
321
+ }), m || i.push(c);
286
322
  };
287
- a(e, "/", 0), s({
323
+ l(e, "/", 0), s({
288
324
  localFileList: t,
289
- dirList: n
325
+ dirList: i
290
326
  });
291
327
  });
292
328
  }
@@ -296,9 +332,9 @@ exit
296
332
  */
297
333
  getArgv(e) {
298
334
  const t = process.argv;
299
- let n;
335
+ let i;
300
336
  return t.forEach((s) => {
301
- s.indexOf(e) > -1 && (n = s.split("=")[1] || "");
302
- }), n;
337
+ s.indexOf(e) > -1 && (i = s.split("=")[1] || "");
338
+ }), i;
303
339
  }
304
340
  };
@@ -1,7 +1,8 @@
1
- (function(h){typeof define=="function"&&define.amd?define(h):h()})(function(){"use strict";var x=Object.defineProperty;var F=(h,u,f)=>u in h?x(h,u,{enumerable:!0,configurable:!0,writable:!0,value:f}):h[u]=f;var w=(h,u,f)=>(F(h,typeof u!="symbol"?u+"":u,f),f);const{stdout:h}=require("single-line-log"),u=require("path"),f=require("fs"),{Client:j}=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","echo - 镜像地址: harbor.yunjingtech.cn:30002/$1/$tag"]},dist:{name:"dist"},parallelDir:20,parallelFile:50,allLog:!1});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 j,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((s,n)=>{this.ssh2Conn.shell((i,a)=>{if(i){console.log("- 远程命令错误:"+i,e),this.breakConnect(),n(i);return}let l=[];a.on("close",()=>{s(l.toString())}).on("data",c=>{let r=c.toString();(t||this.config.allLog||r.startsWith("- "))&&l.push(`
2
- `+r)}).stderr.on("data",c=>{console.log(`- 远程命令错误:
3
- `+c),this.breakConnect(),n(c)}),a.end(e+`
1
+ (function(h){typeof define=="function"&&define.amd?define(h):h()})(function(){"use strict";var v=Object.defineProperty;var F=(h,f,u)=>f in h?v(h,f,{enumerable:!0,configurable:!0,writable:!0,value:u}):h[f]=u;var w=(h,f,u)=>(F(h,typeof f!="symbol"?f+"":f,u),u);const{stdout:h}=require("single-line-log"),f=require("path"),u=require("fs"),{Client:D}=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","echo - 镜像地址: harbor.yunjingtech.cn:30002/$1/$tag"]},dist:{name:"dist"},parallelDir:20,parallelFile:50,allLog:!1,lazyUpload:!1});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 D,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((n,s)=>{this.ssh2Conn.shell((i,l)=>{if(i){console.log("- 远程命令错误:"+i,e),this.breakConnect(),s(i);return}let r=[];l.on("close",()=>{n(r.toString())}).on("data",c=>{let a=c.toString();(t||this.config.allLog||a.startsWith("- "))&&r.push(`
2
+ `+a)}).stderr.on("data",c=>{console.log(`- 远程命令错误:
3
+ `+c),this.breakConnect(),s(c)}),l.end(e+`
4
4
  exit
5
- `)})})}projectInit(e,t){return new Promise(async(s,n)=>{e.mkdir(t,async i=>{if(i)n(i);else{console.log("- 正在创建dockerfile");const a=`${t}/${this.config.dockerfile.name}`;await this.onShell(`touch ${a}`),await this.onShell(`echo '${this.config.dockerfile.content.join(`
6
- `)}' > ${a}`),console.log("- 正在创建upload.sh");const l=`${t}/${this.config.upload.name}`;await this.onShell(`touch ${l}`),await this.onShell(`echo '${this.config.upload.content.join(`
7
- `)}' > ${l}`),s()}})})}upLoadProject(e,t){e.readdir(t,async s=>{if(s){console.log("- 跳板机不存在项目, 开始创建项目"),this.projectInit(e,t).then(()=>{console.log(`- ${this.config.namespace}项目创建成功`),this.upLoadProject(e,t)}).catch(o=>{console.log(`- 创建项目失败: ${o}`),this.breakConnect()});return}const n=`${t}/${this.config.dist.name}`;if(await this.onShell(`rm -rf ${n}`),await this.onShell(`mkdir ${n}`),!this.config.fileDir){console.log("- 请配置待上传文件目录 fileDir"),this.breakConnect();return}const{localFileList:i,dirList:a}=await this.getFilesInDirectory(this.config.fileDir);if(i.length===0){console.log("- 待上传目录没有获取到文件,请检查"),this.breakConnect();return}let l=0,c=i.length;const r=o=>new Promise((p,d)=>{try{const g=f.createReadStream(o.localPath),k=e.createWriteStream(`${n}${o.remotePath}`);k.on("close",()=>{this.progressBar(++l,c),p()}),k.on("error",C=>{console.log(`- 文件 ${o.remotePath} 上传失败:${C}`),d(C),this.breakConnect()}),g.pipe(k)}catch(g){d(g)}}),b=(o,p)=>{let d=[];for(let g=0;g<o.length;g+=p)d.push(o.slice(g,g+p));return d},y=o=>{if(o.length===0)return Promise.resolve();const p=o.shift();return Promise.all(p.map(d=>r(d))).then(()=>y(o))},m=o=>{if(o.length===0)return Promise.resolve();let d=o.shift().map(g=>`${n}${g}`).join(" ");return this.onShell(`mkdir -p ${d}`).then(()=>m(o))};let P=[...b(a,this.config.parallelDir||20)];console.log("- 开始创建目录"),await m(P),console.log("- 创建目录完成"),await new Promise(o=>{setTimeout(()=>{o()},500)});let S=[...b(i,this.config.parallelFile||50)];console.log("- 开始上传文件"),await y(S),console.log("- 文件上传成功"),console.log("- 开始推送镜像");let $=`cd ${t} && sh ${this.config.upload.name}`;const D=this.getArgv("--imageStore")||this.config.imageStore;$+=` ${D}`;const L=this.getArgv("--tmpName")||this.config.tmpName;$+=` ${L}`;const v=await this.onShell($);console.log(v),console.log("- 镜像推送完成"),this.breakConnect()})}progressBar(e,t,s=25){let n=(e/t).toFixed(4),i=Math.floor(n*s),a="";for(let r=0;r<i;r++)a+="█";let l="";for(let r=0;r<s-i;r++)l+="░";let c=`- 文件上传进度: ${a}${l} (${e}/${t}) ${(100*n).toFixed(2)}%`;h(c),e==t&&console.log("")}getFilesInDirectory(e){const t=[],s=[];return new Promise((n,i)=>{const a=(l,c,r)=>{const b=f.readdirSync(l);let y=!1;b.forEach(m=>{const P=u.join(l,m),$=f.statSync(P).isDirectory();$?y=!0:t.push({localPath:P,remotePath:`${c}${m}`,level:r}),$&&a(P,`${c}${m}/`,r+1)}),y||s.push(c)};a(e,"/",0),n({localFileList:t,dirList:s})})}getArgv(e){const t=process.argv;let s;return t.forEach(n=>{n.indexOf(e)>-1&&(s=n.split("=")[1]||"")}),s}}});
5
+ `)})})}projectInit(e,t){return new Promise(async(n,s)=>{e.mkdir(t,async i=>{if(i)s(i);else{console.log("- 正在创建dockerfile");const l=`${t}/${this.config.dockerfile.name}`;await this.onShell(`touch ${l}`),await this.onShell(`echo '${this.config.dockerfile.content.join(`
6
+ `)}' > ${l}`),console.log("- 正在创建upload.sh");const r=`${t}/${this.config.upload.name}`;await this.onShell(`touch ${r}`),await this.onShell(`echo '${this.config.upload.content.join(`
7
+ `)}' > ${r}`),n()}})})}checkRemoteFile(e,t){return new Promise(n=>{e.stat(t,s=>{n(!s)})})}upLoadProject(e,t){e.readdir(t,async n=>{if(n){console.log("- 跳板机不存在项目, 开始创建项目"),this.projectInit(e,t).then(()=>{console.log(`- ${this.config.namespace}项目创建成功`),this.upLoadProject(e,t)}).catch(o=>{console.log(`- 创建项目失败: ${o}`),this.breakConnect()});return}const s=this.getArgv("--lazyUpload")!==void 0||this.config.lazyUpload,i=`${t}/${this.config.dist.name}`;if(s||await this.onShell(`rm -rf ${i}`),await this.onShell(`mkdir ${i}`),!this.config.fileDir){console.log("- 请配置待上传文件目录 fileDir"),this.breakConnect();return}const{localFileList:l,dirList:r}=await this.getFilesInDirectory(this.config.fileDir);if(l.length===0){console.log("- 待上传目录没有获取到文件,请检查"),this.breakConnect();return}let c=0,a=l.length;const S=o=>new Promise(async(p,d)=>{try{if(s&&await this.checkRemoteFile(e,`${i}${o.remotePath}`))return p();const g=u.createReadStream(o.localPath),k=e.createWriteStream(`${i}${o.remotePath}`);k.on("close",()=>{this.progressBar(++c,a),p()}),k.on("error",C=>{console.log(`- 文件 ${o.remotePath} 上传失败:${C}`),d(C),this.breakConnect()}),g.pipe(k)}catch(g){d(g)}}),y=(o,p)=>{let d=[];for(let g=0;g<o.length;g+=p)d.push(o.slice(g,g+p));return d},m=o=>{if(o.length===0)return Promise.resolve();const p=o.shift();return Promise.all(p.map(d=>S(d))).then(()=>m(o))},$=o=>{if(o.length===0)return Promise.resolve();let d=o.shift().map(g=>`${i}${g}`).join(" ");return this.onShell(`mkdir -p ${d}`).then(()=>$(o))};let j=[...y(r,this.config.parallelDir||20)];await $(j),console.log("- 创建目录完成"),await new Promise(o=>{setTimeout(()=>{o()},500)});let P=[...y(l,this.config.parallelFile||50)];await m(P),s&&(this.progressBar(a,a),console.log("\x1B[32m%s\x1B[0m",`- 已开启懒上传,本次共上传 ${c} 个文件`)),console.log("- 文件上传成功"),console.log("- 开始推送镜像");let b=`cd ${t} && sh ${this.config.upload.name}`;if(await this.isNewShell(e,t)){const o=this.getArgv("--imageStore")||this.config.imageStore;b+=` ${o}`}else console.log(`
8
+ - warning 检测到当前脚本为手动创建,不支持镜像仓库配置,imageStore将失效`),console.log("- warning 如需支持imageStore参数,请在命令行后添加 --reset 重新生成脚本");const x=this.getArgv("--tmpName")||this.config.tmpName;b+=` ${x}`;const L=await this.onShell(b);console.log(L),console.log("- 镜像推送完成"),this.breakConnect()})}async isNewShell(e,t){return new Promise((n,s)=>{e.readFile(`${t}/${this.config.upload.name}`,(i,l)=>{if(i)return s(!1);const r=l.toString();n(r.includes("$2"))})})}progressBar(e,t,n=25){let s=(e/t).toFixed(4),i=Math.floor(s*n),l="";for(let a=0;a<i;a++)l+="█";let r="";for(let a=0;a<n-i;a++)r+="░";let c=`- 文件上传进度: ${l}${r} (${e}/${t}) ${(100*s).toFixed(2)}%`;h(c),e==t&&console.log("")}getFilesInDirectory(e){const t=[],n=[];return new Promise((s,i)=>{const l=(r,c,a)=>{const S=u.readdirSync(r);let y=!1;S.forEach(m=>{const $=f.join(r,m),P=u.statSync($).isDirectory();P?y=!0:t.push({localPath:$,remotePath:`${c}${m}`,level:a}),P&&l($,`${c}${m}/`,a+1)}),y||n.push(c)};l(e,"/",0),s({localFileList:t,dirList:n})})}getArgv(e){const t=process.argv;let n;return t.forEach(s=>{s.indexOf(e)>-1&&(n=s.split("=")[1]||"")}),n}}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yj-deploy",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "ssh sftp",
5
5
  "scripts": {
6
6
  "build": "vite build"