BuildMaster 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,25 @@
1
+ = BuildMaster - Building and releasing project and site with Ruby
2
+
3
+ Homepage:: http://buildmaster.rubyforge.org
4
+ Author:: Shane
5
+ Copyright:: (c) 2006 BuildMaster on rubyforge
6
+ License:: Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
7
+
8
+ BuildMaster is a project that targets project building, releasing and site building.
9
+
10
+ Please post your question at http://groups.google.com/group/buildmaster/ or
11
+ send email to buildmaster@googlegroups.com
12
+
13
+ == Project Building and Releasing
14
+
15
+ A ruby version of the project building and releasing script as described in
16
+ "Pragmatic Project Automation" (http://www.pragmaticprogrammer.com/sk/auto/)
17
+
18
+ == Site Building
19
+
20
+ A simple template engine that can build a website by producing the content pages based on the specified source
21
+ and decrorating the content pages with a skin.
22
+
23
+ The supported content sources are <a href="http://www.w3.org/TR/xhtml11/">XHTML</a> and <a href="http://hobix.com/textile/">Textile</a>.
24
+
25
+
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'buildmaster/shell_command'
4
+ require 'buildmaster/release_control'
5
+ require 'buildmaster/ant_client'
6
+ require 'buildmaster/cvs_client'
7
+ require 'buildmaster/svn_driver'
8
+ require 'buildmaster/site'
9
+ require 'buildmaster/xtemplate'
@@ -0,0 +1,132 @@
1
+ class Ant
2
+ include Shell
3
+ def initialize(ant_file = nil, &command_runner)
4
+ @ant_file = ant_file
5
+ @ant_home = check_directory(get_environment('ANT_HOME'))
6
+ #ISSUE: what java wants to split up classpath varies from platform to platform
7
+ #and ruby, following perl, is not too hot at hinting which box it is on.
8
+ #here I assume ":" except on win32, dos, and netware. Add extra tests here as needed.
9
+ #This is the only way I know how to check if on windows
10
+ if File.directory?('C:')
11
+ @class_path_delimiter = ';'
12
+ @path_seperator='\\'
13
+ else
14
+ @class_path_delimiter = ':'
15
+ @path_seperator='/'
16
+ end
17
+ @java_command = get_java_command()
18
+ @ant_options = get_environment_or_default('ANT_OPTS').split(' ')
19
+ @ant_arguments = get_environment_or_default('ANT_ARGS').split(' ')
20
+ @ant_options.putsh(ENV['JIKESPATH']) if ENV['JIKESPATH']
21
+ @classpath=ENV['CLASSPATH']
22
+ @command_runner = command_runner
23
+ end
24
+
25
+ def get_java_command
26
+ java_home = get_environment_or_default('JAVA_HOME')
27
+ if java_home
28
+ command = [java_home, 'bin', 'java'].join(@path_seperator)
29
+ else
30
+ command = get_environment_or_default('JAVA_CMD', 'java')
31
+ end
32
+ return command
33
+ end
34
+
35
+ def project_help
36
+ launch('-projecthelp')
37
+ end
38
+
39
+ def target(name)
40
+ launch(name)
41
+ end
42
+
43
+ def launch(arguments)
44
+ local_path = "#{@ant_home}/lib/ant-launcher.jar"
45
+ if (@classpath and @classpath.length() > 0)
46
+ local_path = "#{local_path}#{@class_path_delimiter}#{@classpath}"
47
+ end
48
+ all_arguments = Array.new()
49
+ all_arguments.push(@ant_options)
50
+ all_arguments.push('-classpath', local_path)
51
+ all_arguments.push("-Dant.home=#{@ant_home}")
52
+ all_arguments.push('org.apache.tools.ant.launch.Launcher', @ant_arguments);
53
+ all_arguments.push('-f', @ant_file) if @ant_file
54
+ all_arguments.push(arguments)
55
+ command_line = "#{@java_command} #{all_arguments.join(' ')}"
56
+ run_command(command_line)
57
+ end
58
+
59
+ private :get_java_command
60
+
61
+ end
62
+
63
+ class RubyAnt
64
+ def initialize(ant_file = nil, &command_runner)
65
+ @ant_file = ant_file
66
+ @ant_home = check_directory(get_environment('ANT_HOME'))
67
+ @java_command = get_environment_or_default('JAVA_CMD', 'java')
68
+ #ISSUE: what java wants to split up classpath varies from platform to platform
69
+ #and ruby, following perl, is not too hot at hinting which box it is on.
70
+ #here I assume ":" except on win32, dos, and netware. Add extra tests here as needed.
71
+ #This is the only way I know how to check if on windows
72
+ if File.directory?('C:')
73
+ @class_path_delimiter = ';'
74
+ else
75
+ @class_path_delimiter = ':'
76
+ end
77
+ @ant_options = get_environment_or_default('ANT_OPTS').split(' ')
78
+ @ant_arguments = get_environment_or_default('ANT_ARGS').split(' ')
79
+ @ant_options.putsh(ENV['JIKESPATH']) if ENV['JIKESPATH']
80
+ @classpath=ENV['CLASSPATH']
81
+ @command_runner = command_runner
82
+ end
83
+
84
+ def launch(arguments)
85
+ local_path = "#{@ant_home}/lib/ant-launcher.jar"
86
+ if (@classpath and @classpath.length() > 0)
87
+ local_path = "#{local_path}#{@class_path_delimiter}#{@classpath}"
88
+ end
89
+ all_arguments = Array.new()
90
+ all_arguments.push(@ant_options)
91
+ all_arguments.push('-classpath', local_path)
92
+ all_arguments.push("-Dant.home=#{@ant_home}")
93
+ all_arguments.push('org.apache.tools.ant.launch.Launcher', @ant_arguments);
94
+ all_arguments.push('-f', @ant_file) if @ant_file
95
+ all_arguments.push(arguments)
96
+ command_line = "#{@java_command} #{all_arguments.join(' ')}"
97
+ run_command(command_line)
98
+ end
99
+
100
+ private
101
+ def run(command)
102
+ if (!system(command))
103
+ raise "error running command: #{command}"
104
+ end
105
+ end
106
+
107
+ def run_command(full_command)
108
+ if (@command_runner)
109
+ @command_runner.call(full_command)
110
+ else
111
+ run(full_command)
112
+ end
113
+ end
114
+
115
+ private
116
+ def check_directory(path)
117
+ raise "#{path} is not a directory" unless File.directory? path
118
+ return path
119
+ end
120
+
121
+ def get_environment(name)
122
+ value = ENV[name]
123
+ raise "#{name} is not set" unless value
124
+ return value
125
+ end
126
+
127
+ def get_environment_or_default(name, default_value='')
128
+ value = ENV[name]
129
+ value = default_value if not value
130
+ return value
131
+ end
132
+ end
@@ -0,0 +1,11 @@
1
+ class BuildFile
2
+ def initialize(file_name)
3
+ end
4
+
5
+ def increase(property_name)
6
+ return 1
7
+ end
8
+
9
+ def save()
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ class CvsInfo
2
+ attr_reader :root, :repository
3
+
4
+ public
5
+ def initialize(root, repository)
6
+ @root = root
7
+ @repository = repository
8
+ end
9
+
10
+ def CvsInfo.load(folder)
11
+ return CvsInfo.new(read("#{folder}/Root").strip!, read("#{folder}/Repository").strip!)
12
+ end
13
+
14
+ private
15
+ def CvsInfo.read(file)
16
+ File.open(file) do |file|
17
+ return file.gets
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ class CvsClient
24
+ include Shell
25
+ def CvsClient.load(working_directory)
26
+ return CvsClient.new(CvsInfo.load("#{working_directory}/CVS"), working_directory)
27
+ end
28
+
29
+ def initialize(cvs_info, working_directory, &command_runner)
30
+ @cvs_info = cvs_info
31
+ @working_directory = working_directory
32
+ @command_runner = command_runner
33
+ end
34
+
35
+ def command(command)
36
+ run_command "cvs -d #{@cvs_info.root} #{command} #{@working_directory}"
37
+ end
38
+
39
+ def checkout()
40
+ run_command("cvs -d #{@cvs_info.root} co -d #{@working_directory} #{@cvs_info.repository}")
41
+ end
42
+
43
+ def update(option='')
44
+ if (option.length > 0)
45
+ option = ' ' + option
46
+ end
47
+ command("update#{option}")
48
+ end
49
+
50
+ def tag(name)
51
+ command("tag #{name}")
52
+ end
53
+
54
+ def commit(comment)
55
+ command("commit -m \"#{comment}\"")
56
+ end
57
+ end
@@ -0,0 +1,62 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ module BuildMaster
4
+
5
+ class FileProcessor
6
+ def initialize(template, content_path, evaluator)
7
+ @template = template
8
+ @content_path = content_path
9
+ @evaluator = evaluator
10
+ end
11
+
12
+ def process_textile()
13
+ textile_content = IO.read(@content_path)
14
+ match_result = TEXTILE_REGX.match(textile_content)
15
+ title = ''
16
+ if match_result != nil
17
+ title = match_result[2]
18
+ textile_content = match_result.post_match
19
+ end
20
+ html_body = RedCloth.new(textile_content).to_html
21
+ html_content = <<HTML
22
+ <!DOCTYPE html
23
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
24
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
25
+ <html xmlns="http://www.w3.org/1999/xhtml">
26
+ <head>
27
+ <title>#{title}</title>
28
+ </head>
29
+ <body>
30
+ #{html_body}
31
+ </body>
32
+ </html>
33
+ HTML
34
+ return process_html_content(html_content)
35
+ end
36
+
37
+ def process_html()
38
+ return process_html_content(File.open(@content_path))
39
+ end
40
+
41
+ private
42
+ def process_html_content(source)
43
+ document_with_skin = @template.process(source) do |message|
44
+ begin
45
+ method = @evaluator.method(message)
46
+ if (method.arity == 0)
47
+ method.call
48
+ else
49
+ method.call(@content_path)
50
+ end
51
+ rescue NameError
52
+ raise TemplateException,
53
+ "unable to process message: #{message}: #{$!}" ,
54
+ caller
55
+ end
56
+ end
57
+ return document_with_skin
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,19 @@
1
+ class Release
2
+ def initialize(vcs_driver, builder)
3
+ @vcs_driver = vcs_driver
4
+ @builder = builder
5
+ end
6
+
7
+ def release_candidate(tag)
8
+ @vcs_driver.checkout
9
+ @vcs_driver.tag(tag)
10
+ @builder.build
11
+ @vcs_driver.commit
12
+ end
13
+
14
+ def release_rebuild(tag)
15
+ @vcs_driver.checkout(tag)
16
+ @builder.invoke
17
+ end
18
+ end
19
+
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/ruby
2
+
3
+ #######################################################################
4
+ =begin
5
+ runant.rb
6
+
7
+ This script is a translation of the runant.pl written by Steve Loughran.
8
+ It runs ant with/out arguments
9
+ This script has been tested with ruby1.8.2-14/WinXP
10
+
11
+ created: 2005-03-08
12
+ author: Shane Duan
13
+ =end
14
+
15
+ # Debugging flag
16
+ $debug = true
17
+
18
+ # Assumptions:
19
+ #
20
+ # - the "java" executable/script is on the command path
21
+ # - ANT_HOME has been set
22
+ # - target platform uses ":" as classpath separator or a "C:" exists (windows)
23
+ # - target platform uses "/" as directory separator.
24
+
25
+ #######################################################################
26
+ require 'Ant'
27
+
28
+ if not Ant.new().launch(ARGV)
29
+ raise 'Ant failed'
30
+ end
31
+
@@ -0,0 +1,38 @@
1
+ module Shell
2
+ private
3
+ def run(command)
4
+ if (!system(command))
5
+ raise "error running command: #{command}"
6
+ end
7
+ end
8
+
9
+ def run_command(full_command)
10
+ if (@command_runner)
11
+ @command_runner.call(full_command)
12
+ else
13
+ run(full_command)
14
+ end
15
+ end
16
+
17
+ def check_directory(path)
18
+ raise "#{path} is not a directory" unless File.directory? path
19
+ return path
20
+ end
21
+
22
+ def get_environment(name)
23
+ value = ENV[name]
24
+ raise "#{name} is not set" unless value
25
+ return value
26
+ end
27
+
28
+ def get_environment_or_default(name, default_value='')
29
+ value = ENV[name]
30
+ value = default_value if not value
31
+ return value
32
+ end
33
+
34
+ end
35
+
36
+ class SystemCommandRunner
37
+
38
+ end
@@ -0,0 +1,147 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'fileutils'
4
+ require 'redcloth'
5
+ require 'webrick'
6
+ require 'source_file_handler'
7
+ require 'file_processor'
8
+
9
+ module BuildMaster
10
+ #todo match only beginning of the file
11
+ TEXTILE_REGX = /---(-)*\n(.*)\n(-)*---/
12
+
13
+ class SiteSpec
14
+ def self.get_instance
15
+ self.new()
16
+ end
17
+
18
+ attr_accessor :output_dir, :content_dir, :template, :template_file
19
+
20
+ def validate_inputs
21
+ validate_dir(@content_dir, :content_dir)
22
+ end
23
+
24
+ def load_template
25
+ if (@template)
26
+ return XTemplate.new(@template)
27
+ else
28
+ return XTemplate.new(File.open(@template_file))
29
+ end
30
+ end
31
+
32
+ private
33
+ def validate_dir(directory, symbol)
34
+ if not directory
35
+ raise "Directory for #{symbol.id2name} not specified"
36
+ end
37
+ if not File.exists? directory
38
+ raise "Directory for #{symbol.id2name} -- <#{directory}#> does not exist"
39
+ end
40
+ if not File.directory? directory
41
+ raise "<#{directory}> should be a directory for #{symbol.id2name}"
42
+ end
43
+ end
44
+
45
+ def validate_file(file, symbol)
46
+ if not file
47
+ raise "File for #{symbol.id2name} not specified"
48
+ end
49
+ if (not File.exists? file)
50
+ raise "File for #{symbol.id2name} -- <#{file}> does not exist"
51
+ end
52
+ if not File.file? file
53
+ raise "#<{file} should be a file for #{symbol.id2name}"
54
+ end
55
+ end
56
+ end
57
+
58
+ class Site
59
+ def initialize(spec)
60
+ @spec = spec
61
+ @spec.validate_inputs
62
+ @template = @spec.load_template
63
+ end
64
+
65
+ public
66
+ def execute(arguments)
67
+ action = 'build'
68
+ if arguments.size > 0
69
+ action = arguments[0]
70
+ end
71
+ method(action).call
72
+ end
73
+
74
+ def build
75
+ @count = 0
76
+ ensure_directory_exists(@spec.output_dir)
77
+ build_directory(@spec.output_dir, @spec.content_dir, @template)
78
+ puts "Generated file count: #{@count}"
79
+ end
80
+
81
+ def server
82
+ mime_types = WEBrick::HTTPUtils::DefaultMimeTypes.update(
83
+ {"textile" => "text/plain"}
84
+ )
85
+ server = WEBrick::HTTPServer.new(
86
+ :Port => 2000,
87
+ :Logger => WEBrick::Log.new($stdout, WEBrick::Log::INFO),
88
+ :MimeTypes => mime_types
89
+ )
90
+ server.mount('/', SourceFileHandler, @spec)
91
+ server.mount('/source', WEBrick::HTTPServlet::FileHandler, @spec.content_dir, true)
92
+ ['INT', 'TERM'].each { |signal|
93
+ trap(signal){ server.shutdown}
94
+ }
95
+ server.start
96
+ end
97
+
98
+ private
99
+ def ensure_directory_exists(dir_name)
100
+ if (not File.exist?(dir_name))
101
+ ensure_directory_exists(File.join(dir_name, '..'))
102
+ Dir.mkdir dir_name
103
+ end
104
+ end
105
+
106
+ def build_directory(out_dir, content_dir, template)
107
+ Dir.foreach(content_dir) do |item|
108
+ content_path = File.join(content_dir, item)
109
+ if (item == '.' || item == '..' || item == '.svn' || item == 'CVS')
110
+ elsif (File.directory? content_path)
111
+ build_directory(File.join(out_dir, item), content_path, template)
112
+ else
113
+ @current_file_name = content_path
114
+ process_file(content_path, out_dir, content_dir, item)
115
+ @count = @count + 1
116
+ end
117
+ end
118
+ end
119
+
120
+ def process_file(content_path, out_dir, content_dir, item)
121
+ print ">> #{content_path}"
122
+ extension = File.extname(content_path)
123
+ isIndex = @current_file_name =~ /^index/
124
+ file_processor = FileProcessor.new(@template, content_path, @spec)
125
+ if (extension.casecmp('.textile') == 0)
126
+ base_name = File.basename(item, extension)
127
+ output_file = File.join(out_dir, base_name + '.html')
128
+ write(output_file, file_processor.process_textile())
129
+ elsif (extension.casecmp('.html') == 0)
130
+ output_file = File.join(out_dir, item)
131
+ write(output_file, file_processor.process_html())
132
+ else
133
+ output_file = File.join(out_dir, item)
134
+ puts " ==> #{output_file}"
135
+ FileUtils.cp content_path, output_file
136
+ end
137
+ end
138
+
139
+ def write(target_file, xhtml)
140
+ puts " ==> #{target_file}"
141
+ File.open(target_file, "w") do |file|
142
+ xhtml.write(file, 0, false, true)
143
+ end
144
+ end
145
+
146
+ end
147
+ end