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
data/lib/envirobly/config.rb
CHANGED
@@ -1,208 +1,38 @@
|
|
1
|
-
require "
|
2
|
-
require "json"
|
3
|
-
require "digest"
|
1
|
+
require "dotenv"
|
4
2
|
|
5
3
|
class Envirobly::Config
|
6
4
|
DIR = ".envirobly"
|
7
|
-
|
5
|
+
BASE = "deploy.yml"
|
6
|
+
ENV_VARS = "env"
|
7
|
+
OVERRIDES_PATTERN = /deploy\.([a-z0-9\-_]+)\.yml/i
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(commit)
|
12
|
-
@commit = commit
|
13
|
-
@errors = []
|
14
|
-
@result = {}
|
15
|
-
@project_url = nil
|
16
|
-
@raw = @commit.file_content PATH
|
17
|
-
@project = parse
|
18
|
-
end
|
19
|
-
|
20
|
-
def dig(*args)
|
21
|
-
@project.dig(*args)
|
22
|
-
rescue NoMethodError
|
23
|
-
nil
|
24
|
-
end
|
25
|
-
|
26
|
-
def validate
|
27
|
-
return unless @project
|
28
|
-
validate_top_level_keys
|
29
|
-
validate_services @project.fetch(:services)
|
30
|
-
validate_environments
|
31
|
-
end
|
32
|
-
|
33
|
-
def compile(environment = nil)
|
34
|
-
return unless @project
|
35
|
-
@environment = environment
|
36
|
-
@result = @project.slice(:services)
|
37
|
-
set_project_url
|
38
|
-
merge_environment_overrides! unless @environment.nil?
|
39
|
-
append_image_tags!
|
9
|
+
def initialize(dir = DIR)
|
10
|
+
@dir = Pathname.new dir
|
40
11
|
end
|
41
12
|
|
42
|
-
def
|
13
|
+
def to_params
|
43
14
|
{
|
44
|
-
|
45
|
-
|
46
|
-
project_url: @project_url
|
47
|
-
},
|
48
|
-
commit: {
|
49
|
-
ref: @commit.ref,
|
50
|
-
time: @commit.time,
|
51
|
-
message: @commit.message
|
52
|
-
},
|
53
|
-
config: @result,
|
54
|
-
raw_config: @raw
|
15
|
+
configs:,
|
16
|
+
env_vars:
|
55
17
|
}
|
56
18
|
end
|
57
19
|
|
58
20
|
private
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
@errors << exception.message
|
63
|
-
nil
|
64
|
-
end
|
65
|
-
|
66
|
-
def set_project_url
|
67
|
-
@project_url = dig :project
|
68
|
-
end
|
69
|
-
|
70
|
-
NON_BUILDABLE_TYPES = %w[ postgres mysql valkey ]
|
71
|
-
BUILD_DEFAULTS = {
|
72
|
-
dockerfile: [ "Dockerfile", :file_exists? ],
|
73
|
-
build_context: [ ".", :dir_exists? ]
|
74
|
-
}
|
75
|
-
def append_image_tags!
|
76
|
-
@result[:services].each do |name, service|
|
77
|
-
next if NON_BUILDABLE_TYPES.include?(service[:type]) || service[:image].present?
|
78
|
-
checksums = []
|
79
|
-
|
80
|
-
BUILD_DEFAULTS.each do |attribute, options|
|
81
|
-
value = service.fetch(attribute, options.first)
|
82
|
-
if @commit.public_send(options.second, value)
|
83
|
-
checksums << @commit.objects_with_checksum_at(value)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
if checksums.size == 2
|
88
|
-
@result[:services][name][:image_tag] = Digest::SHA1.hexdigest checksums.to_json
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def merge_environment_overrides!
|
94
|
-
return unless services = @project.dig(:environments, @environment.to_sym)
|
95
|
-
services.each do |name, service|
|
96
|
-
service.each do |attribute, value|
|
97
|
-
if value.is_a?(Hash) && @result[:services][name][attribute].is_a?(Hash)
|
98
|
-
@result[:services][name][attribute].merge! value
|
99
|
-
@result[:services][name][attribute].compact!
|
100
|
-
else
|
101
|
-
@result[:services][name][attribute] = value
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
VALID_TOP_LEVEL_KEYS = %i[ project services environments ]
|
108
|
-
def validate_top_level_keys
|
109
|
-
unless @project.is_a?(Hash)
|
110
|
-
@errors << "Config doesn't contain a top level hash structure."
|
111
|
-
return
|
112
|
-
end
|
21
|
+
def configs
|
22
|
+
Dir.entries(@dir).map do |file|
|
23
|
+
path = File.join(@dir, file)
|
113
24
|
|
114
|
-
|
25
|
+
next unless File.file?(path) && config_file?(file)
|
115
26
|
|
116
|
-
|
117
|
-
|
118
|
-
@errors << "Top level key `#{key}` is not allowed. Allowed keys: #{VALID_TOP_LEVEL_KEYS.map{ "`#{_1}`" }.join(", ")}."
|
119
|
-
end
|
120
|
-
end
|
27
|
+
[ "#{DIR}/#{file}", File.read(path) ]
|
28
|
+
end.compact.to_h
|
121
29
|
end
|
122
30
|
|
123
|
-
|
124
|
-
|
125
|
-
image
|
126
|
-
build
|
127
|
-
engine_version
|
128
|
-
instance_type
|
129
|
-
min_instances
|
130
|
-
max_instances
|
131
|
-
volume_size
|
132
|
-
volume_mount
|
133
|
-
dockerfile
|
134
|
-
build_context
|
135
|
-
command
|
136
|
-
release_command
|
137
|
-
env
|
138
|
-
health_check
|
139
|
-
http
|
140
|
-
private
|
141
|
-
aliases
|
142
|
-
]
|
143
|
-
NAME_FORMAT = /\A[a-z0-9\-_\.\/]+\z/i
|
144
|
-
BUILD_VALUE_FORMAT = /\Av[\d+]\z/
|
145
|
-
def validate_services(services)
|
146
|
-
unless services.is_a?(Hash)
|
147
|
-
@errors << "`services` key must be a hash."
|
148
|
-
return
|
149
|
-
end
|
150
|
-
|
151
|
-
services.each do |name, service|
|
152
|
-
unless name =~ NAME_FORMAT
|
153
|
-
@errors << "`#{name}` is not a valid service name. Use aplhanumeric characters, dash, underscore, slash or dot."
|
154
|
-
end
|
155
|
-
|
156
|
-
unless service.is_a?(Hash)
|
157
|
-
@errors << "Service `#{name}` must be a hash."
|
158
|
-
next
|
159
|
-
end
|
160
|
-
|
161
|
-
service.each do |attribute, value|
|
162
|
-
unless VALID_SERVICE_KEYS.include?(attribute)
|
163
|
-
@errors << "Service `#{name}` attribute `#{attribute}` is not a valid attribute."
|
164
|
-
end
|
165
|
-
|
166
|
-
if attribute == :build
|
167
|
-
unless value =~ BUILD_VALUE_FORMAT
|
168
|
-
@errors << "Service `#{name}` attribute `#{attribute}` format needs to be a number prefixed with letter \"v\", for example \"v1\"."
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
BUILD_DEFAULTS.each do |attribute, options|
|
174
|
-
value = service.fetch(attribute, options.first)
|
175
|
-
unless @commit.public_send(options.second, value)
|
176
|
-
@errors << "Service `#{name}` specifies `#{attribute}` as `#{value}` which doesn't exist in this commit."
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
service.fetch(:env, {}).each do |key, value|
|
181
|
-
if value.is_a?(Hash) && value.has_key?(:file)
|
182
|
-
unless @commit.file_exists?(value.fetch(:file))
|
183
|
-
@errors << "Environment variable `#{key}` referring to a file `#{value.fetch(:file)}` doesn't exist in this commit."
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
31
|
+
def env_vars
|
32
|
+
Dotenv.parse @dir.join(ENV_VARS)
|
188
33
|
end
|
189
34
|
|
190
|
-
def
|
191
|
-
|
192
|
-
|
193
|
-
environments = @project.fetch :environments, nil
|
194
|
-
|
195
|
-
unless environments.is_a?(Hash)
|
196
|
-
@errors << "`environments` key must be a hash."
|
197
|
-
return
|
198
|
-
end
|
199
|
-
|
200
|
-
environments.each do |environment, services|
|
201
|
-
unless environment =~ NAME_FORMAT
|
202
|
-
@errors << "`#{environment}` is not a valid environment name. Use aplhanumeric characters, dash, underscore, slash or dot."
|
203
|
-
end
|
204
|
-
|
205
|
-
validate_services services
|
206
|
-
end
|
35
|
+
def config_file?(file)
|
36
|
+
file == BASE || file.match?(OVERRIDES_PATTERN)
|
207
37
|
end
|
208
38
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Envirobly::Default
|
2
|
+
attr_accessor :shell
|
3
|
+
|
4
|
+
def self.key = "url"
|
5
|
+
|
6
|
+
def initialize(shell: nil)
|
7
|
+
@path = File.join Envirobly::Config::DIR, "defaults", self.class.file
|
8
|
+
@shell = shell
|
9
|
+
end
|
10
|
+
|
11
|
+
def id
|
12
|
+
if File.exist?(@path)
|
13
|
+
content = YAML.safe_load_file(@path)
|
14
|
+
|
15
|
+
if content[self.class.key] =~ self.class.regexp
|
16
|
+
return cast_id($1)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def save(url)
|
24
|
+
unless url =~ self.class.regexp
|
25
|
+
raise ArgumentError, "'#{url}' must match #{self.class.regexp}"
|
26
|
+
end
|
27
|
+
|
28
|
+
FileUtils.mkdir_p(File.dirname(@path))
|
29
|
+
content = YAML.dump({ self.class.key => url })
|
30
|
+
File.write(@path, content)
|
31
|
+
end
|
32
|
+
|
33
|
+
def save_if_none(url)
|
34
|
+
return if id.present?
|
35
|
+
|
36
|
+
save(url)
|
37
|
+
end
|
38
|
+
|
39
|
+
def require_if_none
|
40
|
+
id || require_id
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def cast_id(value)
|
45
|
+
value.to_i
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Envirobly::Defaults::Account < Envirobly::Default
|
2
|
+
include Envirobly::Colorize
|
3
|
+
|
4
|
+
def self.file = "account.yml"
|
5
|
+
def self.regexp = /accounts\/(\d+)/
|
6
|
+
|
7
|
+
def require_id
|
8
|
+
api = Envirobly::Api.new
|
9
|
+
accounts = api.list_accounts
|
10
|
+
|
11
|
+
if accounts.object.blank?
|
12
|
+
shell.say_error "Please connect an AWS account to your Envirobly account first."
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
account = accounts.object.first
|
17
|
+
id = account["id"]
|
18
|
+
|
19
|
+
if accounts.object.size > 1
|
20
|
+
puts "Choose default account to deploy this project to:"
|
21
|
+
|
22
|
+
data = [ [ "ID", "Name", "AWS number", "URL" ] ] +
|
23
|
+
accounts.object.pluck("id", "name", "aws_id", "url")
|
24
|
+
|
25
|
+
shell.print_table data, borders: true
|
26
|
+
|
27
|
+
limited_to = accounts.object.pluck("id").map(&:to_s)
|
28
|
+
|
29
|
+
begin
|
30
|
+
id = shell.ask("Type in the account ID:", limited_to:).to_i
|
31
|
+
rescue Interrupt
|
32
|
+
shell.say_error "Cancelled"
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
|
36
|
+
account = accounts.object.find { |a| a["id"] == id }
|
37
|
+
end
|
38
|
+
|
39
|
+
save account["url"]
|
40
|
+
|
41
|
+
shell.say "Account ##{id} set as project default "
|
42
|
+
shell.say green_check
|
43
|
+
|
44
|
+
id
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Envirobly::Defaults::Region < Envirobly::Default
|
2
|
+
include Envirobly::Colorize
|
3
|
+
|
4
|
+
def self.file = "region.yml"
|
5
|
+
def self.regexp = /([a-z0-9\-)]+)/
|
6
|
+
def self.key = "code"
|
7
|
+
|
8
|
+
def require_id
|
9
|
+
api = Envirobly::Api.new
|
10
|
+
response = api.list_regions
|
11
|
+
|
12
|
+
shell.say "Choose default project region to deploy to:"
|
13
|
+
shell.print_table [ [ "Name", "Location", "Group" ] ] +
|
14
|
+
response.object.pluck("code", "title", "group_title"), borders: true
|
15
|
+
|
16
|
+
code = nil
|
17
|
+
limited_to = response.object.pluck("code")
|
18
|
+
|
19
|
+
while code.nil?
|
20
|
+
begin
|
21
|
+
code = shell.ask("Type in the region name:", default: "us-east-1")
|
22
|
+
rescue Interrupt
|
23
|
+
shell.say_error "Cancelled"
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
unless code.in?(limited_to)
|
28
|
+
shell.say_error "'#{code}' is not a supported region, please try again"
|
29
|
+
code = nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
save code
|
34
|
+
|
35
|
+
shell.say "Region '#{id}' set as project default "
|
36
|
+
shell.say green_check
|
37
|
+
|
38
|
+
id
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def cast_id(value)
|
43
|
+
value
|
44
|
+
end
|
45
|
+
end
|
data/lib/envirobly/deployment.rb
CHANGED
@@ -1,50 +1,99 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
1
3
|
class Envirobly::Deployment
|
2
|
-
|
3
|
-
|
4
|
+
include Envirobly::Colorize
|
5
|
+
|
6
|
+
def initialize(environ_name:, commit:, account_id:, project_name:, project_id:, region:, shell:)
|
7
|
+
@environ_name = environ_name
|
8
|
+
@commit = commit
|
9
|
+
@config = Envirobly::Config.new
|
10
|
+
@default_account = Envirobly::Defaults::Account.new(shell:)
|
11
|
+
@default_project = Envirobly::Defaults::Project.new(shell:)
|
12
|
+
@default_region = Envirobly::Defaults::Region.new(shell:)
|
4
13
|
|
5
|
-
|
6
|
-
|
7
|
-
exit 1
|
14
|
+
if account_id.blank?
|
15
|
+
account_id = @default_account.require_if_none
|
8
16
|
end
|
9
17
|
|
10
|
-
|
11
|
-
|
18
|
+
if project_id.blank? && project_name.blank?
|
19
|
+
project_id = @default_project.id
|
12
20
|
|
13
|
-
|
14
|
-
|
15
|
-
$stderr.puts
|
16
|
-
config.errors.each do |error|
|
17
|
-
$stderr.puts " - #{error}"
|
21
|
+
if project_id.nil?
|
22
|
+
project_name = File.basename(Dir.pwd)
|
18
23
|
end
|
19
|
-
$stderr.puts
|
20
|
-
$stderr.puts "Please fix these, commit the changes and try again."
|
21
|
-
exit 1
|
22
24
|
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
+
if region.blank?
|
27
|
+
region = @default_region.require_if_none
|
28
|
+
end
|
29
|
+
|
30
|
+
@params = {
|
31
|
+
account: {
|
32
|
+
id: account_id
|
33
|
+
},
|
34
|
+
project: {
|
35
|
+
id: project_id,
|
36
|
+
name: project_name,
|
37
|
+
region:
|
38
|
+
},
|
39
|
+
deployment: {
|
40
|
+
environ_name:,
|
41
|
+
commit_ref: @commit.ref,
|
42
|
+
commit_time: @commit.time,
|
43
|
+
commit_message: @commit.message,
|
44
|
+
object_tree_checksum: @commit.object_tree_checksum,
|
45
|
+
**@config.to_params
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
26
49
|
|
27
|
-
|
28
|
-
puts
|
50
|
+
def perform(dry_run:)
|
51
|
+
puts [ "Deploying commit", yellow(@commit.short_ref), faint("→"), green(@environ_name) ].join(" ")
|
52
|
+
puts
|
53
|
+
puts " #{@commit.message}"
|
54
|
+
puts
|
29
55
|
|
30
|
-
|
56
|
+
if dry_run
|
57
|
+
puts YAML.dump(@params)
|
58
|
+
return
|
59
|
+
end
|
31
60
|
|
61
|
+
# Create deployment
|
32
62
|
api = Envirobly::Api.new
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
63
|
+
|
64
|
+
Envirobly::Duration.measure do
|
65
|
+
response = api.create_deployment @params
|
66
|
+
|
67
|
+
unless response.success?
|
68
|
+
display_config_errors response.object.fetch("errors")
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
|
72
|
+
print "Preparing project..."
|
73
|
+
|
74
|
+
@default_account.save_if_none response.object.fetch("account_url")
|
75
|
+
@default_project.save_if_none response.object.fetch("project_url")
|
76
|
+
@default_region.save_if_none response.object.fetch("region")
|
77
|
+
|
78
|
+
# Fetch credentials for build context upload
|
79
|
+
@deployment_url = response.object.fetch("url")
|
80
|
+
@credentials_response = api.get_deployment_with_delay_and_retry @deployment_url
|
43
81
|
end
|
44
82
|
|
45
|
-
|
46
|
-
|
83
|
+
credentials = @credentials_response.object.fetch("credentials")
|
84
|
+
region = @credentials_response.object.fetch("region")
|
85
|
+
bucket = @credentials_response.object.fetch("bucket")
|
86
|
+
watch_deployment_url = @credentials_response.object.fetch("deployment_url")
|
87
|
+
|
88
|
+
Envirobly::Duration.measure do
|
89
|
+
# Upload build context
|
90
|
+
s3 = Envirobly::Aws::S3.new(bucket:, region:, credentials:)
|
91
|
+
s3.push @commit
|
92
|
+
|
93
|
+
# Perform deployment
|
94
|
+
api.put_as_json @deployment_url
|
95
|
+
end
|
47
96
|
|
48
|
-
|
97
|
+
puts "Follow at #{watch_deployment_url}"
|
49
98
|
end
|
50
99
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
|
3
|
+
class Envirobly::Duration
|
4
|
+
class << self
|
5
|
+
include Envirobly::Colorize
|
6
|
+
|
7
|
+
def measure(message = nil)
|
8
|
+
measurement = Benchmark.measure do
|
9
|
+
yield
|
10
|
+
end
|
11
|
+
|
12
|
+
duration = format_duration(measurement)
|
13
|
+
|
14
|
+
if message.nil?
|
15
|
+
puts [ "", green_check, faint(duration) ].join(" ")
|
16
|
+
else
|
17
|
+
puts sprintf(message, duration)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def format_duration(tms)
|
22
|
+
ms = (tms.real * 1000).to_i
|
23
|
+
|
24
|
+
if ms >= 60_000
|
25
|
+
minutes = ms / 60_000
|
26
|
+
seconds = (ms % 60_000) / 1000
|
27
|
+
sprintf("%dm%ds", minutes, seconds)
|
28
|
+
elsif ms >= 1000
|
29
|
+
seconds = ms / 1000
|
30
|
+
sprintf("%ds", seconds)
|
31
|
+
else
|
32
|
+
sprintf("%dms", ms)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/envirobly/git/commit.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
require "time"
|
2
2
|
require "open3"
|
3
3
|
|
4
|
-
class Envirobly::Git::Commit
|
4
|
+
class Envirobly::Git::Commit < Envirobly::Git
|
5
|
+
EXECUTABLE_FILE_MODE = "100755"
|
6
|
+
SYMLINK_FILE_MODE = "120000"
|
7
|
+
|
8
|
+
attr_reader :working_dir
|
9
|
+
|
5
10
|
def initialize(ref, working_dir: Dir.getwd)
|
6
11
|
@ref = ref
|
7
|
-
|
12
|
+
super working_dir
|
8
13
|
end
|
9
14
|
|
10
15
|
def exists?
|
@@ -15,6 +20,10 @@ class Envirobly::Git::Commit
|
|
15
20
|
@normalized_ref ||= git(%(rev-parse #{@ref})).stdout.strip
|
16
21
|
end
|
17
22
|
|
23
|
+
def short_ref
|
24
|
+
@short_ref ||= ref[0..6]
|
25
|
+
end
|
26
|
+
|
18
27
|
def message
|
19
28
|
git(%(log #{@ref} -n1 --pretty=%B)).stdout.strip
|
20
29
|
end
|
@@ -36,25 +45,35 @@ class Envirobly::Git::Commit
|
|
36
45
|
git(%(show #{@ref}:#{path})).stdout
|
37
46
|
end
|
38
47
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
48
|
+
def object_tree(ref: @ref, chdir: @working_dir)
|
49
|
+
@object_tree ||= begin
|
50
|
+
objects = {}
|
51
|
+
objects[chdir] = []
|
43
52
|
|
44
|
-
|
45
|
-
|
46
|
-
|
53
|
+
git(%(ls-tree -r #{ref}), chdir:).stdout.lines.each do |line|
|
54
|
+
mode, type, object_hash, path = line.split(/\s+/)
|
55
|
+
|
56
|
+
next if path.start_with?("#{Envirobly::Config::DIR}/")
|
47
57
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
OUTPUT.new stdout.read, stderr.read, thread.value.exitstatus, thread.value.success?
|
58
|
+
if type == "commit"
|
59
|
+
objects.merge! object_tree(ref: object_hash, chdir: File.join(chdir, path))
|
60
|
+
else
|
61
|
+
objects[chdir] << [ mode, type, object_hash, path ]
|
62
|
+
end
|
54
63
|
end
|
55
|
-
end
|
56
64
|
|
57
|
-
|
58
|
-
"s3://#{bucket}/#{ref}.tar.gz"
|
65
|
+
objects
|
59
66
|
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def object_tree_checksum
|
70
|
+
digestable = object_tree.values.flatten.to_json
|
71
|
+
@object_tree_checksum ||= Digest::SHA256.hexdigest(digestable)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @deprecated
|
75
|
+
def objects_with_checksum_at(path)
|
76
|
+
git(%{ls-tree #{@ref} --format='%(objectname) %(path)' #{path}}).stdout.lines.map(&:chomp).
|
77
|
+
reject { _1.split(" ").last == Envirobly::Config::DIR }
|
78
|
+
end
|
60
79
|
end
|
data/lib/envirobly/git.rb
CHANGED
@@ -1,2 +1,17 @@
|
|
1
|
-
|
1
|
+
class Envirobly::Git
|
2
|
+
def initialize(working_dir = Dir.getwd)
|
3
|
+
@working_dir = working_dir
|
4
|
+
end
|
5
|
+
|
6
|
+
OUTPUT = Struct.new :stdout, :stderr, :exit_code, :success?
|
7
|
+
def git(cmd, chdir: @working_dir)
|
8
|
+
Open3.popen3("git #{cmd}", chdir:) do |stdin, stdout, stderr, thread|
|
9
|
+
stdin.close
|
10
|
+
OUTPUT.new stdout.read, stderr.read, thread.value.exitstatus, thread.value.success?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def current_branch
|
15
|
+
git("branch --show-current").stdout.strip
|
16
|
+
end
|
2
17
|
end
|
data/lib/envirobly/version.rb
CHANGED
data/lib/envirobly.rb
CHANGED