djin 0.1.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/.github/workflows/ruby.yml +34 -0
- data/.rubocop.yml +8 -0
- data/.rubocop_todo.yml +17 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +35 -10
- data/README.md +160 -14
- data/Rakefile +6 -4
- data/Vertofile +45 -0
- data/bin/console +4 -3
- data/djin.gemspec +25 -20
- data/djin.yml +30 -12
- data/examples/djin.yml +25 -23
- data/exe/djin +3 -0
- data/lib/djin.rb +35 -22
- data/lib/djin/cli.rb +13 -1
- data/lib/djin/config_loader.rb +92 -0
- data/lib/djin/entities/task.rb +4 -1
- data/lib/djin/entities/types.rb +2 -0
- data/lib/djin/executor.rb +24 -10
- data/lib/djin/extensions/hash_extensions.rb +7 -1
- data/lib/djin/interpreter.rb +24 -54
- data/lib/djin/interpreter/base_command_builder.rb +26 -0
- data/lib/djin/interpreter/docker_command_builder.rb +29 -0
- data/lib/djin/interpreter/docker_compose_command_builder.rb +17 -0
- data/lib/djin/interpreter/local_command_builder.rb +11 -0
- data/lib/djin/repositories/task_repository.rb +19 -0
- data/lib/djin/task_contract.rb +39 -7
- data/lib/djin/version.rb +3 -1
- metadata +64 -12
- data/lib/djin/extensions/custom_predicates.rb +0 -18
data/lib/djin/interpreter.rb
CHANGED
@@ -1,23 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Djin
|
2
4
|
class Interpreter
|
3
5
|
using Djin::HashExtensions
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
# TODO: Move Errors to ConfigLoader
|
8
|
+
InvalidConfigurationError = Class.new(StandardError)
|
9
|
+
MissingVersionError = Class.new(InvalidConfigurationError)
|
10
|
+
VersionNotSupportedError = Class.new(InvalidConfigurationError)
|
11
|
+
InvalidSyntaxError = Class.new(InvalidConfigurationError)
|
8
12
|
|
9
13
|
class << self
|
10
|
-
def load!(
|
11
|
-
task_params = params.except(*RESERVED_WORDS)
|
14
|
+
def load!(tasks_params)
|
12
15
|
contract = TaskContract.new
|
13
16
|
|
14
|
-
|
17
|
+
tasks_params.map do |task_name, options|
|
15
18
|
result = contract.call(options)
|
16
19
|
|
17
|
-
raise
|
20
|
+
raise InvalidSyntaxError, { task_name.to_sym => result.errors.to_h } if result.failure?
|
18
21
|
|
19
22
|
command, build_command = build_commands(options, task_name: task_name)
|
20
|
-
|
23
|
+
|
24
|
+
task_params = {
|
25
|
+
name: task_name,
|
26
|
+
build_command: build_command,
|
27
|
+
command: command,
|
28
|
+
depends_on: options['depends_on']
|
29
|
+
}.compact
|
30
|
+
|
31
|
+
Djin::Task.new(**task_params)
|
21
32
|
end
|
22
33
|
end
|
23
34
|
|
@@ -27,54 +38,13 @@ module Djin
|
|
27
38
|
# Validate that only one ot the two is passed
|
28
39
|
docker_params = params['docker']
|
29
40
|
docker_compose_params = params['docker-compose']
|
41
|
+
local_params = params['local']
|
30
42
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
def build_docker_commands(params, task_name:)
|
36
|
-
current_folder_name = Pathname.getwd.basename.to_s
|
37
|
-
image = params['image'] || "djin_#{current_folder_name}_#{task_name}"
|
38
|
-
|
39
|
-
build_params = params['build']
|
40
|
-
|
41
|
-
if build_params.is_a?(Hash)
|
42
|
-
build_context = build_params['context']
|
43
|
-
build_options = build_params['options']
|
44
|
-
end
|
45
|
-
|
46
|
-
build_context ||= build_params
|
47
|
-
|
48
|
-
run_command, run_options = build_run_params(params['run'])
|
49
|
-
|
50
|
-
command = %Q{docker run #{run_options} #{image} sh -c "#{run_command}"}.squeeze(' ')
|
51
|
-
|
52
|
-
build_command = "docker build #{build_context} #{build_options} -t #{image}".squeeze(' ') if build_context
|
53
|
-
|
54
|
-
[command, build_command]
|
55
|
-
end
|
56
|
-
|
57
|
-
def build_docker_compose_commands(params)
|
58
|
-
service = params['service']
|
59
|
-
|
60
|
-
compose_options = params['options']
|
61
|
-
|
62
|
-
run_command, run_options = build_run_params(params['run'])
|
63
|
-
|
64
|
-
[%Q{docker-compose #{compose_options} run #{run_options} #{service} sh -c "#{run_command}"}.squeeze(' '), nil]
|
65
|
-
end
|
66
|
-
|
67
|
-
def build_run_params(run_params)
|
68
|
-
run_command = run_params
|
69
|
-
|
70
|
-
if run_params.is_a?(Hash)
|
71
|
-
run_command = run_params['commands']
|
72
|
-
run_options = run_params['options']
|
73
|
-
end
|
74
|
-
|
75
|
-
run_command = run_command.join(' && ') if run_command.is_a?(Array)
|
43
|
+
# TODO: Refactor to use chain of responsability
|
44
|
+
return DockerCommandBuilder.call(docker_params, task_name: task_name) if docker_params
|
45
|
+
return DockerComposeCommandBuilder.call(docker_compose_params) if docker_compose_params
|
76
46
|
|
77
|
-
|
47
|
+
LocalCommandBuilder.call(local_params) if local_params
|
78
48
|
end
|
79
49
|
end
|
80
50
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Djin
|
4
|
+
class Interpreter
|
5
|
+
class BaseCommandBuilder
|
6
|
+
def self.call(*options)
|
7
|
+
new.call(*options)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def build_run_params(run_params)
|
13
|
+
run_command = run_params
|
14
|
+
|
15
|
+
if run_params.is_a?(Hash)
|
16
|
+
run_command = run_params['commands']
|
17
|
+
run_options = run_params['options']
|
18
|
+
end
|
19
|
+
|
20
|
+
run_command = run_command.join(' && ') if run_command.is_a?(Array)
|
21
|
+
|
22
|
+
[run_command, run_options]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Djin
|
4
|
+
class Interpreter
|
5
|
+
class DockerCommandBuilder < BaseCommandBuilder
|
6
|
+
def call(params, task_name:)
|
7
|
+
current_folder_name = Pathname.getwd.basename.to_s
|
8
|
+
image = params['image'] || "djin_#{current_folder_name}_#{task_name}"
|
9
|
+
|
10
|
+
build_params = params['build']
|
11
|
+
|
12
|
+
if build_params.is_a?(Hash)
|
13
|
+
build_context = build_params['context']
|
14
|
+
build_options = build_params['options']
|
15
|
+
end
|
16
|
+
|
17
|
+
build_context ||= build_params
|
18
|
+
|
19
|
+
run_command, run_options = build_run_params(params['run'])
|
20
|
+
|
21
|
+
command = %(docker run #{run_options} #{image} sh -c "#{run_command}").squeeze(' ')
|
22
|
+
|
23
|
+
build_command = "docker build #{build_context} #{build_options} -t #{image}".squeeze(' ') if build_context
|
24
|
+
|
25
|
+
[command, build_command]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Djin
|
4
|
+
class Interpreter
|
5
|
+
class DockerComposeCommandBuilder < BaseCommandBuilder
|
6
|
+
def call(params, **_)
|
7
|
+
service = params['service']
|
8
|
+
|
9
|
+
compose_options = params['options']
|
10
|
+
|
11
|
+
run_command, run_options = build_run_params(params['run'])
|
12
|
+
|
13
|
+
[%(docker-compose #{compose_options} run #{run_options} #{service} sh -c "#{run_command}").squeeze(' '), nil]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TaskRepository
|
4
|
+
def initialize(tasks = [])
|
5
|
+
@tasks = tasks
|
6
|
+
end
|
7
|
+
|
8
|
+
def add(*tasks)
|
9
|
+
@tasks += tasks
|
10
|
+
end
|
11
|
+
|
12
|
+
def all
|
13
|
+
@tasks
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_by_names(names)
|
17
|
+
@tasks.select { |task| names.include?(task.name) }
|
18
|
+
end
|
19
|
+
end
|
data/lib/djin/task_contract.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Djin
|
2
4
|
class TaskContract < Dry::Validation::Contract
|
3
|
-
NOT_EMPTY = ->
|
4
|
-
OK = ->
|
5
|
+
NOT_EMPTY = ->(value) { !value.empty? }
|
6
|
+
OK = ->(_) { true }
|
7
|
+
NOT_OK = ->(_) { false }
|
5
8
|
|
6
9
|
BuildSchema = Dry::Schema.Params do
|
7
10
|
required(:context).filled(:string)
|
@@ -13,6 +16,10 @@ module Djin
|
|
13
16
|
required(:options).filled(:string)
|
14
17
|
end
|
15
18
|
|
19
|
+
RunLocalSchema = Dry::Schema.Params do
|
20
|
+
required(:commands).filled
|
21
|
+
end
|
22
|
+
|
16
23
|
DockerSchema = Dry::Schema.Params do
|
17
24
|
optional(:image).maybe(:string)
|
18
25
|
optional(:build)
|
@@ -25,30 +32,49 @@ module Djin
|
|
25
32
|
required(:run).filled
|
26
33
|
end
|
27
34
|
|
35
|
+
LocalSchema = Dry::Schema.Params do
|
36
|
+
required(:run).filled
|
37
|
+
end
|
38
|
+
|
28
39
|
params do
|
29
40
|
optional(:docker).filled do
|
30
41
|
hash(DockerSchema)
|
31
42
|
end
|
43
|
+
|
32
44
|
optional(:"docker-compose").filled do
|
33
45
|
hash(DockerComposeSchema)
|
34
46
|
end
|
47
|
+
|
48
|
+
optional(:local).filled do
|
49
|
+
hash(LocalSchema)
|
50
|
+
end
|
51
|
+
|
52
|
+
optional(:depends_on).each(:str?)
|
35
53
|
end
|
36
54
|
|
37
|
-
rule(:docker, :"docker-compose") do
|
38
|
-
|
55
|
+
rule(:docker, :"docker-compose", :local, :depends_on) do
|
56
|
+
unless values[:docker] || values[:"docker-compose"] || values[:depends_on] || values[:local]
|
57
|
+
key.failure('docker, docker-compose, local or depends_on key is required')
|
58
|
+
end
|
39
59
|
end
|
40
60
|
|
41
|
-
rule(:'docker-compose',docker: [
|
42
|
-
key.failure('image or build param is required for docker tasks') unless values.dig(:docker, :image) ||
|
61
|
+
rule(:depends_on, :'docker-compose', :local, docker: %i[image build]) do
|
62
|
+
key.failure('image or build param is required for docker tasks') unless values.dig(:docker, :image) ||
|
63
|
+
values.dig(:docker, :build) ||
|
64
|
+
values[:'docker-compose'] ||
|
65
|
+
values[:depends_on] ||
|
66
|
+
values.dig(:local, :run)
|
43
67
|
end
|
44
68
|
|
69
|
+
# TODO: Extract validations to command builders
|
70
|
+
|
45
71
|
rule(docker: :build) do
|
46
72
|
result, errors = validate_for(value, Hash => BuildSchema, String => NOT_EMPTY, NilClass => OK)
|
47
73
|
|
48
74
|
key.failure(errors) unless result
|
49
75
|
end
|
50
76
|
|
51
|
-
rule(
|
77
|
+
rule('docker-compose': :run) do
|
52
78
|
result, errors = validate_for(value, Hash => RunSchema, Array => NOT_EMPTY, NilClass => OK)
|
53
79
|
|
54
80
|
key.failure(errors) unless result
|
@@ -60,6 +86,12 @@ module Djin
|
|
60
86
|
key.failure(errors) unless result
|
61
87
|
end
|
62
88
|
|
89
|
+
rule(local: :run) do
|
90
|
+
result, errors = validate_for(value, Hash => RunLocalSchema, Array => NOT_EMPTY, NilClass => OK)
|
91
|
+
|
92
|
+
key.failure(errors) unless result
|
93
|
+
end
|
94
|
+
|
63
95
|
private
|
64
96
|
|
65
97
|
def validate_for(value, validations)
|
data/lib/djin/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: djin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carlos Atkinson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-cli
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.6.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.6.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: dry-struct
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -25,33 +39,47 @@ dependencies:
|
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: 1.3.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: dry-
|
42
|
+
name: dry-validation
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
47
|
+
version: 1.5.1
|
34
48
|
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
54
|
+
version: 1.5.1
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: mustache
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.
|
61
|
+
version: 1.1.1
|
48
62
|
type: :runtime
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.
|
68
|
+
version: 1.1.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: vseries
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.1.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.1.0
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: bundler
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,20 +94,34 @@ dependencies:
|
|
66
94
|
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '2.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: rake
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
72
114
|
requirements:
|
73
115
|
- - "~>"
|
74
116
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
117
|
+
version: '13.0'
|
76
118
|
type: :development
|
77
119
|
prerelease: false
|
78
120
|
version_requirements: !ruby/object:Gem::Requirement
|
79
121
|
requirements:
|
80
122
|
- - "~>"
|
81
123
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
124
|
+
version: '13.0'
|
83
125
|
- !ruby/object:Gem::Dependency
|
84
126
|
name: rspec
|
85
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,7 +137,7 @@ dependencies:
|
|
95
137
|
- !ruby/object:Gem::Version
|
96
138
|
version: '3.0'
|
97
139
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
140
|
+
name: rubocop
|
99
141
|
requirement: !ruby/object:Gem::Requirement
|
100
142
|
requirements:
|
101
143
|
- - ">="
|
@@ -116,15 +158,20 @@ executables:
|
|
116
158
|
extensions: []
|
117
159
|
extra_rdoc_files: []
|
118
160
|
files:
|
161
|
+
- ".github/workflows/ruby.yml"
|
119
162
|
- ".gitignore"
|
120
163
|
- ".rspec"
|
164
|
+
- ".rubocop.yml"
|
165
|
+
- ".rubocop_todo.yml"
|
121
166
|
- ".travis.yml"
|
167
|
+
- CHANGELOG.md
|
122
168
|
- Dockerfile
|
123
169
|
- Gemfile
|
124
170
|
- Gemfile.lock
|
125
171
|
- LICENSE.txt
|
126
172
|
- README.md
|
127
173
|
- Rakefile
|
174
|
+
- Vertofile
|
128
175
|
- bin/console
|
129
176
|
- bin/setup
|
130
177
|
- djin.gemspec
|
@@ -134,12 +181,17 @@ files:
|
|
134
181
|
- exe/djin
|
135
182
|
- lib/djin.rb
|
136
183
|
- lib/djin/cli.rb
|
184
|
+
- lib/djin/config_loader.rb
|
137
185
|
- lib/djin/entities/task.rb
|
138
186
|
- lib/djin/entities/types.rb
|
139
187
|
- lib/djin/executor.rb
|
140
|
-
- lib/djin/extensions/custom_predicates.rb
|
141
188
|
- lib/djin/extensions/hash_extensions.rb
|
142
189
|
- lib/djin/interpreter.rb
|
190
|
+
- lib/djin/interpreter/base_command_builder.rb
|
191
|
+
- lib/djin/interpreter/docker_command_builder.rb
|
192
|
+
- lib/djin/interpreter/docker_compose_command_builder.rb
|
193
|
+
- lib/djin/interpreter/local_command_builder.rb
|
194
|
+
- lib/djin/repositories/task_repository.rb
|
143
195
|
- lib/djin/task_contract.rb
|
144
196
|
- lib/djin/version.rb
|
145
197
|
homepage: https://github.com/catks/djin
|