require "builder" module Jenkins class JobConfigBuilder attr_accessor :job_type attr_accessor :steps, :rubies attr_accessor :scm, :public_scm, :scm_branches attr_accessor :scm, :public_scm, :git_branches attr_accessor :assigned_node, :node_labels # TODO just one of these attr_accessor :envfile InvalidTemplate = Class.new(StandardError) VALID_JOB_TEMPLATES = %w[none rails rails3 ruby rubygem] # +job_type+ - template of default steps to create with the job # +steps+ - array of [:method, cmd], e.g. [:build_shell_step, "bundle initial"] # - Default is based on +job_type+. # +scm+ - URL to the repository. Currently only support git URLs. # +public_scm+ - convert the +scm+ URL to a publicly accessible URL for the Jenkins job config. # +scm_branches+ - array of branches to run builds. Default: ['master'] # +rubies+ - list of RVM rubies to run tests (via Jenkins Axes). # +assigned_node+ - restrict this job to running on slaves with these labels (space separated) def initialize(job_type = :ruby, &block) self.job_type = job_type.to_s if job_type yield self self.scm_branches ||= ["master"] raise InvalidTemplate unless VALID_JOB_TEMPLATES.include?(job_type.to_s) end def builder b = Builder::XmlMarkup.new :indent => 2 b.instruct! b.tag!(matrix_project? ? "matrix-project" : "project") do b.actions b.description b.keepDependencies false b.properties build_scm b b.assignedNode assigned_node if assigned_node b.canRoam !assigned_node b.disabled false b.blockBuildWhenUpstreamBuilding false b.triggers :class => "vector" b.concurrentBuild false build_axes b if matrix_project? build_steps b b.publishers build_wrappers b b.runSequentially false if matrix_project? end end def to_xml builder.to_s end protected # ... def build_scm(b) if scm && scm =~ /git/ scm_url = public_scm ? public_only_git_scm(scm) : scm b.scm :class => "hudson.plugins.git.GitSCM" do b.configVersion 1 b.remoteRepositories do b.tag! "org.spearce.jgit.transport.RemoteConfig" do b.string "origin" b.int 5 b.string "fetch" b.string "+refs/heads/*:refs/remotes/origin/*" b.string "receivepack" b.string "git-upload-pack" b.string "uploadpack" b.string "git-upload-pack" b.string "url" b.string scm_url b.string "tagopt" b.string end end if scm_branches b.branches do scm_branches.each do |branch| b.tag! "hudson.plugins.git.BranchSpec" do b.name branch end end end end b.localBranch b.mergeOptions b.recursiveSubmodules false b.doGenerateSubmoduleConfigurations false b.authorOrCommitter false b.clean false b.wipeOutWorkspace false b.buildChooser :class => "hudson.plugins.git.util.DefaultBuildChooser" b.gitTool "Default" b.submoduleCfg :class => "list" b.relativeTargetDir b.excludedRegions b.excludedUsers end end end def matrix_project? !(rubies.blank? && node_labels.blank?) end # # RUBY_VERSION # # 1.8.7 # 1.9.2 # rbx-head # jruby # # # # label # # 1.8.7 # ubuntu # # def build_axes(b) b.axes do unless rubies.blank? b.tag! "hudson.matrix.TextAxis" do b.name "RUBY_VERSION" b.values do rubies.each do |rvm_name| b.string rvm_name end end end end unless node_labels.blank? b.tag! "hudson.matrix.LabelAxis" do b.name "label" b.values do node_labels.each do |label| b.string label end end end end end end # Example: # # # /path/to/env/file # # def build_wrappers(b) if envfile b.buildWrappers do self.envfile = [envfile] unless envfile.is_a?(Array) b.tag! "hudson.plugins.envfile.EnvFileBuildWrapper" do envfile.each do |file| b.filePath file end end end else b.buildWrappers end end # The important sequence of steps that are run to process a job build. # Can be defaulted by the +job_type+ using +default_steps(job_type)+, # or customized via +steps+ array. def build_steps(b) b.builders do self.steps ||= default_steps(job_type) steps.each do |step| method, cmd = step send(method.to_sym, b, cmd) # e.g. build_shell_step(b, "bundle install") end end end def default_steps(job_type) steps = case job_type.to_sym when :rails, :rails3 [ [:build_shell_step, "bundle install"], [:build_ruby_step, <<-RUBY.gsub(/^ /, '')], unless File.exist?("config/database.yml") require 'fileutils' example = Dir["config/database*"].first puts "Using \#{example} for config/database.yml" FileUtils.cp example, "config/database.yml" end RUBY [:build_shell_step, "bundle exec rake db:create:all"], [:build_shell_step, <<-RUBY.gsub(/^ /, '')], if [ -f db/schema.rb ]; then bundle exec rake db:schema:load else bundle exec rake db:migrate fi RUBY [:build_shell_step, "bundle exec rake"] ] when :ruby, :rubygems [ [:build_shell_step, "bundle install"], [:build_shell_step, "bundle exec rake"] ] else [ [:build_shell_step, 'echo "THERE ARE NO STEPS! Except this one..."'] ] end rubies.blank? ? steps : default_rvm_steps + steps end def default_rvm_steps [ [:build_shell_step, "rvm $RUBY_VERSION"], [:build_shell_step, "rvm gemset create ruby-$RUBY_VERSION && rvm gemset use ruby-$RUBY_VERSION"] ] end # # echo 'THERE ARE NO STEPS! Except this one...' # def build_shell_step(b, command) b.tag! "hudson.tasks.Shell" do b.command command.to_xs.gsub("&", '&') #.gsub(%r{"}, '"').gsub(%r{'}, ''') end end # # unless File.exist?("config/database.yml") # require 'fileutils' # example = Dir["config/database*"].first # puts "Using #{example} for config/database.yml" # FileUtils.cp example, "config/database.yml" # end # def build_ruby_step(b, command) b.tag! "hudson.plugins.ruby.Ruby" do b.command do b << command.to_xs.gsub(%r{"}, '"').gsub(%r{'}, ''') end end end # Usage: build_ruby_step b, "db:schema:load" # # # (Default) # # # # db:schema:load # false # def build_rake_step(b, tasks) b.tag! "hudson.plugins.rake.Rake" do b.rakeInstallation "(Default)" b.rakeFile b.rakeLibDir b.rakeWorkingDir b.tasks tasks b.silent false end end # Converts git@github.com:drnic/newgem.git into git://github.com/drnic/newgem.git def public_only_git_scm(scm_url) if scm_url =~ /git@([\w\-_.]+):(.+)\.git/ "git://#{$1}/#{$2}.git" else scm_url end end end end