hu 1.6.5 → 2.0.0
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.
- 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
|