nexus_cli_nx 4.1.2
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 +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +23 -0
- data/Gemfile +41 -0
- data/Guardfile +27 -0
- data/LICENSE +15 -0
- data/README.md +123 -0
- data/Rakefile +1 -0
- data/Thorfile +66 -0
- data/VERSION +1 -0
- data/bin/nexus-cli +10 -0
- data/data/pom.xml.erb +13 -0
- data/features/nexus_oss.feature +259 -0
- data/features/pro/nexus_custom_metadata.feature +116 -0
- data/features/pro/nexus_pro.feature +101 -0
- data/features/step_definitions/cli_steps.rb +105 -0
- data/features/support/env.rb +64 -0
- data/lib/nexus_cli.rb +44 -0
- data/lib/nexus_cli/artifact.rb +44 -0
- data/lib/nexus_cli/base_remote.rb +16 -0
- data/lib/nexus_cli/cli.rb +7 -0
- data/lib/nexus_cli/configuration.rb +101 -0
- data/lib/nexus_cli/connection.rb +84 -0
- data/lib/nexus_cli/errors.rb +259 -0
- data/lib/nexus_cli/mixins/artifact_actions.rb +239 -0
- data/lib/nexus_cli/mixins/global_settings_actions.rb +64 -0
- data/lib/nexus_cli/mixins/logging_actions.rb +45 -0
- data/lib/nexus_cli/mixins/pro/custom_metadata_actions.rb +176 -0
- data/lib/nexus_cli/mixins/pro/smart_proxy_actions.rb +219 -0
- data/lib/nexus_cli/mixins/repository_actions.rb +245 -0
- data/lib/nexus_cli/mixins/user_actions.rb +125 -0
- data/lib/nexus_cli/n3_metadata.rb +77 -0
- data/lib/nexus_cli/remote/oss_remote.rb +11 -0
- data/lib/nexus_cli/remote/pro_remote.rb +59 -0
- data/lib/nexus_cli/remote_factory.rb +30 -0
- data/lib/nexus_cli/tasks.rb +507 -0
- data/lib/nexus_cli/version.rb +6 -0
- data/nexus_cli_nx.gemspec +32 -0
- data/spec/fixtures/metadata_search.xml +10 -0
- data/spec/fixtures/nexus.config +4 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/unit/nexus_cli/artifact_spec.rb +82 -0
- data/spec/unit/nexus_cli/configuration_spec.rb +159 -0
- data/spec/unit/nexus_cli/mixins/pro/custom_metadata_actions_spec.rb +21 -0
- data/spec/unit/nexus_cli/oss_remote_spec.rb +83 -0
- data/spec/unit/nexus_cli/pro_remote_spec.rb +110 -0
- data/spec/unit/nexus_cli/remote_factory_spec.rb +42 -0
- metadata +263 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
module NexusCli
|
2
|
+
class BaseRemote
|
3
|
+
attr_reader :configuration
|
4
|
+
attr_reader :connection
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@connection, :status, :nexus_url, :nexus, :sanitize_for_id, :running_nexus_pro?
|
8
|
+
|
9
|
+
# @param [Hash] overrides
|
10
|
+
# @param [Boolean] ssl_verify
|
11
|
+
def initialize(overrides, ssl_verify=true)
|
12
|
+
@configuration = overrides ? Configuration.from_overrides(overrides) : Configuration.from_file
|
13
|
+
@connection = Connection.new(configuration, ssl_verify)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module NexusCli
|
4
|
+
class Configuration
|
5
|
+
DEFAULT_FILE = (ENV['HOME'] ? "~/.nexus_cli" : "/root/.nexus_cli").freeze
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# The filepath to the nexus cli configuration file
|
9
|
+
#
|
10
|
+
# @return [String]
|
11
|
+
def file_path
|
12
|
+
File.expand_path(ENV['NEXUS_CONFIG'] || File.expand_path(DEFAULT_FILE))
|
13
|
+
end
|
14
|
+
|
15
|
+
# Creates a new instance of the Configuration object based on some overrides
|
16
|
+
#
|
17
|
+
# @param [Hash] overrides
|
18
|
+
#
|
19
|
+
# @return [NexusCli::Configuration]
|
20
|
+
def from_overrides(overrides)
|
21
|
+
raise MissingSettingsFileException unless overrides
|
22
|
+
overrides = overrides.with_indifferent_access
|
23
|
+
|
24
|
+
configuration = (load_config || Hash.new).with_indifferent_access
|
25
|
+
configuration.merge!(overrides)
|
26
|
+
new(configuration)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates a new instance of the Configuration object from the config file
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# @return [NexusCli::Configuration]
|
33
|
+
def from_file
|
34
|
+
config = load_config
|
35
|
+
raise MissingSettingsFileException unless config
|
36
|
+
config = config.with_indifferent_access
|
37
|
+
new(config)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Validates an instance of the Configuration object and raises when there
|
41
|
+
# is an error with it
|
42
|
+
#
|
43
|
+
# @param config [NexusCli::Configuration]
|
44
|
+
#
|
45
|
+
# @raise [NexusCli::InvalidSettingsException]
|
46
|
+
def validate!(config)
|
47
|
+
unless config.valid?
|
48
|
+
raise InvalidSettingsException.new(config.errors)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Loads the config file
|
55
|
+
#
|
56
|
+
# @return [Hash]
|
57
|
+
def load_config
|
58
|
+
begin
|
59
|
+
config = YAML.load_file(file_path)
|
60
|
+
rescue Errno::ENOENT
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate!
|
67
|
+
self.class.validate!(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid?
|
71
|
+
errors.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
def errors
|
75
|
+
result = Hash.new
|
76
|
+
result[:url] = ["url required"] unless url.is_a?(String) && url.size > 0
|
77
|
+
result[:repository] = ["repository required"] unless repository.is_a?(String) && repository.size > 0
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_accessor :url
|
82
|
+
attr_accessor :repository
|
83
|
+
attr_accessor :username
|
84
|
+
attr_accessor :password
|
85
|
+
|
86
|
+
def initialize(options)
|
87
|
+
@url = options[:url]
|
88
|
+
@repository = options[:repository]
|
89
|
+
@username = options[:username]
|
90
|
+
@password = options[:password]
|
91
|
+
|
92
|
+
if @repository.is_a?(String)
|
93
|
+
@repository = @repository.gsub(' ', '_')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def [](attr)
|
98
|
+
self.instance_variable_get('@' + attr.to_s)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module NexusCli
|
2
|
+
class Connection
|
3
|
+
attr_reader :nexus
|
4
|
+
attr_reader :configuration
|
5
|
+
attr_reader :ssl_verify
|
6
|
+
|
7
|
+
def initialize(configuration, ssl_verify)
|
8
|
+
@configuration = configuration
|
9
|
+
@ssl_verify = ssl_verify
|
10
|
+
@nexus = setup_nexus(configuration)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Joins a given url to the current url stored in the configuraiton
|
14
|
+
# and returns the combined String.
|
15
|
+
#
|
16
|
+
# @param [String] url
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
def nexus_url(url)
|
20
|
+
File.join(configuration['url'], url)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets that current status of the Nexus server. On a non-error
|
24
|
+
# status code, returns a Hash of values from the server.
|
25
|
+
#
|
26
|
+
# @return [Hash]
|
27
|
+
def status
|
28
|
+
response = nexus.get(nexus_url("service/local/status"))
|
29
|
+
case response.status
|
30
|
+
when 200
|
31
|
+
doc = REXML::Document.new(response.content).elements["/status/data"]
|
32
|
+
data = Hash.new
|
33
|
+
data['app_name'] = doc.elements["appName"].text
|
34
|
+
data['version'] = doc.elements["version"].text
|
35
|
+
data['edition_long'] = doc.elements["editionLong"].text
|
36
|
+
data['state'] = doc.elements["state"].text
|
37
|
+
data['started_at'] = doc.elements["startedAt"].text
|
38
|
+
data['base_url'] = doc.elements["baseUrl"].text
|
39
|
+
return data
|
40
|
+
when 401
|
41
|
+
raise PermissionsException
|
42
|
+
when 503
|
43
|
+
raise CouldNotConnectToNexusException
|
44
|
+
else
|
45
|
+
raise UnexpectedStatusCodeException.new(response.status)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Transforms a given [String] into a sanitized version by
|
50
|
+
# replacing spaces with underscores and downcasing.
|
51
|
+
#
|
52
|
+
# @param unsanitized_string [String] the String to sanitize
|
53
|
+
#
|
54
|
+
# @return [String] the sanitized String
|
55
|
+
def sanitize_for_id(unsanitized_string)
|
56
|
+
unsanitized_string.gsub(" ", "_").downcase
|
57
|
+
end
|
58
|
+
|
59
|
+
# Determines whether or not the Nexus server being
|
60
|
+
# connected to is running Nexus Pro.
|
61
|
+
def running_nexus_pro?
|
62
|
+
status['edition_long'] == "Professional"
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Returns an HTTPClient instance with settings to connect
|
68
|
+
# to a Nexus server.
|
69
|
+
#
|
70
|
+
# @return [HTTPClient]
|
71
|
+
def setup_nexus(configuration)
|
72
|
+
client = HTTPClient.new
|
73
|
+
client.send_timeout = 6000
|
74
|
+
client.receive_timeout = 6000
|
75
|
+
if configuration['username'] and configuration['password']
|
76
|
+
# https://github.com/nahi/httpclient/issues/63
|
77
|
+
client.set_auth(nil, configuration['username'], configuration['password'])
|
78
|
+
client.www_auth.basic_auth.challenge(configuration['url'])
|
79
|
+
end
|
80
|
+
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE unless ssl_verify
|
81
|
+
client
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module NexusCli
|
4
|
+
class NexusCliError < StandardError
|
5
|
+
class << self
|
6
|
+
def status_code(code)
|
7
|
+
define_method(:status_code) { code }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ArtifactMalformedException < NexusCliError
|
13
|
+
def message
|
14
|
+
"Please submit your request using 4 colon-separated values. `groupId:artifactId:version:extension`"
|
15
|
+
end
|
16
|
+
status_code(100)
|
17
|
+
end
|
18
|
+
|
19
|
+
class ArtifactNotFoundException < NexusCliError
|
20
|
+
def message
|
21
|
+
"The artifact you requested information for could not be found. Please ensure it exists inside the Nexus."
|
22
|
+
end
|
23
|
+
status_code(101)
|
24
|
+
end
|
25
|
+
|
26
|
+
class InvalidSettingsException < NexusCliError
|
27
|
+
def initialize(errors)
|
28
|
+
@errors = errors
|
29
|
+
end
|
30
|
+
|
31
|
+
def message
|
32
|
+
"Your configuration has an error: #{@errors}"
|
33
|
+
end
|
34
|
+
status_code(102)
|
35
|
+
end
|
36
|
+
|
37
|
+
class MissingSettingsFileException < NexusCliError
|
38
|
+
def message
|
39
|
+
"The .nexus_cli file is missing or corrupt. You can either fix the .nexus_cli file or pass the --overrides hash."
|
40
|
+
end
|
41
|
+
status_code(103)
|
42
|
+
end
|
43
|
+
|
44
|
+
class NonSecureConnectionException < NexusCliError
|
45
|
+
def message
|
46
|
+
"Your communication with a server using an SSL certificate failed during validation. You may want to try the --insecure option."
|
47
|
+
end
|
48
|
+
status_code(104)
|
49
|
+
end
|
50
|
+
|
51
|
+
class CouldNotConnectToNexusException < NexusCliError
|
52
|
+
def message
|
53
|
+
"Could not connect to Nexus. Please ensure the url you are using is reachable."
|
54
|
+
end
|
55
|
+
status_code(105)
|
56
|
+
end
|
57
|
+
|
58
|
+
class PermissionsException < NexusCliError
|
59
|
+
def message
|
60
|
+
"Your request was denied by the Nexus server due to a permissions error. You will need to administer the Nexus or use a different user/password in .nexus_cli."
|
61
|
+
end
|
62
|
+
status_code(106)
|
63
|
+
end
|
64
|
+
|
65
|
+
class BadUploadRequestException < NexusCliError
|
66
|
+
def message
|
67
|
+
%{Your request was denied by the Nexus server due to a bad request and your artifact has not been uploaded.
|
68
|
+
This could mean several things:
|
69
|
+
Your .nexus_cli['repository'] is invalid.
|
70
|
+
The artifact with this identifier already exists inside the repository and that repository does not allow multiple deployments.}
|
71
|
+
end
|
72
|
+
status_code(107)
|
73
|
+
end
|
74
|
+
|
75
|
+
class NotNexusProException < NexusCliError
|
76
|
+
def message
|
77
|
+
"You cannot use this feature unless you are using Nexus Professional."
|
78
|
+
end
|
79
|
+
status_code(108)
|
80
|
+
end
|
81
|
+
|
82
|
+
class SearchParameterMalformedException < NexusCliError
|
83
|
+
def message
|
84
|
+
"Submit your search request specifying one or more 3 colon-separated values: `key:type:value`. The available search types are `equal`, `matches`, `bounded`, and `notequal`."
|
85
|
+
end
|
86
|
+
status_code(109)
|
87
|
+
end
|
88
|
+
|
89
|
+
class BadSearchRequestException < NexusCliError
|
90
|
+
def message
|
91
|
+
"Your request was denied by the Nexus server due to a bad request. Check that your search parameters contain valid values."
|
92
|
+
end
|
93
|
+
status_code(110)
|
94
|
+
end
|
95
|
+
|
96
|
+
class BadSettingsException < NexusCliError
|
97
|
+
def initialize(body)
|
98
|
+
@server_response = JSON.pretty_generate(JSON.parse(body))
|
99
|
+
end
|
100
|
+
|
101
|
+
def message
|
102
|
+
%{Your global_settings.json file is malformed and could not be uploaded to Nexus.
|
103
|
+
The output from the server was:
|
104
|
+
#{@server_response}}
|
105
|
+
end
|
106
|
+
status_code(111)
|
107
|
+
end
|
108
|
+
|
109
|
+
class CreateRepsitoryException < NexusCliError
|
110
|
+
def initialize(body)
|
111
|
+
@server_response = JSON.pretty_generate(JSON.parse(body))
|
112
|
+
end
|
113
|
+
|
114
|
+
def message
|
115
|
+
%{Your create repository command failed due to the following:
|
116
|
+
#{@server_response}}
|
117
|
+
end
|
118
|
+
status_code(112)
|
119
|
+
end
|
120
|
+
|
121
|
+
class RepositoryDoesNotExistException < NexusCliError
|
122
|
+
def message
|
123
|
+
"The repository you are trying to delete does not exist."
|
124
|
+
end
|
125
|
+
status_code(113)
|
126
|
+
end
|
127
|
+
|
128
|
+
class RepositoryNotFoundException < NexusCliError
|
129
|
+
def message
|
130
|
+
"The repository you provided could not be found. Please ensure the repository exists."
|
131
|
+
end
|
132
|
+
status_code(114)
|
133
|
+
end
|
134
|
+
|
135
|
+
class UnexpectedStatusCodeException < NexusCliError
|
136
|
+
def initialize(code)
|
137
|
+
@code = code
|
138
|
+
end
|
139
|
+
|
140
|
+
def message
|
141
|
+
"The server responded with a #{@code} status code which is unexpected. Please submit a bug."
|
142
|
+
end
|
143
|
+
status_code(115)
|
144
|
+
end
|
145
|
+
|
146
|
+
class N3ParameterMalformedException < NexusCliError
|
147
|
+
def message
|
148
|
+
"Submit your tag request specifying one or more 2 colon-separated values: `key:value`. The key can only consist of alphanumeric characters."
|
149
|
+
end
|
150
|
+
status_code(116)
|
151
|
+
end
|
152
|
+
|
153
|
+
class CreateUserException < NexusCliError
|
154
|
+
def initialize(body)
|
155
|
+
@server_response = JSON.pretty_generate(JSON.parse(body))
|
156
|
+
end
|
157
|
+
|
158
|
+
def message
|
159
|
+
%{Your create user command failed due to the following:
|
160
|
+
#{@server_response}}
|
161
|
+
end
|
162
|
+
status_code(117)
|
163
|
+
end
|
164
|
+
|
165
|
+
class UserNotFoundException < NexusCliError
|
166
|
+
def initialize(id)
|
167
|
+
@id = id
|
168
|
+
end
|
169
|
+
|
170
|
+
def message
|
171
|
+
"A user with the ID of #{@id} could not be found. Please ensure it exists."
|
172
|
+
end
|
173
|
+
status_code(118)
|
174
|
+
end
|
175
|
+
|
176
|
+
class UpdateUserException < NexusCliError
|
177
|
+
def initialize(body)
|
178
|
+
@server_response = JSON.pretty_generate(JSON.parse(body))
|
179
|
+
end
|
180
|
+
|
181
|
+
def message
|
182
|
+
%{Your update user command failed due to the following:
|
183
|
+
#{@server_response}}
|
184
|
+
end
|
185
|
+
status_code(119)
|
186
|
+
end
|
187
|
+
|
188
|
+
class InvalidCredentialsException < NexusCliError
|
189
|
+
def message
|
190
|
+
"Invalid Credentials were supplied. Please make sure you are passing the correct values."
|
191
|
+
end
|
192
|
+
status_code(120)
|
193
|
+
end
|
194
|
+
|
195
|
+
class NotProxyRepositoryException < NexusCliError
|
196
|
+
def initialize(repository_id)
|
197
|
+
@repository_id = repository_id
|
198
|
+
end
|
199
|
+
|
200
|
+
def message
|
201
|
+
"The #{@repository_id} repository is not a Proxy repository and cannot subscribe to artifact updates."
|
202
|
+
end
|
203
|
+
status_code(121)
|
204
|
+
end
|
205
|
+
|
206
|
+
class LicenseInstallFailure < NexusCliError
|
207
|
+
def message
|
208
|
+
"Either your Nexus already has a license installed or there was a problem with the file you uploaded."
|
209
|
+
end
|
210
|
+
status_code(122)
|
211
|
+
end
|
212
|
+
|
213
|
+
class InvalidLoggingLevelException < NexusCliError
|
214
|
+
def message
|
215
|
+
"Logging level must be set to one of either INFO, DEBUG, or ERROR."
|
216
|
+
end
|
217
|
+
status_code(123)
|
218
|
+
end
|
219
|
+
|
220
|
+
class N3NotFoundException < NexusCliError
|
221
|
+
def message
|
222
|
+
"The artifact does not have any custom metadata added yet."
|
223
|
+
end
|
224
|
+
status_code(124)
|
225
|
+
end
|
226
|
+
|
227
|
+
class SSLException < NexusCliError
|
228
|
+
def message
|
229
|
+
"You are attempting to communicate securely with a server that has an untrusted certificate. Please ensure your certificate is correct or set ssl_verify to false."
|
230
|
+
end
|
231
|
+
status_code(125)
|
232
|
+
end
|
233
|
+
|
234
|
+
class RepositoryInGroupException < NexusCliError
|
235
|
+
def message
|
236
|
+
"You are attempting to add a repository that is already a part of this group."
|
237
|
+
end
|
238
|
+
status_code(126)
|
239
|
+
end
|
240
|
+
|
241
|
+
class RepositoryNotInGroupException < NexusCliError
|
242
|
+
def message
|
243
|
+
"You are attempting to remove a repository that isn't a part of the group."
|
244
|
+
end
|
245
|
+
status_code(127)
|
246
|
+
end
|
247
|
+
|
248
|
+
class NexusHTTP404 < NexusCliError
|
249
|
+
def initialize(body)
|
250
|
+
@server_response = body
|
251
|
+
end
|
252
|
+
|
253
|
+
def message
|
254
|
+
%{Your command failed and the server returned an error code. The output of the response was:
|
255
|
+
#{@server_response}}
|
256
|
+
end
|
257
|
+
status_code(128)
|
258
|
+
end
|
259
|
+
end
|