yj-deploy 0.0.3 → 0.0.5
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 +58 -28
- package/dist/yj-deploy.mjs +133 -111
- package/dist/yj-deploy.umd.js +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ const deploy = new yjDeploy({
|
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
## 完整配置(包含默认配置)
|
|
44
|
-
```
|
|
44
|
+
```javascript
|
|
45
45
|
{
|
|
46
46
|
host: '', // ip (必填)
|
|
47
47
|
port: '', // 端口 (必填)
|
|
@@ -80,16 +80,25 @@ const deploy = new yjDeploy({
|
|
|
80
80
|
// 上传dist目录信息 (除非运维改变,否则不要配置)
|
|
81
81
|
dist: {
|
|
82
82
|
name: 'dist'
|
|
83
|
-
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
parallelDir: 20, // 文件夹并行创建数量,如果在创建文件夹阶段报错,可以尝试减少此配置
|
|
86
|
+
|
|
87
|
+
parallelFile: 50, // 文件并行上传数量,如果在上传文件阶段报错,可以减少此配置
|
|
88
|
+
|
|
89
|
+
allLog: false // 是否打印所有log信息,主要是包含推送镜像阶段的日志,如果报错可以打开此开关
|
|
84
90
|
}
|
|
85
91
|
```
|
|
86
92
|
|
|
87
93
|
<br />
|
|
88
94
|
|
|
89
95
|
# 使用
|
|
96
|
+
|
|
90
97
|
## 配合打包命令使用
|
|
98
|
+
可以配合项目脚手架使用
|
|
99
|
+
|
|
100
|
+
### webpack 中使用
|
|
91
101
|
```javascript
|
|
92
|
-
// webpack 中使用
|
|
93
102
|
//vue.config.js
|
|
94
103
|
const path = require('path')
|
|
95
104
|
const yjDeploy = require('yj-deploy')
|
|
@@ -118,8 +127,8 @@ module.exports = {
|
|
|
118
127
|
// 使用 yarn deploy 或 npm run deploy
|
|
119
128
|
```
|
|
120
129
|
|
|
130
|
+
### vite 项目中使用
|
|
121
131
|
```javascript
|
|
122
|
-
// vite 项目中使用
|
|
123
132
|
//vite.config.js
|
|
124
133
|
import path from 'path'
|
|
125
134
|
import yjDeploy from 'yj-deploy'
|
|
@@ -145,11 +154,35 @@ export default defineConfig({
|
|
|
145
154
|
// 使用 yarn deploy 或 npm run deploy
|
|
146
155
|
```
|
|
147
156
|
|
|
157
|
+
### 上传任意项目
|
|
158
|
+
```javascript
|
|
159
|
+
// 1、在项目中创建uploader.js
|
|
160
|
+
// 2、配置和webpack插件模式相同
|
|
161
|
+
const deploy = new yjDeploy({
|
|
162
|
+
host: '',
|
|
163
|
+
port: '',
|
|
164
|
+
username: '',
|
|
165
|
+
password: '',
|
|
166
|
+
namespace: '', // 项目命名空间,等同于镜像地址目录名称
|
|
167
|
+
// 本地文件目录
|
|
168
|
+
fileDir: path.resolve('./dist'),
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
deploy.upload() // 开始上传
|
|
172
|
+
// 然后在项目根目录终端下运行如下命令(node直接上传不需要加--deploy动态指令)
|
|
173
|
+
node uploader.js
|
|
174
|
+
|
|
175
|
+
// 增加动态配置
|
|
176
|
+
node uploader.js --tmpName=test
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
|
|
148
180
|
## 动态配置项
|
|
149
181
|
由于部分项目可能需要区分环境,所以需要动态配置项,目前支持动态配置项如下:
|
|
150
182
|
<br />
|
|
151
183
|
|
|
152
|
-
|
|
184
|
+
### --deploy
|
|
185
|
+
`--deploy` :是否启用自动部署功能,在配合vite或webpack时需要使用,如果启用,在命令后跟上 --deploy即可
|
|
153
186
|
```javascript
|
|
154
187
|
// vite项目 package.json
|
|
155
188
|
"scripts": {
|
|
@@ -167,10 +200,12 @@ export default defineConfig({
|
|
|
167
200
|
|
|
168
201
|
<br />
|
|
169
202
|
|
|
170
|
-
|
|
203
|
+
### --reset
|
|
204
|
+
`--reset` :是否重置项目(默认为不重置),如果配置了此选项,则每次执行命令时会删除 `dockerfile` `upload.sh` 配置文件,然后重新初始化项目
|
|
171
205
|
|
|
172
206
|
<br />
|
|
173
207
|
|
|
208
|
+
### --imageStore
|
|
174
209
|
`--imageStore` :镜像仓库名称(默认为dev-images),在测试或者正式环境可能需要配置,需要推送到指定的镜像仓库时配置,增加此动态配置可以使用更灵活,同一套主配置可以满足多个环境推送到不同的镜像仓库,如:
|
|
175
210
|
```javascript
|
|
176
211
|
"scripts": {
|
|
@@ -183,6 +218,7 @@ export default defineConfig({
|
|
|
183
218
|
|
|
184
219
|
<br />
|
|
185
220
|
|
|
221
|
+
### --tmpName
|
|
186
222
|
`--tmpName` :镜像环境名称(默认为 dev),在测试或者正式环境可能需要配置,需要推送到指定的镜像环境时配置,防止镜像名重复导致覆盖,同一套主配置可以满足多个环境推送到不同的镜像地址,如:
|
|
187
223
|
```javascript
|
|
188
224
|
"scripts": {
|
|
@@ -195,33 +231,27 @@ export default defineConfig({
|
|
|
195
231
|
}
|
|
196
232
|
```
|
|
197
233
|
|
|
234
|
+
<br />
|
|
198
235
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// 1、在项目中创建uploader.js
|
|
202
|
-
// 2、配置和webpack插件模式相同
|
|
203
|
-
const deploy = new yjDeploy({
|
|
204
|
-
host: '',
|
|
205
|
-
port: '',
|
|
206
|
-
username: '',
|
|
207
|
-
password: '',
|
|
208
|
-
namespace: '', // 项目命名空间,等同于镜像地址目录名称
|
|
209
|
-
// 本地文件目录
|
|
210
|
-
fileDir: path.resolve('./dist'),
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
deploy.upload() // 开始上传
|
|
214
|
-
// 然后在项目根目录终端下运行如下命令(node直接上传不需要加--deploy动态指令)
|
|
215
|
-
node uploader.js
|
|
216
|
-
|
|
217
|
-
// 增加动态配置
|
|
218
|
-
node uploader.js --tmpName=test
|
|
219
|
-
```
|
|
236
|
+
### --allLog
|
|
237
|
+
`--allLog` :是否打印所有log信息,主要是包含推送镜像阶段的日志,如果报错可以打开此开关
|
|
220
238
|
|
|
239
|
+
<br />
|
|
221
240
|
|
|
222
241
|
# 最后
|
|
223
242
|
如有意见或建议,或bug或文档不清晰等问题,可随时向我反馈,或直接修改后提交,共同完善这个工具。
|
|
224
243
|
|
|
225
244
|
<br />
|
|
226
245
|
|
|
227
|
-
项目已提交到git:https://gitlab.yunjingtech.cn:10010/frontend/yj-deploy
|
|
246
|
+
项目已提交到git:https://gitlab.yunjingtech.cn:10010/frontend/yj-deploy
|
|
247
|
+
|
|
248
|
+
## 升级日志
|
|
249
|
+
|
|
250
|
+
### v0.0.5
|
|
251
|
+
兼容根据运维文档手动创建的脚本推送镜像
|
|
252
|
+
|
|
253
|
+
### v0.0.4
|
|
254
|
+
解决因为并行上传文件太多导致上传失败的问题,支持动态配置并行上传数据量
|
|
255
|
+
|
|
256
|
+
### v0.0.3
|
|
257
|
+
解决文件夹嵌套太深,可能会导致上传文件时文件夹还没有创建导致报错的问题
|
package/dist/yj-deploy.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
const { stdout:
|
|
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");
|
|
5
5
|
module.exports = class {
|
|
6
|
-
constructor(
|
|
7
|
-
|
|
6
|
+
constructor(e) {
|
|
7
|
+
y(this, "config", {
|
|
8
8
|
host: "",
|
|
9
9
|
port: "",
|
|
10
10
|
username: "",
|
|
@@ -46,14 +46,20 @@ module.exports = class {
|
|
|
46
46
|
// 上传dist目录信息
|
|
47
47
|
dist: {
|
|
48
48
|
name: "dist"
|
|
49
|
-
}
|
|
49
|
+
},
|
|
50
|
+
parallelDir: 20,
|
|
51
|
+
// 文件夹并行创建数量,如果报错,可以减少此配置
|
|
52
|
+
parallelFile: 50,
|
|
53
|
+
// 文件并行上传数量,如果报错,可以减少此配置
|
|
54
|
+
allLog: !1
|
|
55
|
+
// 是否显示全部日志
|
|
50
56
|
});
|
|
51
|
-
|
|
57
|
+
y(this, "ssh2Conn", null);
|
|
52
58
|
// 上传状态
|
|
53
|
-
|
|
59
|
+
y(this, "uploading", !1);
|
|
54
60
|
// 定时器
|
|
55
|
-
|
|
56
|
-
return this.config = Object.assign(this.config,
|
|
61
|
+
y(this, "trim", null);
|
|
62
|
+
return this.config = Object.assign(this.config, e), {
|
|
57
63
|
name: "yj-deploy",
|
|
58
64
|
isPut: this.isPut.bind(this),
|
|
59
65
|
upload: this.upload.bind(this),
|
|
@@ -64,8 +70,8 @@ module.exports = class {
|
|
|
64
70
|
};
|
|
65
71
|
}
|
|
66
72
|
// webpack钩子
|
|
67
|
-
apply(
|
|
68
|
-
return
|
|
73
|
+
apply(e) {
|
|
74
|
+
return e && e.hooks && e.hooks.done && e.hooks.done.tap("yj-deploy", () => {
|
|
69
75
|
this.isPut();
|
|
70
76
|
}), "build";
|
|
71
77
|
}
|
|
@@ -99,17 +105,17 @@ module.exports = class {
|
|
|
99
105
|
console.log("- 请配置跳板机密码 password");
|
|
100
106
|
return;
|
|
101
107
|
}
|
|
102
|
-
this.uploading || (this.ssh2Conn = new
|
|
103
|
-
console.log("- 跳板机连接成功!"), this.ssh2Conn.sftp(async (
|
|
104
|
-
|
|
108
|
+
this.uploading || (this.ssh2Conn = new A(), this.ssh2Conn.on("ready", () => {
|
|
109
|
+
console.log("- 跳板机连接成功!"), this.ssh2Conn.sftp(async (e, t) => {
|
|
110
|
+
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}`);
|
|
105
111
|
});
|
|
106
112
|
}).connect({
|
|
107
113
|
host: this.config.host,
|
|
108
114
|
port: this.config.port,
|
|
109
115
|
username: this.config.username,
|
|
110
116
|
password: this.config.password
|
|
111
|
-
}), this.ssh2Conn.on("error", (
|
|
112
|
-
console.log(`- 连接失败: ${
|
|
117
|
+
}), this.ssh2Conn.on("error", (e) => {
|
|
118
|
+
console.log(`- 连接失败: ${e}`), this.breakConnect();
|
|
113
119
|
}), this.ssh2Conn.on("end", () => {
|
|
114
120
|
this.uploading = !1;
|
|
115
121
|
}));
|
|
@@ -125,23 +131,25 @@ module.exports = class {
|
|
|
125
131
|
* @param {*} shell
|
|
126
132
|
* @returns Promise
|
|
127
133
|
*/
|
|
128
|
-
onShell(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
onShell(e) {
|
|
135
|
+
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);
|
|
133
140
|
return;
|
|
134
141
|
}
|
|
135
142
|
let l = [];
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}).on("data", (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
a.on("close", () => {
|
|
144
|
+
n(l.toString());
|
|
145
|
+
}).on("data", (c) => {
|
|
146
|
+
let r = c.toString();
|
|
147
|
+
(t || this.config.allLog || r.startsWith("- ")) && l.push(`
|
|
148
|
+
` + r);
|
|
149
|
+
}).stderr.on("data", (c) => {
|
|
142
150
|
console.log(`- 远程命令错误:
|
|
143
|
-
` +
|
|
144
|
-
}),
|
|
151
|
+
` + c), this.breakConnect(), s(c);
|
|
152
|
+
}), a.end(e + `
|
|
145
153
|
exit
|
|
146
154
|
`);
|
|
147
155
|
});
|
|
@@ -152,19 +160,19 @@ exit
|
|
|
152
160
|
* @param {*} sftp
|
|
153
161
|
* @param {*} dir
|
|
154
162
|
*/
|
|
155
|
-
projectInit(
|
|
156
|
-
return new Promise(async (
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
|
|
163
|
+
projectInit(e, t) {
|
|
164
|
+
return new Promise(async (n, s) => {
|
|
165
|
+
e.mkdir(t, async (i) => {
|
|
166
|
+
if (i)
|
|
167
|
+
s(i);
|
|
160
168
|
else {
|
|
161
169
|
console.log("- 正在创建dockerfile");
|
|
162
|
-
const
|
|
163
|
-
await this.onShell(`touch ${
|
|
164
|
-
`)}' > ${
|
|
165
|
-
const
|
|
166
|
-
await this.onShell(`touch ${
|
|
167
|
-
`)}' > ${
|
|
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();
|
|
168
176
|
}
|
|
169
177
|
});
|
|
170
178
|
});
|
|
@@ -174,61 +182,69 @@ exit
|
|
|
174
182
|
* @param {*} sftp
|
|
175
183
|
* @param {*} dir
|
|
176
184
|
*/
|
|
177
|
-
upLoadProject(
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
console.log("- 跳板机不存在项目, 开始创建项目"), this.projectInit(
|
|
181
|
-
console.log(`- ${this.config.namespace}项目创建成功`), this.upLoadProject(
|
|
182
|
-
}).catch((
|
|
183
|
-
console.log(`- 创建项目失败: ${
|
|
185
|
+
upLoadProject(e, t) {
|
|
186
|
+
e.readdir(t, async (n) => {
|
|
187
|
+
if (n) {
|
|
188
|
+
console.log("- 跳板机不存在项目, 开始创建项目"), this.projectInit(e, t).then(() => {
|
|
189
|
+
console.log(`- ${this.config.namespace}项目创建成功`), this.upLoadProject(e, t);
|
|
190
|
+
}).catch((o) => {
|
|
191
|
+
console.log(`- 创建项目失败: ${o}`), this.breakConnect();
|
|
184
192
|
});
|
|
185
193
|
return;
|
|
186
194
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (await this.onShell(`rm -rf ${n}`), await this.onShell(`mkdir ${n}`), !this.config.fileDir) {
|
|
195
|
+
const s = `${t}/${this.config.dist.name}`;
|
|
196
|
+
if (await this.onShell(`rm -rf ${s}`), await this.onShell(`mkdir ${s}`), !this.config.fileDir) {
|
|
190
197
|
console.log("- 请配置待上传文件目录 fileDir"), this.breakConnect();
|
|
191
198
|
return;
|
|
192
199
|
}
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
-
console.log("-
|
|
200
|
+
const { localFileList: i, dirList: a } = await this.getFilesInDirectory(this.config.fileDir);
|
|
201
|
+
if (i.length === 0) {
|
|
202
|
+
console.log("- 待上传目录没有获取到文件,请检查"), this.breakConnect();
|
|
196
203
|
return;
|
|
197
204
|
}
|
|
198
|
-
let l = 0,
|
|
199
|
-
const
|
|
200
|
-
this.progressBar(++l, i);
|
|
201
|
-
}) : new Promise((f, w) => {
|
|
205
|
+
let l = 0, c = i.length;
|
|
206
|
+
const r = (o) => new Promise((u, g) => {
|
|
202
207
|
try {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
this.progressBar(++l,
|
|
206
|
-
}),
|
|
207
|
-
console.log(`- 文件 ${
|
|
208
|
-
}),
|
|
209
|
-
} catch (
|
|
210
|
-
|
|
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);
|
|
214
|
+
} catch (h) {
|
|
215
|
+
g(h);
|
|
211
216
|
}
|
|
212
|
-
}),
|
|
213
|
-
|
|
217
|
+
}), P = (o, u) => {
|
|
218
|
+
let g = [];
|
|
219
|
+
for (let h = 0; h < o.length; h += u)
|
|
220
|
+
g.push(o.slice(h, h + u));
|
|
221
|
+
return g;
|
|
222
|
+
}, m = (o) => {
|
|
223
|
+
if (o.length === 0)
|
|
224
|
+
return Promise.resolve();
|
|
225
|
+
const u = o.shift();
|
|
226
|
+
return Promise.all(u.map((g) => r(g))).then(() => m(o));
|
|
227
|
+
}, d = (o) => {
|
|
228
|
+
if (o.length === 0)
|
|
214
229
|
return Promise.resolve();
|
|
215
|
-
|
|
216
|
-
return g
|
|
217
|
-
}
|
|
218
|
-
|
|
230
|
+
let g = o.shift().map((h) => `${s}${h}`).join(" ");
|
|
231
|
+
return this.onShell(`mkdir -p ${g}`).then(() => d(o));
|
|
232
|
+
};
|
|
233
|
+
let $ = [...P(a, this.config.parallelDir || 20)];
|
|
234
|
+
console.log("- 开始创建目录"), await d($), console.log("- 创建目录完成"), await new Promise((o) => {
|
|
219
235
|
setTimeout(() => {
|
|
220
|
-
|
|
236
|
+
o();
|
|
221
237
|
}, 500);
|
|
222
238
|
});
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
let
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
console.log(
|
|
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}`;
|
|
244
|
+
const j = this.getArgv("--tmpName") || this.config.tmpName;
|
|
245
|
+
p += ` ${j}`;
|
|
246
|
+
const D = await this.onShell(p);
|
|
247
|
+
console.log(D), console.log("- 镜像推送完成"), this.breakConnect();
|
|
232
248
|
});
|
|
233
249
|
}
|
|
234
250
|
/**
|
|
@@ -236,47 +252,53 @@ exit
|
|
|
236
252
|
* @param description 命令行开头的文字信息
|
|
237
253
|
* @param bar_length 进度条的长度(单位:字符),默认设为 25
|
|
238
254
|
*/
|
|
239
|
-
progressBar(
|
|
240
|
-
let
|
|
241
|
-
for (let
|
|
242
|
-
|
|
243
|
-
let
|
|
244
|
-
for (let
|
|
245
|
-
|
|
246
|
-
let
|
|
247
|
-
|
|
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("");
|
|
248
264
|
}
|
|
249
265
|
/**
|
|
250
266
|
* 获取目录下所有文件并分类
|
|
251
267
|
* @param {*} dir
|
|
252
268
|
*/
|
|
253
|
-
getFilesInDirectory(
|
|
254
|
-
const
|
|
255
|
-
return new Promise((
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
269
|
+
getFilesInDirectory(e) {
|
|
270
|
+
const t = [], n = [];
|
|
271
|
+
return new Promise((s, i) => {
|
|
272
|
+
const a = (l, c, r) => {
|
|
273
|
+
const P = b.readdirSync(l);
|
|
274
|
+
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: $,
|
|
262
279
|
// 本地路径
|
|
263
|
-
remotePath: `${
|
|
280
|
+
remotePath: `${c}${d}`,
|
|
264
281
|
// 远程路径
|
|
265
|
-
|
|
266
|
-
|
|
282
|
+
level: r
|
|
283
|
+
// 文件或目录层级
|
|
284
|
+
}), p && a($, `${c}${d}/`, r + 1);
|
|
285
|
+
}), m || n.push(c);
|
|
267
286
|
};
|
|
268
|
-
|
|
287
|
+
a(e, "/", 0), s({
|
|
288
|
+
localFileList: t,
|
|
289
|
+
dirList: n
|
|
290
|
+
});
|
|
269
291
|
});
|
|
270
292
|
}
|
|
271
293
|
/**
|
|
272
294
|
* 获取命令行参数
|
|
273
295
|
* @param { string } name
|
|
274
296
|
*/
|
|
275
|
-
getArgv(
|
|
276
|
-
const
|
|
277
|
-
let
|
|
278
|
-
return
|
|
279
|
-
|
|
280
|
-
}),
|
|
297
|
+
getArgv(e) {
|
|
298
|
+
const t = process.argv;
|
|
299
|
+
let n;
|
|
300
|
+
return t.forEach((s) => {
|
|
301
|
+
s.indexOf(e) > -1 && (n = s.split("=")[1] || "");
|
|
302
|
+
}), n;
|
|
281
303
|
}
|
|
282
304
|
};
|
package/dist/yj-deploy.umd.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
`+
|
|
3
|
-
`+
|
|
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+`
|
|
4
4
|
exit
|
|
5
|
-
`)})})}projectInit(e,
|
|
6
|
-
`)}' > ${
|
|
7
|
-
`)}' > ${
|
|
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}}});
|