cicd-builder 0.9.23 → 0.9.25
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +136 -0
- data/cicd-builder.gemspec +14 -12
- data/lib/cicd/builder.rb +23 -20
- data/lib/cicd/builder/mixlib/build.rb +144 -69
- data/lib/cicd/builder/mixlib/environment.rb +51 -17
- data/lib/cicd/builder/mixlib/errors.rb +35 -0
- data/lib/cicd/builder/mixlib/options.rb +14 -8
- data/lib/cicd/builder/mixlib/repo.rb +42 -383
- data/lib/cicd/builder/mixlib/repo/S3.rb +355 -0
- data/lib/cicd/builder/mixlib/repo/artifactory.rb +237 -0
- data/lib/cicd/builder/mixlib/repo/base.rb +102 -0
- data/lib/cicd/builder/mixlib/utils.rb +11 -1
- data/lib/cicd/builder/version.rb +1 -1
- metadata +132 -16
@@ -0,0 +1,355 @@
|
|
1
|
+
require 'aws-sdk-core'
|
2
|
+
require 'aws-sdk-resources'
|
3
|
+
|
4
|
+
module CiCd
|
5
|
+
module Builder
|
6
|
+
module Repo
|
7
|
+
class S3 < CiCd::Builder::Repo::Base
|
8
|
+
|
9
|
+
# ---------------------------------------------------------------------------------------------------------------
|
10
|
+
def initialize(builder)
|
11
|
+
raise "Missing variable AWS_S3_BUCKET" unless ENV.has_key?('AWS_S3_BUCKET')
|
12
|
+
super(builder)
|
13
|
+
end
|
14
|
+
|
15
|
+
# ---------------------------------------------------------------------------------------------------------------
|
16
|
+
def getS3()
|
17
|
+
region = ENV['AWS_REGION'] || ::Aws.config[:region] || 'us-east-1'
|
18
|
+
unless @s3
|
19
|
+
@s3 = ::Aws::S3::Client.new(region: region)
|
20
|
+
end
|
21
|
+
unless @s3 and ((@s3.config.access_key_id and @s3.config.secret_access_key) or @s3.config.credentials)
|
22
|
+
@logger.warn "Unable to find AWS credentials in standard locations:
|
23
|
+
ENV['AWS_ACCESS_KEY'] and ENV['AWS_SECRET_ACCESS_KEY']
|
24
|
+
Aws.config[:credentials]
|
25
|
+
Shared credentials file, ~/.aws/credentials
|
26
|
+
EC2 Instance profile
|
27
|
+
"
|
28
|
+
if ENV['AWS_PROFILE']
|
29
|
+
@logger.info "Trying profile '#{ENV['AWS_PROFILE']}' explicitly"
|
30
|
+
creds = Aws::SharedCredentials.new( path: File.expand_path('~/.aws/credentials'), profile: ENV['AWS_PROFILE'] )
|
31
|
+
if creds.loadable?
|
32
|
+
@s3 = ::Aws::S3::Client.new(region: region, credentials: creds)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
@logger.warn 'No AWS_PROFILE defined'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
unless @s3 and ((@s3.config.access_key_id and @s3.config.secret_access_key) or @s3.config.credentials)
|
39
|
+
raise 'Unable to find AWS credentials!'
|
40
|
+
end
|
41
|
+
@s3
|
42
|
+
end
|
43
|
+
|
44
|
+
# ---------------------------------------------------------------------------------------------------------------
|
45
|
+
def uploadToRepo(artifacts)
|
46
|
+
s3 = getS3()
|
47
|
+
artifacts.each{|art|
|
48
|
+
|
49
|
+
s3_obj = maybeS3Object(art[:key], s3)
|
50
|
+
upload = false
|
51
|
+
md5 = nil
|
52
|
+
if art[:data][:data]
|
53
|
+
# md5 = Digest::MD5.hexdigest(art[:data][:data])
|
54
|
+
tempArtifactFile('artifact', art[:data])
|
55
|
+
end
|
56
|
+
if s3_obj.nil?
|
57
|
+
upload = true
|
58
|
+
else
|
59
|
+
@logger.info "s3://#{ENV['AWS_S3_BUCKET']}/#{art[:key]} exists"
|
60
|
+
etag = s3_obj.etag.gsub(/"/, '')
|
61
|
+
md5 = if art[:data].has_key?(:file)
|
62
|
+
# md5 = Digest::MD5.file(art[:data][:file]).hexdigest
|
63
|
+
calcLocalETag(etag, art[:data][:file])
|
64
|
+
else
|
65
|
+
raise "Internal error: No :file in #{art[:data].ai}"
|
66
|
+
end
|
67
|
+
unless etag == md5
|
68
|
+
checksum = s3_obj.metadata[:checksum]
|
69
|
+
unless checksum and checksum == md5
|
70
|
+
@logger.warn "s3://#{ENV['AWS_S3_BUCKET']}/#{art[:key]} is different from our #{art[:key]}(#{s3_obj.etag} <=> #{md5})"
|
71
|
+
upload = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
if upload
|
77
|
+
@logger.info "Upload new s3://#{ENV['AWS_S3_BUCKET']}/#{art[:key]}"
|
78
|
+
# Get size before upload changes our object
|
79
|
+
body = nil
|
80
|
+
if art[:data].has_key?(:file)
|
81
|
+
size = File.size(art[:data][:file])
|
82
|
+
body = File.open(art[:data][:file], 'r')
|
83
|
+
else
|
84
|
+
# size = art[:data][:data].length
|
85
|
+
# body = art[:data][:data]
|
86
|
+
raise "Internal error: No :file in #{art[:data].ai}"
|
87
|
+
end
|
88
|
+
art[:data][:metadata] = {checksum: md5, digest: "md5=#{md5}"}
|
89
|
+
# art[:data][:'x-amz-meta-digest'] = "md5=#{md5}"
|
90
|
+
res = s3.put_object( bucket: ENV['AWS_S3_BUCKET'],
|
91
|
+
key: art[:key],
|
92
|
+
body: body,
|
93
|
+
# acl: 'authenticated-read',
|
94
|
+
content_length: size,
|
95
|
+
metadata: art[:data][:metadata],
|
96
|
+
)
|
97
|
+
s3_obj = maybeS3Object(art[:key], s3)
|
98
|
+
raise "Failed to upload '#{art[:key]}'" unless s3_obj
|
99
|
+
if art.has_key?(:public_url)
|
100
|
+
@vars[art[:public_url]] = s3_obj.public_url
|
101
|
+
end
|
102
|
+
if art.has_key?(:read_url)
|
103
|
+
@vars[art[:read_url]] = s3_obj.presigned_url(:get, expires_in: 86400)
|
104
|
+
@logger.info "#{art[:label]}: #{@vars[art[:read_url]]}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
if art[:data][:temp]
|
108
|
+
File.unlink(art[:data][:file])
|
109
|
+
end
|
110
|
+
}
|
111
|
+
0
|
112
|
+
end
|
113
|
+
|
114
|
+
def maybeS3Object(key, s3 = nil)
|
115
|
+
s3 ||= getS3()
|
116
|
+
s3_obj = begin
|
117
|
+
obj = ::Aws::S3::Object.new(bucket_name: ENV['AWS_S3_BUCKET'], key: key, client: s3)
|
118
|
+
obj.etag
|
119
|
+
obj
|
120
|
+
rescue Aws::S3::Errors::NotFound
|
121
|
+
nil
|
122
|
+
rescue Aws::S3::Errors::NoSuchKey
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
s3_obj
|
126
|
+
end
|
127
|
+
|
128
|
+
# ---------------------------------------------------------------------------------------------------------------
|
129
|
+
def takeInventory()
|
130
|
+
def _update(hash, key, value)
|
131
|
+
h = {}
|
132
|
+
i = -1
|
133
|
+
hash[key].each { |v| h[v] = i+=1 }
|
134
|
+
unless h.has_key?(value)
|
135
|
+
h[value] = h.keys.size # No -1 because this is evaluated BEFORE we make the addition!
|
136
|
+
end
|
137
|
+
s = h.sort_by { |_, v| v }
|
138
|
+
s = s.map { |v| v[0] }
|
139
|
+
hash[key] = s
|
140
|
+
h[value]
|
141
|
+
end
|
142
|
+
|
143
|
+
# Read and parse in JSON
|
144
|
+
json_s = ''
|
145
|
+
json = nil
|
146
|
+
varianth = nil
|
147
|
+
|
148
|
+
key = "#{@vars[:project_name]}/INVENTORY.json"
|
149
|
+
s3_obj = maybeS3Object(key)
|
150
|
+
# If the inventory has started then add to it else create a new one
|
151
|
+
if s3_obj.nil?
|
152
|
+
# Start a new inventory
|
153
|
+
over = true
|
154
|
+
else
|
155
|
+
resp = s3_obj.get()
|
156
|
+
body = resp.body
|
157
|
+
if body.is_a?(String)
|
158
|
+
json_s = resp.data
|
159
|
+
else
|
160
|
+
body.rewind
|
161
|
+
json_s = body.read()
|
162
|
+
end
|
163
|
+
json = Yajl::Parser.parse(json_s)
|
164
|
+
over = false
|
165
|
+
# Is the inventory format up to date ...
|
166
|
+
constraint = ::Semverse::Constraint.new "<= #{@options[:gen]}"
|
167
|
+
version = ::Semverse::Version.new(json['gen'])
|
168
|
+
# raise CiCd::Builder::Errors::InvalidVersion.new "The constraint failed: #{json['gen']} #{constraint}"
|
169
|
+
|
170
|
+
unless constraint.satisfies?(version)
|
171
|
+
raise CiCd::Builder::Errors::InvalidVersion.new "The inventory generation is newer than I can manage: #{version} <=> #{@options[:gen]}"
|
172
|
+
end
|
173
|
+
if json['container'] and json['container']['variants']
|
174
|
+
# but does not have our variant then add it
|
175
|
+
variants = json['container']['variants']
|
176
|
+
unless variants[@vars[:variant]]
|
177
|
+
variants[@vars[:variant]] = {}
|
178
|
+
varianth = variants[@vars[:variant]]
|
179
|
+
varianth['builds'] = []
|
180
|
+
varianth['branches'] = []
|
181
|
+
varianth['versions'] = []
|
182
|
+
varianth['releases'] = []
|
183
|
+
varianth['latest'] = {
|
184
|
+
branch: -1,
|
185
|
+
version: -1,
|
186
|
+
build: -1,
|
187
|
+
release: -1,
|
188
|
+
}
|
189
|
+
end
|
190
|
+
varianth = variants[@vars[:variant]]
|
191
|
+
# If the inventory 'latest' format is up to date ...
|
192
|
+
unless varianth['latest'] and
|
193
|
+
varianth['latest'].is_a?(Hash)
|
194
|
+
# Start over ... too old/ incompatible
|
195
|
+
over = true
|
196
|
+
end
|
197
|
+
else
|
198
|
+
# Start over ... too old/ incompatible
|
199
|
+
over = true
|
200
|
+
end
|
201
|
+
end
|
202
|
+
# Starting fresh ?
|
203
|
+
if over or json.nil?
|
204
|
+
json_s = initInventory()
|
205
|
+
else
|
206
|
+
raise CiCd::Builder::Errors::Internal.new sprintf('Internal logic error! %s::%d', __FILE__,__LINE__) if varianth.nil?
|
207
|
+
# Add the new build if we don't have it
|
208
|
+
unless varianth['builds'].map { |b| b['build_name'] }.include?(@vars[:build_nmn])
|
209
|
+
#noinspection RubyStringKeysInHashInspection
|
210
|
+
filing = {
|
211
|
+
'drawer' => @vars[:build_nam],
|
212
|
+
'build_name' => @vars[:build_nmn],
|
213
|
+
'build_number' => @vars[:build_num],
|
214
|
+
'release' => @vars[:release],
|
215
|
+
}
|
216
|
+
if @vars.has_key?(:artifacts)
|
217
|
+
filing['artifacts'] = @vars[:artifacts].map { |artifact| File.basename(artifact[:key]) }
|
218
|
+
end
|
219
|
+
assembly = json['container']['assembly'] or raise("Expected an 'assembly'")
|
220
|
+
if assembly['extension'] != !vars[:build_ext]
|
221
|
+
# noinspection RubyStringKeysInHashInspection
|
222
|
+
filing['assembly'] = {
|
223
|
+
'extension' => @vars[:build_ext],
|
224
|
+
'type' => 'tarbzip2'
|
225
|
+
}
|
226
|
+
end
|
227
|
+
varianth['builds'] << filing
|
228
|
+
end
|
229
|
+
build_lst = (varianth['builds'].size-1)
|
230
|
+
build_rel = build_lst
|
231
|
+
i = -1
|
232
|
+
varianth['builds'].each{ |h|
|
233
|
+
i += 1
|
234
|
+
convert_build(h)
|
235
|
+
convert_build(varianth['builds'][build_rel])
|
236
|
+
if h['release'].to_i > varianth['builds'][build_rel]['release'].to_i
|
237
|
+
build_rel = i
|
238
|
+
elsif h['release'] == varianth['builds'][build_rel]['release']
|
239
|
+
build_rel = i if h['build_number'].to_i > varianth['builds'][build_rel]['build_number'].to_i
|
240
|
+
end
|
241
|
+
}
|
242
|
+
|
243
|
+
# Add new branch ...
|
244
|
+
build_bra = _update(varianth, 'branches', @vars[:build_bra])
|
245
|
+
# Add new version ...
|
246
|
+
build_ver = _update(varianth, 'versions', @vars[:build_ver])
|
247
|
+
|
248
|
+
# Set latest
|
249
|
+
varianth['latest'] = {
|
250
|
+
branch: build_bra,
|
251
|
+
version: build_ver,
|
252
|
+
build: build_lst,
|
253
|
+
release: build_rel,
|
254
|
+
}
|
255
|
+
json['gen'] = @options[:gen]
|
256
|
+
json_s = JSON.pretty_generate( json, { indent: "\t", space: ' '})
|
257
|
+
end
|
258
|
+
begin
|
259
|
+
md5 = Digest::MD5.hexdigest(json_s)
|
260
|
+
# [:'x-amz-meta-digest'] = "md5=#{md5}"
|
261
|
+
resp = getS3.put_object( bucket: ENV['AWS_S3_BUCKET'],
|
262
|
+
key: key,
|
263
|
+
body: json_s,
|
264
|
+
# acl: 'authenticated-read',
|
265
|
+
metadata: {checksum: md5, digest: "md5=#{md5}"},
|
266
|
+
)
|
267
|
+
s3_obj = maybeS3Object(key)
|
268
|
+
# s3_obj.etag
|
269
|
+
@logger.info "Inventory URL: #{s3_obj.presigned_url(:get, expires_in: 86400)}"
|
270
|
+
return 0
|
271
|
+
rescue Exception => e
|
272
|
+
@logger.error("Exception: #{e.class.name}: #{e.message}\n#{e.backtrace.ai}")
|
273
|
+
return Errors::INVENTORY_UPLOAD_EXCEPTION
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def convert_build(h)
|
278
|
+
if h.has_key?('number')
|
279
|
+
h['build_number'] = h['number']
|
280
|
+
h.delete 'number'
|
281
|
+
elsif h.has_key?('build_number')
|
282
|
+
h.delete 'number'
|
283
|
+
else
|
284
|
+
h_build = h.has_key?('build') ? h['build'] : h['build_name']
|
285
|
+
h_number = h_build.gsub(/^.*?-build-([0-9]+)$/, '\1').to_i
|
286
|
+
|
287
|
+
h['build_number'] = h_number
|
288
|
+
h['build_name'] = h_build
|
289
|
+
h.delete 'build'
|
290
|
+
h.delete 'number'
|
291
|
+
end
|
292
|
+
if h.has_key?('build')
|
293
|
+
h_build = h.has_key?('build')
|
294
|
+
h_number = h_build.gsub(/^.*?-build-([0-9]+)$/, '\1').to_i
|
295
|
+
|
296
|
+
h['build_number'] = h_number
|
297
|
+
h['build_name'] = h_build
|
298
|
+
h.delete 'build'
|
299
|
+
h.delete 'number'
|
300
|
+
end
|
301
|
+
h
|
302
|
+
end
|
303
|
+
|
304
|
+
# ---------------------------------------------------------------------------------------------------------------
|
305
|
+
def uploadBuildArtifacts()
|
306
|
+
if @vars.has_key?(:build_dir) and @vars.has_key?(:build_pkg)
|
307
|
+
artifacts = @vars[:artifacts] rescue []
|
308
|
+
|
309
|
+
key = getKey()
|
310
|
+
if File.exists?(@vars[:build_pkg])
|
311
|
+
# Store the assembly - be sure to inherit possible overrides in pkg name and ext but dictate the drawer!
|
312
|
+
artifacts << {
|
313
|
+
key: "#{File.join(File.dirname(key),File.basename(@vars[:build_pkg]))}",
|
314
|
+
data: {:file => @vars[:build_pkg]},
|
315
|
+
public_url: :build_url,
|
316
|
+
label: 'Package URL'
|
317
|
+
}
|
318
|
+
else
|
319
|
+
@logger.warn "Skipping upload of missing artifact: '#{@vars[:build_pkg]}'"
|
320
|
+
end
|
321
|
+
|
322
|
+
# Store the metadata
|
323
|
+
manifest = manifestMetadata()
|
324
|
+
artifacts << {
|
325
|
+
key: "#{key}.MANIFEST.json",
|
326
|
+
data: {:data => manifest},
|
327
|
+
public_url: :manifest_url,
|
328
|
+
read_url: :manifest_url,
|
329
|
+
label: 'Manifest URL'
|
330
|
+
}
|
331
|
+
|
332
|
+
# Store the checksum
|
333
|
+
artifacts << {
|
334
|
+
key: "#{@vars[:project_name]}/#{@vars[:variant]}/#{@vars[:build_nam]}/#{@vars[:build_nmn]}.checksum",
|
335
|
+
data: {:data => @vars[:build_sha]},
|
336
|
+
public_url: :checksum_url,
|
337
|
+
read_url: :checksum_url,
|
338
|
+
label: 'Checksum URL'
|
339
|
+
}
|
340
|
+
|
341
|
+
@vars[:return_code] = uploadToRepo(artifacts)
|
342
|
+
if 0 == @vars[:return_code]
|
343
|
+
@vars[:return_code] = takeInventory()
|
344
|
+
end
|
345
|
+
@vars[:return_code]
|
346
|
+
else
|
347
|
+
@vars[:return_code] = Errors::NO_ARTIFACTS
|
348
|
+
end
|
349
|
+
@vars[:return_code]
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'artifactory'
|
2
|
+
|
3
|
+
module CiCd
|
4
|
+
module Builder
|
5
|
+
module Repo
|
6
|
+
class Artifactory < CiCd::Builder::Repo::Base
|
7
|
+
# include ::Artifactory::Resource
|
8
|
+
|
9
|
+
# ---------------------------------------------------------------------------------------------------------------
|
10
|
+
def initialize(builder)
|
11
|
+
# Check for the necessary environment variables
|
12
|
+
map_keys = {}
|
13
|
+
|
14
|
+
%w[ARTIFACTORY_ENDPOINT ARTIFACTORY_USERNAME ARTIFACTORY_PASSWORD ARTIFACTORY_REPO].each { |k|
|
15
|
+
map_keys[k]= (not ENV.has_key?(k) or ENV[k].empty?)
|
16
|
+
}
|
17
|
+
missing = map_keys.keys.select{ |k| map_keys[k] }
|
18
|
+
|
19
|
+
if missing.count() > 0
|
20
|
+
raise("Need these environment variables: #{missing.ai}")
|
21
|
+
end
|
22
|
+
|
23
|
+
super(builder)
|
24
|
+
|
25
|
+
# ::Artifactory.configure do |config|
|
26
|
+
# # The endpoint for the Artifactory server. If you are running the "default"
|
27
|
+
# # Artifactory installation using tomcat, don't forget to include the
|
28
|
+
# # +/artifactoy+ part of the URL.
|
29
|
+
# config.endpoint = artifactory_endpoint()
|
30
|
+
#
|
31
|
+
# # The basic authentication information. Since this uses HTTP Basic Auth, it
|
32
|
+
# # is highly recommended that you run Artifactory over SSL.
|
33
|
+
# config.username = ENV['ARTIFACTORY_USERNAME']
|
34
|
+
# config.password = ENV['ARTIFACTORY_PASSWORD']
|
35
|
+
#
|
36
|
+
# # Speaking of SSL, you can specify the path to a pem file with your custom
|
37
|
+
# # certificates and the gem will wire it all up for you (NOTE: it must be a
|
38
|
+
# # valid PEM file).
|
39
|
+
# # config.ssl_pem_file = '/path/to/my.pem'
|
40
|
+
#
|
41
|
+
# # Or if you are feelying frisky, you can always disable SSL verification
|
42
|
+
# # config.ssl_verify = false
|
43
|
+
#
|
44
|
+
# # You can specify any proxy information, including any authentication
|
45
|
+
# # information in the URL.
|
46
|
+
# # config.proxy_username = 'user'
|
47
|
+
# # config.proxy_password = 'password'
|
48
|
+
# # config.proxy_address = 'my.proxy.server'
|
49
|
+
# # config.proxy_port = '8080'
|
50
|
+
# end
|
51
|
+
@client = ::Artifactory::Client.new()
|
52
|
+
end
|
53
|
+
|
54
|
+
# ---------------------------------------------------------------------------------------------------------------
|
55
|
+
def method_missing(name, *args)
|
56
|
+
if name =~ %r'^artifactory_'
|
57
|
+
key = name.to_s.upcase
|
58
|
+
raise "ENV has no key #{key}" unless ENV.has_key?(key)
|
59
|
+
ENV[key]
|
60
|
+
else
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# ---------------------------------------------------------------------------------------------------------------
|
66
|
+
def uploadToRepo(artifacts)
|
67
|
+
# Set a few build properties on the endpoint URL
|
68
|
+
@properties_matrix = {
|
69
|
+
:'build.name' => @vars[:build_mdd][:Project],
|
70
|
+
:'build.number' => @vars[:build_mdd][:Build],
|
71
|
+
:'vcs.revision' => @vars[:build_mdd][:Commit]
|
72
|
+
}
|
73
|
+
@vars[:build_mdd].each do |k,v|
|
74
|
+
@properties_matrix["build.#{k.downcase}"] = v
|
75
|
+
end
|
76
|
+
# matrix = properties.map{|k,v| (v.nil? or v.empty?) ? nil : "#{k}=#{v}"}.join("\;").gsub(%r'^\;*(.*?)\;*$', '\1')
|
77
|
+
# @client.endpoint += ";#{matrix}"
|
78
|
+
manifest = {}
|
79
|
+
artifacts.each{|art|
|
80
|
+
data = art[:data]
|
81
|
+
objects = maybeArtifactoryObject(data)
|
82
|
+
upload = false
|
83
|
+
if data.has_key?(:data)
|
84
|
+
tempArtifactFile("manifest-#{data[:name]}", data)
|
85
|
+
end
|
86
|
+
if data.has_key?(:file)
|
87
|
+
sha1 = Digest::SHA1.file(data[:file]).hexdigest
|
88
|
+
md5 = Digest::MD5.file(data[:file]).hexdigest
|
89
|
+
else
|
90
|
+
raise 'Artifact does not have file or data?'
|
91
|
+
end
|
92
|
+
file_name = File.basename(data[:file])
|
93
|
+
if file_name =~ %r'^#{data[:name]}'
|
94
|
+
file_name.gsub!(%r'^#{data[:name]}\.*','')
|
95
|
+
end
|
96
|
+
file_name.gsub!(%r'\.*-*#{data[:version]}','')
|
97
|
+
file_name.gsub!(%r'\.*-*#{data[:build]}-*','')
|
98
|
+
file_ext = file_name.dup
|
99
|
+
file_ext.gsub!(%r'^.*?\.*(tar\.gz|tgz|tar\.bzip2|bzip2|tar\.bz2|bz2|jar|war|groovy)$','\1')
|
100
|
+
unless file_ext.empty?
|
101
|
+
file_name.gsub!(%r'\.*#{file_ext}$','')
|
102
|
+
end
|
103
|
+
if file_name =~ %r'\.+'
|
104
|
+
raise "Unable to parse out file name in #{data[:file]}"
|
105
|
+
end
|
106
|
+
unless file_name.empty? or file_name.match(%r'^-')
|
107
|
+
file_name = "-#{file_name}"
|
108
|
+
end
|
109
|
+
artifact_name = "#{data[:name]}-#{data[:version]}#{file_name}-#{data[:build]}.#{file_ext}" # -#{@vars[:variant]}
|
110
|
+
artifact_path = "#{artifactory_org_path()}/#{data[:name]}/#{data[:version]}-#{@vars[:variant]}/#{artifact_name}"
|
111
|
+
manifest[data[:name]] = artifact_path
|
112
|
+
if objects.nil? or objects.size == 0
|
113
|
+
upload = true
|
114
|
+
else
|
115
|
+
@logger.info "#{artifactory_endpoint()}/#{artifactory_repo()}/#{artifact_path} exists - #{objects.size} results"
|
116
|
+
# Check the checksum of the artifact
|
117
|
+
matched = false
|
118
|
+
objects.each do |artifact|
|
119
|
+
@logger.debug "\tChecking: #{artifact.attributes.ai} for #{artifact_path}"
|
120
|
+
if artifact.uri.match(%r'#{artifact_path}$')
|
121
|
+
matched = true
|
122
|
+
@logger.info "\tMatched: #{artifact.attributes.select{|k,_| k != :client}.ai}"
|
123
|
+
if artifact.md5 != md5 or artifact.sha1 != sha1
|
124
|
+
upload = true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
upload ||= (not matched)
|
129
|
+
end
|
130
|
+
|
131
|
+
if upload
|
132
|
+
data[:properties] = @properties_matrix
|
133
|
+
uploadArtifact(artifact_path, data, md5, sha1)
|
134
|
+
else
|
135
|
+
@logger.info "Keep existing #{artifactory_endpoint()}/#{artifact_path}"
|
136
|
+
end
|
137
|
+
if data[:temp]
|
138
|
+
File.unlink(data[:file])
|
139
|
+
end
|
140
|
+
}
|
141
|
+
manifest_data = ''
|
142
|
+
manifest.each do |k,v|
|
143
|
+
manifest_data += "#{k}=#{v}\n"
|
144
|
+
end
|
145
|
+
data = { data: manifest_data, version: @vars[:build_ver], build: @vars[:build_num], properties: @properties_matrix }
|
146
|
+
tempArtifactFile('manifest', data)
|
147
|
+
sha1 = Digest::SHA1.file(data[:file]).hexdigest
|
148
|
+
md5 = Digest::MD5.file(data[:file]).hexdigest
|
149
|
+
artifact_name = "#{artifactory_manifest_name}-#{data[:version]}-#{data[:build]}.properties"
|
150
|
+
artifact_path = "#{artifactory_org_path()}/#{artifactory_manifest_module}/#{data[:version]}-#{@vars[:variant]}/#{artifact_name}"
|
151
|
+
uploadArtifact(artifact_path, data, md5, sha1)
|
152
|
+
if data[:temp]
|
153
|
+
File.unlink(data[:file])
|
154
|
+
end
|
155
|
+
0
|
156
|
+
end
|
157
|
+
|
158
|
+
def uploadArtifact(artifact_path, data, md5, sha1)
|
159
|
+
data[:size] = File.size(data[:file])
|
160
|
+
@logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S %z')}] Start upload #{artifact_path} = #{data[:size]} bytes"
|
161
|
+
artifact = ::Artifactory::Resource::Artifact.new(local_path: data[:file], client: @client)
|
162
|
+
# noinspection RubyStringKeysInHashInspection
|
163
|
+
artifact.checksums = {
|
164
|
+
'md5' => md5,
|
165
|
+
'sha1' => sha1
|
166
|
+
}
|
167
|
+
artifact.size = data[:size]
|
168
|
+
result = artifact.upload(artifactory_repo(), "#{artifact_path}", data[:properties] || {})
|
169
|
+
@logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S %z')}] Uploaded: #{result.attributes.select { |k, _| k != :client }.ai}"
|
170
|
+
artifact.upload_checksum(artifactory_repo(), "#{artifact_path}", :sha1, sha1)
|
171
|
+
artifact.upload_checksum(artifactory_repo(), "#{artifact_path}", :md5, md5)
|
172
|
+
objects = maybeArtifactoryObject(data, false)
|
173
|
+
raise "Failed to upload '#{artifact_path}'" unless objects.size > 0
|
174
|
+
end
|
175
|
+
|
176
|
+
def maybeArtifactoryObject(data,wide=true)
|
177
|
+
begin
|
178
|
+
# Get a list of matching artifacts in this repository
|
179
|
+
result = @client.artifact_gavc_search(group: artifactory_org_path(), name: data[:name], version: "#{data[:version]}-#{@vars[:variant]}", repos: [artifactory_repo()])
|
180
|
+
if result.size > 0
|
181
|
+
@logger.info "Artifactory gavc_search match g=#{artifactory_org_path()},a=#{data[:name]},v=#{data[:version]}-#{@vars[:variant]},r=#{artifactory_repo()}: #{result}"
|
182
|
+
# raise "GAVC started working: #{result.ai}"
|
183
|
+
elsif wide
|
184
|
+
@logger.warn 'GAVC search came up empty!'
|
185
|
+
result = @client.artifact_search(name: data[:name], repos: [artifactory_repo()])
|
186
|
+
@logger.info "Artifactory search match a=#{data[:name]},r=#{artifactory_repo()}: #{result}"
|
187
|
+
end
|
188
|
+
result
|
189
|
+
rescue Exception => e
|
190
|
+
@logger.error "Artifactory error: #{e.class.name} #{e.message}"
|
191
|
+
raise e
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# ---------------------------------------------------------------------------------------------------------------
|
196
|
+
def uploadBuildArtifacts()
|
197
|
+
if @vars.has_key?(:build_dir) and @vars.has_key?(:build_pkg)
|
198
|
+
begin
|
199
|
+
artifacts = @vars[:artifacts] rescue []
|
200
|
+
|
201
|
+
key = getKey()
|
202
|
+
if File.exists?(@vars[:build_pkg])
|
203
|
+
# Store the assembly - be sure to inherit possible overrides in pkg name and ext but dictate the drawer!
|
204
|
+
artifacts << {
|
205
|
+
key: "#{File.join(File.dirname(key),File.basename(@vars[:build_pkg]))}",
|
206
|
+
data: {:file => @vars[:build_pkg]},
|
207
|
+
public_url: :build_url,
|
208
|
+
label: 'Package URL'
|
209
|
+
}
|
210
|
+
else
|
211
|
+
@logger.warn "Skipping upload of missing artifact: '#{@vars[:build_pkg]}'"
|
212
|
+
end
|
213
|
+
|
214
|
+
# Store the metadata
|
215
|
+
manifest = manifestMetadata()
|
216
|
+
hash = JSON.parse(manifest)
|
217
|
+
|
218
|
+
@vars[:return_code] = uploadToRepo(artifacts)
|
219
|
+
# if 0 == @vars[:return_code]
|
220
|
+
# @vars[:return_code] = takeInventory()
|
221
|
+
# end
|
222
|
+
@vars[:return_code]
|
223
|
+
rescue => e
|
224
|
+
@logger.error "#{e.class.name} #{e.message}"
|
225
|
+
@vars[:return_code] = Errors::ARTIFACT_UPLOAD_EXCEPTION
|
226
|
+
raise e
|
227
|
+
end
|
228
|
+
else
|
229
|
+
@vars[:return_code] = Errors::NO_ARTIFACTS
|
230
|
+
end
|
231
|
+
@vars[:return_code]
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|