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/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'djin'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "djin"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
data/djin.gemspec
CHANGED
@@ -1,35 +1,40 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require
|
5
|
+
require 'djin/version'
|
4
6
|
|
5
7
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
8
|
+
spec.name = 'djin'
|
7
9
|
spec.version = Djin::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
10
|
+
spec.authors = ['Carlos Atkinson']
|
11
|
+
spec.email = ['carlos.atks@gmail.com']
|
10
12
|
|
11
|
-
spec.summary =
|
12
|
-
spec.homepage =
|
13
|
-
spec.license =
|
13
|
+
spec.summary = 'djin is a make-like utility for docker containers'
|
14
|
+
spec.homepage = 'https://github.com/catks/djin'
|
15
|
+
spec.license = 'MIT'
|
14
16
|
|
15
17
|
# spec.metadata["homepage_uri"] = spec.homepage
|
16
|
-
#spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
17
|
-
#spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
18
|
+
# spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
19
|
+
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
18
20
|
|
19
21
|
# Specify which files should be added to the gem when it is released.
|
20
22
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
-
spec.files = Dir.chdir(File.expand_path(
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
24
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
25
|
end
|
24
|
-
spec.bindir =
|
26
|
+
spec.bindir = 'exe'
|
25
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
-
spec.require_paths = [
|
28
|
+
spec.require_paths = ['lib']
|
27
29
|
|
28
|
-
spec.add_dependency
|
29
|
-
spec.add_dependency
|
30
|
-
spec.add_dependency
|
31
|
-
spec.
|
32
|
-
spec.
|
33
|
-
spec.add_development_dependency
|
34
|
-
spec.add_development_dependency
|
30
|
+
spec.add_dependency 'dry-cli', '~> 0.6.0'
|
31
|
+
spec.add_dependency 'dry-struct', '~> 1.3.0'
|
32
|
+
spec.add_dependency 'dry-validation', '~> 1.5.1'
|
33
|
+
spec.add_dependency 'mustache', '~> 1.1.1'
|
34
|
+
spec.add_dependency 'vseries', '~> 0.1.0'
|
35
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
36
|
+
spec.add_development_dependency 'byebug'
|
37
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
38
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
39
|
+
spec.add_development_dependency 'rubocop'
|
35
40
|
end
|
data/djin.yml
CHANGED
@@ -1,13 +1,31 @@
|
|
1
|
-
|
2
|
-
docker-compose:
|
3
|
-
service: app
|
4
|
-
run:
|
5
|
-
commands: "cd /usr/src/djin && rspec"
|
6
|
-
options: "--rm --entrypoint=''"
|
7
|
-
sh:
|
8
|
-
docker-compose:
|
9
|
-
service: app
|
10
|
-
run:
|
11
|
-
commands: "sh"
|
12
|
-
options: "--rm --entrypoint=''"
|
1
|
+
djin_version: '0.6.0'
|
13
2
|
|
3
|
+
_default_run_options: &default_run_options
|
4
|
+
options: "--rm --entrypoint=''"
|
5
|
+
|
6
|
+
tasks:
|
7
|
+
test:
|
8
|
+
docker-compose:
|
9
|
+
service: app
|
10
|
+
run:
|
11
|
+
commands: "cd /usr/src/djin && rspec {{args}}"
|
12
|
+
<<: *default_run_options
|
13
|
+
|
14
|
+
sh:
|
15
|
+
docker-compose:
|
16
|
+
service: app
|
17
|
+
run:
|
18
|
+
commands: "sh"
|
19
|
+
<<: *default_run_options
|
20
|
+
run:
|
21
|
+
docker-compose:
|
22
|
+
service: app
|
23
|
+
run:
|
24
|
+
commands: "sh -c '{{args}}'"
|
25
|
+
<<: *default_run_options
|
26
|
+
|
27
|
+
release:
|
28
|
+
local:
|
29
|
+
run:
|
30
|
+
- verto tag up {{args}}
|
31
|
+
- bundle exec rake release
|
data/examples/djin.yml
CHANGED
@@ -1,27 +1,29 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
docker:
|
4
|
-
image: "ruby:2.5"
|
5
|
-
run:
|
6
|
-
- "ruby -e 'puts \\\" Hello\\\"'"
|
7
|
-
test:
|
8
|
-
docker-compose:
|
9
|
-
service: app
|
10
|
-
run:
|
11
|
-
commands: rspec
|
12
|
-
options: "--rm"
|
2
|
+
djin_version: '0.6.0'
|
13
3
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
- "ruby
|
20
|
-
|
4
|
+
tasks:
|
5
|
+
default:
|
6
|
+
docker:
|
7
|
+
image: "ruby:2.5"
|
8
|
+
run:
|
9
|
+
- "ruby -e 'puts \\\" Hello\\\"'"
|
10
|
+
test:
|
11
|
+
docker-compose:
|
12
|
+
service: app
|
13
|
+
run:
|
14
|
+
commands: rspec
|
15
|
+
options: "--rm"
|
21
16
|
|
17
|
+
script:
|
18
|
+
docker:
|
19
|
+
image: "ruby:2.6"
|
20
|
+
run:
|
21
|
+
commands:
|
22
|
+
- "ruby /scripts/my_ruby_script.rb"
|
23
|
+
options: "--rm -v $(pwd)/my_ruby_script.rb:/scripts/my_ruby_script.rb"
|
22
24
|
|
23
|
-
with_build:
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
with_build:
|
26
|
+
docker:
|
27
|
+
build: .
|
28
|
+
run:
|
29
|
+
- "ruby -e 'puts \" Hello\"'"
|
data/exe/djin
CHANGED
data/lib/djin.rb
CHANGED
@@ -1,36 +1,49 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'djin/version'
|
4
|
+
require 'pathname'
|
5
|
+
require 'yaml'
|
6
|
+
require 'dry-struct'
|
7
|
+
require 'dry-validation'
|
8
|
+
require 'vseries'
|
9
|
+
require 'dry/cli'
|
10
|
+
require 'mustache'
|
11
|
+
require 'djin/extensions/hash_extensions'
|
12
|
+
require 'djin/entities/types'
|
13
|
+
require 'djin/entities/task'
|
14
|
+
require 'djin/interpreter/base_command_builder'
|
15
|
+
require 'djin/interpreter/docker_command_builder'
|
16
|
+
require 'djin/interpreter/docker_compose_command_builder'
|
17
|
+
require 'djin/interpreter/local_command_builder'
|
18
|
+
require 'djin/interpreter'
|
19
|
+
require 'djin/config_loader'
|
20
|
+
require 'djin/executor'
|
21
|
+
require 'djin/cli'
|
22
|
+
require 'djin/task_contract'
|
23
|
+
require 'djin/repositories/task_repository'
|
15
24
|
|
16
25
|
module Djin
|
17
26
|
class Error < StandardError; end
|
18
|
-
# Your code goes here...
|
19
27
|
|
20
28
|
def self.load_tasks!(path = Pathname.getwd.join('djin.yml'))
|
21
29
|
abort 'Error: djin.yml not found' unless path.exist?
|
22
30
|
|
23
|
-
|
24
|
-
@tasks = Djin::Interpreter.load!(djin_file)
|
31
|
+
djin_config = ConfigLoader.load!(path.read)
|
25
32
|
|
26
|
-
|
33
|
+
# TODO: Make all tasks be under 'tasks' key, passing only the tasks here
|
34
|
+
tasks = Interpreter.load!(djin_config)
|
27
35
|
|
28
|
-
|
29
|
-
|
36
|
+
@task_repository = TaskRepository.new(tasks)
|
37
|
+
CLI.load_tasks!(tasks)
|
38
|
+
rescue Djin::Interpreter::InvalidConfigurationError => e
|
39
|
+
abort(e.message)
|
30
40
|
end
|
31
41
|
|
32
42
|
def self.tasks
|
33
|
-
|
43
|
+
task_repository.all
|
34
44
|
end
|
35
|
-
end
|
36
45
|
|
46
|
+
def self.task_repository
|
47
|
+
@task_repository ||= TaskRepository.new
|
48
|
+
end
|
49
|
+
end
|
data/lib/djin/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Djin
|
2
4
|
class CLI
|
3
5
|
extend Dry::CLI::Registry
|
@@ -9,7 +11,7 @@ module Djin
|
|
9
11
|
|
10
12
|
define_method(:task) { task }
|
11
13
|
|
12
|
-
def call(**
|
14
|
+
def call(**)
|
13
15
|
Executor.new.call(task)
|
14
16
|
end
|
15
17
|
end
|
@@ -17,5 +19,15 @@ module Djin
|
|
17
19
|
register task.name, command
|
18
20
|
end
|
19
21
|
end
|
22
|
+
|
23
|
+
class Version < Dry::CLI::Command
|
24
|
+
desc 'Prints Djin Version'
|
25
|
+
|
26
|
+
def call(*)
|
27
|
+
puts Djin::VERSION
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
register '--version', Version, aliases: ['-v']
|
20
32
|
end
|
21
33
|
end
|
@@ -0,0 +1,92 @@
|
|
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].freeze
|
10
|
+
|
11
|
+
def self.load!(template_file)
|
12
|
+
new(template_file).load!
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(template_file)
|
16
|
+
@template_file = template_file
|
17
|
+
end
|
18
|
+
|
19
|
+
def load!
|
20
|
+
validate_version!
|
21
|
+
|
22
|
+
# TODO: Return a DjinConfig Entity
|
23
|
+
tasks
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def raw_djin_config
|
29
|
+
@raw_djin_config ||= yaml_load(@template_file)
|
30
|
+
end
|
31
|
+
|
32
|
+
def rendered_djin_config
|
33
|
+
@rendered_djin_config ||= begin
|
34
|
+
locals = env.merge(variables)
|
35
|
+
|
36
|
+
rendered_yaml = Mustache.render(@template_file,
|
37
|
+
args: args.join(' '),
|
38
|
+
args?: args.any?,
|
39
|
+
**locals)
|
40
|
+
yaml_load(rendered_yaml)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def version
|
45
|
+
# TODO: Deprecates djin_version and use version instead
|
46
|
+
@version || raw_djin_config['djin_version']
|
47
|
+
end
|
48
|
+
|
49
|
+
def variables
|
50
|
+
@variables ||= raw_djin_config['variables']&.symbolize_keys || {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def tasks
|
54
|
+
rendered_djin_config['tasks'] || legacy_tasks
|
55
|
+
end
|
56
|
+
|
57
|
+
def legacy_tasks
|
58
|
+
warn '[DEPRECATED] Root tasks are deprecated and will be removed in Djin 1.0.0,' \
|
59
|
+
' put the tasks under \'tasks\' keyword'
|
60
|
+
|
61
|
+
rendered_djin_config.except(*RESERVED_WORDS).reject { |task| task.start_with?('_') }
|
62
|
+
end
|
63
|
+
|
64
|
+
def args
|
65
|
+
index = ARGV.index('--')
|
66
|
+
|
67
|
+
return [] unless index
|
68
|
+
|
69
|
+
ARGV.slice((index + 1)..ARGV.size)
|
70
|
+
end
|
71
|
+
|
72
|
+
def env
|
73
|
+
@env ||= ENV.to_h.symbolize_keys
|
74
|
+
end
|
75
|
+
|
76
|
+
def yaml_load(text)
|
77
|
+
YAML.safe_load(text, [], [], true)
|
78
|
+
end
|
79
|
+
|
80
|
+
def version_supported?
|
81
|
+
Vseries::SemanticVersion.new(Djin::VERSION) >= Vseries::SemanticVersion.new(version)
|
82
|
+
end
|
83
|
+
|
84
|
+
def validate_version!
|
85
|
+
raise Interpreter::MissingVersionError, 'Missing djin_version' unless version
|
86
|
+
|
87
|
+
return if version_supported?
|
88
|
+
|
89
|
+
raise Interpreter::VersionNotSupportedError, "Version #{version} is not supported, use #{Djin::VERSION} or higher"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/djin/entities/task.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Djin
|
2
4
|
class Task < Dry::Struct
|
3
5
|
attribute :name, Types::String
|
4
6
|
attribute :description, Types::String.optional.default(nil)
|
5
7
|
attribute :build_command, Types::String.optional.default(nil)
|
6
|
-
attribute :command, Types::String
|
8
|
+
attribute :command, Types::String.optional.default(nil)
|
9
|
+
attribute :depends_on, Types::Array.of(Types::String).optional.default([].freeze)
|
7
10
|
|
8
11
|
def ==(other)
|
9
12
|
name == other.name &&
|
data/lib/djin/entities/types.rb
CHANGED
data/lib/djin/executor.rb
CHANGED
@@ -1,16 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Djin
|
2
4
|
class Executor
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def initialize(task_repository: Djin.task_repository)
|
6
|
+
@task_repository = task_repository
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(*tasks)
|
10
|
+
tasks.each do |task|
|
11
|
+
run_task(task)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def run_task(task)
|
18
|
+
@task_repository.find_by_names(task.depends_on).each do |dependent_task|
|
19
|
+
run_task dependent_task
|
20
|
+
end
|
9
21
|
|
10
|
-
|
22
|
+
run task.build_command if task.build_command
|
23
|
+
run task.command if task.command
|
24
|
+
end
|
11
25
|
|
12
|
-
|
13
|
-
|
14
|
-
|
26
|
+
def run(command)
|
27
|
+
system command
|
28
|
+
end
|
15
29
|
end
|
16
30
|
end
|
@@ -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
|