djin 0.5.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a40e01844f1a9b034d592696cf6af915622c834a9e231f0c56209c03d11660ad
4
- data.tar.gz: facbe98a7362ec81b5f819af27a554807790a1445c7cc47161a85d5f0d5b7276
3
+ metadata.gz: e763f1dad923699c768e998f4dd5caa53ac7a8b0ad4d7acd84c708effdcb627c
4
+ data.tar.gz: 9ab841b39557b775ec7832a3c1cd0befaf983e89ba931459b89a52d5a5df0bda
5
5
  SHA512:
6
- metadata.gz: 72f5ea393c498311acf74a482b02670d94f11704a75032e31e914e68b947de694bec077ba03e22b75fd73c9307f9f98242ea35009f5341c2ab9b3d1af0314901
7
- data.tar.gz: 4bf6650c80d7defc1dd3b2850c21b74c54ecdb7f4dd374c0a98dd3bfe2610c7175f7102a71de53d6367a4dfd1fcdae47437a2db24bee83bc1c195a4c0fea9da7
6
+ metadata.gz: e22bc8b7d3deb635180db1d5c49f1e7d5df1eef3f1b4184b13d65b24eaa45b6f716edfae1bb4799abfe4d4255387958f5f078d931c72cf0b5e407abf9d85ee51
7
+ data.tar.gz: e603ff5f1bab170ed9758331e7c64e39f1832424fc2c0e069aeef2ce85372081ccc15e1a2080cae3c5edc4c8d8fd1286643e4aaa02616249f7173c6d660ffcf2
@@ -14,10 +14,12 @@ jobs:
14
14
  - uses: actions/setup-ruby@v1
15
15
  with:
16
16
  ruby-version: ${{ matrix.ruby }}
17
- - run: |
17
+ - name: Install Gems
18
+ run: |
18
19
  gem install bundler
19
20
  bundle install --jobs 4 --retry 3
20
- bundle exec rake
21
+ - name: Run tests
22
+ run: bundle exec rake
21
23
 
22
24
  lint:
23
25
  runs-on: ubuntu-16.04
@@ -27,8 +29,10 @@ jobs:
27
29
  - uses: actions/setup-ruby@v1
28
30
  with:
29
31
  ruby-version: '2.6'
30
- - run: |
32
+ - name: Install Gems
33
+ run: |
31
34
  gem install bundler
32
35
  bundle install --jobs 4 --retry 3
33
- bundle exec rubocop
36
+ - name: Rubocop
37
+ run: bundle exec rubocop
34
38
 
@@ -1,3 +1,18 @@
1
+ ## 0.9.0 - 17/09/2020
2
+ * [FEATURE] Include Option
3
+
4
+ ## 0.8.0 - 31/08/2020
5
+ * [FEATURE] Adds aliases option
6
+
7
+ ## 0.7.0 - 20/08/2020
8
+ * [FEATURE] Adds description option
9
+
10
+ ## 0.6.1 - 11/08/2020
11
+ * [TECH] Better Error Handling
12
+
13
+ ## 0.6.0 - 22/07/2020
14
+ * [FEATURE] Djin Variables
15
+
1
16
  ## 0.5.0 - 13/07/2020
2
17
  * [FEATURE] Adds local command task
3
18
  * [FEATURE] Template Args for entire djin.yml
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- djin (0.5.0)
4
+ djin (0.9.0)
5
5
  dry-cli (~> 0.6.0)
6
+ dry-equalizer (~> 0.3.0)
6
7
  dry-struct (~> 1.3.0)
7
8
  dry-validation (~> 1.5.1)
8
9
  mustache (~> 1.1.1)
@@ -13,7 +14,7 @@ GEM
13
14
  specs:
14
15
  ast (2.4.1)
15
16
  byebug (11.1.1)
16
- concurrent-ruby (1.1.6)
17
+ concurrent-ruby (1.1.7)
17
18
  diff-lcs (1.3)
18
19
  dry-cli (0.6.0)
19
20
  concurrent-ruby (~> 1.0)
@@ -29,11 +30,11 @@ GEM
29
30
  dry-equalizer (0.3.0)
30
31
  dry-inflector (0.2.0)
31
32
  dry-initializer (3.0.3)
32
- dry-logic (1.0.6)
33
+ dry-logic (1.0.7)
33
34
  concurrent-ruby (~> 1.0)
34
35
  dry-core (~> 0.2)
35
36
  dry-equalizer (~> 0.2)
36
- dry-schema (1.5.2)
37
+ dry-schema (1.5.4)
37
38
  concurrent-ruby (~> 1.0)
38
39
  dry-configurable (~> 0.8, >= 0.8.3)
39
40
  dry-core (~> 0.4)
@@ -53,13 +54,13 @@ GEM
53
54
  dry-equalizer (~> 0.3)
54
55
  dry-inflector (~> 0.1, >= 0.1.2)
55
56
  dry-logic (~> 1.0, >= 1.0.2)
56
- dry-validation (1.5.1)
57
+ dry-validation (1.5.6)
57
58
  concurrent-ruby (~> 1.0)
58
59
  dry-container (~> 0.7, >= 0.7.1)
59
60
  dry-core (~> 0.4)
60
61
  dry-equalizer (~> 0.2)
61
62
  dry-initializer (~> 3.0)
62
- dry-schema (~> 1.5)
63
+ dry-schema (~> 1.5, >= 1.5.2)
63
64
  ice_nine (0.11.2)
64
65
  mustache (1.1.1)
65
66
  parallel (1.19.2)
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Djin
2
2
 
3
3
  ![](https://github.com/catks/djin/workflows/Ruby/badge.svg?branch=master)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/824a2e78399813543212/maintainability)](https://codeclimate.com/github/catks/djin/maintainability)
4
5
 
5
6
  Djin is a make-like utility for docker containers
6
7
 
@@ -25,139 +26,253 @@ If you use Rbenv you can install djin only once and create a alias in your .basr
25
26
  To use djin first you need to create a djin.yml file:
26
27
 
27
28
  ```yaml
28
- djin_version: '0.5.0'
29
-
30
- # With a docker image
31
- script:
32
- docker:
33
- image: "ruby:2.6"
34
- run:
35
- commands:
36
- - "ruby /scripts/my_ruby_script.rb"
37
- options: "--rm -v $(pwd)/my_ruby_script.rb:/scripts/my_ruby_script.rb"
38
-
39
- # Using a docker-compose service
40
- test:
41
- docker-compose:
42
- service: app
43
- run:
44
- commands: rspec
45
- options: "--rm"
46
-
29
+ djin_version: '0.9.0'
30
+
31
+ tasks:
32
+ # With a docker image
33
+ script:
34
+ docker:
35
+ image: "ruby:2.6"
36
+ run:
37
+ commands:
38
+ - "ruby /scripts/my_ruby_script.rb"
39
+ options: "--rm -v $(pwd)/my_ruby_script.rb:/scripts/my_ruby_script.rb"
40
+
41
+ # Using a docker-compose service
42
+ test:
43
+ docker-compose:
44
+ service: app
45
+ run:
46
+ commands: rspec
47
+ options: "--rm"
48
+ aliases: # Optional Array of strings
49
+ - rspec
47
50
  ```
48
51
 
49
52
  You can also set task dependencies with depends_on option:
50
53
 
51
54
 
52
55
  ```yaml
53
- djin_version: '0.5.0'
56
+ djin_version: '0.9.0'
54
57
 
55
58
  _default_run_options: &default_run_options
56
59
  options: "--rm"
57
60
 
58
- "db:create":
59
- docker-compose:
60
- service: app
61
- run:
62
- commands: rake db:create
63
- <<: *default_run_options
64
-
65
- "db:migrate":
66
- docker-compose:
67
- service: app
68
- run:
69
- commands: rake db:migrate
70
- <<: *default_run_options
71
-
72
- "db:setup":
73
- depends_on:
74
- - "db:create"
75
- - "db:migrate"
76
-
61
+ tasks:
62
+ "db:create":
63
+ docker-compose:
64
+ service: app
65
+ run:
66
+ commands: rake db:create
67
+ <<: *default_run_options
68
+
69
+ "db:migrate":
70
+ docker-compose:
71
+ service: app
72
+ run:
73
+ commands: rake db:migrate
74
+ <<: *default_run_options
75
+
76
+ "db:setup":
77
+ depends_on:
78
+ - "db:create"
79
+ - "db:migrate"
77
80
  ```
78
81
 
79
82
  Or mix local commands and docker/docker-compose commands:
80
83
 
81
84
  ```yaml
82
- djin_version: '0.5.0'
85
+ djin_version: '0.9.0'
83
86
 
84
87
  _default_run_options: &default_run_options
85
88
  options: "--rm"
86
89
 
87
- "db:create":
88
- docker-compose:
89
- service: app
90
- run:
91
- commands: rake db:create
92
- <<: *default_run_options
93
-
94
- "db:migrate":
95
- docker-compose:
96
- service: app
97
- run:
98
- commands: rake db:migrate
99
- <<: *default_run_options
100
-
101
- "setup:copy_samples":
102
- local:
103
- run:
104
- - cp config/database.yml.sample config/database.yml
105
-
106
- "setup":
107
- depends_on:
108
- - "setup:copy_samples"
109
- - "db:create"
110
- - "db:migrate"
111
-
90
+ tasks:
91
+ "db:create":
92
+ docker-compose:
93
+ service: app
94
+ run:
95
+ commands: rake db:create
96
+ <<: *default_run_options
97
+
98
+ "db:migrate":
99
+ docker-compose:
100
+ service: app
101
+ run:
102
+ commands: rake db:migrate
103
+ <<: *default_run_options
104
+
105
+ "setup:copy_samples":
106
+ local:
107
+ run:
108
+ - cp config/database.yml.sample config/database.yml
109
+
110
+ "setup":
111
+ depends_on:
112
+ - "setup:copy_samples"
113
+ - "db:create"
114
+ - "db:migrate"
112
115
  ```
113
116
 
114
117
  After that you can run `djin {{task_name}}`, like `djin script` or `djin test`
115
118
 
116
- ## Using Environment variables and custom args in djin.yml run tasks
119
+ ## Using Environment variables, custom variables and custom args in djin.yml tasks
117
120
 
118
121
  You can also use environment variables using the '{{YOUR_ENV_HERE}}' syntax, like so:
119
122
 
120
123
  ```yaml
121
- djin_version: '0.5.0'
124
+ djin_version: '0.9.0'
122
125
 
123
126
  _default_run_options: &default_run_options
124
127
  options: "--rm"
125
128
 
126
- "db:migrate":
127
- docker-compose:
128
- service: app
129
- run:
130
- commands: ENV={{ENV}} rake db:migrate
131
- <<: *default_run_options
129
+ tasks:
130
+ "db:migrate":
131
+ docker-compose:
132
+ service: app
133
+ run:
134
+ commands: ENV={{ENV}} rake db:migrate
135
+ <<: *default_run_options
132
136
 
133
137
  ```
134
138
 
135
- It's also possible to pass custom arguments to the command, wich means is possible to make a djin task act like the command itself:
139
+ Or define some variables to use in multiple locations
140
+ ```yaml
141
+ djin_version: '0.9.0'
142
+
143
+ _default_run_options: &default_run_options
144
+ options: "--rm"
145
+
146
+ variables:
147
+ my_ssh_user: user
148
+ some_host: test.local
149
+
150
+ tasks:
151
+ "some_host:ssh":
152
+ local:
153
+ run:
154
+ - ssh {{my_ssh_user}}@{{some_host}}
155
+
156
+ "some_host:logs":
157
+ local:
158
+ run:
159
+ - ssh -t {{my_ssh_user}}@{{some_host}} 'tail -f /var/log/syslog'
160
+ ```
161
+
162
+ It's also possible to pass custom arguments to the command, which means is possible to make a djin task act like the command itself:
136
163
 
137
164
  ```yaml
138
- djin_version: '0.5.0'
165
+ djin_version: '0.9.0'
139
166
 
140
167
  _default_run_options: &default_run_options
141
168
  options: "--rm"
142
169
 
143
- "rubocop":
144
- docker-compose:
145
- service: app
146
- run:
147
- commands: rubocop {{args}}
148
- <<: *default_run_options
170
+ tasks:
171
+ "rubocop":
172
+ docker-compose:
173
+ service: app
174
+ run:
175
+ commands: rubocop {{args}}
176
+ <<: *default_run_options
177
+ aliases:
178
+ - lint
149
179
 
150
180
  ```
151
181
 
152
182
  With that you can pass custom args after `--`, eg: `djin rubocop -- --parallel`, which wil make djin runs `rubocop --parallel` inside the service `app`.
153
183
 
154
- Under the hood djin uses [Mustache](https://mustache.github.io/), so you can use other features like conditionals: `{{#IS_ENABLE}} Enabled {{/IS_ENABLE}}` (for args use the `args?`, eg: {{#args} {{args}} --and-other-thing{{/args?}}), to see more more options you can access this [Link](https://mustache.github.io/mustache.5.html)
184
+ Under the hood djin uses [Mustache](https://mustache.github.io/), so you can use other features like conditionals: `{{#IS_ENABLE}} Enabled {{/IS_ENABLE}}` (for args use the `args?`, eg: `{{#args?}} {{args}} --and-other-thing{{/args?}}`), to see more more options you can access this [Link](https://mustache.github.io/mustache.5.html)
185
+
186
+ ### Reusing tasks
187
+
188
+ If you have multiple tasks with similar behaviour and with small differences you can use the `include` keyword, so this:
189
+
190
+ ```yaml
191
+ djin_version: '0.9.0'
192
+
193
+ tasks:
194
+ "host1:ssh":
195
+ local:
196
+ run:
197
+ - ssh my_user@host1.com.br
198
+
199
+ "host1:restart":
200
+ local:
201
+ run:
202
+ - ssh -t my_user@host1.com.br restart
203
+
204
+ "host1:logs":
205
+ local:
206
+ run:
207
+ - ssh -t my_user@host1.com.br tail -f /var/log/my_log
208
+
209
+ "host2:ssh":
210
+ local:
211
+ run:
212
+ - ssh my_user@host2.com.br
213
+
214
+ "host2:restart":
215
+ local:
216
+ run:
217
+ - ssh -t my_user@host2.com.br restart
218
+
219
+ "host2:logs":
220
+ local:
221
+ run:
222
+ - ssh -t my_user@host2.com.br tail -f /var/log/my_file
223
+
224
+ ```
225
+
226
+ can become this:
227
+
228
+ ```yaml
229
+ # djin.yml
230
+ djin_version: '0.9.0'
231
+
232
+ include:
233
+ - file: '.djin/server_tasks.yml'
234
+ context:
235
+ variables:
236
+ namespace: host1
237
+ host: host1.com
238
+ ssh_user: my_user
239
+
240
+ - file: '.djin/server_tasks.yml'
241
+ context:
242
+ variables:
243
+ namespace: host2
244
+ host: host2.com
245
+ ssh_user: my_user
246
+
247
+ ```
248
+
249
+
250
+ ```yaml
251
+ # .djin/server_tasks.yml
252
+ djin_version: '0.9.0'
253
+
254
+ tasks:
255
+ "{{namespace}}:ssh":
256
+ local:
257
+ run:
258
+ - ssh {{ssh_user}}@{{host}}
259
+
260
+ "{{namespace}}:restart":
261
+ local:
262
+ run:
263
+ - ssh -t {{ssh_user}}@{{host}} restart
264
+
265
+ "{{namespace}}:logs":
266
+ local:
267
+ run:
268
+ - ssh -t {{ssh_user}}@{{host}} tail -f /var/log/my_log
269
+ ```
155
270
 
156
271
  ## Development
157
272
 
158
273
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
159
274
 
160
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
275
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, run `djin release -- {{increment_option}}` (where {{incremment_option}} can be `--patch`, `--minor` or `major`), which will change version, update the CHANGELOG.md, create a new commit, create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
161
276
 
162
277
  ## TODO:
163
278
 
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ['lib']
29
29
 
30
30
  spec.add_dependency 'dry-cli', '~> 0.6.0'
31
+ spec.add_dependency 'dry-equalizer', '~> 0.3.0'
31
32
  spec.add_dependency 'dry-struct', '~> 1.3.0'
32
33
  spec.add_dependency 'dry-validation', '~> 1.5.1'
33
34
  spec.add_dependency 'mustache', '~> 1.1.1'
data/djin.yml CHANGED
@@ -1,33 +1,35 @@
1
- djin_version: '0.5.0'
1
+ djin_version: '0.9.0'
2
2
 
3
3
  _default_run_options: &default_run_options
4
4
  options: "--rm --entrypoint=''"
5
5
 
6
- test:
7
- docker-compose:
8
- service: app
9
- run:
10
- commands: "cd /usr/src/djin && rspec {{args}}"
11
- <<: *default_run_options
12
-
13
- sh:
14
- docker-compose:
15
- service: app
16
- run:
17
- commands: "sh"
18
- <<: *default_run_options
19
- run:
20
- docker-compose:
21
- service: app
22
- run:
23
- commands: "sh -c '{{args}}'"
24
- <<: *default_run_options
25
-
26
- release:
27
- local:
28
- run:
29
- - verto tag up {{args}}
30
- - rake release
31
-
6
+ tasks:
7
+ test:
8
+ description: Runs Specs
9
+ docker-compose:
10
+ service: app
11
+ run:
12
+ commands: "cd /usr/src/djin && rspec {{args}}"
13
+ <<: *default_run_options
14
+ aliases:
15
+ - rspec
32
16
 
17
+ sh:
18
+ description: Enter app service shell
19
+ docker-compose:
20
+ service: app
21
+ run:
22
+ commands: "sh"
23
+ <<: *default_run_options
24
+ run:
25
+ docker-compose:
26
+ service: app
27
+ run:
28
+ commands: "sh -c '{{args}}'"
29
+ <<: *default_run_options
33
30
 
31
+ release:
32
+ local:
33
+ run:
34
+ - verto tag up {{args}}
35
+ - bundle exec rake release
@@ -1,29 +1,35 @@
1
1
  ---
2
- djin_version: '0.5.0'
2
+ djin_version: '0.9.0'
3
3
 
4
- default:
5
- docker:
6
- image: "ruby:2.5"
7
- run:
8
- - "ruby -e 'puts \\\" Hello\\\"'"
9
- test:
10
- docker-compose:
11
- service: app
12
- run:
13
- commands: rspec
14
- options: "--rm"
4
+ include:
5
+ - file: 'djin_lib/test.yml'
6
+ context:
7
+ variables:
8
+ namespace: 'test:'
15
9
 
16
- script:
17
- docker:
18
- image: "ruby:2.6"
19
- run:
20
- commands:
21
- - "ruby /scripts/my_ruby_script.rb"
22
- options: "--rm -v $(pwd)/my_ruby_script.rb:/scripts/my_ruby_script.rb"
10
+ - file: 'djin_lib/test.yml'
11
+ context:
12
+ variables:
13
+ namespace: 'test2:'
23
14
 
24
15
 
25
- with_build:
26
- docker:
27
- build: .
28
- run:
29
- - "ruby -e 'puts \" Hello\"'"
16
+ tasks:
17
+ default:
18
+ docker:
19
+ image: "ruby:2.5"
20
+ run:
21
+ - "ruby -e 'puts \\\" Hello\\\"'"
22
+
23
+ script:
24
+ docker:
25
+ image: "ruby:2.6"
26
+ run:
27
+ commands:
28
+ - "ruby /scripts/my_ruby_script.rb"
29
+ options: "--rm -v $(pwd)/my_ruby_script.rb:/scripts/my_ruby_script.rb"
30
+
31
+ with_build:
32
+ docker:
33
+ build: .
34
+ run:
35
+ - "ruby -e 'puts \" Hello\"'"
@@ -0,0 +1,12 @@
1
+ djin_version: '0.9.0'
2
+
3
+ _default_run_options: &default_run_options
4
+ options: "--rm --entrypoint=''"
5
+
6
+ tasks:
7
+ "{{namespace}}unit":
8
+ docker-compose:
9
+ service: app
10
+ run:
11
+ commands: "cd /usr/src/djin && rspec {{args}}"
12
+ <<: *default_run_options
@@ -0,0 +1,17 @@
1
+ djin_version: '0.8.0'
2
+
3
+ tasks:
4
+ "{{namespace}}:ssh":
5
+ local:
6
+ run:
7
+ - ssh {{ssh_user}}@{{host}}
8
+
9
+ "{{namespace}}:restart":
10
+ local:
11
+ run:
12
+ - ssh -t {{ssh_user}}@{{host}} restart
13
+
14
+ "{{namespace}}:logs":
15
+ local:
16
+ run:
17
+ - ssh -t {{ssh_user}}@{{host}} tail -f /var/log/my_log
@@ -0,0 +1,22 @@
1
+ djin_version: '0.9.0'
2
+
3
+ include:
4
+ - file: '.djin/server_tasks.yml'
5
+ context:
6
+ variables:
7
+ namespace: host1
8
+ host: host1.com
9
+ ssh_user: my_user
10
+
11
+ - file: '.djin/server_tasks.yml'
12
+ context:
13
+ variables:
14
+ namespace: host2
15
+ host: host2.com
16
+ ssh_user: my_user
17
+
18
+ tasks:
19
+ hello_command:
20
+ local:
21
+ run:
22
+ - echo 'Hello Djin'
@@ -11,32 +11,35 @@ require 'mustache'
11
11
  require 'djin/extensions/hash_extensions'
12
12
  require 'djin/entities/types'
13
13
  require 'djin/entities/task'
14
+ require 'djin/entities/file_config'
14
15
  require 'djin/interpreter/base_command_builder'
15
16
  require 'djin/interpreter/docker_command_builder'
16
17
  require 'djin/interpreter/docker_compose_command_builder'
17
18
  require 'djin/interpreter/local_command_builder'
18
19
  require 'djin/interpreter'
19
- require 'djin/template_renderer'
20
+ require 'djin/config_loader'
20
21
  require 'djin/executor'
21
22
  require 'djin/cli'
22
23
  require 'djin/task_contract'
23
24
  require 'djin/repositories/task_repository'
25
+ require 'djin/memory_cache'
24
26
 
25
27
  module Djin
26
28
  class Error < StandardError; end
27
29
 
28
- def self.load_tasks!(path = Pathname.getwd.join('djin.yml'))
29
- abort 'Error: djin.yml not found' unless path.exist?
30
+ def self.load_tasks!(file_path = Pathname.getwd.join('djin.yml'))
31
+ abort 'Error: djin.yml not found' unless file_path.exist?
30
32
 
31
- rendered_djin_file = TemplateRenderer.render(path.read)
32
- djin_config = YAML.safe_load(rendered_djin_file, [], [], true)
33
+ file_config = ConfigLoader.load!(file_path)
33
34
 
34
- tasks = Interpreter.load!(djin_config)
35
+ # TODO: Make all tasks be under 'tasks' key, passing only the tasks here
36
+ tasks = Interpreter.load!(file_config)
35
37
 
36
38
  @task_repository = TaskRepository.new(tasks)
37
39
  CLI.load_tasks!(tasks)
38
40
  rescue Djin::Interpreter::InvalidConfigurationError => e
39
- abort(e.message)
41
+ error_name = e.class.name.split('::').last
42
+ abort("[#{error_name}] #{e.message}")
40
43
  end
41
44
 
42
45
  def self.tasks
@@ -46,4 +49,12 @@ module Djin
46
49
  def self.task_repository
47
50
  @task_repository ||= TaskRepository.new
48
51
  end
52
+
53
+ def self.cache
54
+ @cache ||= MemoryCache.new
55
+ end
56
+
57
+ def self.root_path
58
+ Pathname.new File.expand_path(File.dirname(__FILE__) + '/..')
59
+ end
49
60
  end
@@ -7,7 +7,7 @@ module Djin
7
7
  def self.load_tasks!(tasks)
8
8
  tasks.each do |task|
9
9
  command = Class.new(Dry::CLI::Command) do
10
- desc "Runs: #{task.command}"
10
+ desc task.description
11
11
 
12
12
  define_method(:task) { task }
13
13
 
@@ -16,7 +16,7 @@ module Djin
16
16
  end
17
17
  end
18
18
 
19
- register task.name, command
19
+ register(task.name, command, aliases: task.aliases)
20
20
  end
21
21
  end
22
22
 
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Djin
4
+ # TODO: Refactor this class to be the Interpreter
5
+ # class and use the current interpreter as
6
+ # a TaskLoader
7
+ class ConfigLoader
8
+ using Djin::HashExtensions
9
+ RESERVED_WORDS = %w[djin_version variables tasks include].freeze
10
+
11
+ def self.load!(template_file, runtime_config: {})
12
+ new(template_file, runtime_config: runtime_config).load!
13
+ end
14
+
15
+ def initialize(template_file, runtime_config: {})
16
+ @template_file = template_file
17
+ @template_file_content = Djin.cache.fetch(template_file.realpath.to_s) { template_file.read }
18
+ @runtime_config = runtime_config
19
+ end
20
+
21
+ def load!
22
+ validate_version!
23
+
24
+ file_config
25
+ end
26
+
27
+ private
28
+
29
+ def file_config
30
+ FileConfig.new(
31
+ djin_version: version,
32
+ variables: variables,
33
+ tasks: tasks,
34
+ raw_tasks: raw_tasks
35
+ )
36
+ end
37
+
38
+ def version
39
+ # TODO: Deprecates djin_version and use version instead
40
+ @version || raw_djin_config['djin_version']
41
+ end
42
+
43
+ def variables
44
+ @variables ||= included_variables.merge(raw_djin_config['variables']&.symbolize_keys || {})
45
+ end
46
+
47
+ def tasks
48
+ included_tasks.merge(rendered_djin_config['tasks'] || legacy_tasks)
49
+ end
50
+
51
+ def raw_tasks
52
+ included_raw_tasks.merge(raw_djin_config['tasks'] || legacy_raw_tasks)
53
+ end
54
+
55
+ def legacy_tasks
56
+ warn '[DEPRECATED] Root tasks are deprecated and will be removed in Djin 1.0.0,' \
57
+ ' put the tasks under \'tasks\' keyword'
58
+
59
+ rendered_djin_config.except(*RESERVED_WORDS).reject { |task| task.start_with?('_') }
60
+ end
61
+
62
+ def legacy_raw_tasks
63
+ raw_djin_config.except(*RESERVED_WORDS).reject { |task| task.start_with?('_') }
64
+ end
65
+
66
+ def included_variables
67
+ return {} unless included_config
68
+
69
+ included_config.variables
70
+ end
71
+
72
+ def included_tasks
73
+ return {} unless included_config
74
+
75
+ included_config.tasks
76
+ end
77
+
78
+ def included_raw_tasks
79
+ return {} unless included_config
80
+
81
+ included_config.raw_tasks
82
+ end
83
+
84
+ def included_config
85
+ @included_config ||= raw_djin_config['include']&.map do |tasks_reference|
86
+ external_config_file = Pathname.new(tasks_reference['file'])
87
+
88
+ ConfigLoader.load!(external_config_file, runtime_config: tasks_reference['context'] || {})
89
+ end&.reduce(:deep_merge)
90
+ rescue Errno::ENOENT => e
91
+ raise Interpreter::InvalidConfigFileError, e.message
92
+ end
93
+
94
+ def args
95
+ index = ARGV.index('--')
96
+
97
+ return [] unless index
98
+
99
+ ARGV.slice((index + 1)..ARGV.size)
100
+ end
101
+
102
+ def env
103
+ @env ||= ENV.to_h.symbolize_keys
104
+ end
105
+
106
+ def raw_djin_config
107
+ @raw_djin_config ||= yaml_load(@template_file_content).deep_merge(@runtime_config)
108
+ rescue Psych::SyntaxError => e
109
+ raise Interpreter::InvalidConfigFileError, "File: #{@template_file.realpath}\n #{e.message}"
110
+ end
111
+
112
+ def rendered_djin_config
113
+ @rendered_djin_config ||= begin
114
+ locals = env.merge(variables)
115
+
116
+ rendered_yaml = Mustache.render(@template_file_content,
117
+ args: args.join(' '),
118
+ args?: args.any?,
119
+ **locals)
120
+ yaml_load(rendered_yaml).merge(@runtime_config)
121
+ end
122
+ end
123
+
124
+ def yaml_load(text)
125
+ YAML.safe_load(text, [], [], true)
126
+ end
127
+
128
+ def validate_version!
129
+ raise Interpreter::MissingVersionError, 'Missing djin_version' unless version
130
+
131
+ return if file_config.version_supported?
132
+
133
+ raise Interpreter::VersionNotSupportedError, "Version #{version} is not supported, use #{Djin::VERSION} or higher"
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Djin
4
+ class FileConfig < Dry::Struct
5
+ using HashExtensions
6
+
7
+ attribute :djin_version, Types::String
8
+ attribute :variables, Types::Hash.optional.default({}.freeze)
9
+ attribute :tasks, Types::Hash
10
+ attribute :raw_tasks, Types::Hash
11
+ # TODO: Add env and args
12
+
13
+ include Dry::Equalizer(:djin_version, :variables, :tasks, :raw_tasks)
14
+
15
+ def version_supported?
16
+ Vseries::SemanticVersion.new(Djin::VERSION) >= Vseries::SemanticVersion.new(djin_version)
17
+ end
18
+
19
+ def merge(file_config)
20
+ merged_hash = to_h.merge(file_config.to_h)
21
+
22
+ FileConfig.new(merged_hash)
23
+ end
24
+
25
+ def deep_merge(file_config)
26
+ merged_hash = to_h.deep_merge(file_config.to_h)
27
+
28
+ FileConfig.new(merged_hash)
29
+ end
30
+ end
31
+ end
@@ -6,12 +6,10 @@ module Djin
6
6
  attribute :description, Types::String.optional.default(nil)
7
7
  attribute :build_command, Types::String.optional.default(nil)
8
8
  attribute :command, Types::String.optional.default(nil)
9
+ attribute :raw_command, Types::String.optional.default(nil)
10
+ attribute :aliases, Types::Array.of(Types::String).optional.default([].freeze)
9
11
  attribute :depends_on, Types::Array.of(Types::String).optional.default([].freeze)
10
12
 
11
- def ==(other)
12
- name == other.name &&
13
- command == other.command &&
14
- build_command == other.build_command
15
- end
13
+ include Dry::Equalizer(:name, :command, :build_command)
16
14
  end
17
15
  end
@@ -6,6 +6,24 @@ module Djin
6
6
  def except(*keys)
7
7
  reject { |key, _| keys.include?(key) }
8
8
  end
9
+
10
+ def symbolize_keys
11
+ map { |key, value| [key.to_sym, value] }.to_h
12
+ end
13
+
14
+ def deep_merge(other_hash)
15
+ dup.deep_merge!(other_hash)
16
+ end
17
+
18
+ def deep_merge!(other_hash)
19
+ merge!(other_hash) do |_, this_val, other_val|
20
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
21
+ this_val.deep_merge(other_val)
22
+ else
23
+ other_val
24
+ end
25
+ end
26
+ end
9
27
  end
10
28
  end
11
29
  end
@@ -4,41 +4,46 @@ module Djin
4
4
  class Interpreter
5
5
  using Djin::HashExtensions
6
6
 
7
- RESERVED_WORDS = %w[djin_version _default_options].freeze
8
-
7
+ # TODO: Move Errors to ConfigLoader
9
8
  InvalidConfigurationError = Class.new(StandardError)
9
+ InvalidConfigFileError = Class.new(InvalidConfigurationError)
10
10
  MissingVersionError = Class.new(InvalidConfigurationError)
11
11
  VersionNotSupportedError = Class.new(InvalidConfigurationError)
12
12
  InvalidSyntaxError = Class.new(InvalidConfigurationError)
13
13
 
14
14
  class << self
15
- def load!(params)
16
- version = params['djin_version']
17
- raise MissingVersionError, 'Missing djin_version' unless version
18
- unless version_supported?(version)
19
- raise VersionNotSupportedError, "Version #{version} is not supported, use #{Djin::VERSION} or higher"
20
- end
21
-
22
- tasks_params = params.except(*RESERVED_WORDS).reject { |task| task.start_with?('_') }
23
- contract = TaskContract.new
15
+ # rubocop:disable Metrics/AbcSize
16
+ def load!(file_config)
17
+ # TODO: Move task validation to ConfigLoader and add variables/include validations
18
+ task_contract = TaskContract.new
24
19
 
25
- tasks_params.map do |task_name, options|
26
- result = contract.call(options)
20
+ file_config.tasks.map do |task_name, options|
21
+ result = task_contract.call(options)
27
22
 
28
23
  raise InvalidSyntaxError, { task_name.to_sym => result.errors.to_h } if result.failure?
29
24
 
30
25
  command, build_command = build_commands(options, task_name: task_name)
31
26
 
27
+ # FIXME(1): Handle dynamic named tasks, eg: {{namespace}}unit and remove condition
28
+ if file_config.raw_tasks[task_name]
29
+ raw_command, = build_commands(file_config.raw_tasks[task_name], task_name: task_name)
30
+ end
31
+
32
32
  task_params = {
33
33
  name: task_name,
34
34
  build_command: build_command,
35
+ # TODO: Remove `|| command` after FIXME(1)
36
+ description: options['description'] || "Runs: #{raw_command || command}",
35
37
  command: command,
38
+ raw_command: raw_command,
39
+ aliases: options['aliases'],
36
40
  depends_on: options['depends_on']
37
41
  }.compact
38
42
 
39
43
  Djin::Task.new(**task_params)
40
44
  end
41
45
  end
46
+ # rubocop:enable Metrics/AbcSize
42
47
 
43
48
  private
44
49
 
@@ -54,10 +59,6 @@ module Djin
54
59
 
55
60
  LocalCommandBuilder.call(local_params) if local_params
56
61
  end
57
-
58
- def version_supported?(version)
59
- Vseries::SemanticVersion.new(Djin::VERSION) >= Vseries::SemanticVersion.new(version)
60
- end
61
62
  end
62
63
  end
63
64
  end
@@ -17,6 +17,7 @@ module Djin
17
17
  run_options = run_params['options']
18
18
  end
19
19
 
20
+ # TODO: Remove empty values
20
21
  run_command = run_command.join(' && ') if run_command.is_a?(Array)
21
22
 
22
23
  [run_command, run_options]
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Djin
4
+ class MemoryCache
5
+ def initialize(hash_store = {})
6
+ @hash_store = hash_store
7
+ end
8
+
9
+ def fetch(key)
10
+ @hash_store[key] || @hash_store[key] = yield
11
+ end
12
+
13
+ def clear
14
+ @hash_store = {}
15
+ end
16
+ end
17
+ end
@@ -49,6 +49,7 @@ module Djin
49
49
  hash(LocalSchema)
50
50
  end
51
51
 
52
+ optional(:aliases).each(:str?)
52
53
  optional(:depends_on).each(:str?)
53
54
  end
54
55
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Djin
4
- VERSION = '0.5.0'
4
+ VERSION = '0.9.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: djin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.9.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-07-14 00:00:00.000000000 Z
11
+ date: 2020-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-cli
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
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
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: dry-struct
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -178,9 +192,14 @@ files:
178
192
  - djin.yml
179
193
  - docker-compose.yml
180
194
  - examples/djin.yml
195
+ - examples/djin_lib/test.yml
196
+ - examples/local_tasks/.djin/server_tasks.yml
197
+ - examples/local_tasks/djin.yml
181
198
  - exe/djin
182
199
  - lib/djin.rb
183
200
  - lib/djin/cli.rb
201
+ - lib/djin/config_loader.rb
202
+ - lib/djin/entities/file_config.rb
184
203
  - lib/djin/entities/task.rb
185
204
  - lib/djin/entities/types.rb
186
205
  - lib/djin/executor.rb
@@ -190,9 +209,9 @@ files:
190
209
  - lib/djin/interpreter/docker_command_builder.rb
191
210
  - lib/djin/interpreter/docker_compose_command_builder.rb
192
211
  - lib/djin/interpreter/local_command_builder.rb
212
+ - lib/djin/memory_cache.rb
193
213
  - lib/djin/repositories/task_repository.rb
194
214
  - lib/djin/task_contract.rb
195
- - lib/djin/template_renderer.rb
196
215
  - lib/djin/version.rb
197
216
  homepage: https://github.com/catks/djin
198
217
  licenses:
@@ -213,7 +232,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
213
232
  - !ruby/object:Gem::Version
214
233
  version: '0'
215
234
  requirements: []
216
- rubygems_version: 3.0.3
235
+ rubyforge_project:
236
+ rubygems_version: 2.7.6
217
237
  signing_key:
218
238
  specification_version: 4
219
239
  summary: djin is a make-like utility for docker containers
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Djin
4
- class TemplateRenderer
5
- def self.render(template_file)
6
- new(template_file).render
7
- end
8
-
9
- def initialize(template_file)
10
- @template_file = template_file
11
- end
12
-
13
- def render
14
- Mustache.render(@template_file,
15
- args: args.join(' '),
16
- args?: args.any?,
17
- **env)
18
- end
19
-
20
- private
21
-
22
- def args
23
- index = ARGV.index('--')
24
-
25
- return [] unless index
26
-
27
- ARGV.slice((index + 1)..ARGV.size)
28
- end
29
-
30
- def env
31
- @env ||= ENV.to_h.map { |k, v| [k.to_sym, v] }.to_h
32
- end
33
- end
34
- end