envirobly 0.5.1 → 0.6.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/cli/main.rb +22 -1
- data/lib/envirobly/config.rb +118 -26
- data/lib/envirobly/deployment.rb +2 -1
- data/lib/envirobly/git/commit.rb +9 -0
- data/lib/envirobly/git/unstaged.rb +17 -0
- data/lib/envirobly/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2750aa4a83c0407fbba1c7db3aa5b7cebaa6ea08fd2112064a4c6c5d88aad4c
|
4
|
+
data.tar.gz: 464b4a65da73a81d6a62c1f2fa35f4dd927bd790e16cc3f0f58e3d1048e0d87c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09321732787c09ee02ca9c4071fb916930db737f07dee8a2cd0ffe482620f3f61586a01d796df4a8452b5c7e2b4f6186e8f9ad7ad9feb633f2f5135c5ce7aefb'
|
7
|
+
data.tar.gz: 44513ce52f34fef1310f54095589dec1ab05a0233c1a16f77309ebc9c3cbce2cacf1dea70b01a63fafeab26ae7005de6b9ecb46e8d6b25aa6d938d8e939bcc81
|
data/lib/envirobly/cli/main.rb
CHANGED
@@ -4,6 +4,25 @@ class Envirobly::Cli::Main < Envirobly::Base
|
|
4
4
|
puts Envirobly::VERSION
|
5
5
|
end
|
6
6
|
|
7
|
+
desc "validate", "Validates config"
|
8
|
+
def validate
|
9
|
+
commit = Envirobly::Git::Unstaged.new
|
10
|
+
config = Envirobly::Config.new(commit)
|
11
|
+
config.validate
|
12
|
+
|
13
|
+
if config.errors.any?
|
14
|
+
puts "Issues found validating `#{Envirobly::Config::PATH}`:"
|
15
|
+
puts
|
16
|
+
config.errors.each_with_index do |error, index|
|
17
|
+
puts " #{index + 1}. #{error}"
|
18
|
+
end
|
19
|
+
puts
|
20
|
+
exit 1
|
21
|
+
else
|
22
|
+
puts "All checks pass."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
7
26
|
desc "deploy ENVIRONMENT", "Deploy to environment identified by name or URL"
|
8
27
|
method_option :commit, type: :string, default: "HEAD"
|
9
28
|
method_option :dry_run, type: :boolean, default: false
|
@@ -15,7 +34,9 @@ class Envirobly::Cli::Main < Envirobly::Base
|
|
15
34
|
desc "set_access_token TOKEN", "Save and use an access token generated at Envirobly"
|
16
35
|
def set_access_token
|
17
36
|
token = ask("Access Token:", echo: false).strip
|
18
|
-
|
37
|
+
|
38
|
+
if token.blank?
|
39
|
+
$stderr.puts
|
19
40
|
$stderr.puts "Token can't be empty."
|
20
41
|
exit 1
|
21
42
|
end
|
data/lib/envirobly/config.rb
CHANGED
@@ -11,9 +11,10 @@ class Envirobly::Config
|
|
11
11
|
def initialize(commit)
|
12
12
|
@commit = commit
|
13
13
|
@errors = []
|
14
|
-
@result =
|
14
|
+
@result = {}
|
15
15
|
@project_url = nil
|
16
16
|
@raw = @commit.file_content PATH
|
17
|
+
@project = parse
|
17
18
|
end
|
18
19
|
|
19
20
|
def dig(*args)
|
@@ -22,20 +23,27 @@ class Envirobly::Config
|
|
22
23
|
nil
|
23
24
|
end
|
24
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
|
+
|
25
33
|
def compile(environment = nil)
|
34
|
+
return unless @project
|
26
35
|
@environment = environment
|
27
|
-
|
36
|
+
@result = @project.slice(:services)
|
28
37
|
set_project_url
|
29
38
|
merge_environment_overrides! unless @environment.nil?
|
30
39
|
transform_env_var_values!
|
31
40
|
append_image_tags!
|
32
|
-
@result = @project.slice(:services)
|
33
41
|
end
|
34
42
|
|
35
43
|
def to_deployment_params
|
36
44
|
{
|
37
45
|
environ: {
|
38
|
-
|
46
|
+
name: @environment,
|
39
47
|
project_url: @project_url
|
40
48
|
},
|
41
49
|
commit: {
|
@@ -57,17 +65,14 @@ class Envirobly::Config
|
|
57
65
|
end
|
58
66
|
|
59
67
|
def set_project_url
|
60
|
-
@project_url = dig :
|
61
|
-
if @project_url.blank?
|
62
|
-
@errors << "Missing a `remote.origin` link to project."
|
63
|
-
end
|
68
|
+
@project_url = dig :project
|
64
69
|
end
|
65
70
|
|
66
71
|
def transform_env_var_values!
|
67
|
-
@
|
72
|
+
@result[:services].each do |name, service|
|
68
73
|
service.fetch(:env, {}).each do |key, value|
|
69
74
|
if value.is_a?(Hash) && value.has_key?(:file)
|
70
|
-
@
|
75
|
+
@result[:services][name][:env][key] = @commit.file_content(value.fetch(:file)).strip
|
71
76
|
end
|
72
77
|
end
|
73
78
|
end
|
@@ -75,41 +80,128 @@ class Envirobly::Config
|
|
75
80
|
|
76
81
|
NON_BUILDABLE_TYPES = %w[ postgres mysql valkey ]
|
77
82
|
BUILD_DEFAULTS = {
|
78
|
-
dockerfile: "Dockerfile",
|
79
|
-
build_context: "."
|
83
|
+
dockerfile: [ "Dockerfile", :file_exists? ],
|
84
|
+
build_context: [ ".", :dir_exists? ]
|
80
85
|
}
|
81
86
|
def append_image_tags!
|
82
|
-
@
|
87
|
+
@result[:services].each do |name, service|
|
83
88
|
next if NON_BUILDABLE_TYPES.include?(service[:type]) || service[:image].present?
|
84
89
|
checksums = []
|
85
90
|
|
86
|
-
BUILD_DEFAULTS.each do |attribute,
|
87
|
-
value = service.fetch(attribute,
|
88
|
-
|
89
|
-
|
90
|
-
@errors << "Service `#{logical_id}` specifies `#{attribute}` as `#{value}` which doesn't exist in this commit."
|
91
|
-
else
|
92
|
-
checksums << checksum
|
91
|
+
BUILD_DEFAULTS.each do |attribute, options|
|
92
|
+
value = service.fetch(attribute, options.first)
|
93
|
+
if @commit.public_send(options.second, value)
|
94
|
+
checksums << @commit.objects_with_checksum_at(value)
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
96
98
|
if checksums.size == 2
|
97
|
-
@
|
99
|
+
@result[:services][name][:image_tag] = Digest::SHA1.hexdigest checksums.to_json
|
98
100
|
end
|
99
101
|
end
|
100
102
|
end
|
101
103
|
|
102
104
|
def merge_environment_overrides!
|
103
105
|
return unless services = @project.dig(:environments, @environment.to_sym)
|
104
|
-
services.each do |
|
106
|
+
services.each do |name, service|
|
105
107
|
service.each do |attribute, value|
|
106
|
-
if value.is_a?(Hash) && @
|
107
|
-
@
|
108
|
-
@
|
108
|
+
if value.is_a?(Hash) && @result[:services][name][attribute].is_a?(Hash)
|
109
|
+
@result[:services][name][attribute].merge! value
|
110
|
+
@result[:services][name][attribute].compact!
|
109
111
|
else
|
110
|
-
@
|
112
|
+
@result[:services][name][attribute] = value
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
VALID_TOP_LEVEL_KEYS = %i[ project services environments ]
|
119
|
+
def validate_top_level_keys
|
120
|
+
unless @project.is_a?(Hash)
|
121
|
+
@errors << "Config doesn't contain a top level hash structure."
|
122
|
+
return
|
123
|
+
end
|
124
|
+
|
125
|
+
@errors << "Missing `project: <url>` top level attribute." if @project[:project].blank?
|
126
|
+
|
127
|
+
@project.keys.each do |key|
|
128
|
+
unless VALID_TOP_LEVEL_KEYS.include?(key)
|
129
|
+
@errors << "Top level key `#{key}` is not allowed. Allowed keys: #{VALID_TOP_LEVEL_KEYS.map{ "`#{_1}`" }.join(", ")}."
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
VALID_SERVICE_KEYS = %i[
|
135
|
+
type
|
136
|
+
image
|
137
|
+
engine_version
|
138
|
+
instance_type
|
139
|
+
volume_size
|
140
|
+
volume_mount
|
141
|
+
dockerfile
|
142
|
+
build_context
|
143
|
+
command
|
144
|
+
env
|
145
|
+
health_check
|
146
|
+
private
|
147
|
+
aliases
|
148
|
+
]
|
149
|
+
NAME_FORMAT = /\A[a-z0-9\-_]+\z/
|
150
|
+
def validate_services(services)
|
151
|
+
unless services.is_a?(Hash)
|
152
|
+
@errors << "`services` key must be a hash."
|
153
|
+
return
|
154
|
+
end
|
155
|
+
|
156
|
+
services.each do |name, service|
|
157
|
+
unless name =~ NAME_FORMAT
|
158
|
+
@errors << "`#{name}` is not a valid service name. Allowed characters: a-z, 0-9, -, _"
|
159
|
+
end
|
160
|
+
|
161
|
+
unless service.is_a?(Hash)
|
162
|
+
@errors << "Service `#{name}` must be a hash."
|
163
|
+
next
|
164
|
+
end
|
165
|
+
|
166
|
+
service.each do |attribute, value|
|
167
|
+
unless VALID_SERVICE_KEYS.include?(attribute)
|
168
|
+
@errors << "Service `#{name}` attribute `#{attribute}` is not a valid attribute."
|
111
169
|
end
|
112
170
|
end
|
171
|
+
|
172
|
+
BUILD_DEFAULTS.each do |attribute, options|
|
173
|
+
value = service.fetch(attribute, options.first)
|
174
|
+
unless @commit.public_send(options.second, value)
|
175
|
+
@errors << "Service `#{name}` specifies `#{attribute}` as `#{value}` which doesn't exist in this commit."
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
service.fetch(:env, {}).each do |key, value|
|
180
|
+
if value.is_a?(Hash) && value.has_key?(:file)
|
181
|
+
unless @commit.file_exists?(value.fetch(:file))
|
182
|
+
@errors << "Environment variable `#{key}` referring to a file `#{value.fetch(:file)}` doesn't exist in this commit."
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def validate_environments
|
190
|
+
return unless @project.has_key?(:environments)
|
191
|
+
|
192
|
+
environments = @project.fetch :environments, nil
|
193
|
+
|
194
|
+
unless environments.is_a?(Hash)
|
195
|
+
@errors << "`environments` key must be a hash."
|
196
|
+
return
|
197
|
+
end
|
198
|
+
|
199
|
+
environments.each do |environment, services|
|
200
|
+
unless environment =~ NAME_FORMAT
|
201
|
+
@errors << "`#{environment}` is not a valid environment name. Allowed characters: a-z, 0-9, -, _"
|
202
|
+
end
|
203
|
+
|
204
|
+
validate_services services
|
113
205
|
end
|
114
206
|
end
|
115
207
|
end
|
data/lib/envirobly/deployment.rb
CHANGED
@@ -8,7 +8,7 @@ class Envirobly::Deployment
|
|
8
8
|
end
|
9
9
|
|
10
10
|
config = Envirobly::Config.new(commit)
|
11
|
-
config.
|
11
|
+
config.validate
|
12
12
|
|
13
13
|
if config.errors.any?
|
14
14
|
$stderr.puts "Errors found while parsing #{Envirobly::Config::PATH}:"
|
@@ -21,6 +21,7 @@ class Envirobly::Deployment
|
|
21
21
|
exit 1
|
22
22
|
end
|
23
23
|
|
24
|
+
config.compile(environment)
|
24
25
|
params = config.to_deployment_params
|
25
26
|
|
26
27
|
puts "Deployment config:"
|
data/lib/envirobly/git/commit.rb
CHANGED
@@ -23,6 +23,15 @@ class Envirobly::Git::Commit
|
|
23
23
|
Time.parse git(%(log #{@ref} -n1 --date=iso --pretty=format:"%ad")).stdout
|
24
24
|
end
|
25
25
|
|
26
|
+
def file_exists?(path)
|
27
|
+
git(%(cat-file -t #{@ref}:#{path})).stdout.strip == "blob"
|
28
|
+
end
|
29
|
+
|
30
|
+
def dir_exists?(path)
|
31
|
+
suffix = path.end_with?("/") ? nil : "/"
|
32
|
+
git(%(cat-file -t #{@ref}:#{path}#{suffix})).stdout.strip == "tree"
|
33
|
+
end
|
34
|
+
|
26
35
|
def file_content(path)
|
27
36
|
git(%(show #{@ref}:#{path})).stdout
|
28
37
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Envirobly::Git::Unstaged < Envirobly::Git::Commit
|
2
|
+
def initialize(working_dir: Dir.getwd)
|
3
|
+
@working_dir = working_dir
|
4
|
+
end
|
5
|
+
|
6
|
+
def file_exists?(path)
|
7
|
+
File.exist? path
|
8
|
+
end
|
9
|
+
|
10
|
+
def dir_exists?(path)
|
11
|
+
Dir.exist? path
|
12
|
+
end
|
13
|
+
|
14
|
+
def file_content(path)
|
15
|
+
File.read path
|
16
|
+
end
|
17
|
+
end
|
data/lib/envirobly/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: envirobly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Starsi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -130,6 +130,7 @@ files:
|
|
130
130
|
- lib/envirobly/deployment.rb
|
131
131
|
- lib/envirobly/git.rb
|
132
132
|
- lib/envirobly/git/commit.rb
|
133
|
+
- lib/envirobly/git/unstaged.rb
|
133
134
|
- lib/envirobly/version.rb
|
134
135
|
homepage: https://github.com/envirobly/envirobly-cli
|
135
136
|
licenses:
|