singularity_dsl 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +10 -0
  5. data/.singularityrc +12 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +52 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +38 -0
  10. data/Rakefile +24 -0
  11. data/bin/singularity_runner +5 -0
  12. data/lib/singularity_dsl/application.rb +76 -0
  13. data/lib/singularity_dsl/cli/cli.rb +125 -0
  14. data/lib/singularity_dsl/cli/table.rb +32 -0
  15. data/lib/singularity_dsl/dsl/batch.rb +23 -0
  16. data/lib/singularity_dsl/dsl/changeset.rb +39 -0
  17. data/lib/singularity_dsl/dsl/components.rb +7 -0
  18. data/lib/singularity_dsl/dsl/dsl.rb +71 -0
  19. data/lib/singularity_dsl/dsl/event_store.rb +34 -0
  20. data/lib/singularity_dsl/dsl/registry.rb +60 -0
  21. data/lib/singularity_dsl/dsl/runner.rb +67 -0
  22. data/lib/singularity_dsl/dsl/utils.rb +28 -0
  23. data/lib/singularity_dsl/errors.rb +35 -0
  24. data/lib/singularity_dsl/files.rb +20 -0
  25. data/lib/singularity_dsl/git_helper.rb +94 -0
  26. data/lib/singularity_dsl/runstate.rb +30 -0
  27. data/lib/singularity_dsl/stdout.rb +16 -0
  28. data/lib/singularity_dsl/task.rb +30 -0
  29. data/lib/singularity_dsl/tasks/rake.rb +31 -0
  30. data/lib/singularity_dsl/tasks/rspec.rb +32 -0
  31. data/lib/singularity_dsl/tasks/rubocop.rb +54 -0
  32. data/lib/singularity_dsl/tasks/shell_task.rb +90 -0
  33. data/lib/singularity_dsl/version.rb +6 -0
  34. data/lib/singularity_dsl.rb +9 -0
  35. data/singularity_dsl.gemspec +31 -0
  36. data/spec/singularity_dsl/application_spec.rb +92 -0
  37. data/spec/singularity_dsl/dsl/batch_spec.rb +30 -0
  38. data/spec/singularity_dsl/dsl/changeset_spec.rb +54 -0
  39. data/spec/singularity_dsl/dsl/dsl_spec.rb +45 -0
  40. data/spec/singularity_dsl/dsl/event_store_spec.rb +44 -0
  41. data/spec/singularity_dsl/dsl/registry_spec.rb +39 -0
  42. data/spec/singularity_dsl/dsl/runner_spec.rb +39 -0
  43. data/spec/singularity_dsl/dsl/stubs/tasks/dummy_task.rb +4 -0
  44. data/spec/singularity_dsl/dsl/utils_spec.rb +33 -0
  45. data/spec/singularity_dsl/git_helper_spec.rb +72 -0
  46. data/spec/singularity_dsl/runstate_spec.rb +73 -0
  47. data/spec/singularity_dsl/task_spec.rb +54 -0
  48. data/spec/singularity_dsl/tasks/shell_task_spec.rb +119 -0
  49. metadata +231 -0
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+
3
+ require 'singularity_dsl/dsl/batch'
4
+ require 'singularity_dsl/dsl/dsl'
5
+ require 'singularity_dsl/errors'
6
+ require 'singularity_dsl/runstate'
7
+
8
+ module SingularityDsl
9
+ # DSL classes & fxs
10
+ module Dsl
11
+ # class that runs Singularity::Dsl
12
+ class Runner
13
+ include SingularityDsl::Errors
14
+
15
+ attr_reader :state, :dsl
16
+
17
+ def initialize
18
+ @dsl = Dsl.new
19
+ @state = Runstate.new
20
+ end
21
+
22
+ def execute(batch = false, pass_errors = false)
23
+ @dsl.registry.run_list(batch).each do |task|
24
+ task.execute.tap do |status|
25
+ failed = task.failed_status status
26
+ record_failure task if failed
27
+ resource_fail task if failed && !pass_errors
28
+ end
29
+ end
30
+ end
31
+
32
+ def load_ex_script(path)
33
+ @dsl.instance_eval(::File.read path)
34
+ end
35
+
36
+ def post_actions
37
+ @dsl.error_proc.call if @state.error
38
+ @dsl.fail_proc.call if @state.failed
39
+ @dsl.success_proc.call unless @state.failed || @state.error
40
+ @dsl.always_proc.call
41
+ end
42
+
43
+ private
44
+
45
+ def record_failure(task)
46
+ failure = klass_failed(task)
47
+ failure += " #{task.task_name}" if task.task_name
48
+ @state.add_failure failure
49
+ end
50
+
51
+ def raise_dsl_set_err(dsl)
52
+ fail "Invalid object given #{dsl}"
53
+ end
54
+
55
+ def execute_task(task)
56
+ failed = false
57
+ begin
58
+ failed = task.execute
59
+ rescue ::StandardError => err
60
+ @state.add_error "#{err.message}\n#{err.backtrace}"
61
+ resource_err task
62
+ end
63
+ failed
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ module SingularityDsl
4
+ # DSL classes & fxs
5
+ module Dsl
6
+ # Utility functions mixin module
7
+ module Utils
8
+ def task_name(klass)
9
+ klass.to_s.split(':').last
10
+ end
11
+
12
+ def task(klass)
13
+ task_name(klass).downcase.to_sym
14
+ end
15
+
16
+ def task_list
17
+ klasses = []
18
+ SingularityDsl.constants.each do |klass|
19
+ klass = SingularityDsl.const_get(klass)
20
+ next unless klass.is_a? Class
21
+ next unless klass < Task
22
+ klasses.push klass
23
+ end
24
+ klasses
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module SingularityDsl
4
+ # error & failure classes / methods
5
+ module Errors
6
+ class ResourceFail < RuntimeError
7
+ end
8
+
9
+ class ResourceError < RuntimeError
10
+ end
11
+
12
+ def klass_name(obj)
13
+ return obj.class if obj.class < Object
14
+ obj
15
+ end
16
+
17
+ def klass_error(klass)
18
+ klass = klass_name klass
19
+ "#{klass} threw an exception while executing"
20
+ end
21
+
22
+ def klass_failed(klass)
23
+ klass = klass_name klass
24
+ "#{klass} failed."
25
+ end
26
+
27
+ def resource_fail(klass)
28
+ fail ResourceFail, klass_failed(klass)
29
+ end
30
+
31
+ def resource_err(klass)
32
+ fail ResourceError, klass_error(klass)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ module SingularityDsl
4
+ # File helper fx mixin
5
+ module Files
6
+ private
7
+
8
+ def files_in_path(path)
9
+ paths = [path] if ::File.file? path
10
+ paths = dir_glob path if ::File.directory? path
11
+ paths ||= []
12
+ paths
13
+ end
14
+
15
+ def dir_glob(dir)
16
+ dir = ::File.join dir, '**'
17
+ ::Dir.glob dir
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+
3
+ require 'mixlib/shellout'
4
+
5
+ module SingularityDsl
6
+ # wrapper class for rugged
7
+ class GitHelper
8
+ attr_reader :dir
9
+
10
+ def initialize
11
+ throw 'git not installed' unless git_installed
12
+ end
13
+
14
+ def clean_reset
15
+ fail 'failed to clean' unless (reset | clean) == 0
16
+ end
17
+
18
+ def checkout_remote(branch, remote)
19
+ remote_action branch, remote, 'checkout'
20
+ end
21
+
22
+ def merge_remote(branch, url)
23
+ remote_action branch, url, 'merge'
24
+ end
25
+
26
+ def diff_remote(branch, url, flags = '')
27
+ flags = flags.join ' ' if flags.kind_of? Array
28
+ cmd = remote_cmd branch, url, "diff #{flags}"
29
+ task = Mixlib::ShellOut.new cmd
30
+ task.run_command
31
+ task.stdout
32
+ end
33
+
34
+ def add_remote(url)
35
+ remote = remote_from_url url
36
+ exec("git remote add #{remote} #{url}")
37
+ fetch_all
38
+ end
39
+
40
+ def remove_remote(url)
41
+ remote = remote_from_url url
42
+ return 0 if remote.eql? 'origin'
43
+ exec("git remote rm #{remote}")
44
+ end
45
+
46
+ private
47
+
48
+ def remote_cmd(branch, url, action)
49
+ remote = remote_from_url url
50
+ "git #{action} #{remote}/#{branch}"
51
+ end
52
+
53
+ def remote_action(branch, url, action)
54
+ status = exec(remote_cmd branch, url, action)
55
+ fail "failed to #{action}" unless status == 0
56
+ status
57
+ end
58
+
59
+ def remote_from_url(url)
60
+ return 'origin' if url.nil? || !url
61
+ url.split(':').last.gsub('/', '_')
62
+ end
63
+
64
+ def fetch_all
65
+ exec 'git fetch --all'
66
+ end
67
+
68
+ def remotes
69
+ (`git remote`.split "\n") - ['origin']
70
+ end
71
+
72
+ def index_path
73
+ ::File.join(@repo.path, 'index')
74
+ end
75
+
76
+ def git_installed
77
+ !`which git`.empty?
78
+ end
79
+
80
+ def reset
81
+ exec 'git add . && git reset --hard'
82
+ end
83
+
84
+ def clean
85
+ exec 'git clean -fdx'
86
+ end
87
+
88
+ def exec(cmd)
89
+ task = Mixlib::ShellOut.new cmd
90
+ task.run_command
91
+ task.exitstatus
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ module SingularityDsl
4
+ # abstraction class for the overall runtime state
5
+ class Runstate
6
+ attr_reader :errors, :failures, :error, :failed
7
+
8
+ def initialize
9
+ @error = false
10
+ @errors = []
11
+ @failed = false
12
+ @failures = []
13
+ end
14
+
15
+ def add_failure(fail_msg)
16
+ @failed = true
17
+ @failures.push fail_msg
18
+ end
19
+
20
+ def add_error(err_msg)
21
+ @error = true
22
+ @errors.push err_msg
23
+ end
24
+
25
+ def exit_code
26
+ return 1 if @error || @failed
27
+ 0
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rainbow'
4
+
5
+ module SingularityDsl
6
+ # mixin for output wrappers
7
+ module Stdout
8
+ def info(message)
9
+ puts Rainbow(message).cyan
10
+ end
11
+
12
+ def data(message)
13
+ puts Rainbow(message).green
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ module SingularityDsl
4
+ # Task abstraction class
5
+ class Task
6
+ def initialize(&block)
7
+ instance_eval(&block) unless block.nil?
8
+ end
9
+
10
+ def validate_file(file)
11
+ throw "Cannot find #{file}" unless File.exist? file
12
+ end
13
+
14
+ def execute
15
+ fail 'SingularityDsl::Task::execute not implemented'
16
+ end
17
+
18
+ def failed_status(status)
19
+ ![nil, 0, false].include? status
20
+ end
21
+
22
+ def description
23
+ "Runs #{self.class} task"
24
+ end
25
+
26
+ def task_name
27
+ false
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake'
4
+
5
+ # Rake Task
6
+ class Rake < Task
7
+ attr_accessor :target, :rake
8
+
9
+ def initialize(&block)
10
+ ::Rake.application.init
11
+ @rake = ::Rake.application
12
+ super(&block)
13
+ end
14
+
15
+ def target(target)
16
+ @target = target
17
+ @target.strip!
18
+ end
19
+
20
+ def execute
21
+ throw 'target is required' if @target.nil?
22
+ @rake.load_rakefile
23
+ ret = @rake[@target].invoke
24
+ return ret.count if ret.kind_of? Array
25
+ ret
26
+ end
27
+
28
+ def description
29
+ 'Simple resource to just wrap the Rake CLI'
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rspec'
4
+
5
+ # RSpec Task
6
+ class RSpec < Task
7
+ attr_accessor :spec_dir, :config_file
8
+
9
+ def initialize(&block)
10
+ @spec_dir ||= './spec'
11
+ @config_file ||= './.rspec'
12
+ super(&block)
13
+ end
14
+
15
+ def config_file(file)
16
+ validate_file file
17
+ @config_file = file
18
+ end
19
+
20
+ def spec_dir(dir)
21
+ validate_file dir
22
+ @spec_dir = dir
23
+ end
24
+
25
+ def execute
26
+ ::RSpec::Core::Runner.run([@spec_dir])
27
+ end
28
+
29
+ def description
30
+ 'Run RSpec tests. Uses RSpec::Core'
31
+ end
32
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubocop'
4
+
5
+ # Rubocop task
6
+ # Intentionally NOT a rake task - for some reason the syck YAML parser
7
+ # that Rubocop uses internally freaks out when a
8
+ # ::Rake::Application[:task].invoke is called from a task
9
+ class Rubocop < Task
10
+ # :files => specific files to run against
11
+ # :cfg_file => separate config file
12
+ def initialize(&block)
13
+ @default_config = './.rubocop.yml'
14
+ @files = []
15
+ @cfg_files = [@default_config]
16
+ @cfg_store = ::RuboCop::ConfigStore.new
17
+ super(&block)
18
+ end
19
+
20
+ def config_file(file)
21
+ validate_file file
22
+
23
+ return if @cfg_files.include? file
24
+ warn 'Loading multiple configs' if File.exist? @default_config
25
+
26
+ @cfg_files.push file
27
+
28
+ # this does a merge of options...
29
+ @cfg_store.options_config = file
30
+ end
31
+
32
+ def file(file)
33
+ validate_file file
34
+ @files.push file
35
+ end
36
+
37
+ def execute
38
+ inspector = ::RuboCop::FileInspector.new({})
39
+ # yes, have to pass in a block
40
+ inspector.process_files files, @cfg_store do
41
+ false
42
+ end
43
+ end
44
+
45
+ def description
46
+ 'Runs rubocop, loads .rubocop.yml from ./'
47
+ end
48
+
49
+ private
50
+
51
+ def files
52
+ ::RuboCop::TargetFinder.new(@cfg_store).find @files
53
+ end
54
+ end
@@ -0,0 +1,90 @@
1
+ # encoding: utf-8
2
+
3
+ require 'mixlib/shellout'
4
+ require 'singularity_dsl/stdout'
5
+
6
+ # shell-out resource for any ol commands
7
+ class ShellTask < SingularityDsl::Task
8
+ include SingularityDsl::Stdout
9
+
10
+ attr_reader :shell, :conditionals, :alternative, :no_fail
11
+ attr_writer :live_stream
12
+
13
+ def initialize(&block)
14
+ @live_stream = STDOUT
15
+ @conditionals = []
16
+ @no_fail = false
17
+ @alternative = 'echo "no alternative shell cmd defined"'
18
+ super(&block)
19
+ end
20
+
21
+ def condition(cmd)
22
+ invalid_cmd 'condition' unless cmd.is_a? String
23
+ @conditionals.push cmd
24
+ end
25
+
26
+ def no_fail(switch)
27
+ fail 'no_fail must be bool' unless bool? switch
28
+ @no_fail = switch
29
+ end
30
+
31
+ def alt(cmd)
32
+ invalid_cmd 'alt' unless cmd.is_a? String
33
+ @alternative = cmd
34
+ end
35
+
36
+ def command(cmd)
37
+ @task_name = cmd
38
+ @shell = setup_shell cmd
39
+ end
40
+
41
+ def task_name
42
+ return @task_name if @task_name
43
+ super
44
+ end
45
+
46
+ def execute
47
+ throw 'command never defined' if @shell.nil?
48
+ command @alternative unless evaluate_conditionals
49
+ @live_stream << log_shell if @live_stream
50
+ @shell.run_command
51
+ return 0 if @no_fail
52
+ @shell.exitstatus
53
+ end
54
+
55
+ def description
56
+ 'Runs a SH command using Mixlib::ShellOut'
57
+ end
58
+
59
+ private
60
+
61
+ def log_shell(pre = '', shell = false)
62
+ shell ||= @shell
63
+ pre ||= ''
64
+ log = "[ShellTask]:#{pre}:#{shell.command}"
65
+ data(log)
66
+ end
67
+
68
+ def bool?(val)
69
+ val.is_a?(TrueClass) || val.is_a?(FalseClass)
70
+ end
71
+
72
+ def setup_shell(cmd)
73
+ shell = ::Mixlib::ShellOut.new cmd
74
+ shell.live_stream = @live_stream if @live_stream
75
+ shell
76
+ end
77
+
78
+ def invalid_cmd(type)
79
+ throw "#{type} must be string"
80
+ end
81
+
82
+ def evaluate_conditionals
83
+ @conditionals.all? do |cmd|
84
+ shell = setup_shell cmd
85
+ log_shell '[conditional]', shell
86
+ shell.run_command
87
+ !failed_status(shell.exitstatus)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+
3
+ # version const for gem
4
+ module SingularityDsl
5
+ VERSION = '1.2.5'
6
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ # YEAH, THAT'S RIGHT
4
+ if RUBY_PLATFORM =~ /mswin|mingw32|windows/
5
+ throw 'Sorry, wont run on mswin|mingw32|windows'
6
+ end
7
+
8
+ require 'singularity_dsl/application'
9
+ require 'singularity_dsl/dsl/components'
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'singularity_dsl/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'singularity_dsl'
8
+ spec.version = SingularityDsl::VERSION
9
+ spec.authors = ['chr0n1x']
10
+ spec.email = ['heilong24@gmail.com']
11
+ spec.description = %q{DSL for your SingularityCI instance.}
12
+ spec.summary = %q{}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'mixlib-shellout', '~> 1.4'
22
+ spec.add_dependency 'rainbow', '~> 2.0.0'
23
+ spec.add_dependency 'rake', '~> 10.3'
24
+ spec.add_dependency 'rspec', '~> 2.6'
25
+ spec.add_dependency 'rubocop', '~> 0.23.0'
26
+ spec.add_dependency 'terminal-table', '~> 1.4'
27
+ spec.add_dependency 'thor', '~> 0.19'
28
+
29
+ spec.add_development_dependency 'bundler', '~> 1.3'
30
+ spec.add_development_dependency 'gem-release'
31
+ end
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ require 'singularity_dsl/application'
4
+
5
+ describe 'Application' do
6
+ let(:app) { SingularityDsl::Application.new }
7
+
8
+ context '#initialize' do
9
+ it 'creates base runner' do
10
+ expect(app.runner).to be_kind_of SingularityDsl::Dsl::Runner
11
+ end
12
+ end
13
+
14
+ context '#load_script' do
15
+ it 'just calls runner.load_ex_script' do
16
+ stub_res = 'spooooooky ghooooost'
17
+ expect(app.runner).to(receive(:load_ex_script).and_return stub_res)
18
+ expect(app.load_script 'dummy').to eql stub_res
19
+ end
20
+ end
21
+
22
+ context '#run' do
23
+ it 'logs resource failures' do
24
+ # don't want the entire thing to exit...
25
+ app.stub(:post_task_runner_actions)
26
+ app.runner.stub(:execute).and_raise(SingularityDsl::Errors::ResourceFail)
27
+ expect(app).to receive(:log_resource_fail)
28
+ app.run
29
+ end
30
+
31
+ it 'runner post-script actions are evaulated on failure' do
32
+ app.stub(:post_task_runner_actions)
33
+ app.stub(:log_resource_fail)
34
+ app.runner.stub(:execute).and_raise(SingularityDsl::Errors::ResourceFail)
35
+ expect(app).to receive(:post_task_runner_actions)
36
+ app.run
37
+ end
38
+
39
+ it 'logs resource errors' do
40
+ app.stub(:post_task_runner_actions)
41
+ app.runner.stub(:execute).and_raise(SingularityDsl::Errors::ResourceError)
42
+ expect(app).to receive(:log_resource_error)
43
+ app.run
44
+ end
45
+
46
+ it 'runner post-script actions are evaulated on error' do
47
+ app.stub(:post_task_runner_actions)
48
+ app.stub(:log_resource_error)
49
+ app.runner.stub(:execute).and_raise(SingularityDsl::Errors::ResourceError)
50
+ expect(app).to receive(:post_task_runner_actions)
51
+ app.run
52
+ end
53
+
54
+ it 'runner post-script actions are evaulated on error' do
55
+ app.stub(:post_task_runner_actions)
56
+ app.stub(:execute)
57
+ expect(app.runner.state).to receive :exit_code
58
+ app.run
59
+ end
60
+ end
61
+
62
+ context '#post_task_runner_actions' do
63
+ it 'outputs warning when script fails' do
64
+ app.stub :script_warn
65
+ app.runner.stub :post_actions
66
+ app.stub :exit_run
67
+ app.runner.state.stub(:failed).and_return true
68
+ expect(app).to receive(:script_warn)
69
+ app.post_task_runner_actions
70
+ end
71
+
72
+ it 'outputs warning when script errors' do
73
+ app.stub :script_error
74
+ app.runner.stub :post_actions
75
+ app.stub :exit_run
76
+ app.runner.state.stub(:error).and_return true
77
+ expect(app).to receive(:script_error)
78
+ app.post_task_runner_actions
79
+ end
80
+ end
81
+
82
+ context '#change_list' do
83
+ it 'returns a sorted list' do
84
+ expect(app.change_list(%w(b c e a d)))
85
+ .to eql %w(a b c d e)
86
+ end
87
+
88
+ it 'handles empty lists' do
89
+ expect(app.change_list([])).to eql []
90
+ end
91
+ end
92
+ end