yj-deploy 0.0.1 → 0.0.3

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
@@ -0,0 +1,227 @@
1
+ # yj-deploy
2
+
3
+ `yj-deploy` 是为解决公司内部项目部署到镜像服务器开发的工具,可帮助开发者自动构建镜像,并自动部署到镜像服务器上。
4
+
5
+ ## 目前已实现如下功能
6
+ - ✔ 项目打包完成后自动上传到跳板机
7
+ - ✔ 自动生成项目目录,自动创建dockerfile,upload.sh脚本文件
8
+ - ✔ 自动运行脚本,自动打包镜像,自动推送到镜像服务器(全程不用手动操作)
9
+ - ✔ 直接集成到vite,webpack项目
10
+ - ✔ 支持手动上传其他任何类型项目
11
+ - ✔ 支持灵活配置dockerfile,upload.sh等脚本内容
12
+ - ✔ 支持多种方式配置 `镜像仓库` `镜像环境`等参数
13
+ > 全程不需要连接跳板机做任何操作即可完成镜像推送
14
+
15
+ ## 运行情况
16
+ ![预览](./img.png)
17
+
18
+ ## 安装
19
+ ![NPM](https://nodei.co/npm/yj-deploy.png)
20
+ ```sh
21
+ $ yarn add yj-deploy
22
+ $ npm i yj-deploy -D
23
+ ```
24
+
25
+
26
+ ## 简单配置
27
+
28
+ ```javascript
29
+ import path from 'path'
30
+ import yjDeploy from 'yj-deploy'
31
+
32
+ const deploy = new yjDeploy({
33
+ host: '', // ip
34
+ port: '', // 端口
35
+ username: '', // 账号
36
+ password: '', // 密码
37
+ namespace: '', // 项目名称,等同git地址目录名称
38
+ // 本地文件目录
39
+ fileDir: path.resolve('./dist'),
40
+ })
41
+ ```
42
+
43
+ ## 完整配置(包含默认配置)
44
+ ```json
45
+ {
46
+ host: '', // ip (必填)
47
+ port: '', // 端口 (必填)
48
+ username: '', // 账号 (必填)
49
+ password: '', // 密码 (必填)
50
+ namespace: '', // 项目名称,等同git地址目录名称(必填)
51
+ imageStore: 'dev-images', // 镜像仓库, 默认为 dev-images
52
+ tmpName: 'dev', // 镜像环境, 默认 dev
53
+ delay: 0, // 延迟上传时间 (部分项目构建需要时间,延迟上传可以解决)
54
+ fileDir: '', // 本地文件目录(必填)
55
+ rootDir: '/home/yjweb', // 远程sftp服务根目录 默认为 /home/yjweb
56
+ // dockerfile文件信息
57
+ dockerfile: {
58
+ name: 'Dockerfile',
59
+ content: [
60
+ 'FROM harbor.yunjingtech.cn:30002/yj-base/nginx:latest',
61
+ 'RUN rm -rf /usr/share/nginx/html/\*',
62
+ 'COPY dist /usr/share/nginx/html/'
63
+ ]
64
+ },
65
+
66
+ // upload.sh文件信息(可覆盖,但需要注意 $1 和 $2参数的顺序)
67
+ upload: {
68
+ name: 'upload.sh',
69
+ content: [
70
+ '#!/bin/sh',
71
+ 'tag=`basename \\`pwd\\``:$2-`date +%Y%m%d`',
72
+ 'echo "- 镜像仓库: $1"',
73
+ 'echo "- 镜像环境: $2"',
74
+ 'docker build -t harbor.yunjingtech.cn:30002/$1/$tag .',
75
+ 'docker push harbor.yunjingtech.cn:30002/$1/$tag',
76
+ 'echo - 镜像地址: harbor.yunjingtech.cn:30002/$1/$tag',
77
+ ]
78
+ },
79
+
80
+ // 上传dist目录信息 (除非运维改变,否则不要配置)
81
+ dist: {
82
+ name: 'dist'
83
+ }
84
+ }
85
+ ```
86
+
87
+ <br />
88
+
89
+ # 使用
90
+ ## 配合打包命令使用
91
+ ```javascript
92
+ // webpack 中使用
93
+ //vue.config.js
94
+ const path = require('path')
95
+ const yjDeploy = require('yj-deploy')
96
+ module.exports = {
97
+ configureWebpack: config => {
98
+ return {
99
+ plugins: [
100
+ new yjDeploy({
101
+ host: '',
102
+ port: '',
103
+ username: '',
104
+ password: '',
105
+ namespace: '', // 项目名称,等同git地址目录名称
106
+ // 本地文件目录
107
+ fileDir: path.resolve('./dist'),
108
+ })
109
+ ]
110
+ }
111
+ }
112
+ }
113
+
114
+ // package.json
115
+ "scripts": {
116
+ "deploy": "vue-cli-service build --mode development --deploy"
117
+ }
118
+ // 使用 yarn deploy 或 npm run deploy
119
+ ```
120
+
121
+ ```javascript
122
+ // vite 项目中使用
123
+ //vite.config.js
124
+ import path from 'path'
125
+ import yjDeploy from 'yj-deploy'
126
+
127
+ export default defineConfig({
128
+ plugins: [
129
+ new yjDeploy({
130
+ host: '',
131
+ port: '',
132
+ username: '',
133
+ password: '',
134
+ namespace: '', // 项目名称,等同git地址目录名称
135
+ // 本地文件目录
136
+ fileDir: path.resolve('./dist'),
137
+ })
138
+ ]
139
+ })
140
+
141
+ // package.json
142
+ "scripts": {
143
+ "deploy": "vite build --mode development -- --deploy"
144
+ }
145
+ // 使用 yarn deploy 或 npm run deploy
146
+ ```
147
+
148
+ ## 动态配置项
149
+ 由于部分项目可能需要区分环境,所以需要动态配置项,目前支持动态配置项如下:
150
+ <br />
151
+
152
+ `--deploy` :是否启用自动部署功能,如果启用,在命令后跟上 --deploy即可
153
+ ```javascript
154
+ // vite项目 package.json
155
+ "scripts": {
156
+ "deploy": "vite build --mode development -- --deploy"
157
+ }
158
+
159
+ // webpack项目 package.json
160
+ "scripts": {
161
+ "deploy": "vue-cli-service build --mode development --deploy"
162
+ }
163
+ ```
164
+ ::: warning
165
+ 由于vite的自定义命令需要在vite专属配置后加 -- 分割,所以vite项目的命令中间会多一个 -- 分割,这就是vite和webpack执行命令的差异,后续不再提及
166
+ :::
167
+
168
+ <br />
169
+
170
+ `--reset` :是否重置项目,如果配置了此选项,则每次执行命令时会删除 `dockerfile` `upload.sh` 配置文件,然后重新初始化项目,默认为不重置
171
+
172
+ <br />
173
+
174
+ `--imageStore` :镜像仓库名称(默认为dev-images),在测试或者正式环境可能需要配置,需要推送到指定的镜像仓库时配置,增加此动态配置可以使用更灵活,同一套主配置可以满足多个环境推送到不同的镜像仓库,如:
175
+ ```javascript
176
+ "scripts": {
177
+ // 开发环境
178
+ "deploy": "vite build --mode development -- --deploy"
179
+ // 测试环境
180
+ "deploy-test": "vite build --mode test -- --deploy --imageStore=sot-admin"
181
+ }
182
+ ```
183
+
184
+ <br />
185
+
186
+ `--tmpName` :镜像环境名称(默认为 dev),在测试或者正式环境可能需要配置,需要推送到指定的镜像环境时配置,防止镜像名重复导致覆盖,同一套主配置可以满足多个环境推送到不同的镜像地址,如:
187
+ ```javascript
188
+ "scripts": {
189
+ // 开发环境
190
+ "deploy": "vite build --mode development -- --deploy --tmpName=dev"
191
+ // 测试环境
192
+ "deploy-test": "vite build --mode test -- --deploy --tmpName=test"
193
+ // 正式环境
194
+ "deploy-prod": "vite build --mode production -- --deploy --tmpName=prod"
195
+ }
196
+ ```
197
+
198
+
199
+ ## 上传任意项目
200
+ ```javascript
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
+ ```
220
+
221
+
222
+ # 最后
223
+ 如有意见或建议,或bug或文档不清晰等问题,可随时向我反馈,或直接修改后提交,共同完善这个工具。
224
+
225
+ <br />
226
+
227
+ 项目已提交到git:https://gitlab.yunjingtech.cn:10010/frontend/yj-deploy
@@ -0,0 +1,282 @@
1
+ var C = Object.defineProperty;
2
+ var j = (a, o, e) => o in a ? C(a, o, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[o] = e;
3
+ var d = (a, o, e) => (j(a, typeof o != "symbol" ? o + "" : o, e), e);
4
+ const { stdout: D } = require("single-line-log"), v = require("path"), $ = require("fs"), { Client: x } = require("ssh2");
5
+ module.exports = class {
6
+ constructor(o) {
7
+ d(this, "config", {
8
+ host: "",
9
+ port: "",
10
+ username: "",
11
+ password: "",
12
+ namespace: "",
13
+ // 项目命名空间,等同于镜像地址目录名称
14
+ imageStore: "dev-images",
15
+ // 镜像仓库
16
+ tmpName: "dev",
17
+ // 镜像环境
18
+ delay: 0,
19
+ // 延迟上传时间
20
+ // 本地文件目录(必填)
21
+ fileDir: "",
22
+ // 远程sftp服务根目录
23
+ rootDir: "/home/yjweb",
24
+ // dockerfile文件信息
25
+ dockerfile: {
26
+ name: "Dockerfile",
27
+ content: [
28
+ "FROM harbor.yunjingtech.cn:30002/yj-base/nginx:latest",
29
+ "RUN rm -rf /usr/share/nginx/html/*",
30
+ "COPY dist /usr/share/nginx/html/"
31
+ ]
32
+ },
33
+ // upload.sh文件信息
34
+ upload: {
35
+ name: "upload.sh",
36
+ content: [
37
+ "#!/bin/sh",
38
+ "tag=`basename \\`pwd\\``:$2-`date +%Y%m%d`",
39
+ 'echo "- 镜像仓库: $1"',
40
+ 'echo "- 镜像环境: $2"',
41
+ "docker build -t harbor.yunjingtech.cn:30002/$1/$tag .",
42
+ "docker push harbor.yunjingtech.cn:30002/$1/$tag",
43
+ "echo - 镜像地址: harbor.yunjingtech.cn:30002/$1/$tag"
44
+ ]
45
+ },
46
+ // 上传dist目录信息
47
+ dist: {
48
+ name: "dist"
49
+ }
50
+ });
51
+ d(this, "ssh2Conn", null);
52
+ // 上传状态
53
+ d(this, "uploading", !1);
54
+ // 定时器
55
+ d(this, "trim", null);
56
+ return this.config = Object.assign(this.config, o), {
57
+ name: "yj-deploy",
58
+ isPut: this.isPut.bind(this),
59
+ upload: this.upload.bind(this),
60
+ // webpack钩子
61
+ apply: this.apply.bind(this),
62
+ // vite上传钩子
63
+ closeBundle: this.isPut.bind(this)
64
+ };
65
+ }
66
+ // webpack钩子
67
+ apply(o) {
68
+ return o && o.hooks && o.hooks.done && o.hooks.done.tap("yj-deploy", () => {
69
+ this.isPut();
70
+ }), "build";
71
+ }
72
+ /**
73
+ * 判断是否需要上传,
74
+ * vite和 webpack环境在每次编译完都会触发当前方法
75
+ * 需要通过是否有命令行参数 --deploy
76
+ */
77
+ isPut() {
78
+ this.getArgv("--deploy") != null && (clearTimeout(this.trim), this.trim = setTimeout(() => {
79
+ this.upload();
80
+ }, this.config.delay || 0));
81
+ }
82
+ /**
83
+ * 连接跳板机并开始上传
84
+ */
85
+ upload() {
86
+ if (console.log("-------------- deploy-start --------------"), !this.config.host) {
87
+ console.log("- 请配置跳板机地址 host");
88
+ return;
89
+ }
90
+ if (!this.config.port) {
91
+ console.log("- 请配置跳板机端口 port");
92
+ return;
93
+ }
94
+ if (!this.config.username) {
95
+ console.log("- 请配置跳板机账号 username");
96
+ return;
97
+ }
98
+ if (!this.config.password) {
99
+ console.log("- 请配置跳板机密码 password");
100
+ return;
101
+ }
102
+ this.uploading || (this.ssh2Conn = new x(), this.ssh2Conn.on("ready", () => {
103
+ console.log("- 跳板机连接成功!"), this.ssh2Conn.sftp(async (o, e) => {
104
+ o && (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(e, `${this.config.rootDir}/${this.config.namespace}`);
105
+ });
106
+ }).connect({
107
+ host: this.config.host,
108
+ port: this.config.port,
109
+ username: this.config.username,
110
+ password: this.config.password
111
+ }), this.ssh2Conn.on("error", (o) => {
112
+ console.log(`- 连接失败: ${o}`), this.breakConnect();
113
+ }), this.ssh2Conn.on("end", () => {
114
+ this.uploading = !1;
115
+ }));
116
+ }
117
+ /**
118
+ * 断开连接
119
+ */
120
+ breakConnect() {
121
+ this.uploading = !1, console.log("- 已断开连接"), console.log("-------------- deploy-end --------------"), this.ssh2Conn.end();
122
+ }
123
+ /**
124
+ * 运行shell命令
125
+ * @param {*} shell
126
+ * @returns Promise
127
+ */
128
+ onShell(o) {
129
+ return new Promise((e, r) => {
130
+ this.ssh2Conn.shell((n, s) => {
131
+ if (n) {
132
+ console.log("- 远程命令错误:" + n, o), this.breakConnect(), r(n);
133
+ return;
134
+ }
135
+ let l = [];
136
+ s.on("close", () => {
137
+ e(l.toString());
138
+ }).on("data", (i) => {
139
+ l.push(`
140
+ ` + i);
141
+ }).stderr.on("data", (i) => {
142
+ console.log(`- 远程命令错误:
143
+ ` + i), this.breakConnect(), r(i);
144
+ }), s.end(o + `
145
+ exit
146
+ `);
147
+ });
148
+ });
149
+ }
150
+ /**
151
+ * 初始化项目
152
+ * @param {*} sftp
153
+ * @param {*} dir
154
+ */
155
+ projectInit(o, e) {
156
+ return new Promise(async (r, n) => {
157
+ o.mkdir(e, async (s) => {
158
+ if (s)
159
+ n(s);
160
+ else {
161
+ console.log("- 正在创建dockerfile");
162
+ const l = `${e}/${this.config.dockerfile.name}`;
163
+ await this.onShell(`touch ${l}`), await this.onShell(`echo '${this.config.dockerfile.content.join(`
164
+ `)}' > ${l}`), console.log("- 正在创建upload.sh");
165
+ const i = `${e}/${this.config.upload.name}`;
166
+ await this.onShell(`touch ${i}`), await this.onShell(`echo '${this.config.upload.content.join(`
167
+ `)}' > ${i}`), r();
168
+ }
169
+ });
170
+ });
171
+ }
172
+ /**
173
+ * 上传项目
174
+ * @param {*} sftp
175
+ * @param {*} dir
176
+ */
177
+ upLoadProject(o, e) {
178
+ o.readdir(e, async (r) => {
179
+ if (r) {
180
+ console.log("- 跳板机不存在项目, 开始创建项目"), this.projectInit(o, e).then(() => {
181
+ console.log(`- ${this.config.namespace}项目创建成功`), this.upLoadProject(o, e);
182
+ }).catch((t) => {
183
+ console.log(`- 创建项目失败: ${t}`), this.breakConnect();
184
+ });
185
+ return;
186
+ }
187
+ console.log("- 开始上传文件");
188
+ const n = `${e}/${this.config.dist.name}`;
189
+ if (await this.onShell(`rm -rf ${n}`), await this.onShell(`mkdir ${n}`), !this.config.fileDir) {
190
+ console.log("- 请配置待上传文件目录 fileDir"), this.breakConnect();
191
+ return;
192
+ }
193
+ const s = await this.getFilesInDirectory(this.config.fileDir);
194
+ if (s.length === 0) {
195
+ console.log("- 待上传目录为空,请检查"), this.breakConnect();
196
+ return;
197
+ }
198
+ let l = 0, i = s.length;
199
+ const g = (t) => t.isDirectory ? this.onShell(`mkdir ${n}${t.remotePath}`).then(() => {
200
+ this.progressBar(++l, i);
201
+ }) : new Promise((f, w) => {
202
+ try {
203
+ const p = $.createReadStream(t.localPath), m = o.createWriteStream(`${n}${t.remotePath}`);
204
+ m.on("close", () => {
205
+ this.progressBar(++l, i), f();
206
+ }), m.on("error", (b) => {
207
+ console.log(`- 文件 ${t.remotePath} 上传失败:${b}`), w(b), this.breakConnect();
208
+ }), p.pipe(m);
209
+ } catch (p) {
210
+ w(p);
211
+ }
212
+ }), c = (t) => {
213
+ if (t.length === 0)
214
+ return Promise.resolve();
215
+ const f = t.shift();
216
+ return g(f).then(() => c(t));
217
+ }, u = s.filter((t) => t.isDirectory);
218
+ await c(u), await new Promise((t) => {
219
+ setTimeout(() => {
220
+ t();
221
+ }, 500);
222
+ });
223
+ const y = s.filter((t) => !t.isDirectory);
224
+ await Promise.all(y.map((t) => g(t))), console.log("- 文件上传成功"), console.log("- 开始推送镜像");
225
+ let h = `cd ${e} && sh ${this.config.upload.name}`;
226
+ const k = this.getArgv("--imageStore") || this.config.imageStore;
227
+ h += ` ${k}`;
228
+ const P = this.getArgv("--tmpName") || this.config.tmpName;
229
+ h += ` ${P}`;
230
+ const S = await this.onShell(h);
231
+ console.log(S), console.log("- 镜像推送完成"), this.breakConnect();
232
+ });
233
+ }
234
+ /**
235
+ * 进度条
236
+ * @param description 命令行开头的文字信息
237
+ * @param bar_length 进度条的长度(单位:字符),默认设为 25
238
+ */
239
+ progressBar(o, e, r = 25) {
240
+ let n = (o / e).toFixed(4), s = Math.floor(n * r), l = "";
241
+ for (let c = 0; c < s; c++)
242
+ l += "█";
243
+ let i = "";
244
+ for (let c = 0; c < r - s; c++)
245
+ i += "░";
246
+ let g = `- 上传进度: ${l}${i} ${(100 * n).toFixed(2)}% (${o}/${e})`;
247
+ D(g), o == e && console.log("");
248
+ }
249
+ /**
250
+ * 获取目录下所有文件并分类
251
+ * @param {*} dir
252
+ */
253
+ getFilesInDirectory(o) {
254
+ const e = [];
255
+ return new Promise((r, n) => {
256
+ const s = (l, i) => {
257
+ $.readdirSync(l).forEach((c) => {
258
+ const u = v.join(l, c), h = $.statSync(u).isDirectory();
259
+ e.push({
260
+ isDirectory: h,
261
+ localPath: u,
262
+ // 本地路径
263
+ remotePath: `${i}${c}`
264
+ // 远程路径
265
+ }), h && s(u, `${i}${c}/`);
266
+ });
267
+ };
268
+ s(o, "/"), r(e);
269
+ });
270
+ }
271
+ /**
272
+ * 获取命令行参数
273
+ * @param { string } name
274
+ */
275
+ getArgv(o) {
276
+ const e = process.argv;
277
+ let r;
278
+ return e.forEach((n) => {
279
+ n.indexOf(o) > -1 && (r = n.split("=")[1] || "");
280
+ }), r;
281
+ }
282
+ };
@@ -0,0 +1,7 @@
1
+ (function(a){typeof define=="function"&&define.amd?define(a):a()})(function(){"use strict";var D=Object.defineProperty;var v=(a,h,g)=>h in a?D(a,h,{enumerable:!0,configurable:!0,writable:!0,value:g}):a[h]=g;var p=(a,h,g)=>(v(a,typeof h!="symbol"?h+"":h,g),g);const{stdout:a}=require("single-line-log"),h=require("path"),g=require("fs"),{Client:P}=require("ssh2");module.exports=class{constructor(e){p(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"}});p(this,"ssh2Conn",null);p(this,"uploading",!1);p(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 P,this.ssh2Conn.on("ready",()=>{console.log("- 跳板机连接成功!"),this.ssh2Conn.sftp(async(e,o)=>{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}`)})}).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){return new Promise((o,r)=>{this.ssh2Conn.shell((n,s)=>{if(n){console.log("- 远程命令错误:"+n,e),this.breakConnect(),r(n);return}let l=[];s.on("close",()=>{o(l.toString())}).on("data",i=>{l.push(`
2
+ `+i)}).stderr.on("data",i=>{console.log(`- 远程命令错误:
3
+ `+i),this.breakConnect(),r(i)}),s.end(e+`
4
+ exit
5
+ `)})})}projectInit(e,o){return new Promise(async(r,n)=>{e.mkdir(o,async s=>{if(s)n(s);else{console.log("- 正在创建dockerfile");const l=`${o}/${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 i=`${o}/${this.config.upload.name}`;await this.onShell(`touch ${i}`),await this.onShell(`echo '${this.config.upload.content.join(`
7
+ `)}' > ${i}`),r()}})})}upLoadProject(e,o){e.readdir(o,async r=>{if(r){console.log("- 跳板机不存在项目, 开始创建项目"),this.projectInit(e,o).then(()=>{console.log(`- ${this.config.namespace}项目创建成功`),this.upLoadProject(e,o)}).catch(t=>{console.log(`- 创建项目失败: ${t}`),this.breakConnect()});return}console.log("- 开始上传文件");const n=`${o}/${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 s=await this.getFilesInDirectory(this.config.fileDir);if(s.length===0){console.log("- 待上传目录为空,请检查"),this.breakConnect();return}let l=0,i=s.length;const d=t=>t.isDirectory?this.onShell(`mkdir ${n}${t.remotePath}`).then(()=>{this.progressBar(++l,i)}):new Promise((m,b)=>{try{const $=g.createReadStream(t.localPath),y=e.createWriteStream(`${n}${t.remotePath}`);y.on("close",()=>{this.progressBar(++l,i),m()}),y.on("error",k=>{console.log(`- 文件 ${t.remotePath} 上传失败:${k}`),b(k),this.breakConnect()}),$.pipe(y)}catch($){b($)}}),c=t=>{if(t.length===0)return Promise.resolve();const m=t.shift();return d(m).then(()=>c(t))},f=s.filter(t=>t.isDirectory);await c(f),await new Promise(t=>{setTimeout(()=>{t()},500)});const w=s.filter(t=>!t.isDirectory);await Promise.all(w.map(t=>d(t))),console.log("- 文件上传成功"),console.log("- 开始推送镜像");let u=`cd ${o} && sh ${this.config.upload.name}`;const S=this.getArgv("--imageStore")||this.config.imageStore;u+=` ${S}`;const C=this.getArgv("--tmpName")||this.config.tmpName;u+=` ${C}`;const j=await this.onShell(u);console.log(j),console.log("- 镜像推送完成"),this.breakConnect()})}progressBar(e,o,r=25){let n=(e/o).toFixed(4),s=Math.floor(n*r),l="";for(let c=0;c<s;c++)l+="█";let i="";for(let c=0;c<r-s;c++)i+="░";let d=`- 上传进度: ${l}${i} ${(100*n).toFixed(2)}% (${e}/${o})`;a(d),e==o&&console.log("")}getFilesInDirectory(e){const o=[];return new Promise((r,n)=>{const s=(l,i)=>{g.readdirSync(l).forEach(c=>{const f=h.join(l,c),u=g.statSync(f).isDirectory();o.push({isDirectory:u,localPath:f,remotePath:`${i}${c}`}),u&&s(f,`${i}${c}/`)})};s(e,"/"),r(o)})}getArgv(e){const o=process.argv;let r;return o.forEach(n=>{n.indexOf(e)>-1&&(r=n.split("=")[1]||"")}),r}}});
package/package.json CHANGED
@@ -1,32 +1,33 @@
1
- {
2
- "name": "yj-deploy",
3
- "version": "0.0.1",
4
- "description": "ssh",
5
- "scripts": {
6
- "build": "vite build"
7
- },
8
- "type": "module",
9
- "files": [
10
- "dist",
11
- "package.json",
12
- "README.md"
13
- ],
14
- "main": "./dist/yj-deploy.umd.cjs",
15
- "module": "./dist/yj-deploy.js",
16
- "exports": {
17
- ".": {
18
- "import": "./dist/yj-deploy.js",
19
- "require": "./dist/yj-deploy.umd.cjs"
20
- }
21
- },
22
- "author": "",
23
- "license": "ISC",
24
- "devDependencies": {
25
- "single-line-log": "^1.1.2",
26
- "ssh2": "^1.15.0",
27
- "vite": "4.5"
28
- },
29
- "publishConfig": {
30
- "registry": "https://registry.npmjs.org"
31
- }
32
- }
1
+ {
2
+ "name": "yj-deploy",
3
+ "version": "0.0.3",
4
+ "description": "ssh sftp",
5
+ "scripts": {
6
+ "build": "vite build"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "package.json",
11
+ "README.md"
12
+ ],
13
+ "main": "./dist/yj-deploy.umd.js",
14
+ "module": "./dist/yj-deploy.mjs",
15
+ "exports": {
16
+ ".": {
17
+ "import": "./dist/yj-deploy.mjs",
18
+ "require": "./dist/yj-deploy.umd.js"
19
+ }
20
+ },
21
+ "author": "",
22
+ "license": "ISC",
23
+ "dependencies": {
24
+ "single-line-log": "^1.1.2",
25
+ "ssh2": "^1.15.0"
26
+ },
27
+ "devDependencies": {
28
+ "vite": "4.5"
29
+ },
30
+ "publishConfig": {
31
+ "registry": "https://registry.npmjs.org"
32
+ }
33
+ }