jenkins 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.gitignore +6 -0
  2. data/Changelog.md +40 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +58 -0
  5. data/README.md +154 -0
  6. data/Rakefile +82 -0
  7. data/bin/jenkins +10 -0
  8. data/features/default_host.feature +19 -0
  9. data/features/development.feature +14 -0
  10. data/features/launch_server.feature +16 -0
  11. data/features/listing_jobs.feature +34 -0
  12. data/features/manage_jobs.feature +208 -0
  13. data/features/manage_slave_nodes.feature +82 -0
  14. data/features/step_definitions/common_steps.rb +192 -0
  15. data/features/step_definitions/fixture_project_steps.rb +8 -0
  16. data/features/step_definitions/jenkins_steps.rb +104 -0
  17. data/features/step_definitions/scm_steps.rb +12 -0
  18. data/features/support/common.rb +37 -0
  19. data/features/support/env.rb +19 -0
  20. data/features/support/hooks.rb +16 -0
  21. data/features/support/jenkins_helpers.rb +6 -0
  22. data/features/support/matchers.rb +10 -0
  23. data/fixtures/jenkins/envfile.hpi +0 -0
  24. data/fixtures/jenkins/git.hpi +0 -0
  25. data/fixtures/jenkins/github.hpi +0 -0
  26. data/fixtures/jenkins/greenballs.hpi +0 -0
  27. data/fixtures/jenkins/rake.hpi +0 -0
  28. data/fixtures/jenkins/ruby.hpi +0 -0
  29. data/fixtures/projects/non-bundler/Rakefile +4 -0
  30. data/fixtures/projects/rails-3/.gitignore +4 -0
  31. data/fixtures/projects/rails-3/Gemfile +30 -0
  32. data/fixtures/projects/rails-3/Gemfile.lock +74 -0
  33. data/fixtures/projects/rails-3/README +256 -0
  34. data/fixtures/projects/rails-3/Rakefile +7 -0
  35. data/fixtures/projects/rails-3/app/controllers/application_controller.rb +3 -0
  36. data/fixtures/projects/rails-3/app/helpers/application_helper.rb +2 -0
  37. data/fixtures/projects/rails-3/app/views/layouts/application.html.erb +14 -0
  38. data/fixtures/projects/rails-3/config.ru +4 -0
  39. data/fixtures/projects/rails-3/config/application.rb +42 -0
  40. data/fixtures/projects/rails-3/config/boot.rb +13 -0
  41. data/fixtures/projects/rails-3/config/database.yml +22 -0
  42. data/fixtures/projects/rails-3/config/environment.rb +5 -0
  43. data/fixtures/projects/rails-3/config/environments/development.rb +26 -0
  44. data/fixtures/projects/rails-3/config/environments/production.rb +49 -0
  45. data/fixtures/projects/rails-3/config/environments/test.rb +35 -0
  46. data/fixtures/projects/rails-3/config/initializers/backtrace_silencers.rb +7 -0
  47. data/fixtures/projects/rails-3/config/initializers/inflections.rb +10 -0
  48. data/fixtures/projects/rails-3/config/initializers/mime_types.rb +5 -0
  49. data/fixtures/projects/rails-3/config/initializers/secret_token.rb +7 -0
  50. data/fixtures/projects/rails-3/config/initializers/session_store.rb +8 -0
  51. data/fixtures/projects/rails-3/config/locales/en.yml +5 -0
  52. data/fixtures/projects/rails-3/config/routes.rb +58 -0
  53. data/fixtures/projects/rails-3/db/seeds.rb +7 -0
  54. data/fixtures/projects/rails-3/doc/README_FOR_APP +2 -0
  55. data/fixtures/projects/rails-3/lib/tasks/.gitkeep +0 -0
  56. data/fixtures/projects/rails-3/public/404.html +26 -0
  57. data/fixtures/projects/rails-3/public/422.html +26 -0
  58. data/fixtures/projects/rails-3/public/500.html +26 -0
  59. data/fixtures/projects/rails-3/public/favicon.ico +0 -0
  60. data/fixtures/projects/rails-3/public/images/rails.png +0 -0
  61. data/fixtures/projects/rails-3/public/index.html +239 -0
  62. data/fixtures/projects/rails-3/public/javascripts/application.js +2 -0
  63. data/fixtures/projects/rails-3/public/javascripts/controls.js +965 -0
  64. data/fixtures/projects/rails-3/public/javascripts/dragdrop.js +974 -0
  65. data/fixtures/projects/rails-3/public/javascripts/effects.js +1123 -0
  66. data/fixtures/projects/rails-3/public/javascripts/prototype.js +6001 -0
  67. data/fixtures/projects/rails-3/public/javascripts/rails.js +175 -0
  68. data/fixtures/projects/rails-3/public/robots.txt +5 -0
  69. data/fixtures/projects/rails-3/public/stylesheets/.gitkeep +0 -0
  70. data/fixtures/projects/rails-3/script/rails +6 -0
  71. data/fixtures/projects/rails-3/test/performance/browsing_test.rb +9 -0
  72. data/fixtures/projects/rails-3/test/test_helper.rb +13 -0
  73. data/fixtures/projects/rails-3/vendor/plugins/.gitkeep +0 -0
  74. data/fixtures/projects/ruby/Gemfile +3 -0
  75. data/fixtures/projects/ruby/Gemfile.lock +10 -0
  76. data/fixtures/projects/ruby/Rakefile +4 -0
  77. data/jenkins.gemspec +34 -0
  78. data/lib/jenkins.rb +6 -0
  79. data/lib/jenkins/api.rb +219 -0
  80. data/lib/jenkins/cli.rb +249 -0
  81. data/lib/jenkins/cli/formatting.rb +53 -0
  82. data/lib/jenkins/config.rb +27 -0
  83. data/lib/jenkins/core_ext/hash.rb +9 -0
  84. data/lib/jenkins/core_ext/object/blank.rb +77 -0
  85. data/lib/jenkins/hudson-cli.jar +0 -0
  86. data/lib/jenkins/job_config_builder.rb +287 -0
  87. data/lib/jenkins/project_scm.rb +22 -0
  88. data/lib/jenkins/remote.rb +11 -0
  89. data/lib/jenkins/version.rb +3 -0
  90. data/spec/fixtures/ec2_global.config.xml +103 -0
  91. data/spec/fixtures/rails.multi.config.xml +82 -0
  92. data/spec/fixtures/rails.single.config.triggers.xml +84 -0
  93. data/spec/fixtures/rails.single.config.xml +80 -0
  94. data/spec/fixtures/ruby.multi-ruby-multi-labels.config.xml +84 -0
  95. data/spec/fixtures/ruby.multi.config.xml +77 -0
  96. data/spec/fixtures/ruby.single.config.xml +58 -0
  97. data/spec/fixtures/therubyracer.config.xml +77 -0
  98. data/spec/hash_key_cleaner_spec.rb +25 -0
  99. data/spec/job_config_builder_spec.rb +137 -0
  100. data/spec/spec_helper.rb +15 -0
  101. metadata +355 -0
@@ -0,0 +1,249 @@
1
+ require 'thor'
2
+ require 'jenkins/core_ext/object/blank'
3
+ require 'jenkins/core_ext/hash'
4
+ require 'jenkins/cli/formatting'
5
+ require 'jenkins/remote'
6
+
7
+ module Jenkins
8
+ class CLI < Thor
9
+ include CLI::Formatting
10
+
11
+ map "-v" => :version, "--version" => :version, "-h" => :help, "--help" => :help
12
+
13
+ def self.common_options
14
+ method_option :host, :desc => 'connect to jenkins server on this host'
15
+ method_option :port, :desc => 'connect to jenkins server on this port'
16
+ end
17
+
18
+ desc "server [options]", "run a jenkins server"
19
+ method_option :home, :desc => "use this directory to store server data", :type => :string, :default => File.join(ENV['HOME'], ".jenkins", "server"), :banner => "PATH"
20
+ method_option :port, :desc => "run jenkins server on this port", :type => :numeric, :default => 3001, :aliases => "-p"
21
+ method_option :control, :desc => "set the shutdown/control port", :type => :numeric, :default => 3002, :aliases => "-c"
22
+ method_option :daemon, :desc => "fork into background and run as a daemon", :type => :boolean, :default => false
23
+ method_option :kill, :desc => "send shutdown signal to control port", :type => :boolean, :aliases => "-k"
24
+ method_option :logfile, :desc => "redirect log messages to this file", :type => :string, :banner => "PATH"
25
+ def server
26
+ begin
27
+ require 'jenkins/war'
28
+ Jenkins::War::server(options)
29
+ rescue LoadError
30
+ puts "To run a jenkins server, you need to install the jenkins-war gem. try:"
31
+ puts "gem install jenkins-war"
32
+ end
33
+ end
34
+
35
+ desc "create project_path [options]", "create a build for your project"
36
+ common_options
37
+ method_option :rubies, :desc => "run tests against multiple explicit rubies via RVM", :type => :string
38
+ method_option :"node-labels", :desc => "run tests against multiple slave nodes by their label (comma separated)"
39
+ method_option :"assigned-node", :desc => "only use slave nodes with this label (similar to --node-labels)"
40
+ method_option :"no-build", :desc => "create job without initial build", :type => :boolean, :default => false
41
+ method_option :override, :desc => "override if job exists", :type => :boolean, :default => false
42
+ method_option :"scm", :desc => "specific SCM URI", :type => :string
43
+ method_option :"scm-branches", :desc => "list of branches to build from (comma separated)", :type => :string, :default => "master"
44
+ method_option :"public-scm", :desc => "use public scm URL", :type => :boolean, :default => false
45
+ method_option :template, :desc => "template of job steps (available: #{JobConfigBuilder::VALID_JOB_TEMPLATES.join ','})", :default => 'ruby'
46
+ method_option :"no-template", :desc => "do not use a template of default steps; avoids Gemfile requirement", :type => :boolean, :default => false
47
+ def create(project_path)
48
+ select_jenkins_server(options)
49
+ FileUtils.chdir(project_path) do
50
+ unless scm = Jenkins::ProjectScm.discover(options[:scm])
51
+ error "Cannot determine project SCM. Currently supported: #{Jenkins::ProjectScm.supported}"
52
+ end
53
+ unless (options[:template] == "none" || options[:"no-template"]) || File.exists?("Gemfile")
54
+ error "Ruby/Rails projects without a Gemfile are currently unsupported."
55
+ end
56
+ begin
57
+ template = options[:"no-template"] ? 'none' : options[:template]
58
+ job_config = Jenkins::JobConfigBuilder.new(template) do |c|
59
+ c.rubies = options[:rubies].split(/\s*,\s*/) if options[:rubies]
60
+ c.node_labels = options[:"node-labels"].split(/\s*,\s*/) if options[:"node-labels"]
61
+ c.scm = scm.url
62
+ c.scm_branches = options[:"scm-branches"].split(/\s*,\s*/)
63
+ c.assigned_node = options[:"assigned-node"] if options[:"assigned-node"]
64
+ c.public_scm = options[:"public-scm"]
65
+ end
66
+ name = File.basename(FileUtils.pwd)
67
+ if Jenkins::Api.create_job(name, job_config, options)
68
+ build_url = "#{@uri}/job/#{name.gsub(/\s/,'%20')}/build"
69
+ shell.say "Added#{' ' + template unless template == 'none'} project '#{name}' to Jenkins.", :green
70
+ unless options[:"no-build"]
71
+ shell.say "Triggering initial build..."
72
+ Jenkins::Api.build_job(name)
73
+ shell.say "Trigger additional builds via:"
74
+ else
75
+ shell.say "Trigger builds via:"
76
+ end
77
+ shell.say " URL: "; shell.say "#{build_url}", :yellow
78
+ shell.say " CLI: "; shell.say "#{cmd} build #{name}", :yellow
79
+ else
80
+ error "Failed to create project '#{name}'"
81
+ end
82
+ rescue Jenkins::JobConfigBuilder::InvalidTemplate
83
+ error "Invalid job template '#{template}'."
84
+ rescue Jenkins::Api::JobAlreadyExistsError
85
+ error "Job '#{name}' already exists."
86
+ end
87
+ end
88
+ end
89
+
90
+ desc "build [PROJECT_PATH]", "trigger build of this project's build job"
91
+ common_options
92
+ def build(project_path = ".")
93
+ select_jenkins_server(options)
94
+ FileUtils.chdir(project_path) do
95
+ name = File.basename(FileUtils.pwd)
96
+ if Jenkins::Api.build_job(name)
97
+ shell.say "Build for '#{name}' running now..."
98
+ else
99
+ error "No job '#{name}' on server."
100
+ end
101
+ end
102
+ end
103
+
104
+ desc "remove PROJECT_PATH", "remove this project's build job from Jenkins"
105
+ common_options
106
+ def remove(project_path)
107
+ select_jenkins_server(options)
108
+ FileUtils.chdir(project_path) do
109
+ name = File.basename(FileUtils.pwd)
110
+ if Jenkins::Api.delete_job(name)
111
+ shell.say "Removed project '#{name}' from Jenkins."
112
+ else
113
+ error "Failed to delete project '#{name}'."
114
+ end
115
+ end
116
+ end
117
+
118
+ desc "job NAME", "Display job details"
119
+ method_option :hash, :desc => 'Dump as formatted Ruby hash format'
120
+ method_option :json, :desc => 'Dump as JSON format'
121
+ method_option :yaml, :desc => 'Dump as YAML format'
122
+ common_options
123
+ def job(name)
124
+ select_jenkins_server(options)
125
+ if job = Jenkins::Api.job(name)
126
+ if options[:hash]
127
+ require "ap"
128
+ ap job.parsed_response
129
+ elsif options[:json]
130
+ puts job.parsed_response.to_json
131
+ elsif options[:yaml]
132
+ require "yaml"
133
+ puts job.parsed_response.to_yaml
134
+ else
135
+ error "Select an output format: --json, --xml, --yaml, --hash"
136
+ end
137
+ else
138
+ error "Cannot find project '#{name}'."
139
+ end
140
+ end
141
+
142
+ desc "list [options]", "list jobs on a jenkins server"
143
+ common_options
144
+ def list
145
+ select_jenkins_server(options)
146
+ summary = Jenkins::Api.summary
147
+ unless summary["jobs"].blank?
148
+ shell.say "#{@uri}:", :bold
149
+ summary["jobs"].each do |job|
150
+ bold = job['color'] =~ /anime/
151
+ color = 'red' if job['color'] =~ /red/
152
+ color = 'green' if job['color'] =~ /(blue|green)/
153
+ color ||= 'yellow' # if color =~ /grey/ || color == 'disabled'
154
+ shell.say "* "; shell.say(shell.set_color(job['name'], color.to_sym, bold), nil, true)
155
+ end
156
+ shell.say ""
157
+ else
158
+ shell.say "#{@uri}: "; shell.say "no jobs", :yellow
159
+ end
160
+ end
161
+
162
+ desc "nodes", "list jenkins server nodes"
163
+ common_options
164
+ def nodes
165
+ select_jenkins_server(options)
166
+ nodes = Jenkins::Api.nodes
167
+ nodes["computer"].each do |node|
168
+ color = node["offline"] ? :red : :green
169
+ shell.say node["displayName"], color
170
+ end
171
+ end
172
+
173
+ desc "add_node SLAVE_HOST", "add a URI (user@host:port) server as a slave node"
174
+ method_option :labels, :desc => 'Labels for a job --assigned_node to match against to select a slave (comma separated)'
175
+ method_option :"slave-user", :desc => 'SSH user for Jenkins to connect to slave node (default: deploy)'
176
+ method_option :"slave-port", :desc => 'SSH port for Jenkins to connect to slave node (default: 22)'
177
+ method_option :"master-key", :desc => 'Location of master public key or identity file'
178
+ method_option :"slave-fs", :desc => 'Location of file system on slave for Jenkins to use'
179
+ method_option :name, :desc => 'Name of slave node (default SLAVE_HOST)'
180
+ method_option :vagrant, :desc => 'Use settings for a Vagrant VM', :type => :boolean, :default => false
181
+ common_options
182
+ def add_node(slave_host)
183
+ select_jenkins_server(options)
184
+ if results = Jenkins::Api.add_node({:slave_host => slave_host}.merge(options))
185
+ shell.say "Added slave node '#{results[:name]}' to #{results[:slave_host]}", :green
186
+ else
187
+ error "Failed to add slave node #{slave_host}"
188
+ end
189
+ end
190
+
191
+ desc "default_host", "display current default host:port URI"
192
+ def default_host
193
+ if select_jenkins_server({})
194
+ display Jenkins::Api.base_uri
195
+ else
196
+ display "No default host yet. Use '--host host --port port' on your first request."
197
+ end
198
+ end
199
+
200
+ desc "help [command]", "show help for jenkins or for a specific command"
201
+ def help(*args)
202
+ super(*args)
203
+ end
204
+
205
+ desc "version", "show version information"
206
+ def version
207
+ shell.say "#{Jenkins::VERSION}"
208
+ end
209
+
210
+ def self.help(shell, *)
211
+ list = printable_tasks
212
+ shell.say <<-USEAGE
213
+ Jenkins.rb is a smart set of utilities for making
214
+ continuous integration as simple as possible
215
+
216
+ Usage: jenkins command [arguments] [options]
217
+
218
+ USEAGE
219
+
220
+ shell.say "Commands:"
221
+ shell.print_table(list, :ident => 2, :truncate => true)
222
+ shell.say
223
+ class_options_help(shell)
224
+ end
225
+
226
+ private
227
+
228
+ def select_jenkins_server(options)
229
+ unless @uri = Jenkins::Api.setup_base_url(options)
230
+ error "Either use --host or add remote servers."
231
+ end
232
+ @uri
233
+ end
234
+
235
+ def display(text)
236
+ shell.say text
237
+ exit
238
+ end
239
+
240
+ def error(text)
241
+ shell.say "ERROR: #{text}", :red
242
+ exit
243
+ end
244
+
245
+ def cmd
246
+ ENV['CUCUMBER_RUNNING'] ? 'jenkins' : $0
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,53 @@
1
+ require 'term/ansicolor'
2
+
3
+ module Jenkins
4
+ class CLI < Thor
5
+ module Formatting
6
+ module ClassMethods
7
+ def task_help(shell, task_name)
8
+ meth = normalize_task_name(task_name)
9
+ task = all_tasks[meth]
10
+ handle_no_task_error(meth) unless task
11
+
12
+ shell.say "usage: #{banner(task)}"
13
+ shell.say
14
+ class_options_help(shell, nil => task.options.map { |_, o| o })
15
+ # shell.say task.description
16
+ # shell.say
17
+ end
18
+
19
+
20
+ def print_options(shell, options, grp = nil)
21
+ return if options.empty?
22
+ table = options.map do |option|
23
+ prototype = if option.default
24
+ " [#{option.default}]"
25
+ elsif option.boolean?
26
+ ""
27
+ elsif option.required?
28
+ " #{option.banner}"
29
+ else
30
+ " [#{option.banner}]"
31
+ end
32
+ aliases = option.aliases.empty? ? "" : option.aliases.join(" ") + ","
33
+ [aliases, "--#{option.name}#{prototype}", "\t",option.description]
34
+ end
35
+ shell.print_table(table, :ident => 2)
36
+ shell.say
37
+ end
38
+ end
39
+
40
+ module InstanceMethods
41
+ def c
42
+ Term::ANSIColor
43
+ end
44
+ end
45
+
46
+ def self.included(receiver)
47
+ receiver.extend ClassMethods
48
+ receiver.send :include, InstanceMethods
49
+ end
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,27 @@
1
+ module Jenkins
2
+ module Config
3
+ extend self
4
+
5
+ def [](key)
6
+ config[key]
7
+ end
8
+
9
+ def config
10
+ @config ||= if File.exist?(config_file)
11
+ JSON.parse(File.read(config_file))
12
+ else
13
+ {}
14
+ end
15
+ end
16
+
17
+ def store!
18
+ @config ||= {}
19
+ FileUtils.mkdir_p(File.dirname(config_file))
20
+ File.open(config_file, "w") { |file| file << @config.to_json }
21
+ end
22
+
23
+ def config_file
24
+ @config_file ||= "#{ENV['HOME']}/.jenkins/jenkinsrb-config.json"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ class Hash
2
+ def with_clean_keys
3
+ self.inject({}) do |mem, (key, value)|
4
+ clean_key = key.to_s.gsub(/-/,"_").to_sym
5
+ mem[clean_key] = value
6
+ mem
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,77 @@
1
+ # Vendored from activesupport 3.0.1
2
+ class Object
3
+ # An object is blank if it's false, empty, or a whitespace string.
4
+ # For example, "", " ", +nil+, [], and {} are blank.
5
+ #
6
+ # This simplifies:
7
+ #
8
+ # if !address.nil? && !address.empty?
9
+ #
10
+ # ...to:
11
+ #
12
+ # if !address.blank?
13
+ def blank?
14
+ respond_to?(:empty?) ? empty? : !self
15
+ end
16
+
17
+ # An object is present if it's not blank.
18
+ def present?
19
+ !blank?
20
+ end
21
+
22
+ # Returns object if it's #present? otherwise returns nil.
23
+ # object.presence is equivalent to object.present? ? object : nil.
24
+ #
25
+ # This is handy for any representation of objects where blank is the same
26
+ # as not present at all. For example, this simplifies a common check for
27
+ # HTTP POST/query parameters:
28
+ #
29
+ # state = params[:state] if params[:state].present?
30
+ # country = params[:country] if params[:country].present?
31
+ # region = state || country || 'US'
32
+ #
33
+ # ...becomes:
34
+ #
35
+ # region = params[:state].presence || params[:country].presence || 'US'
36
+ def presence
37
+ self if present?
38
+ end
39
+ end
40
+
41
+ class NilClass #:nodoc:
42
+ def blank?
43
+ true
44
+ end
45
+ end
46
+
47
+ class FalseClass #:nodoc:
48
+ def blank?
49
+ true
50
+ end
51
+ end
52
+
53
+ class TrueClass #:nodoc:
54
+ def blank?
55
+ false
56
+ end
57
+ end
58
+
59
+ class Array #:nodoc:
60
+ alias_method :blank?, :empty?
61
+ end
62
+
63
+ class Hash #:nodoc:
64
+ alias_method :blank?, :empty?
65
+ end
66
+
67
+ class String #:nodoc:
68
+ def blank?
69
+ self !~ /\S/
70
+ end
71
+ end
72
+
73
+ class Numeric #:nodoc:
74
+ def blank?
75
+ false
76
+ end
77
+ end
@@ -0,0 +1,287 @@
1
+ require "builder"
2
+
3
+ module Jenkins
4
+ class JobConfigBuilder
5
+ attr_accessor :job_type
6
+ attr_accessor :steps, :rubies
7
+ attr_accessor :scm, :public_scm, :scm_branches
8
+ attr_accessor :scm, :public_scm, :git_branches
9
+ attr_accessor :assigned_node, :node_labels # TODO just one of these
10
+ attr_accessor :envfile
11
+
12
+ InvalidTemplate = Class.new(StandardError)
13
+
14
+ VALID_JOB_TEMPLATES = %w[none rails rails3 ruby rubygem]
15
+
16
+ # +job_type+ - template of default steps to create with the job
17
+ # +steps+ - array of [:method, cmd], e.g. [:build_shell_step, "bundle initial"]
18
+ # - Default is based on +job_type+.
19
+ # +scm+ - URL to the repository. Currently only support git URLs.
20
+ # +public_scm+ - convert the +scm+ URL to a publicly accessible URL for the Jenkins job config.
21
+ # +scm_branches+ - array of branches to run builds. Default: ['master']
22
+ # +rubies+ - list of RVM rubies to run tests (via Jenkins Axes).
23
+ # +assigned_node+ - restrict this job to running on slaves with these labels (space separated)
24
+ def initialize(job_type = :ruby, &block)
25
+ self.job_type = job_type.to_s if job_type
26
+
27
+ yield self
28
+
29
+ self.scm_branches ||= ["master"]
30
+ raise InvalidTemplate unless VALID_JOB_TEMPLATES.include?(job_type.to_s)
31
+ end
32
+
33
+ def builder
34
+ b = Builder::XmlMarkup.new :indent => 2
35
+ b.instruct!
36
+ b.tag!(matrix_project? ? "matrix-project" : "project") do
37
+ b.actions
38
+ b.description
39
+ b.keepDependencies false
40
+ b.properties
41
+ build_scm b
42
+ b.assignedNode assigned_node if assigned_node
43
+ b.canRoam !assigned_node
44
+ b.disabled false
45
+ b.blockBuildWhenUpstreamBuilding false
46
+ b.triggers :class => "vector"
47
+ b.concurrentBuild false
48
+ build_axes b if matrix_project?
49
+ build_steps b
50
+ b.publishers
51
+ build_wrappers b
52
+ b.runSequentially false if matrix_project?
53
+ end
54
+ end
55
+
56
+ def to_xml
57
+ builder.to_s
58
+ end
59
+
60
+ protected
61
+
62
+ # <scm class="hudson.plugins.git.GitSCM"> ... </scm>
63
+ def build_scm(b)
64
+ if scm && scm =~ /git/
65
+ scm_url = public_scm ? public_only_git_scm(scm) : scm
66
+ b.scm :class => "hudson.plugins.git.GitSCM" do
67
+ b.configVersion 1
68
+ b.remoteRepositories do
69
+ b.tag! "org.spearce.jgit.transport.RemoteConfig" do
70
+ b.string "origin"
71
+ b.int 5
72
+ b.string "fetch"
73
+ b.string "+refs/heads/*:refs/remotes/origin/*"
74
+ b.string "receivepack"
75
+ b.string "git-upload-pack"
76
+ b.string "uploadpack"
77
+ b.string "git-upload-pack"
78
+ b.string "url"
79
+ b.string scm_url
80
+ b.string "tagopt"
81
+ b.string
82
+ end
83
+ end
84
+
85
+ if scm_branches
86
+ b.branches do
87
+ scm_branches.each do |branch|
88
+ b.tag! "hudson.plugins.git.BranchSpec" do
89
+ b.name branch
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ b.localBranch
96
+ b.mergeOptions
97
+ b.recursiveSubmodules false
98
+ b.doGenerateSubmoduleConfigurations false
99
+ b.authorOrCommitter false
100
+ b.clean false
101
+ b.wipeOutWorkspace false
102
+ b.buildChooser :class => "hudson.plugins.git.util.DefaultBuildChooser"
103
+ b.gitTool "Default"
104
+ b.submoduleCfg :class => "list"
105
+ b.relativeTargetDir
106
+ b.excludedRegions
107
+ b.excludedUsers
108
+ end
109
+ end
110
+ end
111
+
112
+ def matrix_project?
113
+ !(rubies.blank? && node_labels.blank?)
114
+ end
115
+
116
+ # <hudson.matrix.TextAxis>
117
+ # <name>RUBY_VERSION</name>
118
+ # <values>
119
+ # <string>1.8.7</string>
120
+ # <string>1.9.2</string>
121
+ # <string>rbx-head</string>
122
+ # <string>jruby</string>
123
+ # </values>
124
+ # </hudson.matrix.TextAxis>
125
+ # <hudson.matrix.LabelAxis>
126
+ # <name>label</name>
127
+ # <values>
128
+ # <string>1.8.7</string>
129
+ # <string>ubuntu</string>
130
+ # </values>
131
+ # </hudson.matrix.LabelAxis>
132
+ def build_axes(b)
133
+ b.axes do
134
+ unless rubies.blank?
135
+ b.tag! "hudson.matrix.TextAxis" do
136
+ b.name "RUBY_VERSION"
137
+ b.values do
138
+ rubies.each do |rvm_name|
139
+ b.string rvm_name
140
+ end
141
+ end
142
+ end
143
+ end
144
+ unless node_labels.blank?
145
+ b.tag! "hudson.matrix.LabelAxis" do
146
+ b.name "label"
147
+ b.values do
148
+ node_labels.each do |label|
149
+ b.string label
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ # Example:
158
+ # <buildWrappers>
159
+ # <hudson.plugins.envfile.EnvFileBuildWrapper>
160
+ # <filePath>/path/to/env/file</filePath>
161
+ # </hudson.plugins.envfile.EnvFileBuildWrapper>
162
+ # </buildWrappers>
163
+ def build_wrappers(b)
164
+ if envfile
165
+ b.buildWrappers do
166
+ self.envfile = [envfile] unless envfile.is_a?(Array)
167
+ b.tag! "hudson.plugins.envfile.EnvFileBuildWrapper" do
168
+ envfile.each do |file|
169
+ b.filePath file
170
+ end
171
+ end
172
+ end
173
+ else
174
+ b.buildWrappers
175
+ end
176
+ end
177
+
178
+ # The important sequence of steps that are run to process a job build.
179
+ # Can be defaulted by the +job_type+ using +default_steps(job_type)+,
180
+ # or customized via +steps+ array.
181
+ def build_steps(b)
182
+ b.builders do
183
+ self.steps ||= default_steps(job_type)
184
+ steps.each do |step|
185
+ method, cmd = step
186
+ send(method.to_sym, b, cmd) # e.g. build_shell_step(b, "bundle install")
187
+ end
188
+ end
189
+ end
190
+
191
+ def default_steps(job_type)
192
+ steps = case job_type.to_sym
193
+ when :rails, :rails3
194
+ [
195
+ [:build_shell_step, "bundle install"],
196
+ [:build_ruby_step, <<-RUBY.gsub(/^ /, '')],
197
+ unless File.exist?("config/database.yml")
198
+ require 'fileutils'
199
+ example = Dir["config/database*"].first
200
+ puts "Using \#{example} for config/database.yml"
201
+ FileUtils.cp example, "config/database.yml"
202
+ end
203
+ RUBY
204
+ [:build_shell_step, "bundle exec rake db:create:all"],
205
+ [:build_shell_step, <<-RUBY.gsub(/^ /, '')],
206
+ if [ -f db/schema.rb ]; then
207
+ bundle exec rake db:schema:load
208
+ else
209
+ bundle exec rake db:migrate
210
+ fi
211
+ RUBY
212
+ [:build_shell_step, "bundle exec rake"]
213
+ ]
214
+ when :ruby, :rubygems
215
+ [
216
+ [:build_shell_step, "bundle install"],
217
+ [:build_shell_step, "bundle exec rake"]
218
+ ]
219
+ else
220
+ [ [:build_shell_step, 'echo "THERE ARE NO STEPS! Except this one..."'] ]
221
+ end
222
+ rubies.blank? ? steps : default_rvm_steps + steps
223
+ end
224
+
225
+ def default_rvm_steps
226
+ [
227
+ [:build_shell_step, "rvm $RUBY_VERSION"],
228
+ [:build_shell_step, "rvm gemset create ruby-$RUBY_VERSION && rvm gemset use ruby-$RUBY_VERSION"]
229
+ ]
230
+ end
231
+
232
+ # <hudson.tasks.Shell>
233
+ # <command>echo &apos;THERE ARE NO STEPS! Except this one...&apos;</command>
234
+ # </hudson.tasks.Shell>
235
+ def build_shell_step(b, command)
236
+ b.tag! "hudson.tasks.Shell" do
237
+ b.command command.to_xs.gsub("&amp;", '&') #.gsub(%r{"}, '&quot;').gsub(%r{'}, '&apos;')
238
+ end
239
+ end
240
+
241
+ # <hudson.plugins.ruby.Ruby>
242
+ # <command>unless File.exist?(&quot;config/database.yml&quot;)
243
+ # require &apos;fileutils&apos;
244
+ # example = Dir[&quot;config/database*&quot;].first
245
+ # puts &quot;Using #{example} for config/database.yml&quot;
246
+ # FileUtils.cp example, &quot;config/database.yml&quot;
247
+ # end</command>
248
+ # </hudson.plugins.ruby.Ruby>
249
+ def build_ruby_step(b, command)
250
+ b.tag! "hudson.plugins.ruby.Ruby" do
251
+ b.command do
252
+ b << command.to_xs.gsub(%r{"}, '&quot;').gsub(%r{'}, '&apos;')
253
+ end
254
+ end
255
+ end
256
+
257
+ # Usage: build_ruby_step b, "db:schema:load"
258
+ #
259
+ # <hudson.plugins.rake.Rake>
260
+ # <rakeInstallation>(Default)</rakeInstallation>
261
+ # <rakeFile></rakeFile>
262
+ # <rakeLibDir></rakeLibDir>
263
+ # <rakeWorkingDir></rakeWorkingDir>
264
+ # <tasks>db:schema:load</tasks>
265
+ # <silent>false</silent>
266
+ # </hudson.plugins.rake.Rake>
267
+ def build_rake_step(b, tasks)
268
+ b.tag! "hudson.plugins.rake.Rake" do
269
+ b.rakeInstallation "(Default)"
270
+ b.rakeFile
271
+ b.rakeLibDir
272
+ b.rakeWorkingDir
273
+ b.tasks tasks
274
+ b.silent false
275
+ end
276
+ end
277
+
278
+ # Converts git@github.com:drnic/newgem.git into git://github.com/drnic/newgem.git
279
+ def public_only_git_scm(scm_url)
280
+ if scm_url =~ /git@([\w\-_.]+):(.+)\.git/
281
+ "git://#{$1}/#{$2}.git"
282
+ else
283
+ scm_url
284
+ end
285
+ end
286
+ end
287
+ end