rake-funnel 0.0.1.pre

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 (111) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +21 -0
  5. data/Gemfile +29 -0
  6. data/Guardfile +24 -0
  7. data/README.md +29 -0
  8. data/Rakefile +77 -0
  9. data/build.cmd +30 -0
  10. data/bundle.cmd +26 -0
  11. data/config/.gitignore +1 -0
  12. data/config/.local.yaml.example +9 -0
  13. data/config/default.yaml +5 -0
  14. data/config/dev.yaml +0 -0
  15. data/config/production.yaml +3 -0
  16. data/lib/rake/funnel/ambiguous_file_error.rb +29 -0
  17. data/lib/rake/funnel/execution_error.rb +26 -0
  18. data/lib/rake/funnel/extensions/camel_case.rb +19 -0
  19. data/lib/rake/funnel/extensions/common_path.rb +52 -0
  20. data/lib/rake/funnel/extensions/disable_colors.rb +27 -0
  21. data/lib/rake/funnel/extensions/rexml.rb +23 -0
  22. data/lib/rake/funnel/extensions/shell.rb +56 -0
  23. data/lib/rake/funnel/framework.rb +15 -0
  24. data/lib/rake/funnel/integration/progress_report.rb +70 -0
  25. data/lib/rake/funnel/integration/sync_output.rb +8 -0
  26. data/lib/rake/funnel/integration/teamcity/nunit_plugin.rb +59 -0
  27. data/lib/rake/funnel/integration/teamcity/progress_report.rb +33 -0
  28. data/lib/rake/funnel/integration/teamcity/service_messages.rb +40 -0
  29. data/lib/rake/funnel/integration/teamcity/teamcity.rb +15 -0
  30. data/lib/rake/funnel/integration/teamcity.rb +5 -0
  31. data/lib/rake/funnel/support/finder.rb +51 -0
  32. data/lib/rake/funnel/support/mapper.rb +81 -0
  33. data/lib/rake/funnel/support/mapper_styles/default.rb +31 -0
  34. data/lib/rake/funnel/support/mapper_styles/msbuild.rb +33 -0
  35. data/lib/rake/funnel/support/mapper_styles/msdeploy.rb +47 -0
  36. data/lib/rake/funnel/support/mapper_styles/nunit.rb +33 -0
  37. data/lib/rake/funnel/support/mono.rb +17 -0
  38. data/lib/rake/funnel/support/patch.rb +37 -0
  39. data/lib/rake/funnel/support/template_engine.rb +26 -0
  40. data/lib/rake/funnel/support/which.rb +15 -0
  41. data/lib/rake/funnel/tasks/bin_path.rb +34 -0
  42. data/lib/rake/funnel/tasks/copy.rb +54 -0
  43. data/lib/rake/funnel/tasks/environments.rb +74 -0
  44. data/lib/rake/funnel/tasks/environments_support/loader.rb +37 -0
  45. data/lib/rake/funnel/tasks/msbuild.rb +52 -0
  46. data/lib/rake/funnel/tasks/msbuild_support/build_tool.rb +28 -0
  47. data/lib/rake/funnel/tasks/msdeploy.rb +58 -0
  48. data/lib/rake/funnel/tasks/msdeploy_support/registry_patch.rb +84 -0
  49. data/lib/rake/funnel/tasks/nunit.rb +46 -0
  50. data/lib/rake/funnel/tasks/paket.rb +39 -0
  51. data/lib/rake/funnel/tasks/quick_template.rb +45 -0
  52. data/lib/rake/funnel/tasks/side_by_side_specs.rb +33 -0
  53. data/lib/rake/funnel/tasks/side_by_side_specs_support/remover.rb +62 -0
  54. data/lib/rake/funnel/tasks/timing.rb +100 -0
  55. data/lib/rake/funnel/tasks/timing_support/report.rb +89 -0
  56. data/lib/rake/funnel/tasks/timing_support/statistics.rb +26 -0
  57. data/lib/rake/funnel/tasks/zip.rb +66 -0
  58. data/lib/rake/funnel/version.rb +5 -0
  59. data/lib/rake/funnel.rb +7 -0
  60. data/rake-funnel.gemspec +28 -0
  61. data/spec/rake/funnel/execution_error_spec.rb +67 -0
  62. data/spec/rake/funnel/extensions/camel_case_spec.rb +17 -0
  63. data/spec/rake/funnel/extensions/common_path_spec.rb +56 -0
  64. data/spec/rake/funnel/extensions/disable_colors_spec.rb +33 -0
  65. data/spec/rake/funnel/extensions/rexml_spec.rb +20 -0
  66. data/spec/rake/funnel/extensions/shell_spec.rb +237 -0
  67. data/spec/rake/funnel/integration/progress_report_spec.rb +149 -0
  68. data/spec/rake/funnel/integration/sync_output_spec.rb +16 -0
  69. data/spec/rake/funnel/integration/teamcity/nunit_plugin_spec.rb +112 -0
  70. data/spec/rake/funnel/integration/teamcity/progress_report_spec.rb +174 -0
  71. data/spec/rake/funnel/integration/teamcity/service_messages_spec.rb +136 -0
  72. data/spec/rake/funnel/integration/teamcity/teamcity_spec.rb +34 -0
  73. data/spec/rake/funnel/support/finder_spec.rb +210 -0
  74. data/spec/rake/funnel/support/mapper_spec.rb +87 -0
  75. data/spec/rake/funnel/support/mapper_styles/msdeploy_spec.rb +222 -0
  76. data/spec/rake/funnel/support/mapper_styles/nunit_spec.rb +25 -0
  77. data/spec/rake/funnel/support/mapper_styles/styles_spec.rb +214 -0
  78. data/spec/rake/funnel/support/mono_spec.rb +57 -0
  79. data/spec/rake/funnel/support/patch_spec.rb +108 -0
  80. data/spec/rake/funnel/support/template_engine_spec.rb +65 -0
  81. data/spec/rake/funnel/support/which_spec.rb +65 -0
  82. data/spec/rake/funnel/tasks/bin_path_spec.rb +40 -0
  83. data/spec/rake/funnel/tasks/copy_spec.rb +101 -0
  84. data/spec/rake/funnel/tasks/environments_spec.rb +237 -0
  85. data/spec/rake/funnel/tasks/environments_support/loader_spec.rb +114 -0
  86. data/spec/rake/funnel/tasks/msbuild_spec.rb +91 -0
  87. data/spec/rake/funnel/tasks/msbuild_support/build_tool_spec.rb +21 -0
  88. data/spec/rake/funnel/tasks/msdeploy_spec.rb +243 -0
  89. data/spec/rake/funnel/tasks/msdeploy_support/registry_patch_spec.rb +139 -0
  90. data/spec/rake/funnel/tasks/nunit_spec.rb +76 -0
  91. data/spec/rake/funnel/tasks/paket_spec.rb +184 -0
  92. data/spec/rake/funnel/tasks/quick_template_spec.rb +89 -0
  93. data/spec/rake/funnel/tasks/side_by_side_specs_spec.rb +58 -0
  94. data/spec/rake/funnel/tasks/side_by_side_specs_support/example/FooCode.cs +0 -0
  95. data/spec/rake/funnel/tasks/side_by_side_specs_support/example/FooSpecs.cs +0 -0
  96. data/spec/rake/funnel/tasks/side_by_side_specs_support/example/Sample.csproj +28 -0
  97. data/spec/rake/funnel/tasks/side_by_side_specs_support/example/Specs.cs +0 -0
  98. data/spec/rake/funnel/tasks/side_by_side_specs_support/example/subdir/BarCode.cs +0 -0
  99. data/spec/rake/funnel/tasks/side_by_side_specs_support/example/subdir/BarSpecs.cs +0 -0
  100. data/spec/rake/funnel/tasks/side_by_side_specs_support/remover_spec.rb +116 -0
  101. data/spec/rake/funnel/tasks/timing_spec.rb +133 -0
  102. data/spec/rake/funnel/tasks/timing_support/report_spec.rb +129 -0
  103. data/spec/rake/funnel/tasks/zip_spec.rb +119 -0
  104. data/spec/spec_helper.rb +32 -0
  105. data/tools/MSDeploy/Microsoft.Web.Delegation.dll +0 -0
  106. data/tools/MSDeploy/Microsoft.Web.Deployment.Tracing.dll +0 -0
  107. data/tools/MSDeploy/Microsoft.Web.Deployment.dll +0 -0
  108. data/tools/MSDeploy/en/msdeploy.resources.dll +0 -0
  109. data/tools/MSDeploy/msdeploy.exe +0 -0
  110. data/tools/MSDeploy/msdeploy.exe.config +6 -0
  111. metadata +253 -0
@@ -0,0 +1,52 @@
1
+ require 'rake/tasklib'
2
+
3
+ Dir["#{File.dirname(__FILE__)}/msbuild_support/*.rb"].each do |path|
4
+ require path
5
+ end
6
+
7
+ module Rake::Funnel::Tasks
8
+ class MSBuild < Rake::TaskLib
9
+ attr_accessor :name, :project_or_solution, :args, :search_pattern
10
+
11
+ def initialize(name = :compile)
12
+ @name = name
13
+ @args = {}
14
+ @search_pattern = %w(**/*.sln)
15
+
16
+ yield self if block_given?
17
+ define
18
+ end
19
+
20
+ def msbuild
21
+ @_msbuild || MSBuildSupport::BuildTool.find
22
+ end
23
+
24
+ def msbuild=(value)
25
+ @_msbuild = value
26
+ end
27
+
28
+ def project_or_solution
29
+ Rake::Funnel::Support::Finder.new(@_project_or_solution || search_pattern, self, 'No projects or more than one project found.')
30
+ end
31
+
32
+ def project_or_solution=(value)
33
+ @_project_or_solution = value
34
+ end
35
+
36
+ private
37
+ def define
38
+ desc "Compile #{project_or_solution.single_or_default}"
39
+ task @name do
40
+ cmd = [
41
+ msbuild,
42
+ project_or_solution.single,
43
+ *Rake::Funnel::Support::Mapper.new(:MSBuild).map(args)
44
+ ]
45
+
46
+ sh(*cmd)
47
+ end
48
+
49
+ self
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ module Rake::Funnel::Tasks::MSBuildSupport
2
+ class BuildTool
3
+ class << self
4
+ def find
5
+ if Rake::Win32.windows?
6
+ require 'win32/registry'
7
+
8
+ %w{12.0 4.0 3.5 2.0}.collect { |version|
9
+ msbuild = nil
10
+ key = "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\#{version}"
11
+
12
+ begin
13
+ Win32::Registry::HKEY_LOCAL_MACHINE.open(key) do |reg|
14
+ candidate = "#{reg['MSBuildToolsPath']}\\msbuild.exe"
15
+ msbuild = candidate if File.exists?(candidate)
16
+ end
17
+ rescue
18
+ end
19
+
20
+ msbuild
21
+ }.compact.first
22
+ else
23
+ 'xbuild'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,58 @@
1
+ require 'rake/clean'
2
+ require 'rake/tasklib'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/msdeploy_support/*.rb"].each do |path|
5
+ require path
6
+ end
7
+
8
+ module Rake::Funnel::Tasks
9
+ class MSDeploy < Rake::TaskLib
10
+ attr_accessor :name, :msdeploy, :log_file, :args
11
+
12
+ def initialize(name = :msdeploy)
13
+ @name = name
14
+ @msdeploy = 'msdeploy'
15
+ @args = {}
16
+
17
+ yield self if block_given?
18
+ define
19
+ end
20
+
21
+ def log_file
22
+ @log_file || "#{@name}.log"
23
+ end
24
+
25
+ private
26
+ def define
27
+ CLEAN.include(log_file)
28
+
29
+ desc "Deploy #{deploy_source(args)}"
30
+ task @name do
31
+ mapper = Rake::Funnel::Support::Mapper.new(:MSDeploy)
32
+ cmd = [quote(msdeploy), mapper.map(args)]
33
+ .flatten
34
+ .join(' ')
35
+
36
+ MSDeploySupport::RegistryPatch.new do
37
+ shell(cmd, log_file: log_file, error_lines: /^(error|[\w\.]*exception)/i)
38
+ end
39
+ end
40
+
41
+ self
42
+ end
43
+
44
+ def deploy_source(args)
45
+ source = (args || {}).fetch(:source, {})
46
+ path = source.first
47
+ return if path.nil?
48
+
49
+ Pathname.new(path[1]).relative_path_from(Pathname.new('.').realpath) rescue path[1]
50
+ end
51
+
52
+ def quote(value)
53
+ value = value.gsub(/"/, '""') if value.kind_of?(String)
54
+ return %Q{"#{value}"} if value =~ /\s/
55
+ value
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,84 @@
1
+ module Rake::Funnel::Tasks::MSDeploySupport
2
+ class RegistryPatch
3
+ KEYS = [
4
+ 'SOFTWARE\Microsoft\IIS Extensions\MSDeploy\3',
5
+ 'SOFTWARE\Wow6432Node\Microsoft\IIS Extensions\MSDeploy\3'
6
+ ]
7
+ VERSION_VALUE = 'Version'
8
+ FAKE_VERSION = '99.0.0.0'
9
+
10
+ def initialize(&block)
11
+ begin
12
+ patch.apply!
13
+ yield block if block_given?
14
+ ensure
15
+ patch.revert!
16
+ end
17
+ end
18
+
19
+ private
20
+ def patch
21
+ @patch ||= create_patch
22
+ end
23
+
24
+ def root
25
+ Win32::Registry::HKEY_LOCAL_MACHINE
26
+ end
27
+
28
+ def delete_key(key)
29
+ return nil unless key.created?
30
+
31
+ keyname = key.keyname
32
+ Proc.new {
33
+ root.create(File.dirname(keyname)) do |r|
34
+ r.delete_key(File.basename(keyname), true)
35
+ end
36
+ }
37
+ end
38
+
39
+ def delete_value(key, value)
40
+ keyname = key.keyname
41
+ Proc.new {
42
+ root.create(keyname) do |r|
43
+ r.delete_value(value)
44
+ end
45
+ }
46
+ end
47
+
48
+ def create_patch
49
+ begin
50
+ require 'win32/registry'
51
+ rescue LoadError
52
+ return Rake::Funnel::Support::Patch.new(self)
53
+ end
54
+
55
+ Rake::Funnel::Support::Patch.new(self) do |p|
56
+ resets = []
57
+
58
+ p.setup do
59
+ resets = KEYS.map do |key|
60
+ root.create(key) do |r|
61
+ begin
62
+ r[VERSION_VALUE]
63
+
64
+ delete_version = Proc.new {}
65
+ rescue Win32::Registry::Error
66
+ r[VERSION_VALUE] = FAKE_VERSION
67
+
68
+ delete_version = delete_value(r, VERSION_VALUE)
69
+ end
70
+
71
+ delete_key(r) || delete_version
72
+ end
73
+ end
74
+ end
75
+
76
+ p.reset do
77
+ resets.compact.each do |reset|
78
+ reset.call
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,46 @@
1
+ require 'rake/tasklib'
2
+
3
+ module Rake::Funnel::Tasks
4
+ class NUnit < Rake::TaskLib
5
+ attr_accessor :name, :files, :args
6
+
7
+ def initialize(name = :test)
8
+ @name = name
9
+ @args = {}
10
+ @files = %w(build/specs/**/*.dll build/specs/**/*.exe)
11
+
12
+ yield self if block_given?
13
+ define
14
+ end
15
+
16
+ def nunit
17
+ @_nunit || 'nunit-console.exe'
18
+ end
19
+
20
+ def nunit=(value)
21
+ @_nunit = value
22
+ end
23
+
24
+ private
25
+ def define
26
+ desc "Test #{test_assemblies.all_or_default.join(', ')}"
27
+ task name do
28
+ Rake::Funnel::Integration::TeamCity::NUnitPlugin.setup(nunit)
29
+
30
+ cmd = [
31
+ *Rake::Funnel::Support::Mono.invocation(nunit),
32
+ *test_assemblies.all,
33
+ *Rake::Funnel::Support::Mapper.new(:NUnit).map(args)
34
+ ]
35
+
36
+ sh(*cmd)
37
+ end
38
+
39
+ self
40
+ end
41
+
42
+ def test_assemblies
43
+ Rake::Funnel::Support::Finder.new(files, self, 'No test assemblies found.')
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,39 @@
1
+ require 'rake/tasklib'
2
+
3
+ module Rake::Funnel::Tasks
4
+ class Paket < Rake::TaskLib
5
+ attr_accessor :name, :paket, :paket_args, :bootstrapper, :bootstrapper_args
6
+
7
+ def initialize(name = :paket)
8
+ @name = name
9
+
10
+ @paket = File.join('.paket', 'paket.exe')
11
+ @paket_args = 'restore'
12
+
13
+ @bootstrapper = File.join('.paket', 'paket.bootstrapper.exe')
14
+ @bootstrapper_args = nil
15
+
16
+ yield self if block_given?
17
+ define
18
+ end
19
+
20
+ private
21
+ def define
22
+ desc "#{paket_cmd.join(' ')} (optionally #{bootstrapper_cmd.join(' ')})"
23
+ task name do
24
+ sh(*bootstrapper_cmd) unless File.exist?(paket)
25
+ sh(*paket_cmd)
26
+ end
27
+
28
+ self
29
+ end
30
+
31
+ def bootstrapper_cmd
32
+ Rake::Funnel::Support::Mono.invocation(bootstrapper, bootstrapper_args)
33
+ end
34
+
35
+ def paket_cmd
36
+ Rake::Funnel::Support::Mono.invocation(paket, paket_args)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ require 'rake/clean'
2
+ require 'rake/tasklib'
3
+
4
+ module Rake::Funnel::Tasks
5
+ class QuickTemplate < Rake::TaskLib
6
+ attr_accessor :name, :search_pattern, :context
7
+
8
+ def initialize(name = :template)
9
+ @name = name
10
+
11
+ @search_pattern = %w(**/*.template)
12
+ @context = binding
13
+
14
+ yield self if block_given?
15
+ define
16
+ end
17
+
18
+ private
19
+ def define
20
+ results = templates.all_or_default.map { |t| result_filename(t) }
21
+ CLEAN.include(*results)
22
+
23
+ desc "Generate #{templates.all_or_default.join(', ')}"
24
+ task name do
25
+ templates.all_or_default.each do |template|
26
+ target = result_filename(template)
27
+ Rake.rake_output_message "Creating file #{target}"
28
+
29
+ content = Rake::Funnel::Support::TemplateEngine.render(File.read(template), template, context)
30
+ File.write(target, content)
31
+ end
32
+ end
33
+
34
+ self
35
+ end
36
+
37
+ def templates
38
+ Rake::Funnel::Support::Finder.new(search_pattern, self, 'No templates found.')
39
+ end
40
+
41
+ def result_filename(template)
42
+ template.ext
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ require 'rake/tasklib'
2
+
3
+ Dir["#{File.dirname(__FILE__)}/side_by_side_specs_support/*.rb"].each do |path|
4
+ require path
5
+ end
6
+
7
+ module Rake::Funnel::Tasks
8
+ class SideBySideSpecs < Rake::TaskLib
9
+ attr_accessor :name, :projects, :references, :specs, :enabled
10
+
11
+ def initialize(name = :compile)
12
+ @name = name
13
+
14
+ @projects = %w(**/*.csproj **/*.vbproj **/*.fsproj)
15
+ @references = []
16
+ @specs = %w(*Specs.cs **/*Specs.cs *Tests.cs **/*Tests.cs)
17
+ @enabled = false
18
+
19
+ yield self if block_given?
20
+ define
21
+ end
22
+
23
+ private
24
+ def define
25
+ task name do
26
+ next unless enabled
27
+ SideBySideSpecsSupport::Remover.remove({ projects: projects, references: references, specs: specs })
28
+ end
29
+
30
+ self
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ require 'rexml/document'
2
+
3
+ module Rake::Funnel::Tasks::SideBySideSpecsSupport
4
+ class Remover
5
+ class << self
6
+ DEFAULTS = {
7
+ projects: [],
8
+ references: [],
9
+ specs: []
10
+ }
11
+
12
+ def remove(args = {})
13
+ args = DEFAULTS.merge(args)
14
+
15
+ projects(args).each do |project|
16
+ xml = REXML::Document.new(File.read(project), { attribute_quote: :quote })
17
+
18
+ removed = remove_references(args, xml) + remove_specs(args, xml)
19
+
20
+ write_xml(project, xml) if removed.flatten.any?
21
+ end
22
+
23
+ delete_specs(args)
24
+ end
25
+
26
+ private
27
+ def write_xml(project, xml)
28
+ File.open(project, 'w+') do |file|
29
+ xml.write(output: file, ie_hack: true)
30
+ end
31
+ end
32
+
33
+ def delete_specs(args)
34
+ Dir[*args[:specs]].uniq.each do |spec|
35
+ RakeFileUtils.rm(spec)
36
+ end
37
+ end
38
+
39
+ def projects(args)
40
+ Dir[*args[:projects]]
41
+ end
42
+
43
+ def list(args)
44
+ ([] << args).flatten
45
+ end
46
+
47
+ def remove_references(args, xml)
48
+ list(args[:references]).map do |ref|
49
+ query = "/Project//Reference[starts-with(lower-case(@Include), '#{ref.downcase}')]"
50
+ xml.elements.delete_all(query)
51
+ end
52
+ end
53
+
54
+ def remove_specs(args, xml)
55
+ list(args[:specs]).map do |glob|
56
+ query = "/Project//Compile[matches(lower-case(@Include), '#{glob}')]"
57
+ xml.elements.delete_all(query)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,100 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/timing_support/*.rb"].each do |path|
5
+ require path
6
+ end
7
+
8
+ module Rake::Funnel::Tasks
9
+ class Timing < Rake::TaskLib
10
+ attr_accessor :name
11
+ attr_reader :stats
12
+
13
+ def initialize(name = :timing)
14
+ @name = name
15
+ @stats = TimingSupport::Statistics.new
16
+
17
+ yield self if block_given?
18
+
19
+ define
20
+ end
21
+
22
+ def reset!
23
+ patches.each { |p| p.revert! }
24
+ end
25
+
26
+ private
27
+ def define
28
+ patches.each { |p| p.apply! }
29
+
30
+ task @name, :failed do |task, args|
31
+ TimingSupport::Report.new(@stats, args).render
32
+ end
33
+
34
+ timing_task = Rake.application.current_scope.path_with_task_name(@name)
35
+ Rake.application.top_level_tasks.push(timing_task)
36
+
37
+ self
38
+ end
39
+
40
+ def patches
41
+ @patches ||= create_patches
42
+ end
43
+
44
+ def create_patches
45
+ report = Rake::Funnel::Support::Patch.new do |p|
46
+ report_invoker = -> (opts) { TimingSupport::Report.new(@stats, opts).render }
47
+
48
+ p.setup do
49
+ Rake::Application.class_eval do
50
+ orig_display_error_message = instance_method(:display_error_message)
51
+
52
+ define_method(:display_error_message) do |ex|
53
+ orig_display_error_message.bind(self).call(ex)
54
+
55
+ report_invoker.call(failed: true)
56
+ end
57
+
58
+ orig_display_error_message
59
+ end
60
+ end
61
+
62
+ p.reset do |memo|
63
+ Rake::Application.class_eval do
64
+ define_method(:display_error_message) do |ex|
65
+ memo.bind(self).call(ex)
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ benchmark = Rake::Funnel::Support::Patch.new do |p|
72
+ benchmark_invoker = -> (task, &block) { @stats.benchmark(task, &block) }
73
+
74
+ p.setup do
75
+ Rake::Task.class_eval do
76
+ orig_execute = instance_method(:execute)
77
+
78
+ define_method(:execute) do |args|
79
+ benchmark_invoker.call(self) do
80
+ orig_execute.bind(self).call(args)
81
+ end
82
+ end
83
+
84
+ orig_execute
85
+ end
86
+ end
87
+
88
+ p.reset do |memo|
89
+ Rake::Task.class_eval do
90
+ define_method(:execute) do |ex|
91
+ memo.bind(self).call(ex)
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ [report, benchmark]
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,89 @@
1
+ require 'smart_colored/extend'
2
+
3
+ module Rake::Funnel::Tasks::TimingSupport
4
+ class Report
5
+ class Column
6
+ attr_reader :header
7
+
8
+ def initialize(stats: [], header: '', accessor: -> (_) { '' })
9
+ @stats = stats
10
+ @header = header
11
+ @accessor = accessor
12
+ end
13
+
14
+ def width
15
+ longest_value = @stats.map { |s| @accessor.call(s) }.max_by { |m| m.length } || ''
16
+ width = longest_value.length
17
+ width = @header.length if width < @header.length
18
+ width
19
+ end
20
+
21
+ def format_header
22
+ @header.ljust(width)
23
+ end
24
+
25
+ def format_value(value)
26
+ @accessor.call(value).ljust(width)
27
+ end
28
+ end
29
+
30
+ SPACE = 3
31
+ HEADER_WIDTH = 70
32
+
33
+ def initialize(stats, opts = {})
34
+ @stats = stats
35
+ @opts = opts
36
+ end
37
+
38
+ def render
39
+ header
40
+ rows
41
+ footer
42
+ end
43
+
44
+ def columns
45
+ @columns ||= ([
46
+ Column.new(stats: @stats, header: 'Target', accessor: -> (timing) { timing[:task].name }),
47
+ Column.new(stats: @stats, header: 'Duration', accessor: -> (timing) { format(timing[:time]) })
48
+ ])
49
+ end
50
+
51
+ private
52
+ def header
53
+ puts '-' * HEADER_WIDTH
54
+ puts 'Build time report'
55
+ puts '-' * HEADER_WIDTH
56
+
57
+ puts columns.map { |c| c.format_header }.join(' ' * SPACE)
58
+ puts columns.map { |c| c.format_header.gsub(/./, '-') }.join(' ' * SPACE)
59
+ end
60
+
61
+ def rows
62
+ @stats.each do |timing|
63
+ puts columns.map { |c| c.format_value(timing) }.join(' ' * SPACE)
64
+ end
65
+ end
66
+
67
+ def footer
68
+ puts '-' * HEADER_WIDTH
69
+ puts 'Total'.ljust(columns[0].width) + ' ' * SPACE + format(Time.now - @stats.started_at)
70
+ status_message
71
+ puts '-' * HEADER_WIDTH
72
+ end
73
+
74
+ def format(seconds)
75
+ Time.at(seconds).utc.strftime('%H:%M:%S')
76
+ end
77
+
78
+ def status_message
79
+ status = @opts[:failed] ? 'Failed' : 'OK'
80
+ status = 'Status'.ljust(columns[0].width) + ' ' * SPACE + status
81
+
82
+ if @opts[:failed]
83
+ $stderr.puts status.bold.red
84
+ else
85
+ $stdout.puts status.bold.green
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,26 @@
1
+ module Rake::Funnel::Tasks::TimingSupport
2
+ class Statistics
3
+ include Enumerable
4
+
5
+ attr_reader :started_at
6
+
7
+ def initialize
8
+ @stats = []
9
+ @started_at = Time.now
10
+ end
11
+
12
+ def each(&block)
13
+ @stats.each(&block)
14
+ end
15
+
16
+ def benchmark(task)
17
+ t0 = Time.now
18
+ begin
19
+ yield if block_given?
20
+ ensure
21
+ t1 = Time.now
22
+ @stats << { task: task, time: t1 - t0 }
23
+ end
24
+ end
25
+ end
26
+ end