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.
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "djin"
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 "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
@@ -1,35 +1,40 @@
1
- lib = File.expand_path("lib", __dir__)
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 "djin/version"
5
+ require 'djin/version'
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = "djin"
8
+ spec.name = 'djin'
7
9
  spec.version = Djin::VERSION
8
- spec.authors = ["Carlos Atkinson"]
9
- spec.email = ["carlos.atks@gmail.com"]
10
+ spec.authors = ['Carlos Atkinson']
11
+ spec.email = ['carlos.atks@gmail.com']
10
12
 
11
- spec.summary = %q{djin is a make-like utility for docker containers}
12
- spec.homepage = "https://github.com/catks/djin"
13
- spec.license = "MIT"
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('..', __FILE__)) do
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 = "exe"
26
+ spec.bindir = 'exe'
25
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ["lib"]
28
+ spec.require_paths = ['lib']
27
29
 
28
- spec.add_dependency "dry-struct", "~> 1.3.0"
29
- spec.add_dependency "dry-cli", "~> 0.5.0"
30
- spec.add_dependency "dry-validation", "~> 1.5.0"
31
- spec.add_development_dependency "bundler", "~> 2.0"
32
- spec.add_development_dependency "rake", "~> 10.0"
33
- spec.add_development_dependency "rspec", "~> 3.0"
34
- spec.add_development_dependency "byebug"
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
- test:
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
@@ -1,27 +1,29 @@
1
1
  ---
2
- default:
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
- script:
15
- docker:
16
- image: "ruby:2.6"
17
- run:
18
- commands:
19
- - "ruby /scripts/my_ruby_script.rb"
20
- options: "--rm -v $(pwd)/my_ruby_script.rb:/scripts/my_ruby_script.rb"
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
- docker:
25
- build: .
26
- run:
27
- - "ruby -e 'puts \" Hello\"'"
25
+ with_build:
26
+ docker:
27
+ build: .
28
+ run:
29
+ - "ruby -e 'puts \" Hello\"'"
data/exe/djin CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ Signal.trap('INT') { exit 2 }
2
5
 
3
6
  require_relative '../lib/djin'
4
7
 
@@ -1,36 +1,49 @@
1
- require "djin/version"
2
- require "pathname"
3
- require "yaml"
4
- require "dry-struct"
5
- require "dry-validation"
6
- require "dry/cli"
7
- require "djin/extensions/hash_extensions"
8
- require "djin/extensions/custom_predicates"
9
- require "djin/entities/types"
10
- require "djin/entities/task"
11
- require "djin/interpreter"
12
- require "djin/executor"
13
- require "djin/cli"
14
- require "djin/task_contract"
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
- djin_file = YAML.load(path.read)
24
- @tasks = Djin::Interpreter.load!(djin_file)
31
+ djin_config = ConfigLoader.load!(path.read)
25
32
 
26
- CLI.load_tasks!(@tasks)
33
+ # TODO: Make all tasks be under 'tasks' key, passing only the tasks here
34
+ tasks = Interpreter.load!(djin_config)
27
35
 
28
- rescue Djin::Interpreter::InvalidSyntax => ex
29
- abort(ex.message)
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
- @tasks || []
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
@@ -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(**options)
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
@@ -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 &&
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Djin
2
4
  module Types
3
5
  include Dry.Types()
@@ -1,16 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Djin
2
4
  class Executor
3
- def call(*tasks)
4
- tasks.each do |task|
5
- run task.build_command if task.build_command
6
- run task.command
7
- end
8
- end
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
- private
22
+ run task.build_command if task.build_command
23
+ run task.command if task.command
24
+ end
11
25
 
12
- def run(command)
13
- system command
14
- end
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