djin 0.3.0 → 0.7.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.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Djin
2
4
  class Executor
3
5
  def initialize(task_repository: Djin.task_repository)
@@ -1,8 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Djin
2
4
  module HashExtensions
3
5
  refine Hash do
4
6
  def except(*keys)
5
- reject { |key,_| keys.include?(key) }
7
+ reject { |key, _| keys.include?(key) }
8
+ end
9
+
10
+ def symbolize_keys
11
+ map { |key, value| [key.to_sym, value] }.to_h
6
12
  end
7
13
  end
8
14
  end
@@ -1,34 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Djin
2
4
  class Interpreter
3
5
  using Djin::HashExtensions
4
6
 
5
- RESERVED_WORDS = %w[djin_version _default_options].freeze
6
-
7
+ # TODO: Move Errors to ConfigLoader
7
8
  InvalidConfigurationError = Class.new(StandardError)
9
+ InvalidConfigFileError = Class.new(InvalidConfigurationError)
8
10
  MissingVersionError = Class.new(InvalidConfigurationError)
9
11
  VersionNotSupportedError = Class.new(InvalidConfigurationError)
10
12
  InvalidSyntaxError = Class.new(InvalidConfigurationError)
11
13
 
12
14
  class << self
13
- def load!(params)
14
- version = params['djin_version']
15
- raise MissingVersionError, 'Missing djin_version' unless version
16
- raise VersionNotSupportedError, "Version #{version} is not supported, use #{Djin::VERSION} or higher" unless version_supported?(version)
17
-
18
- tasks_params = params.except(*RESERVED_WORDS).reject { |task| task.start_with?('_') }
15
+ def load!(file_config)
19
16
  contract = TaskContract.new
20
17
 
21
- tasks_params.map do |task_name, options|
18
+ file_config.tasks.map do |task_name, options|
22
19
  result = contract.call(options)
23
20
 
24
21
  raise InvalidSyntaxError, { task_name.to_sym => result.errors.to_h } if result.failure?
25
22
 
26
23
  command, build_command = build_commands(options, task_name: task_name)
27
24
 
25
+ raw_command, = build_commands(file_config.raw_tasks[task_name], task_name: task_name)
26
+
28
27
  task_params = {
29
28
  name: task_name,
30
29
  build_command: build_command,
30
+ description: options['description'] || "Runs: #{raw_command}",
31
31
  command: command,
32
+ raw_command: raw_command,
32
33
  depends_on: options['depends_on']
33
34
  }.compact
34
35
 
@@ -42,62 +43,13 @@ module Djin
42
43
  # Validate that only one ot the two is passed
43
44
  docker_params = params['docker']
44
45
  docker_compose_params = params['docker-compose']
46
+ local_params = params['local']
45
47
 
46
- return build_docker_commands(docker_params, task_name: task_name) if docker_params
47
- build_docker_compose_commands(docker_compose_params) if docker_compose_params
48
- end
49
-
50
- def build_docker_commands(params, task_name:)
51
- current_folder_name = Pathname.getwd.basename.to_s
52
- image = params['image'] || "djin_#{current_folder_name}_#{task_name}"
53
-
54
- build_params = params['build']
55
-
56
- if build_params.is_a?(Hash)
57
- build_context = build_params['context']
58
- build_options = build_params['options']
59
- end
60
-
61
- build_context ||= build_params
62
-
63
- run_command, run_options = build_run_params(params['run'])
64
-
65
- command = %Q{docker run #{run_options} #{image} sh -c "#{run_command}"}.squeeze(' ')
66
-
67
- build_command = "docker build #{build_context} #{build_options} -t #{image}".squeeze(' ') if build_context
68
-
69
- [command, build_command]
70
- end
71
-
72
- def build_docker_compose_commands(params)
73
- service = params['service']
74
-
75
- compose_options = params['options']
76
-
77
- run_command, run_options = build_run_params(params['run'])
78
-
79
- [%Q{docker-compose #{compose_options} run #{run_options} #{service} sh -c "#{run_command}"}.squeeze(' '), nil]
80
- end
81
-
82
- def build_run_params(run_params)
83
- run_command = run_params
84
-
85
- if run_params.is_a?(Hash)
86
- run_command = run_params['commands']
87
- run_options = run_params['options']
88
- end
89
-
90
- run_command = run_command.join(' && ') if run_command.is_a?(Array)
91
-
92
- [run_command, run_options]
93
- end
94
-
95
- def validate_version!(version)
96
-
97
- end
48
+ # TODO: Refactor to use chain of responsability
49
+ return DockerCommandBuilder.call(docker_params, task_name: task_name) if docker_params
50
+ return DockerComposeCommandBuilder.call(docker_compose_params) if docker_compose_params
98
51
 
99
- def version_supported?(version)
100
- Vseries::SemanticVersion.new(Djin::VERSION) >= Vseries::SemanticVersion.new(version)
52
+ LocalCommandBuilder.call(local_params) if local_params
101
53
  end
102
54
  end
103
55
  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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Djin
4
+ class Interpreter
5
+ class LocalCommandBuilder < BaseCommandBuilder
6
+ def call(params, **_)
7
+ build_run_params(params['run'])
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class TaskRepository
2
4
  def initialize(tasks = [])
3
5
  @tasks = tasks
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Djin
2
4
  class TaskContract < Dry::Validation::Contract
3
- NOT_EMPTY = -> (value) { ! value.empty? }
4
- OK = -> (_) { true }
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,32 +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
35
47
 
48
+ optional(:local).filled do
49
+ hash(LocalSchema)
50
+ end
51
+
36
52
  optional(:depends_on).each(:str?)
37
53
  end
38
54
 
39
- rule(:docker, :"docker-compose", :depends_on) do
40
- key.failure('docker, docker-compose or depends_on key is required') unless values[:docker] || values[:"docker-compose"] || values[:depends_on]
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
41
59
  end
42
60
 
43
- rule(:depends_on, :'docker-compose',docker: [:image, :build]) do
44
- key.failure('image or build param is required for docker tasks') unless values.dig(:docker, :image) || values.dig(:docker, :build) || values[:'docker-compose'] || values[:depends_on]
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)
45
67
  end
46
68
 
69
+ # TODO: Extract validations to command builders
70
+
47
71
  rule(docker: :build) do
48
72
  result, errors = validate_for(value, Hash => BuildSchema, String => NOT_EMPTY, NilClass => OK)
49
73
 
50
74
  key.failure(errors) unless result
51
75
  end
52
76
 
53
- rule(:'docker-compose' => :run) do
77
+ rule('docker-compose': :run) do
54
78
  result, errors = validate_for(value, Hash => RunSchema, Array => NOT_EMPTY, NilClass => OK)
55
79
 
56
80
  key.failure(errors) unless result
@@ -62,6 +86,12 @@ module Djin
62
86
  key.failure(errors) unless result
63
87
  end
64
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
+
65
95
  private
66
96
 
67
97
  def validate_for(value, validations)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Djin
2
- VERSION = "0.3.0"
4
+ VERSION = '0.7.0'
3
5
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: djin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.7.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-05-19 00:00:00.000000000 Z
11
+ date: 2020-08-21 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
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-equalizer
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.3.0
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: dry-struct
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -25,33 +53,33 @@ dependencies:
25
53
  - !ruby/object:Gem::Version
26
54
  version: 1.3.0
27
55
  - !ruby/object:Gem::Dependency
28
- name: dry-cli
56
+ name: dry-validation
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - "~>"
32
60
  - !ruby/object:Gem::Version
33
- version: 0.5.0
61
+ version: 1.5.1
34
62
  type: :runtime
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
- version: 0.5.0
68
+ version: 1.5.1
41
69
  - !ruby/object:Gem::Dependency
42
- name: dry-validation
70
+ name: mustache
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: 1.5.0
75
+ version: 1.1.1
48
76
  type: :runtime
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: 1.5.0
82
+ version: 1.1.1
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: vseries
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +108,20 @@ dependencies:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
110
  version: '2.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
83
125
  - !ruby/object:Gem::Dependency
84
126
  name: rake
85
127
  requirement: !ruby/object:Gem::Requirement
@@ -109,7 +151,7 @@ dependencies:
109
151
  - !ruby/object:Gem::Version
110
152
  version: '3.0'
111
153
  - !ruby/object:Gem::Dependency
112
- name: byebug
154
+ name: rubocop
113
155
  requirement: !ruby/object:Gem::Requirement
114
156
  requirements:
115
157
  - - ">="
@@ -133,6 +175,8 @@ files:
133
175
  - ".github/workflows/ruby.yml"
134
176
  - ".gitignore"
135
177
  - ".rspec"
178
+ - ".rubocop.yml"
179
+ - ".rubocop_todo.yml"
136
180
  - ".travis.yml"
137
181
  - CHANGELOG.md
138
182
  - Dockerfile
@@ -141,6 +185,7 @@ files:
141
185
  - LICENSE.txt
142
186
  - README.md
143
187
  - Rakefile
188
+ - Vertofile
144
189
  - bin/console
145
190
  - bin/setup
146
191
  - djin.gemspec
@@ -150,12 +195,17 @@ files:
150
195
  - exe/djin
151
196
  - lib/djin.rb
152
197
  - lib/djin/cli.rb
198
+ - lib/djin/config_loader.rb
199
+ - lib/djin/entities/file_config.rb
153
200
  - lib/djin/entities/task.rb
154
201
  - lib/djin/entities/types.rb
155
202
  - lib/djin/executor.rb
156
- - lib/djin/extensions/custom_predicates.rb
157
203
  - lib/djin/extensions/hash_extensions.rb
158
204
  - lib/djin/interpreter.rb
205
+ - lib/djin/interpreter/base_command_builder.rb
206
+ - lib/djin/interpreter/docker_command_builder.rb
207
+ - lib/djin/interpreter/docker_compose_command_builder.rb
208
+ - lib/djin/interpreter/local_command_builder.rb
159
209
  - lib/djin/repositories/task_repository.rb
160
210
  - lib/djin/task_contract.rb
161
211
  - lib/djin/version.rb
@@ -178,7 +228,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
228
  - !ruby/object:Gem::Version
179
229
  version: '0'
180
230
  requirements: []
181
- rubygems_version: 3.0.3
231
+ rubyforge_project:
232
+ rubygems_version: 2.7.6
182
233
  signing_key:
183
234
  specification_version: 4
184
235
  summary: djin is a make-like utility for docker containers