jenkins 0.6.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 (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