tumugi 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +27 -1
  4. data/examples/event_callbacks.rb +37 -0
  5. data/lib/tumugi/cli.rb +14 -6
  6. data/lib/tumugi/command/new.rb +16 -60
  7. data/lib/tumugi/command/new/generator.rb +76 -0
  8. data/lib/tumugi/command/new/plugin_generator.rb +60 -0
  9. data/lib/tumugi/command/new/project_generator.rb +45 -0
  10. data/lib/tumugi/command/run.rb +2 -9
  11. data/lib/tumugi/dag_result_reporter.rb +5 -5
  12. data/lib/tumugi/data/new/{Gemfile.erb → plugin/Gemfile.erb} +0 -0
  13. data/lib/tumugi/data/new/{README.md.erb → plugin/README.md.erb} +0 -0
  14. data/lib/tumugi/data/new/{Rakefile.erb → plugin/Rakefile.erb} +0 -0
  15. data/lib/tumugi/data/new/{examples → plugin/examples}/example.rb.erb +0 -0
  16. data/lib/tumugi/data/new/{gemspec.erb → plugin/gemspec.erb} +0 -0
  17. data/lib/tumugi/data/new/{gitignore.erb → plugin/gitignore.erb} +0 -0
  18. data/lib/tumugi/data/new/{lib → plugin/lib}/tumugi/plugin/target/target.rb.erb +0 -0
  19. data/lib/tumugi/data/new/{lib → plugin/lib}/tumugi/plugin/task/task.rb.erb +0 -0
  20. data/lib/tumugi/data/new/{test → plugin/test}/plugin/target/target_test.rb.erb +0 -0
  21. data/lib/tumugi/data/new/{test → plugin/test}/plugin/task/task_test.rb.erb +0 -0
  22. data/lib/tumugi/data/new/{test → plugin/test}/test.rb.erb +0 -0
  23. data/lib/tumugi/data/new/{test → plugin/test}/test_helper.rb.erb +0 -0
  24. data/lib/tumugi/data/new/project/Gemfile.erb +3 -0
  25. data/lib/tumugi/data/new/project/tumugi_config.rb.erb +33 -0
  26. data/lib/tumugi/data/new/project/workflow.rb.erb +3 -0
  27. data/lib/tumugi/event.rb +11 -0
  28. data/lib/tumugi/executor/local_executor.rb +16 -12
  29. data/lib/tumugi/mixin/human_readable.rb +16 -0
  30. data/lib/tumugi/parameter/parameter.rb +10 -8
  31. data/lib/tumugi/task.rb +46 -3
  32. data/lib/tumugi/task_definition.rb +32 -4
  33. data/lib/tumugi/version.rb +1 -1
  34. data/lib/tumugi/workflow.rb +27 -11
  35. metadata +23 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5377ec144cacd5bddd1555300c31f7794b628dbc
4
- data.tar.gz: aa97c713302feba8c3735ab4876bc1616953bb3a
3
+ metadata.gz: e1245ddebd2d69768433b3cfdea9ea0c3e39cff8
4
+ data.tar.gz: f40eded5362a22f951caf384411387758fb668ce
5
5
  SHA512:
6
- metadata.gz: 09197a49b7d0b0b59a04ef6f038c2f4990dfdd61a54683d578b894203b5f48a34fdb99465359cbd48f10c556190b61e63833f08d2f5ceea69595e028dcd8cc57
7
- data.tar.gz: 4397186e5d06c7e21531835f0de0d98ecc3f4237ee40cf927155ec1b29dc58fcf0e69c8d5dbee0d2ea61433ce3a26296ef3b8743099867b2223f3086a944c9c3
6
+ metadata.gz: e1d78046c13e29e2f0cff749e54cfb020256de583c5561c9664edbabfcdae7a45fc64918b5cb4582a0ea7321e8f87932e0470365f142e12c792b82e432d94bff
7
+ data.tar.gz: b6745f25485a5aaa1e6ad494e468640209839fe77a7952ba955f888554f41764ccd31c6c983b04e6030a82e2e040f51af01d8f8d7c51b097ec62016ea5eb0bd0
@@ -6,7 +6,7 @@ rvm:
6
6
  - 2.3.1
7
7
  - ruby-head
8
8
  - jruby-9.0.5.0
9
- - jruby-9.1.2.0
9
+ - jruby-9.1.5.0
10
10
  - jruby-head
11
11
  before_install:
12
12
  - gem install bundler
@@ -1,6 +1,28 @@
1
1
  # Change Log
2
2
 
3
- ## [v0.6.3](https://github.com/tumugi/tumugi/tree/v0.6.3) (2016-08-14)
3
+ ## [v0.7.0](https://github.com/tumugi/tumugi/tree/v0.7.0) (2016-11-01)
4
+ [Full Changelog](https://github.com/tumugi/tumugi/compare/v0.6.3...v0.7.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Force run all tasks in a workflow [\#125](https://github.com/tumugi/tumugi/issues/125)
9
+ - Custom callback on success/retry/failure for each task [\#122](https://github.com/tumugi/tumugi/issues/122)
10
+ - Add secret option for parameter [\#117](https://github.com/tumugi/tumugi/issues/117)
11
+ - Add log about elapsed time for each tasks and workflow [\#116](https://github.com/tumugi/tumugi/issues/116)
12
+ - \[Breaking Change\] Introduce subcommand of `tumugi new` [\#111](https://github.com/tumugi/tumugi/issues/111)
13
+
14
+ **Merged pull requests:**
15
+
16
+ - Introduce success/failure event callback [\#128](https://github.com/tumugi/tumugi/pull/128) ([hakobera](https://github.com/hakobera))
17
+ - Support force run all tasks [\#127](https://github.com/tumugi/tumugi/pull/127) ([hakobera](https://github.com/hakobera))
18
+ - Write workflow and task elapsed time to log [\#124](https://github.com/tumugi/tumugi/pull/124) ([hakobera](https://github.com/hakobera))
19
+ - Filter coverage target files [\#121](https://github.com/tumugi/tumugi/pull/121) ([hakobera](https://github.com/hakobera))
20
+ - Test with jruby-9.1.5.0 on travis [\#120](https://github.com/tumugi/tumugi/pull/120) ([hakobera](https://github.com/hakobera))
21
+ - Introduce new sub commands [\#119](https://github.com/tumugi/tumugi/pull/119) ([hakobera](https://github.com/hakobera))
22
+ - Add param secret option [\#118](https://github.com/tumugi/tumugi/pull/118) ([hakobera](https://github.com/hakobera))
23
+ - Bumpup version to 0.7.0.dev [\#115](https://github.com/tumugi/tumugi/pull/115) ([hakobera](https://github.com/hakobera))
24
+
25
+ ## [v0.6.3](https://github.com/tumugi/tumugi/tree/v0.6.3) (2016-08-13)
4
26
  [Full Changelog](https://github.com/tumugi/tumugi/compare/v0.6.2...v0.6.3)
5
27
 
6
28
  **Implemented enhancements:**
@@ -12,6 +34,10 @@
12
34
  - Fix plugin template [\#112](https://github.com/tumugi/tumugi/pull/112) ([hakobera](https://github.com/hakobera))
13
35
  - Fix commad failed error message [\#110](https://github.com/tumugi/tumugi/pull/110) ([hakobera](https://github.com/hakobera))
14
36
 
37
+ **Merged pull requests:**
38
+
39
+ - Prepare release for 0.6.3 [\#114](https://github.com/tumugi/tumugi/pull/114) ([hakobera](https://github.com/hakobera))
40
+
15
41
  ## [v0.6.2](https://github.com/tumugi/tumugi/tree/v0.6.2) (2016-08-02)
16
42
  [Full Changelog](https://github.com/tumugi/tumugi/compare/v0.6.1...v0.6.2)
17
43
 
@@ -0,0 +1,37 @@
1
+ task :task1 do
2
+ requires [:task2, :task3]
3
+ run { log 'task1#run' }
4
+ end
5
+
6
+ task :task2 do
7
+ requires [:task4]
8
+ run { raise "error in #{id}" }
9
+ on_retry do
10
+ log 'task2#on_retry'
11
+ end
12
+ on_failure do
13
+ log 'task2#on_failure'
14
+ end
15
+ end
16
+
17
+ task :task3 do
18
+ requires [:task4]
19
+ run { log 'task3#run' }
20
+ on_success do
21
+ log 'task3#on_success'
22
+ end
23
+ end
24
+
25
+ task :task4 do
26
+ run do
27
+ log 'task4#run'
28
+ sleep 1
29
+ end
30
+ on_success do
31
+ log 'task4#on_success'
32
+ end
33
+ end
34
+
35
+ on_failure do
36
+ log 'task1#on_failure'
37
+ end
@@ -30,6 +30,7 @@ module Tumugi
30
30
  map "run" => "run_" # run is thor's reserved word, so this trick is needed
31
31
  option :workers, aliases: '-w', type: :numeric, desc: 'Number of workers to run task concurrently'
32
32
  option :out, aliases: '-o', desc: 'Output log filename. If not specified, log is write to STDOUT'
33
+ option :all, aliases: '-a', type: :boolean, desc: 'Run all tasks even if target exists', default: false
33
34
  common_options
34
35
  def run_(task)
35
36
  execute(:run, task, options)
@@ -45,9 +46,14 @@ module Tumugi
45
46
  execute(:show, task, opts.freeze)
46
47
  end
47
48
 
48
- desc "new PLUGIN_NAME", "Create new plugin project"
49
- def new(name)
50
- generate_plugin(name, options)
49
+ desc "new type NAME", "Create a new template of type. type is 'plugin' or 'project'"
50
+ def new(type, name)
51
+ generate_project(type, name, options)
52
+ end
53
+
54
+ desc "init", "Create an tumugi project template"
55
+ def init(path='')
56
+ generate_project('project', path, force: true)
51
57
  end
52
58
 
53
59
  private
@@ -55,6 +61,7 @@ module Tumugi
55
61
  def execute(command, task, options)
56
62
  logger.info "tumugi v#{Tumugi::VERSION}"
57
63
  args = { task: task, options: options }
64
+ logger.info "status: running, command: #{command}, args: #{args}"
58
65
  success = Tumugi.workflow.execute(command, task, options)
59
66
  unless success
60
67
  raise Thor::Error, "execute finished, but failed"
@@ -64,10 +71,11 @@ module Tumugi
64
71
  handle_error(command, e, args)
65
72
  end
66
73
 
67
- def generate_plugin(name, options)
74
+ def generate_project(type, name, options)
68
75
  logger.info "tumugi v#{Tumugi::VERSION}"
69
- args = { name: name, options: options }
70
- Tumugi::Command::New.new.execute(name, options)
76
+ args = { type: type, name: name, options: options }
77
+ logger.info "status: running, command: new, args: #{args}"
78
+ Tumugi::Command::New.new.execute(type, name, options)
71
79
  logger.info "status: success, command: new, args: #{args}"
72
80
  rescue => e
73
81
  handle_error("new", e, args)
@@ -1,71 +1,27 @@
1
- require 'fileutils'
2
- require 'erubis'
1
+ require_relative './new/plugin_generator'
2
+ require_relative './new/project_generator'
3
3
 
4
4
  module Tumugi
5
5
  module Command
6
6
  class New
7
- def execute(name, options={})
8
- full_project_name = "tumugi-plugin-#{name}"
9
- templates = [
10
- [ "Gemfile.erb", "Gemfile" ],
11
- [ "gemspec.erb", "#{full_project_name}.gemspec" ],
12
- [ "gitignore.erb", ".gitignore" ],
13
- [ "Rakefile.erb", "Rakefile" ],
14
- [ "README.md.erb", "README.md" ],
15
- [ "examples/example.rb.erb", "examples/example.rb" ],
16
- [ "lib/tumugi/plugin/target/target.rb.erb", "lib/tumugi/plugin/target/#{name}.rb" ],
17
- [ "lib/tumugi/plugin/task/task.rb.erb", "lib/tumugi/plugin/task/#{name}.rb" ],
18
- [ "test/test_helper.rb.erb", "test/test_helper.rb" ],
19
- [ "test/test.rb.erb", "test/#{name}_test.rb" ],
20
- [ "test/plugin/target/target_test.rb.erb", "test/plugin/target/#{name}_test.rb" ],
21
- [ "test/plugin/task/task_test.rb.erb", "test/plugin/task/#{name}_test.rb" ],
22
- ]
23
-
24
- @data_dir = "#{File.expand_path(File.dirname(__FILE__))}/../data/new"
25
- @dest_dir = "#{(options[:path] || '.')}/#{full_project_name}"
26
- if File.exist?(@dest_dir)
27
- puts "#{@dest_dir} is already exists. Please delete it first"
28
- return false
29
- end
30
-
31
- puts "Create #{@dest_dir}"
32
- FileUtils.mkdir_p(@dest_dir)
33
-
34
- templates.each do |value|
35
- src_file, dest_file = value
36
- eruby = Erubis::Eruby.new(File.read(src_path(src_file)))
37
- eruby.filename = src_path(src_file)
38
- context = {
39
- full_project_name: full_project_name,
40
- name: name,
41
- tumugi_version: Tumugi::VERSION,
42
- }
43
- puts " Create #{dest_path(dest_file)}"
44
- FileUtils.mkdir_p(File.dirname(dest_path(dest_file)))
45
- File.write(dest_path(dest_file), eruby.result(context))
46
- end
47
-
48
- puts ""
49
- puts "Plugin template is successfully generated."
50
- puts "Next steps:"
51
- puts ""
52
- puts " $ cd #{full_project_name}"
53
- puts " $ git init"
54
- puts " $ bundle install"
55
- puts " $ bundle exec rake"
56
- puts ""
57
-
58
- return true
7
+ def execute(type, name, options={})
8
+ generator = create_generator(type, name, options)
9
+ generator.generate
59
10
  end
60
11
 
61
- private
62
-
63
- def src_path(file)
64
- "#{@data_dir}/#{file}"
12
+ def logger
13
+ @logger ||= Tumugi::ScopedLogger.new("tumugi-new")
65
14
  end
66
15
 
67
- def dest_path(file)
68
- "#{@dest_dir}/#{file}"
16
+ def create_generator(type, name, options)
17
+ case type
18
+ when "plugin"
19
+ PluginGenerator.new(name, options)
20
+ when "project"
21
+ ProjectGenerator.new(name, options)
22
+ else
23
+ raise Tumugi::TumugiError.new("Unsupported type of new sub command: #{type}")
24
+ end
69
25
  end
70
26
  end
71
27
  end
@@ -0,0 +1,76 @@
1
+ require 'fileutils'
2
+ require 'erubis'
3
+
4
+ module Tumugi
5
+ module Command
6
+ class New
7
+ class Generator
8
+ attr_reader :name, :options
9
+
10
+ def initialize(name, options={})
11
+ @name = name
12
+ @options = options
13
+ end
14
+
15
+ def generate
16
+ if File.exist?(dest_dir) && !options[:force]
17
+ logger.error "#{dest_dir} is already exists. Please delete it first"
18
+ return false
19
+ end
20
+
21
+ logger.info "Create #{dest_dir}"
22
+ FileUtils.mkdir_p(dest_dir)
23
+
24
+ templates.each do |value|
25
+ src_file, dest_file = value
26
+ eruby = Erubis::Eruby.new(File.read(src_path(src_file)))
27
+ eruby.filename = src_path(src_file)
28
+ logger.info " Create #{dest_path(dest_file)}"
29
+ FileUtils.mkdir_p(File.dirname(dest_path(dest_file)))
30
+ File.write(dest_path(dest_file), eruby.result(context))
31
+ end
32
+
33
+ unless post_messages.empty?
34
+ post_messages.each{|msg| logger.info msg }
35
+ end
36
+
37
+ true
38
+ end
39
+
40
+ def data_dir
41
+ nil
42
+ end
43
+
44
+ def dest_dir
45
+ nil
46
+ end
47
+
48
+ def templates
49
+ []
50
+ end
51
+
52
+ def context
53
+ {}
54
+ end
55
+
56
+ def post_messages
57
+ []
58
+ end
59
+
60
+ def logger
61
+ @logger ||= Tumugi::ScopedLogger.new("tumugi-new")
62
+ end
63
+
64
+ private
65
+
66
+ def src_path(file)
67
+ File.join(data_dir, file)
68
+ end
69
+
70
+ def dest_path(file)
71
+ File.join(dest_dir, file)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,60 @@
1
+ require_relative 'generator'
2
+
3
+ module Tumugi
4
+ module Command
5
+ class New
6
+ class PluginGenerator < Generator
7
+ def data_dir
8
+ "#{File.expand_path(File.dirname(__FILE__))}/../../data/new/plugin"
9
+ end
10
+
11
+ def dest_dir
12
+ File.join(options[:path] || '.', full_project_name)
13
+ end
14
+
15
+ def templates
16
+ [
17
+ [ "Gemfile.erb", "Gemfile" ],
18
+ [ "gemspec.erb", "#{full_project_name}.gemspec" ],
19
+ [ "gitignore.erb", ".gitignore" ],
20
+ [ "Rakefile.erb", "Rakefile" ],
21
+ [ "README.md.erb", "README.md" ],
22
+ [ "examples/example.rb.erb", "examples/example.rb" ],
23
+ [ "lib/tumugi/plugin/target/target.rb.erb", "lib/tumugi/plugin/target/#{name}.rb" ],
24
+ [ "lib/tumugi/plugin/task/task.rb.erb", "lib/tumugi/plugin/task/#{name}.rb" ],
25
+ [ "test/test_helper.rb.erb", "test/test_helper.rb" ],
26
+ [ "test/test.rb.erb", "test/#{name}_test.rb" ],
27
+ [ "test/plugin/target/target_test.rb.erb", "test/plugin/target/#{name}_test.rb" ],
28
+ [ "test/plugin/task/task_test.rb.erb", "test/plugin/task/#{name}_test.rb" ],
29
+ ]
30
+ end
31
+
32
+ def full_project_name
33
+ "tumugi-plugin-#{name}"
34
+ end
35
+
36
+ def context
37
+ {
38
+ full_project_name: full_project_name,
39
+ name: name,
40
+ tumugi_version: Tumugi::VERSION,
41
+ }
42
+ end
43
+
44
+ def post_messages
45
+ [
46
+ "",
47
+ "Plugin template is successfully generated.",
48
+ "Next steps:",
49
+ "",
50
+ " $ cd #{full_project_name}",
51
+ " $ git init",
52
+ " $ bundle install",
53
+ " $ bundle exec rake",
54
+ "",
55
+ ]
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'generator'
2
+
3
+ module Tumugi
4
+ module Command
5
+ class New
6
+ class ProjectGenerator < Generator
7
+ def data_dir
8
+ "#{File.expand_path(File.dirname(__FILE__))}/../../data/new/project"
9
+ end
10
+
11
+ def dest_dir
12
+ File.join(options[:path] || '.', name)
13
+ end
14
+
15
+ def templates
16
+ [
17
+ [ "Gemfile.erb", "Gemfile" ],
18
+ [ "workflow.rb.erb", "workflow.rb" ],
19
+ [ "tumugi_config.rb.erb", "tumugi_config.rb" ],
20
+ ]
21
+ end
22
+
23
+ def context
24
+ {
25
+ name: name,
26
+ tumugi_version: Tumugi::VERSION,
27
+ }
28
+ end
29
+
30
+ def post_messages
31
+ [
32
+ "",
33
+ "Project template is successfully generated.",
34
+ "Next steps:",
35
+ "",
36
+ name.empty? ? "" : " $ cd #{name}",
37
+ " $ bundle install",
38
+ " $ bundle exec tumugi -f workflow.rb main",
39
+ "",
40
+ ]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -6,21 +6,14 @@ module Tumugi
6
6
  class Run
7
7
  def execute(dag, options={})
8
8
  worker_num = options[:workers] || Tumugi.config.workers
9
- executor = Tumugi::Executor::LocalExecutor.new(dag, worker_num: worker_num)
10
- result = start(executor)
9
+ executor = Tumugi::Executor::LocalExecutor.new(dag, worker_num: worker_num, run_all: options[:all])
10
+ result = executor.execute
11
11
  show_result_report(dag)
12
12
  result
13
13
  end
14
14
 
15
15
  private
16
16
 
17
- def start(executor)
18
- logger.info "workflow_start: #{Tumugi.workflow.id}"
19
- result = executor.execute
20
- logger.info "workflow_end: #{Tumugi.workflow.id}"
21
- result
22
- end
23
-
24
17
  def show_result_report(dag)
25
18
  reporter = Tumugi::DAGResultReporter.new
26
19
  report = reporter.show(dag)
@@ -1,5 +1,4 @@
1
1
  require 'terminal-table'
2
-
3
2
  require 'tumugi/mixin/listable'
4
3
 
5
4
  module Tumugi
@@ -7,18 +6,19 @@ module Tumugi
7
6
  include Tumugi::Mixin::Listable
8
7
 
9
8
  def show(dag)
10
- headings = ['Task', 'Requires', 'Parameters', 'State']
9
+ headings = ['Task', 'Requires', 'Parameters', 'State', 'Elapsed']
11
10
  Terminal::Table.new title: "Workflow Result", headings: headings do |t|
12
11
  dag.tsort.map.with_index do |task, index|
13
12
  proxy = task.class.merged_parameter_proxy
14
13
  requires = list(task.requires).map do |r|
15
14
  r.id
16
15
  end
17
- params = proxy.params.map do |name, _|
18
- "#{name}=#{truncate(task.send(name.to_sym).to_s, 25)}"
16
+ params = proxy.params.map do |name, param|
17
+ val = param.secret? ? '***' : truncate(task.send(name.to_sym).to_s, 25)
18
+ "#{name}=#{val}"
19
19
  end
20
20
  t << :separator if index != 0
21
- t << [ task.id, requires.join("\n"), params.join("\n"), task.state ]
21
+ t << [ task.id, requires.join("\n"), params.join("\n"), task.state, task.elapsed_time ]
22
22
  end
23
23
  end
24
24
  end
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'tumugi', '~> <%= tumugi_version %>'
@@ -0,0 +1,33 @@
1
+ Tumugi.configure do |config|
2
+ ##############################################################################
3
+ # Number of worker threads for parallel execution, default value is 1.
4
+ # You can override this value by CLI '-w' option like:
5
+ #
6
+ # $ tumugi run -f workflow.rb main -w 2
7
+ ##############################################################################
8
+ # config.workers = 1
9
+
10
+ ##############################################################################
11
+ # Max retry count for each task, default value is 3.
12
+ ##############################################################################
13
+ # config.max_retry = 3
14
+
15
+ ##############################################################################
16
+ # Retry interval seconds for each task, default value is 300 seconds.
17
+ ##############################################################################
18
+ # config.retry_interval = 300
19
+
20
+ ##############################################################################
21
+ # Timeout seconds for each task, default value is nil, it means no timeout.
22
+ ##############################################################################
23
+ # config.timeout = nil
24
+
25
+ ##############################################################################
26
+ # If you use plugins and plugins support config section,
27
+ # define it like following.
28
+ ##############################################################################
29
+
30
+ # config.section("section_name") do |section|
31
+ # section.key = "value"
32
+ # end
33
+ end
@@ -0,0 +1,3 @@
1
+ task :main do
2
+ run { log "Hello, tumugi!" }
3
+ end
@@ -0,0 +1,11 @@
1
+ module Tumugi
2
+ module Event
3
+ SUCCESS = :success
4
+ FAILURE = :failure
5
+ RETRY = :retry
6
+
7
+ def self.all
8
+ self.constants.map{|name| self.const_get(name) }
9
+ end
10
+ end
11
+ end
@@ -7,17 +7,18 @@ require 'tumugi/error'
7
7
  module Tumugi
8
8
  module Executor
9
9
  class LocalExecutor
10
- def initialize(dag, worker_num: 1)
10
+ def initialize(dag, worker_num: 1, run_all: false)
11
11
  @dag = dag
12
12
  @main_task = dag.tsort.last
13
- @options = { worker_num: worker_num }
13
+ @worker_num = worker_num
14
+ @run_all = run_all
14
15
  @mutex = Mutex.new
15
16
  end
16
17
 
17
18
  def execute
18
19
  pool = Concurrent::ThreadPoolExecutor.new(
19
- min_threads: @options[:worker_num],
20
- max_threads: @options[:worker_num]
20
+ min_threads: @worker_num,
21
+ max_threads: @worker_num
21
22
  )
22
23
 
23
24
  setup_task_queue(@dag)
@@ -27,7 +28,7 @@ module Tumugi
27
28
 
28
29
  Concurrent::Future.execute(executor: pool) do
29
30
  if !task.runnable?(Time.now)
30
- logger.trace { "task_cannot_run: #{task.id}" }
31
+ logger.trace { "task_not_runnable: #{task.id}" }
31
32
  enqueue_task(task)
32
33
  else
33
34
  begin
@@ -37,7 +38,8 @@ module Tumugi
37
38
  task.run
38
39
  end
39
40
  task.trigger!(:complete)
40
- logger.info { "task_#{task.state}: #{task.id}" }
41
+ logger.info { "task_#{task.state}: #{task.id}, elapsed_time: #{task.elapsed_time}" }
42
+ task.on_success
41
43
  rescue => e
42
44
  handle_error(task, e)
43
45
  end
@@ -83,10 +85,10 @@ module Tumugi
83
85
 
84
86
  if task.requires_failed?
85
87
  task.trigger!(:requires_fail)
86
- logger.info { "task_#{task.state}: #{task.id} has failed requires task" }
87
- elsif task.completed?
88
+ logger.info { "task_#{task.state}: #{task.id} has failed requires task, elapsed_time: #{task.elapsed_time}" }
89
+ elsif task.completed? && !@run_all
88
90
  task.trigger!(:skip)
89
- logger.info { "task_#{task.state}: #{task.id} is already completed" }
91
+ logger.info { "task_#{task.state}: #{task.id} is already completed, elapsed_time: #{task.elapsed_time}" }
90
92
  else
91
93
  break task
92
94
  end
@@ -102,15 +104,17 @@ module Tumugi
102
104
  def handle_error(task, err)
103
105
  if task.retry
104
106
  task.trigger!(:pend)
107
+ logger.info { "task_#{task.state}: #{task.id} failed, elapsed_time: #{task.elapsed_time}" }
105
108
  logger.error { "#{err.class}: '#{err.message}' - #{task.tries} tries and wait #{task.retry_interval} seconds until the next try." }
106
109
  enqueue_task(task)
110
+ task.on_retry
107
111
  else
108
112
  task.trigger!(:fail)
113
+ logger.info { "task_#{task.state}: #{task.id} failed, elapsed_time: #{task.elapsed_time}" }
109
114
  logger.error { "#{err.class}: '#{err.message}' - #{task.tries} tries and reached max retry count, so task #{task.id} failed." }
110
- logger.error { "#{err.message}" }
111
- logger.error { err.backtrace.join("\n") }
115
+ task.on_failure
112
116
  end
113
- logger.info { "task_#{task.state}: error handeling done for #{task.id}" }
117
+ logger.error { err.backtrace.join("\n") }
114
118
  end
115
119
 
116
120
  def logger
@@ -0,0 +1,16 @@
1
+ module Tumugi
2
+ module Mixin
3
+ module HumanReadable
4
+ def human_readable_time(seconds)
5
+ [[60, :s], [60, :m], [10000, :h]].map{|count, name|
6
+ if seconds > 0
7
+ seconds, n = seconds.divmod(count)
8
+ "#{sprintf('%02d', n)}"
9
+ else
10
+ '00'
11
+ end
12
+ }.compact.reverse.join(':')
13
+ end
14
+ end
15
+ end
16
+ end
@@ -20,15 +20,11 @@ module Tumugi
20
20
  end
21
21
 
22
22
  def auto_bind?
23
- if @opts[:auto_bind].nil?
24
- false
25
- else
26
- @opts[:auto_bind]
27
- end
23
+ option_as_bool(:auto_bind)
28
24
  end
29
25
 
30
26
  def required?
31
- @opts[:required].nil? ? false : @opts[:required]
27
+ option_as_bool(:required)
32
28
  end
33
29
 
34
30
  def type
@@ -43,6 +39,10 @@ module Tumugi
43
39
  self.class.new(@name, @opts.merge(required: false, default: value))
44
40
  end
45
41
 
42
+ def secret?
43
+ option_as_bool(:secret)
44
+ end
45
+
46
46
  private
47
47
 
48
48
  def search_from_workflow_parameters
@@ -51,13 +51,15 @@ module Tumugi
51
51
  value ? Converter.convert(type, value) : nil
52
52
  end
53
53
 
54
- private
55
-
56
54
  def validate
57
55
  if required? && !default_value.nil?
58
56
  raise Tumugi::ParameterError.new("When you set required: true, you cannot set default value")
59
57
  end
60
58
  end
59
+
60
+ def option_as_bool(key)
61
+ @opts[key].nil? ? false : @opts[key]
62
+ end
61
63
  end
62
64
  end
63
65
  end
@@ -1,15 +1,18 @@
1
+ require 'tumugi/event'
1
2
  require 'tumugi/logger/scoped_logger'
2
3
  require 'tumugi/mixin/listable'
3
4
  require 'tumugi/mixin/task_helper'
4
5
  require 'tumugi/mixin/parameterizable'
6
+ require 'tumugi/mixin/human_readable'
5
7
 
6
8
  module Tumugi
7
9
  class Task
8
10
  include Tumugi::Mixin::Parameterizable
9
11
  include Tumugi::Mixin::Listable
10
12
  include Tumugi::Mixin::TaskHelper
13
+ include Tumugi::Mixin::HumanReadable
11
14
 
12
- attr_reader :visible_at, :tries, :max_retry, :retry_interval
15
+ attr_reader :visible_at, :tries, :max_retry, :retry_interval, :elapsed_time
13
16
 
14
17
  AVAILABLE_STATES = [
15
18
  :pending,
@@ -28,6 +31,8 @@ module Tumugi
28
31
  @retry_interval = Tumugi.config.retry_interval
29
32
  @state = :pending
30
33
  @lock = Mutex.new
34
+ @_elapsed_times = []
35
+ @elapsed_time = '00:00:00'
31
36
  end
32
37
 
33
38
  def id
@@ -98,6 +103,27 @@ module Tumugi
98
103
  ready? && visible?(now)
99
104
  end
100
105
 
106
+ def ready?
107
+ list(_requires).all? { |t| t.instance.completed? }
108
+ end
109
+
110
+ def completed?
111
+ outputs = list(output)
112
+ if outputs.empty?
113
+ success?
114
+ else
115
+ outputs.all?(&:exist?)
116
+ end
117
+ end
118
+
119
+ def requires_failed?
120
+ list(_requires).any? { |t| t.instance.finished? && !t.instance.success? }
121
+ end
122
+
123
+ def runnable?(now)
124
+ ready? && visible?(now)
125
+ end
126
+
101
127
  def success?
102
128
  case state
103
129
  when :completed, :skipped
@@ -107,15 +133,19 @@ module Tumugi
107
133
  end
108
134
  end
109
135
 
110
- def finished?
136
+ def failure?
111
137
  case state
112
- when :completed, :skipped, :failed, :requires_failed
138
+ when :failed, :requires_failed
113
139
  true
114
140
  else
115
141
  false
116
142
  end
117
143
  end
118
144
 
145
+ def finished?
146
+ success? or failure?
147
+ end
148
+
119
149
  def timeout
120
150
  nil # meaning use default timeout
121
151
  end
@@ -132,6 +162,9 @@ module Tumugi
132
162
 
133
163
  def trigger!(event)
134
164
  @lock.synchronize do
165
+ now = Time.now
166
+ @_elapsed_times[tries] ||= { start: now }
167
+
135
168
  s = case event
136
169
  when :skip
137
170
  :skipped
@@ -153,10 +186,20 @@ module Tumugi
153
186
  raise Tumugi::TumugiError.new("Invalid state: #{s}")
154
187
  end
155
188
 
189
+ @_elapsed_times[tries][:end] = now
190
+ @elapsed_time = human_readable_time((@_elapsed_times[tries][:end] - @_elapsed_times[tries][:start]).to_i)
156
191
  @state = s
157
192
  end
158
193
  end
159
194
 
195
+ # Event callbacks
196
+ Event.all.each do |event|
197
+ class_eval <<-EOS
198
+ def on_#{event}
199
+ end
200
+ EOS
201
+ end
202
+
160
203
  # Following methods are internal use only
161
204
 
162
205
  def _requires
@@ -1,3 +1,4 @@
1
+ require 'tumugi/event'
1
2
  require 'tumugi/plugin'
2
3
  require 'tumugi/task'
3
4
  require 'tumugi/logger/logger'
@@ -64,6 +65,14 @@ module Tumugi
64
65
  @run = block
65
66
  end
66
67
 
68
+ Event.all.each do |event|
69
+ class_eval <<-EOS
70
+ def on_#{event}(&block)
71
+ @on_#{event} ||= block
72
+ end
73
+ EOS
74
+ end
75
+
67
76
  def output_eval(task)
68
77
  @out ||= @outputs.is_a?(Proc) ? task.instance_eval(&@outputs) : @outputs
69
78
  end
@@ -72,10 +81,6 @@ module Tumugi
72
81
  @required_tasks
73
82
  end
74
83
 
75
- def run_block(task)
76
- task.instance_eval(&@run)
77
- end
78
-
79
84
  def parent_task_class
80
85
  if @opts[:type].is_a?(Class)
81
86
  @opts[:type]
@@ -84,6 +89,15 @@ module Tumugi
84
89
  end
85
90
  end
86
91
 
92
+ def run_block(task)
93
+ task.instance_eval(&@run)
94
+ end
95
+
96
+ def event_block(task, event)
97
+ callback = instance_variable_get("@on_#{event}")
98
+ task.instance_eval(&callback)
99
+ end
100
+
87
101
  private
88
102
 
89
103
  def create_task
@@ -98,6 +112,7 @@ module Tumugi
98
112
  define_requires_method(task_class)
99
113
  define_output_method(task_class)
100
114
  define_run_method(task_class)
115
+ define_event_callback_methods(task_class)
101
116
  setup_params(task_class)
102
117
  task_class
103
118
  end
@@ -138,6 +153,19 @@ module Tumugi
138
153
  end unless @run.nil?
139
154
  end
140
155
 
156
+ def define_event_callback_methods(task_class)
157
+ td = self
158
+ Event.all.each do |event|
159
+ if instance_variable_get("@on_#{event}")
160
+ task_class.class_eval do
161
+ define_method(:"on_#{event}") do
162
+ td.event_block(self, event)
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+
141
169
  def setup_params(task_class)
142
170
  @params.each do |name, opts|
143
171
  task_class.param(name, opts)
@@ -1,3 +1,3 @@
1
1
  module Tumugi
2
- VERSION = "0.6.3"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -5,11 +5,14 @@ require 'tumugi/plugin'
5
5
  require 'tumugi/target'
6
6
  require 'tumugi/command/run'
7
7
  require 'tumugi/command/show'
8
+ require 'tumugi/mixin/human_readable'
8
9
 
9
10
  require 'securerandom'
10
11
 
11
12
  module Tumugi
12
13
  class Workflow
14
+ include Tumugi::Mixin::HumanReadable
15
+
13
16
  attr_reader :id
14
17
  attr_accessor :params
15
18
 
@@ -22,12 +25,14 @@ module Tumugi
22
25
  end
23
26
 
24
27
  def execute(command, root_task_id, options)
28
+ @start_time = Time.now
29
+ logger.info "start id: #{id}"
25
30
  process_common_options(command, options)
26
31
  load_workflow_file(options[:file])
27
- dag = create_dag(root_task_id)
28
- command_module = Kernel.const_get("Tumugi").const_get("Command")
29
- cmd = command_module.const_get("#{command.to_s.capitalize}").new
30
- cmd.execute(dag, options)
32
+ result = execute_command(command, root_task_id, options)
33
+ @end_time = Time.now
34
+ logger.info "end id: #{id}, elapsed_time: #{elapsed_time}"
35
+ result
31
36
  end
32
37
 
33
38
  def add_task(id, task)
@@ -55,13 +60,6 @@ module Tumugi
55
60
  end
56
61
  end
57
62
 
58
- def create_dag(id)
59
- dag = Tumugi::DAG.new
60
- task = find_task(id)
61
- dag.add_task(task)
62
- dag
63
- end
64
-
65
63
  def process_common_options(command, options)
66
64
  setup_logger(command, options)
67
65
  load_config(options)
@@ -111,5 +109,23 @@ module Tumugi
111
109
  logger.info "Parameters: #{@params}"
112
110
  end
113
111
  end
112
+
113
+ def elapsed_time
114
+ human_readable_time((@end_time - @start_time).to_i)
115
+ end
116
+
117
+ def execute_command(command, root_task_id, options)
118
+ dag = create_dag(root_task_id)
119
+ command_module = Kernel.const_get("Tumugi").const_get("Command")
120
+ cmd = command_module.const_get("#{command.to_s.capitalize}").new
121
+ cmd.execute(dag, options)
122
+ end
123
+
124
+ def create_dag(id)
125
+ dag = Tumugi::DAG.new
126
+ task = find_task(id)
127
+ dag.add_task(task)
128
+ dag
129
+ end
114
130
  end
115
131
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tumugi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuyuki Honda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-08-13 00:00:00.000000000 Z
11
+ date: 2016-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -184,6 +184,7 @@ files:
184
184
  - examples/concurrent_task_run.rb
185
185
  - examples/config_section.rb
186
186
  - examples/data_pipeline.rb
187
+ - examples/event_callbacks.rb
187
188
  - examples/fail_first_task.rb
188
189
  - examples/fail_intermediate_task.rb
189
190
  - examples/fail_last_task.rb
@@ -198,29 +199,37 @@ files:
198
199
  - lib/tumugi/atomic_file.rb
199
200
  - lib/tumugi/cli.rb
200
201
  - lib/tumugi/command/new.rb
202
+ - lib/tumugi/command/new/generator.rb
203
+ - lib/tumugi/command/new/plugin_generator.rb
204
+ - lib/tumugi/command/new/project_generator.rb
201
205
  - lib/tumugi/command/run.rb
202
206
  - lib/tumugi/command/show.rb
203
207
  - lib/tumugi/config.rb
204
208
  - lib/tumugi/dag.rb
205
209
  - lib/tumugi/dag_result_reporter.rb
206
- - lib/tumugi/data/new/Gemfile.erb
207
- - lib/tumugi/data/new/README.md.erb
208
- - lib/tumugi/data/new/Rakefile.erb
209
- - lib/tumugi/data/new/examples/example.rb.erb
210
- - lib/tumugi/data/new/gemspec.erb
211
- - lib/tumugi/data/new/gitignore.erb
212
- - lib/tumugi/data/new/lib/tumugi/plugin/target/target.rb.erb
213
- - lib/tumugi/data/new/lib/tumugi/plugin/task/task.rb.erb
214
- - lib/tumugi/data/new/test/plugin/target/target_test.rb.erb
215
- - lib/tumugi/data/new/test/plugin/task/task_test.rb.erb
216
- - lib/tumugi/data/new/test/test.rb.erb
217
- - lib/tumugi/data/new/test/test_helper.rb.erb
210
+ - lib/tumugi/data/new/plugin/Gemfile.erb
211
+ - lib/tumugi/data/new/plugin/README.md.erb
212
+ - lib/tumugi/data/new/plugin/Rakefile.erb
213
+ - lib/tumugi/data/new/plugin/examples/example.rb.erb
214
+ - lib/tumugi/data/new/plugin/gemspec.erb
215
+ - lib/tumugi/data/new/plugin/gitignore.erb
216
+ - lib/tumugi/data/new/plugin/lib/tumugi/plugin/target/target.rb.erb
217
+ - lib/tumugi/data/new/plugin/lib/tumugi/plugin/task/task.rb.erb
218
+ - lib/tumugi/data/new/plugin/test/plugin/target/target_test.rb.erb
219
+ - lib/tumugi/data/new/plugin/test/plugin/task/task_test.rb.erb
220
+ - lib/tumugi/data/new/plugin/test/test.rb.erb
221
+ - lib/tumugi/data/new/plugin/test/test_helper.rb.erb
222
+ - lib/tumugi/data/new/project/Gemfile.erb
223
+ - lib/tumugi/data/new/project/tumugi_config.rb.erb
224
+ - lib/tumugi/data/new/project/workflow.rb.erb
218
225
  - lib/tumugi/dsl.rb
219
226
  - lib/tumugi/error.rb
227
+ - lib/tumugi/event.rb
220
228
  - lib/tumugi/executor/local_executor.rb
221
229
  - lib/tumugi/file_system.rb
222
230
  - lib/tumugi/logger/logger.rb
223
231
  - lib/tumugi/logger/scoped_logger.rb
232
+ - lib/tumugi/mixin/human_readable.rb
224
233
  - lib/tumugi/mixin/listable.rb
225
234
  - lib/tumugi/mixin/parameterizable.rb
226
235
  - lib/tumugi/mixin/task_helper.rb