- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576
- 577
- 578
- 579
- 580
- 581
- 582
- 583
- 584
- 585
- 586
- 587
- 588
- 589
- 590
- 591
- 592
- 593
- 594
- 595
- 596
- 597
- 598
- 599
- 600
- 601
- 602
- 603
- 604
- 605
- 606
- 607
- 608
- 609
- 610
- 611
- 612
- 613
- 614
- 615
- 616
- 617
- 618
- 619
- 620
- 621
- 622
- 623
- 624
- 625
- 626
- 627
- 628
- 629
- 630
- 631
- 632
- 633
- 634
- 635
- 636
- 637
- 638
- 639
- 640
- 641
- 642
- 643
- 644
- 645
- 646
- 647
- 648
- 649
- 650
- 651
- 652
- 653
- 654
- 655
- 656
- 657
- 658
- 659
- 660
- 661
- 662
- 663
- 664
- 665
- 666
- 667
- 668
- 669
- 670
- 671
- 672
- 673
- 674
- 675
- 676
- 677
- 678
- 679
- 680
- 681
- 682
- 683
- 684
- 685
- 686
- 687
- 688
- 689
- 690
- 691
- 692
- 693
- 694
- 695
- 696
- 697
- 698
- 699
- 700
- 701
- 702
- 703
- 704
- 705
- 706
- 707
- 708
- 709
- 710
- 711
- 712
- 713
- 714
- 715
- 716
- 717
- 718
- 719
- 720
- 721
- 722
- 723
- 724
- 725
- 726
- 727
- 728
- 729
- 730
- 731
- 732
- 733
- 734
- 735
- 736
- 737
- 738
- 739
- 740
- 741
- 742
- 743
'use strict';
var fs = require('fs')
, _ = require('lodash')
, Connection = require('ssh2')
, colors = require('colors')
, path = require('path')
, cli = require('command-line-args')
, blessed = require('blessed')
;
var args = cli([
{name: 'action', alias: 'a', type: String, defaultValue: 'up'},
{name: 'env', alias: 'e', type: String, defaultValue: 'stg', required: true},
{name: 'mastheads', alias: 'm', type: Boolean},
{name: 'templates', alias: 't', type: String, multiple: true, defaultValue: []},
{name: 'pages', alias: 'p', type: String, multiple: true, defaultOption: true}
]).parse()
if (typeof args.pages == 'undefined') throw '** Must specify pages to upload';
if (!fs.existsSync(process.env.HOME + '/.ssh/id_rsa')) throw '** Needs private key in ~/.ssh/id_rsa'.red
class Terminal {
constructor() {
this.screen = blessed.screen({
smartCSR: true,
title: 'swag..?'
});
this.mainbox = blessed.box({
left: 0,
top: 0,
width: this.screen.width,
height: this.screen.height,
})
this.title = blessed.box({
top: 0,
left: 'center',
width: '100%',
height: 'shrink',
tags: true,
content: `Pushing to {bold}${args.env}!{/bold}`,
style: {
fg: 'white',
bg: function() {return args.env == 'stg' ? 'blue' : 'magenta'}()
}
})
this.log = blessed.log({
top: 2,
height: '100%-5',
mouse: true,
tags: true,
scrollbar: {
track: {
bg: 'black',
fg: 'cyan'
},
style: {
bg: 'red'
}
}
})
this.deleteForm = blessed.Form({
keys: true,
left: 'center',
top: 'center',
width: '60%',
shrink: true,
bg: 'white',
fg: 'black',
content: `Delete from ${args.env}?`,
padding: {
left: 2,
right: 2,
top: 1,
bottom: 1
}
})
this.deleteCancel = blessed.button({
parent: this.deleteForm,
mouse: true,
keys: true,
shrink: true,
right: 2,
width: '30%',
top: 2,
name: 'cancel',
content: 'Cancel',
style: {
bg: 'yellow',
fg: 'black'
},
padding: {
left: 1,
right: 1,
top: 1,
bottom: 1
},
})
this.deleteOK = blessed.button({
parent: this.deleteForm,
mouse: true,
keys: true,
shrink: true,
left: 2,
top: 2,
width: '30%',
tags: true,
name: 'ok',
content: ' Yes',
style: {
bg: 'yellow',
fg: 'black',
hover: { bg: 'red' },
focus: { bg: 'green'}
},
padding: {
left: 1,
right: 1,
top: 1,
bottom: 1
}
})
this.progressbar = blessed.ProgressBar({
bottom: 0,
left: 0,
height: 'shrink',
width: '100%',
filled: 1,
content: '',
border: {
type: 'line'
},
style: {
bg: 'black',
bar: {
bg: 'green',
fg: 'black'
},
}
})
this.statusBox = blessed.box({
top: '50%-4',
left: 'center',
height: 'shrink',
padding: 1,
tags: true,
content: '{bold}Creating directories...{/bold}',
style: {
fg: 'cyan',
bg: 'black'
},
border: {
type: 'line'
}
})
this.screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
}
uploadLayout() {
this.screen.append(this.mainbox)
this.mainbox.append(this.title)
this.mainbox.append(this.log)
this.mainbox.append(this.progressbar)
this.screen.render()
}
deleteLayout() {
this.screen.append(this.mainbox)
this.mainbox.append(this.log)
this.mainbox.append(this.deleteForm)
this.screen.render()
}
updateProgress(innerValue, value, logmsg) {
this.progressbar.progress(value)
this.progressbar.setContent(innerValue)
this.log.add(logmsg)
this.screen.render()
}
toggleStatusBox(msg, kill) {
if (kill) {
this.statusBox.hide()
} else {
this.statusBox.show()
this.statusBox.setContent(msg)
this.mainbox.append(this.statusBox)
}
this.screen.render()
}
}
class Config {
constructor(env) {
this.stg = {
host: '10.130.250.237',
user: 'lpstagingadmin',
deployPath: '/mnt/landingpagestg/autolandingpages/us'
}
this.prod = {
host: '10.130.250.237',
user: 'lpprodadmin',
deployPath: '/mnt/landingpageprod/autolandingpages/us'
}
this.env = env;
this.max_connections = 9;
this.deployPath = this[env].deployPath
this.ssh2 = {
host: this[env].host,
port: 22,
username: this[env].user,
password: this[env].password,
readyTimeout: 15000,
keepaliveInterval: 0,
privateKey: fs.readFileSync(process.env.HOME + '/.ssh/id_rsa'),
}
}
}
var errors = {
ENETUNREACH: 'Connection was refused. Check your internet connection.',
ENOTFOUND: 'Remote address not found.',
ECONNRESET: 'Connection dropped or reset.'
}
var config = new Config(args.env)
var templates = fs.readdirSync('../templates/')
var pages = _.intersection(fs.readdirSync('../deploy/sbsr/'), args.pages);
var fourOhfours = _.difference(args.pages, pages);
if (args.action == 'up' && fourOhfours.length) throw `*** Below pages not found:\n${fourOhfours}`.red
if (args.templates.length) {
var diff = _.difference(args.templates, templates)
if (diff.length)
throw `** Following template(s) not found:\n${diff}`.red
templates = _.without(args.templates, templates)
}
var toDeploy = {
mastheads: [],
files: []
}
if (args.mastheads && args.action == 'up') {
var json = JSON.parse(fs.readFileSync('../data/pages.json', {encoding: 'utf8'}))
var mastheads = pages
.filter(page => !page.match(/js|assets|css/))
.map(page => json[page].header.mastheadImage)
var mastheadDirs = _.intersection(
templates,
fs.readdirSync('../deploy/globals/img/mastheads/')
);
toDeploy.mastheads = mastheads.map((masthead, i) => {
return mastheadDirs.map(dir => {
return `../deploy/globals/img/mastheads/${dir}/${masthead}`
})
})[0]
var connection = new Connection()
connection.connect(config.ssh2)
connection.on('ready', () => createRemoteDirs(connection, toDeploy.mastheads))
connection.on('close', () => {
var conn = new Connection()
conn.connect(config.ssh2)
conn.on('ready', (err, sftp) => {
conn.sftp((err, sftp) => {
toDeploy.mastheads.forEach(function(img, i) {
sftp.fastPut(img, remotify(img), (err) => {
var logMessage = `{yellow-fg}✔ ${img}{/yellow-bg}`
if (err) logMessage = `{red-fg}{bold}✕ ${img}{/bold}{/red-bg}\n${err}`
terminal.updateProgress(0, 0, logMessage)
})
})
})
})
})
}
for (var template of templates) {
for (var page of pages) {
let uri = `../deploy/${template}/${page}`
try {
fs.readdirSync(uri).forEach(file => toDeploy.files.push(`${uri}/${file}`))
} catch (e) {
throw `
Deploy dirs are not in sync. run rsifast and try again.
This error means some deploy/<template> has folders
that some other template(s) dont, when in theory, all
folders inside deploy should have the same pages.
ie, if deploy/sbs/foobar exists, but deploy/newlang/foobar does not,
this error happens
`.red
}
}
}
var models = _.intersection(fs.readdirSync('../rsi'), args.pages)
models.every(model => toDeploy.files.push(`../deploy/globals/models/${model}.js`))
function up() {
var connection = new Connection()
connection.connect(config.ssh2)
connection.on('ready', (sftp) => createRemoteDirs(connection, toDeploy.files))
connection.on('close', () => deploy())
function deploy() {
let connections = []
let openConnections = 0
for (let i = 0; i < config.max_connections; i++) {
connections.push(new Connection())
}
terminal.toggleStatusBox(`creating ${config.max_connections} connections..`)
connections.forEach((connection, i) => {
connection.on('ready', () => {
++openConnections
connection.sftp((err, sftp) => upload(connection, sftp,i))
if (i == config.max_connections-1) terminal.toggleStatusBox(null, true)
}).connect(config.ssh2)
})
var totalFiles = toDeploy.files.length
var totalPushed = 0
function upload(conn, sftp, i) {
var barValue = `${totalPushed}/${totalFiles}`
var barProgress = (100/totalFiles)
if (toDeploy.files.length) {
var file = toDeploy.files.shift()
sftp.fastPut(file, remotify(file), (err) => {
file = file.slice(10)
var logMessage;
if (err) {
logMessage = `{red-fg}{bold}✕ ${file}{/bold}{/red-bg}\n${err}`
} else {
++totalPushed
var logMessage = `{green-fg}✔ ${file}{/green-bg}`
}
terminal.updateProgress(barValue, barProgress, logMessage)
upload(conn, sftp,i)
})
} else {
conn.end()
--openConnections
if (!openConnections) {
var completedMessage = '{bold}-- Completed.{/bold}'
if (totalPushed !== totalFiles)
completedMessage += '\n{red-fg}-- Some files didn\'t make it.{/red-fg}'
terminal.updateProgress(barValue, barProgress, completedMessage)
}
}
}
}
}
var terminal = new Terminal()
if (args.action == 'up') {
terminal.uploadLayout()
up()
}
if (args.action == 'down') {
terminal.deleteLayout()
terminal.deleteOK.on('press', () => down())
terminal.deleteCancel.on('press', () => process.exit(0))
terminal.screen.render()
}
function down() {
terminal.deleteForm.destroy()
terminal.screen.render()
terminal.updateProgress(0, 0, '{red-fg}{bold}deleting....{/bold}{red-fg}')
var cmd = _.unique(toDeploy.files.map(uri => `'${path.dirname(uri)}'`))
cmd.forEach(uri => terminal.updateProgress(0, 0, `{yellow-fg}{bold}${remotify(uri)}{/bold}{/yellow-bg}`))
cmd = remotify(`rm -rfv ${cmd.join(' ')}`)
var connection = new Connection()
connection.on('ready', function() {
connection.exec(cmd, function(err, stream) {
if (err) throw 'ERROR! could not execute delete command'.red
stream.on('data', function(data) {
terminal.updateProgress(0, 0, data)
})
stream.on('close', function(code, signal) {
terminal.updateProgress(0, 0, '-- done --')
})
stream.stderr.on('data', function(data) {
terminal.updateProgress(0, 0, `{red-fg}data{/red-fg}`)
})
})
}).connect(config.ssh2)
}
function createRemoteDirs(connection, dirsArray) {
terminal.toggleStatusBox('Creating dirs..')
let dirs = dirsArray.map(uri => `'${path.dirname(uri)}'`)
dirs.push('../deploy/globals/models/')
let cmd = remotify(`mkdir -p ${dirs.join(' ')}`)
connection.exec(cmd, (e, stream) => {
if (e) throw errors[e].red
terminal.toggleStatusBox(null,true)
connection.end()
})
}
function remotify(uri) {
return uri.replace(/\.\.\/deploy/gi, config.deployPath)
}