envirobly 0.7.2 → 1.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/lib/envirobly/access_token.rb +67 -16
- data/lib/envirobly/api.rb +52 -14
- data/lib/envirobly/aws/credentials.rb +5 -0
- data/lib/envirobly/aws/s3.rb +221 -0
- data/lib/envirobly/cli/main.rb +86 -34
- data/lib/envirobly/colorize.rb +54 -0
- data/lib/envirobly/config.rb +19 -189
- data/lib/envirobly/default.rb +47 -0
- data/lib/envirobly/defaults/account.rb +46 -0
- data/lib/envirobly/defaults/project.rb +4 -0
- data/lib/envirobly/defaults/region.rb +45 -0
- data/lib/envirobly/defaults.rb +2 -0
- data/lib/envirobly/deployment.rb +82 -33
- data/lib/envirobly/duration.rb +36 -0
- data/lib/envirobly/git/commit.rb +37 -18
- data/lib/envirobly/git.rb +16 -1
- data/lib/envirobly/version.rb +1 -1
- data/lib/envirobly.rb +2 -2
- metadata +68 -37
- data/lib/core_ext.rb +0 -113
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ef94e7e6db204bc73126d41dae8d3417e4ea7ac35e7cbecd5d496dcee842ee3
|
4
|
+
data.tar.gz: 32f53215e81d8e3d984cd7caf58fc0944c9636ba587e59dbd4573bfcd04f8a08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef5d512c3dab84e7f4cc5ce45804d30cce526b61c83407895952536a45a901715aee2ceb39b71e5b8d68d8e477f20b4628f9f94df41a932f603a511e3d9fface
|
7
|
+
data.tar.gz: d5444e2eb995be568dcc57e7df33145d2b17792f212c9ed605a3c456d511f0e2b80968d4900642c3483d9eb01afb550e1b0071f212877706a8e7a9e265d87a9e
|
@@ -2,35 +2,86 @@ require "fileutils"
|
|
2
2
|
require "pathname"
|
3
3
|
|
4
4
|
class Envirobly::AccessToken
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
include Envirobly::Colorize
|
6
|
+
|
7
|
+
attr_reader :shell
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def destroy
|
11
|
+
if File.exist?(path)
|
12
|
+
FileUtils.rm path
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def dir
|
17
|
+
if ENV["XDG_CONFIG_HOME"]
|
18
|
+
Pathname.new(ENV["XDG_CONFIG_HOME"]).join("envirobly")
|
19
|
+
else
|
20
|
+
Pathname.new(Dir.home).join(".envirobly")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def path
|
25
|
+
dir.join "access_token"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(token = ENV["ENVIROBLY_ACCESS_TOKEN"].presence, shell: nil)
|
30
|
+
@shell = shell
|
31
|
+
|
32
|
+
if token.blank? && File.exist?(self.class.path)
|
33
|
+
@token = File.read(self.class.path)
|
8
34
|
else
|
9
35
|
@token = token
|
10
36
|
end
|
11
37
|
end
|
12
38
|
|
13
39
|
def save
|
14
|
-
FileUtils.mkdir_p
|
15
|
-
File.write
|
16
|
-
File.chmod 0600,
|
17
|
-
puts "Access token saved to #{access_token_path}"
|
40
|
+
FileUtils.mkdir_p self.class.dir
|
41
|
+
File.write self.class.path, @token
|
42
|
+
File.chmod 0600, self.class.path
|
18
43
|
end
|
19
44
|
|
20
45
|
def as_http_bearer
|
21
46
|
"Bearer #{@token}"
|
22
47
|
end
|
23
48
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
49
|
+
def require!
|
50
|
+
return if @token.present?
|
51
|
+
|
52
|
+
shell.say "This action requires you to be signed in."
|
53
|
+
shell.say "Please visit https://on.envirobly.com/profile/access_tokens"
|
54
|
+
shell.say "to generate an access token and then paste it in here."
|
55
|
+
shell.say
|
56
|
+
|
57
|
+
set
|
58
|
+
end
|
59
|
+
|
60
|
+
def set
|
61
|
+
@token = nil
|
62
|
+
|
63
|
+
while @token.blank?
|
64
|
+
begin
|
65
|
+
@token = shell.ask("Access Token:", echo: false)
|
66
|
+
rescue Interrupt
|
67
|
+
shell.say
|
68
|
+
shell.say_error "Cancelled"
|
69
|
+
exit
|
30
70
|
end
|
31
|
-
end
|
32
71
|
|
33
|
-
|
34
|
-
|
72
|
+
api = Envirobly::Api.new(access_token: self)
|
73
|
+
|
74
|
+
# TODO: Eventually replace with custom `whoami` API that returns name, email...
|
75
|
+
if api.list_accounts.success?
|
76
|
+
save
|
77
|
+
shell.say
|
78
|
+
shell.say "Successfully signed in "
|
79
|
+
shell.say green_check
|
80
|
+
else
|
81
|
+
shell.say
|
82
|
+
shell.say_error "This token is invalid. Please try again"
|
83
|
+
@token = nil
|
84
|
+
end
|
35
85
|
end
|
86
|
+
end
|
36
87
|
end
|
data/lib/envirobly/api.rb
CHANGED
@@ -4,36 +4,54 @@ require "socket"
|
|
4
4
|
require "uri"
|
5
5
|
|
6
6
|
class Envirobly::Api
|
7
|
-
HOST = ENV["ENVIROBLY_API_HOST"] || "envirobly.com"
|
7
|
+
HOST = ENV["ENVIROBLY_API_HOST"].presence || "on.envirobly.com"
|
8
8
|
USER_AGENT = "Envirobly CLI v#{Envirobly::VERSION}"
|
9
9
|
CONTENT_TYPE = "application/json"
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@access_token =
|
11
|
+
def initialize(access_token: Envirobly::AccessToken.new)
|
12
|
+
@access_token = access_token
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
post_as_json(
|
17
|
-
unless response.
|
18
|
-
$stderr.puts "
|
15
|
+
def validate_shape(params)
|
16
|
+
post_as_json(api_v1_shape_validations_url, params:, headers: authorization_headers).tap do |response|
|
17
|
+
unless response.success?
|
18
|
+
$stderr.puts "Validation request responded with #{response.code}. Aborting."
|
19
19
|
exit 1
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
def create_deployment(params)
|
25
|
+
post_as_json(api_v1_deployments_url, params:, headers: authorization_headers)
|
26
|
+
end
|
27
|
+
|
28
|
+
def list_accounts
|
29
|
+
get_as_json api_v1_accounts_url, headers: authorization_headers
|
30
|
+
end
|
31
|
+
|
32
|
+
def list_regions
|
33
|
+
get_as_json api_v1_regions_url, headers: authorization_headers
|
34
|
+
end
|
35
|
+
|
36
|
+
MAX_RETRIES = 30
|
37
|
+
SHORT_RETRY_INTERVAL = 2.seconds
|
38
|
+
LONG_RETRY_INTERVAL = 6.seconds
|
26
39
|
def get_deployment_with_delay_and_retry(url, tries = 1)
|
27
|
-
sleep
|
40
|
+
sleep SHORT_RETRY_INTERVAL * tries
|
28
41
|
response = get_as_json URI(url)
|
29
42
|
|
30
|
-
if response.
|
43
|
+
if response.success?
|
31
44
|
response
|
32
45
|
elsif MAX_RETRIES <= tries
|
33
46
|
$stderr.puts "Max retries exhausted while waiting for deployment credentials. Aborting."
|
34
47
|
exit 1
|
35
48
|
else
|
36
|
-
|
49
|
+
if tries > 3
|
50
|
+
sleep LONG_RETRY_INTERVAL
|
51
|
+
else
|
52
|
+
sleep SHORT_RETRY_INTERVAL
|
53
|
+
end
|
54
|
+
|
37
55
|
get_deployment_with_delay_and_retry(url, tries + 1)
|
38
56
|
end
|
39
57
|
end
|
@@ -55,8 +73,24 @@ class Envirobly::Api
|
|
55
73
|
end
|
56
74
|
|
57
75
|
private
|
76
|
+
def api_v1_shape_validations_url
|
77
|
+
api_url_for "v1/shape_validations"
|
78
|
+
end
|
79
|
+
|
58
80
|
def api_v1_deployments_url
|
59
|
-
|
81
|
+
api_url_for "v1/deployments"
|
82
|
+
end
|
83
|
+
|
84
|
+
def api_v1_accounts_url
|
85
|
+
api_url_for "v1/accounts"
|
86
|
+
end
|
87
|
+
|
88
|
+
def api_v1_regions_url
|
89
|
+
api_url_for "v1/regions"
|
90
|
+
end
|
91
|
+
|
92
|
+
def api_url_for(path)
|
93
|
+
URI::HTTPS.build(host: HOST, path: "/api/#{path}")
|
60
94
|
end
|
61
95
|
|
62
96
|
def request(url, type:, headers: {})
|
@@ -74,7 +108,11 @@ class Envirobly::Api
|
|
74
108
|
|
75
109
|
http.request(request).tap do |response|
|
76
110
|
def response.object
|
77
|
-
@json_parsed_body ||= JSON.parse
|
111
|
+
@json_parsed_body ||= JSON.parse(body)
|
112
|
+
end
|
113
|
+
|
114
|
+
def response.success?
|
115
|
+
(200..299).include?(code.to_i)
|
78
116
|
end
|
79
117
|
end
|
80
118
|
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require "zlib"
|
2
|
+
require "json"
|
3
|
+
require "open3"
|
4
|
+
require "concurrent"
|
5
|
+
require "aws-sdk-s3"
|
6
|
+
|
7
|
+
class Envirobly::Aws::S3
|
8
|
+
OBJECTS_PREFIX = "blobs"
|
9
|
+
MANIFESTS_PREFIX = "manifests"
|
10
|
+
CONCURRENCY = 6
|
11
|
+
|
12
|
+
def initialize(bucket:, region:, credentials: nil)
|
13
|
+
@region = region
|
14
|
+
@bucket = bucket
|
15
|
+
|
16
|
+
client_options = { region: }
|
17
|
+
unless credentials.nil?
|
18
|
+
client_options.merge! credentials.transform_keys(&:to_sym)
|
19
|
+
end
|
20
|
+
|
21
|
+
@client = Aws::S3::Client.new(client_options)
|
22
|
+
resource = Aws::S3::Resource.new(client: @client)
|
23
|
+
@bucket_resource = resource.bucket(@bucket)
|
24
|
+
end
|
25
|
+
|
26
|
+
def push(commit)
|
27
|
+
if object_exists?(manifest_key(commit.object_tree_checksum))
|
28
|
+
print "Build context is already uploaded"
|
29
|
+
$stdout.flush
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
# puts "Pushing #{commit.object_tree_checksum} to #{@bucket}"
|
34
|
+
|
35
|
+
manifest = []
|
36
|
+
objects_count = 0
|
37
|
+
objects_to_upload = []
|
38
|
+
remote_object_hashes = list_object_hashes
|
39
|
+
|
40
|
+
commit.object_tree.each do |chdir, objects|
|
41
|
+
objects.each do |(mode, type, object_hash, path)|
|
42
|
+
objects_count += 1
|
43
|
+
path = File.join chdir.delete_prefix(commit.working_dir), path
|
44
|
+
manifest << [ mode, type, object_hash, path.delete_prefix("/") ]
|
45
|
+
|
46
|
+
next if remote_object_hashes.include?(object_hash)
|
47
|
+
objects_to_upload << [ chdir, object_hash ]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
upload_git_objects(objects_to_upload)
|
52
|
+
upload_manifest manifest_key(commit.object_tree_checksum), manifest
|
53
|
+
end
|
54
|
+
|
55
|
+
def pull(object_tree_checksum, target_dir)
|
56
|
+
puts "Pulling #{object_tree_checksum} into #{target_dir}"
|
57
|
+
|
58
|
+
manifest = fetch_manifest(object_tree_checksum)
|
59
|
+
FileUtils.mkdir_p(target_dir)
|
60
|
+
|
61
|
+
puts "Downloading #{manifest.size} files"
|
62
|
+
pool = Concurrent::FixedThreadPool.new(CONCURRENCY)
|
63
|
+
|
64
|
+
manifest.each do |(mode, type, object_hash, path)|
|
65
|
+
pool.post do
|
66
|
+
target_path = File.join target_dir, path
|
67
|
+
|
68
|
+
if mode == Envirobly::Git::Commit::SYMLINK_FILE_MODE
|
69
|
+
fetch_symlink(object_hash, target_path:)
|
70
|
+
else
|
71
|
+
fetch_object(object_hash, target_path:)
|
72
|
+
|
73
|
+
if mode == Envirobly::Git::Commit::EXECUTABLE_FILE_MODE
|
74
|
+
FileUtils.chmod("+x", target_path)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
pool.shutdown
|
81
|
+
pool.wait_for_termination
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def list_object_hashes
|
86
|
+
@client.list_objects({
|
87
|
+
bucket: @bucket,
|
88
|
+
prefix: "#{OBJECTS_PREFIX}/"
|
89
|
+
}).map do |response|
|
90
|
+
response.contents.map do |object|
|
91
|
+
object.key.delete_prefix("#{OBJECTS_PREFIX}/").delete_suffix(".gz")
|
92
|
+
end
|
93
|
+
end.flatten
|
94
|
+
end
|
95
|
+
|
96
|
+
def object_key(object_hash)
|
97
|
+
"#{OBJECTS_PREFIX}/#{object_hash}.gz"
|
98
|
+
end
|
99
|
+
|
100
|
+
def manifest_key(object_tree_checksum)
|
101
|
+
"#{MANIFESTS_PREFIX}/#{object_tree_checksum}.gz"
|
102
|
+
end
|
103
|
+
|
104
|
+
def object_exists?(key)
|
105
|
+
@client.head_object(bucket: @bucket, key:)
|
106
|
+
true
|
107
|
+
rescue Aws::S3::Errors::NotFound
|
108
|
+
false
|
109
|
+
end
|
110
|
+
|
111
|
+
def compress_and_upload_object(object_hash, chdir:)
|
112
|
+
key = object_key object_hash
|
113
|
+
|
114
|
+
Tempfile.create([ "envirobly-push", ".gz" ]) do |tempfile|
|
115
|
+
gz = Zlib::GzipWriter.new(tempfile)
|
116
|
+
|
117
|
+
Open3.popen3("git", "cat-file", "-p", object_hash, chdir:) do |_, stdout, stderr, thread|
|
118
|
+
IO.copy_stream(stdout, gz)
|
119
|
+
|
120
|
+
unless thread.value.success?
|
121
|
+
raise "`git cat-file -p #{object_hash}` failed: #{stderr.read}"
|
122
|
+
end
|
123
|
+
ensure
|
124
|
+
gz.close
|
125
|
+
end
|
126
|
+
|
127
|
+
@client.put_object(bucket: @bucket, body: tempfile, key:)
|
128
|
+
|
129
|
+
# puts "⤴ #{key}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def upload_git_objects(objects)
|
134
|
+
pool = Concurrent::FixedThreadPool.new(CONCURRENCY)
|
135
|
+
uploaded = Concurrent::AtomicFixnum.new
|
136
|
+
objects_count = objects.count
|
137
|
+
|
138
|
+
notifier = Thread.new do
|
139
|
+
next unless objects_count > 0
|
140
|
+
|
141
|
+
# Hide cursor
|
142
|
+
# print "\e[?25l"
|
143
|
+
# $stdout.flush
|
144
|
+
|
145
|
+
loop do
|
146
|
+
value = uploaded.value
|
147
|
+
print "\rUploading build context files: #{value}/#{objects_count}"
|
148
|
+
$stdout.flush
|
149
|
+
sleep 0.5
|
150
|
+
break if value >= objects_count
|
151
|
+
end
|
152
|
+
|
153
|
+
# Show cursor again
|
154
|
+
# print "\e[?25h\n"
|
155
|
+
end
|
156
|
+
|
157
|
+
objects.each do |(chdir, object_hash)|
|
158
|
+
pool.post do
|
159
|
+
compress_and_upload_object(object_hash, chdir:)
|
160
|
+
uploaded.increment
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
pool.shutdown
|
165
|
+
pool.wait_for_termination
|
166
|
+
notifier.join
|
167
|
+
end
|
168
|
+
|
169
|
+
def upload_manifest(key, content)
|
170
|
+
Tempfile.create([ "envirobly-push", ".gz" ]) do |tempfile|
|
171
|
+
gz = Zlib::GzipWriter.new(tempfile)
|
172
|
+
gz.write JSON.dump(content)
|
173
|
+
gz.close
|
174
|
+
|
175
|
+
@client.put_object(bucket: @bucket, body: tempfile, key:)
|
176
|
+
|
177
|
+
# puts "⤴ #{key}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def fetch_manifest(ref)
|
182
|
+
stream = @bucket_resource.object(manifest_key(ref)).get.body
|
183
|
+
JSON.parse Zlib::GzipReader.new(stream).read
|
184
|
+
rescue Aws::S3::Errors::NoSuchKey
|
185
|
+
puts "Commit #{ref} doesn't exist at s3://#{@bucket}"
|
186
|
+
exit 1
|
187
|
+
end
|
188
|
+
|
189
|
+
def fetch_object(object_hash, target_path:)
|
190
|
+
FileUtils.mkdir_p File.dirname(target_path)
|
191
|
+
|
192
|
+
key = object_key object_hash
|
193
|
+
stream = @bucket_resource.object(key).get.body
|
194
|
+
|
195
|
+
File.open(target_path, "wb") do |target|
|
196
|
+
gz = Zlib::GzipReader.new(stream)
|
197
|
+
IO.copy_stream(gz, target)
|
198
|
+
gz.close
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def fetch_symlink(object_hash, target_path:)
|
203
|
+
FileUtils.mkdir_p File.dirname(target_path)
|
204
|
+
|
205
|
+
key = object_key object_hash
|
206
|
+
gz = Zlib::GzipReader.new @bucket_resource.object(key).get.body
|
207
|
+
symlink_to = gz.read
|
208
|
+
gz.close
|
209
|
+
|
210
|
+
FileUtils.ln_s symlink_to, target_path
|
211
|
+
end
|
212
|
+
|
213
|
+
def format_duration(duration)
|
214
|
+
total_seconds = duration.to_i
|
215
|
+
minutes = (total_seconds / 60).floor
|
216
|
+
seconds = (total_seconds % 60).ceil
|
217
|
+
result = [ "#{seconds}s" ]
|
218
|
+
result.prepend "#{minutes}m" if minutes > 0
|
219
|
+
result.join " "
|
220
|
+
end
|
221
|
+
end
|
data/lib/envirobly/cli/main.rb
CHANGED
@@ -1,55 +1,107 @@
|
|
1
1
|
class Envirobly::Cli::Main < Envirobly::Base
|
2
|
+
include Envirobly::Colorize
|
3
|
+
|
2
4
|
desc "version", "Show Envirobly CLI version"
|
5
|
+
method_option :pure, type: :boolean, default: false
|
3
6
|
def version
|
4
|
-
|
7
|
+
if options.pure
|
8
|
+
puts Envirobly::VERSION
|
9
|
+
else
|
10
|
+
puts "envirobly CLI v#{Envirobly::VERSION}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "signin", "Set access token generated at Envirobly"
|
15
|
+
def signin
|
16
|
+
access_token = Envirobly::AccessToken.new(shell:)
|
17
|
+
access_token.set
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "signout", "Sign out"
|
21
|
+
def signout
|
22
|
+
Envirobly::AccessToken.destroy
|
23
|
+
say "You've signed out."
|
24
|
+
say "This didn't delete the access token itself."
|
25
|
+
say "You can sign in again with `envirobly signin`."
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "set_default_account", "Choose default account to deploy the current project to"
|
29
|
+
def set_default_account
|
30
|
+
Envirobly::Defaults::Account.new(shell:).require_id
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "set_default_region", "Set default region for the current project when deploying for the first time"
|
34
|
+
def set_default_region
|
35
|
+
Envirobly::Defaults::Region.new(shell:).require_id
|
5
36
|
end
|
6
37
|
|
7
38
|
desc "validate", "Validates config"
|
8
39
|
def validate
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
puts
|
20
|
-
exit 1
|
40
|
+
Envirobly::AccessToken.new(shell:).require!
|
41
|
+
|
42
|
+
configs = Envirobly::Config.new
|
43
|
+
api = Envirobly::Api.new
|
44
|
+
|
45
|
+
params = { validation: configs.to_params }
|
46
|
+
response = api.validate_shape params
|
47
|
+
|
48
|
+
if response.object.fetch("valid")
|
49
|
+
puts "Config is valid #{green_check}"
|
21
50
|
else
|
22
|
-
|
51
|
+
display_config_errors response.object.fetch("errors")
|
52
|
+
exit 1
|
23
53
|
end
|
24
54
|
end
|
25
55
|
|
26
|
-
desc "deploy
|
56
|
+
desc "deploy [ENVIRON_NAME]", <<~TXT
|
57
|
+
Deploy to environ identified by name.
|
58
|
+
Name can contain letters, numbers, dashes or underscores.
|
59
|
+
If environ name is left blank, current git branch name is used.
|
60
|
+
TXT
|
61
|
+
method_option :account_id, type: :numeric
|
62
|
+
method_option :region, type: :string
|
63
|
+
method_option :project, type: :string
|
27
64
|
method_option :commit, type: :string, default: "HEAD"
|
28
65
|
method_option :dry_run, type: :boolean, default: false
|
29
|
-
def deploy(
|
30
|
-
|
31
|
-
Envirobly::Deployment.new environment, options
|
32
|
-
end
|
33
|
-
|
34
|
-
desc "set_access_token TOKEN", "Save and use an access token generated at Envirobly"
|
35
|
-
def set_access_token
|
36
|
-
token = ask("Access Token:", echo: false).strip
|
66
|
+
def deploy(environ_name = nil)
|
67
|
+
commit = Envirobly::Git::Commit.new options.commit
|
37
68
|
|
38
|
-
|
39
|
-
|
40
|
-
$stderr.puts "Token can't be empty."
|
69
|
+
unless commit.exists?
|
70
|
+
say_error "Commit '#{commit.ref}' doesn't exist in this repository. Aborting."
|
41
71
|
exit 1
|
42
72
|
end
|
43
73
|
|
44
|
-
Envirobly::AccessToken.new(
|
45
|
-
end
|
74
|
+
Envirobly::AccessToken.new(shell:).require!
|
46
75
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
76
|
+
environ_name = environ_name.presence || commit.current_branch
|
77
|
+
project_name = nil
|
78
|
+
project_id = nil
|
79
|
+
|
80
|
+
if options.project.present?
|
81
|
+
if options.project =~ Envirobly::Defaults::Project.regexp
|
82
|
+
project_id = $1.to_i
|
83
|
+
else
|
84
|
+
project_name = options.project
|
53
85
|
end
|
54
86
|
end
|
87
|
+
|
88
|
+
deployment = Envirobly::Deployment.new(
|
89
|
+
account_id: options.account_id,
|
90
|
+
region: options.region,
|
91
|
+
project_name:,
|
92
|
+
environ_name:,
|
93
|
+
project_id:,
|
94
|
+
commit:,
|
95
|
+
shell:
|
96
|
+
)
|
97
|
+
deployment.perform(dry_run: options.dry_run)
|
98
|
+
end
|
99
|
+
|
100
|
+
desc "pull", "Download build context"
|
101
|
+
def pull(region, bucket, ref, path)
|
102
|
+
Envirobly::Duration.measure("Build context download took %s") do
|
103
|
+
s3 = Envirobly::Aws::S3.new(region:, bucket:)
|
104
|
+
s3.pull ref, path
|
105
|
+
end
|
106
|
+
end
|
55
107
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Envirobly::Colorize
|
2
|
+
GREEN = "\e[32m"
|
3
|
+
RED = "\e[31m"
|
4
|
+
YELLOW = "\e[33m"
|
5
|
+
BLUE = "\e[34m"
|
6
|
+
RESET = "\e[0m"
|
7
|
+
BOLD = "\e[1m"
|
8
|
+
FAINT = "\e[2m"
|
9
|
+
|
10
|
+
def faint(text)
|
11
|
+
[ FAINT, text, RESET ].join
|
12
|
+
end
|
13
|
+
|
14
|
+
def bold(text)
|
15
|
+
[ BOLD, text, RESET ].join
|
16
|
+
end
|
17
|
+
|
18
|
+
def green(text)
|
19
|
+
[ GREEN, text, RESET ].join
|
20
|
+
end
|
21
|
+
|
22
|
+
def yellow(text)
|
23
|
+
[ YELLOW, text, RESET ].join
|
24
|
+
end
|
25
|
+
|
26
|
+
def red(text)
|
27
|
+
[ RED, text, RESET ].join
|
28
|
+
end
|
29
|
+
|
30
|
+
def green_check
|
31
|
+
green("✔")
|
32
|
+
end
|
33
|
+
|
34
|
+
def downwards_arrow_to_right
|
35
|
+
"↳"
|
36
|
+
end
|
37
|
+
|
38
|
+
def cross
|
39
|
+
"✖"
|
40
|
+
end
|
41
|
+
|
42
|
+
def display_config_errors(errors)
|
43
|
+
puts "#{red(cross)} Config contains the following issues:"
|
44
|
+
|
45
|
+
errors.each do |error|
|
46
|
+
puts
|
47
|
+
puts " #{error["message"]}"
|
48
|
+
|
49
|
+
if error["path"]
|
50
|
+
puts faint(" #{downwards_arrow_to_right} #{error["path"]}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|