hu 1.6.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/hu.gemspec +2 -0
- data/lib/hu/deploy.rb +429 -86
- data/lib/hu/version.rb +1 -1
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7cc26af0d32047802199428e01f0ba865a44f4a
|
4
|
+
data.tar.gz: abc0ac52217ac81fb9ee3c3bb15970df9675afa5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90ba3f3689b51068377b7c6fed9a5f88dff552499caaf7e29d2e4a41f56569de865d06bfe709bd8240aa6bb63bb19e2bf13858843567cdf684f47413b901beca
|
7
|
+
data.tar.gz: 21801cab2013fce68791d172245f7d65e76f5862ea470da43602c32d30454ea1afb3bae3f5d8c63ffe3ea1d430d656c8391f7808b93430d4e0819299bed201b9
|
data/hu.gemspec
CHANGED
@@ -34,6 +34,8 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_dependency 'tty-prompt', '~> 0.13.2'
|
35
35
|
spec.add_dependency 'tty-spinner', '= 0.3.0'
|
36
36
|
spec.add_dependency 'tty-table', '~> 0.9.0'
|
37
|
+
spec.add_dependency 'fidget', '~> 0.0.6'
|
38
|
+
spec.add_dependency 'octokit'
|
37
39
|
spec.add_dependency 'tty-cursor'
|
38
40
|
spec.add_dependency 'rainbow'
|
39
41
|
spec.add_dependency 'netrc', '= 0.11.0'
|
data/lib/hu/deploy.rb
CHANGED
@@ -7,12 +7,27 @@ module Hu
|
|
7
7
|
::TTY::Formats::FORMATS[:hu] = { frames: '🌑🌒🌓🌔🌕🌖🌗🌘'.chars, interval: 10 }
|
8
8
|
::TTY::Formats::FORMATS[:huroku] = { frames: '⣷⣯⣟⡿⢿⣻⣽⣾'.chars, interval: 10 }
|
9
9
|
|
10
|
+
class SigQuit < StandardError; end
|
11
|
+
|
10
12
|
RELEASE_TYPE_HINT = {
|
11
13
|
'patch' => 'only bugfixes',
|
12
14
|
'minor' => 'fully backwards compatible',
|
13
15
|
'major' => 'not backwards compatible'
|
14
16
|
}
|
15
17
|
|
18
|
+
NUMBERS = {
|
19
|
+
0 => 'Zero',
|
20
|
+
1 => 'One',
|
21
|
+
2 => 'Two',
|
22
|
+
3 => 'Three',
|
23
|
+
4 => 'Four',
|
24
|
+
5 => 'Five',
|
25
|
+
6 => 'Six',
|
26
|
+
7 => 'Seven',
|
27
|
+
8 => 'Eight',
|
28
|
+
9 => 'Nine'
|
29
|
+
}
|
30
|
+
|
16
31
|
$stdout.sync
|
17
32
|
@@shutting_down = false
|
18
33
|
@@spinner = nil
|
@@ -27,48 +42,57 @@ module Hu
|
|
27
42
|
text "\e[1mWARNING: Environment variable 'HEROKU_API_KEY' must be set.\e[0m"
|
28
43
|
end
|
29
44
|
filter do
|
30
|
-
|
31
|
-
|
45
|
+
begin
|
46
|
+
if Hu::API_TOKEN.nil?
|
47
|
+
STDERR.puts "\e[0;31;1mERROR: Environment variable 'HEROKU_API_KEY' must be set.\e[0m"
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
require 'tty-cursor'
|
51
|
+
print TTY::Cursor.hide + "\e[30;1ms"
|
52
|
+
require 'rainbow'
|
53
|
+
print 'y'
|
54
|
+
require 'rainbow/ext/string'
|
55
|
+
print 'n'
|
56
|
+
require 'platform-api'
|
57
|
+
print 'c'
|
58
|
+
require 'version_sorter'
|
59
|
+
print 'h'
|
60
|
+
require 'versionomy'
|
61
|
+
print 'r'
|
62
|
+
require 'tty-prompt'
|
63
|
+
print 'o'
|
64
|
+
require 'tty-table'
|
65
|
+
require 'octokit'
|
66
|
+
print 'n'
|
67
|
+
require 'open3'
|
68
|
+
require 'fidget'
|
69
|
+
print 'i'
|
70
|
+
require 'json'
|
71
|
+
require 'awesome_print'
|
72
|
+
print 'z'
|
73
|
+
require 'chronic_duration'
|
74
|
+
require 'tempfile'
|
75
|
+
print 'i'
|
76
|
+
require 'thread_safe'
|
77
|
+
require 'io/console'
|
78
|
+
print 'n'
|
79
|
+
require 'rugged'
|
80
|
+
require 'pty'
|
81
|
+
print 'g'
|
82
|
+
require 'thread'
|
83
|
+
require 'paint'
|
84
|
+
require 'lolcat/lol'
|
85
|
+
require 'io/console'
|
86
|
+
rescue Interrupt
|
87
|
+
puts "\e[0m*** Abort (SIGINT)"
|
88
|
+
puts TTY::Cursor.show
|
32
89
|
exit 1
|
33
90
|
end
|
34
|
-
require 'tty-cursor'
|
35
|
-
print TTY::Cursor.hide + "\e[30;1ms"
|
36
|
-
require 'rainbow'
|
37
|
-
print 'y'
|
38
|
-
require 'rainbow/ext/string'
|
39
|
-
print 'n'
|
40
|
-
require 'platform-api'
|
41
|
-
print 'c'
|
42
|
-
require 'version_sorter'
|
43
|
-
print 'h'
|
44
|
-
require 'versionomy'
|
45
|
-
print 'r'
|
46
|
-
require 'tty-prompt'
|
47
|
-
print 'o'
|
48
|
-
require 'tty-table'
|
49
|
-
print 'n'
|
50
|
-
require 'open3'
|
51
|
-
print 'i'
|
52
|
-
require 'json'
|
53
|
-
require 'awesome_print'
|
54
|
-
print 'z'
|
55
|
-
require 'chronic_duration'
|
56
|
-
require 'tempfile'
|
57
|
-
print 'i'
|
58
|
-
require 'thread_safe'
|
59
|
-
require 'io/console'
|
60
|
-
print 'n'
|
61
|
-
require 'rugged'
|
62
|
-
require 'pty'
|
63
|
-
print 'g'
|
64
|
-
require 'thread'
|
65
|
-
require 'paint'
|
66
|
-
require 'lolcat/lol'
|
67
|
-
require 'io/console'
|
68
91
|
end
|
69
92
|
|
70
93
|
def deploy(_cmd, _opts, _argv)
|
71
94
|
trap('INT') { shutdown; puts "\e[0m\e[35;1m^C\e[0m"; exit 1 }
|
95
|
+
|
72
96
|
at_exit do
|
73
97
|
if $!.class == SystemExit && 130 == $!.status
|
74
98
|
puts "\n\n"
|
@@ -287,7 +311,7 @@ EOM
|
|
287
311
|
puts
|
288
312
|
|
289
313
|
unless git_revisions[:release] == git_revisions[stag_app_name] || !release_branch_exists
|
290
|
-
puts ' Phase 1/
|
314
|
+
puts ' Phase 1/2 '.inverse + ' The local release branch ' + "release/#{release_tag}".bright + ' was created.'
|
291
315
|
puts ' Nothing else has happened so far. Push this branch to'
|
292
316
|
puts ' ' + stag_app_name.to_s.bright + ' to begin the deploy procedure.'
|
293
317
|
puts
|
@@ -296,24 +320,25 @@ EOM
|
|
296
320
|
if release_branch_exists && git_revisions[:release] == git_revisions[stag_app_name]
|
297
321
|
hyperlink = "\e]8;;#{app['web_url']}\007#{app['web_url']}\e]8;;\007"
|
298
322
|
|
299
|
-
puts ' Phase 2/
|
323
|
+
puts ' Phase 2/2 '.inverse + ' Your local ' + "release/#{release_tag}".bright + ' (formerly ' + 'develop'.bright + ') is live on ' + stag_app_name.to_s.bright + '.'
|
300
324
|
puts
|
301
325
|
puts ' Please test here: ' + hyperlink.bright
|
302
326
|
puts
|
303
|
-
puts ' If everything looks good you may proceed and
|
327
|
+
puts ' If everything looks good you may proceed and deploy to production.'
|
304
328
|
puts ' If there are problems: Quit, delete the release branch and start fixing.'
|
305
329
|
puts
|
306
330
|
elsif git_revisions[prod_app_name] != git_revisions[stag_app_name] && !release_branch_exists && git_revisions[:release] != git_revisions[stag_app_name]
|
307
331
|
hyperlink = "\e]8;;#{app['web_url']}\007#{app['web_url']}\e]8;;\007"
|
308
332
|
|
309
|
-
puts '
|
310
|
-
puts '
|
333
|
+
puts ' DEPLOY '.inverse + ' HEADS UP! This is the last chance to detect problems.'.bright
|
334
|
+
puts ' The final version of ' + "release/#{release_tag}".bright + ' is staged.'
|
311
335
|
puts
|
312
|
-
puts '
|
313
|
-
sleep 1
|
336
|
+
puts ' Test here: ' + hyperlink.bright
|
337
|
+
sleep 0.1
|
338
|
+
puts
|
339
|
+
puts ' This is the exact version that will be promoted to production.'
|
340
|
+
type " From here you are on your own. Good luck #{`whoami`.chomp}!"
|
314
341
|
puts
|
315
|
-
puts ' This is the exact version that will be promoted to production.'
|
316
|
-
puts " From here you are on your own. Good luck #{`whoami`.chomp}!"
|
317
342
|
puts
|
318
343
|
end
|
319
344
|
|
@@ -323,7 +348,7 @@ EOM
|
|
323
348
|
menu.choice 'Refresh', :refresh
|
324
349
|
menu.choice 'Quit', :abort_ask
|
325
350
|
unless git_revisions[:release] == git_revisions[stag_app_name] || !release_branch_exists
|
326
|
-
menu.choice "Push release/#{release_tag} to #{stag_app_name}", :push_to_staging
|
351
|
+
menu.choice "Push develop to origin/develop and release/#{release_tag} to #{stag_app_name}", :push_to_staging
|
327
352
|
end
|
328
353
|
if release_branch_exists
|
329
354
|
unless release_tag == tiny_bump
|
@@ -339,54 +364,147 @@ EOM
|
|
339
364
|
end
|
340
365
|
|
341
366
|
if git_revisions[:release] == git_revisions[stag_app_name]
|
342
|
-
menu.choice
|
367
|
+
menu.choice "DEPLOY to #{prod_app_name}", :finish_release
|
343
368
|
end
|
344
369
|
elsif git_revisions[prod_app_name] != git_revisions[stag_app_name]
|
345
370
|
menu.choice "DEPLOY (promote #{stag_app_name} to #{prod_app_name})", :DEPLOY
|
346
371
|
end
|
347
372
|
end
|
348
|
-
rescue TTY::
|
373
|
+
rescue TTY::Reader::InputInterrupt
|
349
374
|
choice = :abort
|
350
375
|
puts "\n\n"
|
351
376
|
end
|
352
377
|
|
353
378
|
case choice
|
354
379
|
when :DEPLOY
|
355
|
-
|
380
|
+
Fidget.prevent_sleep(:display, :sleep, :user) do
|
381
|
+
promote_to_production
|
382
|
+
end
|
356
383
|
anykey
|
357
384
|
when :finish_release
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
385
|
+
Fidget.prevent_sleep(:display, :sleep, :user) do
|
386
|
+
if ci_clear?
|
387
|
+
old_editor = ENV['EDITOR']
|
388
|
+
old_git_editor = ENV['GIT_EDITOR']
|
389
|
+
tf = Tempfile.new('hu-tag')
|
390
|
+
tf.write "#{release_tag}\n#{changelog}"
|
391
|
+
tf.close
|
392
|
+
ENV['EDITOR'] = ENV['GIT_EDITOR'] = "cp #{tf.path}"
|
393
|
+
env = {
|
394
|
+
'PREVIOUS_TAG' => highest_version,
|
395
|
+
'RELEASE_TAG' => release_tag,
|
396
|
+
'GIT_MERGE_AUTOEDIT' => 'no'
|
397
|
+
}
|
398
|
+
unless 0 == finish_release(release_tag, env, tf.path)
|
399
|
+
abort_merge
|
400
|
+
puts '*** ERROR! Could not finish release *** '.color(:red)
|
401
|
+
puts
|
402
|
+
puts 'This usually means a merge conflict or'
|
403
|
+
puts 'something equally annoying has occured.'
|
404
|
+
puts
|
405
|
+
puts 'Please bring the universe into a state'
|
406
|
+
puts 'where the above sequence of commands can'
|
407
|
+
puts 'succeed. Then try again.'
|
408
|
+
puts
|
409
|
+
exit 1
|
410
|
+
end
|
411
|
+
ENV['EDITOR'] = old_editor
|
412
|
+
ENV['GIT_EDITOR'] = old_git_editor
|
413
|
+
|
414
|
+
promote_to_production
|
415
|
+
promoted_at = Time.now.to_i
|
416
|
+
|
417
|
+
formation = h.formation.info(prod_app_name, 'web')
|
418
|
+
dyno_count = formation['quantity']
|
419
|
+
|
420
|
+
phase = :init
|
421
|
+
want = dyno_count
|
422
|
+
have = 0
|
423
|
+
release_rev = `git rev-parse develop`[0..7]
|
424
|
+
parser = Proc.new do |line, pid|
|
425
|
+
source, line = line.chomp.split(' ', 2)[1].split(' ', 2)
|
426
|
+
source = /\[(.*)\]:/.match(source)[1]
|
427
|
+
prefix = "\e[0m"
|
428
|
+
case phase
|
429
|
+
when :init
|
430
|
+
if line =~ /Deploy #{release_rev}/
|
431
|
+
phase = :observe
|
432
|
+
end
|
433
|
+
when :observe
|
434
|
+
if line =~ /State changed from starting to crashed/
|
435
|
+
prefix = "\e[31;1m"
|
436
|
+
elsif line =~ /State changed from starting to up/
|
437
|
+
prefix = "\e[32;1m"
|
438
|
+
have += 1
|
439
|
+
end
|
440
|
+
|
441
|
+
t = Time.now.to_i - promoted_at
|
442
|
+
ts = sprintf("%02d:%02d", t / 60, t % 60)
|
443
|
+
print "\e[30;1m[\e[0;33mT+#{ts} #{prefix}#{have}\e[0m/#{prefix}#{want}\e[30;1m] \e[0m"
|
444
|
+
print "#{source}: " unless source == 'api'
|
445
|
+
print prefix + line
|
446
|
+
puts
|
447
|
+
|
448
|
+
if have >= want
|
449
|
+
Process.kill("TERM", pid)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
puts
|
455
|
+
puts "\e[0;1m# Observe startup\e[0m"
|
456
|
+
if h.app_feature.info(prod_app_name, 'preboot').dig('enabled')
|
457
|
+
puts <<EOF
|
458
|
+
#
|
459
|
+
# \e[0m\e]8;;https://devcenter.heroku.com/articles/preboot\007Preboot\e]8;;\007 is \e[32;1menabled\e[0m for \e[1m#{prod_app_name}\e[0m.
|
460
|
+
#
|
461
|
+
# #{NUMBERS[formation['quantity']] || formation['quantity']} new dyno#{formation['quantity'] == 1 ? '' : 's'} (\e[1m#{formation['size']}\e[0m) #{formation['quantity'] == 1 ? 'is' : 'are'} starting up.
|
462
|
+
# The old dynos will shut down within 3 minutes.
|
463
|
+
EOF
|
464
|
+
end
|
465
|
+
|
466
|
+
script = <<-EOS.strip_heredoc
|
467
|
+
:stream
|
468
|
+
:quiet
|
469
|
+
:failquiet
|
470
|
+
:nospinner
|
471
|
+
:return
|
472
|
+
heroku logs --tail -a #{prod_app_name} | grep -E "heroku\\[|app\\[api\\]"
|
473
|
+
EOS
|
474
|
+
|
475
|
+
sigint_handler = Proc.new do
|
476
|
+
puts
|
477
|
+
print TTY::Cursor.up + TTY::Cursor.clear_line + TTY::Cursor.show
|
478
|
+
|
479
|
+
puts
|
480
|
+
puts "\e[43m \e[0m \e[0;32mRelease \e[1m#{release_tag}\e[0;32m is being launched on \e[1m#{prod_app_name}\e[0;32m\e[0m \e[43m \e[0m"
|
481
|
+
puts
|
482
|
+
|
483
|
+
shutdown
|
484
|
+
|
485
|
+
exit 0
|
486
|
+
end
|
487
|
+
|
488
|
+
run_each(script, parser: parser, sigint_handler: sigint_handler)
|
489
|
+
|
490
|
+
puts
|
491
|
+
print"\a"; sleep 0.4
|
492
|
+
puts "\e[42m \e[0m \e[0;32mRelease \e[1m#{release_tag}\e[0;32m has been deployed to \e[1m#{prod_app_name}\e[0;32m\e[0m \e[42m \e[0m"
|
493
|
+
print"\a"; sleep 0.4
|
494
|
+
puts
|
495
|
+
print"\a"; sleep 0.4
|
496
|
+
|
497
|
+
exit 0
|
498
|
+
end
|
381
499
|
end
|
382
|
-
ENV['EDITOR'] = old_editor
|
383
|
-
ENV['GIT_EDITOR'] = old_git_editor
|
384
|
-
anykey
|
385
500
|
when :push_to_staging
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
501
|
+
Fidget.prevent_sleep(:display, :sleep, :user) do
|
502
|
+
run_each <<-EOS.strip_heredoc
|
503
|
+
:stream
|
504
|
+
git push origin develop
|
505
|
+
git push #{push_url} release/#{release_tag}:master -f
|
506
|
+
EOS
|
507
|
+
end
|
390
508
|
anykey
|
391
509
|
when :abort_ask
|
392
510
|
puts if delete_branch("release/#{release_tag}")
|
@@ -411,6 +529,169 @@ EOM
|
|
411
529
|
end
|
412
530
|
end
|
413
531
|
|
532
|
+
def type(text, delay=0.01)
|
533
|
+
text.chars.each do |c|
|
534
|
+
print c
|
535
|
+
sleep rand * delay unless c == ' '
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
def ci_info
|
540
|
+
puts
|
541
|
+
if ENV['HU_GITHUB_ACCESS_TOKEN'].nil?
|
542
|
+
msg = "ERROR: Environment variable 'HU_GITHUB_ACCESS_TOKEN' must be set."
|
543
|
+
else
|
544
|
+
msg = 'ERROR: Github access token is invalid or has insufficient permissions'
|
545
|
+
end
|
546
|
+
puts msg.color(:red)
|
547
|
+
puts <<EOF
|
548
|
+
|
549
|
+
1. Go to \e]8;;https://github.com/settings/tokens\007https://github.com/settings/tokens\e]8;;\007
|
550
|
+
|
551
|
+
2. Click on [Generate new token]
|
552
|
+
|
553
|
+
3. Create a token with (only) the following permissions:
|
554
|
+
|
555
|
+
- repo:status
|
556
|
+
- repo_deployment
|
557
|
+
- public_repo
|
558
|
+
- read:user
|
559
|
+
|
560
|
+
4. Add the following line to your shell environment (e.g. ~/.bash_profile):
|
561
|
+
|
562
|
+
\e[1mexport HU_GITHUB_ACCESS_TOKEN=<your_token>\e[0m
|
563
|
+
|
564
|
+
EOF
|
565
|
+
end
|
566
|
+
|
567
|
+
def ci_status(release_branch_exists=true)
|
568
|
+
okit = Octokit::Client.new(access_token: ENV['HU_GITHUB_ACCESS_TOKEN'])
|
569
|
+
|
570
|
+
repo_name = @git.remotes['origin'].url.split(':')[1].gsub('.git', '')
|
571
|
+
|
572
|
+
begin
|
573
|
+
raw_status_develop = status_develop = okit.status(repo_name, @git.branches["origin/develop"].target_id)
|
574
|
+
status_develop = status_develop[:statuses].empty? ? ' ' : status_develop[:state]
|
575
|
+
status_develop = ' ' if @git.branches["origin/develop"].target_id != @git.branches["develop"].target_id
|
576
|
+
status_develop = ' ' unless release_branch_exists
|
577
|
+
rescue Octokit::NotFound, Octokit::Unauthorized
|
578
|
+
return :error
|
579
|
+
end
|
580
|
+
|
581
|
+
begin
|
582
|
+
# status_master = okit.status(repo_name, @git.branches["origin/master"].target_id)
|
583
|
+
# p status_master
|
584
|
+
# status_master = status_master[:statuses].empty? ? 'n/a' : status_master[:state]
|
585
|
+
# status_master = ' ' if @git.branches["origin/master"].target_id != @git.branches["master"].target_id
|
586
|
+
status_master = ' '
|
587
|
+
rescue Octokit::NotFound
|
588
|
+
status_master = 'unknown'
|
589
|
+
end
|
590
|
+
|
591
|
+
{
|
592
|
+
master: status_master,
|
593
|
+
develop: status_develop,
|
594
|
+
raw_develop: raw_status_develop
|
595
|
+
}
|
596
|
+
end
|
597
|
+
|
598
|
+
def ci_symbol(value)
|
599
|
+
case value
|
600
|
+
when ' '
|
601
|
+
''
|
602
|
+
when 'pending'
|
603
|
+
' 🐌'
|
604
|
+
when 'success'
|
605
|
+
' ✅'
|
606
|
+
else
|
607
|
+
' ❌'
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def ci_clear?
|
612
|
+
return true if ENV['HU_GITHUB_ACCESS_TOKEN'].nil?
|
613
|
+
msg = ''
|
614
|
+
prefix = "CI: "
|
615
|
+
ci_develop = ''
|
616
|
+
begin_wait_at = Time.now - 1
|
617
|
+
i = 0
|
618
|
+
puts
|
619
|
+
Signal.trap('QUIT') { raise SigQuit }
|
620
|
+
Signal.trap('INT') { raise Interrupt }
|
621
|
+
while ci_develop != 'success' do
|
622
|
+
puts
|
623
|
+
print TTY::Cursor.up + TTY::Cursor.clear_line + TTY::Cursor.hide
|
624
|
+
print prefix
|
625
|
+
print ('-' * (ci_develop.length + 2)) unless i == 0
|
626
|
+
print msg unless i == 0
|
627
|
+
ci_develop = ci_status(true)[:develop] if i % 10 == 0
|
628
|
+
# ci_develop = 'failed'
|
629
|
+
|
630
|
+
puts
|
631
|
+
print TTY::Cursor.up + TTY::Cursor.clear_line + TTY::Cursor.show
|
632
|
+
|
633
|
+
print TTY::Cursor.hide + "\n" + TTY::Cursor.up
|
634
|
+
msg = " - press ^\\ to override, ^C to abort (#{ChronicDuration.output((Time.now - begin_wait_at).to_i, format: :short)}) "
|
635
|
+
|
636
|
+
status = ci_develop.upcase
|
637
|
+
case status
|
638
|
+
when 'PENDING'
|
639
|
+
status = "\e[44;33;1m PENDING \e[0m"
|
640
|
+
when 'SUCCESS'
|
641
|
+
status = "\e[42;30;1m CLEAR \e[0m"
|
642
|
+
when ' '
|
643
|
+
status = "\e[40;30;1m UNCONFIGURED \e[0m"
|
644
|
+
else
|
645
|
+
status = "\e[41;33;1m #{status} \e[0m"
|
646
|
+
end
|
647
|
+
print prefix + status
|
648
|
+
print msg unless ci_develop == 'success' || ci_develop == ' '
|
649
|
+
|
650
|
+
# key = nil
|
651
|
+
|
652
|
+
sleep 1
|
653
|
+
|
654
|
+
break if ci_develop == ' '
|
655
|
+
# catch :sigint do
|
656
|
+
# begin
|
657
|
+
# Timeout::timeout(1) do
|
658
|
+
# Signal.trap('INT') { throw :sigint }
|
659
|
+
# key = STDIN.getch
|
660
|
+
# end
|
661
|
+
# rescue Timeout::Error
|
662
|
+
# key = :timeout
|
663
|
+
# ensure
|
664
|
+
# Signal.trap('INT', 'DEFAULT')
|
665
|
+
# end
|
666
|
+
# end
|
667
|
+
# if key == "\u0003"
|
668
|
+
# puts
|
669
|
+
# print TTY::Cursor.up + TTY::Cursor.clear_line + TTY::Cursor.show
|
670
|
+
# return false
|
671
|
+
# end
|
672
|
+
|
673
|
+
i += 1
|
674
|
+
end
|
675
|
+
puts
|
676
|
+
print TTY::Cursor.up + TTY::Cursor.clear_line + TTY::Cursor.show
|
677
|
+
print TTY::Cursor.up
|
678
|
+
return true
|
679
|
+
rescue Interrupt
|
680
|
+
puts
|
681
|
+
print TTY::Cursor.up + TTY::Cursor.clear_line + TTY::Cursor.show
|
682
|
+
return false
|
683
|
+
rescue SigQuit
|
684
|
+
puts
|
685
|
+
print TTY::Cursor.up + TTY::Cursor.clear_line + TTY::Cursor.show
|
686
|
+
puts "CI: \e[41;33;1m OVERRIDE \e[0m"
|
687
|
+
return true
|
688
|
+
ensure
|
689
|
+
Signal.trap('QUIT', 'DEFAULT')
|
690
|
+
Signal.trap('INT', 'DEFAULT')
|
691
|
+
puts
|
692
|
+
print TTY::Cursor.up + TTY::Cursor.clear_line + TTY::Cursor.show
|
693
|
+
end
|
694
|
+
|
414
695
|
def show_pipeline_status(pipeline_name, stag_app_name, prod_app_name, release_tag, clear = true)
|
415
696
|
table = TTY::Table.new header: ['', 'commit', 'tag', 'last_modified', 'last_modified_by', 'dynos', '']
|
416
697
|
busy 'synchronizing', :dots
|
@@ -493,6 +774,17 @@ EOM
|
|
493
774
|
end
|
494
775
|
end
|
495
776
|
|
777
|
+
ci = Thread.new do
|
778
|
+
ci_status(branch_exists?("release/#{release_tag}"))
|
779
|
+
end
|
780
|
+
|
781
|
+
ci = ci.value
|
782
|
+
if ci == :error
|
783
|
+
unbusy
|
784
|
+
ci_info
|
785
|
+
exit 1
|
786
|
+
end
|
787
|
+
|
496
788
|
workers.each(&:join)
|
497
789
|
|
498
790
|
rows = []
|
@@ -506,6 +798,7 @@ EOM
|
|
506
798
|
revs[:master] = row[1] = `git rev-parse master`[0..5]
|
507
799
|
row[2] = `git tag --points-at master`
|
508
800
|
row[1] = row[1].color(row[1] == revs[:develop] ? :green : :red)
|
801
|
+
# row[3] = color_ci(ci[:master])
|
509
802
|
rows.unshift row
|
510
803
|
|
511
804
|
if branch_exists? "release/#{release_tag}"
|
@@ -517,10 +810,19 @@ EOM
|
|
517
810
|
rows.unshift row
|
518
811
|
end
|
519
812
|
|
813
|
+
row = tpl_row.dup
|
814
|
+
row[0] = 'origin/develop'
|
815
|
+
row[1] = `git rev-parse origin/develop`[0..5]
|
816
|
+
row[1] = row[1].color(row[1] == revs[:develop] ? :green : :red) + ci_symbol(ci[:develop])
|
817
|
+
row[2] = `git tag --points-at origin/develop`
|
818
|
+
# row[3] = color_ci(ci[:develop])
|
819
|
+
rows.unshift row
|
820
|
+
|
520
821
|
row = tpl_row.dup
|
521
822
|
row[0] = 'develop'
|
522
823
|
row[1] = revs[:develop].color(:green)
|
523
824
|
row[2] = `git tag --points-at develop`
|
825
|
+
# row[3] = color_ci(ci[:develop]) if
|
524
826
|
rows.unshift row
|
525
827
|
|
526
828
|
unbusy
|
@@ -557,7 +859,24 @@ EOM
|
|
557
859
|
end
|
558
860
|
end
|
559
861
|
|
862
|
+
# p ci
|
863
|
+
if ci[:develop] != ' '
|
864
|
+
ci[:raw_develop][:statuses].each do |status|
|
865
|
+
if ['failure', 'error'].include? status[:state]
|
866
|
+
puts
|
867
|
+
print " CI #{status[:state].upcase} ".background(:red).color(:yellow).bright
|
868
|
+
print " \033]1337;RequestAttention=fireworks\a"
|
869
|
+
sleep 1
|
870
|
+
puts "#{status[:description]}".bright
|
871
|
+
puts " " + (" " * status[:state].length) + status[:target_url]
|
872
|
+
end
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
560
876
|
revs
|
877
|
+
rescue Interrupt
|
878
|
+
puts "*** Abort"
|
879
|
+
exit 1
|
561
880
|
end
|
562
881
|
|
563
882
|
def heroku_app_by_git(git_url)
|
@@ -603,10 +922,14 @@ EOM
|
|
603
922
|
opts = {
|
604
923
|
quiet: false,
|
605
924
|
failfast: true,
|
925
|
+
failquiet: false,
|
606
926
|
spinner: true,
|
607
|
-
stream: false
|
927
|
+
stream: false,
|
928
|
+
parser: nil
|
608
929
|
}.merge(opts)
|
609
930
|
|
931
|
+
parser = opts[:parser]
|
932
|
+
|
610
933
|
@spinlock ||= Mutex.new # :P
|
611
934
|
script.lines.each_with_index do |line, i|
|
612
935
|
line.chomp!
|
@@ -616,6 +939,7 @@ EOM
|
|
616
939
|
when ':'
|
617
940
|
opts[:quiet] = true if line == ':quiet'
|
618
941
|
opts[:failfast] = false if line == ':return'
|
942
|
+
opts[:failquiet] = true if line == ':failquiet'
|
619
943
|
opts[:spinner] = false if line == ':nospinner'
|
620
944
|
if line == ':stream'
|
621
945
|
opts[:stream] = true
|
@@ -633,7 +957,7 @@ EOM
|
|
633
957
|
@tspin ||= Thread.new do
|
634
958
|
i = 0
|
635
959
|
loop do
|
636
|
-
break if @minispin_last_char_at == :end
|
960
|
+
break if @minispin_last_char_at == :end || @shutdown
|
637
961
|
begin
|
638
962
|
if 0.23 > Time.now - @minispin_last_char_at || @minispin_disable
|
639
963
|
sleep 0.1
|
@@ -658,10 +982,17 @@ EOM
|
|
658
982
|
|
659
983
|
PTY.spawn("stty rows #{rows} cols #{cols}; " + line) do |r, _w, pid|
|
660
984
|
begin
|
985
|
+
l = ''
|
661
986
|
until r.eof?
|
662
987
|
c = r.getc
|
663
988
|
@spinlock.synchronize do
|
664
|
-
print c
|
989
|
+
print c unless opts[:quiet]
|
990
|
+
if c == "\n" && parser
|
991
|
+
parser.call(l, pid)
|
992
|
+
l = ''
|
993
|
+
else
|
994
|
+
l += c
|
995
|
+
end
|
665
996
|
@minispin_last_char_at = Time.now
|
666
997
|
c = c.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: "\e") # barf.
|
667
998
|
# hold on when we are (likely) inside an escape sequence
|
@@ -673,6 +1004,14 @@ EOM
|
|
673
1004
|
# Linux raises EIO on EOF, cf.
|
674
1005
|
# https://github.com/ruby/ruby/blob/57fb2199059cb55b632d093c2e64c8a3c60acfbb/ext/pty/pty.c#L519
|
675
1006
|
nil
|
1007
|
+
rescue Interrupt
|
1008
|
+
if opts[:sigint_handler]
|
1009
|
+
opts[:sigint_handler].call
|
1010
|
+
else
|
1011
|
+
puts
|
1012
|
+
puts "*** Abort (SIGINT)"
|
1013
|
+
exit 1
|
1014
|
+
end
|
676
1015
|
end
|
677
1016
|
|
678
1017
|
_pid, status = Process.wait2(pid)
|
@@ -694,7 +1033,7 @@ EOM
|
|
694
1033
|
end
|
695
1034
|
next unless status.exitstatus != 0
|
696
1035
|
shutdown if opts[:failfast]
|
697
|
-
puts "Error, exit #{status.exitstatus}: #{line} (L#{i})".color(:red).bright
|
1036
|
+
puts "Error, exit #{status.exitstatus}: #{line} (L#{i})".color(:red).bright unless opts[:failquiet]
|
698
1037
|
|
699
1038
|
exit status.exitstatus if opts[:failfast]
|
700
1039
|
return status.exitstatus
|
@@ -734,7 +1073,11 @@ EOM
|
|
734
1073
|
|
735
1074
|
def delete_branch(branch_name)
|
736
1075
|
return false unless branch_exists? branch_name
|
737
|
-
|
1076
|
+
begin
|
1077
|
+
return false if TTY::Prompt.new.no?("Delete branch #{branch_name}?")
|
1078
|
+
rescue TTY::Reader::InputInterrupt
|
1079
|
+
return false
|
1080
|
+
end
|
738
1081
|
run_each <<-EOS.strip_heredoc
|
739
1082
|
:quiet
|
740
1083
|
# Delete branch #{branch_name}
|
@@ -1015,8 +1358,8 @@ EOM
|
|
1015
1358
|
unbusy
|
1016
1359
|
end
|
1017
1360
|
|
1018
|
-
def anykey
|
1019
|
-
unless ENV['HU_ANYKEY']
|
1361
|
+
def anykey(force=false)
|
1362
|
+
unless ENV['HU_ANYKEY'] || force
|
1020
1363
|
puts
|
1021
1364
|
return
|
1022
1365
|
end
|
data/lib/hu/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- moe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -192,6 +192,34 @@ dependencies:
|
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: 0.9.0
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: fidget
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: 0.0.6
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: 0.0.6
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: octokit
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :runtime
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
195
223
|
- !ruby/object:Gem::Dependency
|
196
224
|
name: tty-cursor
|
197
225
|
requirement: !ruby/object:Gem::Requirement
|