hu 1.3.6 → 1.3.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +56 -0
- data/Gemfile +1 -0
- data/Rakefile +2 -1
- data/bin/hu +1 -1
- data/hu.gemspec +28 -27
- data/lib/hu/cli.rb +5 -5
- data/lib/hu/collab.rb +58 -58
- data/lib/hu/common.rb +10 -5
- data/lib/hu/deploy.rb +161 -167
- data/lib/hu/version.rb +2 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8a02a4340c2f4801397be2b132d2288cbf6753a
|
4
|
+
data.tar.gz: 7b2dc932c8dc82a4a67ce5b976b33d7e45de401a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65aa3c9352155a1c6a975a97b6cda88b26b6b923f44dddfb87a42e62919e594cb26422fdda354a3932ead1d63ae52d441644af8fe9df22648de53813d8df97d5
|
7
|
+
data.tar.gz: d3517a63fa07bce2035acaffbd3cb95415a59041b3241b1a05c3004ae5883956dcde6e1ef46c63bc242cb6df1f5532405b3563aace78467a20d70bb55a6c6ab9
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Offense count: 13
|
2
|
+
Metrics/AbcSize:
|
3
|
+
Max: 276
|
4
|
+
|
5
|
+
# Offense count: 2
|
6
|
+
# Configuration parameters: CountComments.
|
7
|
+
Metrics/ClassLength:
|
8
|
+
Max: 651
|
9
|
+
|
10
|
+
# Offense count: 6
|
11
|
+
Metrics/CyclomaticComplexity:
|
12
|
+
Max: 45
|
13
|
+
|
14
|
+
# Offense count: 83
|
15
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
|
16
|
+
# URISchemes: http, https
|
17
|
+
Metrics/LineLength:
|
18
|
+
Max: 176
|
19
|
+
|
20
|
+
# Offense count: 14
|
21
|
+
# Configuration parameters: CountComments.
|
22
|
+
Metrics/MethodLength:
|
23
|
+
Max: 230
|
24
|
+
|
25
|
+
# Offense count: 4
|
26
|
+
Metrics/PerceivedComplexity:
|
27
|
+
Max: 43
|
28
|
+
|
29
|
+
# Offense count: 4
|
30
|
+
Style/ClassVars:
|
31
|
+
Exclude:
|
32
|
+
- 'lib/hu/deploy.rb'
|
33
|
+
|
34
|
+
# Offense count: 5
|
35
|
+
Style/Documentation:
|
36
|
+
Exclude:
|
37
|
+
- 'spec/**/*'
|
38
|
+
- 'test/**/*'
|
39
|
+
- 'lib/hu/cli.rb'
|
40
|
+
- 'lib/hu/collab.rb'
|
41
|
+
- 'lib/hu/common.rb'
|
42
|
+
- 'lib/hu/deploy.rb'
|
43
|
+
|
44
|
+
# Offense count: 3
|
45
|
+
# Configuration parameters: AllowedVariables.
|
46
|
+
Style/GlobalVars:
|
47
|
+
Exclude:
|
48
|
+
- 'lib/hu/cli.rb'
|
49
|
+
- 'lib/hu/collab.rb'
|
50
|
+
|
51
|
+
# Offense count: 1
|
52
|
+
# Cop supports --auto-correct.
|
53
|
+
# Configuration parameters: AllowAsExpressionSeparator.
|
54
|
+
Style/Semicolon:
|
55
|
+
Exclude:
|
56
|
+
- 'lib/hu/deploy.rb'
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'bundler/gem_tasks'
|
data/bin/hu
CHANGED
data/hu.gemspec
CHANGED
@@ -1,41 +1,42 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'hu/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
+
spec.name = 'hu'
|
8
9
|
spec.version = Hu::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
10
|
+
spec.authors = ['moe']
|
11
|
+
spec.email = ['moe@busyloop.net']
|
12
|
+
spec.summary = 'Heroku Utility.'
|
13
|
+
spec.description = 'Heroku Utility.'
|
14
|
+
spec.homepage = 'https://github.com/busyloop/hu'
|
15
|
+
spec.license = 'MIT'
|
15
16
|
|
16
|
-
spec.files = `git ls-files`.split(
|
17
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
20
21
|
spec.required_ruby_version = '>= 2.3.0'
|
21
22
|
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
24
|
+
spec.add_development_dependency 'rake'
|
25
|
+
spec.add_development_dependency 'bump'
|
25
26
|
|
26
|
-
spec.add_dependency
|
27
|
-
spec.add_dependency
|
28
|
-
spec.add_dependency
|
29
|
-
spec.add_dependency
|
30
|
-
spec.add_dependency
|
31
|
-
spec.add_dependency
|
32
|
-
spec.add_dependency
|
33
|
-
spec.add_dependency
|
34
|
-
spec.add_dependency
|
35
|
-
spec.add_dependency
|
36
|
-
spec.add_dependency
|
37
|
-
spec.add_dependency
|
38
|
-
spec.add_dependency
|
39
|
-
spec.add_dependency
|
40
|
-
spec.add_dependency
|
27
|
+
spec.add_dependency 'optix', '~> 1.2.4'
|
28
|
+
spec.add_dependency 'blackbox', '~> 3.1.4'
|
29
|
+
spec.add_dependency 'platform-api', '~> 0.7.0'
|
30
|
+
spec.add_dependency 'powerbar', '>= 1.0.16'
|
31
|
+
spec.add_dependency 'hashdiff', '~> 0.3.0'
|
32
|
+
spec.add_dependency 'version_sorter', '~> 2.0.0'
|
33
|
+
spec.add_dependency 'versionomy', '~> 0.5.0'
|
34
|
+
spec.add_dependency 'tty-prompt'
|
35
|
+
spec.add_dependency 'tty-spinner'
|
36
|
+
spec.add_dependency 'tty-table'
|
37
|
+
spec.add_dependency 'rainbow'
|
38
|
+
spec.add_dependency 'netrc'
|
39
|
+
spec.add_dependency 'chronic_duration'
|
40
|
+
spec.add_dependency 'thread_safe'
|
41
|
+
spec.add_dependency 'rugged'
|
41
42
|
end
|
data/lib/hu/cli.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'hu/version'
|
2
3
|
require 'optix'
|
3
4
|
require 'powerbar'
|
@@ -11,18 +12,17 @@ require 'hu/deploy'
|
|
11
12
|
|
12
13
|
module Hu
|
13
14
|
class Cli < Optix::Cli
|
14
|
-
Optix
|
15
|
+
Optix.command do
|
15
16
|
text "Hu v#{Hu::VERSION} - Heroku Utility"
|
16
|
-
opt :quiet,
|
17
|
-
opt :version,
|
17
|
+
opt :quiet, 'Quiet mode (no progress output)', default: false
|
18
|
+
opt :version, 'Print version and exit', short: :none
|
18
19
|
trigger :version do
|
19
20
|
puts "Hu v#{Hu::VERSION}"
|
20
21
|
end
|
21
|
-
filter do |
|
22
|
+
filter do |_cmd, opts, _argv|
|
22
23
|
$quiet = opts[:quiet]
|
23
24
|
$quiet = true unless STDOUT.isatty
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
28
|
-
|
data/lib/hu/collab.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'powerbar'
|
2
3
|
require 'yaml'
|
3
4
|
require 'hashdiff'
|
@@ -12,25 +13,25 @@ module Hu
|
|
12
13
|
class InvalidPlan < StandardError
|
13
14
|
attr_accessor :invalid_plan
|
14
15
|
|
15
|
-
def initialize(message=nil, invalid_plan=nil)
|
16
|
+
def initialize(message = nil, invalid_plan = nil)
|
16
17
|
super(message)
|
17
18
|
self.invalid_plan = invalid_plan
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
|
-
text
|
22
|
-
text
|
23
|
-
text
|
24
|
-
text
|
25
|
-
text
|
26
|
-
text
|
27
|
-
text
|
28
|
-
text
|
29
|
-
text
|
30
|
-
text
|
22
|
+
text 'Manage application/collaborator mapping.'
|
23
|
+
text ''
|
24
|
+
text 'Start by exporting the current mapping,'
|
25
|
+
text 'edit to taste, then diff and import.'
|
26
|
+
text ''
|
27
|
+
text 'The environment variable HU_IGNORE_APPS'
|
28
|
+
text 'may contain space delimited glob(7) patterns'
|
29
|
+
text 'of apps to be ignored.'
|
30
|
+
text ''
|
31
|
+
text 'WARNING: If you remove yourself from an application'
|
31
32
|
text " then hu won't be able to see it anymore."
|
32
33
|
if Hu::API_TOKEN.nil?
|
33
|
-
text
|
34
|
+
text ''
|
34
35
|
text "\e[1mWARNING: Environment variable 'HEROKU_API_KEY' must be set.\e[0m"
|
35
36
|
end
|
36
37
|
filter do
|
@@ -44,14 +45,14 @@ module Hu
|
|
44
45
|
OP_COLORS = {
|
45
46
|
'+' => "\e[0;1;32m",
|
46
47
|
'-' => "\e[0;1;34m",
|
47
|
-
'~' => "\e[0;32m"
|
48
|
-
}
|
49
|
-
desc
|
50
|
-
text
|
51
|
-
parent
|
52
|
-
def diff(
|
48
|
+
'~' => "\e[0;32m"
|
49
|
+
}.freeze
|
50
|
+
desc 'Read mapping from stdin and diff to heroku state'
|
51
|
+
text 'Read mapping from stdin and diff to heroku state'
|
52
|
+
parent 'collab'
|
53
|
+
def diff(_cmd, opts, _argv)
|
53
54
|
parsed_state = parse_as_json_or_yaml(STDIN.read)
|
54
|
-
show_plan(
|
55
|
+
show_plan(plan(HashDiff.diff(heroku_state['apps'], parsed_state['apps']), opts))
|
55
56
|
end
|
56
57
|
|
57
58
|
def show_plan(plan)
|
@@ -61,7 +62,7 @@ module Hu
|
|
61
62
|
icon = ' '
|
62
63
|
if s[:method].nil?
|
63
64
|
color = "\e[0m"
|
64
|
-
msg =
|
65
|
+
msg = '<-- Can not resolve this (NO-OP)'
|
65
66
|
icon = "⚠️"
|
66
67
|
elsif s[:invalid]
|
67
68
|
color = "\e[0m"
|
@@ -74,27 +75,27 @@ module Hu
|
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
77
|
-
desc
|
78
|
-
text
|
79
|
-
opt :format,
|
80
|
-
parent
|
81
|
-
def export(
|
78
|
+
desc 'Print current mapping to stdout'
|
79
|
+
text 'Print current mapping to stdout'
|
80
|
+
opt :format, 'yaml|json', default: 'yaml'
|
81
|
+
parent 'collab', 'Manage application collaborators'
|
82
|
+
def export(_cmd, opts, _argv)
|
82
83
|
puts heroku_state.send("to_#{opts[:format]}".to_sym)
|
83
84
|
end
|
84
85
|
|
85
|
-
desc
|
86
|
-
text
|
87
|
-
opt :allow_create,
|
88
|
-
opt :silent_create,
|
89
|
-
parent
|
90
|
-
def import(
|
86
|
+
desc 'Read mapping from stdin and push to heroku'
|
87
|
+
text 'Read mapping from stdin and push to heroku'
|
88
|
+
opt :allow_create, 'Create new collaborators on heroku', short: 'c'
|
89
|
+
opt :silent_create, 'Suppress email invitation when creating collaborators'
|
90
|
+
parent 'collab'
|
91
|
+
def import(_cmd, opts, _argv)
|
91
92
|
parsed_state = parse_as_json_or_yaml(STDIN.read)
|
92
93
|
validators = {
|
93
|
-
:
|
94
|
-
unless heroku_state['collaborators'].include?
|
94
|
+
op_add_collaborators: proc do |op|
|
95
|
+
unless heroku_state['collaborators'].include?(op[:value]) || opts[:allow_create]
|
95
96
|
raise InvalidOperation, "Use -c to allow creation of new collaborator '#{op[:value]}'"
|
96
97
|
end
|
97
|
-
|
98
|
+
end
|
98
99
|
}
|
99
100
|
begin
|
100
101
|
plan(HashDiff.diff(heroku_state['apps'], parsed_state['apps']), opts, validators).each do |s|
|
@@ -104,14 +105,14 @@ module Hu
|
|
104
105
|
eol = "\e[1G"
|
105
106
|
if s[:method].nil?
|
106
107
|
color = "\e[0;41;33;1m"
|
107
|
-
msg =
|
108
|
+
msg = 'Skipped.'
|
108
109
|
icon = "\e[0;31;1m\u2718\e[0m" # X
|
109
110
|
eol = "\n"
|
110
111
|
end
|
111
112
|
STDERR.printf "%s %s%6s %-30s %-15s %-30s %s\e[0m%s", icon, color, s[:op_name], s[:app_name], s[:role], s[:value], msg, eol
|
112
113
|
next if s[:method].nil?
|
113
114
|
begin
|
114
|
-
|
115
|
+
send(s[:method], s)
|
115
116
|
STDERR.puts "\e[0;32;1m\u2713\e[0m\n" # check
|
116
117
|
rescue => e
|
117
118
|
STDERR.puts "\e[0;31;1m\u2718\e[0m\n" # X
|
@@ -130,9 +131,9 @@ module Hu
|
|
130
131
|
OP_MAP = {
|
131
132
|
'+' => 'add',
|
132
133
|
'-' => 'remove',
|
133
|
-
'~' => 'change'
|
134
|
-
}
|
135
|
-
def plan(diff, env={}, validators={})
|
134
|
+
'~' => 'change'
|
135
|
+
}.freeze
|
136
|
+
def plan(diff, env = {}, validators = {})
|
136
137
|
plan = []
|
137
138
|
last_error = nil
|
138
139
|
diff.each do |op, target, lval, rval|
|
@@ -146,10 +147,10 @@ module Hu
|
|
146
147
|
app_name: app_name,
|
147
148
|
op: op,
|
148
149
|
op_name: op_name,
|
149
|
-
method:
|
150
|
+
method: respond_to?(method_name) ? method_name : nil,
|
150
151
|
value: value,
|
151
152
|
role: role,
|
152
|
-
env: env
|
153
|
+
env: env
|
153
154
|
}
|
154
155
|
if validators.include? method_name
|
155
156
|
begin
|
@@ -160,12 +161,12 @@ module Hu
|
|
160
161
|
end
|
161
162
|
plan << operation
|
162
163
|
end
|
163
|
-
raise InvalidPlan.new(
|
164
|
+
raise InvalidPlan.new('Plan did not pass validation', plan) unless last_error.nil?
|
164
165
|
plan
|
165
166
|
end
|
166
167
|
|
167
168
|
def op_add_collaborators(args)
|
168
|
-
h.collaborator.create(args[:app_name], :
|
169
|
+
h.collaborator.create(args[:app_name], user: args[:value], silent: args[:env][:silent_create])
|
169
170
|
end
|
170
171
|
|
171
172
|
def op_remove_collaborators(args)
|
@@ -179,13 +180,13 @@ module Hu
|
|
179
180
|
begin
|
180
181
|
parsed = YAML.load(input)
|
181
182
|
if parsed.is_a? String
|
182
|
-
raise ArgumentError,
|
183
|
+
raise ArgumentError, 'Input parsed as YAML yields a String'
|
183
184
|
end
|
184
185
|
rescue => yex
|
185
|
-
STDERR.puts
|
186
|
-
STDERR.puts
|
186
|
+
STDERR.puts 'Error: Could neither parse stdin as YAML nor as JSON.'
|
187
|
+
STDERR.puts '-- JSON Error --'
|
187
188
|
STDERR.puts jex
|
188
|
-
STDERR.puts
|
189
|
+
STDERR.puts '-- YAML Error --'
|
189
190
|
STDERR.puts yex
|
190
191
|
raise ArgumentError
|
191
192
|
end
|
@@ -197,7 +198,7 @@ module Hu
|
|
197
198
|
unless parsed.include? 'apps'
|
198
199
|
raise ArgumentError, "Malformed input, key 'apps' not found."
|
199
200
|
end
|
200
|
-
parsed['apps'].reject!{ |e| ignored_app?(e) }
|
201
|
+
parsed['apps'].reject! { |e| ignored_app?(e) }
|
201
202
|
parsed['apps'].each do |app_name, v|
|
202
203
|
unless heroku_state['apps'].include? app_name
|
203
204
|
raise ArgumentError, "Unknown application: #{app_name}"
|
@@ -210,37 +211,37 @@ module Hu
|
|
210
211
|
end
|
211
212
|
|
212
213
|
def ignored_app?(app_name)
|
213
|
-
ENV.fetch('HU_IGNORE_APPS','').split(' ').each do |p|
|
214
|
+
ENV.fetch('HU_IGNORE_APPS', '').split(' ').each do |p|
|
214
215
|
return true if File.fnmatch(p, app_name)
|
215
216
|
end
|
216
217
|
false
|
217
218
|
end
|
218
219
|
|
219
|
-
def heroku_state(force_refresh=false)
|
220
|
-
return @heroku_state unless force_refresh
|
220
|
+
def heroku_state(force_refresh = false)
|
221
|
+
return @heroku_state unless force_refresh || @heroku_state.nil?
|
221
222
|
all_collaborators = Set.new
|
222
223
|
data = { 'apps' => {} }
|
223
|
-
app_names = h.app.list.map{|e| e['name']}.reject{ |e| ignored_app?(e) }
|
224
|
+
app_names = h.app.list.map { |e| e['name'] }.reject { |e| ignored_app?(e) }
|
224
225
|
threads = []
|
225
|
-
app_names.each_with_index do |app_name,
|
226
|
+
app_names.each_with_index do |app_name, _i|
|
226
227
|
threads << Thread.new do
|
227
228
|
d = data['apps'][app_name] = { 'collaborators' => [] }
|
228
|
-
h.collaborator.list(app_name).map
|
229
|
+
h.collaborator.list(app_name).map do |e|
|
229
230
|
case e['role']
|
230
231
|
when 'owner'
|
231
232
|
d['owner'] = e['user']['email']
|
232
233
|
when nil
|
233
234
|
d['collaborators'] << e['user']['email']
|
234
235
|
else
|
235
|
-
raise
|
236
|
+
raise "Unknown collaborator role: #{e['role']}"
|
236
237
|
end
|
237
238
|
all_collaborators << e['user']['email']
|
238
|
-
|
239
|
+
end
|
239
240
|
end
|
240
241
|
end
|
241
242
|
threads.each_with_index do |t, i|
|
242
243
|
t.join
|
243
|
-
pb :
|
244
|
+
pb msg: app_names[i], total: app_names.length, done: i + 1
|
244
245
|
end
|
245
246
|
pb :wipe
|
246
247
|
data['collaborators'] = all_collaborators.to_a.sort
|
@@ -256,7 +257,6 @@ module Hu
|
|
256
257
|
@pb ||= PowerBar.new
|
257
258
|
show_opts == :wipe ? @pb.wipe : @pb.show(show_opts)
|
258
259
|
end
|
259
|
-
|
260
260
|
end
|
261
261
|
end
|
262
262
|
end
|
data/lib/hu/common.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'blackbox/gem'
|
2
3
|
|
3
4
|
module Hu
|
@@ -11,9 +12,13 @@ class String
|
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
unless ENV['SKIP_VERSION_CHECK']
|
16
|
+
version_info = BB::Gem.version_info(check_interval: 900)
|
17
|
+
unless version_info[:installed_is_latest] == true
|
18
|
+
puts "\e[33;1mWoops! \e[0mA newer version of #{version_info[:gem_name]} is available."
|
19
|
+
puts " Please type '\e[1mgem install #{version_info[:gem_name]}\e[0m' to upgrade (v#{version_info[:gem_installed_version]} -> v#{version_info[:gem_latest_version]})."
|
20
|
+
sleep 1
|
21
|
+
puts
|
22
|
+
exit 1
|
23
|
+
end
|
19
24
|
end
|
data/lib/hu/deploy.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'version_sorter'
|
2
3
|
require 'versionomy'
|
3
4
|
require 'tty-prompt'
|
@@ -24,10 +25,10 @@ module Hu
|
|
24
25
|
@@shutting_down = false
|
25
26
|
@@spinner = nil
|
26
27
|
|
27
|
-
text
|
28
|
-
desc
|
28
|
+
text 'Interactive deployment.'
|
29
|
+
desc 'Interactive deployment'
|
29
30
|
if Hu::API_TOKEN.nil?
|
30
|
-
text
|
31
|
+
text ''
|
31
32
|
text "\e[1mWARNING: Environment variable 'HEROKU_API_KEY' must be set.\e[0m"
|
32
33
|
end
|
33
34
|
filter do
|
@@ -37,22 +38,22 @@ module Hu
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
|
-
def deploy(
|
41
|
+
def deploy(_cmd, _opts, _argv)
|
41
42
|
trap('INT') { shutdown; safe_abort; exit 1 }
|
42
|
-
at_exit
|
43
|
-
if
|
43
|
+
at_exit do
|
44
|
+
if $ERROR_INFO.class == SystemExit && 130 == $ERROR_INFO.status
|
44
45
|
shutdown
|
45
46
|
puts
|
46
47
|
safe_abort
|
47
48
|
end
|
48
|
-
|
49
|
+
end
|
49
50
|
|
50
51
|
begin
|
51
52
|
@git = Rugged::Repository.discover('.')
|
52
53
|
rescue Rugged::RepositoryError => e
|
53
54
|
puts
|
54
55
|
puts "Git error: #{e}".color(:red)
|
55
|
-
puts
|
56
|
+
puts 'You need to be inside the working copy of the app that you wish to deploy.'.color(:red)
|
56
57
|
puts
|
57
58
|
safe_abort
|
58
59
|
print TTY::Cursor.prev_line
|
@@ -65,7 +66,7 @@ module Hu
|
|
65
66
|
puts
|
66
67
|
puts "ERROR: Remote of branch 'master' does not point to 'origin'.".color(:red)
|
67
68
|
puts
|
68
|
-
puts
|
69
|
+
puts ' Sorry, we need an origin here. We really do.'
|
69
70
|
puts
|
70
71
|
exit 1
|
71
72
|
end
|
@@ -82,16 +83,16 @@ module Hu
|
|
82
83
|
unless @git.config['gitflow.prefix.versiontag'].nil? ||
|
83
84
|
@git.config['gitflow.prefix.versiontag'].empty?
|
84
85
|
puts
|
85
|
-
puts
|
86
|
+
puts 'ERROR: git-flow version prefix configured.'.color(:red)
|
86
87
|
puts
|
87
|
-
puts
|
88
|
+
puts ' Please use this command to remove the prefix:'
|
88
89
|
puts
|
89
90
|
puts " git config --add gitflow.prefix.versiontag ''".bright
|
90
91
|
puts
|
91
92
|
exit 1
|
92
93
|
end
|
93
94
|
|
94
|
-
push_url =
|
95
|
+
push_url = heroku_git_remote
|
95
96
|
|
96
97
|
wc_update = Thread.new { update_working_copy }
|
97
98
|
|
@@ -100,7 +101,7 @@ module Hu
|
|
100
101
|
if app.nil?
|
101
102
|
puts
|
102
103
|
puts "ERROR: Found no heroku app for git remote #{push_url}".color(:red)
|
103
|
-
puts
|
104
|
+
puts ' Are you logged into the right heroku account?'.color(:red)
|
104
105
|
puts
|
105
106
|
puts " Please run 'git remote rm heroku'. Then run 'hu deploy' again to select a new remote."
|
106
107
|
puts
|
@@ -112,10 +113,10 @@ module Hu
|
|
112
113
|
if app['id'] != stag_app_id
|
113
114
|
puts
|
114
115
|
puts "ERROR: The git remote 'heroku' points to app '#{app['name']}'".color(:red)
|
115
|
-
puts " which is not in stage 'staging'".color(:red)+
|
116
|
+
puts " which is not in stage 'staging'".color(:red) +
|
116
117
|
" of pipeline '#{pipeline_name}'.".color(:red)
|
117
118
|
puts
|
118
|
-
puts
|
119
|
+
puts ' The referenced app MUST be the staging member of the pipeline.'
|
119
120
|
|
120
121
|
puts " Please run 'git remote rm heroku'. Then run 'hu deploy' again to select a new remote."
|
121
122
|
puts
|
@@ -139,14 +140,14 @@ module Hu
|
|
139
140
|
highest_versionomy = Versionomy.parse('v0.0.0')
|
140
141
|
end
|
141
142
|
|
142
|
-
all_tags = Set.new(@git.references.to_a(
|
143
|
+
all_tags = Set.new(@git.references.to_a('refs/tags/*').collect { |o| o.name[10..-1] })
|
143
144
|
|
144
145
|
tiny_bump = highest_versionomy.dup
|
145
146
|
minor_bump = highest_versionomy.dup
|
146
147
|
major_bump = highest_versionomy.dup
|
147
148
|
|
148
149
|
loop do
|
149
|
-
tiny_bump
|
150
|
+
tiny_bump = tiny_bump.bump(:tiny)
|
150
151
|
break unless all_tags.include? tiny_bump.to_s
|
151
152
|
end
|
152
153
|
loop do
|
@@ -172,60 +173,57 @@ module Hu
|
|
172
173
|
git_revisions = show_pipeline_status(pipeline_name, stag_app_name, prod_app_name, release_tag, clearscreen)
|
173
174
|
clearscreen = true
|
174
175
|
|
175
|
-
changelog='Initial revision'
|
176
|
+
changelog = 'Initial revision'
|
176
177
|
release_branch_exists = branch_exists?("release/#{release_tag}")
|
177
178
|
|
178
179
|
if release_branch_exists
|
179
|
-
puts "\nThis release will be "+release_tag.color(:red).bright
|
180
|
+
puts "\nThis release will be " + release_tag.color(:red).bright
|
180
181
|
unless highest_version == 'v0.0.0'
|
181
182
|
env = {
|
182
183
|
'PREVIOUS_TAG' => highest_version,
|
183
184
|
'RELEASE_TAG' => release_tag
|
184
185
|
}
|
185
|
-
changelog=create_changelog(env) unless highest_version == 'v0.0.0'
|
186
|
+
changelog = create_changelog(env) unless highest_version == 'v0.0.0'
|
186
187
|
unless changelog.empty?
|
187
|
-
puts "\nChanges since "+highest_version.bright+
|
188
|
+
puts "\nChanges since " + highest_version.bright + ':'
|
188
189
|
puts changelog
|
189
190
|
end
|
190
191
|
end
|
191
|
-
puts
|
192
192
|
else
|
193
|
-
puts "\nThis is release "+release_tag.color(:green).bright
|
194
|
-
puts
|
193
|
+
puts "\nThis is release " + release_tag.color(:green).bright
|
195
194
|
end
|
195
|
+
puts
|
196
196
|
|
197
|
-
unless git_revisions[:release] == git_revisions[stag_app_name]
|
198
|
-
puts
|
199
|
-
puts
|
200
|
-
puts
|
197
|
+
unless git_revisions[:release] == git_revisions[stag_app_name] || !release_branch_exists
|
198
|
+
puts 'Phase 1/3: The local release branch ' + "release/#{release_tag}".bright + ' was created.'
|
199
|
+
puts ' Nothing else has happened so far. Push this branch to'
|
200
|
+
puts ' ' + stag_app_name.to_s.bright + ' to begin the deploy procedure.'
|
201
201
|
puts
|
202
202
|
end
|
203
203
|
|
204
204
|
if release_branch_exists && git_revisions[:release] == git_revisions[stag_app_name]
|
205
|
-
puts
|
206
|
-
puts
|
207
|
-
puts
|
208
|
-
puts
|
205
|
+
puts 'Phase 2/3: Your local ' + "release/#{release_tag}".bright + ' (formerly ' + 'develop'.bright + ") is now live at #{stag_app_name}."
|
206
|
+
puts ' Please test thoroughly: ' + (app['web_url']).to_s.bright
|
207
|
+
puts ' If everything looks good, you may proceed and finish the release.'
|
208
|
+
puts ' If there are problems: Quit, delete the release branch and start fixing.'
|
209
209
|
puts
|
210
|
-
elsif git_revisions[prod_app_name] != git_revisions[stag_app_name]
|
211
|
-
puts
|
212
|
-
puts
|
210
|
+
elsif git_revisions[prod_app_name] != git_revisions[stag_app_name] && !release_branch_exists && git_revisions[:release] != git_revisions[stag_app_name]
|
211
|
+
puts 'Phase 3/3: HEADS UP. This is the last chance to detect problems.'
|
212
|
+
puts ' The final version of ' + "release/#{release_tag}".bright + ' is now staged.'
|
213
213
|
puts
|
214
|
-
puts
|
214
|
+
puts ' Test here: ' + (app['web_url']).to_s.bright
|
215
215
|
sleep 1
|
216
216
|
puts
|
217
|
-
puts
|
217
|
+
puts ' This is the exact version that will be promoted to production.'
|
218
218
|
puts " From here you are on your own. Good luck #{`whoami`.chomp}!"
|
219
219
|
puts
|
220
220
|
end
|
221
221
|
|
222
|
-
|
223
|
-
|
224
|
-
choice = prompt.select("Choose your destiny") do |menu|
|
222
|
+
choice = prompt.select('Choose your destiny') do |menu|
|
225
223
|
menu.enum '.'
|
226
|
-
menu.choice
|
227
|
-
menu.choice
|
228
|
-
unless git_revisions[:release] == git_revisions[stag_app_name]
|
224
|
+
menu.choice 'Refresh', :refresh
|
225
|
+
menu.choice 'Quit', :abort_ask
|
226
|
+
unless git_revisions[:release] == git_revisions[stag_app_name] || !release_branch_exists
|
229
227
|
menu.choice "Push release/#{release_tag} to #{stag_app_name}", :push_to_staging
|
230
228
|
end
|
231
229
|
if release_branch_exists
|
@@ -242,7 +240,7 @@ module Hu
|
|
242
240
|
end
|
243
241
|
|
244
242
|
if git_revisions[:release] == git_revisions[stag_app_name]
|
245
|
-
menu.choice
|
243
|
+
menu.choice 'Finish release (merge, tag and final stage)', :finish_release
|
246
244
|
end
|
247
245
|
elsif git_revisions[prod_app_name] != git_revisions[stag_app_name]
|
248
246
|
menu.choice "DEPLOY (promote #{stag_app_name} to #{prod_app_name})", :DEPLOY
|
@@ -252,66 +250,66 @@ module Hu
|
|
252
250
|
puts
|
253
251
|
|
254
252
|
case choice
|
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
|
-
|
253
|
+
when :DEPLOY
|
254
|
+
promote_to_production
|
255
|
+
anykey
|
256
|
+
when :finish_release
|
257
|
+
old_editor = ENV['EDITOR']
|
258
|
+
tf = Tempfile.new('hu-tag')
|
259
|
+
tf.write "#{release_tag}\n#{changelog}"
|
260
|
+
tf.close
|
261
|
+
ENV['EDITOR'] = "cp #{tf.path}"
|
262
|
+
env = {
|
263
|
+
'PREVIOUS_TAG' => highest_version,
|
264
|
+
'RELEASE_TAG' => release_tag
|
265
|
+
}
|
266
|
+
unless 0 == finish_release(release_tag, env)
|
267
|
+
abort_merge
|
268
|
+
puts '*** ERROR! Could not finish release *** '.color(:red)
|
269
|
+
puts
|
270
|
+
puts 'This usually means a merge conflict or'
|
271
|
+
puts 'something similarly complicated has occured.'
|
272
|
+
puts
|
273
|
+
puts 'Please bring the universe into a state'
|
274
|
+
puts 'where the above command succeeds, then try again.'
|
275
|
+
puts
|
276
|
+
exit 1
|
277
|
+
end
|
278
|
+
ENV['EDITOR'] = old_editor
|
279
|
+
anykey
|
280
|
+
when :push_to_staging
|
281
|
+
run_each <<-EOS.strip_heredoc
|
284
282
|
:stream
|
285
283
|
git push #{push_url} release/#{release_tag}:master -f
|
286
284
|
EOS
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
285
|
+
anykey
|
286
|
+
when :abort_ask
|
287
|
+
puts if delete_branch("release/#{release_tag}")
|
288
|
+
exit 0
|
289
|
+
when :bump_tiny
|
290
|
+
if delete_branch("release/#{release_tag}")
|
291
|
+
release_tag, branch_already_exists = prompt_for_release_tag(tiny_bump, tiny_bump)
|
292
|
+
end
|
293
|
+
when :bump_minor
|
294
|
+
if delete_branch("release/#{release_tag}")
|
295
|
+
release_tag, branch_already_exists = prompt_for_release_tag(minor_bump, minor_bump)
|
296
|
+
end
|
297
|
+
when :bump_major
|
298
|
+
if delete_branch("release/#{release_tag}")
|
299
|
+
release_tag, branch_already_exists = prompt_for_release_tag(major_bump, major_bump)
|
300
|
+
end
|
303
301
|
end
|
304
302
|
end
|
305
303
|
end
|
306
304
|
|
307
|
-
def show_pipeline_status(pipeline_name, stag_app_name, prod_app_name, release_tag, clear=true)
|
308
|
-
table = TTY::Table.new header: %w
|
305
|
+
def show_pipeline_status(pipeline_name, stag_app_name, prod_app_name, release_tag, clear = true)
|
306
|
+
table = TTY::Table.new header: %w(location commit tag app_last_modified app_last_modified_by dynos# state)
|
309
307
|
busy 'loading', :classic
|
310
308
|
ts = []
|
311
309
|
tpl_row = ['?', '', '', '', '', '', '']
|
312
310
|
revs = ThreadSafe::Hash.new
|
313
311
|
|
314
|
-
[[0,stag_app_name],[1,prod_app_name]].each do |idx, app_name|
|
312
|
+
[[0, stag_app_name], [1, prod_app_name]].each do |idx, app_name|
|
315
313
|
ts << Thread.new do
|
316
314
|
table_row = tpl_row.dup
|
317
315
|
table_row[0] = app_name
|
@@ -325,7 +323,7 @@ module Hu
|
|
325
323
|
release_version = dynos.dig(0, 'release', 'version')
|
326
324
|
break if release_version.nil?
|
327
325
|
|
328
|
-
state = Set.new(dynos.collect{|d| d['state']}).sort.join(', ')
|
326
|
+
state = Set.new(dynos.collect { |d| d['state'] }).sort.join(', ')
|
329
327
|
state_color = (state == 'up') ? 32 : 31
|
330
328
|
table_row[6] = "\e[#{state_color};1m#{state}"
|
331
329
|
|
@@ -340,15 +338,15 @@ module Hu
|
|
340
338
|
revs[app_name] = table_row[1] = slug_info['commit'][0..5]
|
341
339
|
|
342
340
|
table_row[2] = `git tag --points-at #{slug_info['commit']} 2>/dev/null`
|
343
|
-
table_row[2] = '' if
|
341
|
+
table_row[2] = '' if $CHILD_STATUS != 0
|
344
342
|
|
345
343
|
# heroku uses wrong timezone offset in the slug api... /facepalm
|
346
|
-
#table_row[3] = ChronicDuration.output(Time.now.utc - Time.parse(slug_info['updated_at']), :units => 1)
|
344
|
+
# table_row[3] = ChronicDuration.output(Time.now.utc - Time.parse(slug_info['updated_at']), :units => 1)
|
347
345
|
|
348
346
|
delta = Time.now.utc - Time.parse(release_info['updated_at'])
|
349
|
-
table_row[3] = delta < 60 ? 'less than a minute' : ChronicDuration.output(delta, :
|
350
|
-
table_row[3] +=
|
351
|
-
#table_row[3] += "\n\e[30;1m" + slug_info['updated_at']
|
347
|
+
table_row[3] = delta < 60 ? 'less than a minute' : ChronicDuration.output(delta, units: 1)
|
348
|
+
table_row[3] += ' ago'
|
349
|
+
# table_row[3] += "\n\e[30;1m" + slug_info['updated_at']
|
352
350
|
|
353
351
|
table_row[4] = release_info['user']['email']
|
354
352
|
table_row[5] = dynos.length
|
@@ -386,21 +384,21 @@ module Hu
|
|
386
384
|
|
387
385
|
unbusy
|
388
386
|
|
389
|
-
rows.each do |
|
390
|
-
table <<
|
387
|
+
rows.each do |r|
|
388
|
+
table << r
|
391
389
|
end
|
392
390
|
|
393
391
|
puts "\e[H\e[2J" if clear
|
394
392
|
puts " PIPELINE #{pipeline_name} ".inverse
|
395
393
|
puts
|
396
394
|
|
397
|
-
puts table.render(:unicode, padding: [0,1,0,1], multiline: true)
|
395
|
+
puts table.render(:unicode, padding: [0, 1, 0, 1], multiline: true)
|
398
396
|
revs
|
399
397
|
end
|
400
398
|
|
401
399
|
def heroku_app_by_git(git_url)
|
402
400
|
busy('fetching heroku apps', :dots)
|
403
|
-
r = h.app.list.select{ |e| e['git_url'] == git_url }
|
401
|
+
r = h.app.list.select { |e| e['git_url'] == git_url }
|
404
402
|
unbusy
|
405
403
|
raise "FATAL: Found multiple heroku apps with git_url=#{git_url}" if r.length > 1
|
406
404
|
r[0]
|
@@ -410,16 +408,16 @@ module Hu
|
|
410
408
|
busy('fetching heroku pipelines', :dots)
|
411
409
|
couplings = h.pipeline_coupling.list
|
412
410
|
unbusy
|
413
|
-
r = couplings.select{ |e| e['app']['id'] == app['id'] }
|
411
|
+
r = couplings.select { |e| e['app']['id'] == app['id'] }
|
414
412
|
raise "FATAL: Found multiple heroku pipelines with app.id=#{r['id']}" if r.length > 1
|
415
413
|
raise "FATAL: Found no heroku pipeline for app.id=#{r['id']}" if r.length != 1
|
416
414
|
r = r[0]
|
417
415
|
pipeline_name = r['pipeline']['name']
|
418
416
|
|
419
|
-
r = couplings.select{ |e| e['pipeline']['id'] == r['pipeline']['id']
|
417
|
+
r = couplings.select { |e| e['pipeline']['id'] == r['pipeline']['id'] && e['stage'] == 'staging' }[0]
|
420
418
|
staging_app_id = r['app']['id']
|
421
419
|
|
422
|
-
r = couplings.select{ |e| e['pipeline']['id'] == r['pipeline']['id']
|
420
|
+
r = couplings.select { |e| e['pipeline']['id'] == r['pipeline']['id'] && e['stage'] == 'production' }[0]
|
423
421
|
raise "FATAL: No production app in pipeline #{pipeline_name}" if r.nil?
|
424
422
|
prod_app_id = r['app']['id']
|
425
423
|
[pipeline_name, staging_app_id, prod_app_id]
|
@@ -429,7 +427,7 @@ module Hu
|
|
429
427
|
@h ||= PlatformAPI.connect_oauth(Hu::API_TOKEN)
|
430
428
|
end
|
431
429
|
|
432
|
-
def run_each(script, opts={})
|
430
|
+
def run_each(script, opts = {})
|
433
431
|
opts = {
|
434
432
|
quiet: false,
|
435
433
|
failfast: true,
|
@@ -441,23 +439,23 @@ module Hu
|
|
441
439
|
script.lines.each_with_index do |line, i|
|
442
440
|
line.chomp!
|
443
441
|
case line[0]
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
442
|
+
when '#'
|
443
|
+
puts "\n" + line.bright unless opts[:quiet]
|
444
|
+
when ':'
|
445
|
+
opts[:quiet] = true if line == ':quiet'
|
446
|
+
opts[:failfast] = false if line == ':return'
|
447
|
+
opts[:spinner] = false if line == ':nospinner'
|
448
|
+
if line == ':stream'
|
449
|
+
opts[:stream] = true
|
450
|
+
opts[:quiet] = false
|
451
|
+
end
|
454
452
|
end
|
455
|
-
next if line.empty?
|
453
|
+
next if line.empty? || ['#', ':'].include?(line[0])
|
456
454
|
|
457
455
|
status = nil
|
458
456
|
if opts[:stream]
|
459
457
|
puts "\n> ".color(:green) + line.color(:black).bright
|
460
|
-
PTY.spawn(line) do |r,
|
458
|
+
PTY.spawn(line) do |r, _w, pid|
|
461
459
|
@tspin ||= Thread.new do
|
462
460
|
@minispin_last_char = Time.now
|
463
461
|
i = 0
|
@@ -467,48 +465,47 @@ module Hu
|
|
467
465
|
sleep 0.1
|
468
466
|
next
|
469
467
|
end
|
470
|
-
@spinlock.synchronize
|
468
|
+
@spinlock.synchronize do
|
471
469
|
print "\e[?25l"
|
472
|
-
print Paint[' ', '#000', Lol.rainbow(1, i/3.0)]
|
470
|
+
print Paint[' ', '#000', Lol.rainbow(1, i / 3.0)]
|
473
471
|
sleep 0.12
|
474
472
|
print 8.chr
|
475
473
|
print ' '
|
476
474
|
print 8.chr
|
477
475
|
i += 1
|
478
476
|
print "\e[?25h"
|
479
|
-
|
477
|
+
end
|
480
478
|
end
|
481
479
|
end
|
482
480
|
|
483
|
-
|
481
|
+
until r.eof?
|
484
482
|
c = r.getc
|
485
|
-
@spinlock.synchronize
|
483
|
+
@spinlock.synchronize do
|
486
484
|
print c
|
487
485
|
@minispin_last_char = Time.now
|
488
|
-
|
486
|
+
end
|
489
487
|
end
|
490
|
-
|
488
|
+
_pid, status = Process.wait2(pid)
|
491
489
|
@minispin_last_char = :end
|
492
490
|
@tspin.join
|
493
491
|
@tspin = nil
|
494
|
-
#status = PTY.check(pid)
|
492
|
+
# status = PTY.check(pid)
|
495
493
|
end
|
496
494
|
else
|
497
495
|
busy line if opts[:spinner]
|
498
496
|
output, status = Open3.capture2e(line)
|
499
497
|
unbusy if opts[:spinner]
|
500
498
|
color = (status.exitstatus == 0) ? :green : :red
|
501
|
-
if status.exitstatus != 0
|
499
|
+
if status.exitstatus != 0 || !opts[:quiet]
|
502
500
|
puts "\n> ".color(color) + line.color(:black).bright
|
503
501
|
puts output
|
504
502
|
end
|
505
503
|
end
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
end
|
504
|
+
next unless status.exitstatus != 0
|
505
|
+
shutdown if opts[:failfast]
|
506
|
+
puts "Error, exit #{status.exitstatus}: #{line} (L#{i})".color(:red).bright
|
507
|
+
exit status.exitstatus if opts[:failfast]
|
508
|
+
return status.exitstatus
|
512
509
|
end
|
513
510
|
0
|
514
511
|
end
|
@@ -516,13 +513,13 @@ module Hu
|
|
516
513
|
def find_highest_version_tag
|
517
514
|
output, status = Open3.capture2e('git tag')
|
518
515
|
if status.exitstatus != 0
|
519
|
-
puts
|
516
|
+
puts 'Error fetching git tags.'
|
520
517
|
exit status.exitstatus
|
521
518
|
end
|
522
519
|
|
523
|
-
versions = VersionSorter.sort(output.lines.map(&:chomp).map {|e| e[0].
|
520
|
+
versions = VersionSorter.sort(output.lines.map(&:chomp).map { |e| e[0].casecmp('v').zero? ? e.downcase : "v#{e.downcase}" })
|
524
521
|
latest = versions[-1] || 'v0.0.0'
|
525
|
-
latest = "v#{latest}" unless latest[0] ==
|
522
|
+
latest = "v#{latest}" unless latest[0] == 'v'
|
526
523
|
latest
|
527
524
|
end
|
528
525
|
|
@@ -569,7 +566,7 @@ module Hu
|
|
569
566
|
EOS
|
570
567
|
end
|
571
568
|
|
572
|
-
def
|
569
|
+
def heroku_git_remote
|
573
570
|
ensure_repo_has_heroku_remote
|
574
571
|
`git remote show -n heroku | grep Push`.chomp.split(':', 2)[1][1..-1]
|
575
572
|
end
|
@@ -586,23 +583,23 @@ module Hu
|
|
586
583
|
# Setup git remote
|
587
584
|
puts
|
588
585
|
puts "This repository has no 'heroku' remote.".color(:red)
|
589
|
-
puts
|
586
|
+
puts 'We will set one up now. Please select the pipeline that you'
|
590
587
|
puts "wish to deploy to, and we will set the 'heroku' remote"
|
591
|
-
puts
|
588
|
+
puts 'to the staging application in that pipeline.'
|
592
589
|
puts
|
593
590
|
|
594
591
|
busy
|
595
|
-
heroku_apps=JSON.parse(`heroku pipelines:list --json`)
|
592
|
+
heroku_apps = JSON.parse(`heroku pipelines:list --json`)
|
596
593
|
unbusy
|
597
594
|
|
598
595
|
prompt = TTY::Prompt.new
|
599
|
-
pipeline_name = prompt.select(
|
596
|
+
pipeline_name = prompt.select('Select pipeline:') do |menu|
|
600
597
|
menu.enum '.'
|
601
598
|
heroku_apps.each do |app|
|
602
599
|
menu.choice app['name']
|
603
600
|
end
|
604
601
|
end
|
605
|
-
staging_app=JSON.parse(`heroku pipelines:info #{pipeline_name} --json`)['apps'].select{|e| e['coupling']['stage'] == 'staging'}[0]
|
602
|
+
staging_app = JSON.parse(`heroku pipelines:info #{pipeline_name} --json`)['apps'].select { |e| e['coupling']['stage'] == 'staging' }[0]
|
606
603
|
if staging_app.nil?
|
607
604
|
puts "Error: Pipeline #{pipeline_name} has no staging app.".color(:red)
|
608
605
|
exit 1
|
@@ -614,7 +611,7 @@ module Hu
|
|
614
611
|
EOS
|
615
612
|
end
|
616
613
|
|
617
|
-
def prompt_for_release_tag(propose_version='v0.0.1', try_version=nil, keep_existing=false)
|
614
|
+
def prompt_for_release_tag(propose_version = 'v0.0.1', try_version = nil, keep_existing = false)
|
618
615
|
prompt = TTY::Prompt.new
|
619
616
|
loop do
|
620
617
|
if try_version
|
@@ -622,14 +619,12 @@ module Hu
|
|
622
619
|
try_version = nil
|
623
620
|
else
|
624
621
|
show_existing_git_tags
|
625
|
-
release_tag = prompt.ask(
|
622
|
+
release_tag = prompt.ask('Please enter a tag for this release', default: propose_version)
|
626
623
|
begin
|
627
624
|
unless release_tag[0] == 'v'
|
628
|
-
raise ArgumentError,
|
629
|
-
end
|
630
|
-
if release_tag.length < 5
|
631
|
-
raise ArgumentError, "too short"
|
625
|
+
raise ArgumentError, 'Version string must start with the letter v'
|
632
626
|
end
|
627
|
+
raise ArgumentError, 'too short' if release_tag.length < 5
|
633
628
|
Versionomy.parse(release_tag)
|
634
629
|
rescue => e
|
635
630
|
puts "Error: Tag does not look like a semantic version (#{e})".color(:red)
|
@@ -638,32 +633,32 @@ module Hu
|
|
638
633
|
end
|
639
634
|
|
640
635
|
branches = `git for-each-ref refs/heads/ --format='%(refname:short)'`.lines.map(&:chomp)
|
641
|
-
existing_branch = branches.find {|
|
636
|
+
existing_branch = branches.find { |b| b.start_with? 'release/' }
|
642
637
|
branch_already_exists = !existing_branch.nil?
|
643
638
|
release_tag = existing_branch[8..-1] if keep_existing && branch_already_exists
|
644
639
|
|
645
640
|
if branch_already_exists && !keep_existing
|
646
|
-
choice = prompt.expand("The branch '"+"release/#{release_tag}".color(:red)+"' already exists. What shall we do?",
|
647
|
-
|
641
|
+
choice = prompt.expand("The branch '" + "release/#{release_tag}".color(:red) + "' already exists. What shall we do?",
|
642
|
+
default: 0) do |q|
|
648
643
|
q.choice key: 'k', name: 'Keep, continue with the existing branch', value: :keep
|
649
644
|
q.choice key: 'D', name: "Delete branch release/#{release_tag} and retry", value: :delete
|
650
645
|
q.choice key: 'q', name: 'Quit', value: :quit
|
651
646
|
end
|
652
647
|
|
653
648
|
case choice
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
649
|
+
when :quit
|
650
|
+
puts
|
651
|
+
exit 0
|
652
|
+
when :delete
|
653
|
+
delete_branch("release/#{release_tag}")
|
654
|
+
next
|
660
655
|
end
|
661
656
|
end
|
662
657
|
|
663
658
|
if branch_already_exists
|
664
659
|
checkout_branch("release/#{release_tag}")
|
665
660
|
else
|
666
|
-
develop_tag
|
661
|
+
develop_tag = `git tag --points-at develop 2>/dev/null`.lines.find { |tag| tag[0] == 'v' }&.chomp
|
667
662
|
if develop_tag
|
668
663
|
release_tag = develop_tag
|
669
664
|
else
|
@@ -697,7 +692,7 @@ module Hu
|
|
697
692
|
end
|
698
693
|
|
699
694
|
def finish_release(release_tag, env)
|
700
|
-
env.each { |k,v| ENV[k] = v }
|
695
|
+
env.each { |k, v| ENV[k] = v }
|
701
696
|
if File.executable? '.hu/hooks/pre_release'
|
702
697
|
run_each <<-EOS.strip_heredoc
|
703
698
|
# Run pre-release hook
|
@@ -733,7 +728,7 @@ module Hu
|
|
733
728
|
|
734
729
|
def create_changelog(env)
|
735
730
|
if File.executable? '.hu/hooks/changelog'
|
736
|
-
env.each { |k,v| ENV[k] = v }
|
731
|
+
env.each { |k, v| ENV[k] = v }
|
737
732
|
`.hu/hooks/changelog`
|
738
733
|
else
|
739
734
|
`git log --pretty=format:" - %s" #{env['PREVIOUS_TAG']}..HEAD 2>/dev/null`
|
@@ -745,10 +740,10 @@ module Hu
|
|
745
740
|
unbusy
|
746
741
|
end
|
747
742
|
|
748
|
-
def busy(msg='', format
|
743
|
+
def busy(msg = '', format = :classic, clear = true)
|
749
744
|
return if @@shutting_down
|
750
745
|
format ||= TTY::Formats::FORMATS.keys.sample
|
751
|
-
options = {format: format, hide_cursor: true, error_mark: "\e[31;1m✖\e[0m", success_mark: "\e[32;1m✔\e[0m", clear: clear}
|
746
|
+
options = { format: format, hide_cursor: true, error_mark: "\e[31;1m✖\e[0m", success_mark: "\e[32;1m✔\e[0m", clear: clear }
|
752
747
|
@@spinner = TTY::Spinner.new("\e[0;1m#{msg}#{msg.empty? ? '' : ' '}\e[0m\e[32;1m:spinner\e[0m", options)
|
753
748
|
@@spinner.start
|
754
749
|
end
|
@@ -758,15 +753,15 @@ module Hu
|
|
758
753
|
printf "\e[?25h"
|
759
754
|
end
|
760
755
|
|
761
|
-
def with_spinner(msg='', format
|
756
|
+
def with_spinner(msg = '', format = :classic)
|
762
757
|
busy(msg, format)
|
763
|
-
|
758
|
+
yield
|
764
759
|
unbusy
|
765
760
|
end
|
766
761
|
|
767
762
|
def anykey
|
768
763
|
puts TTY::Cursor.hide
|
769
|
-
print
|
764
|
+
print '--- Press any key ---'.color(:cyan)
|
770
765
|
STDIN.getch
|
771
766
|
print TTY::Cursor.clear_line + TTY::Cursor.show
|
772
767
|
end
|
@@ -774,7 +769,7 @@ module Hu
|
|
774
769
|
def dp(label, *args)
|
775
770
|
return unless ENV['DEBUG']
|
776
771
|
puts "--- DEBUG #{label} ---"
|
777
|
-
ap
|
772
|
+
ap(*args)
|
778
773
|
puts "--- ^#{label}^ ---"
|
779
774
|
end
|
780
775
|
|
@@ -800,4 +795,3 @@ module Hu
|
|
800
795
|
end
|
801
796
|
end
|
802
797
|
end
|
803
|
-
|
data/lib/hu/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- moe
|
@@ -271,6 +271,8 @@ extensions: []
|
|
271
271
|
extra_rdoc_files: []
|
272
272
|
files:
|
273
273
|
- ".gitignore"
|
274
|
+
- ".rubocop.yml"
|
275
|
+
- ".rubocop_todo.yml"
|
274
276
|
- Gemfile
|
275
277
|
- LICENSE.txt
|
276
278
|
- README.md
|