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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +14 -0
- data/.rubocop.yml +8 -0
- data/.rubocop_todo.yml +17 -0
- data/CHANGELOG.md +20 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +34 -10
- data/README.md +133 -32
- data/Rakefile +6 -4
- data/Vertofile +45 -0
- data/bin/console +4 -3
- data/djin.gemspec +26 -21
- data/djin.yml +28 -13
- data/examples/djin.yml +25 -23
- data/exe/djin +3 -0
- data/lib/djin.rb +31 -23
- data/lib/djin/cli.rb +5 -5
- data/lib/djin/config_loader.rb +106 -0
- data/lib/djin/entities/file_config.rb +17 -0
- data/lib/djin/entities/task.rb +4 -5
- data/lib/djin/entities/types.rb +2 -0
- data/lib/djin/executor.rb +2 -0
- data/lib/djin/extensions/hash_extensions.rb +7 -1
- data/lib/djin/interpreter.rb +15 -63
- 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 +2 -0
- data/lib/djin/task_contract.rb +37 -7
- data/lib/djin/version.rb +3 -1
- metadata +62 -11
- 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,36 +1,41 @@
|
|
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.add_dependency
|
32
|
-
spec.
|
33
|
-
spec.
|
34
|
-
spec.add_development_dependency
|
35
|
-
spec.add_development_dependency
|
30
|
+
spec.add_dependency 'dry-cli', '~> 0.6.0'
|
31
|
+
spec.add_dependency 'dry-equalizer', '~> 0.3.0'
|
32
|
+
spec.add_dependency 'dry-struct', '~> 1.3.0'
|
33
|
+
spec.add_dependency 'dry-validation', '~> 1.5.1'
|
34
|
+
spec.add_dependency 'mustache', '~> 1.1.1'
|
35
|
+
spec.add_dependency 'vseries', '~> 0.1.0'
|
36
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
37
|
+
spec.add_development_dependency 'byebug'
|
38
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
39
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
40
|
+
spec.add_development_dependency 'rubocop'
|
36
41
|
end
|
data/djin.yml
CHANGED
@@ -1,18 +1,33 @@
|
|
1
|
-
djin_version: '0.
|
1
|
+
djin_version: '0.7.0'
|
2
2
|
|
3
3
|
_default_run_options: &default_run_options
|
4
4
|
options: "--rm --entrypoint=''"
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
12
14
|
|
13
|
-
sh:
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
sh:
|
16
|
+
description: Enter app service shell
|
17
|
+
docker-compose:
|
18
|
+
service: app
|
19
|
+
run:
|
20
|
+
commands: "sh"
|
21
|
+
<<: *default_run_options
|
22
|
+
run:
|
23
|
+
docker-compose:
|
24
|
+
service: app
|
25
|
+
run:
|
26
|
+
commands: "sh -c '{{args}}'"
|
27
|
+
<<: *default_run_options
|
28
|
+
|
29
|
+
release:
|
30
|
+
local:
|
31
|
+
run:
|
32
|
+
- verto tag up {{args}}
|
33
|
+
- 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.7.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,35 +1,44 @@
|
|
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
|
15
|
-
require
|
16
|
-
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/entities/file_config'
|
15
|
+
require 'djin/interpreter/base_command_builder'
|
16
|
+
require 'djin/interpreter/docker_command_builder'
|
17
|
+
require 'djin/interpreter/docker_compose_command_builder'
|
18
|
+
require 'djin/interpreter/local_command_builder'
|
19
|
+
require 'djin/interpreter'
|
20
|
+
require 'djin/config_loader'
|
21
|
+
require 'djin/executor'
|
22
|
+
require 'djin/cli'
|
23
|
+
require 'djin/task_contract'
|
24
|
+
require 'djin/repositories/task_repository'
|
17
25
|
|
18
26
|
module Djin
|
19
27
|
class Error < StandardError; end
|
20
|
-
# Your code goes here...
|
21
28
|
|
22
29
|
def self.load_tasks!(path = Pathname.getwd.join('djin.yml'))
|
23
30
|
abort 'Error: djin.yml not found' unless path.exist?
|
24
31
|
|
25
|
-
|
26
|
-
|
32
|
+
file_config = ConfigLoader.load!(path.read)
|
33
|
+
|
34
|
+
# TODO: Make all tasks be under 'tasks' key, passing only the tasks here
|
35
|
+
tasks = Interpreter.load!(file_config)
|
27
36
|
|
28
37
|
@task_repository = TaskRepository.new(tasks)
|
29
38
|
CLI.load_tasks!(tasks)
|
30
|
-
|
31
|
-
|
32
|
-
abort(
|
39
|
+
rescue Djin::Interpreter::InvalidConfigurationError => e
|
40
|
+
error_name = e.class.name.split('::').last
|
41
|
+
abort("[#{error_name}] #{e.message}")
|
33
42
|
end
|
34
43
|
|
35
44
|
def self.tasks
|
@@ -40,4 +49,3 @@ module Djin
|
|
40
49
|
@task_repository ||= TaskRepository.new
|
41
50
|
end
|
42
51
|
end
|
43
|
-
|
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
|
@@ -5,23 +7,21 @@ module Djin
|
|
5
7
|
def self.load_tasks!(tasks)
|
6
8
|
tasks.each do |task|
|
7
9
|
command = Class.new(Dry::CLI::Command) do
|
8
|
-
desc
|
10
|
+
desc task.description
|
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
|
16
18
|
|
17
19
|
register task.name, command
|
18
|
-
|
19
|
-
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
class Version < Dry::CLI::Command
|
24
|
-
desc
|
24
|
+
desc 'Prints Djin Version'
|
25
25
|
|
26
26
|
def call(*)
|
27
27
|
puts Djin::VERSION
|
@@ -0,0 +1,106 @@
|
|
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
|
+
file_config
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def file_config
|
28
|
+
FileConfig.new(
|
29
|
+
djin_version: version,
|
30
|
+
variables: variables,
|
31
|
+
tasks: tasks,
|
32
|
+
raw_tasks: raw_tasks
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def raw_djin_config
|
37
|
+
@raw_djin_config ||= yaml_load(@template_file)
|
38
|
+
rescue Psych::SyntaxError => e
|
39
|
+
raise Interpreter::InvalidConfigFileError, e.message
|
40
|
+
end
|
41
|
+
|
42
|
+
def rendered_djin_config
|
43
|
+
@rendered_djin_config ||= begin
|
44
|
+
locals = env.merge(variables)
|
45
|
+
|
46
|
+
rendered_yaml = Mustache.render(@template_file,
|
47
|
+
args: args.join(' '),
|
48
|
+
args?: args.any?,
|
49
|
+
**locals)
|
50
|
+
yaml_load(rendered_yaml)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def version
|
55
|
+
# TODO: Deprecates djin_version and use version instead
|
56
|
+
@version || raw_djin_config['djin_version']
|
57
|
+
end
|
58
|
+
|
59
|
+
def variables
|
60
|
+
@variables ||= raw_djin_config['variables']&.symbolize_keys || {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def tasks
|
64
|
+
rendered_djin_config['tasks'] || legacy_tasks
|
65
|
+
end
|
66
|
+
|
67
|
+
def raw_tasks
|
68
|
+
raw_djin_config['tasks'] || legacy_raw_tasks
|
69
|
+
end
|
70
|
+
|
71
|
+
def legacy_tasks
|
72
|
+
warn '[DEPRECATED] Root tasks are deprecated and will be removed in Djin 1.0.0,' \
|
73
|
+
' put the tasks under \'tasks\' keyword'
|
74
|
+
|
75
|
+
rendered_djin_config.except(*RESERVED_WORDS).reject { |task| task.start_with?('_') }
|
76
|
+
end
|
77
|
+
|
78
|
+
def legacy_raw_tasks
|
79
|
+
raw_djin_config.except(*RESERVED_WORDS).reject { |task| task.start_with?('_') }
|
80
|
+
end
|
81
|
+
|
82
|
+
def args
|
83
|
+
index = ARGV.index('--')
|
84
|
+
|
85
|
+
return [] unless index
|
86
|
+
|
87
|
+
ARGV.slice((index + 1)..ARGV.size)
|
88
|
+
end
|
89
|
+
|
90
|
+
def env
|
91
|
+
@env ||= ENV.to_h.symbolize_keys
|
92
|
+
end
|
93
|
+
|
94
|
+
def yaml_load(text)
|
95
|
+
YAML.safe_load(text, [], [], true)
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate_version!
|
99
|
+
raise Interpreter::MissingVersionError, 'Missing djin_version' unless version
|
100
|
+
|
101
|
+
return if file_config.version_supported?
|
102
|
+
|
103
|
+
raise Interpreter::VersionNotSupportedError, "Version #{version} is not supported, use #{Djin::VERSION} or higher"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Djin
|
4
|
+
class FileConfig < Dry::Struct
|
5
|
+
attribute :djin_version, Types::String
|
6
|
+
attribute :variables, Types::Hash.optional.default({}.freeze)
|
7
|
+
attribute :tasks, Types::Hash
|
8
|
+
attribute :raw_tasks, Types::Hash
|
9
|
+
# TODO: Add env and args
|
10
|
+
|
11
|
+
include Dry::Equalizer(:djin_version, :variables, :tasks, :raw_tasks)
|
12
|
+
|
13
|
+
def version_supported?
|
14
|
+
Vseries::SemanticVersion.new(Djin::VERSION) >= Vseries::SemanticVersion.new(djin_version)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/djin/entities/task.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
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
8
|
attribute :command, Types::String.optional.default(nil)
|
9
|
+
attribute :raw_command, Types::String.optional.default(nil)
|
7
10
|
attribute :depends_on, Types::Array.of(Types::String).optional.default([].freeze)
|
8
11
|
|
9
|
-
|
10
|
-
name == other.name &&
|
11
|
-
command == other.command &&
|
12
|
-
build_command == other.build_command
|
13
|
-
end
|
12
|
+
include Dry::Equalizer(:name, :command, :build_command)
|
14
13
|
end
|
15
14
|
end
|