artifactory 1.1.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +11 -7
- data/CHANGELOG.md +18 -0
- data/README.md +25 -4
- data/Rakefile +5 -2
- data/artifactory.gemspec +0 -3
- data/lib/artifactory.rb +2 -4
- data/lib/artifactory/client.rb +210 -85
- data/lib/artifactory/configurable.rb +6 -1
- data/lib/artifactory/defaults.rb +52 -3
- data/lib/artifactory/errors.rb +21 -14
- data/lib/artifactory/resources/artifact.rb +132 -58
- data/lib/artifactory/resources/base.rb +120 -6
- data/lib/artifactory/resources/build.rb +2 -1
- data/lib/artifactory/resources/group.rb +5 -72
- data/lib/artifactory/resources/layout.rb +106 -0
- data/lib/artifactory/resources/repository.rb +62 -114
- data/lib/artifactory/resources/system.rb +5 -1
- data/lib/artifactory/resources/user.rb +5 -79
- data/lib/artifactory/util.rb +8 -2
- data/lib/artifactory/version.rb +1 -1
- data/spec/integration/resources/layout_spec.rb +22 -0
- data/spec/integration/resources/repository_spec.rb +7 -0
- data/spec/integration/resources/system_spec.rb +4 -4
- data/spec/support/api_server/repository_endpoints.rb +5 -0
- data/spec/support/api_server/system_endpoints.rb +18 -0
- data/spec/unit/client_spec.rb +17 -98
- data/spec/unit/resources/artifact_spec.rb +99 -13
- data/spec/unit/resources/build_spec.rb +1 -1
- data/spec/unit/resources/group_spec.rb +1 -1
- data/spec/unit/resources/layout_spec.rb +61 -0
- data/spec/unit/resources/repository_spec.rb +31 -47
- data/spec/unit/resources/system_spec.rb +4 -2
- data/spec/unit/resources/user_spec.rb +1 -1
- metadata +16 -40
- data/locales/en.yml +0 -27
|
@@ -16,10 +16,11 @@ module Artifactory
|
|
|
16
16
|
def all(options = {})
|
|
17
17
|
client = extract_client!(options)
|
|
18
18
|
client.get('/api/build')
|
|
19
|
-
rescue Error::
|
|
19
|
+
rescue Error::HTTPError => e
|
|
20
20
|
# Artifactory returns a 404 instead of an empty list when there are no
|
|
21
21
|
# builds. Whoever decided that was a good idea clearly doesn't
|
|
22
22
|
# understand the point of REST interfaces...
|
|
23
|
+
raise unless e.code == 404
|
|
23
24
|
[]
|
|
24
25
|
end
|
|
25
26
|
end
|
|
@@ -43,54 +43,10 @@ module Artifactory
|
|
|
43
43
|
|
|
44
44
|
response = client.get("/api/security/groups/#{url_safe(name)}")
|
|
45
45
|
from_hash(response, client: client)
|
|
46
|
-
rescue Error::
|
|
46
|
+
rescue Error::HTTPError => e
|
|
47
|
+
raise unless e.code == 404
|
|
47
48
|
nil
|
|
48
49
|
end
|
|
49
|
-
|
|
50
|
-
#
|
|
51
|
-
# Construct a group from the given URL.
|
|
52
|
-
#
|
|
53
|
-
# @example Create an group object from the given URL
|
|
54
|
-
# Group.from_url('/security/groups/readers') #=> #<Resource::Group>
|
|
55
|
-
#
|
|
56
|
-
# @param [Artifactory::Client] client
|
|
57
|
-
# the client object to make the request with
|
|
58
|
-
# @param [String] url
|
|
59
|
-
# the URL to find the group from
|
|
60
|
-
#
|
|
61
|
-
# @return [Resource::Group]
|
|
62
|
-
#
|
|
63
|
-
def from_url(url, options = {})
|
|
64
|
-
client = extract_client!(options)
|
|
65
|
-
from_hash(client.get(url), client: client)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
#
|
|
69
|
-
# Create a instance from the given Hash. This method extracts the "safe"
|
|
70
|
-
# information from the hash and adds them to the instance.
|
|
71
|
-
#
|
|
72
|
-
# @example Create a new group from a hash
|
|
73
|
-
# Group.from_hash('realmAttributes' => '...', 'name' => '...')
|
|
74
|
-
#
|
|
75
|
-
# @param [Artifactory::Client] client
|
|
76
|
-
# the client object to make the request with
|
|
77
|
-
# @param [Hash] hash
|
|
78
|
-
# the hash to create the instance from
|
|
79
|
-
#
|
|
80
|
-
# @return [Resource::Group]
|
|
81
|
-
#
|
|
82
|
-
def from_hash(hash, options = {})
|
|
83
|
-
client = extract_client!(options)
|
|
84
|
-
|
|
85
|
-
new.tap do |instance|
|
|
86
|
-
instance.auto_join = hash['autoJoin']
|
|
87
|
-
instance.client = client
|
|
88
|
-
instance.description = hash['description']
|
|
89
|
-
instance.name = hash['name']
|
|
90
|
-
instance.realm = hash['realm']
|
|
91
|
-
instance.realm_attributes = hash['realmAttributes']
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
50
|
end
|
|
95
51
|
|
|
96
52
|
attribute :auto_join
|
|
@@ -107,8 +63,9 @@ module Artifactory
|
|
|
107
63
|
# true if the object was deleted successfully, false otherwise
|
|
108
64
|
#
|
|
109
65
|
def delete
|
|
110
|
-
|
|
111
|
-
|
|
66
|
+
client.delete(api_path)
|
|
67
|
+
true
|
|
68
|
+
rescue Error::HTTPError
|
|
112
69
|
false
|
|
113
70
|
end
|
|
114
71
|
|
|
@@ -122,30 +79,6 @@ module Artifactory
|
|
|
122
79
|
true
|
|
123
80
|
end
|
|
124
81
|
|
|
125
|
-
#
|
|
126
|
-
# The hash format for this group.
|
|
127
|
-
#
|
|
128
|
-
# @return [Hash]
|
|
129
|
-
#
|
|
130
|
-
def to_hash
|
|
131
|
-
{
|
|
132
|
-
'autoJoin' => auto_join,
|
|
133
|
-
'description' => description,
|
|
134
|
-
'name' => name,
|
|
135
|
-
'realm' => realm,
|
|
136
|
-
'realmAttributes' => realm_attributes,
|
|
137
|
-
}
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
#
|
|
141
|
-
# The JSON representation of this resource.
|
|
142
|
-
#
|
|
143
|
-
# @return [String]
|
|
144
|
-
#
|
|
145
|
-
def to_json
|
|
146
|
-
JSON.fast_generate(to_hash)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
82
|
private
|
|
150
83
|
|
|
151
84
|
#
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
require 'rexml/document'
|
|
2
|
+
|
|
3
|
+
module Artifactory
|
|
4
|
+
class Resource::Layout < Resource::Base
|
|
5
|
+
class << self
|
|
6
|
+
#
|
|
7
|
+
# Get a list of all repository layouts in the system.
|
|
8
|
+
#
|
|
9
|
+
# @param [Hash] options
|
|
10
|
+
# the list of options
|
|
11
|
+
#
|
|
12
|
+
# @option options [Artifactory::Client] :client
|
|
13
|
+
# the client object to make the request with
|
|
14
|
+
#
|
|
15
|
+
# @return [Array<Resource::Layout>]
|
|
16
|
+
# the list of layouts
|
|
17
|
+
#
|
|
18
|
+
def all(options = {})
|
|
19
|
+
config = Resource::System.configuration(options)
|
|
20
|
+
list_from_config('config/repoLayouts/repoLayout', config, options)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Find (fetch) a layout by its name.
|
|
25
|
+
#
|
|
26
|
+
# @example Find a layout by its name
|
|
27
|
+
# Layout.find('maven-2-default') #=> #<Layout name: 'maven-2-default' ...>
|
|
28
|
+
#
|
|
29
|
+
# @param [String] name
|
|
30
|
+
# the name of the layout to find
|
|
31
|
+
# @param [Hash] options
|
|
32
|
+
# the list of options
|
|
33
|
+
#
|
|
34
|
+
# @option options [Artifactory::Client] :client
|
|
35
|
+
# the client object to make the request with
|
|
36
|
+
#
|
|
37
|
+
# @return [Resource::Layout, nil]
|
|
38
|
+
# an instance of the layout that matches the given name, or +nil+
|
|
39
|
+
# if one does not exist
|
|
40
|
+
#
|
|
41
|
+
def find(name, options = {})
|
|
42
|
+
config = Resource::System.configuration(options)
|
|
43
|
+
find_from_config("config/repoLayouts/repoLayout/name[text()='#{name}']", config, options)
|
|
44
|
+
rescue Error::HTTPError => e
|
|
45
|
+
raise unless e.code == 404
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
#
|
|
51
|
+
# List all the child text elements in the Artifactory configuration file
|
|
52
|
+
# of a node matching the specified xpath
|
|
53
|
+
#
|
|
54
|
+
# @param [String] xpath
|
|
55
|
+
# xpath expression for the parent element whose children are to be listed
|
|
56
|
+
#
|
|
57
|
+
# @param [REXML] config
|
|
58
|
+
# Artifactory config as an REXML file
|
|
59
|
+
#
|
|
60
|
+
# @param [Hash] options
|
|
61
|
+
# the list of options
|
|
62
|
+
#
|
|
63
|
+
def list_from_config(xpath, config, options = {})
|
|
64
|
+
REXML::XPath.match(config, xpath).map do |r|
|
|
65
|
+
hash = {}
|
|
66
|
+
|
|
67
|
+
r.each_element_with_text do |l|
|
|
68
|
+
hash[l.name] = l.get_text
|
|
69
|
+
end
|
|
70
|
+
from_hash(hash, options)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
#
|
|
75
|
+
# Find all the sibling text elements in the Artifactory configuration file
|
|
76
|
+
# of a node matching the specified xpath
|
|
77
|
+
#
|
|
78
|
+
# @param [String] xpath
|
|
79
|
+
# xpath expression for the element whose siblings are to be found
|
|
80
|
+
#
|
|
81
|
+
# @param [REXML] config
|
|
82
|
+
# Artifactory configuration file as an REXML doc
|
|
83
|
+
#
|
|
84
|
+
# @param [Hash] options
|
|
85
|
+
# the list of options
|
|
86
|
+
#
|
|
87
|
+
def find_from_config(xpath, config, options = {})
|
|
88
|
+
name_node = REXML::XPath.match(config, xpath)
|
|
89
|
+
return nil if name_node.empty?
|
|
90
|
+
properties = {}
|
|
91
|
+
name_node[0].parent.each_element_with_text do |e|
|
|
92
|
+
properties[e.name] = e.text
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
from_hash(properties, options)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
attribute :name, ->{ raise 'Name missing!' }
|
|
100
|
+
attribute :artifact_path_pattern
|
|
101
|
+
attribute :distinctive_descriptor_path_pattern, 'true'
|
|
102
|
+
attribute :descriptor_path_pattern
|
|
103
|
+
attribute :folder_integration_revision_reg_exp
|
|
104
|
+
attribute :file_integration_revision_reg_exp
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -16,7 +16,7 @@ module Artifactory
|
|
|
16
16
|
def all(options = {})
|
|
17
17
|
client = extract_client!(options)
|
|
18
18
|
client.get('/api/repositories').map do |hash|
|
|
19
|
-
find(
|
|
19
|
+
find(hash['key'], client: client)
|
|
20
20
|
end.compact
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -38,50 +38,15 @@ module Artifactory
|
|
|
38
38
|
# an instance of the repository that matches the given name, or +nil+
|
|
39
39
|
# if one does not exist
|
|
40
40
|
#
|
|
41
|
-
def find(options = {})
|
|
41
|
+
def find(name, options = {})
|
|
42
42
|
client = extract_client!(options)
|
|
43
|
-
name = options[:name]
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
from_hash(
|
|
47
|
-
rescue Error::
|
|
44
|
+
response = client.get("/api/repositories/#{url_safe(name)}")
|
|
45
|
+
from_hash(response, client: client)
|
|
46
|
+
rescue Error::HTTPError => e
|
|
47
|
+
raise unless e.code == 400
|
|
48
48
|
nil
|
|
49
49
|
end
|
|
50
|
-
|
|
51
|
-
#
|
|
52
|
-
# Create a instance from the given Hash. This method extracts the "safe"
|
|
53
|
-
# information from the hash and adds them to the instance.
|
|
54
|
-
#
|
|
55
|
-
# @example Create a new resource from a hash
|
|
56
|
-
# Repository.from_hash('downloadUri' => '...', 'size' => '...')
|
|
57
|
-
#
|
|
58
|
-
# @param [Artifactory::Client] client
|
|
59
|
-
# the client object to make the request with
|
|
60
|
-
# @param [Hash] hash
|
|
61
|
-
# the hash to create the instance from
|
|
62
|
-
#
|
|
63
|
-
# @return [Resource::Repository]
|
|
64
|
-
#
|
|
65
|
-
def from_hash(hash, options = {})
|
|
66
|
-
client = extract_client!(options)
|
|
67
|
-
|
|
68
|
-
new.tap do |instance|
|
|
69
|
-
instance.blacked_out = hash['blackedOut']
|
|
70
|
-
instance.description = hash['description']
|
|
71
|
-
instance.checksum_policy = hash['checksumPolicyType']
|
|
72
|
-
instance.excludes_pattern = hash['excludesPattern']
|
|
73
|
-
instance.handle_releases = hash['handleReleases']
|
|
74
|
-
instance.handle_snapshots = hash['handleSnapshots']
|
|
75
|
-
instance.includes_pattern = hash['includesPattern']
|
|
76
|
-
instance.key = hash['key']
|
|
77
|
-
instance.maximum_unique_snapshots = hash['maxUniqueSnapshots']
|
|
78
|
-
instance.notes = hash['notes']
|
|
79
|
-
instance.property_sets = hash['propertySets']
|
|
80
|
-
instance.snapshot_version_behavior = hash['snapshotVersionBehavior']
|
|
81
|
-
instance.suppress_pom_checks = hash['suppressPomConsistencyChecks']
|
|
82
|
-
instance.type = hash['rclass']
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
50
|
end
|
|
86
51
|
|
|
87
52
|
attribute :blacked_out, false
|
|
@@ -95,57 +60,26 @@ module Artifactory
|
|
|
95
60
|
attribute :maximum_unique_snapshots, 0
|
|
96
61
|
attribute :notes
|
|
97
62
|
attribute :property_sets, []
|
|
63
|
+
attribute :repo_layout_ref, 'maven-2-default'
|
|
64
|
+
attribute :rclass, 'local'
|
|
98
65
|
attribute :snapshot_version_behavior, 'non-unique'
|
|
99
66
|
attribute :suppress_pom_checks, false
|
|
100
|
-
|
|
67
|
+
|
|
68
|
+
def save
|
|
69
|
+
client.put(api_path, to_json, headers)
|
|
70
|
+
true
|
|
71
|
+
end
|
|
101
72
|
|
|
102
73
|
#
|
|
103
|
-
# Upload
|
|
104
|
-
# object, that file descriptor is passed to the uploader. If the first
|
|
105
|
-
# parameter is a string, it is assumed to be the path to a local file on
|
|
106
|
-
# disk. This method will automatically construct the File object from the
|
|
107
|
-
# given path.
|
|
74
|
+
# Upload to a given repository
|
|
108
75
|
#
|
|
109
|
-
# @see
|
|
76
|
+
# @see Artifact#upload Upload syntax examples
|
|
110
77
|
#
|
|
111
|
-
# @
|
|
112
|
-
# file = File.new('/local/path/to/file.deb')
|
|
113
|
-
# repo = Repository.new('libs-release-local')
|
|
114
|
-
# repo.upload(file, 'file.deb')
|
|
115
|
-
#
|
|
116
|
-
# @example Upload an artifact from a path
|
|
117
|
-
# repo = Repository.new('libs-release-local')
|
|
118
|
-
# repo.upload('/local/path/to/file.deb', 'file.deb')
|
|
119
|
-
#
|
|
120
|
-
# @example Upload an artifact with matrix properties
|
|
121
|
-
# repo = Repository.new('libs-release-local')
|
|
122
|
-
# repo.upload('/local/path/to/file.deb', 'file.deb', {
|
|
123
|
-
# status: 'DEV',
|
|
124
|
-
# rating: 5,
|
|
125
|
-
# branch: 'master'
|
|
126
|
-
# })
|
|
127
|
-
#
|
|
128
|
-
# @param [String, File] path_or_io
|
|
129
|
-
# the file or path to the file to upload
|
|
130
|
-
# @param [String] path
|
|
131
|
-
# the path where this resource will live in the remote artifactory
|
|
132
|
-
# repository, relative to the current repo
|
|
133
|
-
# @param [Hash] headers
|
|
134
|
-
# the list of headers to send with the request
|
|
135
|
-
# @param [Hash] properties
|
|
136
|
-
# a list of matrix properties
|
|
78
|
+
# @return [Resource::Artifact]
|
|
137
79
|
#
|
|
138
80
|
def upload(path_or_io, path, properties = {}, headers = {})
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
else
|
|
142
|
-
File.new(File.expand_path(path_or_io))
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
matrix = to_matrix_properties(properties)
|
|
146
|
-
endpoint = File.join("#{url_safe_key}#{matrix}", path)
|
|
147
|
-
|
|
148
|
-
client.put(endpoint, { file: file }, headers)
|
|
81
|
+
artifact = Resource::Artifact.new
|
|
82
|
+
artifact.upload(key, path_or_io, path, properties, headers)
|
|
149
83
|
end
|
|
150
84
|
|
|
151
85
|
#
|
|
@@ -153,15 +87,7 @@ module Artifactory
|
|
|
153
87
|
# documentation for the possible responses when the checksums fail to
|
|
154
88
|
# match.
|
|
155
89
|
#
|
|
156
|
-
# @see
|
|
157
|
-
#
|
|
158
|
-
# @example Upload an artifact with a checksum
|
|
159
|
-
# repo = Repository.new('libs-release-local')
|
|
160
|
-
# repo.upload_with_checksum('/local/file', '/remote/path', 'ABCD1234')
|
|
161
|
-
#
|
|
162
|
-
# @param (see Repository#upload)
|
|
163
|
-
# @param [String] checksum
|
|
164
|
-
# the SHA1 checksum of the artifact to upload
|
|
90
|
+
# @see Artifact#upload More syntax examples
|
|
165
91
|
#
|
|
166
92
|
def upload_with_checksum(path_or_io, path, checksum, properties = {})
|
|
167
93
|
upload(path_or_io, path, properties,
|
|
@@ -174,13 +100,7 @@ module Artifactory
|
|
|
174
100
|
# Upload an artifact with the given archive. Consult the artifactory
|
|
175
101
|
# documentation for the format of the archive to upload.
|
|
176
102
|
#
|
|
177
|
-
# @see
|
|
178
|
-
#
|
|
179
|
-
# @example Upload an artifact with a checksum
|
|
180
|
-
# repo = Repositor.new('libs-release-local')
|
|
181
|
-
# repo.upload_from_archive('/local/archive', '/remote/path')#
|
|
182
|
-
#
|
|
183
|
-
# @param (see Repository#upload)
|
|
103
|
+
# @see Artifact#upload More syntax examples
|
|
184
104
|
#
|
|
185
105
|
def upload_from_archive(path_or_io, path, properties = {})
|
|
186
106
|
upload(path_or_io, path, properties,
|
|
@@ -211,7 +131,7 @@ module Artifactory
|
|
|
211
131
|
#
|
|
212
132
|
#
|
|
213
133
|
def files
|
|
214
|
-
response = get("/api/storage/#{
|
|
134
|
+
response = client.get("/api/storage/#{url_safe(key)}", {
|
|
215
135
|
deep: 0,
|
|
216
136
|
listFolders: 0,
|
|
217
137
|
mdTimestamps: 0,
|
|
@@ -221,30 +141,58 @@ module Artifactory
|
|
|
221
141
|
response['children']
|
|
222
142
|
end
|
|
223
143
|
|
|
144
|
+
#
|
|
145
|
+
# Delete this repository from artifactory, suppressing any +ResourceNotFound+
|
|
146
|
+
# exceptions might occur.
|
|
147
|
+
#
|
|
148
|
+
# @return [Boolean]
|
|
149
|
+
# true if the object was deleted successfully, false otherwise
|
|
150
|
+
#
|
|
151
|
+
def delete
|
|
152
|
+
client.delete(api_path)
|
|
153
|
+
true
|
|
154
|
+
rescue Error::HTTPError => e
|
|
155
|
+
false
|
|
156
|
+
end
|
|
157
|
+
|
|
224
158
|
private
|
|
225
159
|
|
|
226
160
|
#
|
|
161
|
+
# The path to this repository on the server.
|
|
227
162
|
#
|
|
163
|
+
# @return [String]
|
|
228
164
|
#
|
|
229
|
-
def
|
|
230
|
-
|
|
165
|
+
def api_path
|
|
166
|
+
"/api/repositories/#{url_safe(key)}"
|
|
231
167
|
end
|
|
232
168
|
|
|
233
169
|
#
|
|
234
|
-
#
|
|
170
|
+
# The default headers for this object. This includes the +Content-Type+.
|
|
235
171
|
#
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
172
|
+
# @return [Hash]
|
|
173
|
+
#
|
|
174
|
+
def headers
|
|
175
|
+
@headers ||= {
|
|
176
|
+
'Content-Type' => content_type
|
|
177
|
+
}
|
|
178
|
+
end
|
|
243
179
|
|
|
244
|
-
|
|
245
|
-
|
|
180
|
+
#
|
|
181
|
+
# The default Content-Type for this repository. It varies based on the
|
|
182
|
+
# repository type.
|
|
183
|
+
#
|
|
184
|
+
# @return [String]
|
|
185
|
+
#
|
|
186
|
+
def content_type
|
|
187
|
+
case rclass.to_s.downcase
|
|
188
|
+
when 'local'
|
|
189
|
+
'application/vnd.org.jfrog.artifactory.repositories.LocalRepositoryConfiguration+json'
|
|
190
|
+
when 'remote'
|
|
191
|
+
'application/vnd.org.jfrog.artifactory.repositories.RemoteRepositoryConfiguration+json'
|
|
192
|
+
when 'virtual'
|
|
193
|
+
'application/vnd.org.jfrog.artifactory.repositories.VirtualRepositoryConfiguration+json'
|
|
246
194
|
else
|
|
247
|
-
"
|
|
195
|
+
raise "Unknown Repository type `#{rclass}'!"
|
|
248
196
|
end
|
|
249
197
|
end
|
|
250
198
|
end
|