dpl 1.7.13.travis.841.4 → 1.7.13.travis.842.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +92 -0
- data/lib/dpl/provider/bintray.rb +506 -0
- data/lib/dpl/provider.rb +1 -0
- data/spec/provider/bintray_spec.rb +259 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzUxNjMxZWIyYWVkYmZmNGM1MWRlMzYwZmUyNjQzMTBmOTEyZGQxYw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZjU5MjE0YjA4OTMwZDJmNjU3Y2EzY2IwZWYyMWViZWVkODcyMWRjZQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MzUyZTRmOTI2OTY1MDNmOTAzMzZkMDkzZTVkMTI1YzI0MWYxNzQ1MDdjNzAw
|
10
|
+
ZDJiNDA1M2NlMWI2ZjhhYmNlY2JmNjg3ZThkYzE5OGVlMjczMmQyMTMzMjI1
|
11
|
+
YzBhZDQzM2U2M2VkMGU4MzA2YWY1ZTNjZTI0NTQzM2ZmNWI2MzA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YjM3Y2VlY2U4MzhiMzczOGQyOTc3MWNlYjNkN2E3ZjQ4MmYzNjQxZTUzMDA2
|
14
|
+
NDMyMjY3MTQ5OGY3NWMwODdlZjZlMDk1NWIxY2JkZDE1YzQ5YTgzN2YzOTU4
|
15
|
+
MzFjYzk1N2JlZjNmYzg0ZWQ1M2QxYzE3Y2ZiODU3Y2VkNDMyNzY=
|
data/README.md
CHANGED
@@ -6,6 +6,7 @@ Dpl supports the following providers:
|
|
6
6
|
|
7
7
|
* [AppFog](#appfog)
|
8
8
|
* [Biicode](#biicode)
|
9
|
+
* [Bintray](#bintray)
|
9
10
|
* [BitBalloon](#bitballoon)
|
10
11
|
* [Cloud 66](#cloud-66)
|
11
12
|
* [Cloud Foundry](#cloud-foundry)
|
@@ -75,7 +76,98 @@ As a rule of thumb, you should switch to the Git strategy if you run into issues
|
|
75
76
|
dpl --provider=heroku --strategy=git --username=<username> --password=<password> --app=<application>
|
76
77
|
|
77
78
|
|
79
|
+
### Bintray:
|
78
80
|
|
81
|
+
#### Options:
|
82
|
+
|
83
|
+
* **file**: Path to a descriptor file, containing information for the Bintray upload.
|
84
|
+
* **user**: Bintray user
|
85
|
+
* **key**: Bintray API key
|
86
|
+
* **passphrase**: Optional. In case a passphrase is configured on Bintray and GPG signing is used.
|
87
|
+
* **dry-run**: Optional. If set to true, skips sending requests to Bintray. Useful for testing your configuration.
|
88
|
+
|
89
|
+
#### Descriptor file example:
|
90
|
+
```groovy
|
91
|
+
{
|
92
|
+
/* Bintray package information.
|
93
|
+
In case the package already exists on Bintray, only the name, repo and subject
|
94
|
+
fields are mandatory. */
|
95
|
+
|
96
|
+
"package": {
|
97
|
+
"name": "auto-upload", // Bintray package name
|
98
|
+
"repo": "myRepo", // Bintray repository name
|
99
|
+
"subject": "myBintrayUser", // Bintray subject (user or organization)
|
100
|
+
"desc": "I was pushed completely automatically",
|
101
|
+
"website_url": "www.jfrog.com",
|
102
|
+
"issue_tracker_url": "https://github.com/bintray/bintray-client-java/issues",
|
103
|
+
"vcs_url": "https://github.com/bintray/bintray-client-java.git",
|
104
|
+
"github_use_tag_release_notes": true,
|
105
|
+
"github_release_notes_file": "RELEASE.txt",
|
106
|
+
"licenses": ["MIT"],
|
107
|
+
"labels": ["cool", "awesome", "gorilla"],
|
108
|
+
"public_download_numbers": false,
|
109
|
+
"public_stats": false,
|
110
|
+
"attributes": [{"name": "att1", "values" : ["val1"], "type": "string"},
|
111
|
+
{"name": "att2", "values" : [1, 2.2, 4], "type": "number"},
|
112
|
+
{"name": "att5", "values" : ["2014-12-28T19:43:37+0100"], "type": "date"}]
|
113
|
+
},
|
114
|
+
|
115
|
+
/* Package version information.
|
116
|
+
In case the version already exists on Bintray, only the name fields is mandatory. */
|
117
|
+
|
118
|
+
"version": {
|
119
|
+
"name": "0.5",
|
120
|
+
"desc": "This is a version",
|
121
|
+
"released": "2015-01-04",
|
122
|
+
"vcs_tag": "0.5",
|
123
|
+
"attributes": [{"name": "VerAtt1", "values" : ["VerVal1"], "type": "string"},
|
124
|
+
{"name": "VerAtt2", "values" : [1, 3.3, 5], "type": "number"},
|
125
|
+
{"name": "VerAtt3", "values" : ["2015-01-01T19:43:37+0100"], "type": "date"}],
|
126
|
+
"gpgSign": false
|
127
|
+
},
|
128
|
+
|
129
|
+
/* Configure the files you would like to upload to Bintray and their upload path.
|
130
|
+
You can define one or more groups of patterns.
|
131
|
+
Each group contains three patterns:
|
132
|
+
|
133
|
+
includePattern: Pattern in the form of Ruby regular expression, indicating the path of files to be uploaded to Bintray.
|
134
|
+
excludePattern: Optional. Pattern in the form of Ruby regular expression, indicating the path of files to be removed from the list of files specified by the includePattern.
|
135
|
+
uploadPattern: Upload path on Bintray. The path can contain symbols in the form of $1, $2,... that are replaced with capturing groups defined in the include pattern.
|
136
|
+
|
137
|
+
In the example below, the following files are uploaded,
|
138
|
+
1. All gem files located under build/bin/ (including sub directories),
|
139
|
+
except for files under a the do-not-deploy directory.
|
140
|
+
The files will be uploaded to Bintray under the gems folder.
|
141
|
+
2. All files under build/docs. The files will be uploaded to Bintray under the docs folder.
|
142
|
+
|
143
|
+
Note: Regular expressions defined as part of the includePattern and excludePattern properties must be wrapped with brackets. */
|
144
|
+
|
145
|
+
"files":
|
146
|
+
[
|
147
|
+
{"includePattern": "build/bin(.*)*/(.*\.gem)", "excludePattern": ".*/do-not-deploy/.*", "uploadPattern": "gems/$2"},
|
148
|
+
{"includePattern": "build/docs/(.*)", "uploadPattern": "docs/$1"}
|
149
|
+
],
|
150
|
+
"publish": true
|
151
|
+
}
|
152
|
+
```
|
153
|
+
|
154
|
+
#### Debian Upload
|
155
|
+
|
156
|
+
When artifacts are uploaded to a Debian repository using the Automatic index layout, the Debian distribution information is required and must be specified. The information is specified in the descriptor file by the matrixParams as part of the files closure as shown in the following example:
|
157
|
+
```groovy
|
158
|
+
"files":
|
159
|
+
[{"includePattern": "build/bin/(.*\.deb)", "uploadPattern": "$1",
|
160
|
+
"matrixParams": {
|
161
|
+
"deb_distribution": "vivid",
|
162
|
+
"deb_component": "main",
|
163
|
+
"deb_architecture": "amd64"}
|
164
|
+
}
|
165
|
+
]
|
166
|
+
```
|
167
|
+
|
168
|
+
#### Examples:
|
169
|
+
dpl --provider=bintray --file=<path> --user=<username> --key=<api-key>
|
170
|
+
dpl --provider=bintray --file=<path> --user=<username> --key=<api-key> --passphrase=<passphrase>
|
79
171
|
|
80
172
|
### Nodejitsu:
|
81
173
|
|
@@ -0,0 +1,506 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require "uri"
|
4
|
+
require 'find'
|
5
|
+
|
6
|
+
module DPL
|
7
|
+
class Provider
|
8
|
+
class Bintray < Provider
|
9
|
+
def check_auth
|
10
|
+
end
|
11
|
+
|
12
|
+
def needs_key?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :test_mode
|
17
|
+
attr_reader :user
|
18
|
+
attr_reader :key
|
19
|
+
attr_reader :file
|
20
|
+
attr_reader :passphrase
|
21
|
+
attr_reader :url
|
22
|
+
attr_reader :dry_run
|
23
|
+
attr_reader :descriptor
|
24
|
+
|
25
|
+
def initialize(*args)
|
26
|
+
super(*args)
|
27
|
+
@test_mode = false
|
28
|
+
@user = options[:user]
|
29
|
+
@key = options[:key]
|
30
|
+
@url = options[:url]
|
31
|
+
@file = options[:file]
|
32
|
+
@passphrase = options[:passphrase]
|
33
|
+
@dry_run = options[:dry_run]
|
34
|
+
|
35
|
+
if @user.nil?
|
36
|
+
abort("The 'user' argument is required")
|
37
|
+
end
|
38
|
+
if @key.nil?
|
39
|
+
abort("The 'key' argument is required")
|
40
|
+
end
|
41
|
+
if @file.nil?
|
42
|
+
abort("The 'file' argument is required")
|
43
|
+
end
|
44
|
+
if @url.nil?
|
45
|
+
@url = 'https://api.bintray.com'
|
46
|
+
end
|
47
|
+
if @dry_run.nil?
|
48
|
+
@dry_run = false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_descriptor
|
53
|
+
log "Reading descriptor file: #{file}"
|
54
|
+
@descriptor = JSON.parse(File.read(file))
|
55
|
+
end
|
56
|
+
|
57
|
+
def descriptor=(json)
|
58
|
+
@descriptor = JSON.parse(json)
|
59
|
+
end
|
60
|
+
|
61
|
+
def head_request(path)
|
62
|
+
url = URI.parse(self.url)
|
63
|
+
req = Net::HTTP::Head.new(path)
|
64
|
+
req.basic_auth user, key
|
65
|
+
|
66
|
+
sock = Net::HTTP.new(url.host, url.port)
|
67
|
+
sock.use_ssl = true
|
68
|
+
res = sock.start {|http| http.request(req) }
|
69
|
+
|
70
|
+
return res
|
71
|
+
end
|
72
|
+
|
73
|
+
def post_request(path, body)
|
74
|
+
req = Net::HTTP::Post.new(path)
|
75
|
+
req.add_field('Content-Type', 'application/json')
|
76
|
+
req.basic_auth user, key
|
77
|
+
if !body.nil?
|
78
|
+
req.body = body.to_json
|
79
|
+
end
|
80
|
+
|
81
|
+
url = URI.parse(self.url)
|
82
|
+
sock = Net::HTTP.new(url.host, url.port)
|
83
|
+
sock.use_ssl = true
|
84
|
+
res = sock.start {|http| http.request(req) }
|
85
|
+
return res
|
86
|
+
end
|
87
|
+
|
88
|
+
def put_file_request(local_file_path, upload_path, matrix_params)
|
89
|
+
url = URI.parse(self.url)
|
90
|
+
|
91
|
+
data = File.read(local_file_path)
|
92
|
+
http = Net::HTTP.new(url.host, url.port)
|
93
|
+
http.use_ssl = true
|
94
|
+
|
95
|
+
params = ''
|
96
|
+
if !matrix_params.nil?
|
97
|
+
matrix_params.each do |key, val|
|
98
|
+
params << ";#{key}=#{val}"
|
99
|
+
end
|
100
|
+
upload_path << params
|
101
|
+
end
|
102
|
+
|
103
|
+
request = Net::HTTP::Put.new("#{upload_path}")
|
104
|
+
request.basic_auth user, key
|
105
|
+
request.body = data
|
106
|
+
|
107
|
+
return http.request(request)
|
108
|
+
end
|
109
|
+
|
110
|
+
def upload_file(artifact)
|
111
|
+
log "Uploading file '#{artifact.local_path}' to #{artifact.upload_path}"
|
112
|
+
|
113
|
+
if dry_run
|
114
|
+
return
|
115
|
+
end
|
116
|
+
|
117
|
+
package = descriptor["package"]
|
118
|
+
version = descriptor["version"]
|
119
|
+
package_name = package["name"]
|
120
|
+
subject = package["subject"]
|
121
|
+
repo = package["repo"]
|
122
|
+
version_name = version["name"]
|
123
|
+
|
124
|
+
path = "/content/#{subject}/#{repo}/#{package_name}/#{version_name}/#{artifact.upload_path}"
|
125
|
+
res = put_file_request(artifact.local_path, path, artifact.matrix_params)
|
126
|
+
log_bintray_response(res)
|
127
|
+
end
|
128
|
+
|
129
|
+
def package_exists_path
|
130
|
+
package = descriptor["package"]
|
131
|
+
subject = package["subject"]
|
132
|
+
name = package["name"]
|
133
|
+
repo = package["repo"]
|
134
|
+
return "/packages/#{subject}/#{repo}/#{name}"
|
135
|
+
end
|
136
|
+
|
137
|
+
def package_exists?
|
138
|
+
path = package_exists_path
|
139
|
+
if !dry_run
|
140
|
+
res = head_request(path)
|
141
|
+
code = res.code.to_i
|
142
|
+
else
|
143
|
+
code = 404
|
144
|
+
end
|
145
|
+
|
146
|
+
if code == 404
|
147
|
+
return false
|
148
|
+
end
|
149
|
+
if code == 201 || code == 200
|
150
|
+
return true
|
151
|
+
end
|
152
|
+
abort("Unexpected HTTP response code #{code} returned from Bintray while checking if package '#{name}' exists. " +
|
153
|
+
"Response message: #{res.message}")
|
154
|
+
end
|
155
|
+
|
156
|
+
def version_exists_path
|
157
|
+
package = descriptor["package"]
|
158
|
+
version = descriptor["version"]
|
159
|
+
package_name = package["name"]
|
160
|
+
subject = package["subject"]
|
161
|
+
repo = package["repo"]
|
162
|
+
version_name = version["name"]
|
163
|
+
|
164
|
+
return "/packages/#{subject}/#{repo}/#{package_name}/versions/#{version_name}"
|
165
|
+
end
|
166
|
+
|
167
|
+
def version_exists?
|
168
|
+
path = version_exists_path
|
169
|
+
if !dry_run
|
170
|
+
res = head_request(path)
|
171
|
+
code = res.code.to_i
|
172
|
+
else
|
173
|
+
code = 404
|
174
|
+
end
|
175
|
+
|
176
|
+
if code == 404
|
177
|
+
return false
|
178
|
+
end
|
179
|
+
if code == 201 || code == 200
|
180
|
+
return true
|
181
|
+
end
|
182
|
+
abort("Unexpected HTTP response code #{code} returned from Bintray while checking if version '#{version_name}' exists. " +
|
183
|
+
"Response message: #{res.message}")
|
184
|
+
end
|
185
|
+
|
186
|
+
def create_package
|
187
|
+
package = descriptor["package"]
|
188
|
+
repo = package["repo"]
|
189
|
+
body = {}
|
190
|
+
|
191
|
+
add_to_map(body, package, "name")
|
192
|
+
add_to_map(body, package, "desc")
|
193
|
+
add_to_map(body, package, "licenses")
|
194
|
+
add_to_map(body, package, "labels")
|
195
|
+
add_to_map(body, package, "vcs_url")
|
196
|
+
add_to_map(body, package, "website_url")
|
197
|
+
add_to_map(body, package, "issue_tracker_url")
|
198
|
+
add_to_map(body, package, "public_download_numbers")
|
199
|
+
add_to_map(body, package, "public_stats")
|
200
|
+
|
201
|
+
subject = package["subject"]
|
202
|
+
package_name = package["name"]
|
203
|
+
log "Creating package '#{package_name}'..."
|
204
|
+
|
205
|
+
path = "/packages/#{subject}/#{repo}"
|
206
|
+
if !dry_run
|
207
|
+
res = post_request(path, body)
|
208
|
+
log_bintray_response(res)
|
209
|
+
code = res.code.to_i
|
210
|
+
else
|
211
|
+
code = 200
|
212
|
+
end
|
213
|
+
|
214
|
+
if !test_mode
|
215
|
+
if code == 201 || code == 200
|
216
|
+
add_package_attributes
|
217
|
+
end
|
218
|
+
end
|
219
|
+
RequestDetails.new(path, body)
|
220
|
+
end
|
221
|
+
|
222
|
+
def add_package_attributes
|
223
|
+
package = descriptor["package"]
|
224
|
+
repo = package["repo"]
|
225
|
+
subject = package["subject"]
|
226
|
+
package_name = package["name"]
|
227
|
+
attributes = package["attributes"]
|
228
|
+
path = nil
|
229
|
+
if !attributes.nil?
|
230
|
+
log "Adding attributes for package '#{package_name}'..."
|
231
|
+
path = "/packages/#{subject}/#{repo}/#{package_name}/attributes"
|
232
|
+
if !dry_run
|
233
|
+
res = post_request(path, attributes)
|
234
|
+
log_bintray_response(res)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
RequestDetails.new(path, attributes)
|
238
|
+
end
|
239
|
+
|
240
|
+
def create_version
|
241
|
+
package = descriptor["package"]
|
242
|
+
version = descriptor["version"]
|
243
|
+
repo = package["repo"]
|
244
|
+
body = {}
|
245
|
+
|
246
|
+
add_to_map(body, version, "name")
|
247
|
+
add_to_map(body, version, "desc")
|
248
|
+
add_to_map(body, version, "released")
|
249
|
+
add_to_map(body, version, "vcs_tag")
|
250
|
+
add_to_map(body, version, "github_release_notes_file")
|
251
|
+
add_to_map(body, version, "github_use_tag_release_notes")
|
252
|
+
add_to_map(body, version, "attributes")
|
253
|
+
|
254
|
+
package_name = package["name"]
|
255
|
+
subject = package["subject"]
|
256
|
+
version_name = version["name"]
|
257
|
+
log "Creating version '#{version_name}'..."
|
258
|
+
|
259
|
+
path = "/packages/#{subject}/#{repo}/#{package_name}/versions"
|
260
|
+
if !dry_run
|
261
|
+
res = post_request(path, body)
|
262
|
+
log_bintray_response(res)
|
263
|
+
code = res.code.to_i
|
264
|
+
else
|
265
|
+
code = 200
|
266
|
+
end
|
267
|
+
|
268
|
+
if !test_mode
|
269
|
+
if code == 201 || code == 200
|
270
|
+
add_version_attributes
|
271
|
+
end
|
272
|
+
end
|
273
|
+
RequestDetails.new(path, body)
|
274
|
+
end
|
275
|
+
|
276
|
+
def add_version_attributes
|
277
|
+
package = descriptor["package"]
|
278
|
+
package_name = package["name"]
|
279
|
+
subject = package["subject"]
|
280
|
+
version = descriptor["version"]
|
281
|
+
version_name = version["name"]
|
282
|
+
repo = package["repo"]
|
283
|
+
attributes = version["attributes"]
|
284
|
+
path = nil
|
285
|
+
if !attributes.nil?
|
286
|
+
log "Adding attributes for version '#{version_name}'..."
|
287
|
+
path = "/packages/#{subject}/#{repo}/#{package_name}/versions/#{version_name}/attributes"
|
288
|
+
if !dry_run
|
289
|
+
res = post_request(path, attributes)
|
290
|
+
log_bintray_response(res)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
RequestDetails.new(path, attributes)
|
294
|
+
end
|
295
|
+
|
296
|
+
def check_and_create_package
|
297
|
+
if !package_exists?
|
298
|
+
create_package
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def check_and_create_version
|
303
|
+
if !version_exists?
|
304
|
+
create_version
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def upload_files
|
309
|
+
files = files_to_upload
|
310
|
+
|
311
|
+
files.each do |key, artifact|
|
312
|
+
upload_file(artifact)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def publish_version
|
317
|
+
publish = descriptor["publish"]
|
318
|
+
if publish
|
319
|
+
package = descriptor["package"]
|
320
|
+
version = descriptor["version"]
|
321
|
+
repo = package["repo"]
|
322
|
+
package_name = package["name"]
|
323
|
+
subject = package["subject"]
|
324
|
+
version_name = version["name"]
|
325
|
+
|
326
|
+
log "Publishing version '#{version_name}' of package '#{package_name}'..."
|
327
|
+
path = "/content/#{subject}/#{repo}/#{package_name}/#{version_name}/publish"
|
328
|
+
if !dry_run
|
329
|
+
res = post_request(path, nil)
|
330
|
+
log_bintray_response(res)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
RequestDetails.new(path, nil)
|
334
|
+
end
|
335
|
+
|
336
|
+
def gpg_sign_version
|
337
|
+
version = descriptor["version"]
|
338
|
+
gpg_sign = version["gpgSign"]
|
339
|
+
if gpg_sign
|
340
|
+
package = descriptor["package"]
|
341
|
+
repo = package["repo"]
|
342
|
+
package_name = package["name"]
|
343
|
+
subject = package["subject"]
|
344
|
+
version_name = version["name"]
|
345
|
+
|
346
|
+
body = nil
|
347
|
+
if !passphrase.nil?
|
348
|
+
log "Signing version with no passphrase..."
|
349
|
+
body = {}
|
350
|
+
body["passphrase"] = passphrase
|
351
|
+
else
|
352
|
+
log "Signing version with passphrase..."
|
353
|
+
end
|
354
|
+
|
355
|
+
path = "/gpg/#{subject}/#{repo}/#{package_name}/versions/#{version_name}"
|
356
|
+
if !dry_run
|
357
|
+
res = post_request(path, body)
|
358
|
+
log_bintray_response(res)
|
359
|
+
end
|
360
|
+
RequestDetails.new(path, body)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# Get the root path from which to start collecting files to be
|
365
|
+
# uploaded to Bintray.
|
366
|
+
def root_path(str)
|
367
|
+
index = str.index('(')
|
368
|
+
path = nil
|
369
|
+
if index.nil?
|
370
|
+
path = str
|
371
|
+
else
|
372
|
+
path = str[0, index]
|
373
|
+
end
|
374
|
+
|
375
|
+
if !test_mode && !File.exist?(path)
|
376
|
+
log "Warning: Path: #{path} does not exist."
|
377
|
+
return nil
|
378
|
+
end
|
379
|
+
return path
|
380
|
+
end
|
381
|
+
|
382
|
+
# Fills a map with Artifact objects which match
|
383
|
+
# the include pattern and do not match the exclude pattern.
|
384
|
+
# The artifacts are files collected from the file system.
|
385
|
+
def fill_files_map(map, include_pattern, exclude_pattern, upload_pattern, matrix_params)
|
386
|
+
# Get the root path from which to start collecting the files.
|
387
|
+
root_path = root_path(include_pattern)
|
388
|
+
if root_path.nil?
|
389
|
+
return
|
390
|
+
end
|
391
|
+
|
392
|
+
# Start scanning the root path recursively.
|
393
|
+
Find.find(root_path) do |path|
|
394
|
+
add_if_matches(map, path, include_pattern, exclude_pattern, upload_pattern, matrix_params)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def add_if_matches(map, path, include_pattern, exclude_pattern, upload_pattern, matrix_params)
|
399
|
+
res = path.match(/#{include_pattern}/)
|
400
|
+
|
401
|
+
# If the file matches the include pattern and it is not a directory.
|
402
|
+
# In case test_mode is set, we do not check if the file exists.
|
403
|
+
if !res.nil? && (test_mode || File.file?(path))
|
404
|
+
# If the file does not match the exclude pattern.
|
405
|
+
if exclude_pattern.nil? || exclude_pattern.empty? || !path.match(/#{exclude_pattern}/)
|
406
|
+
# Using the capturing groups in the include pattern, replace the $1, $2, ...
|
407
|
+
# in the upload pattern.
|
408
|
+
groups = res.captures
|
409
|
+
replaced_upload_pattern = upload_pattern
|
410
|
+
for i in 0..groups.size-1
|
411
|
+
replaced_upload_pattern = replaced_upload_pattern.gsub("$#{i+1}", groups[i])
|
412
|
+
end
|
413
|
+
map[path] = Artifact.new(path, replaced_upload_pattern, matrix_params)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
# Returns a map containing Artifact objects.
|
419
|
+
# The map contains the files to be uploaded to Bintray.
|
420
|
+
def files_to_upload
|
421
|
+
upload_files = Hash.new()
|
422
|
+
files = descriptor["files"]
|
423
|
+
if files.nil?
|
424
|
+
return upload_files
|
425
|
+
end
|
426
|
+
|
427
|
+
files.each { |patterns|
|
428
|
+
fill_files_map(
|
429
|
+
upload_files,
|
430
|
+
patterns["includePattern"],
|
431
|
+
patterns["excludePattern"],
|
432
|
+
patterns["uploadPattern"],
|
433
|
+
patterns["matrixParams"])
|
434
|
+
}
|
435
|
+
|
436
|
+
return upload_files
|
437
|
+
end
|
438
|
+
|
439
|
+
def deploy
|
440
|
+
read_descriptor
|
441
|
+
check_and_create_package
|
442
|
+
check_and_create_version
|
443
|
+
upload_files
|
444
|
+
gpg_sign_version
|
445
|
+
publish_version
|
446
|
+
end
|
447
|
+
|
448
|
+
# Copies a key from one map to another, if the key exists there.
|
449
|
+
def add_to_map(to_map, from_map, key)
|
450
|
+
if !from_map[key].nil?
|
451
|
+
to_map[key] = from_map[key]
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def log_bintray_response(res)
|
456
|
+
msg = ''
|
457
|
+
if !res.body.nil?
|
458
|
+
begin
|
459
|
+
response = JSON.parse(res.body)
|
460
|
+
msg = response["message"]
|
461
|
+
rescue
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
log "Bintray response: #{res.code.to_i} #{res.message}. #{msg}"
|
466
|
+
end
|
467
|
+
|
468
|
+
def log(msg)
|
469
|
+
puts "[Bintray Upload] #{msg}"
|
470
|
+
end
|
471
|
+
|
472
|
+
# This class represents an artifact (file) to be uploaded to Bintray.
|
473
|
+
class Artifact
|
474
|
+
def initialize(local_path, upload_path, matrix_params)
|
475
|
+
@local_path = local_path
|
476
|
+
@upload_path = upload_path
|
477
|
+
@matrix_params = matrix_params
|
478
|
+
end
|
479
|
+
|
480
|
+
def hash
|
481
|
+
return @localPath.hash
|
482
|
+
end
|
483
|
+
|
484
|
+
def eql?(other)
|
485
|
+
@localPath == other.local_path
|
486
|
+
end
|
487
|
+
|
488
|
+
attr_reader :local_path
|
489
|
+
attr_reader :upload_path
|
490
|
+
attr_reader :matrix_params
|
491
|
+
end
|
492
|
+
|
493
|
+
# Used to return the path and body of REST requests sent to Bintray.
|
494
|
+
# Used for testing.
|
495
|
+
class RequestDetails
|
496
|
+
def initialize(path, body)
|
497
|
+
@path = path
|
498
|
+
@body = body
|
499
|
+
end
|
500
|
+
|
501
|
+
attr_reader :path
|
502
|
+
attr_reader :body
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
data/lib/dpl/provider.rb
CHANGED
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dpl/provider/bintray'
|
3
|
+
|
4
|
+
describe DPL::Provider::Bintray do
|
5
|
+
|
6
|
+
subject :provider do
|
7
|
+
described_class.new(DummyContext.new,
|
8
|
+
:user => 'user',
|
9
|
+
:key => 'key',
|
10
|
+
:file => 'file',
|
11
|
+
:dry_run => 'true')
|
12
|
+
end
|
13
|
+
|
14
|
+
subject :provider_with_passphrase do
|
15
|
+
described_class.new(DummyContext.new,
|
16
|
+
:user => 'user',
|
17
|
+
:key => 'key',
|
18
|
+
:file => 'file',
|
19
|
+
:dry_run => 'true',
|
20
|
+
:passphrase => 'passphrase')
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "package_exists?" do
|
24
|
+
example do
|
25
|
+
descriptor = JSON.parse(descriptor_content)
|
26
|
+
package = descriptor["package"]
|
27
|
+
package_name = package["name"]
|
28
|
+
subject = package["subject"]
|
29
|
+
repo = package["repo"]
|
30
|
+
|
31
|
+
init_provider(provider)
|
32
|
+
expect(provider.package_exists_path).to eq("/packages/#{subject}/#{repo}/#{package_name}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "version_exists?" do
|
37
|
+
example do
|
38
|
+
descriptor = JSON.parse(descriptor_content)
|
39
|
+
package = descriptor["package"]
|
40
|
+
package_name = package["name"]
|
41
|
+
subject = package["subject"]
|
42
|
+
repo = package["repo"]
|
43
|
+
version_name = descriptor["version"]["name"]
|
44
|
+
|
45
|
+
init_provider(provider)
|
46
|
+
expect(provider.version_exists_path).to eq("/packages/#{subject}/#{repo}/#{package_name}/versions/#{version_name}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "create_package" do
|
51
|
+
example do
|
52
|
+
descriptor = JSON.parse(descriptor_content)
|
53
|
+
package = descriptor["package"]
|
54
|
+
subject = package["subject"]
|
55
|
+
repo = package["repo"]
|
56
|
+
|
57
|
+
init_provider(provider)
|
58
|
+
request_details = provider.create_package
|
59
|
+
expect(request_details.path).to eq("/packages/#{subject}/#{repo}")
|
60
|
+
|
61
|
+
body = {
|
62
|
+
'name' => package["name"],
|
63
|
+
'desc' => package["desc"],
|
64
|
+
'licenses' => package["licenses"],
|
65
|
+
'labels' => package["labels"],
|
66
|
+
'vcs_url' => package["vcs_url"],
|
67
|
+
'website_url' => package["website_url"],
|
68
|
+
'issue_tracker_url' => package["issue_tracker_url"],
|
69
|
+
'public_download_numbers' => package["public_download_numbers"],
|
70
|
+
'public_stats' => package["public_stats"]
|
71
|
+
}
|
72
|
+
expect(request_details.body).to eq(body)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "create_version" do
|
77
|
+
example do
|
78
|
+
descriptor = JSON.parse(descriptor_content)
|
79
|
+
package = descriptor["package"]
|
80
|
+
package_name = package["name"]
|
81
|
+
subject = package["subject"]
|
82
|
+
repo = package["repo"]
|
83
|
+
|
84
|
+
init_provider(provider)
|
85
|
+
request_details = provider.create_version
|
86
|
+
expect(request_details.path).to eq("/packages/#{subject}/#{repo}/#{package_name}/versions")
|
87
|
+
|
88
|
+
version = descriptor["version"]
|
89
|
+
body = {
|
90
|
+
'name' => version["name"],
|
91
|
+
'desc' => version["desc"],
|
92
|
+
'released' => version["released"],
|
93
|
+
'vcs_tag' => version["vcs_tag"],
|
94
|
+
'attributes' => version["attributes"]
|
95
|
+
}
|
96
|
+
expect(request_details.body).to eq(body)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "add_package_attributes" do
|
101
|
+
example do
|
102
|
+
descriptor = JSON.parse(descriptor_content)
|
103
|
+
package = descriptor["package"]
|
104
|
+
package_name = package["name"]
|
105
|
+
subject = package["subject"]
|
106
|
+
repo = package["repo"]
|
107
|
+
|
108
|
+
init_provider(provider)
|
109
|
+
request_details = provider.add_package_attributes
|
110
|
+
expect(request_details.path).to eq("/packages/#{subject}/#{repo}/#{package_name}/attributes")
|
111
|
+
|
112
|
+
body = package["attributes"]
|
113
|
+
expect(request_details.body).to eq(body)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "add_version_attributes" do
|
118
|
+
example do
|
119
|
+
descriptor = JSON.parse(descriptor_content)
|
120
|
+
package = descriptor["package"]
|
121
|
+
package_name = package["name"]
|
122
|
+
subject = package["subject"]
|
123
|
+
repo = package["repo"]
|
124
|
+
version_name = descriptor["version"]["name"]
|
125
|
+
|
126
|
+
init_provider(provider)
|
127
|
+
request_details = provider.add_version_attributes
|
128
|
+
expect(request_details.path).to eq("/packages/#{subject}/#{repo}/#{package_name}/versions/#{version_name}/attributes")
|
129
|
+
|
130
|
+
descriptor = JSON.parse(descriptor_content)
|
131
|
+
version = descriptor["version"]
|
132
|
+
body = version["attributes"]
|
133
|
+
expect(request_details.body).to eq(body)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "publish_version" do
|
138
|
+
example do
|
139
|
+
descriptor = JSON.parse(descriptor_content)
|
140
|
+
package = descriptor["package"]
|
141
|
+
package_name = package["name"]
|
142
|
+
subject = package["subject"]
|
143
|
+
repo = package["repo"]
|
144
|
+
version_name = descriptor["version"]["name"]
|
145
|
+
|
146
|
+
init_provider(provider)
|
147
|
+
request_details = provider.publish_version
|
148
|
+
expect(request_details.path).to eq("/content/#{subject}/#{repo}/#{package_name}/#{version_name}/publish")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "gpg_sign_version_without_passphrase" do
|
153
|
+
example do
|
154
|
+
descriptor = JSON.parse(descriptor_content)
|
155
|
+
package = descriptor["package"]
|
156
|
+
package_name = package["name"]
|
157
|
+
subject = package["subject"]
|
158
|
+
repo = package["repo"]
|
159
|
+
version_name = descriptor["version"]["name"]
|
160
|
+
|
161
|
+
init_provider(provider)
|
162
|
+
request_details = provider.gpg_sign_version
|
163
|
+
expect(request_details.path).to eq("/gpg/#{subject}/#{repo}/#{package_name}/versions/#{version_name}")
|
164
|
+
expect(request_details.body).to eq(nil)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "gpg_sign_version_with_passphrase" do
|
169
|
+
example do
|
170
|
+
descriptor = JSON.parse(descriptor_content)
|
171
|
+
package = descriptor["package"]
|
172
|
+
package_name = package["name"]
|
173
|
+
subject = package["subject"]
|
174
|
+
repo = package["repo"]
|
175
|
+
version_name = descriptor["version"]["name"]
|
176
|
+
|
177
|
+
init_provider(provider_with_passphrase)
|
178
|
+
request_details = provider_with_passphrase.gpg_sign_version
|
179
|
+
expect(request_details.path).to eq("/gpg/#{subject}/#{repo}/#{package_name}/versions/#{version_name}")
|
180
|
+
|
181
|
+
body = {
|
182
|
+
'passphrase' => 'passphrase'
|
183
|
+
}
|
184
|
+
expect(request_details.body).to eq(body)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "upload_files" do
|
189
|
+
example do
|
190
|
+
init_provider(provider)
|
191
|
+
files_to_upload = Hash.new()
|
192
|
+
matrix_params = {
|
193
|
+
'p1' => 'a',
|
194
|
+
'p2' => 'b'
|
195
|
+
}
|
196
|
+
|
197
|
+
provider.add_if_matches(files_to_upload, 'build/files/a.gem', 'build/files/(.*.gem)', 'exclude', 'a/b/$1', nil)
|
198
|
+
provider.add_if_matches(files_to_upload, 'build/files/b.gem', 'build/files/(.*.gem)', nil, 'a/b/$1', matrix_params)
|
199
|
+
provider.add_if_matches(files_to_upload, 'build/files/c.gem', 'build/files/(.*.gem)', '.*files.*', 'a/b/$1', nil)
|
200
|
+
provider.add_if_matches(files_to_upload, 'build/files/c.txt', 'build/files/(.*.gem)', 'exclude', 'a/b/$1', nil)
|
201
|
+
|
202
|
+
expect(files_to_upload["build/files/a.gem"]).not_to eq(nil)
|
203
|
+
expect(files_to_upload["build/files/a.gem"].upload_path).to eq('a/b/a.gem')
|
204
|
+
expect(files_to_upload["build/files/a.gem"].matrix_params).to eq(nil)
|
205
|
+
|
206
|
+
expect(files_to_upload["build/files/b.gem"]).not_to eq(nil)
|
207
|
+
expect(files_to_upload["build/files/b.gem"].upload_path).to eq('a/b/b.gem')
|
208
|
+
expect(files_to_upload["build/files/b.gem"].matrix_params).to eq(matrix_params)
|
209
|
+
|
210
|
+
expect(files_to_upload["build/files/c.gem"]).to eq(nil)
|
211
|
+
expect(files_to_upload["build/files/c.txt"]).to eq(nil)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def init_provider(bintray)
|
216
|
+
bintray.descriptor=descriptor_content
|
217
|
+
bintray.test_mode = true
|
218
|
+
end
|
219
|
+
|
220
|
+
def descriptor_content
|
221
|
+
return """ {
|
222
|
+
\"package\": {
|
223
|
+
\"name\": \"auto-upload\",
|
224
|
+
\"repo\": \"myRepo\",
|
225
|
+
\"subject\": \"myBintrayUser\",
|
226
|
+
\"desc\": \"I was pushed completely automatically\",
|
227
|
+
\"website_url\": \"www.jfrog.com\",
|
228
|
+
\"issue_tracker_url\": \"https://github.com/bintray/bintray-client-java/issues\",
|
229
|
+
\"vcs_url\": \"https://github.com/bintray/bintray-client-java.git\",
|
230
|
+
\"github_use_tag_release_notes\": true,
|
231
|
+
\"github_release_notes_file\": \"RELEASE.txt\",
|
232
|
+
\"licenses\": [\"MIT\"],
|
233
|
+
\"labels\": [\"cool\", \"awesome\", \"gorilla\"],
|
234
|
+
\"public_download_numbers\": false,
|
235
|
+
\"public_stats\": false,
|
236
|
+
\"attributes\": [{\"name\": \"att1\", \"values\" : [\"val1\"], \"type\": \"string\"},
|
237
|
+
{\"name\": \"att2\", \"values\" : [1, 2.2, 4], \"type\": \"number\"},
|
238
|
+
{\"name\": \"att5\", \"values\" : [\"2014-12-28T19:43:37+0100\"], \"type\": \"date\"}]
|
239
|
+
},
|
240
|
+
\"version\": {
|
241
|
+
\"name\": \"0.5\",
|
242
|
+
\"desc\": \"This is a version\",
|
243
|
+
\"released\": \"2015-01-04\",
|
244
|
+
\"vcs_tag\": \"0.5\",
|
245
|
+
\"attributes\": [{\"name\": \"VerAtt1\", \"values\" : [\"VerVal1\"], \"type\": \"string\"},
|
246
|
+
{\"name\": \"VerAtt2\", \"values\" : [1, 3.3, 5], \"type\": \"number\"},
|
247
|
+
{\"name\": \"VerAtt3\", \"values\" : [\"2015-01-01T19:43:37+0100\"], \"type\": \"date\"}],
|
248
|
+
\"gpgSign\": true
|
249
|
+
},
|
250
|
+
\"files\":
|
251
|
+
[
|
252
|
+
{\"includePattern\": \"build/bin/(*.gem)\", \"excludePattern\": \".*/do-not-deploy/.*\", \"uploadPattern\": \"gems/$1\"},
|
253
|
+
{\"includePattern\": \"build/docs/(.*)\", \"uploadPattern\": \"docs/$1\"}
|
254
|
+
],
|
255
|
+
\"publish\": true
|
256
|
+
}
|
257
|
+
"""
|
258
|
+
end
|
259
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dpl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.13.travis.
|
4
|
+
version: 1.7.13.travis.842.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Haase
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- lib/dpl/provider.rb
|
102
102
|
- lib/dpl/provider/appfog.rb
|
103
103
|
- lib/dpl/provider/biicode.rb
|
104
|
+
- lib/dpl/provider/bintray.rb
|
104
105
|
- lib/dpl/provider/bitballoon.rb
|
105
106
|
- lib/dpl/provider/chef_supermarket.rb
|
106
107
|
- lib/dpl/provider/cloud66.rb
|
@@ -143,6 +144,7 @@ files:
|
|
143
144
|
- notes/heroku.md
|
144
145
|
- spec/cli_spec.rb
|
145
146
|
- spec/provider/appfog_spec.rb
|
147
|
+
- spec/provider/bintray_spec.rb
|
146
148
|
- spec/provider/bitballoon_spec.rb
|
147
149
|
- spec/provider/chef_supermarket_spec.rb
|
148
150
|
- spec/provider/cloud66_spec.rb
|