cicd-builder 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ cicd-builder
2
+ ============
3
+
4
+ Continuous Integration/ Continuous Deployment builder helper
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler'
7
+ rescue LoadError => e
8
+ warn e.message
9
+ warn "Run `gem install bundler` to install Bundler."
10
+ exit -1
11
+ end
12
+
13
+ begin
14
+ Bundler.setup(:development)
15
+ rescue Bundler::BundlerError => e
16
+ warn e.message
17
+ warn "Run `bundle install` to install missing gems."
18
+ exit e.status_code
19
+ end
20
+
21
+ require 'rake'
22
+
23
+ require 'rubygems/tasks'
24
+ Gem::Tasks.new
25
+
26
+ require 'cucumber/rake/task'
27
+
28
+ Cucumber::Rake::Task.new do |t|
29
+ t.cucumber_opts = %w[--format pretty]
30
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/cicd/builder/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'cicd-builder'
7
+ gem.version = CiCd::Builder::VERSION
8
+ gem.summary = 'Jenkins builder task for CI/CD'
9
+ gem.description = 'Jenkins builder task for CI/CD'
10
+ gem.license = 'Apachev2'
11
+ gem.authors = ['Christo De Lange']
12
+ gem.email = 'rubygems@dldinternet.com'
13
+ gem.homepage = 'https://rubygems.org/gems/cicd-builder'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_dependency 'awesome_print', ">= 0.0.0"
21
+ gem.add_dependency 'inifile', '>= 0.0.0'
22
+ gem.add_dependency 'logging', '>= 0.0.0'
23
+ gem.add_dependency 'json', '= 1.7.7'
24
+ gem.add_dependency 'chef', '>= 11.8.2'
25
+ gem.add_dependency 'aws-sdk', '>= 0.0.0'
26
+ gem.add_dependency 'yajl-ruby', '>= 0.0.0'
27
+ gem.add_dependency 'git', '>= 1.2.7'
28
+
29
+ gem.add_development_dependency 'bundler', '~> 1.0'
30
+ gem.add_development_dependency 'rake', '~> 10.3'
31
+ gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
32
+ gem.add_development_dependency 'cucumber'
33
+ end
data/features/.gitkeep ADDED
File without changes
@@ -0,0 +1 @@
1
+ Feature: Blah blah blah
@@ -0,0 +1 @@
1
+ Feature: Blah blah blah
File without changes
@@ -0,0 +1,87 @@
1
+ require 'cicd/builder/version'
2
+ module CiCd
3
+ module Builder
4
+
5
+ require 'awesome_print'
6
+ require 'optparse'
7
+ require 'inifile'
8
+ require 'logging'
9
+ require 'net/http'
10
+ require 'uri'
11
+ require 'fileutils'
12
+ require 'digest'
13
+ require 'yajl/json_gem'
14
+ require 'aws-sdk'
15
+
16
+ _lib=File.dirname(__FILE__)
17
+ $:.unshift(_lib) unless $:.include?(_lib)
18
+
19
+ require 'cicd/builder/version'
20
+ require 'cicd/builder/mixlib/constants'
21
+
22
+ #noinspection ALL
23
+ class BuilderBase
24
+ attr_accessor :default_options
25
+ attr_accessor :options
26
+ attr_accessor :logger
27
+ attr_accessor :vars
28
+
29
+ def initialize()
30
+ @vars = {
31
+ return_code: -1
32
+ }
33
+ @default_options = {
34
+ builder: ::CiCd::Builder::VERSION,
35
+ env_keys: %w(JENKINS_HOME BUILD_NUMBER JOB_NAME)
36
+ }
37
+ end
38
+
39
+ require 'cicd/builder/mixlib/errors'
40
+ require 'cicd/builder/mixlib/utils'
41
+ require 'cicd/builder/mixlib/options'
42
+ require 'cicd/builder/mixlib/environment'
43
+ require 'cicd/builder/mixlib/repo'
44
+ require 'cicd/builder/mixlib/build'
45
+
46
+ # ---------------------------------------------------------------------------------------------------------------
47
+ def getBuilderVersion
48
+ {
49
+ version: VERSION,
50
+ major: MAJOR,
51
+ minor: MINOR,
52
+ patch: PATCH,
53
+ }
54
+ end
55
+
56
+ # ---------------------------------------------------------------------------------------------------------------
57
+ def run()
58
+ $stdout.write("CiCd::Builder v#{@VERSION}\n")
59
+ parseOptions()
60
+
61
+ ret = checkEnvironment()
62
+ if 0 == ret
63
+ ret = getVars()
64
+ if 0 == ret
65
+ ret = prepareBuild()
66
+ if 0 == ret
67
+ ret = makeBuild()
68
+ if 0 == ret
69
+ ret = saveBuild()
70
+ if 0 == ret
71
+ ret = uploadBuildArtifacts()
72
+ if 0 == ret
73
+ # noop
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ @vars[:return_code]
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,160 @@
1
+ require 'json'
2
+
3
+ module CiCd
4
+ module Builder
5
+
6
+ # ---------------------------------------------------------------------------------------------------------------
7
+ def cleanupBuild()
8
+ [ :build_pkg, :build_chk, :build_mdf, :build_mff ].each do |fil|
9
+ if File.exists?(@vars[fil])
10
+ begin
11
+ FileUtils.rm_f(@vars[fil])
12
+ rescue => e
13
+ @logger.error e.to_s
14
+ #raise e
15
+ return -96
16
+ end
17
+ end
18
+ end
19
+ if Dir.exists?(@vars[:build_dir])
20
+ begin
21
+ FileUtils.rm_r(@vars[:build_dir])
22
+ rescue => e
23
+ @logger.error e.to_s
24
+ #raise e
25
+ return -95
26
+ end
27
+ end
28
+ 0
29
+ end
30
+
31
+ # ---------------------------------------------------------------------------------------------------------------
32
+ def prepareBuild()
33
+ meta = {}
34
+ %w[ WORKSPACE PROJECT_NAME VERSION RELEASE ].each do |e|
35
+ unless ENV.has_key?(e)
36
+ raise "#{e} environment variable is required"
37
+ end
38
+ end
39
+ meta[:Version] = ENV['VERSION']
40
+ meta[:Release] = ENV['RELEASE']
41
+
42
+ place = ''
43
+ begin
44
+ place = 'require "git"'
45
+ eval place
46
+
47
+ # Assuming we are in the workspace ...
48
+ place = "Git.open('#{ENV['WORKSPACE']}')"
49
+ git = Git.open(ENV['WORKSPACE'], :log => @logger)
50
+ place = 'git.log'
51
+ meta[:Commit] = git.log[0].sha
52
+ place = 'git.current_branch'
53
+ meta[:Branch] = git.current_branch
54
+
55
+ @vars[:build_bra] = meta[:Branch].gsub(%r([/|]),'.')
56
+ @vars[:build_ver] = "#{meta[:Version]}"
57
+ @vars[:build_vrb] = "#{@vars[:build_ver]}-release-#{meta[:Release]}-#{@vars[:build_bra]}-#{@vars[:variant]}" #
58
+ @vars[:build_nam] = "#{@vars[:project_name]}-#{@vars[:build_vrb]}"
59
+ @vars[:build_rel] = "#{@vars[:build_nam]}-build-#{@vars[:build_num]}"
60
+ @vars[:build_dir] = "#{ENV['WORKSPACE']}/#{@vars[:build_rel]}"
61
+ @vars[:latest_pkg]= "#{@vars[:build_store]}/#{@vars[:build_rel]}.tar.gz"
62
+ @vars[:build_pkg] = "#{@vars[:build_rel]}.tar.gz"
63
+ @vars[:build_chk] = "#{@vars[:build_rel]}.checksum"
64
+ @vars[:build_mff] = "#{@vars[:build_rel]}.manifest"
65
+ @vars[:build_mdf] = "#{@vars[:build_rel]}.meta"
66
+ @vars[:build_mdd] = meta.dup
67
+ #noinspection RubyArgCount
68
+ @vars[:build_mds] = Digest::SHA256.hexdigest(meta.to_s)
69
+
70
+ @vars[:return_code] = 0
71
+
72
+ rescue Exception => e
73
+ @logger.error "#{e.class}:: '#{place}' - #{e.message}"
74
+ @vars[:return_code] = -98
75
+ end
76
+
77
+ @vars[:return_code]
78
+ end
79
+
80
+ # ---------------------------------------------------------------------------------------------------------------
81
+ def makeBuild()
82
+ if @vars.has_key?(:build_dir) and @vars.has_key?(:build_pkg)
83
+ begin
84
+ do_build = false
85
+ if File.exists?(@vars[:build_chk])
86
+ @vars[:build_sha] = IO.readlines(@vars[:build_chk])
87
+ unless @vars[:build_sha].is_a?(Array)
88
+ @logger.error "Unable to parse build checksum from #{@vars[:build_chk]}"
89
+ return -97
90
+ end
91
+ @vars[:build_sha] = @vars[:build_sha][0].chomp()
92
+ else
93
+ @vars[:build_sha] = ''
94
+ do_build = true
95
+ end
96
+ unless File.exists?(@vars[:build_pkg])
97
+ do_build = true
98
+ end
99
+ if do_build
100
+ @vars[:return_code] = cleanupBuild()
101
+ return @vars[:return_code] unless @vars[:return_code] == 0
102
+ @vars[:build_dte] = DateTime.now.strftime("%F %T%:z")
103
+ createMetaData()
104
+ @vars[:return_code] = packageBuild()
105
+ if 0 == @vars[:return_code]
106
+ @vars[:check_sha] = @vars[:build_sha]
107
+ @vars[:build_sha] = Digest::SHA256.file(@vars[:build_pkg]).hexdigest()
108
+ IO.write(@vars[:build_chk], @vars[:build_sha])
109
+ end
110
+ reportStatus()
111
+ reportResult()
112
+ else
113
+ reportStatus()
114
+
115
+ # No need to build again :)
116
+ @logger.info "NO_CHANGE: #{ENV['JOB_NAME']} #{ENV['BUILD_NUMBER']} #{@vars[:build_nam]} #{@vars[:build_pkg]} #{@vars[:build_chk]} [#{@vars[:build_sha]}]"
117
+ @vars[:return_code] = 0
118
+ return 1
119
+ end
120
+ rescue => e
121
+ @logger.error "#{e.class.name} #{e.message}"
122
+ @vars[:return_code] = -99
123
+ end
124
+ else
125
+ @logger.error ":build_dir or :build_pkg is unknown"
126
+ @vars[:return_code] = 2
127
+ end
128
+ @vars[:return_code]
129
+ end
130
+
131
+ # ---------------------------------------------------------------------------------------------------------------
132
+ def packageBuild()
133
+ excludes=%w(*.iml *.txt *.sh *.md .gitignore .editorconfig .jshintrc *.deprecated adminer doc)
134
+ excludes = excludes.map{ |e| "--exclude=#{@vars[:build_nam]}/#{e}" }.join(' ')
135
+ cmd = %(cd #{ENV['WORKSPACE']}; tar zcvf #{@vars[:build_pkg]} #{excludes} #{@vars[:build_nam]} 1>#{@vars[:build_pkg]}.manifest)
136
+ @logger.info cmd
137
+ logger_info = %x(#{cmd})
138
+ ret = $?.exitstatus
139
+ @logger.info logger_info
140
+ FileUtils.rmtree(@vars[:build_dir])
141
+ ret
142
+ end
143
+
144
+ # ---------------------------------------------------------------------------------------------------------------
145
+ def createMetaData()
146
+ @vars[:build_mdd].merge!({
147
+ :Generation => @options[:gen],
148
+ :Project => @vars[:project_name],
149
+ :Variant => @vars[:variant],
150
+ :Build => @vars[:build_num],
151
+ :Date => @vars[:build_dte],
152
+ :Builder => VERSION,
153
+
154
+ })
155
+ json = JSON.pretty_generate( @vars[:build_mdd], { indent: "\t", space: ' '})
156
+ IO.write(@vars[:build_mdf], json)
157
+ end
158
+
159
+ end
160
+ end
@@ -0,0 +1,18 @@
1
+ module CiCd
2
+ module Builder
3
+ #noinspection RubyStringKeysInHashInspection
4
+ LOGLEVELS = {
5
+ 'crit' => :fatal,
6
+ 'critical' => :fatal,
7
+ 'err' => :error,
8
+ 'error' => :error,
9
+ 'warn' => :warn,
10
+ 'warning' => :warn,
11
+ 'info' => :info,
12
+ 'debug' => :debug,
13
+ }
14
+
15
+ MYNAME = File.basename(__FILE__)
16
+
17
+ end
18
+ end
@@ -0,0 +1,194 @@
1
+ module CiCd
2
+ module Builder
3
+ require 'awesome_print'
4
+
5
+ # ---------------------------------------------------------------------------------------------------------------
6
+ def checkEnvironment()
7
+ # [2013-12-30 Christo] Detect CI ...
8
+ unless ENV.has_key?('JENKINS_HOME')
9
+ puts 'Sorry, your CI environment is not supported at this time (2013-12-30) ... Christo De Lange'
10
+ puts 'This script is developed for Jenkins so either you are not using Jenkins or you ran me outside of the CI ecosystem ...'
11
+ return 99
12
+ end
13
+
14
+ # Check for the necessary environment variables
15
+ map_keys = {}
16
+
17
+ @options[:env_keys].each { |k|
18
+ map_keys[k]= (not ENV.has_key?(k))
19
+ }
20
+ missing = map_keys.keys.select{ |k| map_keys[k] }
21
+
22
+ if missing.count() > 0
23
+ ap missing
24
+ raise Exception.new("Need environment variables: #{missing}")
25
+ end
26
+ 0
27
+ end
28
+
29
+ # ---------------------------------------------------------------------------------------------------------------
30
+ def getVars()
31
+ @vars ||= {}
32
+ @vars[:release] = 'latest'
33
+ @vars[:build_store] = '/tmp'
34
+ @vars[:variant] = 'SNAPSHOT'
35
+
36
+ if ENV.has_key?('PROJECT_NAME')
37
+ @vars[:project_name] = ENV['PROJECT_NAME']
38
+ end
39
+
40
+ if ENV.has_key?('RELEASE')
41
+ @vars[:release] = ENV['RELEASE']
42
+ end
43
+
44
+ if ENV.has_key?('BUILD_STORE')
45
+ @vars[:build_store] = "#{ENV['BUILD_STORE']}"
46
+ end
47
+
48
+ if ENV.has_key?('VARIANT')
49
+ @vars[:variant] = "#{ENV['VARIANT']}"
50
+ end
51
+
52
+ if ENV.has_key?('BUILD_NUMBER')
53
+ @vars[:build_num] = "#{ENV['BUILD_NUMBER']}"
54
+ end
55
+
56
+ @vars[:return_code] = getLatest()
57
+ end
58
+
59
+ def getLatest
60
+ ret = 0
61
+ @vars[:vars_fil] = "#{@vars[:build_store]}/#{ENV['JOB_NAME']}-#{@vars[:variant]}.env"
62
+ @vars[:latest_fil] = "#{@vars[:build_store]}/#{ENV['JOB_NAME']}-#{@vars[:variant]}.latest"
63
+ @vars[:latest_ver] = ''
64
+ @vars[:latest_sha] = ''
65
+ @vars[:latest_pkg] = ''
66
+ if @vars[:build_nam]
67
+ @vars[:latest_pkg]= "#{@vars[:build_store]}/#{@vars[:build_nam]}.tar.gz"
68
+ end
69
+ if File.exists?(@vars[:latest_fil])
70
+ @vars[:latest_ver] = IO.readlines(@vars[:latest_fil])
71
+ unless @vars[:latest_ver].is_a?(Array)
72
+ @logger.error "Unable to parse latest version from #{@vars[:latest_fil]}"
73
+ ret = -97
74
+ end
75
+ @vars[:latest_sha] = @vars[:latest_ver][1].chomp() if (@vars[:latest_ver].length > 1)
76
+ @vars[:latest_ver] = @vars[:latest_ver][0].chomp()
77
+ end
78
+ ret
79
+ end
80
+
81
+ # ---------------------------------------------------------------------------------------------------------------
82
+ def saveEnvironment(ignored=[])
83
+ @logger.info "Save environment to #{@vars[:vars_fil]}"
84
+ vstr = ['[global]']
85
+ ENV.to_hash.sort.each{|k,v|
86
+ vstr << %(#{k}="#{v}") unless ignored.include?(k)
87
+ }
88
+
89
+ IO.write(@vars[:vars_fil], vstr.join("\n"))
90
+ end
91
+
92
+ # ---------------------------------------------------------------------------------------------------------------
93
+ def saveBuild()
94
+ begin
95
+ raise 'ERROR: Checksum not read' unless @vars.has_key?(:latest_sha)
96
+ raise 'ERROR: Checksum not calculated' unless @vars.has_key?(:build_sha)
97
+ change = false
98
+ if @vars[:latest_sha] != @vars[:build_sha]
99
+ change = true
100
+ @logger.info "CHANGE: Checksum [#{@vars[:latest_sha]}] => [#{@vars[:build_sha]}]"
101
+ end
102
+ if @vars[:latest_ver] != @vars[:build_ver]
103
+ change = true
104
+ @logger.info "CHANGE: Release [#{@vars[:latest_ver]}] => [#{@vars[:build_ver]}]"
105
+ end
106
+ unless File.file?(@vars[:build_pkg])
107
+ change = true
108
+ @logger.info "CHANGE: No #{@vars[:build_pkg]}"
109
+ end
110
+ unless File.symlink?(@vars[:latest_pkg])
111
+ change = true
112
+ @logger.info "CHANGE: No #{@vars[:latest_pkg]}"
113
+ end
114
+
115
+ if change
116
+ if @vars[:latest_pkg] != @vars[:build_pkg]
117
+ @logger.info "Link #{@vars[:latest_pkg]} to #{@vars[:build_pkg]}"
118
+ begin
119
+ File.unlink(@vars[:latest_pkg])
120
+ rescue
121
+ # noop
122
+ end
123
+ File.symlink(@vars[:build_pkg], @vars[:latest_pkg])
124
+ end
125
+ @logger.info "Save latest build info to #{@vars[:latest_fil]}"
126
+ IO.write(@vars[:latest_fil], "#{@vars[:build_ver]}\n#{@vars[:build_sha]}")
127
+ saveEnvironment(['LS_COLORS','AWS_ACCESS_KEY_ID','AWS_SECRET_ACCESS_KEY'])
128
+ # NOTE the '.note'!
129
+ @logger.note "CHANGE: #{ENV['JOB_NAME']} (#{@vars[:build_ver]}[#{@vars[:build_sha]}])"
130
+
131
+ else
132
+ @logger.info "Artifact #{@vars[:latest_pkg]} unchanged (#{@vars[:latest_ver]} [#{@vars[:latest_sha]}])"
133
+ @logger.info "NO_CHANGE: #{ENV['JOB_NAME']} #{@vars[:latest_ver]}"
134
+ end
135
+ @vars[:return_code] = 0
136
+ rescue => e
137
+ @logger.error "#{e.backtrace[0]}: #{e.class.name} #{e.message}"
138
+ @vars[:return_code] = 2
139
+ end
140
+ @vars[:return_code]
141
+ end
142
+
143
+ def reportResult()
144
+ if 0 == @vars[:return_code]
145
+ # NOTE the '.note'!
146
+ @logger.note "CHANGE: #{ENV['JOB_NAME']} #{ENV['BUILD_NUMBER']} #{@vars[:build_nam]} (#{@vars[:build_pkg]}) [#{@vars[:check_sha]}] => [#{@vars[:build_sha]}]"
147
+ else
148
+ @logger.error "FAILURE: #{ENV['JOB_NAME']} #{ENV['BUILD_NUMBER']} #{@vars[:build_pkg]} #{@vars[:return_code]}"
149
+ end
150
+ end
151
+
152
+ # ---------------------------------------------------------------------------------------------------------------
153
+ def reportStatus(ignored=[])
154
+ # [2013-12-30 Christo] Report status,environment, etc.
155
+
156
+ if @logger.level < ::Logging::LEVELS['warn']
157
+ @logger.info '='*100
158
+ @logger.info Dir.getwd()
159
+ @logger.info '='*100
160
+
161
+ @logger.info "Config:"
162
+ @options.each{|k,v|
163
+ unless ignored.include?(k)
164
+ @logger.info sprintf("%25s: %s", "#{k.to_s}", "#{v.to_s}")
165
+ end
166
+ }
167
+
168
+ @logger.info '='*100
169
+
170
+ @logger.info "Parameters:"
171
+ @vars.sort.each{|k,v|
172
+ unless ignored.include?(k)
173
+ @logger.info sprintf("%25s: %s", "#{k.to_s}", "#{v.to_s}")
174
+ end
175
+ }
176
+
177
+ @logger.info '='*100
178
+ end
179
+
180
+ if @logger.level < ::Logging::LEVELS['info']
181
+ @logger.debug '='*100
182
+ @logger.debug "Environment:"
183
+ ENV.sort.each{|k,v|
184
+ unless ignored.include?(k)
185
+ @logger.debug sprintf("%25s: %s", "#{k.to_s}", "#{v.to_s}")
186
+ end
187
+ }
188
+
189
+ @logger.debug '='*100
190
+ end
191
+ end
192
+
193
+ end
194
+ end