1 |
|
2 |
|
3 |
|
4 |
|
5 | 'use strict';
|
6 |
|
7 | const util = require('../utils'),
|
8 | extend = require('extend'),
|
9 | EventEmitter = require('events').EventEmitter,
|
10 | LoadingORA = require('../loadProgress').LoadingORA,
|
11 | dateFormat = require('dateformat'),
|
12 | Uploader = require('./uploader'),
|
13 | T = require('../tools');
|
14 |
|
15 | class Deploy extends EventEmitter {
|
16 | constructor(config) {
|
17 | super();
|
18 | this.basePath = T.getArg('cwdir') || process.cwd();
|
19 |
|
20 | this.host = null;
|
21 | this.port = 22;
|
22 | this.username = 'anonymous';
|
23 | this.password = null;
|
24 | this.timeout = 50000;
|
25 |
|
26 |
|
27 | this.localPath = '';
|
28 | this.filters = [];
|
29 |
|
30 |
|
31 | this.remotePath = null;
|
32 | this.platform = 'unix';
|
33 | this.onUploadedComplete = null;
|
34 | this.onUploadedFileSuccess = null;
|
35 | this.onUploadedFileError = null;
|
36 |
|
37 | this.isExecute = true;
|
38 |
|
39 |
|
40 | this.isBuildAfter = true;
|
41 |
|
42 | this.type = 'full';
|
43 |
|
44 | this.log = 'progress';
|
45 |
|
46 | this.logFile = null;
|
47 |
|
48 | this.connectType = 'sftp';
|
49 |
|
50 | this.agent = null;
|
51 | this.agentForward = false;
|
52 |
|
53 | this.privateKey = null;
|
54 | this.passphrase = null;
|
55 |
|
56 | this.keepaliveCountMax = 3;
|
57 |
|
58 | this.authKey = '';
|
59 | this.auth = null;
|
60 | this.authFile = '.ftppass';
|
61 |
|
62 | this.key = {};
|
63 | this.keyLocation = null;
|
64 | this.keyContents = null;
|
65 |
|
66 | this.uploader = null;
|
67 | this.isRollback = true;
|
68 |
|
69 | this.sftp = null;
|
70 | this.ssh2 = null;
|
71 |
|
72 | this.options = config;
|
73 |
|
74 | if (util.isObject(config)) {
|
75 | extend(true, this, config);
|
76 | }
|
77 |
|
78 | this.init();
|
79 | this.loading = new LoadingORA();
|
80 | this.uploader = new Uploader(this.localPath, this.remotePath, this._getConnectOptions());
|
81 | }
|
82 |
|
83 | init() {
|
84 |
|
85 | this.initType();
|
86 |
|
87 |
|
88 | this.initPath();
|
89 |
|
90 |
|
91 | this.initRemotePath();
|
92 |
|
93 |
|
94 | this.initFilters();
|
95 |
|
96 |
|
97 | this.initAuth();
|
98 |
|
99 |
|
100 | this.initPresentKey();
|
101 |
|
102 |
|
103 | this.initLog();
|
104 |
|
105 |
|
106 | this.initTimeout();
|
107 | }
|
108 |
|
109 | start() {
|
110 |
|
111 | this.isExecute ? this._initUploader() : this.stop();
|
112 | return this;
|
113 | }
|
114 |
|
115 | stop() {
|
116 | this.uploader && this.uploader.stop();
|
117 |
|
118 | }
|
119 |
|
120 | initType() {
|
121 |
|
122 | this._isIncrementType = this.type === 'increment';
|
123 |
|
124 | this._isFullType = this.type === 'full';
|
125 | }
|
126 |
|
127 | initLog() {
|
128 | this._isProgressLog = this.log === 'progress';
|
129 | }
|
130 |
|
131 | initTimeout() {
|
132 |
|
133 | if (this.connectType === 'ftp') {
|
134 | this.connTimeout = this.pasvTimeout = this.keepalive = this.timeout;
|
135 | }
|
136 | }
|
137 |
|
138 |
|
139 | initPath() {
|
140 | if (util.isString(this.localPath) && this.localPath.length > 0) {
|
141 | this.localPath = (!T.isAbsolutePath(this.localPath) && [T.Path.resolve(process.cwd(), this.localPath)]) || [this.localPath];
|
142 | } else if (util.isArray(this.localPath)) {
|
143 | this.localPath = this.localPath
|
144 | .filter(p => {
|
145 | return p && util.isString(p);
|
146 | })
|
147 | .map(p => {
|
148 | if (p && p.length > 0) {
|
149 | return !T.isAbsolutePath(p) ? T.Path.resolve(process.cwd(), p) : p;
|
150 | }
|
151 | });
|
152 | } else {
|
153 | T.log.error('× LocalPath Error: localPath is not found ( localPath can be String or Array => vinyl-fs module )');
|
154 | }
|
155 | }
|
156 |
|
157 | initRemotePath() {
|
158 | if (!this.remotePath) {
|
159 | T.log.error('× RemotePath Error: remotePath is not found ( remotePath can be String )');
|
160 | }
|
161 | }
|
162 |
|
163 |
|
164 | initFilters() {
|
165 | if (this.filters && this.filters.length > 0) {
|
166 | this.filters = this.filters.map(f => {
|
167 | f = `!${f}`;
|
168 | this.localPath.push(f);
|
169 | return f;
|
170 | });
|
171 | }
|
172 | }
|
173 |
|
174 |
|
175 | initAuth() {
|
176 | if (util.isObject(this.auth)) {
|
177 | this.auth['key'] && (this.authFile = this.auth['key']);
|
178 | this.auth['file'] && (this.authFile = this.auth['file']);
|
179 | }
|
180 | let authFile = T.Path.join(__dirname, this.authFile);
|
181 | if (this.authKey && T.fs.existsSync(authFile)) {
|
182 | let auth = JSON.parse(T.fs.readFileSync(authFile, 'utf8'))[this.authKey];
|
183 | if (!auth) this.emit('error', new Error('Could not find authkey in .ftppass'));
|
184 | if (typeof auth === 'string' && auth.indexOf(':') !== -1) {
|
185 | let authparts = auth.split(':');
|
186 | auth = {
|
187 | user: authparts[0],
|
188 | pass: authparts[1]
|
189 | };
|
190 | }
|
191 | this.user = auth.user;
|
192 | this.pass = auth.pass;
|
193 | }
|
194 |
|
195 | this.password = this.pass;
|
196 | this.username = this.user;
|
197 | }
|
198 |
|
199 |
|
200 | initPresentKey() {
|
201 | let key = this.key || this.keyLocation || null;
|
202 | if (key && typeof key === 'string') key = {
|
203 | location: key
|
204 | };
|
205 |
|
206 |
|
207 | if (!key && (this.passphrase || this.keyContents || !this.password)) {
|
208 | key = {};
|
209 | }
|
210 |
|
211 | if (key) {
|
212 |
|
213 | key.contents = key.contents || this.keyContents;
|
214 | key.passphrase = key.passphrase || this.passphrase;
|
215 |
|
216 |
|
217 | key.location = key.location || ['~/.ssh/id_rsa', '/.ssh/id_rsa', '~/.ssh/id_dsa', '/.ssh/id_dsa'];
|
218 |
|
219 |
|
220 | if (!util.isArray(key.location)) key.location = [key.location];
|
221 |
|
222 |
|
223 | if (key.location) {
|
224 | let home = process.env.HOME || process.env.USERPROFILE;
|
225 | for (let i = 0; i < key.location.length; ++i)
|
226 | if (key.location[i].substr(0, 2) === '~/') key.location[i] = T.Path.resolve(home, key.location[i].replace(/^~\//, ''));
|
227 |
|
228 | for (let i = 0, keyPath;
|
229 | (keyPath = key.location[i++]);) {
|
230 | if (T.fs.existsSync(keyPath)) {
|
231 | key.contents = T.fs.readFileSync(keyPath);
|
232 | break;
|
233 | }
|
234 | }
|
235 | } else if (!key.contents) {
|
236 | this.emit('error', new Error(`Cannot find RSA key, searched: ${key.location.join(', ')} `));
|
237 | }
|
238 | }
|
239 | this.key = key;
|
240 | if (this.key && this.key.contents) {
|
241 | this.keyContents = this.key.contents;
|
242 | this.privateKey = this.keyContents;
|
243 | this.passphrase = this.key.passphrase || this.passphrase;
|
244 | }
|
245 | }
|
246 |
|
247 |
|
248 | _initUploader() {
|
249 | this.uploader.on('start', this._onStart.bind(this));
|
250 | this.uploader.on('uploaded', this._onUploaded.bind(this));
|
251 | this.uploader.on('done', this._onDone.bind(this));
|
252 | this.uploader.setType(this.type);
|
253 | this.uploader.start();
|
254 | }
|
255 |
|
256 | _onStart({
|
257 | message
|
258 | }) {
|
259 | this._writeLogFile(`\n-------------Start Log [${dateFormat(Date.now(), 'yyyy-mm-dd')}]-------------\n`);
|
260 | T.log.yellow(this._startText());
|
261 | this._writeLogFile(this._startText());
|
262 | message && T.log.gray(message);
|
263 | message && this._writeLogFile(message);
|
264 | this._isProgressLog && this.loading.start(T.msg.green(this._startText()));
|
265 | }
|
266 |
|
267 | _onUploaded(payload) {
|
268 | const {
|
269 | file,
|
270 | realPath,
|
271 | size,
|
272 | error,
|
273 | message
|
274 | } = payload;
|
275 | if (error) {
|
276 | this._writeLogFile(message);
|
277 | return this._isProgressLog ? this.loading.text(message) : T.log.green(T.msg.red(message));
|
278 | }
|
279 | this._writeLogFile(message);
|
280 | util.isFunction(this.onUploadedFileSuccess) ? this.onUploadedFileSuccess.call(this, {
|
281 | file,
|
282 | realPath,
|
283 | size
|
284 | }) : this._isProgressLog ? this.loading.text(message) : T.log.green(message);
|
285 | }
|
286 |
|
287 | _onDone(payload) {
|
288 | this._isProgressLog && this.loading.stop();
|
289 | const {
|
290 | fileCount,
|
291 | modCount,
|
292 | status,
|
293 | duration,
|
294 | uploader
|
295 | } = payload;
|
296 | let iText = this._isIncrementType ? `√ [${this.host}] Deploy Status: ` + T.msg.cyan(status) : '';
|
297 | let dText = iText + T.msg.green(`√ [${this.host}] ${this._isFullType ? fileCount + ' ' : ''}files uploaded OK =^_^= (^_^) =^_^= !!!`);
|
298 | util.isFunction(this.onUploadedComplete) ? this.onUploadedComplete.call(this) : T.log.green(dText);
|
299 | this._writeLogFile(dText);
|
300 | this.uploader.stop();
|
301 | T.log.yellow(this._stopText(duration));
|
302 |
|
303 | this._writeLogFile(this._stopText(duration));
|
304 | this._writeLogFile(`-------------End Log-------------\n`);
|
305 | this.emit('deploy_done', {
|
306 | deploy: this
|
307 | });
|
308 | }
|
309 |
|
310 |
|
311 | _getConnectOptions() {
|
312 | let options = {
|
313 | host: this.host,
|
314 | port: this.port,
|
315 | username: this.username
|
316 | };
|
317 |
|
318 | if (this.password) {
|
319 | options.password = this.password;
|
320 | } else if (this.agent) {
|
321 | options.agent = this.agent;
|
322 | options.agentForward = this.agentForward || false;
|
323 | } else if (this.privateKey && this.passphrase) {
|
324 | options.privateKey = this.privateKey;
|
325 | options.passphrase = this.passphrase;
|
326 | }
|
327 | options.readyTimeout = this.timeout;
|
328 | options.platform = this.platform;
|
329 | options.keepaliveCountMax = this.keepaliveCountMax;
|
330 | return options;
|
331 | }
|
332 |
|
333 | _startText() {
|
334 | return `→ [${this.host}] Deploy start ...... `;
|
335 | }
|
336 | _stopText(duration) {
|
337 | return `√ [${this.host}] Deploy done =^_^= (^_^) =^_^= !!!, after ${T.msg.yellow((duration / 1000).toFixed(2) + ' s')} \n`;
|
338 | }
|
339 | _uploadedText(realPath, size) {
|
340 | return `√ [${T.getTime()}] uploaded '${realPath}', ${T.msg.yellow(size / 1000 + ' kb')}`;
|
341 | }
|
342 | _writeLogFile(txt) {
|
343 | txt = txt.replace(/(()?\[\d{2}m)/g, '') + '\n';
|
344 | if (!this.logFile || !util.isString(this.logFile)) return false;
|
345 | if (!T.fs.existsSync(this.logFile)) {
|
346 | T.fs.writeFileSync(this.logFile, txt, 'utf8');
|
347 | } else {
|
348 | T.fs.appendFileSync(this.logFile, txt, 'utf8');
|
349 | }
|
350 | }
|
351 | }
|
352 |
|
353 | module.exports = Deploy; |
\ | No newline at end of file |