nexus_cli 0.2.1 → 0.3.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.
- data/VERSION +1 -1
- data/features/cli/nexus_oss.feature +49 -0
- data/features/step_definitions/cli_steps.rb +16 -22
- data/features/support/env.rb +12 -7
- data/lib/nexus_cli.rb +3 -1
- data/lib/nexus_cli/errors.rb +14 -0
- data/lib/nexus_cli/nexus_oss_remote.rb +109 -0
- data/lib/nexus_cli/nexus_pro_remote.rb +112 -0
- data/lib/nexus_cli/nexus_remote_factory.rb +59 -0
- data/lib/nexus_cli/tasks.rb +45 -31
- data/nexus_cli.gemspec +1 -0
- metadata +25 -9
- data/features/cli/pull_artifact.feature +0 -17
- data/features/cli/push_artifact.feature +0 -8
- data/lib/nexus_cli/remote.rb +0 -132
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
@@ -0,0 +1,49 @@
|
|
1
|
+
Feature: Use the Nexus CLI
|
2
|
+
As a CLI user
|
3
|
+
I need commands to get Nexus status, push, pull
|
4
|
+
|
5
|
+
Scenario: Get Nexus Status
|
6
|
+
When I call the nexus "status" command
|
7
|
+
Then the output should contain:
|
8
|
+
"""
|
9
|
+
Application Name: Sonatype Nexus
|
10
|
+
Version: 2.0.5
|
11
|
+
"""
|
12
|
+
And the exit status should be 0
|
13
|
+
|
14
|
+
Scenario: Push an Artifact
|
15
|
+
When I push an artifact with the GAV of "com.test:mytest:1.0.0:tgz"
|
16
|
+
Then the output should contain:
|
17
|
+
"""
|
18
|
+
Artifact com.test:mytest:1.0.0:tgz has been successfully pushed to Nexus.
|
19
|
+
"""
|
20
|
+
And the exit status should be 0
|
21
|
+
|
22
|
+
Scenario: Pull an artifact
|
23
|
+
When I call the nexus "pull com.test:mytest:1.0.0:tgz" command
|
24
|
+
Then the output should contain:
|
25
|
+
"""
|
26
|
+
Artifact has been retrived and can be found at path:
|
27
|
+
"""
|
28
|
+
And the exit status should be 0
|
29
|
+
|
30
|
+
Scenario: Pull an artifact to a specific place
|
31
|
+
When I pull an artifact with the GAV of "com.test:mytest:1.0.0:tgz" to a temp directory
|
32
|
+
Then I should have a copy of the "mytest-1.0.0.tgz" artifact in a temp directory
|
33
|
+
|
34
|
+
Scenario: Get an artifact's info
|
35
|
+
When I call the nexus "info com.test:mytest:1.0.0:tgz" command
|
36
|
+
Then the output should contain:
|
37
|
+
"""
|
38
|
+
<groupId>com.test</groupId>
|
39
|
+
"""
|
40
|
+
And the exit status should be 0
|
41
|
+
|
42
|
+
Scenario: Attempt to delete an artifact
|
43
|
+
When I delete an artifact with the GAV of "com.test:mytest:1.0.0:tgz"
|
44
|
+
And I call the nexus "info com.test:mytest:1.0.0:tgz" command
|
45
|
+
Then the output should contain:
|
46
|
+
"""
|
47
|
+
The artifact you requested information for could not be found. Please ensure it exists inside the Nexus.
|
48
|
+
"""
|
49
|
+
And the exit status should be 101
|
@@ -1,32 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
end
|
4
|
-
|
5
|
-
Then /^I should have a copy of the "([^"]*)" artifact on my computer$/ do |arg1|
|
6
|
-
File.exists?(arg1).should be_true
|
7
|
-
end
|
1
|
+
require 'aruba/api'
|
2
|
+
World(Aruba::Api)
|
8
3
|
|
9
|
-
When /^I
|
10
|
-
|
4
|
+
When /^I call the nexus "(.*?)" command$/ do |command|
|
5
|
+
step "I run `nexus-cli #{command} --overrides=#{get_overrides}`"
|
11
6
|
end
|
12
7
|
|
13
|
-
|
14
|
-
|
15
|
-
File.
|
8
|
+
When /^I push an artifact with the GAV of "(.*)"$/ do |gav|
|
9
|
+
groupId, artifactId, version, extension = gav.split(":")
|
10
|
+
file = File.new(File.join(temp_dir, "#{artifactId}-#{version}.#{extension}"), 'w')
|
11
|
+
file.puts "some data"
|
12
|
+
file.close
|
13
|
+
step "I run `nexus-cli push #{gav} #{file.path}`"
|
16
14
|
end
|
17
15
|
|
18
|
-
|
19
|
-
|
16
|
+
When /^I pull an artifact with the GAV of "(.*?)" to a temp directory$/ do |gav|
|
17
|
+
step "I run `nexus-cli pull #{gav} --destination #{temp_dir}`"
|
20
18
|
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
file.puts "some data"
|
25
|
-
file.close
|
26
|
-
file = File.open("myFile.tgz", 'r')
|
27
|
-
NexusCli::Remote.push_artifact "com.foo.bar:myFile:1.0.0:tgz", file
|
20
|
+
Then /^I should have a copy of the "(.*?)" artifact in a temp directory$/ do |fileName|
|
21
|
+
File.exists?(File.join(temp_dir, fileName)).should == true
|
28
22
|
end
|
29
23
|
|
30
|
-
|
31
|
-
|
24
|
+
When /^I delete an artifact with the GAV of "(.*)"$/ do |gav|
|
25
|
+
nexus_remote.delete_artifact(gav)
|
32
26
|
end
|
data/features/support/env.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
require 'aruba/cucumber'
|
2
|
-
|
3
2
|
$:.push "#{File.dirname(__FILE__)}/../../lib/"
|
4
3
|
require 'nexus_cli'
|
5
4
|
require 'rspec'
|
6
5
|
|
7
|
-
|
8
|
-
|
6
|
+
def get_overrides
|
7
|
+
@overrides ||= {:url => 'http://localhost:8081/nexus', :repository => 'releases', :username => 'deployment', :password => 'deployment123'}
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
def temp_dir
|
11
|
+
@tmpdir ||= Dir.mktmpdir
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
def nexus_remote
|
15
|
+
@nexus_remote ||= NexusCli::Factory.create(get_overrides)
|
15
16
|
end
|
17
|
+
|
18
|
+
at_exit do
|
19
|
+
FileUtils.rm_rf(temp_dir)
|
20
|
+
end
|
data/lib/nexus_cli.rb
CHANGED
@@ -2,7 +2,9 @@ require 'nexus_cli/tasks'
|
|
2
2
|
require 'nexus_cli/cli'
|
3
3
|
require 'nexus_cli/errors'
|
4
4
|
require 'nexus_cli/kernel'
|
5
|
-
require 'nexus_cli/
|
5
|
+
require 'nexus_cli/nexus_remote_factory'
|
6
|
+
require 'nexus_cli/nexus_oss_remote'
|
7
|
+
require 'nexus_cli/nexus_pro_remote'
|
6
8
|
|
7
9
|
module NexusCli
|
8
10
|
class << self
|
data/lib/nexus_cli/errors.rb
CHANGED
@@ -76,4 +76,18 @@ This could mean several things:
|
|
76
76
|
end
|
77
77
|
status_code(108)
|
78
78
|
end
|
79
|
+
|
80
|
+
class SearchParameterMalformedException < NexusCliError
|
81
|
+
def message
|
82
|
+
"Submit your search request specifying the search key, type, and value. The available search types are `equal`, `matches`, `bounded`, and `notequal`."
|
83
|
+
end
|
84
|
+
status_code(109)
|
85
|
+
end
|
86
|
+
|
87
|
+
class BadSearchRequestException < NexusCliError
|
88
|
+
def message
|
89
|
+
"Your request was denied by the Nexus server due to a bad request. Check that your search parameters contain valid values."
|
90
|
+
end
|
91
|
+
status_code(110)
|
92
|
+
end
|
79
93
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'restclient'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module NexusCli
|
6
|
+
class OSSRemote
|
7
|
+
|
8
|
+
def initialize(overrides)
|
9
|
+
@configuration = parse_configuration(overrides)
|
10
|
+
end
|
11
|
+
|
12
|
+
def configuration
|
13
|
+
return @configuration if @configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
def nexus
|
17
|
+
@nexus ||= RestClient::Resource.new configuration["url"], :user => configuration["username"], :password => configuration["password"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def status
|
21
|
+
doc = Nokogiri::XML(nexus['service/local/status'].get).xpath("/status/data")
|
22
|
+
data = Hash.new
|
23
|
+
data['app_name'] = doc.xpath("appName")[0].text
|
24
|
+
data['version'] = doc.xpath("version")[0].text
|
25
|
+
data['edition_long'] = doc.xpath("editionLong")[0].text
|
26
|
+
data['state'] = doc.xpath("state")[0].text
|
27
|
+
data['started_at'] = doc.xpath("startedAt")[0].text
|
28
|
+
data['base_url'] = doc.xpath("baseUrl")[0].text
|
29
|
+
return data
|
30
|
+
end
|
31
|
+
|
32
|
+
def pull_artifact(artifact, destination)
|
33
|
+
group_id, artifact_id, version, extension = parse_artifact_string(artifact)
|
34
|
+
begin
|
35
|
+
fileData = nexus['service/local/artifact/maven/redirect'].get({:params => {:r => configuration['repository'], :g => group_id, :a => artifact_id, :v => version, :e => extension}})
|
36
|
+
rescue RestClient::ResourceNotFound
|
37
|
+
raise ArtifactNotFoundException
|
38
|
+
end
|
39
|
+
artifact = nil
|
40
|
+
destination = File.join(File.expand_path(destination || "."), "#{artifact_id}-#{version}.#{extension}")
|
41
|
+
artifact = File.open(destination, 'w')
|
42
|
+
artifact.write(fileData)
|
43
|
+
artifact.close()
|
44
|
+
File.expand_path(artifact.path)
|
45
|
+
end
|
46
|
+
|
47
|
+
def push_artifact(artifact, file)
|
48
|
+
group_id, artifact_id, version, extension = parse_artifact_string(artifact)
|
49
|
+
nexus['service/local/artifact/maven/content'].post({:hasPom => false, :g => group_id, :a => artifact_id, :v => version, :e => extension, :p => extension, :r => configuration['repository'],
|
50
|
+
:file => File.new(file)}) do |response, request, result, &block|
|
51
|
+
case response.code
|
52
|
+
when 400
|
53
|
+
raise BadUploadRequestException
|
54
|
+
when 401
|
55
|
+
raise PermissionsException
|
56
|
+
when 403
|
57
|
+
raise PermissionsException
|
58
|
+
when 404
|
59
|
+
raise CouldNotConnectToNexusException
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete_artifact(artifact)
|
65
|
+
group_id, artifact_id, version = parse_artifact_string(artifact)
|
66
|
+
delete_string = "content/repositories/releases/#{group_id.gsub(".", "/")}/#{artifact_id.gsub(".", "/")}/#{version}"
|
67
|
+
Kernel.quietly {`curl --request DELETE #{File.join(configuration['url'], delete_string)} -u #{configuration['username']}:#{configuration['password']}`}
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_artifact_info(artifact)
|
71
|
+
group_id, artifact_id, version, extension = parse_artifact_string(artifact)
|
72
|
+
begin
|
73
|
+
nexus['service/local/artifact/maven/resolve'].get({:params => {:r => configuration['repository'], :g => group_id, :a => artifact_id, :v => version, :e => extension}})
|
74
|
+
rescue Errno::ECONNREFUSED => e
|
75
|
+
raise CouldNotConnectToNexusException
|
76
|
+
rescue RestClient::ResourceNotFound => e
|
77
|
+
raise ArtifactNotFoundException
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
def parse_artifact_string(artifact)
|
83
|
+
split_artifact = artifact.split(":")
|
84
|
+
if(split_artifact.size < 4)
|
85
|
+
raise ArtifactMalformedException
|
86
|
+
end
|
87
|
+
return split_artifact
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_configuration(overrides)
|
91
|
+
begin
|
92
|
+
config = YAML::load_file(File.expand_path("~/.nexus_cli"))
|
93
|
+
rescue
|
94
|
+
end
|
95
|
+
if config.nil? && (overrides.nil? || overrides.empty?)
|
96
|
+
raise MissingSettingsFileException
|
97
|
+
end
|
98
|
+
overrides.each{|key, value| config[key] = value} unless overrides.nil? || overrides.empty?
|
99
|
+
validate_config(config)
|
100
|
+
config
|
101
|
+
end
|
102
|
+
|
103
|
+
def validate_config(configuration)
|
104
|
+
["url", "repository", "username","password"].each do |key|
|
105
|
+
raise InvalidSettingsException.new(key) unless configuration.has_key?(key)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'restclient'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module NexusCli
|
7
|
+
class ProRemote < OSSRemote
|
8
|
+
|
9
|
+
def get_artifact_custom_info(artifact)
|
10
|
+
parse_n3(get_artifact_custom_info_n3(artifact))
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_artifact_custom_info_n3(artifact)
|
14
|
+
group_id, artifact_id, version, extension = parse_artifact_string(artifact)
|
15
|
+
file_name = "#{artifact_id}-#{version}.#{extension}.n3"
|
16
|
+
get_string = "content/repositories/#{configuration['repository']}/.meta/#{group_id.gsub(".", "/")}/#{artifact_id.gsub(".", "/")}/#{version}/#{file_name}"
|
17
|
+
begin
|
18
|
+
nexus[get_string].get
|
19
|
+
rescue RestClient::ResourceNotFound => e
|
20
|
+
raise ArtifactNotFoundException
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_artifact_custom_info(artifact, file, insecure)
|
25
|
+
# Check if artifact exists before posting custom metadata.
|
26
|
+
get_artifact_info(artifact)
|
27
|
+
# Update the custom metadata using the n3 file.
|
28
|
+
group_id, artifact_id, version, extension = parse_artifact_string(artifact)
|
29
|
+
file_name = "#{artifact_id}-#{version}.#{extension}.n3"
|
30
|
+
post_string = "content/repositories/#{configuration['repository']}/.meta/#{group_id.gsub(".", "/")}/#{artifact_id.gsub(".", "/")}/#{version}/#{file_name}"
|
31
|
+
|
32
|
+
# Read in nexus n3 file. If this is a newly-added artifact, there will be no n3 file so escape the exception.
|
33
|
+
begin
|
34
|
+
nexus_n3 = get_artifact_custom_info_n3(artifact, overrides)
|
35
|
+
rescue ArtifactNotFoundException
|
36
|
+
nexus_n3 = ""
|
37
|
+
end
|
38
|
+
|
39
|
+
# Read in local n3 file.
|
40
|
+
local_n3 = File.open(file).read
|
41
|
+
|
42
|
+
n3_user_urns = { "head" => "<urn:maven/artifact##{group_id}:#{artifact_id}:#{version}::#{extension}> a <urn:maven#artifact>" }
|
43
|
+
# Get all the urn:nexus/user# keys and consolidate.
|
44
|
+
# First, get the nexus keys.
|
45
|
+
nexus_n3.each_line { |line|
|
46
|
+
if line.match(/urn:nexus\/user#/)
|
47
|
+
tag, value = parse_n3_line(line)
|
48
|
+
n3_user_urns[tag] = "\t<urn:nexus/user##{tag}> \"#{value}\"" unless tag.empty? || value.empty?
|
49
|
+
end
|
50
|
+
}
|
51
|
+
# Next, get the local keys and update the nexus keys.
|
52
|
+
local_n3.each_line { |line|
|
53
|
+
if line.match(/urn:nexus\/user#/)
|
54
|
+
tag, value = parse_n3_line(line)
|
55
|
+
# Delete the nexus key if the local key has no value.
|
56
|
+
if n3_user_urns.has_key?(tag) && value.empty?
|
57
|
+
n3_user_urns.delete(tag)
|
58
|
+
else
|
59
|
+
n3_user_urns[tag] = "\t<urn:nexus/user##{tag}> \"#{value}\"" unless tag.empty? || value.empty?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
n3_data = n3_user_urns.values.join(" ;\n") + " ."
|
65
|
+
n3_temp = Tempfile.new("nexus_n3")
|
66
|
+
begin
|
67
|
+
n3_temp.write(n3_data)
|
68
|
+
n3_temp.rewind
|
69
|
+
Kernel.quietly {`curl -T #{n3_temp.path} #{File.join(configuration['url'], post_string)} -u #{configuration['username']}:#{configuration['password']}`}
|
70
|
+
ensure
|
71
|
+
n3_temp.close
|
72
|
+
n3_temp.unlink
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def search_artifacts(key, type, value)
|
77
|
+
if key.empty? || type.empty? || value.empty?
|
78
|
+
raise SearchParameterMalformedException
|
79
|
+
end
|
80
|
+
begin
|
81
|
+
nexus['service/local/search/m2/freeform'].get ({params: {p: key, t: type, v: value}}) do |response, request, result, &block|
|
82
|
+
raise BadSearchRequestException if response.code == 400
|
83
|
+
doc = Nokogiri::XML(response.body).xpath("/search-results")
|
84
|
+
return doc.xpath("count")[0].text.to_i > 0 ? doc.to_s : "No search results."
|
85
|
+
end
|
86
|
+
rescue RestClient::ResourceNotFound => e
|
87
|
+
raise ArtifactNotFoundException
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def parse_n3(data)
|
93
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
94
|
+
xml.send("artifact-resolution") {
|
95
|
+
xml.data {
|
96
|
+
data.each_line { |line|
|
97
|
+
tag, value = parse_n3_line(line)
|
98
|
+
xml.send(tag, value) unless tag.empty? || value.empty?
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
end
|
103
|
+
return builder.doc.root.to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_n3_line(line)
|
107
|
+
tag = line.match(/#(\w*)>/) ? "#{$1}" : ""
|
108
|
+
value = line.match(/"([^"]*)"/) ? "#{$1}" : ""
|
109
|
+
return tag, value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'restclient'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module NexusCli
|
6
|
+
class Factory
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def create(overrides)
|
10
|
+
@configuration = parse_configuration(overrides)
|
11
|
+
running_nexus_pro? ? ProRemote.new(overrides) : OSSRemote.new(overrides)
|
12
|
+
end
|
13
|
+
|
14
|
+
def configuration
|
15
|
+
return @configuration if @configuration
|
16
|
+
end
|
17
|
+
|
18
|
+
def nexus
|
19
|
+
@nexus ||= RestClient::Resource.new configuration["url"], :user => configuration["username"], :password => configuration["password"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def status
|
23
|
+
doc = Nokogiri::XML(nexus['service/local/status'].get).xpath("/status/data")
|
24
|
+
data = Hash.new
|
25
|
+
data['app_name'] = doc.xpath("appName")[0].text
|
26
|
+
data['version'] = doc.xpath("version")[0].text
|
27
|
+
data['edition_long'] = doc.xpath("editionLong")[0].text
|
28
|
+
data['state'] = doc.xpath("state")[0].text
|
29
|
+
data['started_at'] = doc.xpath("startedAt")[0].text
|
30
|
+
data['base_url'] = doc.xpath("baseUrl")[0].text
|
31
|
+
return data
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def running_nexus_pro?
|
36
|
+
return status['edition_long'] == "Professional" ? true : false
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_configuration(overrides)
|
40
|
+
begin
|
41
|
+
config = YAML::load_file(File.expand_path("~/.nexus_cli"))
|
42
|
+
rescue
|
43
|
+
end
|
44
|
+
if config.nil? && (overrides.nil? || overrides.empty?)
|
45
|
+
raise MissingSettingsFileException
|
46
|
+
end
|
47
|
+
overrides.each{|key, value| config[key] = value} unless overrides.nil? || overrides.empty?
|
48
|
+
validate_config(config)
|
49
|
+
config
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_config(configuration)
|
53
|
+
["url", "repository", "username","password"].each do |key|
|
54
|
+
raise InvalidSettingsException.new(key) unless configuration.has_key?(key)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/nexus_cli/tasks.rb
CHANGED
@@ -21,7 +21,7 @@ module NexusCli
|
|
21
21
|
def initialize(*args)
|
22
22
|
super
|
23
23
|
begin
|
24
|
-
|
24
|
+
@nexus_remote = Factory.create(options[:overrides])
|
25
25
|
rescue NexusCliError => e
|
26
26
|
say e.message, :red
|
27
27
|
exit e.status_code
|
@@ -35,7 +35,7 @@ module NexusCli
|
|
35
35
|
desc "pull_artifact artifact", "Pulls an artifact from Nexus and places it on your machine."
|
36
36
|
def pull_artifact(artifact)
|
37
37
|
begin
|
38
|
-
path_to_artifact =
|
38
|
+
path_to_artifact = @nexus_remote.pull_artifact(artifact, options[:destination])
|
39
39
|
say "Artifact has been retrived and can be found at path: #{path_to_artifact}", :green
|
40
40
|
rescue NexusCliError => e
|
41
41
|
say e.message, :red
|
@@ -43,14 +43,10 @@ module NexusCli
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
method_option :insecure,
|
47
|
-
:type => :boolean,
|
48
|
-
:default => false,
|
49
|
-
:desc => "Overrides any failures because of an 'insecure' SSL conncetion."
|
50
46
|
desc "push_artifact artifact file", "Pushes an artifact from your machine onto the Nexus."
|
51
47
|
def push_artifact(artifact, file)
|
52
48
|
begin
|
53
|
-
|
49
|
+
@nexus_remote.push_artifact(artifact, file)
|
54
50
|
say "Artifact #{artifact} has been successfully pushed to Nexus.", :green
|
55
51
|
rescue NexusCliError => e
|
56
52
|
say e.message, :red
|
@@ -61,7 +57,7 @@ module NexusCli
|
|
61
57
|
desc "get_artifact_info artifact", "Gets and returns the metadata in XML format about a particular artifact."
|
62
58
|
def get_artifact_info(artifact)
|
63
59
|
begin
|
64
|
-
say
|
60
|
+
say @nexus_remote.get_artifact_info(artifact), :green
|
65
61
|
rescue NexusCliError => e
|
66
62
|
say e.message, :red
|
67
63
|
exit e.status_code
|
@@ -71,7 +67,45 @@ module NexusCli
|
|
71
67
|
desc "get_artifact_custom_info artifact", "Gets and returns the custom metadata in XML format about a particular artifact."
|
72
68
|
def get_artifact_custom_info(artifact)
|
73
69
|
begin
|
74
|
-
say
|
70
|
+
say @nexus_remote.get_artifact_custom_info(artifact), :green
|
71
|
+
rescue NexusCliError => e
|
72
|
+
say e.message, :red
|
73
|
+
exit e.status_code
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "get_artifact_custom_info_n3 artifact", "Gets and returns the custom metadata in Nexus n3 format about a particular artifact."
|
78
|
+
def get_artifact_custom_info_n3(artifact)
|
79
|
+
begin
|
80
|
+
raise NotNexusProException unless @nexus_remote.kind_of? ProRemote
|
81
|
+
say @nexus_remote.get_artifact_custom_info_n3(artifact), :green
|
82
|
+
rescue NexusCliError => e
|
83
|
+
say e.message, :red
|
84
|
+
exit e.status_code
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
method_option :insecure,
|
89
|
+
:type => :boolean,
|
90
|
+
:default => false,
|
91
|
+
:desc => "Overrides any failures because of an 'insecure' SSL connection."
|
92
|
+
desc "update_artifact_custom_info artifact file", "Updates the artifact custom metadata by pushing the Nexus custom artifact file (n3) from your machine onto the Nexus."
|
93
|
+
def update_artifact_custom_info(artifact, file)
|
94
|
+
begin
|
95
|
+
raise NotNexusProException unless @nexus_remote.kind_of? ProRemote
|
96
|
+
@nexus_remote.update_artifact_custom_info(artifact, file, options[:insecure])
|
97
|
+
say "Custom metadata for artifact #{artifact} has been successfully pushed to Nexus.", :green
|
98
|
+
rescue NexusCliError => e
|
99
|
+
say e.message, :red
|
100
|
+
exit e.status_code
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
desc "search_artifacts key type value", "Searches for artifacts using artifact metadata and returns the result as a list with items in XML format."
|
105
|
+
def search_artifacts(key, type, value)
|
106
|
+
begin
|
107
|
+
raise NotNexusProException unless @nexus_remote.kind_of? ProRemote
|
108
|
+
say @nexus_remote.search_artifacts(key, type, value), :green
|
75
109
|
rescue NexusCliError => e
|
76
110
|
say e.message, :red
|
77
111
|
exit e.status_code
|
@@ -81,7 +115,7 @@ module NexusCli
|
|
81
115
|
desc "get_nexus_configuration", "Prints out configuration from the .nexus_cli file that helps inform where artifacts will be uploaded."
|
82
116
|
def get_nexus_configuration
|
83
117
|
begin
|
84
|
-
config =
|
118
|
+
config = @nexus_remote.configuration
|
85
119
|
say "********* Reading CLI configuration from #{File.expand_path('~/.nexus_cli')} *********", :blue
|
86
120
|
say "Nexus URL: #{config['url']}", :blue
|
87
121
|
say "Nexus Repository: #{config['repository']}", :blue
|
@@ -94,7 +128,7 @@ module NexusCli
|
|
94
128
|
desc "get_nexus_status", "Prints out information about the Nexus instance."
|
95
129
|
def get_nexus_status
|
96
130
|
begin
|
97
|
-
data =
|
131
|
+
data = @nexus_remote.status
|
98
132
|
say "********* Getting Nexus status from #{data['base_url']} *********", :blue
|
99
133
|
say "Application Name: #{data['app_name']}", :blue
|
100
134
|
say "Version: #{data['version']}", :blue
|
@@ -107,26 +141,6 @@ module NexusCli
|
|
107
141
|
exit e.status_code
|
108
142
|
end
|
109
143
|
end
|
110
|
-
|
111
|
-
private
|
112
|
-
def set_remote_configuration(overrides)
|
113
|
-
begin
|
114
|
-
config = YAML::load_file(File.expand_path("~/.nexus_cli"))
|
115
|
-
rescue
|
116
|
-
end
|
117
|
-
if config.nil? && (overrides.nil? || overrides.empty?)
|
118
|
-
raise MissingSettingsFileException
|
119
|
-
end
|
120
|
-
overrides.each{|key, value| config[key] = value} unless overrides.nil? || overrides.empty?
|
121
|
-
validate_config(config)
|
122
|
-
Remote.configuration = config
|
123
|
-
end
|
124
|
-
|
125
|
-
def validate_config(configuration)
|
126
|
-
["url", "repository", "username","password"].each do |key|
|
127
|
-
raise InvalidSettingsException.new(key) unless configuration.has_key?(key)
|
128
|
-
end
|
129
|
-
end
|
130
144
|
end
|
131
145
|
end
|
132
146
|
end
|
data/nexus_cli.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexus_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: nokogiri
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
46
62
|
- !ruby/object:Gem::Dependency
|
47
63
|
name: rspec
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,15 +138,16 @@ files:
|
|
122
138
|
- Rakefile
|
123
139
|
- VERSION
|
124
140
|
- bin/nexus-cli
|
125
|
-
- features/cli/
|
126
|
-
- features/cli/push_artifact.feature
|
141
|
+
- features/cli/nexus_oss.feature
|
127
142
|
- features/step_definitions/cli_steps.rb
|
128
143
|
- features/support/env.rb
|
129
144
|
- lib/nexus_cli.rb
|
130
145
|
- lib/nexus_cli/cli.rb
|
131
146
|
- lib/nexus_cli/errors.rb
|
132
147
|
- lib/nexus_cli/kernel.rb
|
133
|
-
- lib/nexus_cli/
|
148
|
+
- lib/nexus_cli/nexus_oss_remote.rb
|
149
|
+
- lib/nexus_cli/nexus_pro_remote.rb
|
150
|
+
- lib/nexus_cli/nexus_remote_factory.rb
|
134
151
|
- lib/nexus_cli/tasks.rb
|
135
152
|
- lib/nexus_cli/version.rb
|
136
153
|
- nexus_cli.gemspec
|
@@ -149,7 +166,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
166
|
version: '0'
|
150
167
|
segments:
|
151
168
|
- 0
|
152
|
-
hash:
|
169
|
+
hash: 3325532431441857534
|
153
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
171
|
none: false
|
155
172
|
requirements:
|
@@ -158,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
175
|
version: '0'
|
159
176
|
segments:
|
160
177
|
- 0
|
161
|
-
hash:
|
178
|
+
hash: 3325532431441857534
|
162
179
|
requirements: []
|
163
180
|
rubyforge_project:
|
164
181
|
rubygems_version: 1.8.21
|
@@ -166,8 +183,7 @@ signing_key:
|
|
166
183
|
specification_version: 3
|
167
184
|
summary: A command-line wrapper for making REST calls to Sonatype Nexus.
|
168
185
|
test_files:
|
169
|
-
- features/cli/
|
170
|
-
- features/cli/push_artifact.feature
|
186
|
+
- features/cli/nexus_oss.feature
|
171
187
|
- features/step_definitions/cli_steps.rb
|
172
188
|
- features/support/env.rb
|
173
189
|
- spec/remote_spec.rb
|
@@ -1,17 +0,0 @@
|
|
1
|
-
@cli
|
2
|
-
Feature: Pull Artifact
|
3
|
-
As a CLI user
|
4
|
-
I need a command to pull artifacts from the Nexus repository
|
5
|
-
So I have a way to get artifacts from a remote system to my local machine
|
6
|
-
|
7
|
-
Scenario: Pull an artifact
|
8
|
-
When I get the artifact "com.riotgames.tar:mytar:1.0.3:tgz"
|
9
|
-
Then I should have a copy of the "mytar-1.0.3.tgz" artifact on my computer
|
10
|
-
|
11
|
-
Scenario: Pull an artifact to a specific place
|
12
|
-
When I want the artifact "com.riotgames.tar:mytar:1.0.3:tgz" in a temp directory
|
13
|
-
Then I should have a copy of the "mytar-1.0.3.tgz" artifact in a temp directory
|
14
|
-
|
15
|
-
Scenario: Attempt to pull an artifact with the wrong parameters
|
16
|
-
When I run `nexus-cli pull_artifact com.riotgames.whatever:something`
|
17
|
-
Then I should expect an error because I need more colon separated values
|
@@ -1,8 +0,0 @@
|
|
1
|
-
Feature: Push Artifact
|
2
|
-
As a CLI User
|
3
|
-
I need a command to push artifacts into the Nexus repository
|
4
|
-
So I have a way to get artifacts from Nexus when I want them
|
5
|
-
|
6
|
-
Scenario: Push an Artifact
|
7
|
-
When I push an artifact into the Nexus
|
8
|
-
Then I should be able to ask the Nexus for information about it and get a result
|
data/lib/nexus_cli/remote.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
require 'restclient'
|
2
|
-
require 'rexml/document'
|
3
|
-
require 'yaml'
|
4
|
-
require 'open3'
|
5
|
-
|
6
|
-
module NexusCli
|
7
|
-
class Remote
|
8
|
-
class << self
|
9
|
-
|
10
|
-
def configuration=(config = {})
|
11
|
-
@configuration = config
|
12
|
-
end
|
13
|
-
|
14
|
-
def configuration
|
15
|
-
return @configuration if @configuration
|
16
|
-
end
|
17
|
-
|
18
|
-
def nexus
|
19
|
-
@nexus ||= RestClient::Resource.new configuration["url"], :user => configuration["username"], :password => configuration["password"]
|
20
|
-
end
|
21
|
-
|
22
|
-
def status
|
23
|
-
doc = REXML::Document.new(nexus['service/local/status'].get).elements['status/data']
|
24
|
-
data = Hash.new
|
25
|
-
data['app_name'] = doc.elements['appName'].text
|
26
|
-
data['version'] = doc.elements['version'].text
|
27
|
-
data['edition_long'] = doc.elements['editionLong'].text
|
28
|
-
data['state'] = doc.elements['state'].text
|
29
|
-
data['started_at'] = doc.elements['startedAt'].text
|
30
|
-
data['base_url'] = doc.elements['baseUrl'].text
|
31
|
-
return data
|
32
|
-
end
|
33
|
-
|
34
|
-
def pull_artifact(artifact, destination, overrides)
|
35
|
-
split_artifact = artifact.split(":")
|
36
|
-
if(split_artifact.size < 4)
|
37
|
-
raise ArtifactMalformedException
|
38
|
-
end
|
39
|
-
group_id, artifact_id, version, extension = split_artifact
|
40
|
-
begin
|
41
|
-
fileData = nexus['service/local/artifact/maven/redirect'].get({:params => {:r => configuration['repository'], :g => group_id, :a => artifact_id, :v => version, :e => extension}})
|
42
|
-
rescue RestClient::ResourceNotFound
|
43
|
-
raise ArtifactNotFoundException
|
44
|
-
end
|
45
|
-
artifact = nil
|
46
|
-
destination = File.join(File.expand_path(destination || "."), "#{artifact_id}-#{version}.#{extension}")
|
47
|
-
artifact = File.open(destination, 'w')
|
48
|
-
artifact.write(fileData)
|
49
|
-
artifact.close()
|
50
|
-
File.expand_path(artifact.path)
|
51
|
-
end
|
52
|
-
|
53
|
-
def push_artifact(artifact, file, insecure, overrides)
|
54
|
-
split_artifact = artifact.split(":")
|
55
|
-
if(split_artifact.size < 4)
|
56
|
-
raise ArtifactMalformedException
|
57
|
-
end
|
58
|
-
group_id, artifact_id, version, extension = split_artifact
|
59
|
-
nexus['service/local/artifact/maven/content'].post({:hasPom => false, :g => group_id, :a => artifact_id, :v => version, :e => extension, :p => extension, :r => configuration['repository'],
|
60
|
-
:file => File.new(file)}) do |response, request, result, &block|
|
61
|
-
case response.code
|
62
|
-
when 400
|
63
|
-
raise BadUploadRequestException
|
64
|
-
when 401
|
65
|
-
raise PermissionsException
|
66
|
-
when 403
|
67
|
-
raise PermissionsException
|
68
|
-
when 404
|
69
|
-
raise CouldNotConnectToNexusException
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def delete_artifact(artifact)
|
75
|
-
split_artifact = artifact.split(":")
|
76
|
-
if(split_artifact.size < 4)
|
77
|
-
raise ArtifactMalformedException
|
78
|
-
end
|
79
|
-
group_id = split_artifact[0].gsub(".", "/")
|
80
|
-
artifact_id = split_artifact[1].gsub(".", "/")
|
81
|
-
version = split_artifact[2]
|
82
|
-
|
83
|
-
delete_string = "content/repositories/releases/#{group_id}/#{artifact_id}/#{version}"
|
84
|
-
Kernel.quietly {`curl --request DELETE #{File.join(configuration['url'], delete_string)} -u #{configuration['username']}:#{configuration['password']}`}
|
85
|
-
end
|
86
|
-
|
87
|
-
def get_artifact_info(artifact, overrides)
|
88
|
-
split_artifact = artifact.split(":")
|
89
|
-
if(split_artifact.size < 4)
|
90
|
-
raise ArtifactMalformedException
|
91
|
-
end
|
92
|
-
begin
|
93
|
-
nexus['service/local/artifact/maven/resolve'].get({:params => {:r => configuration['repository'], :g => split_artifact[0], :a => split_artifact[1], :v => split_artifact[2], :e => split_artifact[3]}})
|
94
|
-
rescue RestClient::ResourceNotFound => e
|
95
|
-
raise ArtifactNotFoundException
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def get_artifact_custom_info(artifact, overrides)
|
100
|
-
raise NotNexusProException unless running_nexus_pro?
|
101
|
-
split_artifact = artifact.split(":")
|
102
|
-
if(split_artifact.size < 4)
|
103
|
-
raise ArtifactMalformedException
|
104
|
-
end
|
105
|
-
group_id, artifact_id, version, extension = split_artifact
|
106
|
-
file_name = "#{artifact_id}-#{version}.#{extension}"
|
107
|
-
get_string = "content/repositories/#{configuration['repository']}/.meta/#{group_id.gsub(".", "/")}/#{artifact_id.gsub(".", "/")}/#{version}/#{file_name}.n3"
|
108
|
-
begin
|
109
|
-
n3_data = nexus[get_string].get
|
110
|
-
parse_n3(n3_data)
|
111
|
-
rescue RestClient::ResourceNotFound => e
|
112
|
-
raise ArtifactNotFoundException
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
private
|
117
|
-
def running_nexus_pro?
|
118
|
-
return status['edition_long'] == "Professional" ? true : false
|
119
|
-
end
|
120
|
-
|
121
|
-
def parse_n3(data)
|
122
|
-
result = ""
|
123
|
-
data.each_line { |item|
|
124
|
-
tag = item.match(/#(\w*)>/) ? "#{$1}" : ""
|
125
|
-
value = item.match(/"([^"]*)"/) ? "#{$1}" : ""
|
126
|
-
result += " <#{tag}>#{value}</#{tag}>\n" unless tag.empty? && value.empty?
|
127
|
-
}
|
128
|
-
return "<artifact-resolution>\n <data>\n" + result + " </data>\n</artifact-resolution>"
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|