nexus_cli 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|