tumugi 0.6.3 → 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.
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