diff --git a/.ruby-gemset b/.ruby-gemset
new file mode 100644
index 0000000..b45f591
--- /dev/null
+++ b/.ruby-gemset
@@ -0,0 +1 @@
+jenkins_pipeline_builder
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 0000000..1dbd866
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+ruby-2.0.0-p247
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..434273b
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,4 @@
+source "https://rubygems.org"
+
+# Specify your gem's dependencies in jenkins_pipeline_builder.gemspec
+gemspec
diff --git a/README.md b/README.md
index 73afffb..9ea7b63 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,136 @@
jenkins-pipeline-builder
========================
-YAML driven CI pipeline generator enabling to version your artifact pipelines alongside with the artifact source itself.
+YAML driven CI Jenkins Pipeline Builder enabling to version your artifact pipelines alongside with the artifact source itself.
+
+
+# JenkinsPipeline::Generator
+
+TODO: Write a gem description
+
+This is how you can bootstrap a pipeline:
+
+ generate pipeline -d -c config/login.yml bootstrap ./pipeline-archetype
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+ gem 'jenkins_pipeline_builder'
+
+And then execute:
+
+ $ bundle
+
+Or install it yourself as:
+
+ $ gem install jenkins_pipeline_builder
+
+ brew install libxml2 libxslt
+ [optional] brew link libxml2 libxslt
+ gem install nokogiri
+
+## Usage
+
+TODO: Write usage instructions here
+
+## Contributing
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Add some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
+
+## Job DSL
+Here's a high level overview of what's available:
+
+```yaml
+- job:
+ name: nameStr # Name of your Job
+ job_type: free_style # Optional [free_style|multi_project]
+ parameters:
+ - name: param_name
+ type: string
+ default: default value
+ description: text
+ scm_provider: git # See more info on Jenkins Api Client
+ scm_url: git@github.com:your_url_here
+ scm_branch: master
+ scm_params:
+ excuded_users: user
+ local_branch: branch_name
+ recursive_update: true
+ wipe_workspace: true
+ shell_command: '. commit_build.sh'
+ hipchat:
+ room: room name here
+ start-notify: true
+ builders:
+ - job_builder:
+ child_jobs:
+ - job-name-1
+ - job-name-2
+ mark_phase: SUCCESSFUL
+ - inject_vars_file: build_job_info
+ - shell_command: |
+ echo 'Doing some work'
+ run command1
+ - maven3:
+ goals: -B clean
+ wrappers:
+ - timestamp: true
+ - ansicolor: true
+ - artifactory:
+ url: 'https://url.com/path'
+ artifactory-name: 'key'
+ target-repo: gems-local
+ publish: 'pkg/*.gem'
+ publish-build-info: true
+ - inject_env_var: |
+ VAR1 = value_1
+ VAR2 = value_2
+ - inject_passwords:
+ - name: pwd_name
+ value: some_encrypted_password
+ - rvm: "ruby-version@ruby-gemset"
+ publishers:
+ - junit_result:
+ test_results: 'out/**/*.xml'
+ - git:
+ push-merge: true
+ push-only-if-success: false
+ - hipchat:
+ jenkinsUrl: 'https://jenkins_url/'
+ authToken: 'auth_token'
+ room: 'room name'
+ - coverage_result:
+ report_dir: out/coverage/rcov
+ total:
+ healthy: 80
+ unhealthy: 0
+ unstable: 0
+ code:
+ healthy: 80
+ unhealthy: 0
+ unstable: 0
+ - description_setter:
+ regexp: See the build details at (.*)
+ description: 'Build Details: \1'
+ - downstream:
+ project: project_name
+ data:
+ - params: |
+ PARAM1=value1
+ PARAM2=value2
+ - file: promote-job-params
+ triggers:
+ - git_push: true
+ - scm_polling: 'H/5 * * * *'
+ build_flow: |
+ guard {
+ build("job_name1", param1: params["param1"]);
+ } rescue {
+ build("job_name2", param1: build21.environment.get("some_var"))
+ }
+```
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..f40e46b
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,68 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+#!/usr/bin/env rake
+require 'rspec/core/rake_task'
+require 'yard'
+
+RSpec::Core::RakeTask.new(:spec, :tag) do |t, task_args|
+ t.pattern = ".spec/**/*_spec.rb"
+ t.verbose = true
+ t.fail_on_error = false
+ t.rspec_opts = spec_output 'spec.xml'
+end
+
+def spec_output(filename)
+ "--color --format documentation --format RspecJunitFormatter --out out/#{filename}"
+end
+
+RSpec::Core::RakeTask.new(:unit_tests) do |spec|
+ spec.pattern = FileList['spec/unit_tests/*_spec.rb']
+ spec.rspec_opts = ['--color', '--format documentation']
+end
+
+RSpec::Core::RakeTask.new(:func_tests) do |spec|
+ spec.pattern = FileList['spec/func_tests/*_spec.rb']
+ spec.rspec_opts = ['--color', '--format documentation']
+end
+
+RSpec::Core::RakeTask.new(:test) do |spec|
+ spec.pattern = FileList['spec/*/*.rb']
+ spec.rspec_opts = ['--color', '--format documentation']
+end
+
+YARD::Config.load_plugin 'thor'
+YARD::Rake::YardocTask.new do |t|
+ t.files = ['lib/**/*.rb', 'lib/**/**/*.rb']
+end
+
+namespace :doc do
+ # This task requires that graphviz is installed locally. For more info:
+ # http://www.graphviz.org/
+ desc "Generates the class diagram using the yard generated dot file"
+ task :generate_class_diagram do
+ puts "Generating the dot file..."
+ `yard graph --file jenkins_api_client.dot`
+ puts "Generating class diagram from the dot file..."
+ `dot jenkins_api_client.dot -Tpng -o jenkins_api_client_class_diagram.png`
+ end
+end
diff --git a/bin/generate b/bin/generate
new file mode 100755
index 0000000..a15e34e
--- /dev/null
+++ b/bin/generate
@@ -0,0 +1,28 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+#!/usr/bin/env ruby
+
+$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
+require 'jenksin_pipeline_builder'
+JenkinsPipelineBuilder::CLI::Base.start
+
diff --git a/config/login.yml b/config/login.yml
new file mode 100644
index 0000000..983397f
--- /dev/null
+++ b/config/login.yml
@@ -0,0 +1,24 @@
+# The Jenkins server and login information can be stored in a YAML file like this
+# so we don't have to pass in the parameters every we login to the server
+# through this API client.
+
+# This file contains the following four parameters
+
+# The IP address of the Jenkins CI Server
+
+:server_ip: server_ip
+#:server_url: https://server_url/
+
+# The port number on which the Jenkins listens on. The default is 8080
+
+#:server_port: 8080
+
+# If there is a web server routing your Jenkins requests you might need this
+
+#:jenkins_path: ""
+
+# The username and password to authenticate to the server
+
+:username: username
+#:password: my_password
+:password_base64: my_base64_password
diff --git a/jenkins_pipeline_builder.gemspec b/jenkins_pipeline_builder.gemspec
new file mode 100644
index 0000000..7632728
--- /dev/null
+++ b/jenkins_pipeline_builder.gemspec
@@ -0,0 +1,42 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'jenksin_pipeline_builder/version'
+
+Gem::Specification.new do |spec|
+ spec.name = 'jenkins_pipeline_builder'
+ spec.version = JenkinsPipelineBuilder::VERSION
+ spec.authors = ['Igor Moochnick']
+ spec.email = %w(igor.moochnick@gmail.com)
+ spec.description = %q{This is a simple and easy-to-use Jenkins Pipeline generator with features focused on
+automating Job & Pipeline creation from the YAML files checked-in with your application source code}
+ spec.summary = %q{This gem is will boostrap your Jenkins pipelines}
+ spec.homepage = 'https://github.com/IgorShare/jenkins_pipeline_builder'
+ spec.license = 'MIT'
+
+ spec.files = `git ls-files`.split($/)
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
+ spec.require_paths = ["lib"]
+
+ ENV['NOKOGIRI_USE_SYSTEM_LIBRARIES']='true'
+
+ spec.add_dependency 'nokogiri', '~> 1.5.0'
+ spec.add_dependency 'jenkins_api_client', '~> 0.14.1'
+ spec.add_dependency 'thor', '>= 0.18.0'
+
+ spec.add_development_dependency 'bundler', '~> 1.3'
+ spec.add_development_dependency 'rake'
+ spec.add_development_dependency 'rspec', '~> 2.14'
+ spec.add_development_dependency 'bump'
+ spec.add_development_dependency 'gem-release'
+ spec.add_development_dependency 'pry'
+ spec.add_development_dependency 'simplecov'
+ spec.add_development_dependency 'simplecov-rcov'
+ spec.add_development_dependency 'kwalify'
+ spec.add_development_dependency 'equivalent-xml'
+ spec.add_development_dependency 'yard-thor'
+ spec.add_development_dependency 'yard'
+ spec.add_development_dependency 'rspec_junit_formatter'
+ spec.add_development_dependency 'webmock'
+end
diff --git a/lib/jenksin_pipeline_builder.rb b/lib/jenksin_pipeline_builder.rb
new file mode 100644
index 0000000..3c7388d
--- /dev/null
+++ b/lib/jenksin_pipeline_builder.rb
@@ -0,0 +1,39 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+require 'jenksin_pipeline_builder/version'
+require 'jenksin_pipeline_builder/utils'
+require 'jenksin_pipeline_builder/xml_helper'
+require 'jenksin_pipeline_builder/compiler'
+require 'jenksin_pipeline_builder/job_builder'
+require 'jenksin_pipeline_builder/module_registry'
+require 'jenksin_pipeline_builder/builders'
+require 'jenksin_pipeline_builder/wrappers'
+require 'jenksin_pipeline_builder/triggers'
+require 'jenksin_pipeline_builder/publishers'
+require 'jenksin_pipeline_builder/view'
+require 'jenksin_pipeline_builder/generator'
+
+require 'jenksin_pipeline_builder/cli/helper'
+require 'jenksin_pipeline_builder/cli/view'
+require 'jenksin_pipeline_builder/cli/pipeline'
+require 'jenksin_pipeline_builder/cli/base'
diff --git a/lib/jenksin_pipeline_builder/builders.rb b/lib/jenksin_pipeline_builder/builders.rb
new file mode 100644
index 0000000..3241288
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/builders.rb
@@ -0,0 +1,72 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class Builders
+ def self.build_multijob_builder(params, xml)
+ xml.send('com.tikal.jenkins.plugins.multijob.MultiJobBuilder') {
+ xml.phaseName 'Child Jobs'
+ xml.phaseJobs {
+ proc = lambda do |xml, name|
+ xml.send('com.tikal.jenkins.plugins.multijob.PhaseJobsConfig') {
+ xml.jobName name
+ xml.currParams false
+ xml.exposedSCM false
+ xml.configs {
+ xml.send('hudson.plugins.parameterizedtrigger.PredefinedBuildParameters') {
+ xml.properties 'PARENT_WORKSPACE=${WORKSPACE}'
+ }
+ }
+ }
+ end
+ params[:child_jobs].each do |name|
+ proc.call xml, name
+ end
+ }
+ xml.continuationCondition params[:mark_phase]
+ }
+ end
+
+ def self.build_maven3(params, xml)
+ xml.send('org.jfrog.hudson.maven3.Maven3Builder') {
+ xml.mavenName 'tools-maven-3.0.3'
+ xml.rootPom params[:rootPom]
+ xml.goals params[:goals]
+ xml.mavenOpts params[:options]
+ }
+ end
+
+ def self.build_shell_command(param, xml)
+ xml.send('hudson.tasks.Shell') {
+ xml.command param
+ }
+ end
+
+ def self.build_environment_vars_injector(params, xml)
+ xml.EnvInjectBuilder {
+ xml.info {
+ xml.propertiesFilePath params
+ }
+ }
+ end
+ end
+end
diff --git a/lib/jenksin_pipeline_builder/cli/base.rb b/lib/jenksin_pipeline_builder/cli/base.rb
new file mode 100644
index 0000000..21d917c
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/cli/base.rb
@@ -0,0 +1,69 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+require 'thor'
+#require "#{File.dirname(__FILE__)}/pipeline.rb"
+
+module JenkinsPipelineBuilder
+ module CLI
+ class Base < Thor
+
+ class_option :username, :aliases => "-u", :desc => "Name of Jenkins user"
+ class_option :password, :aliases => "-p",
+ :desc => "Password of Jenkins user"
+ class_option :password_base64, :aliases => "-b",
+ :desc => "Base 64 encoded password of Jenkins user"
+ class_option :server_ip, :aliases => "-s",
+ :desc => "Jenkins server IP address"
+ class_option :server_port, :aliases => "-o", :desc => "Jenkins port"
+ class_option :creds_file, :aliases => "-c",
+ :desc => "Credentials file for communicating with Jenkins server"
+ class_option :debug, :type => :boolean, :aliases => "-d", :desc => "Run in debug mode (no Jenkins changes)",
+ :default => false
+
+
+ map "-v" => :version
+
+ desc "version", "Shows current version"
+ # CLI command that returns the version of Jenkins API Client
+ def version
+ puts JenkinsPipelineBuilder::VERSION
+ end
+
+ # Register the CLI::Pipeline class as "pipeline" subcommand to CLI
+ register(
+ CLI::Pipeline,
+ 'pipeline',
+ 'pipeline [subcommand]',
+ 'Provides functions to access pipeline functions of the Jenkins CI server'
+ )
+
+ # Register the CLI::Job class as "view" subcommand to CLI
+ register(
+ CLI::View,
+ 'view',
+ 'view [subcommand]',
+ 'Provides functions to access view interface of Jenkins CI server'
+ )
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/cli/helper.rb b/lib/jenksin_pipeline_builder/cli/helper.rb
new file mode 100644
index 0000000..f3ebf65
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/cli/helper.rb
@@ -0,0 +1,68 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+require 'fileutils'
+require 'yaml'
+require 'jenkins_api_client'
+
+module JenkinsPipelineBuilder
+ module CLI
+ # This is the helper class that sets up the credentials from the command
+ # line parameters given and initializes the Jenkins Pipeline Builder.
+ class Helper
+ # Sets up the credentials and initializes the Jenkins Pipeline Builder
+ #
+ # @param [Hash] options Options obtained from the command line
+ #
+ # @return [JenkinsPipelineBuilder::Generator] A new Client object
+ #
+ def self.setup(options)
+ if options[:username] && options[:server_ip] && \
+ (options[:password] || options[:password_base64])
+ creds = options
+ elsif options[:creds_file]
+ creds = YAML.load_file(
+ #File.expand_path(options[:creds_file], __FILE__)
+ options[:creds_file]
+ )
+ elsif File.exist?("#{ENV['HOME']}/.jenkins_api_client/login.yml")
+ creds = YAML.load_file(
+ File.expand_path(
+ "#{ENV['HOME']}/.jenkins_api_client/login.yml", __FILE__
+ )
+ )
+ else
+ msg = "Credentials are not set. Please pass them as parameters or"
+ msg << " set them in the default credentials file"
+ puts msg
+ exit 1
+ end
+
+ #creds[:log_level] = Logger::DEBUG
+ client = JenkinsApi::Client.new(creds)
+ generator = JenkinsPipelineBuilder::Generator.new(options, client)
+ generator.debug = options[:debug] #== 'debug'
+ return generator
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/cli/pipeline.rb b/lib/jenksin_pipeline_builder/cli/pipeline.rb
new file mode 100644
index 0000000..f9218f9
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/cli/pipeline.rb
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ module CLI
+ # This class provides various command line operations related to jobs.
+ class Pipeline < Thor
+ include Thor::Actions
+
+ desc 'dump', 'Dump job'
+ def dump(job_name)
+ Helper.setup(parent_options).dump(job_name)
+ end
+
+ desc 'bootstrap Path', 'Generates pipeline from folder or a file'
+ def bootstrap(path)
+ Helper.setup(parent_options).bootstrap(path)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/cli/view.rb b/lib/jenksin_pipeline_builder/cli/view.rb
new file mode 100644
index 0000000..28ef6ce
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/cli/view.rb
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ module CLI
+ # This class provides various command line operations related to jobs.
+ class View < Thor
+ include Thor::Actions
+
+ desc 'dump', 'Dump view'
+ def dump(job_name)
+ Helper.setup(parent_options).dump(job_name)
+ end
+
+ desc 'bootstrap Path', 'Generates view from folder or a file'
+ def create(path)
+ Helper.setup(parent_options).view.generate(path)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/compiler.rb b/lib/jenksin_pipeline_builder/compiler.rb
new file mode 100644
index 0000000..d4e981a
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/compiler.rb
@@ -0,0 +1,81 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class Compiler
+ def self.resolve_value(value, settings)
+ value_s = value.to_s.clone
+ vars = value_s.scan(/{{([^}]+)}}/).flatten
+ vars.select! do |var|
+ var_val = settings[var.to_sym]
+ value_s.gsub!("{{#{var.to_s}}}", var_val) unless var_val.nil?
+ var_val.nil?
+ end
+ return nil if vars.count != 0
+ return value_s
+ end
+
+ def self.get_settings_bag(item_bag, settings_bag = {})
+ item = item_bag[:value]
+ bag = {}
+ return unless item.kind_of?(Hash)
+ item.keys.each do |k|
+ val = item[k]
+ if val.kind_of? String
+ new_value = resolve_value(val, settings_bag)
+ return nil if new_value.nil?
+ bag[k] = new_value
+ end
+ end
+ my_settings_bag = settings_bag.clone
+ return my_settings_bag.merge(bag)
+ end
+
+ def self.compile(item, settings = {})
+ case item
+ when String
+ new_value = resolve_value(item, settings)
+ puts "Failed to resolve #{item}" if new_value.nil?
+ return new_value
+ when Hash
+ result = {}
+ item.each do |key, value|
+ new_value = compile(value, settings)
+ puts "Failed to resolve #{value}" if new_value.nil?
+ return nil if new_value.nil?
+ result[key] = new_value
+ end
+ return result
+ when Array
+ result = []
+ item.each do |value|
+ new_value = compile(value, settings)
+ puts "Failed to resolve #{value}" if new_value.nil?
+ return nil if new_value.nil?
+ result << new_value
+ end
+ return result
+ end
+ return item
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/generator.rb b/lib/jenksin_pipeline_builder/generator.rb
new file mode 100644
index 0000000..22cc1a4
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/generator.rb
@@ -0,0 +1,346 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+require 'yaml'
+require 'pp'
+
+module JenkinsPipelineBuilder
+ class Generator
+ # Initialize a Client object with Jenkins Api Client
+ #
+ # @param args [Hash] Arguments to connect to Jenkins server
+ #
+ # @option args [String] :something some option description
+ #
+ # @return [JenkinsPipelineBuilder::Generator] a client generator
+ #
+ # @raise [ArgumentError] when required options are not provided.
+ #
+ def initialize(args, client)
+ @client = client
+ @logger = @client.logger
+ @job_templates = {}
+ @job_collection = {}
+
+ @module_registry = ModuleRegistry.new ({
+ job: {
+ description: JobBuilder.method(:change_description),
+ scm_params: JobBuilder.method(:apply_scm_params),
+ hipchat: JobBuilder.method(:hipchat_notifier),
+ parameters: JobBuilder.method(:build_parameters),
+ builders: {
+ registry: {
+ job_builder: Builders.method(:build_multijob_builder),
+ inject_vars_file: Builders.method(:build_environment_vars_injector),
+ shell_command: Builders.method(:build_shell_command),
+ maven3: Builders.method(:build_maven3)
+ },
+ method:
+ lambda { |registry, params, n_xml| @module_registry.run_registry_on_path('//builders', registry, params, n_xml) }
+ },
+ publishers: {
+ registry: {
+ git: Publishers.method(:push_to_git),
+ hipchat: Publishers.method(:push_to_hipchat),
+ description_setter: Publishers.method(:description_setter),
+ downstream: Publishers.method(:push_to_projects),
+ junit_result: Publishers.method(:publish_junit),
+ coverage_result: Publishers.method(:publish_rcov)
+ },
+ method:
+ lambda { |registry, params, n_xml| @module_registry.run_registry_on_path('//publishers', registry, params, n_xml) }
+ },
+ wrappers: {
+ registry: {
+ timestamp: Wrappers.method(:console_timestamp),
+ ansicolor: Wrappers.method(:ansicolor),
+ artifactory: Wrappers.method(:publish_to_artifactory),
+ rvm: Wrappers.method(:run_with_rvm),
+ inject_env_var: Wrappers.method(:inject_env_vars),
+ inject_passwords: Wrappers.method(:inject_passwords)
+ },
+ method:
+ lambda { |registry, params, n_xml| @module_registry.run_registry_on_path('//buildWrappers', registry, params, n_xml) }
+ },
+ triggers: {
+ registry: {
+ git_push: Triggers.method(:enable_git_push),
+ scm_polling: Triggers.method(:enable_scm_polling)
+ },
+ method:
+ lambda { |registry, params, n_xml| @module_registry.run_registry_on_path('//triggers', registry, params, n_xml) }
+ }
+ }
+ })
+ end
+
+ attr_accessor :client
+ attr_accessor :debug
+ # TODO: WTF?
+ attr_accessor :no_files
+ attr_accessor :job_collection
+
+ # Creates an instance to the View class by passing a reference to self
+ #
+ # @return [JenkinsApi::Client::System] An object to System subclass
+ #
+ def view
+ JenkinsPipelineBuilder::View.new(self)
+ end
+
+ def load_collection_from_path(path, recursively = false)
+ if File.directory?(path)
+ @logger.info "Generating from folder #{path}"
+ Dir.glob(File.join(path, '/*.yaml')).each do |file|
+ if File.directory?(file)
+ if recursively
+ load_collection_from_path(File.join(path, file), recursively)
+ else
+ next
+ end
+ end
+ @logger.info "Loading file #{file}"
+ yaml = YAML.load_file(file)
+ load_job_collection(yaml)
+ end
+ else
+ @logger.info "Loading file #{path}"
+ yaml = YAML.load_file(path)
+ load_job_collection(yaml)
+ end
+ end
+
+ def load_job_collection(yaml)
+ yaml.each do |section|
+ Utils.symbolize_keys_deep!(section)
+ key = section.keys.first
+ value = section[key]
+ name = value[:name]
+ raise "Duplicate item with name '#{name}' was detected." if @job_collection.has_key?(name)
+ @job_collection[name.to_s] = { name: name.to_s, type: key, value: value }
+ end
+ end
+
+ def get_item(name)
+ @job_collection[name.to_s]
+ end
+
+ def resolve_project(project)
+ defaults = get_item('global')
+ settings = defaults.nil? ? {} : defaults[:value] || {}
+
+ project[:settings] = Compiler.get_settings_bag(project, settings) unless project[:settings]
+ project_body = project[:value]
+
+ # Process jobs
+ jobs = project_body[:jobs] || []
+ jobs.map! do |job|
+ job.kind_of?(String) ? { job.to_sym => {} } : job
+ end
+ @logger.info project
+ jobs.each do |job|
+ job_id = job.keys.first
+ settings = project[:settings].clone.merge(job[job_id])
+ job[:result] = resolve_job_by_name(job_id, settings)
+ end
+
+ # Process views
+ views = project_body[:views] || []
+ views.map! do |view|
+ view.kind_of?(String) ? { view.to_sym => {} } : view
+ end
+ views.each do |view|
+ view_id = view.keys.first
+ settings = project[:settings].clone.merge(view[view_id])
+ # TODO: rename resolve_job_by_name properly
+ view[:result] = resolve_job_by_name(view_id, settings)
+ end
+
+ project
+ end
+
+ def resolve_job_by_name(name, settings = {})
+ job = get_item(name)
+ raise "Failed to locate job by name '#{name}'" if job.nil?
+ job_value = job[:value]
+ compiled_job = Compiler.compile(job_value, settings)
+ return compiled_job
+ end
+
+ def projects
+ result = []
+ @job_collection.values.each do |item|
+ result << item if item[:type] == :project
+ end
+ return result
+ end
+
+ def bootstrap(path)
+ @logger.info "Bootstrapping pipeline from path #{path}"
+ load_collection_from_path(path)
+
+ projects.each do |project|
+ compiled_project = resolve_project(project)
+ pp compiled_project
+
+ if compiled_project[:value][:jobs]
+ compiled_project[:value][:jobs].each do |i|
+ job = i[:result]
+ xml = compile_job_to_xml(job)
+ create_or_update(job, xml)
+ end
+ end
+
+ if compiled_project[:value][:views]
+ compiled_project[:value][:views].each do |v|
+ _view = v[:result]
+ view.create(_view)
+ end
+ end
+ end
+
+ end
+
+ def dump(job_name)
+ @logger.info "Debug #{@debug}"
+ @logger.info "Dumping #{job_name} into #{job_name}.xml"
+ xml = @client.job.get_config(job_name)
+ File.open(job_name + '.xml', 'w') { |f| f.write xml }
+ end
+
+ def create_or_update(job, xml)
+ job_name = job[:name]
+ if @debug
+ @logger.info "Will create job #{job}"
+ @logger.info "#{xml}"
+ File.open(job_name + '.xml', 'w') { |f| f.write xml }
+ return
+ end
+
+ if @client.job.exists?(job_name)
+ @client.job.update(job_name, xml)
+ else
+ @client.job.create(job_name, xml)
+ end
+ end
+
+ def compile_job_to_xml(job)
+ raise 'Job name is not specified' unless job[:name]
+
+ @logger.info "Creating Yaml Job #{job}"
+ job[:job_type] = 'free_style' unless job[:job_type]
+ case job[:job_type]
+ when 'job_dsl'
+ xml = compile_freestyle_job_to_xml(job)
+ update_job_dsl(job, xml)
+ when 'multi_project'
+ xml = compile_freestyle_job_to_xml(job)
+ adjust_multi_project xml
+ when 'build_flow'
+ xml = compile_freestyle_job_to_xml(job)
+ add_job_dsl(job, xml)
+ when 'free_style'
+ compile_freestyle_job_to_xml job
+ else
+ @logger.info 'Unknown job type'
+ ''
+ end
+
+ end
+
+ def adjust_multi_project(xml)
+ n_xml = Nokogiri::XML(xml)
+ root = n_xml.root()
+ root.name = 'com.tikal.jenkins.plugins.multijob.MultiJobProject'
+ n_xml.to_xml
+ end
+
+ def compile_freestyle_job_to_xml(params)
+ if params.has_key?(:template)
+ template_name = params[:template]
+ raise "Job template '#{template_name}' can't be resolved." unless @job_templates.has_key?(template_name)
+ params.delete(:template)
+ template = @job_templates[template_name]
+ puts "Template found: #{template}"
+ params = template.deep_merge(params)
+ puts "Template merged: #{template}"
+ end
+
+ xml = @client.job.build_freestyle_config(params)
+ n_xml = Nokogiri::XML(xml)
+
+ @module_registry.traverse_registry_path('job', params, n_xml)
+
+ n_xml.to_xml
+ end
+
+ def add_job_dsl(job, xml)
+ n_xml = Nokogiri::XML(xml)
+ n_xml.root.name = 'com.cloudbees.plugins.flow.BuildFlow'
+ Nokogiri::XML::Builder.with(n_xml.root) do |xml|
+ xml.dsl job[:build_flow]
+ end
+ n_xml.to_xml
+ end
+
+ # TODO: make sure this is tested
+ def update_job_dsl(job, xml)
+ n_xml = Nokogiri::XML(xml)
+ n_builders = n_xml.xpath('//builders').first
+ Nokogiri::XML::Builder.with(n_builders) do |xml|
+ build_job_dsl(job, xml)
+ end
+ n_xml.to_xml
+ end
+
+ def generate_job_dsl_body(params)
+ @logger.info "Generating pipeline"
+
+ xml = @client.job.build_freestyle_config(params)
+
+ n_xml = Nokogiri::XML(xml)
+ if n_xml.xpath('//javaposse.jobdsl.plugin.ExecuteDslScripts').empty?
+ p_xml = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |b_xml|
+ build_job_dsl(params, b_xml)
+ end
+
+ n_xml.xpath('//builders').first.add_child("\r\n" + p_xml.doc.root.to_xml(:indent => 4) + "\r\n")
+ xml = n_xml.to_xml
+ end
+ xml
+ end
+
+ def build_job_dsl(job, xml)
+ xml.send('javaposse.jobdsl.plugin.ExecuteDslScripts') {
+ if job.has_key?(:job_dsl)
+ xml.scriptText job[:job_dsl]
+ xml.usingScriptText true
+ else
+ xml.targets job[:job_dsl_targets]
+ xml.usingScriptText false
+ end
+ xml.ignoreExisting false
+ xml.removedJobAction 'IGNORE'
+ }
+ end
+ end
+end
diff --git a/lib/jenksin_pipeline_builder/job_builder.rb b/lib/jenksin_pipeline_builder/job_builder.rb
new file mode 100644
index 0000000..2364860
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/job_builder.rb
@@ -0,0 +1,82 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class JobBuilder
+ def self.change_description(description, n_xml)
+ desc = n_xml.xpath("//description").first
+ desc.content = "#{description}"
+ end
+
+ def self.apply_scm_params(params, n_xml)
+ XmlHelper.update_node_text(n_xml, '//scm/localBranch', params[:local_branch]) if params[:local_branch]
+ XmlHelper.update_node_text(n_xml, '//scm/recursiveSubmodules', params[:recursive_update]) if params[:recursive_update]
+ XmlHelper.update_node_text(n_xml, '//scm/wipeOutWorkspace', params[:wipe_workspace]) if params[:wipe_workspace]
+ XmlHelper.update_node_text(n_xml, '//scm/excludedUsers', params[:excuded_users]) if params[:excuded_users]
+ end
+
+ def self.hipchat_notifier(params, n_xml)
+ raise "No HipChat room specified" unless params[:room]
+
+ properties = n_xml.xpath("//properties").first
+ Nokogiri::XML::Builder.with(properties) do |xml|
+ xml.send('jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty') {
+ xml.room params[:room]
+ xml.startNotification params[:'start-notify'] || false
+ }
+ end
+ end
+
+ def self.build_parameters(params, n_xml)
+ n_builders = n_xml.xpath('//properties').first
+ Nokogiri::XML::Builder.with(n_builders) do |xml|
+ xml.send('hudson.model.ParametersDefinitionProperty') {
+ xml.parameterDefinitions {
+ param_proc = lambda do |xml, name, type, default, description|
+ xml.send(type) {
+ xml.name name
+ xml.description description
+ xml.defaultValue default
+ }
+ end
+ params.each do |param|
+ case param[:type]
+ when 'string'
+ paramType = 'hudson.model.StringParameterDefinition'
+ when 'bool'
+ paramType = 'hudson.model.BooleanParameterDefinition'
+ when 'text'
+ paramType = 'hudson.model.TextParameterDefinition'
+ when 'password'
+ paramType = 'hudson.model.PasswordParameterDefinition'
+ else
+ paramType = 'hudson.model.StringParameterDefinition'
+ end
+
+ param_proc.call xml, param[:name], paramType, param[:default], param[:description]
+ end
+ }
+ }
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/module_registry.rb b/lib/jenksin_pipeline_builder/module_registry.rb
new file mode 100644
index 0000000..6190a5d
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/module_registry.rb
@@ -0,0 +1,82 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class ModuleRegistry
+ attr_accessor :registry
+
+ def initialize(registry)
+ @registry = registry
+ end
+
+ def get(path)
+ parts = path.split('/')
+ self.get_by_path_collection(parts, @registry)
+ end
+
+ def get_by_path_collection(path, registry)
+ item = registry[path.shift.to_sym]
+ if path.count == 0
+ return item
+ end
+
+ get_by_path_collection(path, item)
+ end
+
+ def traverse_registry_path(path, params, n_xml)
+ registry = get(path)
+ traverse_registry(registry, params, n_xml)
+ end
+
+ def traverse_registry(registry, params, n_xml)
+ params.each do |key, value|
+ execute_registry_item(registry, key, value, n_xml)
+ end
+ end
+
+ def traverse_registry_param_array(registry, params, n_xml)
+ params.each do |item|
+ key = item.keys.first
+ next if key.nil?
+ execute_registry_item(registry, key, item[key], n_xml)
+ end
+ end
+
+ def execute_registry_item(registry, key, value, n_xml)
+ registry_item = registry[key]
+ if registry_item.kind_of?(Hash)
+ sub_registry = registry_item[:registry]
+ method = registry_item[:method]
+ method.call(sub_registry, value, n_xml)
+ elsif registry_item.kind_of?(Method)
+ registry_item.call(value, n_xml) unless registry_item.nil?
+ end
+ end
+
+ def run_registry_on_path(path, registry, params, n_xml)
+ n_builders = n_xml.xpath(path).first
+ Nokogiri::XML::Builder.with(n_builders) do |xml|
+ traverse_registry_param_array(registry, params, xml)
+ end
+ end
+ end
+end
diff --git a/lib/jenksin_pipeline_builder/publishers.rb b/lib/jenksin_pipeline_builder/publishers.rb
new file mode 100644
index 0000000..af2c3d1
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/publishers.rb
@@ -0,0 +1,113 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class Publishers
+ def self.description_setter(params, xml)
+ xml.send('hudson.plugins.descriptionsetter.DescriptionSetterPublisher') {
+ xml.regexp params[:regexp]
+ xml.regexpForFailed params[:regexp]
+ xml.description params[:description]
+ xml.descriptionForFailed params[:description]
+ xml.setForMatrix false
+ }
+ end
+
+ def self.push_to_projects(params, xml)
+ xml.send('hudson.plugins.parameterizedtrigger.BuildTrigger') {
+ xml.configs {
+ xml.send('hudson.plugins.parameterizedtrigger.BuildTriggerConfig') {
+ xml.configs {
+ params[:data] = [ { params: "" } ] unless params[:data]
+ params[:data].each do |config|
+ if config[:params]
+ xml.send('hudson.plugins.parameterizedtrigger.PredefinedBuildParameters') {
+ xml.properties config[:params]
+ }
+ end
+ if config[:file]
+ xml.send('hudson.plugins.parameterizedtrigger.FileBuildParameters') {
+ xml.propertiesFile config[:file]
+ xml.failTriggerOnMissing false
+ }
+ end
+ end
+ }
+ xml.projects params[:project]
+ xml.condition 'SUCCESS'
+ xml.triggerWithNoParameters false
+ }
+ }
+ }
+ end
+
+ def self.push_to_hipchat(params, xml)
+ params = {} if params == true
+ xml.send('jenkins.plugins.hipchat.HipChatNotifier') {
+ xml.jenkinsUrl params[:jenkinsUrl] || ''
+ xml.authToken params[:authToken] || ''
+ xml.room params[:room] || ''
+ }
+ end
+
+ def self.push_to_git(params, xml)
+ xml.send('hudson.plugins.git.GitPublisher') {
+ xml.configVersion params[:configVersion] || 2
+ xml.pushMerge params[:'push-merge'] || false
+ xml.pushOnlyIfSuccess params[:'push-only-if-success'] || false
+ xml.branchesToPush {
+ xml.send('hudson.plugins.git.GitPublisher_-BranchToPush') {
+ xml.targetRepoName params[:targetRepoName] || 'origin'
+ xml.branchName params[:branchName] || 'master'
+ }
+ }
+ }
+ end
+
+ def self.publish_junit(params, xml)
+ xml.send('hudson.tasks.junit.JUnitResultArchiver') {
+ xml.testResults params[:test_results] || ''
+ xml.keepLongStdio false
+ xml.testDataPublishers
+ }
+ end
+
+ def self.coverage_metric(name, params, xml)
+ xml.send('hudson.plugins.rubyMetrics.rcov.model.MetricTarget') {
+ xml.metric name
+ xml.healthy params[:healthy]
+ xml.unhealthy params[:unhealthy]
+ xml.unstable params[:unstable]
+ }
+ end
+
+ def self.publish_rcov(params, xml)
+ xml.send('hudson.plugins.rubyMetrics.rcov.RcovPublisher') {
+ xml.reportDir params[:report_dir]
+ xml.targets {
+ coverage_metric('TOTAL_COVERAGE', params[:total], xml)
+ coverage_metric('CODE_COVERAGE', params[:code], xml)
+ }
+ }
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/triggers.rb b/lib/jenksin_pipeline_builder/triggers.rb
new file mode 100644
index 0000000..e8be968
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/triggers.rb
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class Triggers
+ def self.enable_git_push(git_push, xml)
+ xml.send('com.cloudbees.jenkins.GitHubPushTrigger') {
+ xml.spec
+ }
+ end
+
+ def self.enable_scm_polling(scm_polling, xml)
+ xml.send('hudson.triggers.SCMTrigger') {
+ xml.spec scm_polling
+ xml.ignorePostCommitHooks false
+ }
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/utils.rb b/lib/jenksin_pipeline_builder/utils.rb
new file mode 100644
index 0000000..52661d6
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/utils.rb
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class ::Hash
+ def deep_merge(second)
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
+ self.merge(second, &merger)
+ end
+ end
+
+ class Utils
+ # Code was duplicated from jeknins_api_client
+ def self.symbolize_keys_deep!(h)
+ return unless h.kind_of?(Hash)
+ h.keys.each do |k|
+ ks = k.respond_to?(:to_sym) ? k.to_sym : k
+ h[ks] = h.delete k # Preserve order even when k == ks
+ symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
+ if h[ks].kind_of? Array
+ #puts "Array #{h[ks]}"
+ h[ks].each { |item| symbolize_keys_deep!(item) }
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/version.rb b/lib/jenksin_pipeline_builder/version.rb
new file mode 100644
index 0000000..ac6a2fd
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/version.rb
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ VERSION = "0.2.0"
+end
diff --git a/lib/jenksin_pipeline_builder/view.rb b/lib/jenksin_pipeline_builder/view.rb
new file mode 100644
index 0000000..25d951b
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/view.rb
@@ -0,0 +1,259 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class View
+ # Initializes a new View object.
+ #
+ # @param generator [Generator] the client object
+ #
+ # @return [View] the view object
+ #
+ def initialize(generator)
+ @generator = generator
+ @client = generator.client
+ @logger = @client.logger
+ end
+
+ def generate(path)
+ yaml = YAML.load_file(path)
+
+ yaml.each do |item|
+ Utils.symbolize_keys_deep!(item)
+ create(item[:view]) if item[:view]
+ end
+ end
+
+ def get_mode(type)
+ case type
+ when 'listview'
+ 'hudson.model.ListView'
+ when 'myview'
+ 'hudson.model.MyView'
+ when 'nestedView'
+ 'hudson.plugins.nested_view.NestedView'
+ when 'categorizedView'
+ 'org.jenkinsci.plugins.categorizedview.CategorizedJobsView'
+ when 'dashboardView'
+ 'hudson.plugins.view.dashboard.Dashboard'
+ when 'multijobView'
+ 'com.tikal.jenkins.plugins.multijob.views.MultiJobView'
+ else
+ raise "Type #{type} is not supported by Jenkins."
+ end
+ end
+
+ # Creates a new empty view of the given type
+ #
+ # @param [String] view_name Name of the view to be created
+ # @param [String] type Type of view to be created. Valid options:
+ # listview, myview. Default: listview
+ #
+ def create_base_view(view_name, type = 'listview', parent_view_name = nil)
+ @logger.info "Creating a view '#{view_name}' of type '#{type}'"
+ mode = get_mode(type)
+ initial_post_params = {
+ 'name' => view_name,
+ 'mode' => mode,
+ 'json' => {
+ 'name' => view_name,
+ 'mode' => mode
+ }.to_json
+ }
+
+ if @generator.debug
+ pp initial_post_params
+ return
+ end
+
+ view_path = parent_view_name.nil? ? '' : "/view/#{path_encode parent_view_name}"
+ view_path += '/createView'
+
+ @client.api_post_request(view_path, initial_post_params)
+ end
+
+ # Creates a listview by accepting the given parameters hash
+ #
+ # @param [Hash] params options to create the new view
+ # @option params [String] :name Name of the view
+ # @option params [String] :type Description of the view
+ # @option params [String] :description Description of the view
+ # @option params [String] :status_filter Filter jobs based on the status.
+ # Valid options: all_selected_jobs, enabled_jobs_only,
+ # disabled_jobs_only. Default: all_selected_jobs
+ # @option params [Boolean] :filter_queue true or false
+ # @option params [Boolean] :filter_executors true or false
+ # @option params [String] :regex Regular expression to filter jobs that
+ # are to be added to the view
+ #
+ # @raise [ArgumentError] if the required parameter +:name+ is not
+ # specified
+ #
+ def create(params)
+ # Name is a required parameter. Raise an error if not specified
+ raise ArgumentError, "Name is required for creating view" \
+ unless params.is_a?(Hash) && params[:name]
+ unless @generator.debug
+ if @client.view.exists?(params[:name])
+ @client.view.delete(params[:name])
+ end
+ end
+ params[:type] = 'listview' unless params[:type]
+ create_base_view(params[:name], params[:type], params[:parent_view])
+ @logger.debug "Creating a #{params[:type]} view with params: #{params.inspect}"
+ status_filter = case params[:status_filter]
+ when "all_selected_jobs"
+ ""
+ when "enabled_jobs_only"
+ "1"
+ when "disabled_jobs_only"
+ "2"
+ else
+ ""
+ end
+
+ json = {
+ "name" => params[:name],
+ "description" => params[:description],
+ "mode" => get_mode(params[:type]),
+ "statusFilter" => "",
+ "columns" => get_columns(params[:type])
+ }
+ json.merge!("groupingRules" => params[:groupingRules]) if params[:groupingRules]
+ post_params = {
+ "name" => params[:name],
+ "mode" => get_mode(params[:type]),
+ "description" => params[:description],
+ "statusFilter" => status_filter,
+ "json" => json.to_json
+ }
+ post_params.merge!("filterQueue" => "on") if params[:filter_queue]
+ post_params.merge!("filterExecutors" => "on") if params[:filter_executors]
+ post_params.merge!("useincluderegex" => "on",
+ "includeRegex" => params[:regex]) if params[:regex]
+
+ if @generator.debug
+ pp post_params
+ return
+ end
+
+ view_path = params[:parent_view].nil? ? '' : "/view/#{path_encode params[:parent_view]}"
+ view_path += "/view/#{path_encode params[:name]}/configSubmit"
+
+ @client.api_post_request(view_path, post_params)
+ end
+
+ def get_columns(type)
+ columns_repository = {
+ 'Status' =>
+ {
+ 'stapler-class' => 'hudson.views.StatusColumn',
+ 'kind' => 'hudson.views.StatusColumn'
+ },
+ 'Weather' =>
+ {
+ "stapler-class" => "hudson.views.WeatherColumn",
+ "kind" => "hudson.views.WeatherColumn"
+ },
+ 'Name' =>
+ {
+ "stapler-class" => "hudson.views.JobColumn",
+ "kind" => "hudson.views.JobColumn"
+ },
+ 'Last Success' =>
+ {
+ "stapler-class" => "hudson.views.LastSuccessColumn",
+ "kind" => "hudson.views.LastSuccessColumn"
+ },
+ 'Last Failure' =>
+ {
+ "stapler-class" => "hudson.views.LastFailureColumn",
+ "kind" => "hudson.views.LastFailureColumn"
+ },
+ 'Last Duration' =>
+ {
+ "stapler-class" => "hudson.views.LastDurationColumn",
+ "kind" => "hudson.views.LastDurationColumn"
+ },
+ 'Build Button' =>
+ {
+ 'stapler-class' => 'hudson.views.BuildButtonColumn',
+ 'kind' => 'hudson.views.BuildButtonColumn'
+ },
+ 'Categorized - Job' =>
+ {
+ 'stapler-class' => 'org.jenkinsci.plugins.categorizedview.IndentedJobColumn',
+ 'kind' => 'org.jenkinsci.plugins.categorizedview.IndentedJobColumn'
+ }
+ }
+
+ column_names = case type
+ when 'categorizedView'
+ ['Status', 'Weather', 'Categorized - Job', 'Last Success', 'Last Failure', 'Last Duration', 'Build Button']
+ else
+ ['Status', 'Weather', 'Name', 'Last Success', 'Last Failure', 'Last Duration', 'Build Button']
+ end
+
+ result = []
+ column_names.each do |name|
+ result << columns_repository[name]
+ end
+ return result
+ end
+
+ def path_encode(path)
+ URI.escape(path.encode(Encoding::UTF_8))
+ end
+
+ # This method lists all views
+ #
+ # @param [String] parent_view a name of the parent view
+ # @param [String] filter a regex to filter view names
+ # @param [Bool] ignorecase whether to be case sensitive or not
+ #
+ def list_children(parent_view = nil, filter = "", ignorecase = true)
+ @logger.info "Obtaining children views of parent #{parent_view} based on filter '#{filter}'"
+ view_names = []
+ path = parent_view.nil? ? '' : "/view/#{path_encode parent_view}"
+ response_json = @client.api_get_request(path)
+ response_json["views"].each { |view|
+ if ignorecase
+ view_names << view["name"] if view["name"] =~ /#{filter}/i
+ else
+ view_names << view["name"] if view["name"] =~ /#{filter}/
+ end
+ }
+ view_names
+ end
+
+ # Delete a view
+ #
+ # @param [String] view_name
+ #
+ def delete(view_name, parent_view = nil)
+ @logger.info "Deleting view '#{view_name}'"
+ path = parent_view.nil? ? '' : "/view/#{path_encode parent_view}"
+ path += "/view/#{path_encode view_name}/doDelete"
+ @client.api_post_request(path)
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/wrappers.rb b/lib/jenksin_pipeline_builder/wrappers.rb
new file mode 100644
index 0000000..0fa3d94
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/wrappers.rb
@@ -0,0 +1,91 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class Wrappers
+ def self.ansicolor(wrapper, xml)
+ xml.send('hudson.plugins.ansicolor.AnsiColorBuildWrapper') {
+ xml.colorMapName 'xterm'
+ }
+ end
+
+ def self.console_timestamp(wrapper, xml)
+ xml.send('hudson.plugins.timestamper.TimestamperBuildWrapper')
+ end
+
+ def self.run_with_rvm(wrapper, xml)
+ xml.send('ruby-proxy-object') {
+ xml.send('ruby-object', 'ruby-class' => 'Jenkins::Plugin::Proxies::BuildWrapper', 'pluginid' => 'rvm') {
+ xml.object('ruby-class' => 'RvmWrapper', 'pluginid' => 'rvm') {
+ xml.impl('pluginid' => "rvm", 'ruby-class' => 'String') { xml.text wrapper }
+ }
+ xml.pluginid(:pluginid => 'rvm', 'ruby-class' => 'String') { xml.text 'rvm' }
+ }
+ }
+ end
+
+ def self.inject_passwords(passwords, xml)
+ xml.EnvInjectPasswordWrapper {
+ xml.injectGlobalPasswords false
+ xml.passwordEntries {
+ passwords.each do |password|
+ xml.EnvInjectPasswordEntry {
+ xml.name password[:name]
+ xml.value password[:value]
+ }
+ end
+ }
+ }
+ end
+
+ def self.inject_env_vars(wrapper, xml)
+ xml.EnvInjectBuildWrapper {
+ xml.info {
+ xml.propertiesContent wrapper
+ xml.loadFilesFromMaster false
+ }
+ }
+ end
+
+ def self.publish_to_artifactory(wrapper, xml)
+ xml.send('org.jfrog.hudson.generic.ArtifactoryGenericConfigurator') {
+ xml.details {
+ xml.artifactoryUrl wrapper[:url]
+ xml.artifactoryName wrapper[:'artifactory-name']
+ xml.repositoryKey wrapper[:'target-repo']
+ xml.snapshotsRepositoryKey wrapper[:'target-repo']
+ }
+ xml.deployPattern wrapper[:publish]
+ xml.resolvePattern
+ xml.matrixParams
+ xml.deployBuildInfo wrapper[:'publish-build-info']
+ xml.includeEnvVars false
+ xml.envVarsPatterns {
+ xml.includePatterns
+ xml.excludePatterns '*password*,*secret*'
+ }
+ xml.discardOldBuilds false
+ xml.discardBuildArtifacts true
+ }
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/jenksin_pipeline_builder/xml_helper.rb b/lib/jenksin_pipeline_builder/xml_helper.rb
new file mode 100644
index 0000000..6106ec5
--- /dev/null
+++ b/lib/jenksin_pipeline_builder/xml_helper.rb
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2014 Igor Moochnick
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+module JenkinsPipelineBuilder
+ class XmlHelper
+ def self.update_node_text(n_xml, path, value)
+ n_node = n_xml.xpath(path).first
+ if n_node.nil?
+ left, right = path.match(/^(.*)\/([^\/]*)$/).captures
+ parent_node = n_xml.xpath(left).first
+ Nokogiri::XML::Builder.with(parent_node) do |xml|
+ xml.send(right) {
+ xml.text value
+ }
+ end
+ else
+ n_node.content = value
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/func_tests/spec_helper.rb b/spec/func_tests/spec_helper.rb
new file mode 100644
index 0000000..02b513d
--- /dev/null
+++ b/spec/func_tests/spec_helper.rb
@@ -0,0 +1,18 @@
+require 'logger'
+require 'rspec'
+
+require 'simplecov'
+require 'simplecov-rcov'
+
+SimpleCov.start if ENV["COVERAGE"]
+
+require File.expand_path('../../../lib/jenksin_pipeline_builder', __FILE__)
+
+RSpec.configure do |config|
+ config.treat_symbols_as_metadata_keys_with_true_values = true
+ config.run_all_when_everything_filtered = true
+ config.filter_run_excluding :broken => true
+
+ config.before(:each) do
+ end
+end
diff --git a/spec/func_tests/view_spec.rb b/spec/func_tests/view_spec.rb
new file mode 100644
index 0000000..6606319
--- /dev/null
+++ b/spec/func_tests/view_spec.rb
@@ -0,0 +1,93 @@
+require File.expand_path('../spec_helper', __FILE__)
+
+describe JenkinsPipelineBuilder::View do
+ context "With properly initialized client" do
+ before(:all) do
+ @creds_file = '~/.jenkins_api_client/login.yml'
+ @valid_post_responses = [200, 201, 302]
+ begin
+ @client = JenkinsApi::Client.new(
+ YAML.load_file(File.expand_path(@creds_file, __FILE__))
+ )
+ @client.logger.level = Logger::DEBUG
+ @generator = JenkinsPipelineBuilder::Generator.new(nil, @client)
+ @generator.no_files = true
+ rescue Exception => e
+ puts 'WARNING: Credentials are not set properly.'
+ puts e.message
+ end
+ end
+
+ describe 'InstanceMethods' do
+ describe '#create' do
+ def create_and_validate(params)
+ name = params[:name]
+ @valid_post_responses.should include(
+ @generator.view.create(params).to_i
+ )
+ @generator.view.list_children(params[:parent_view], name).include?(name).should be_true
+ end
+
+ def destroy_and_validate(params)
+ name = params[:name]
+ @valid_post_responses.should include(
+ @generator.view.delete(name, params[:parent_view]).to_i
+ )
+ @generator.view.list_children(params[:parent_view], name).include?(name).should be_false
+ end
+
+ def test_and_validate(params)
+ create_and_validate(params)
+ destroy_and_validate(params)
+ end
+
+ it 'accepts the name of the view and creates the view' do
+ params = {
+ :name => 'test_list_view'
+ }
+
+ test_and_validate(params)
+ end
+
+ it 'creates a Nested view with a child' do
+ params_parent = {
+ name: 'test_nested_view',
+ type: 'nestedView'
+ }
+
+ create_and_validate(params_parent)
+
+ params_child = {
+ name: 'test_list_view',
+ parent_view: params_parent[:name]
+ }
+
+ test_and_validate(params_child)
+
+ destroy_and_validate(params_parent)
+ end
+
+ it 'creates a categorized view with columns' do
+ params = {
+ name: 'test_category_view',
+ type: 'categorizedView',
+ description: 'Blah blah',
+ regex: 'Job-.*',
+ groupingRules: [{
+ groupRegex: 'Step-1.*',
+ namingRule: '1. Commit'
+ },{
+ groupRegex: 'Step-2.*',
+ namingRule: '2. Acceptance'
+ },{
+ groupRegex: 'Step-3.*',
+ namingRule: '3. Release'
+ }]
+ }
+
+ test_and_validate(params)
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/unit_tests/compiler_spec.rb b/spec/unit_tests/compiler_spec.rb
new file mode 100644
index 0000000..d303475
--- /dev/null
+++ b/spec/unit_tests/compiler_spec.rb
@@ -0,0 +1,19 @@
+require File.expand_path('../spec_helper', __FILE__)
+
+describe 'Compiler' do
+ it 'transforms hash into hash' do
+ hash = {
+ a: 'A sentence',
+ b: 'B sentence',
+ hash: {
+ c: 5,
+ d: true
+ },
+ z: false
+ }
+
+ result = JenkinsPipelineBuilder::Compiler.compile(hash)
+
+ result.should == hash
+ end
+end
\ No newline at end of file
diff --git a/spec/unit_tests/fixtures/files/Job-Build-Flow.xml b/spec/unit_tests/fixtures/files/Job-Build-Flow.xml
new file mode 100644
index 0000000..4bcecd9
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Build-Flow.xml
@@ -0,0 +1,57 @@
+
+
+
+
+ false
+
+
+ HipChat Room Name
+ true
+
+
+
+
+ param1
+
+
+
+
+
+
+
+ true
+ false
+ false
+ false
+
+ false
+
+
+
+
+
+
+
+
+
+
+ project_name
+ SUCCESS
+ false
+
+
+
+
+ https://jenkins_url/
+ auth_token
+ room name
+
+
+
+ guard {
+ build("job_name1", param1: params["param1"]);
+ } rescue {
+ build("job_name2", param1: build21.environment.get("some_var"))
+ }
+
+
diff --git a/spec/unit_tests/fixtures/files/Job-Build-Flow.yaml b/spec/unit_tests/fixtures/files/Job-Build-Flow.yaml
new file mode 100644
index 0000000..38e2164
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Build-Flow.yaml
@@ -0,0 +1,22 @@
+---
+- job:
+ name: Job-Build-Flow
+ job_type: build_flow
+ hipchat:
+ room: 'HipChat Room Name'
+ start-notify: true
+ parameters:
+ - name: param1
+ publishers:
+ - downstream:
+ project: project_name
+ - hipchat:
+ jenkinsUrl: 'https://jenkins_url/'
+ authToken: 'auth_token'
+ room: 'room name'
+ build_flow: |
+ guard {
+ build("job_name1", param1: params["param1"]);
+ } rescue {
+ build("job_name2", param1: build21.environment.get("some_var"))
+ }
diff --git a/spec/unit_tests/fixtures/files/Job-Build-Maven.xml b/spec/unit_tests/fixtures/files/Job-Build-Maven.xml
new file mode 100644
index 0000000..008de17
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Build-Maven.xml
@@ -0,0 +1,90 @@
+
+
+
+
+ false
+
+
+ 2
+
+
+
+
+ git@github.com:devops/repo.git
+
+
+
+
+ release
+
+
+ release
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+
+ Default
+
+
+
+
+ user
+
+
+ false
+
+
+
+ true
+ false
+ false
+ false
+
+ false
+
+
+ echo 'Doing some work'
+ run command1
+
+
+
+ tools-maven-3.0.3
+
+ -B clean
+
+
+
+
+
+ See the QE build details at (.*)
+ See the QE build details at (.*)
+ QE Build Details: <a href="\1">\1</a>
+ QE Build Details: <a href="\1">\1</a>
+ false
+
+
+
+
+
+
+ VM_NAME=${VM_NAME}
+
+
+ project_name
+ SUCCESS
+ false
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spec/unit_tests/fixtures/files/Job-Build-Maven.yaml b/spec/unit_tests/fixtures/files/Job-Build-Maven.yaml
new file mode 100644
index 0000000..9f0001e
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Build-Maven.yaml
@@ -0,0 +1,26 @@
+---
+- job:
+ name: Job-Build-Maven
+ scm_provider: git
+ scm_url: git@github.com:devops/repo.git
+ scm_branch: release
+ scm_params:
+ local_branch: release
+ wipe_workspace: true
+ excuded_users: user
+ builders:
+ - shell_command: |
+ echo 'Doing some work'
+ run command1
+ - maven3:
+ goals: -B clean
+ publishers:
+ - description_setter:
+ regexp: See the QE build details at (.*)
+ description: 'QE Build Details: \1'
+ - downstream:
+ project: project_name
+ data:
+ - params: VM_NAME=${VM_NAME}
+ wrappers:
+ - timestamp: true
diff --git a/spec/unit_tests/fixtures/files/Job-DSL.yaml b/spec/unit_tests/fixtures/files/Job-DSL.yaml
new file mode 100644
index 0000000..dff2af2
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-DSL.yaml
@@ -0,0 +1,14 @@
+---
+- job:
+ name: Job-DSL1
+ job_type: job_dsl
+ job_dsl: |
+ job {
+ name 'DSL-Job'
+ }
+
+- job:
+ name: Job-DSL2
+ job_type: job_dsl
+ job_dsl_targets: |
+ **/*.groovy
diff --git a/spec/unit_tests/fixtures/files/Job-DSL1.xml b/spec/unit_tests/fixtures/files/Job-DSL1.xml
new file mode 100644
index 0000000..fde70f2
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-DSL1.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ false
+
+
+ true
+ false
+ false
+ false
+
+ false
+
+
+ job {
+ name 'DSL-Job'
+ }
+
+ true
+ false
+ IGNORE
+
+
+
+
+
diff --git a/spec/unit_tests/fixtures/files/Job-DSL2.xml b/spec/unit_tests/fixtures/files/Job-DSL2.xml
new file mode 100644
index 0000000..59ee7f0
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-DSL2.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ false
+
+
+ true
+ false
+ false
+ false
+
+ false
+
+
+ **/*.groovy
+
+ false
+ false
+ IGNORE
+
+
+
+
+
diff --git a/spec/unit_tests/fixtures/files/Job-Gem-Build.xml b/spec/unit_tests/fixtures/files/Job-Gem-Build.xml
new file mode 100644
index 0000000..26f8536
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Gem-Build.xml
@@ -0,0 +1,142 @@
+
+
+
+
+ false
+
+
+ 2
+
+
+
+
+ git@github.com:devops/repo.git
+
+
+
+
+ master
+
+
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+
+ Default
+
+
+
+
+ user
+
+
+ false
+
+
+
+ true
+ false
+ false
+ false
+
+
+ H/5 * * * *
+ false
+
+
+ false
+
+
+ run.sh
+
+
+
+
+ out/**/*.xml
+ false
+
+
+
+ 2
+ true
+ false
+
+
+ origin
+ master
+
+
+
+
+ out/coverage/rcov
+
+
+ TOTAL_COVERAGE
+ 80
+ 0
+ 0
+
+
+ CODE_COVERAGE
+ 80
+ 0
+ 0
+
+
+
+
+
+
+ xterm
+
+
+
+ https://artifactory.com/artifactory
+ key
+ gems-local
+ gems-local
+
+ pkg/*.gem
+
+
+ true
+ false
+
+
+ *password*,*secret*
+
+ false
+ true
+
+
+
+ VAR1 = value_1
+ false
+
+
+
+ false
+
+
+ OS_PASSWORD
+ some_encrypted_password
+
+
+
+
+
+
+ rvm
+
+
+
+
\ No newline at end of file
diff --git a/spec/unit_tests/fixtures/files/Job-Gem-Build.yaml b/spec/unit_tests/fixtures/files/Job-Gem-Build.yaml
new file mode 100644
index 0000000..fd8846e
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Gem-Build.yaml
@@ -0,0 +1,41 @@
+- job:
+ name: 'Job-Gem-Build'
+ job_type: free_style
+ scm_provider: git
+ scm_url: git@github.com:devops/repo.git
+ scm_branch: master
+ scm_params:
+ excuded_users: user
+ shell_command: 'run.sh'
+ triggers:
+ - scm_polling: 'H/5 * * * *'
+ wrappers:
+ - ansicolor: true
+ - artifactory:
+ url: 'https://artifactory.com/artifactory'
+ artifactory-name: 'key'
+ target-repo: gems-local
+ publish: 'pkg/*.gem'
+ publish-build-info: true
+ - inject_env_var: >
+ VAR1 = value_1
+ - inject_passwords:
+ - name: OS_PASSWORD
+ value: some_encrypted_password
+ - rvm: "`cat .ruby-version`@`cat .ruby-gemset`"
+ publishers:
+ - junit_result:
+ test_results: 'out/**/*.xml'
+ - git:
+ push-merge: true
+ push-only-if-success: false
+ - coverage_result:
+ report_dir: out/coverage/rcov
+ total:
+ healthy: 80
+ unhealthy: 0
+ unstable: 0
+ code:
+ healthy: 80
+ unhealthy: 0
+ unstable: 0
diff --git a/spec/unit_tests/fixtures/files/Job-Generate-From-Template.xml b/spec/unit_tests/fixtures/files/Job-Generate-From-Template.xml
new file mode 100644
index 0000000..c9205e4
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Generate-From-Template.xml
@@ -0,0 +1,32 @@
+
+
+
+ Do not edit this job through the web!
+ false
+
+
+
+
+ param1
+ Some description
+
+
+
+
+
+
+ true
+ false
+ false
+ false
+
+ false
+
+
+ run.sh
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spec/unit_tests/fixtures/files/Job-Generate-From-Template.yaml b/spec/unit_tests/fixtures/files/Job-Generate-From-Template.yaml
new file mode 100644
index 0000000..44ab4cd
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Generate-From-Template.yaml
@@ -0,0 +1,8 @@
+---
+- job-template:
+ name: Job-{{name}}-From-Template
+ description: '{{description}}'
+ parameters:
+ - name: param1
+ description: Some description
+ shell_command: '{{command}}'
diff --git a/spec/unit_tests/fixtures/files/Job-Multi-Project.xml b/spec/unit_tests/fixtures/files/Job-Multi-Project.xml
new file mode 100644
index 0000000..0b713f9
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Multi-Project.xml
@@ -0,0 +1,117 @@
+
+
+
+
+ false
+
+
+
+
+ Param1
+ The git branch to be used when building application
+ release
+
+
+
+
+
+ 2
+
+
+
+
+ git@github:org/repo
+
+
+
+
+ release
+
+
+ false
+ true
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+
+ Default
+
+
+
+
+ user
+
+
+ false
+
+
+ ${GIT_BRANCH}
+
+ true
+ false
+ false
+ false
+
+
+
+
+
+ H/5 * * * *
+ false
+
+
+ false
+
+
+ Child Jobs
+
+
+ Child-Job
+ false
+ false
+
+
+ PARENT_WORKSPACE=${WORKSPACE}
+
+
+
+
+ SUCCESSFUL
+
+
+
+ build_job_info
+
+
+
+
+
+
+
+
+
+ PARAM1=value1
+ PARAM2=value2
+
+
+
+ promote-job-params
+ false
+
+
+ Downstream-Job
+ SUCCESS
+ false
+
+
+
+
+
+
+
+
diff --git a/spec/unit_tests/fixtures/files/Job-Multi-Project.yaml b/spec/unit_tests/fixtures/files/Job-Multi-Project.yaml
new file mode 100644
index 0000000..49dbc93
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/Job-Multi-Project.yaml
@@ -0,0 +1,36 @@
+---
+- job:
+ name: Job-Multi-Project
+ job_type: multi_project
+ parameters:
+ - name: Param1
+ type: string
+ default: release
+ description: The git branch to be used when building application
+ scm_provider: git
+ scm_url: git@github:org/repo
+ scm_branch: release
+ scm_params:
+ local_branch: ${GIT_BRANCH}
+ recursive_update: true
+ wipe_workspace: true
+ excuded_users: user
+ triggers:
+ - git_push: true
+ - scm_polling: "H/5 * * * *"
+ builders:
+ - job_builder:
+ child_jobs:
+ - Child-Job
+ mark_phase: SUCCESSFUL
+ - inject_vars_file: build_job_info
+ publishers:
+ - downstream:
+ project: Downstream-Job
+ data:
+ - params: |
+ PARAM1=value1
+ PARAM2=value2
+ - file: promote-job-params
+ wrappers:
+ - timestamp: true
diff --git a/spec/unit_tests/fixtures/files/project.yaml b/spec/unit_tests/fixtures/files/project.yaml
new file mode 100644
index 0000000..2e5ea5c
--- /dev/null
+++ b/spec/unit_tests/fixtures/files/project.yaml
@@ -0,0 +1,15 @@
+- defaults:
+ name: global
+ description: 'Do not edit this job through the web!'
+ hipchat_room: HipChat Room Name
+ os_username: admin
+ os_priv_key: /home/user/qa-oskey.priv
+ os_password: some_encrypted_password
+
+- project:
+ name: Generate
+ jobs:
+ - Job-{{name}}-From-Template:
+ command: run.sh
+ - Job-DSL1
+ - Job-DSL2
diff --git a/spec/unit_tests/generator_spec.rb b/spec/unit_tests/generator_spec.rb
new file mode 100644
index 0000000..474872c
--- /dev/null
+++ b/spec/unit_tests/generator_spec.rb
@@ -0,0 +1,67 @@
+require File.expand_path('../spec_helper', __FILE__)
+require 'equivalent-xml'
+
+describe 'Test YAML jobs conversion to XML' do
+ context 'Loading YAML files' do
+ before do
+ @client = JenkinsApi::Client.new(
+ :server_ip => '127.0.0.1',
+ :server_port => 8080,
+ :username => 'username',
+ :password => 'password',
+ :log_location => '/dev/null'
+ )
+ @generator = JenkinsPipelineBuilder::Generator.new(nil, @client)
+ @generator.debug = true
+ @generator.no_files = true
+ end
+
+ def compare_jobs(job, path)
+ xml = @generator.compile_job_to_xml(job)
+ doc1 = Nokogiri::XML(xml)
+
+ sample_job_xml = File.read(path + '.xml')
+
+ doc2 = Nokogiri::XML(sample_job_xml)
+
+ doc1.should be_equivalent_to(doc2)
+ end
+
+ [
+ 'Job-Multi-Project',
+ 'Job-Build-Maven',
+ 'Job-Build-Flow',
+ 'Job-Gem-Build'
+ ].each do |file|
+ it "should create expected XML from YAML '#{file}'" do
+ path = File.expand_path('../fixtures/files/' + file, __FILE__)
+
+ @generator.load_collection_from_path path + '.yaml'
+ job_name = @generator.job_collection.keys.first
+ job = @generator.resolve_job_by_name(job_name)
+
+ compare_jobs job, path
+ end
+ end
+
+ it "should create expected XML from YAML collection" do
+ path = File.expand_path('../fixtures/files/', __FILE__)
+
+ @generator.load_collection_from_path(path)
+
+ project_name = @generator.projects.first[:name]
+
+ project = @generator.resolve_project(@generator.get_item(project_name))
+
+ project[:value][:jobs].should_not be_nil
+
+ project[:value][:jobs].each do |i|
+ job = i[:result]
+ job.should_not be_nil
+
+ file_name = File.join(path, job[:name])
+ compare_jobs job, file_name
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/unit_tests/module_registry_spec.rb b/spec/unit_tests/module_registry_spec.rb
new file mode 100644
index 0000000..600aef9
--- /dev/null
+++ b/spec/unit_tests/module_registry_spec.rb
@@ -0,0 +1,19 @@
+require File.expand_path('../spec_helper', __FILE__)
+
+describe 'ModuleRegistry' do
+
+ it 'should return item by a specified path' do
+
+ registry = JenkinsPipelineBuilder::ModuleRegistry.new ({
+ zz: {
+ aa: 'aa',
+ bb: 'bb',
+ cc: {
+ dd: 'dd'
+ }
+ }
+ })
+
+ registry.get('zz/aa').should be == 'aa'
+ end
+end
\ No newline at end of file
diff --git a/spec/unit_tests/resolve_dependencies_spec.rb b/spec/unit_tests/resolve_dependencies_spec.rb
new file mode 100644
index 0000000..693db4b
--- /dev/null
+++ b/spec/unit_tests/resolve_dependencies_spec.rb
@@ -0,0 +1,230 @@
+require File.expand_path('../spec_helper', __FILE__)
+
+describe 'Templates resolver' do
+ before(:each) do
+ @client = JenkinsApi::Client.new(
+ :server_ip => '127.0.0.1',
+ :server_port => 8080,
+ :username => 'username',
+ :password => 'password',
+ :log_location => '/dev/null'
+ )
+ @generator = JenkinsPipelineBuilder::Generator.new(nil, @client)
+ @generator.debug = true
+ @generator.no_files = true
+ end
+
+ describe 'resolving settings bags' do
+ it 'gives a bag when all the variables can be resolved' do
+ str = %{
+- project:
+ name: project-name
+ db: my_own_db_{{else}}
+ }
+ project = YAML.load(str)
+ @generator.load_job_collection project
+
+ #@generator.resolve_item('project-name')
+ settings = JenkinsPipelineBuilder::Compiler.get_settings_bag(@generator.get_item('project-name'), { db: 'blah', else: "bum" })
+ settings.should == { name: "project-name", db: "my_own_db_bum", else: "bum", }
+ end
+
+ it 'returns nil when all the variables cant be resolved' do
+ str = %{
+- project:
+ name: project-name
+ db: my_own_db_{{else}}_{{blah}}
+ }
+ project = YAML.load(str)
+ @generator.load_job_collection project
+
+ #@generator.resolve_item('project-name')
+ settings = JenkinsPipelineBuilder::Compiler.get_settings_bag(@generator.get_item('project-name'), { db: 'blah', else: "bum" })
+ settings.should be_nil
+ end
+ end
+
+ it 'starts with the defaults section for settings bag' do
+ str = %{
+- defaults:
+ name: global
+ description: 'Do not edit this job through the web!'
+- job-template:
+ name: 'foo-bar'
+ description: '{{description}}'
+ builders:
+ - shell: perftest
+- project:
+ name: project-name
+ db: my_own_db
+ jobs:
+ - 'foo-bar'
+ }
+ project = YAML.load(str)
+ @generator.load_job_collection project
+
+ @generator.resolve_project(@generator.get_item('project-name')).should ==
+ {:name=>"project-name",
+ :type=>:project,
+ :value=>
+ {:name=>"project-name",
+ :db=>"my_own_db",
+ :jobs=>
+ [{:"foo-bar"=>{},
+ :result=>
+ {:name=>"foo-bar",
+ :description=>"Do not edit this job through the web!",
+ :builders=>[{:shell=>"perftest"}]}}]},
+ :settings=>
+ {:name=>"project-name",
+ :description=>"Do not edit this job through the web!",
+ :db=>"my_own_db"}}
+ end
+
+ it 'should build project collection from jobs templates' do
+ str = %{
+- job-template:
+ name: '{{name}}-unit-tests'
+ builders:
+ - shell: unittest
+ publishers:
+ - email:
+ recipients: '{{mail-to}}'
+
+- job-template:
+ name: '{{name}}-perf-tests'
+ builders:
+ - shell: perftest
+ publishers:
+ - email:
+ recipients: '{{mail-to}}'
+
+- project:
+ name: project-name
+ db: my_own_db
+ jobs:
+ - '{{name}}-unit-tests':
+ mail-to: developer@nowhere.net
+ - '{{name}}-perf-tests':
+ mail-to: projmanager@nowhere.net
+}
+
+ project = YAML.load(str)
+ @generator.load_job_collection project
+
+ @generator.resolve_project(@generator.get_item('project-name')).should ==
+ {:name=>"project-name",
+ :type=>:project,
+ :value=>
+ {:name=>"project-name",
+ :db=>"my_own_db",
+ :jobs=>
+ [{:"{{name}}-unit-tests"=>{:"mail-to"=>"developer@nowhere.net"},
+ :result=>
+ {:name=>"project-name-unit-tests",
+ :builders=>[{:shell=>"unittest"}],
+ :publishers=>[{:email=>{:recipients=>"developer@nowhere.net"}}]}},
+ {:"{{name}}-perf-tests"=>{:"mail-to"=>"projmanager@nowhere.net"},
+ :result=>
+ {:name=>"project-name-perf-tests",
+ :builders=>[{:shell=>"perftest"}],
+ :publishers=>[{:email=>{:recipients=>"projmanager@nowhere.net"}}]}}]},
+ :settings=>{:name=>"project-name", :db=>"my_own_db"}}
+ end
+
+ it 'should build project collection from jobs and jobs templates' do
+ str = %{
+- job-template:
+ name: '{{name}}-unit-tests'
+ builders:
+ - shell: unittest
+ publishers:
+ - email:
+ recipients: '{{mail-to}}'
+
+- job:
+ name: 'foo-bar'
+ builders:
+ - shell: perftest
+
+- project:
+ name: project-name
+ db: my_own_db
+ jobs:
+ - 'foo-bar'
+ - '{{name}}-unit-tests':
+ mail-to: projmanager@nowhere.net
+}
+
+ project = YAML.load(str)
+ @generator.load_job_collection project
+
+ @generator.resolve_project(@generator.get_item('project-name')).should ==
+ {:name=>"project-name",
+ :type=>:project,
+ :value=>
+ {:name=>"project-name",
+ :db=>"my_own_db",
+ :jobs=>
+ [{:"foo-bar"=>{},
+ :result=>
+ {:name=>"foo-bar",
+ :builders=>[{:shell=>"perftest"}]}},
+ {:"{{name}}-unit-tests"=>{:"mail-to"=>"projmanager@nowhere.net"},
+ :result=>
+ {:name=>"project-name-unit-tests",
+ :builders=>[{:shell=>"unittest"}],
+ :publishers=>[{:email=>{:recipients=>"projmanager@nowhere.net"}}]}}]},
+ :settings=>{:name=>"project-name", :db=>"my_own_db"}}
+ end
+
+
+ describe 'compilation of templates' do
+ it 'compiles String' do
+ JenkinsPipelineBuilder::Compiler.compile('blah', { item1: 'data1'}).should == 'blah'
+ end
+
+ it 'compiles simple Hash' do
+ JenkinsPipelineBuilder::Compiler.compile({ name: 'item-{{item1}}', value: 'item1-data'}, { item1: 'data1'}).should ==
+ { name: 'item-data1', value: 'item1-data'}
+ end
+
+ it 'compiles nested Hash' do
+ JenkinsPipelineBuilder::Compiler.compile({ name: 'item-{{item1}}', value: { house: 'house-{{item1}}'}}, { item1: 'data1'}).should ==
+ { name: 'item-data1', value: { house: 'house-data1'}}
+ end
+
+ it 'compiles complex Hash' do
+ template = {:name=>"{{name}}-unit-tests",
+ :builders=>[{:shell=>"unittest"}],
+ :publishers=>[{:email=>{:recipients=>"{{mail-to}}"}}]}
+ settings = {:name=>"project-name", :db=>"my_own_db", :'mail-to' => 'developer@nowhere.net'}
+
+ JenkinsPipelineBuilder::Compiler.compile(template, settings).should ==
+ {:name=>"project-name-unit-tests",
+ :builders=>[{:shell=>"unittest"}],
+ :publishers=>[{:email=>{:recipients=>"developer@nowhere.net"}}]}
+ end
+ end
+
+ it 'shoult resolve job template into a job' do
+ file = 'project_simple'
+ path = File.expand_path('../fixtures/templates/' + file, __FILE__)
+ project = YAML.load_file(path + '.yaml')
+
+ @generator.load_job_collection project
+
+ @generator.resolve_job_by_name('{{name}}-unit-tests', { name: 'project-name', db: 'my_own_db', :'mail-to' => 'developer@nowhere.net' }).should ==
+ {:name=>"project-name-unit-tests",
+ :builders=>[{:shell=>"unittest"}],
+ :publishers=>[{:email=>{:recipients=>"developer@nowhere.net"}}]}
+ end
+
+ it 'should load from folder' do
+ path = File.expand_path('../fixtures/templates/', __FILE__)
+ @generator.load_collection_from_path(path)
+
+ @generator.job_collection.count.should == 4
+ @generator.projects.count == 1
+ end
+end
\ No newline at end of file
diff --git a/spec/unit_tests/spec_helper.rb b/spec/unit_tests/spec_helper.rb
new file mode 100644
index 0000000..51fdb60
--- /dev/null
+++ b/spec/unit_tests/spec_helper.rb
@@ -0,0 +1,29 @@
+require 'logger'
+require 'rspec'
+
+require 'simplecov'
+require 'simplecov-rcov'
+
+SimpleCov.profiles.define 'spec' do
+ add_group 'jenksin_pipeline_builder', '/lib/'
+ coverage_dir 'out/coverage'
+ formatter SimpleCov::Formatter::MultiFormatter[
+ SimpleCov::Formatter::Console,
+ SimpleCov::Formatter::RcovFormatter,
+ ]
+end
+
+class SimpleCov::Formatter::Console
+ def format(result)
+ print "COVERAGE: #{result.covered_percent.round(2)}%\n"
+ end
+end
+
+SimpleCov.start 'spec' #if ENV["COVERAGE"]
+
+require File.expand_path('../../../lib/jenksin_pipeline_builder', __FILE__)
+
+RSpec.configure do |config|
+ config.before(:each) do
+ end
+end