bozo-scripts 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,128 @@
1
+ require 'erubis'
2
+
3
+ module Bozo
4
+
5
+ # Class for creating files based upon configuration files and ERB-style
6
+ # templates.
7
+ #
8
+ # == Overview
9
+ #
10
+ # This class is primarily intended for generating config files with shared
11
+ # values but it is capable of generating whatever files you want.
12
+ #
13
+ # == Configuration files
14
+ #
15
+ # Configuration files specify a hash of hashes in a more readable format.
16
+ # For example:
17
+ #
18
+ # group :example do
19
+ # set :one, 'foo'
20
+ # set :two, 'bar'
21
+ # end
22
+ #
23
+ # Internally creates a hash like:
24
+ #
25
+ # {:example => {:one => 'foo', :two => 'bar'}}
26
+ #
27
+ # A configuration file can overwrite the values specified by a preceding one
28
+ # without error. Groups can be opened and closed as desired and nesting
29
+ # groups is possible.
30
+ #
31
+ # == Template files
32
+ #
33
+ # To use a value within an ERB template you specify the hash hierarchy as if
34
+ # they were method names rather than having to use the full hash syntax.
35
+ #
36
+ # Therefore, this is valid:
37
+ #
38
+ # Foo is <%= example.one %>
39
+ #
40
+ # Whilst this is *not* valid:
41
+ #
42
+ # Foo is <%= self[:example][:one] %>
43
+ #
44
+ # If a template uses a value that is not specified within the configuration
45
+ # then the hook will raise an error and halt the build.
46
+ class ErubisTemplatingCoordinator
47
+
48
+ # Create a new instance.
49
+ #
50
+ # @param [Configuration] configuration
51
+ # The configuration to use with templates. If not specified a new
52
+ # object is created.
53
+ def initialize(configuration = Bozo::Configuration.new)
54
+ @templates = []
55
+ @configuration = configuration
56
+ end
57
+
58
+ # Adds a required configuration file to the underlying configuration
59
+ # object.
60
+ #
61
+ # @param [String] path
62
+ # The path of the configuration file to load.
63
+ def required_config_file(path)
64
+ raise RuntimeError.new "Required config file #{path} could not be found" unless File.exist? path
65
+ config_file path
66
+ end
67
+
68
+ # Adds a configuration file to the underlying configuration object if a
69
+ # file exists at the given path.
70
+ #
71
+ # @param [String] path
72
+ # The path of the configuration file to load.
73
+ def config_file(path)
74
+ @configuration.load path if File.exist? path
75
+ end
76
+
77
+ # Adds a template file to use when generating files.
78
+ #
79
+ # @param [String] path
80
+ # The path of the template file.
81
+ def template_file(path)
82
+ @templates << path
83
+ end
84
+
85
+ # Adds a selection of template files to use when generating files.
86
+ #
87
+ # @param [String] glob
88
+ # A glob from that matches template files.
89
+ def template_files(glob)
90
+ @templates = @templates + Dir[glob]
91
+ end
92
+
93
+ # Generate all the files matching the underlying configuration.
94
+ #
95
+ # @param [Proc] block
96
+ # A block that will be called with the template path and target file
97
+ # path when provided.
98
+ def generate_files(&block)
99
+ @templates.each {|template| generate_file template, block}
100
+ end
101
+
102
+ private
103
+
104
+ # Generate the file relating to the template file using the given
105
+ # configuration.
106
+ #
107
+ # @param [String] template_path
108
+ # The path of the template file that should be filled using the
109
+ # configuration.
110
+ # @param [Proc] block
111
+ # A block that will be called with the template path and target file
112
+ # path when provided.
113
+ def generate_file(template_path, block)
114
+ target_path = template_path.sub /\.[^\.]+$/, ''
115
+
116
+ block.call(template_path, target_path) unless block.nil?
117
+
118
+ template_content = IO.read template_path
119
+ template = Erubis::Eruby.new template_content
120
+
121
+ content = @configuration.apply {|binding| template.result(binding)}
122
+
123
+ File.open(target_path, 'w+') {|f| f.write(content)}
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,152 @@
1
+ module Bozo::Hooks
2
+
3
+ # Specifies a hook for running FxCop.
4
+ #
5
+ # The default configuration runs against the compiled assemblies produced via
6
+ # msbuild.
7
+ #
8
+ # Alternatively a specific .fxcop project file can be specified in the
9
+ # configuration.
10
+ class FxCop
11
+
12
+ def self.default_path
13
+ if ENV['ProgramFiles(x86)'].nil?
14
+ program_files_path = ENV['ProgramFiles']
15
+ else
16
+ program_files_path = ENV['ProgramFiles(x86)']
17
+ end
18
+
19
+ File.join(program_files_path, 'Microsoft Fxcop 10.0', 'fxcopcmd.exe') unless program_files_path.nil?
20
+ end
21
+
22
+ def initialize
23
+ @@defaults = {
24
+ :types => [],
25
+ :framework_versions => [:net35, :net40],
26
+ :project => nil,
27
+ :path => FxCop.default_path
28
+ }
29
+
30
+ @config = {}
31
+ end
32
+
33
+ # Adds a type to analyze
34
+ def type(type)
35
+ @config[:types] ||= []
36
+ @config[:types] << type
37
+ end
38
+
39
+ # Specifies an fxcop project file
40
+ def project(project)
41
+ @config[:project] = project
42
+ end
43
+
44
+ # Specifies the fxcop path
45
+ def path(path = nil)
46
+ @config[:path] = path unless path.nil?
47
+
48
+ @config[:path]
49
+ end
50
+
51
+ # Runs the post_compile hook
52
+ def post_compile
53
+ config = config_with_defaults
54
+
55
+ raise no_executable_path_specified if path.nil?
56
+ raise no_executable_exists unless File.exists?(path)
57
+
58
+ if config[:project].nil?
59
+ execute_projects config
60
+ else
61
+ execute_fxcop_project config
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def config_with_defaults
68
+ @@defaults.merge @config
69
+ end
70
+
71
+ # The path to output the fxcop results to
72
+ def output_path
73
+ out_path = File.expand_path File.join('temp', 'fxcop')
74
+ FileUtils.mkdir_p out_path
75
+ out_path
76
+ end
77
+
78
+ # Executes fxcop against the msbuild built assemblies
79
+ #
80
+ # @param [Hash] config
81
+ # The fxcop configuration
82
+ def execute_projects(config)
83
+ log_debug "Executing projects with '#{path}'" if config[:framework_versions].any?
84
+
85
+ config[:framework_versions].each do |framework_version|
86
+ args = []
87
+ args << '"' + path + '"'
88
+ args << "/out:#{output_path}\\#{Time.now.to_i}-#{framework_version}-FxCop-report.xml"
89
+ args << "/types:" + config[:types].join(',') unless config[:types].empty?
90
+
91
+ project_dirs.each do |project|
92
+ projects = project_files(project, framework_version)
93
+
94
+ projects.each do |project_file|
95
+ project_path = File.expand_path(project_file).gsub(/\//, '\\')
96
+ args << "/file:\"#{project_path}\""
97
+ end
98
+ end
99
+
100
+ execute_command :fx_cop, args
101
+ end
102
+ end
103
+
104
+ # Executes a .fxcop file
105
+ #
106
+ # @param [Hash] config
107
+ # The fxcop configuration
108
+ def execute_fxcop_project(config)
109
+ log_debug "Executing fxcop project '#{config[:project]}' with '#{path}'"
110
+
111
+ args = []
112
+ args << '"' + path + '"'
113
+ args << "/out:\"#{output_path}\\#{File.basename(config[:project], '.*')}-FxCop-report.xml\""
114
+ args << "/project:\"#{config[:project]}\""
115
+ args << "/types:" + config[:types].join(',') unless config[:types].empty?
116
+
117
+ execute_command :fx_cop, args
118
+ end
119
+
120
+ # Create a new error specifying that the executable path
121
+ # has not been specified.
122
+ def no_executable_path_specified
123
+ ConfigurationError.new "No path specified for fxcop"
124
+ end
125
+
126
+ # Create a new error specifying that the fxcop executable
127
+ # does not exist at the path.
128
+ def no_executable_exists
129
+ ConfigurationError.new "FxCop executable does not exist at #{path}"
130
+ end
131
+
132
+ # List of compiled assemblies and executables
133
+
134
+ # @param [String] project_path
135
+ # The path of the project
136
+ # @param [Symbol] framework_version
137
+ # The framework_version to find assemblies for
138
+ def project_files(project_path, framework_version)
139
+ project_name = File.basename(project_path)
140
+ file_matcher = File.expand_path File.join(project_path, framework_version.to_s, "#{project_name}.{dll,exe}")
141
+ Dir[file_matcher]
142
+ end
143
+
144
+ # List of all the msbuild built projects
145
+ def project_dirs
146
+ project_file_matcher = File.expand_path File.join('temp', 'msbuild', '*')
147
+ Dir[project_file_matcher]
148
+ end
149
+
150
+ end
151
+
152
+ end
@@ -0,0 +1,12 @@
1
+ module Bozo::Hooks
2
+
3
+ class GitCommitHashes
4
+
5
+ def post_dependencies
6
+ env['GIT_HASH'] = `git log -1 --format="%h"`.strip
7
+ env['GIT_HASH_FULL'] = `git log -1 --format="%H"`.strip
8
+ end
9
+
10
+ end
11
+
12
+ end
@@ -0,0 +1,23 @@
1
+ module Bozo::Hooks
2
+
3
+ # Hook to tag a git repository when a release is published from a build
4
+ # server.
5
+ class GitTagRelease
6
+
7
+ def post_publish
8
+ return unless build_server?
9
+ log_info "Tagging repository for release #{version}"
10
+
11
+ tag_name = "rel-#{version}"
12
+
13
+ if `git tag`.split("\n").include? tag_name
14
+ raise Bozo::ConfigurationError.new "The tag #{tag_name} already exists"
15
+ end
16
+
17
+ execute_command :git, ['git', 'tag', tag_name]
18
+ execute_command :git, ['git', 'push', '--tags']
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,92 @@
1
+ module Bozo::Hooks
2
+
3
+ class Teamcity
4
+
5
+ include Bozo::ClassNameHelpers
6
+
7
+ def pre_compile
8
+ return unless Teamcity.hosted_in_teamcity?
9
+ log_pre_step :compile
10
+
11
+ puts "##teamcity[buildNumber '#{version}']"
12
+ # currently a general compiler which wraps everything. Once a compiler hook is added can distinguish
13
+ # each specific compiler
14
+ puts "##teamcity[compilationStarted compiler='Bozo']"
15
+ end
16
+
17
+ def post_compile
18
+ return unless Teamcity.hosted_in_teamcity?
19
+ puts "##teamcity[compilationFinished compiler='Bozo']"
20
+ log_post_step :compile
21
+ end
22
+
23
+ def post_test
24
+ return unless Teamcity.hosted_in_teamcity?
25
+
26
+ report
27
+ report_dotnetcoverage
28
+
29
+ log_post_step :test
30
+ end
31
+
32
+ def method_missing(method, *args)
33
+ if method.to_s =~ /^(pre|post)_(.+)/
34
+ send "log_#{$1}_step".to_sym, $2
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ def respond_to?(method)
41
+ method.to_s =~ /^(pre|post)_(.+)/ or super
42
+ end
43
+
44
+ # Returns whether teamcity is hosting bozo
45
+ def self.hosted_in_teamcity?
46
+ not ENV['TEAMCITY_VERSION'].nil?
47
+ end
48
+
49
+ private
50
+
51
+ # Notifies teamcity of general reports such as test runner results
52
+ def report
53
+ report_types = [:nunit, :fxcop]
54
+ report_types.each do |type|
55
+ reports = report_files(File.join(Dir.pwd, "/temp"), type)
56
+
57
+ reports.each do |report|
58
+ puts "##teamcity[importData type='#{type}' path='#{report}']"
59
+ end
60
+ end
61
+ end
62
+
63
+ # Notifies teamcity of dotNetCoverage results
64
+ def report_dotnetcoverage
65
+ tool_types = [:dot_cover]
66
+
67
+ tool_types.each do |type|
68
+ reports = report_files(File.join(Dir.pwd, "/temp"), type)
69
+
70
+ reports.each do |report|
71
+ tool_name = to_class_name(type).downcase
72
+ puts "##teamcity[importData type='dotNetCoverage' tool='#{tool_name}' path='#{report}']"
73
+ end
74
+ end
75
+ end
76
+
77
+ def log_pre_step(step)
78
+ puts "##teamcity[progressStart 'Pre #{step}']" if Teamcity.hosted_in_teamcity?
79
+ end
80
+
81
+ def log_post_step(step)
82
+ puts "##teamcity[progressEnd 'Post #{step}']" if Teamcity.hosted_in_teamcity?
83
+ end
84
+
85
+ def report_files(path, type)
86
+ files = File.expand_path(File.join(path, "/**/*-#{to_class_name(type)}-report.xml"))
87
+ Dir[files]
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -0,0 +1,41 @@
1
+ module Bozo::Hooks
2
+
3
+ class Timing
4
+
5
+ def initialize
6
+ @timings = {}
7
+ end
8
+
9
+ def print_timings
10
+ puts ''
11
+ @timings.each do |stage, times|
12
+ puts format_timing(stage, times).bright.color(stage == :build ? :cyan : :black)
13
+ end
14
+ end
15
+
16
+ def format_timing(stage, args)
17
+ time_taken = (args[:post] - args[:pre]).round(1)
18
+ "#{stage.to_s.capitalize.ljust(14)} #{time_taken.to_s.rjust(5)}s"
19
+ end
20
+
21
+ def record(stage, point)
22
+ @timings[stage] ||= {}
23
+ @timings[stage][point] = Time.now
24
+ end
25
+
26
+ def method_missing(method, *args)
27
+ if method.to_s =~ /^(pre|post)_(.+)/
28
+ record $2.to_sym, $1.to_sym
29
+ print_timings if $1 == 'post' and $2 == 'build'
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ def respond_to?(method)
36
+ method.to_s =~ /^(pre|post)_(.+)/ or super
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,226 @@
1
+ require 'nokogiri'
2
+
3
+ module Bozo::Packagers
4
+
5
+ class Nuget
6
+
7
+ def initialize
8
+ @libraries = []
9
+ @executables = []
10
+ @websites = []
11
+ end
12
+
13
+ def destination(destination)
14
+ @destination = destination
15
+ end
16
+
17
+ def library(project)
18
+ @libraries << LibraryPackage.new(project, self)
19
+ end
20
+
21
+ def executable(project)
22
+ @executables << ExecutablePackage.new(project)
23
+ end
24
+
25
+ def website(project)
26
+ @websites << WebsitePackage.new(project)
27
+ end
28
+
29
+ def required_tools
30
+ :nuget
31
+ end
32
+
33
+ def project_url(url)
34
+ @project_url = url
35
+ end
36
+
37
+ def license_url(url)
38
+ @license_url = url
39
+ end
40
+
41
+ def author(author)
42
+ @author = author
43
+ end
44
+
45
+ def to_s
46
+ "Publish projects with nuget #{@libraries | @executables | @websites} to #{@destination}"
47
+ end
48
+
49
+ def execute
50
+ @libraries.each {|project| package project}
51
+ @executables.each {|project| package project}
52
+ @websites.each {|project| package project}
53
+ end
54
+
55
+ # Returns the version that the package should be given.
56
+ def package_version
57
+ # If running on a build server then it is a real release, otherwise it is
58
+ # a preview release and the version should reflect that.
59
+ if build_server?
60
+ version
61
+ else
62
+ "#{version}-pre#{env['GIT_HASH']}"
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def package(project)
69
+ spec_path = generate_specification(project)
70
+ create_package(project.name, spec_path, true)
71
+ end
72
+
73
+ def generate_specification(project)
74
+ log_debug "Generating specification for #{project.name}"
75
+ builder = Nokogiri::XML::Builder.new do |doc|
76
+ doc.package(:xmlns => "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd") do
77
+ doc.metadata do
78
+ doc.id project.name
79
+ doc.version_ package_version
80
+ doc.authors @author
81
+ doc.description project.name
82
+ doc.projectUrl @project_url
83
+ doc.licenseUrl @license_url
84
+ doc.dependencies do
85
+ project.dependencies.each do |dep|
86
+ doc.dependency(dep)
87
+ end
88
+ end
89
+ end
90
+ doc.files do
91
+ project.files.each do |file|
92
+ doc.file(file)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ spec_path = File.expand_path(File.join('temp', 'nuget', "#{project.name}.nuspec"))
98
+ FileUtils.mkdir_p File.dirname(spec_path)
99
+ File.open(spec_path, 'w+') {|f| f.write(builder.to_xml)}
100
+ spec_path
101
+ end
102
+
103
+ def create_package(project, spec_path, omit_analysis = false)
104
+ args = []
105
+
106
+ dist_dir = File.expand_path(File.join('dist', 'nuget'))
107
+
108
+ args << File.expand_path(File.join('build', 'tools', 'nuget', 'NuGet.exe'))
109
+ args << 'pack'
110
+ args << "\"#{spec_path}\""
111
+ args << '-OutputDirectory'
112
+ args << "\"#{dist_dir}\""
113
+ args << '-NoPackageAnalysis' if omit_analysis
114
+
115
+ # Ensure the directory is there because Nuget won't make it
116
+ FileUtils.mkdir_p dist_dir
117
+
118
+ log_debug "Creating nuget package for #{project}"
119
+
120
+ execute_command :nuget, args
121
+ end
122
+
123
+ end
124
+
125
+ private
126
+
127
+ class ExecutablePackage
128
+
129
+ def initialize(project)
130
+ @name = project
131
+ end
132
+
133
+ def name
134
+ @name
135
+ end
136
+
137
+ def dependencies
138
+ []
139
+ end
140
+
141
+ def files
142
+ [{:src => File.expand_path(File.join('temp', 'msbuild', @name, '**', '*.*')).gsub(/\//, '\\'), :target => 'exe'}]
143
+ end
144
+
145
+ end
146
+
147
+ class LibraryPackage
148
+
149
+ def initialize(project, nuget)
150
+ @name = project
151
+ @nuget = nuget
152
+ end
153
+
154
+ def name
155
+ @name
156
+ end
157
+
158
+ def dependencies
159
+ project_reference_dependencies + nuget_dependencies
160
+ end
161
+
162
+ def files
163
+ %w{dll pdb xml}.map do |extension|
164
+ {:src => File.expand_path(File.join('temp', 'msbuild', @name, '**', "#{@name}.#{extension}")).gsub(/\//, '\\'), :target => 'lib'}
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ def project_reference_dependencies
171
+ doc = Nokogiri::XML(File.open(project_file))
172
+
173
+ doc.xpath('//proj:Project/proj:ItemGroup/proj:ProjectReference/proj:Name', {"proj" => "http://schemas.microsoft.com/developer/msbuild/2003"}).map do |node|
174
+ {:id => node.text, :version => "[#{@nuget.package_version}]"}
175
+ end
176
+ end
177
+
178
+ # get dependencies from packages.config
179
+ def nuget_dependencies
180
+ package_file = packages_file
181
+ return [] unless File.exist? package_file
182
+
183
+ doc = Nokogiri::XML(File.open(package_file))
184
+
185
+ doc.xpath('//packages/package').map do |node|
186
+ {:id => node[:id], :version => "[#{node[:version]}]"}
187
+ end
188
+ end
189
+
190
+ def packages_file
191
+ find_file_in_project 'packages.config'
192
+ end
193
+
194
+ def project_file
195
+ find_file_in_project "#{@name}.csproj"
196
+ end
197
+
198
+ def find_file_in_project(file_name)
199
+ file = File.expand_path(File.join('src', 'csharp', @name, file_name))
200
+ file = File.expand_path(File.join('test', 'csharp', @name, file_name)) unless File.exist? file
201
+ file
202
+ end
203
+
204
+ end
205
+
206
+ class WebsitePackage
207
+
208
+ def initialize(project)
209
+ @name = project
210
+ end
211
+
212
+ def name
213
+ @name
214
+ end
215
+
216
+ def dependencies
217
+ []
218
+ end
219
+
220
+ def files
221
+ [{:src => File.expand_path(File.join('temp', 'msbuild', @name, '**', '*.*')).gsub(/\//, '\\'), :target => 'website'}]
222
+ end
223
+
224
+ end
225
+
226
+ end
@@ -0,0 +1,24 @@
1
+ module Bozo::Packagers
2
+
3
+ # Specifies gem packager.
4
+ #
5
+ # Builds any '*.gemspec' file in the root directory
6
+ class Rubygems
7
+
8
+ def execute
9
+ dist_dir = File.expand_path(File.join('dist', 'gem'))
10
+ FileUtils.mkdir_p dist_dir
11
+
12
+ Dir['*.gemspec'].each { |spec| build_gem spec }
13
+ Dir['*.gem'].each { |file| FileUtils.mv file, File.join(dist_dir, file) }
14
+ end
15
+
16
+ private
17
+
18
+ def build_gem(spec)
19
+ execute_command :rubygems, ['gem', 'build', spec]
20
+ end
21
+
22
+ end
23
+
24
+ end