jenkins_pipeline_builder 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +28 -43
  4. data/{spec/unit_tests/spec_helper.rb → .simplecov} +2 -15
  5. data/README.md +66 -11
  6. data/Rakefile +1 -25
  7. data/bin/generate +0 -1
  8. data/jenkins_pipeline_builder.gemspec +6 -6
  9. data/lib/jenkins_pipeline_builder.rb +14 -8
  10. data/lib/jenkins_pipeline_builder/builders.rb +207 -155
  11. data/lib/jenkins_pipeline_builder/cli/base.rb +1 -1
  12. data/lib/jenkins_pipeline_builder/cli/describe.rb +12 -8
  13. data/lib/jenkins_pipeline_builder/cli/list.rb +7 -7
  14. data/lib/jenkins_pipeline_builder/compiler.rb +62 -49
  15. data/lib/jenkins_pipeline_builder/extensions.rb +108 -0
  16. data/lib/jenkins_pipeline_builder/generator.rb +209 -160
  17. data/lib/jenkins_pipeline_builder/job_builder.rb +174 -105
  18. data/lib/jenkins_pipeline_builder/module_registry.rb +43 -59
  19. data/lib/jenkins_pipeline_builder/publishers.rb +166 -93
  20. data/lib/jenkins_pipeline_builder/pull_request.rb +7 -7
  21. data/lib/jenkins_pipeline_builder/triggers.rb +63 -40
  22. data/lib/jenkins_pipeline_builder/utils.rb +10 -10
  23. data/lib/jenkins_pipeline_builder/version.rb +1 -1
  24. data/lib/jenkins_pipeline_builder/view.rb +94 -123
  25. data/lib/jenkins_pipeline_builder/wrappers.rb +163 -96
  26. data/spec/lib/jenkins_pipeline_builder/compiler_spec.rb +31 -0
  27. data/spec/lib/jenkins_pipeline_builder/extensions_spec.rb +145 -0
  28. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/pullrequest_pipeline/PullRequest-10-SampleJob.yaml +2 -0
  29. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/pullrequest_pipeline/PullRequest-40-PullRequestGenerator.yaml +12 -0
  30. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/pullrequest_pipeline/project.yaml +12 -0
  31. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/sample_pipeline/SamplePipeline-10-Commit.yaml +3 -0
  32. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/sample_pipeline/SamplePipeline-20-Acceptance.yaml +3 -0
  33. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/sample_pipeline/SamplePipeline-30-Release.yaml +3 -0
  34. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/sample_pipeline/project.yaml +12 -0
  35. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/sample_pipeline/view.yaml +5 -0
  36. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/template_pipeline/jobs.tar.gz +0 -0
  37. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/template_pipeline/project.yaml +15 -0
  38. data/spec/lib/jenkins_pipeline_builder/fixtures/generator_tests/test_job.xml +48 -0
  39. data/spec/lib/jenkins_pipeline_builder/fixtures/view_test/duplicate_view.yaml +4 -0
  40. data/spec/lib/jenkins_pipeline_builder/fixtures/view_test/parent_view.yaml +9 -0
  41. data/spec/lib/jenkins_pipeline_builder/fixtures/view_test/regular_view.yaml +5 -0
  42. data/spec/lib/jenkins_pipeline_builder/generator_spec.rb +165 -0
  43. data/spec/lib/jenkins_pipeline_builder/module_registry_spec.rb +147 -0
  44. data/spec/lib/jenkins_pipeline_builder/pull_request_spec.rb +47 -0
  45. data/spec/lib/jenkins_pipeline_builder/spec_helper.rb +25 -0
  46. data/spec/lib/jenkins_pipeline_builder/utils_spec.rb +22 -0
  47. data/spec/lib/jenkins_pipeline_builder/view_spec.rb +54 -0
  48. metadata +54 -136
  49. data/lib/jenkins_pipeline_builder/extendable.rb +0 -42
  50. data/lib/jenkins_pipeline_builder/xml_helper.rb +0 -40
  51. data/spec/func_tests/spec_helper.rb +0 -18
  52. data/spec/func_tests/view_spec.rb +0 -90
  53. data/spec/unit_tests/compiler_spec.rb +0 -20
  54. data/spec/unit_tests/fixtures/files/Job-Build-Flow.xml +0 -57
  55. data/spec/unit_tests/fixtures/files/Job-Build-Flow.yaml +0 -22
  56. data/spec/unit_tests/fixtures/files/Job-Build-Maven.xml +0 -129
  57. data/spec/unit_tests/fixtures/files/Job-Build-Maven.yaml +0 -31
  58. data/spec/unit_tests/fixtures/files/Job-DSL.yaml +0 -14
  59. data/spec/unit_tests/fixtures/files/Job-DSL1.xml +0 -27
  60. data/spec/unit_tests/fixtures/files/Job-DSL2.xml +0 -25
  61. data/spec/unit_tests/fixtures/files/Job-Gem-Build.xml +0 -142
  62. data/spec/unit_tests/fixtures/files/Job-Gem-Build.yaml +0 -43
  63. data/spec/unit_tests/fixtures/files/Job-Generate-From-Template.xml +0 -32
  64. data/spec/unit_tests/fixtures/files/Job-Generate-From-Template.yaml +0 -8
  65. data/spec/unit_tests/fixtures/files/Job-Multi-Project.xml +0 -134
  66. data/spec/unit_tests/fixtures/files/Job-Multi-Project.yaml +0 -49
  67. data/spec/unit_tests/fixtures/files/archive_artifact.xml +0 -23
  68. data/spec/unit_tests/fixtures/files/archive_artifact.yaml +0 -5
  69. data/spec/unit_tests/fixtures/files/choice_parameter.xml +0 -39
  70. data/spec/unit_tests/fixtures/files/choice_parameter.yaml +0 -14
  71. data/spec/unit_tests/fixtures/files/concurrent_build.xml +0 -17
  72. data/spec/unit_tests/fixtures/files/concurrent_build.yaml +0 -4
  73. data/spec/unit_tests/fixtures/files/copy_artifact.xml +0 -30
  74. data/spec/unit_tests/fixtures/files/copy_artifact.yaml +0 -14
  75. data/spec/unit_tests/fixtures/files/discard_old.xml +0 -17
  76. data/spec/unit_tests/fixtures/files/discard_old.yaml +0 -8
  77. data/spec/unit_tests/fixtures/files/downstream.xml +0 -36
  78. data/spec/unit_tests/fixtures/files/downstream.yaml +0 -13
  79. data/spec/unit_tests/fixtures/files/downstream_blocking.xml +0 -19
  80. data/spec/unit_tests/fixtures/files/downstream_blocking.yaml +0 -15
  81. data/spec/unit_tests/fixtures/files/git_include_exclude.xml +0 -53
  82. data/spec/unit_tests/fixtures/files/git_include_exclude.yaml +0 -8
  83. data/spec/unit_tests/fixtures/files/groovy_postbuild.xml +0 -29
  84. data/spec/unit_tests/fixtures/files/groovy_postbuild.yaml +0 -9
  85. data/spec/unit_tests/fixtures/files/periodic_build.xml +0 -21
  86. data/spec/unit_tests/fixtures/files/periodic_build.yaml +0 -5
  87. data/spec/unit_tests/fixtures/files/post_build_script.xml +0 -28
  88. data/spec/unit_tests/fixtures/files/post_build_script.yaml +0 -11
  89. data/spec/unit_tests/fixtures/files/prepare_environment.xml +0 -17
  90. data/spec/unit_tests/fixtures/files/prepare_environment.yaml +0 -7
  91. data/spec/unit_tests/fixtures/files/project.yaml +0 -15
  92. data/spec/unit_tests/fixtures/files/properties_file.xml +0 -27
  93. data/spec/unit_tests/fixtures/files/properties_file.yaml +0 -8
  94. data/spec/unit_tests/fixtures/files/remote_job.xml +0 -18
  95. data/spec/unit_tests/fixtures/files/remote_job.yaml +0 -19
  96. data/spec/unit_tests/fixtures/files/rvm05.xml +0 -26
  97. data/spec/unit_tests/fixtures/files/rvm05.yaml +0 -5
  98. data/spec/unit_tests/fixtures/files/specific_priority.xml +0 -22
  99. data/spec/unit_tests/fixtures/files/specific_priority.yaml +0 -6
  100. data/spec/unit_tests/fixtures/files/throttle.xml +0 -17
  101. data/spec/unit_tests/fixtures/files/throttle.yaml +0 -8
  102. data/spec/unit_tests/fixtures/files/upstream.xml +0 -17
  103. data/spec/unit_tests/fixtures/files/upstream.yaml +0 -7
  104. data/spec/unit_tests/fixtures/pull_request/00.yaml +0 -14
  105. data/spec/unit_tests/fixtures/pull_request/10.yaml +0 -7
  106. data/spec/unit_tests/fixtures/pull_request/11.yaml +0 -4
  107. data/spec/unit_tests/fixtures/pull_request/project.yaml +0 -11
  108. data/spec/unit_tests/fixtures/templates/external_job.yaml +0 -4
  109. data/spec/unit_tests/fixtures/templates/project_with_jobs.yaml +0 -25
  110. data/spec/unit_tests/generator_spec.rb +0 -94
  111. data/spec/unit_tests/module_registry_spec.rb +0 -16
  112. data/spec/unit_tests/resolve_dependencies_spec.rb +0 -266
@@ -31,7 +31,7 @@ module JenkinsPipelineBuilder
31
31
  class_option :server_ip, aliases: '-s', desc: 'Jenkins server IP address'
32
32
  class_option :server_port, aliases: '-o', desc: 'Jenkins port'
33
33
  class_option :creds_file, aliases: '-c', desc: 'Credentials file for communicating with Jenkins server'
34
- class_option :debug, type: :boolean, aliases: '-d', desc: 'Run in debug mode (no Jenkins changes)', default: false
34
+ class_option :debug, type: :boolean, aliases: '-d', desc: 'Run in debug mode (no Jenkins changes)', default: false
35
35
 
36
36
  map '-v' => :version
37
37
 
@@ -22,24 +22,28 @@
22
22
 
23
23
  module JenkinsPipelineBuilder
24
24
  module CLI
25
- JenkinsPipelineBuilder.registry.entries.each do |entry, _path|
25
+ JenkinsPipelineBuilder.registry.entries.keys.each do |entry|
26
26
  klass_name = entry.to_s.classify
27
+ # rubocop:disable Style/AccessModifierIndentation
27
28
  klass = Class.new(Thor) do
28
29
 
29
- modules = JenkinsPipelineBuilder.registry.registered_modules[entry]
30
- modules.each do |mod, values|
31
- desc mod, "Details for #{mod}"
32
- define_method(mod) do
33
- display_module(mod, values)
30
+ extensions = JenkinsPipelineBuilder.registry.registry[:job][entry]
31
+ extensions.each do |key, exts|
32
+ # TODO: don't just take the first
33
+ ext = exts.first
34
+ desc key, "Details for #{ext.name}"
35
+ define_method(ext.name) do
36
+ display_module(ext)
34
37
  end
35
38
  end
36
39
 
37
40
  private
38
41
 
39
- def display_module(mod, values)
40
- puts "#{mod}: #{values[:description]}"
42
+ def display_module(ext)
43
+ puts "#{ext.name}: #{ext.description}"
41
44
  end
42
45
  end
46
+ # rubocop:enable Style/AccessModifierIndentation
43
47
  Module.const_set(klass_name, klass)
44
48
  end
45
49
  class Describe < Thor
@@ -23,24 +23,24 @@
23
23
  module JenkinsPipelineBuilder
24
24
  module CLI
25
25
  class List < Thor
26
- JenkinsPipelineBuilder.registry.entries.each do |entry, _path|
26
+ JenkinsPipelineBuilder.registry.entries.keys.each do |entry|
27
27
  desc entry, "List all #{entry}"
28
28
  define_method(entry) do
29
- modules = JenkinsPipelineBuilder.registry.registered_modules[entry]
30
- modules.each { |mod, values| display_module(mod, values) }
29
+ extensions = JenkinsPipelineBuilder.registry.registry[:job][entry]
30
+ extensions.each { |name, ext| display_module(name, ext) }
31
31
  end
32
32
  end
33
33
 
34
34
  desc 'job_attributes', 'List all job attributes'
35
35
  def job_attributes
36
- modules = JenkinsPipelineBuilder.registry.registered_modules[:job_attributes]
37
- modules.each { |mod, values| display_module(mod, values) }
36
+ extensions = JenkinsPipelineBuilder.registry.registry[:job]
37
+ extensions.each { |name, ext| display_module(name, ext) }
38
38
  end
39
39
 
40
40
  private
41
41
 
42
- def display_module(mod, values)
43
- puts "#{mod}: Jenkins Name: #{values[:jenkins_name]}"
42
+ def display_module(name, ext)
43
+ puts "#{name}: Jenkins Name: #{ext.jenkins_name}"
44
44
  end
45
45
  end
46
46
  end
@@ -48,69 +48,82 @@ module JenkinsPipelineBuilder
48
48
  def self.get_settings_bag(item_bag, settings_bag = {})
49
49
  item = item_bag[:value]
50
50
  bag = {}
51
- return unless item.kind_of?(Hash)
51
+ return unless item.is_a?(Hash)
52
52
  item.keys.each do |k|
53
53
  val = item[k]
54
- if val.kind_of? String
55
- new_value = resolve_value(val, settings_bag, {})
56
- return nil if new_value.nil?
57
- bag[k] = new_value
58
- end
54
+ next unless val.is_a? String
55
+ new_value = resolve_value(val, settings_bag, {})
56
+ return nil if new_value.nil?
57
+ bag[k] = new_value
59
58
  end
60
59
  my_settings_bag = settings_bag.clone
61
60
  my_settings_bag.merge(bag)
62
61
  end
63
62
 
64
63
  def self.compile(item, settings = {}, job_collection = {})
65
- errors = {}
66
64
  case item
67
65
  when String
68
- new_value = resolve_value(item, settings, job_collection)
69
- errors[item] = "Failed to resolve #{item}" if new_value.nil?
70
- return false, errors unless errors.empty?
71
- return true, new_value
66
+ return compile_string item, settings, job_collection
72
67
  when Hash
73
- result = {}
74
- item.each do |key, value|
75
- if value.nil?
76
- errors[key] = "key: #{key} has a nil value, this is likely a yaml syntax error. Skipping children and siblings"
77
- break
78
- end
79
- success, payload = compile(value, settings, job_collection)
80
- unless success
81
- errors.merge!(payload)
82
- next
83
- end
84
- if payload.nil?
85
- errors[key] = "Failed to resolve:\n===>key: #{key}\n\n===>value: #{value}\n\n===>of: #{item}"
86
- next
87
- end
88
- result[key] = payload
89
- end
90
- return false, errors unless errors.empty?
91
- return true, result
68
+ return compile_hash item, settings, job_collection
92
69
  when Array
93
- result = []
94
- item.each do |value|
95
- if value.nil?
96
- errors[item] = "found a nil value when processing following array:\n #{item.inspect}"
97
- break
98
- end
99
- success, payload = compile(value, settings, job_collection)
100
- unless success
101
- errors.merge!(payload)
102
- next
103
- end
104
- if payload.nil?
105
- errors[value] = "Failed to resolve:\n===>item #{value}\n\n===>of list: #{item}"
106
- next
107
- end
108
- result << payload
109
- end
110
- return false, errors unless errors.empty?
111
- return true, result
70
+ return compile_array item, settings, job_collection
112
71
  end
113
72
  [true, item]
114
73
  end
74
+
75
+ def self.compile_string(item, settings, job_collection)
76
+ errors = {}
77
+ new_value = resolve_value(item, settings, job_collection)
78
+ errors[item] = "Failed to resolve #{item}" if new_value.nil?
79
+ return false, errors unless errors.empty?
80
+ [true, new_value]
81
+ end
82
+
83
+ def self.compile_array(item, settings, job_collection)
84
+ errors = {}
85
+ result = []
86
+ item.each do |value|
87
+ if value.nil?
88
+ errors[item] = "found a nil value when processing following array:\n #{item.inspect}"
89
+ break
90
+ end
91
+ success, payload = compile(value, settings, job_collection)
92
+ unless success
93
+ errors.merge!(payload)
94
+ next
95
+ end
96
+ if payload.nil?
97
+ errors[value] = "Failed to resolve:\n===>item #{value}\n\n===>of list: #{item}"
98
+ next
99
+ end
100
+ result << payload
101
+ end
102
+ return false, errors unless errors.empty?
103
+ [true, result]
104
+ end
105
+
106
+ def self.compile_hash(item, settings, job_collection)
107
+ errors = {}
108
+ result = {}
109
+ item.each do |key, value|
110
+ if value.nil?
111
+ errors[key] = "key: #{key} has a nil value, this is often a yaml syntax error. Skipping children and siblings"
112
+ break
113
+ end
114
+ success, payload = compile(value, settings, job_collection)
115
+ unless success
116
+ errors.merge!(payload)
117
+ next
118
+ end
119
+ if payload.nil?
120
+ errors[key] = "Failed to resolve:\n===>key: #{key}\n\n===>value: #{value}\n\n===>of: #{item}"
121
+ next
122
+ end
123
+ result[key] = payload
124
+ end
125
+ return false, errors unless errors.empty?
126
+ [true, result]
127
+ end
115
128
  end
116
129
  end
@@ -0,0 +1,108 @@
1
+ #
2
+ # Copyright (c) 2014 Constant Contact
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+
22
+ require 'jenkins_pipeline_builder'
23
+ JenkinsPipelineBuilder.registry.entries.each do |type, path|
24
+ singular_type = type.to_s.singularize
25
+ define_method singular_type do |&block|
26
+ ext = JenkinsPipelineBuilder::Extension.new
27
+ ext.instance_eval(&block)
28
+ ext.path path
29
+ ext.type singular_type
30
+ unless ext.valid?
31
+ name = ext.name || 'A plugin with no name provided'
32
+ puts "Encountered errors while registering #{name}"
33
+ puts ext.errors.map { |k, v| "#{k}: #{v}" }.join(', ')
34
+ return false
35
+ end
36
+ JenkinsPipelineBuilder.registry.register([:job, type], ext)
37
+ puts "Successfully registered #{ext.name} for versions #{ext.min_version} and higher" if ext.announced
38
+ end
39
+ end
40
+
41
+ def job_attribute(&block)
42
+ ext = JenkinsPipelineBuilder::Extension.new
43
+ ext.instance_eval(&block)
44
+ ext.type :job_attribute
45
+ unless ext.valid?
46
+ name = ext.name || 'A plugin with no name provided'
47
+ puts "Encountered errors while registering #{name}"
48
+ puts ext.errors.map { |k, v| "#{k}: #{v}" }.join(', ')
49
+ return false
50
+ end
51
+ JenkinsPipelineBuilder.registry.register([:job], ext)
52
+ puts "Successfully registered #{ext.name} for versions #{ext.min_version} and higher" if ext.announced
53
+ end
54
+
55
+ module JenkinsPipelineBuilder
56
+ class Extension
57
+ DSL_METHODS = {
58
+ name: false,
59
+ plugin_id: false,
60
+ min_version: false,
61
+ jenkins_name: 'No jenkins display name set',
62
+ description: 'No description set',
63
+ path: false,
64
+ announced: true,
65
+ type: false
66
+ }
67
+ DSL_METHODS.keys.each do |method_name|
68
+ define_method method_name do |value = nil|
69
+ return instance_variable_get("@#{method_name}") if value.nil?
70
+ instance_variable_set("@#{method_name}", value)
71
+ end
72
+ end
73
+
74
+ def xml(path = false, &block)
75
+ @path = path if path
76
+ return @xml unless block
77
+ @xml = block
78
+ end
79
+
80
+ def after(&block)
81
+ return @after unless block
82
+ @after = block
83
+ end
84
+
85
+ def before(&block)
86
+ return @before unless block
87
+ @before = block
88
+ end
89
+
90
+ def initialize
91
+ DSL_METHODS.each do |key, value|
92
+ instance_variable_set("@#{key}", value) if value
93
+ end
94
+ end
95
+
96
+ def valid?
97
+ errors.empty?
98
+ end
99
+
100
+ def errors
101
+ errors = {}
102
+ DSL_METHODS.keys.each do |name|
103
+ errors[name] = 'Must be set' if send(name).nil?
104
+ end
105
+ errors
106
+ end
107
+ end
108
+ end
@@ -21,10 +21,12 @@
21
21
  #
22
22
 
23
23
  require 'yaml'
24
- require 'pp'
25
24
 
26
25
  module JenkinsPipelineBuilder
27
26
  class Generator
27
+ attr_reader :debug
28
+ attr_accessor :no_files, :job_templates, :job_collection, :logger, :module_registry, :remote_depends
29
+
28
30
  # Initialize a Client object with Jenkins Api Client
29
31
  #
30
32
  # @param args [Hash] Arguments to connect to Jenkins server
@@ -43,50 +45,19 @@ module JenkinsPipelineBuilder
43
45
  @module_registry = ModuleRegistry.new
44
46
  end
45
47
 
46
- def load_extensions(path)
47
- path = "#{path}/extensions"
48
- path = File.expand_path(path, Dir.getwd)
49
- if File.directory?(path)
50
- @logger.info "Loading extensions from folder #{path}"
51
- Dir[File.join(path, '/*.yaml'), File.join(path, '/*.yml')].each do |file|
52
- @logger.info "Loading file #{file}"
53
- yaml = YAML.load_file(file)
54
- yaml.each do |ext|
55
- Utils.symbolize_keys_deep!(ext)
56
- ext = ext[:extension]
57
- name = ext[:name]
58
- type = ext[:type]
59
- function = ext[:function]
60
- fail "Duplicate extension with name '#{name}' was detected." if @extensions.key?(name)
61
- @extensions[name.to_s] = { name: name.to_s, type: type, function: function }
62
- end
63
- end
64
- end
65
- @extensions.each_value do |ext|
66
- name = ext[:name].to_sym
67
- registry = @module_registry.registry[:job]
68
- function = eval "Proc.new {|params,xml| #{ext[:function]} }"
69
- type = ext[:type].downcase.pluralize.to_sym if ext[:type]
70
- if type
71
- registry[type][:registry][name] = function
72
- else
73
- registry[name] = function
74
- end
75
- end
76
- end
77
-
78
48
  def debug=(value)
79
49
  @debug = value
80
- @logger.level = (value) ? Logger::DEBUG : Logger::INFO
50
+ logger.level = (value) ? Logger::DEBUG : Logger::INFO
51
+ end
52
+
53
+ def logger
54
+ JenkinsPipelineBuilder.logger
81
55
  end
82
56
 
83
57
  def client
84
58
  JenkinsPipelineBuilder.client
85
59
  end
86
60
 
87
- attr_reader :debug
88
- attr_accessor :no_files, :job_collection, :logger, :module_registry
89
-
90
61
  # Creates an instance to the View class by passing a reference to self
91
62
  #
92
63
  # @return [JenkinsApi::Client::System] An object to System subclass
@@ -95,24 +66,128 @@ module JenkinsPipelineBuilder
95
66
  JenkinsPipelineBuilder::View.new(self)
96
67
  end
97
68
 
98
- def load_collection_from_path(path, recursively = false, remote = false)
69
+ def bootstrap(path, project_name)
70
+ @logger.info "Bootstrapping pipeline from path #{path}"
71
+ load_collection_from_path(path)
72
+ # @logger.info @job_collection
73
+ cleanup_temp_remote
74
+ load_extensions(path)
75
+ errors = {}
76
+ # Publish all the jobs if the projects are not found
77
+ if projects.count == 0
78
+ errors = publish_jobs(jobs)
79
+ else
80
+ errors = publish_project(project_name)
81
+ end
82
+ errors.each do |k, v|
83
+ @logger.error "Encountered errors compiling: #{k}:"
84
+ @logger.error v
85
+ end
86
+ errors
87
+ end
88
+
89
+ def pull_request(path, project_name)
90
+ success = false
91
+ @logger.info "Pull Request Generator Running from path #{path}"
92
+ load_collection_from_path(path)
93
+ cleanup_temp_remote
94
+ load_extensions(path)
95
+ @logger.info "Project: #{projects}"
96
+ projects.each do |project|
97
+ next unless project[:name] == project_name || project_name.nil?
98
+ @logger.info "Using Project #{project}"
99
+ pull_job = find_pull_request_generator(project)
100
+ p_success, p_payload = compile_pull_request_generator(pull_job[:name], project)
101
+ next unless p_success
102
+ jobs = filter_pull_request_jobs(pull_job)
103
+ pull = JenkinsPipelineBuilder::PullRequestGenerator.new(project, jobs, p_payload)
104
+
105
+ @job_collection.merge! pull.jobs
106
+ success = create_pull_request_jobs(pull)
107
+ purge_pull_request_jobs(pull)
108
+ end
109
+ success
110
+ end
111
+
112
+ def dump(job_name)
113
+ @logger.info "Debug #{@debug}"
114
+ @logger.info "Dumping #{job_name} into #{job_name}.xml"
115
+ xml = client.job.get_config(job_name)
116
+ File.open(job_name + '.xml', 'w') { |f| f.write xml }
117
+ end
118
+
119
+ #
120
+ # BEGIN PRIVATE METHODS
121
+ #
122
+
123
+ private
124
+
125
+ def purge_pull_request_jobs(pull)
126
+ pull.purge.each do |purge_job|
127
+ jobs = client.job.list "#{purge_job}.*"
128
+ jobs.each do |job|
129
+ client.job.delete job
130
+ end
131
+ end
132
+ end
133
+
134
+ def create_pull_request_jobs(pull)
135
+ success = false
136
+ pull.create.each do |pull_project|
137
+ success, compiled_project = resolve_project(pull_project)
138
+ compiled_project[:value][:jobs].each do |i|
139
+ job = i[:result]
140
+ success, payload = compile_job_to_xml(job)
141
+ create_or_update(job, payload) if success
142
+ end
143
+ end
144
+ success
145
+ end
146
+
147
+ def find_pull_request_generator(project)
148
+ project_jobs = project[:value][:jobs] || []
149
+ pull_job = nil
150
+ project_jobs.each do |job|
151
+ job = job.keys.first if job.is_a? Hash
152
+ job = @job_collection[job.to_s]
153
+ pull_job = job if job[:value][:job_type] == 'pull_request_generator'
154
+ end
155
+ fail 'No Pull Request Found for Project' unless pull_job
156
+ pull_job
157
+ end
158
+
159
+ def filter_pull_request_jobs(pull_job)
160
+ jobs = {}
161
+ pull_jobs = pull_job[:value][:jobs] || []
162
+ pull_jobs.each do |job|
163
+ if job.is_a? String
164
+ jobs[job.to_s] = @job_collection[job.to_s]
165
+ else
166
+ jobs[job.keys.first.to_s] = @job_collection[job.keys.first.to_s]
167
+ end
168
+ end
169
+ fail 'No jobs found for pull request' if jobs.empty?
170
+ jobs
171
+ end
172
+
173
+ def compile_pull_request_generator(pull_job, project)
174
+ defaults = get_item('global')
175
+ settings = defaults.nil? ? {} : defaults[:value] || {}
176
+ settings = Compiler.get_settings_bag(project, settings)
177
+ resolve_job_by_name(pull_job, settings)
178
+ end
179
+
180
+ def load_collection_from_path(path, remote = false)
99
181
  path = File.expand_path(path, Dir.getwd)
100
182
  if File.directory?(path)
101
- @logger.info "Generating from folder #{path}"
183
+ logger.info "Generating from folder #{path}"
102
184
  Dir[File.join(path, '/*.yaml'), File.join(path, '/*.yml')].each do |file|
103
- if File.directory?(file) # TODO: This doesn't work unless the folder contains .yml or .yaml at the end
104
- if recursively
105
- load_collection_from_path(File.join(path, file), recursively)
106
- else
107
- next
108
- end
109
- end
110
- @logger.info "Loading file #{file}"
185
+ logger.info "Loading file #{file}"
111
186
  yaml = YAML.load_file(file)
112
187
  load_job_collection(yaml, remote)
113
188
  end
114
189
  else
115
- @logger.info "Loading file #{path}"
190
+ logger.info "Loading file #{path}"
116
191
  yaml = YAML.load_file(path)
117
192
  load_job_collection(yaml, remote)
118
193
  end
@@ -124,14 +199,14 @@ module JenkinsPipelineBuilder
124
199
  key = section.keys.first
125
200
  value = section[key]
126
201
  if key == :dependencies
127
- @logger.info 'Resolving Dependencies for remote project'
202
+ logger.info 'Resolving Dependencies for remote project'
128
203
  load_remote_yaml(value)
129
204
  next
130
205
  end
131
206
  name = value[:name]
132
207
  if @job_collection.key?(name)
133
208
  if remote
134
- @logger.info "Duplicate item with name '#{name}' was detected from the remote folder."
209
+ logger.info "Duplicate item with name '#{name}' was detected from the remote folder."
135
210
  else
136
211
  fail "Duplicate item with name '#{name}' was detected."
137
212
  end
@@ -145,6 +220,54 @@ module JenkinsPipelineBuilder
145
220
  @job_collection[name.to_s]
146
221
  end
147
222
 
223
+ def load_extensions(path)
224
+ path = "#{path}/extensions"
225
+ path = File.expand_path(path, Dir.getwd)
226
+ unless File.directory?(path)
227
+ logger.info "Loading extensions from folder #{path}"
228
+ logger.info Dir.glob("#{path}/*.rb").inspect
229
+ Dir.glob("#{path}/*.rb").each do |file|
230
+ logger.info "Loaded #{file}"
231
+ require file
232
+ end
233
+ end
234
+ match_extension_versions
235
+ end
236
+
237
+ def match_extension_versions
238
+ registry = @module_registry.registry[:job]
239
+ installed_plugins = @debug ? nil : list_plugins # Only get plugins if not in debug mode
240
+ @logger.debug 'Loading newest version of all plugins since we are in debug mode.'
241
+ registry.each do |registry_key, registry_value|
242
+ if registry_value.count > 1
243
+ registry_value.each_key do |extension_key|
244
+ registry[registry_key][extension_key] = newest_compatible(extension_key, installed_plugins, registry_key)
245
+ end
246
+ else
247
+ registry[registry_key] = newest_compatible(registry_key, installed_plugins)
248
+ end
249
+ end
250
+ end
251
+
252
+ def newest_compatible(extension, installed_plugins, key = nil)
253
+ # Fetch the registrered_modules for the extension
254
+ registry = @module_registry.registry[:job]
255
+ registry = registry[key] unless key.nil?
256
+ registry = registry[extension]
257
+ keep = nil
258
+ keep_version = ''
259
+ registry.each do |ex|
260
+ is_available = @debug ? true : ex.min_version.to_s <= installed_plugins[ex.plugin_id.to_s].to_s
261
+ is_newer = ex.min_version.to_s >= keep_version
262
+
263
+ next unless keep.nil? || (is_available && is_newer)
264
+
265
+ keep = ex
266
+ keep_version = ex.min_version
267
+ end
268
+ keep
269
+ end
270
+
148
271
  def load_template(path, template)
149
272
  # If we specify what folder the yaml is in, load that
150
273
  if template[:folder]
@@ -154,10 +277,7 @@ module JenkinsPipelineBuilder
154
277
  # If we are looking for the newest version or no version was set
155
278
  if (template[:version].nil? || template[:version] == 'newest') && File.directory?(path)
156
279
  folders = Dir.entries(path)
157
- highest = '0' # Default to v1
158
- folders.each do |f|
159
- highest = f if f > highest # Note: to_i returns any integers in the folder name
160
- end
280
+ highest = folders.max
161
281
  template[:version] = highest unless highest == 0
162
282
  end
163
283
  path = File.join(path, template[:version]) unless template[:version].nil?
@@ -165,8 +285,8 @@ module JenkinsPipelineBuilder
165
285
  end
166
286
 
167
287
  if File.directory?(path)
168
- @logger.info "Loading from #{path}"
169
- load_collection_from_path(path, false, true)
288
+ logger.info "Loading from #{path}"
289
+ load_collection_from_path(path, true)
170
290
  true
171
291
  else
172
292
  false
@@ -175,7 +295,7 @@ module JenkinsPipelineBuilder
175
295
 
176
296
  def download_yaml(url, file)
177
297
  @remote_depends[url] = file
178
- @logger.info "Downloading #{url} to #{file}.tar"
298
+ logger.info "Downloading #{url} to #{file}.tar"
179
299
  open("#{file}.tar", 'w') do |local_file|
180
300
  open(url) do |remote_file|
181
301
  local_file.write(Zlib::GzipReader.new(remote_file).read)
@@ -183,7 +303,7 @@ module JenkinsPipelineBuilder
183
303
  end
184
304
 
185
305
  # Extract Tar.gz to 'remote' folder
186
- @logger.info "Unpacking #{file}.tar to #{file} folder"
306
+ logger.info "Unpacking #{file}.tar to #{file} folder"
187
307
  Archive::Tar::Minitar.unpack("#{file}.tar", file)
188
308
  end
189
309
 
@@ -203,7 +323,7 @@ module JenkinsPipelineBuilder
203
323
  path = File.expand_path(file, Dir.getwd)
204
324
  # Load templates recursively
205
325
  unless source[:templates]
206
- @logger.info 'No specific template specified'
326
+ logger.info 'No specific template specified'
207
327
  # Try to load the folder or the pipeline folder
208
328
  path = File.join(path, 'pipeline') if Dir.entries(path).include? 'pipeline'
209
329
  return load_collection_from_path(path)
@@ -216,12 +336,12 @@ module JenkinsPipelineBuilder
216
336
  def load_templates(path, templates)
217
337
  templates.each do |template|
218
338
  version = template[:version] || 'newest'
219
- @logger.info "Loading #{template[:name]} at version #{version}"
339
+ logger.info "Loading #{template[:name]} at version #{version}"
220
340
  # Move into the remote folder and look for the template folder
221
341
  remote = Dir.entries(path)
222
342
  if remote.include? template[:name]
223
343
  # We found the template name, load this path
224
- @logger.info 'We found the template!'
344
+ logger.info 'We found the template!'
225
345
  load_template(path, template)
226
346
  else
227
347
  # Many cases we must dig one layer deep
@@ -239,9 +359,13 @@ module JenkinsPipelineBuilder
239
359
  end
240
360
  end
241
361
 
362
+ def list_plugins
363
+ client.plugin.list_installed
364
+ end
365
+
242
366
  def prepare_jobs(jobs)
243
367
  jobs.map! do |job|
244
- job.kind_of?(String) ? { job.to_sym => {} } : job
368
+ job.is_a?(String) ? { job.to_sym => {} } : job
245
369
  end
246
370
  end
247
371
 
@@ -249,6 +373,9 @@ module JenkinsPipelineBuilder
249
373
  jobs.each do |job|
250
374
  job_id = job.keys.first
251
375
  j = get_item(job_id)
376
+
377
+ next unless j
378
+
252
379
  Utils.hash_merge!(j, job[job_id])
253
380
  j[:value][:name] = j[:job_name] if j[:job_name]
254
381
  end
@@ -256,7 +383,7 @@ module JenkinsPipelineBuilder
256
383
 
257
384
  def process_views(views, project, errors = {})
258
385
  views.map! do |view|
259
- view.kind_of?(String) ? { view.to_sym => {} } : view
386
+ view.is_a?(String) ? { view.to_sym => {} } : view
260
387
  end
261
388
  views.each do |view|
262
389
  view_id = view.keys.first
@@ -293,11 +420,10 @@ module JenkinsPipelineBuilder
293
420
  project_body = project[:value]
294
421
 
295
422
  jobs = prepare_jobs(project_body[:jobs]) if project_body[:jobs]
296
- @logger.info project
423
+ logger.info project
297
424
  process_job_changes(jobs)
298
425
  errors = process_jobs(jobs, project)
299
426
  errors = process_views(project_body[:views], project, errors) if project_body[:views]
300
-
301
427
  errors.each do |k, v|
302
428
  puts "Encountered errors processing: #{k}:"
303
429
  v.each do |key, error|
@@ -314,7 +440,7 @@ module JenkinsPipelineBuilder
314
440
  job = get_item(name)
315
441
  fail "Failed to locate job by name '#{name}'" if job.nil?
316
442
  job_value = job[:value]
317
- @logger.debug "Compiling job #{name}"
443
+ logger.debug "Compiling job #{name}"
318
444
  success, payload = Compiler.compile(job_value, settings, @job_collection)
319
445
  [success, payload]
320
446
  end
@@ -340,21 +466,17 @@ module JenkinsPipelineBuilder
340
466
  next unless project_name.nil? || project[:name] == project_name
341
467
  success, payload = resolve_project(project)
342
468
  if success
343
- puts 'successfully resolved project'
469
+ @logger.info 'successfully resolved project'
344
470
  compiled_project = payload
345
471
  else
346
- puts payload
347
472
  return false
348
473
  end
349
474
 
350
- if compiled_project[:value][:jobs]
351
- errors = publish_jobs(compiled_project[:value][:jobs])
352
- end
353
- if compiled_project[:value][:views]
354
- compiled_project[:value][:views].each do |v|
355
- compiled_view = v[:result]
356
- view.create(compiled_view)
357
- end
475
+ errors = publish_jobs(compiled_project[:value][:jobs]) if compiled_project[:value][:jobs]
476
+ next unless compiled_project[:value][:views]
477
+ compiled_project[:value][:views].each do |v|
478
+ compiled_view = v[:result]
479
+ view.create(compiled_view)
358
480
  end
359
481
  end
360
482
  errors
@@ -362,7 +484,7 @@ module JenkinsPipelineBuilder
362
484
 
363
485
  def publish_jobs(jobs, errors = {})
364
486
  jobs.each do |i|
365
- puts "Processing #{i}"
487
+ @logger.info "Processing #{i}"
366
488
  job = i[:result]
367
489
  fail "Result is empty for #{i}" if job.nil?
368
490
  success, payload = compile_job_to_xml(job)
@@ -375,86 +497,11 @@ module JenkinsPipelineBuilder
375
497
  errors
376
498
  end
377
499
 
378
- def bootstrap(path, project_name)
379
- @logger.info "Bootstrapping pipeline from path #{path}"
380
- load_collection_from_path(path)
381
- @logger.info @job_collection
382
- cleanup_temp_remote
383
- load_extensions(path)
384
- errors = {}
385
- # Publish all the jobs if the projects are not found
386
- if projects.count == 0
387
- errors = publish_jobs(jobs)
388
- else
389
- errors = publish_project(project_name)
390
- end
391
- errors.each do |k, v|
392
- @logger.error "Encountered errors compiling: #{k}:"
393
- @logger.error v
394
- end
395
- end
396
-
397
- def pull_request(path, project_name)
398
- @logger.info "Pull Request Generator Running from path #{path}"
399
- load_collection_from_path(path)
400
- cleanup_temp_remote
401
- load_extensions(path)
402
- jobs = {}
403
- @logger.info "Project: #{projects}"
404
- projects.each do |project|
405
- if project[:name] == project_name || project_name.nil?
406
- project_body = project[:value]
407
- project_jobs = project_body[:jobs] || []
408
- @logger.info "Using Project #{project}"
409
- pull_job = nil
410
- project_jobs.each do |job|
411
- job = job.keys.first if job.is_a? Hash
412
- job = @job_collection[job.to_s]
413
- pull_job = job if job[:value][:job_type] == 'pull_request_generator'
414
- end
415
- fail 'No Pull Request Found for Project' unless pull_job
416
- pull_jobs = pull_job[:value][:jobs] || []
417
- pull_jobs.each do |job|
418
- if job.is_a? String
419
- jobs[job.to_s] = @job_collection[job.to_s]
420
- else
421
- jobs[job.keys.first.to_s] = @job_collection[job.keys.first.to_s]
422
- end
423
- end
424
- pull = JenkinsPipelineBuilder::PullRequestGenerator.new(project, jobs, pull_job)
425
- # Build the jobs
426
- @job_collection.merge! pull.jobs
427
- pull.create.each do |project|
428
- success, compiled_project = resolve_project(project)
429
- compiled_project[:value][:jobs].each do |i|
430
- job = i[:result]
431
- success, payload = compile_job_to_xml(job)
432
- create_or_update(job, payload) if success
433
- end
434
- end
435
- # Purge old jobs
436
- pull.purge.each do |job|
437
- jobs = @client.job.list "#{job}.*"
438
- jobs.each do |job|
439
- @client.job.delete job
440
- end
441
- end
442
- end
443
- end
444
- end
445
-
446
- def dump(job_name)
447
- @logger.info "Debug #{@debug}"
448
- @logger.info "Dumping #{job_name} into #{job_name}.xml"
449
- xml = client.job.get_config(job_name)
450
- File.open(job_name + '.xml', 'w') { |f| f.write xml }
451
- end
452
-
453
500
  def create_or_update(job, xml)
454
501
  job_name = job[:name]
455
502
  if @debug
456
- @logger.info "Will create job #{job}"
457
- @logger.info "#{xml}"
503
+ logger.info "Will create job #{job}"
504
+ logger.info "#{xml}"
458
505
  File.open(job_name + '.xml', 'w') { |f| f.write xml }
459
506
  return
460
507
  end
@@ -469,7 +516,7 @@ module JenkinsPipelineBuilder
469
516
  def compile_job_to_xml(job)
470
517
  fail 'Job name is not specified' unless job[:name]
471
518
 
472
- @logger.info "Creating Yaml Job #{job}"
519
+ logger.info "Creating Yaml Job #{job}"
473
520
  job[:job_type] = 'free_style' unless job[:job_type]
474
521
  case job[:job_type]
475
522
  when 'job_dsl'
@@ -511,7 +558,9 @@ module JenkinsPipelineBuilder
511
558
  xml = client.job.build_freestyle_config(params)
512
559
  n_xml = Nokogiri::XML(xml)
513
560
 
561
+ logger.debug 'Loading the required modules'
514
562
  @module_registry.traverse_registry_path('job', params, n_xml)
563
+ logger.debug 'Module loading complete'
515
564
 
516
565
  n_xml.to_xml
517
566
  end
@@ -519,8 +568,8 @@ module JenkinsPipelineBuilder
519
568
  def add_job_dsl(job, xml)
520
569
  n_xml = Nokogiri::XML(xml)
521
570
  n_xml.root.name = 'com.cloudbees.plugins.flow.BuildFlow'
522
- Nokogiri::XML::Builder.with(n_xml.root) do |xml|
523
- xml.dsl job[:build_flow]
571
+ Nokogiri::XML::Builder.with(n_xml.root) do |b_xml|
572
+ b_xml.dsl job[:build_flow]
524
573
  end
525
574
  n_xml.to_xml
526
575
  end
@@ -529,14 +578,14 @@ module JenkinsPipelineBuilder
529
578
  def update_job_dsl(job, xml)
530
579
  n_xml = Nokogiri::XML(xml)
531
580
  n_builders = n_xml.xpath('//builders').first
532
- Nokogiri::XML::Builder.with(n_builders) do |xml|
533
- build_job_dsl(job, xml)
581
+ Nokogiri::XML::Builder.with(n_builders) do |b_xml|
582
+ build_job_dsl(job, b_xml)
534
583
  end
535
584
  n_xml.to_xml
536
585
  end
537
586
 
538
587
  def generate_job_dsl_body(params)
539
- @logger.info 'Generating pipeline'
588
+ logger.info 'Generating pipeline'
540
589
 
541
590
  xml = client.job.build_freestyle_config(params)
542
591