wss_agent 0.0.17 → 0.0.21
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/.gitignore +1 -0
- data/lib/config/default.yml +2 -1
- data/lib/data/ca-certificates.crt +4049 -0
- data/lib/wss_agent.rb +7 -8
- data/lib/wss_agent/cli.rb +11 -8
- data/lib/wss_agent/client.rb +37 -8
- data/lib/wss_agent/configure.rb +12 -10
- data/lib/wss_agent/gem_sha1.rb +3 -6
- data/lib/wss_agent/project.rb +15 -19
- data/lib/wss_agent/response.rb +3 -3
- data/lib/wss_agent/response_inventory.rb +7 -6
- data/lib/wss_agent/response_policies.rb +7 -6
- data/lib/wss_agent/specifications.rb +41 -15
- data/lib/wss_agent/version.rb +2 -2
- data/spec/fixtures/wss_agent.yml +1 -2
- data/spec/wss_agent/client_spec.rb +4 -4
- data/spec/wss_agent/configure_spec.rb +26 -26
- data/wss_agent.gemspec +19 -19
- metadata +26 -55
data/lib/wss_agent.rb
CHANGED
@@ -2,7 +2,7 @@ require 'thor'
|
|
2
2
|
require 'net/http'
|
3
3
|
require 'awesome_print'
|
4
4
|
require 'yaml'
|
5
|
-
require '
|
5
|
+
require 'multi_json'
|
6
6
|
require 'faraday'
|
7
7
|
require 'faraday_middleware'
|
8
8
|
require 'yell'
|
@@ -17,9 +17,8 @@ require 'wss_agent/client'
|
|
17
17
|
require 'wss_agent/gem_sha1'
|
18
18
|
require 'wss_agent/project'
|
19
19
|
|
20
|
-
|
21
20
|
module WssAgent
|
22
|
-
|
21
|
+
DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
|
23
22
|
|
24
23
|
class WssAgentError < StandardError
|
25
24
|
def self.status_code(code)
|
@@ -27,11 +26,11 @@ module WssAgent
|
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
30
|
-
class NotFoundConfigFile < WssAgentError; status_code(8)
|
31
|
-
class InvalidConfigFile < WssAgentError; status_code(9)
|
32
|
-
class TokenNotFound < WssAgentError; status_code(10)
|
33
|
-
class ApiUrlNotFound < WssAgentError; status_code(11)
|
34
|
-
class ApiUrlInvalid < WssAgentError; status_code(12)
|
29
|
+
class NotFoundConfigFile < WssAgentError; status_code(8); end
|
30
|
+
class InvalidConfigFile < WssAgentError; status_code(9); end
|
31
|
+
class TokenNotFound < WssAgentError; status_code(10); end
|
32
|
+
class ApiUrlNotFound < WssAgentError; status_code(11); end
|
33
|
+
class ApiUrlInvalid < WssAgentError; status_code(12); end
|
35
34
|
|
36
35
|
def self.logger
|
37
36
|
@logger ||= Yell.new STDOUT, level: [:info]
|
data/lib/wss_agent/cli.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
module WssAgent
|
2
2
|
class CLI < Thor
|
3
|
-
desc
|
3
|
+
desc 'config', 'create config file'
|
4
4
|
def config
|
5
5
|
File.open(File.join(Dir.pwd, Configure::CURRENT_CONFIG_FILE), 'w') do |f|
|
6
6
|
f << File.read(Configure.custom_default_path)
|
7
7
|
end
|
8
8
|
ap 'Created the config file: wss_agent.yml'
|
9
9
|
end
|
10
|
+
map init: :config
|
10
11
|
|
11
12
|
desc 'list', 'display list dependencies'
|
12
|
-
|
13
|
-
|
14
|
-
method_option :verbose, :
|
13
|
+
method_option :all, type: :boolean
|
14
|
+
method_option :excludes, type: :string
|
15
|
+
method_option :verbose, aliases: '-v', desc: 'Be verbose'
|
15
16
|
def list
|
16
17
|
WssAgent.enable_debug! if options['verbose']
|
17
18
|
results = Specifications.list(options)
|
@@ -24,9 +25,10 @@ module WssAgent
|
|
24
25
|
end
|
25
26
|
|
26
27
|
desc 'update', 'update open source inventory'
|
27
|
-
|
28
|
-
|
29
|
-
method_option :verbose, :
|
28
|
+
method_option :all, type: :boolean
|
29
|
+
method_option :excludes, type: :string
|
30
|
+
method_option :verbose, aliases: '-v', desc: 'Be verbose'
|
31
|
+
method_option :force, type: :boolean, aliases: '-f', desc: 'Force Check All Dependencies'
|
30
32
|
def update
|
31
33
|
WssAgent.enable_debug! if options['verbose']
|
32
34
|
Specifications.update(options)
|
@@ -35,7 +37,8 @@ module WssAgent
|
|
35
37
|
end
|
36
38
|
|
37
39
|
desc 'check_policies', 'checking dependencies that they conforms with company policy.'
|
38
|
-
method_option :verbose, :
|
40
|
+
method_option :verbose, aliases: '-v', desc: 'Be verbose'
|
41
|
+
method_option :force, type: :boolean, aliases: '-f', desc: 'Force Check All Dependencies'
|
39
42
|
def check_policies
|
40
43
|
WssAgent.enable_debug! if options['verbose']
|
41
44
|
Specifications.check_policies(options)
|
data/lib/wss_agent/client.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
module WssAgent
|
2
2
|
class Client
|
3
|
-
|
4
3
|
attr_accessor :connection
|
5
|
-
|
6
|
-
|
4
|
+
POLICY_TYPES = {
|
5
|
+
basic: 'CHECK_POLICIES',
|
6
|
+
compliance: 'CHECK_POLICY_COMPLIANCE'
|
7
|
+
}.freeze
|
8
|
+
|
9
|
+
UPDATE_TYPE = 'UPDATE'.freeze
|
7
10
|
REQUEST_TIMEOUT = 120
|
8
11
|
|
9
12
|
def initialize
|
10
|
-
@connection ||= Faraday.new(
|
13
|
+
@connection ||= Faraday.new(connection_options) do |h|
|
11
14
|
h.port = Configure.port
|
12
15
|
h.headers[:content_type] = 'application/x-www-form-urlencoded'
|
13
16
|
h.request :url_encoded
|
@@ -25,7 +28,7 @@ module WssAgent
|
|
25
28
|
if Configure['project_token']
|
26
29
|
diff_data['projectToken'] = Configure['project_token']
|
27
30
|
end
|
28
|
-
|
31
|
+
MultiJson.dump([diff_data])
|
29
32
|
end
|
30
33
|
|
31
34
|
def payload(gem_list, options = {})
|
@@ -41,11 +44,18 @@ module WssAgent
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def update(gem_list)
|
44
|
-
ResponseInventory.new(request(gem_list,
|
47
|
+
ResponseInventory.new(request(gem_list, type: UPDATE_TYPE))
|
45
48
|
end
|
46
49
|
|
47
|
-
def check_policies(gem_list)
|
48
|
-
|
50
|
+
def check_policies(gem_list, options = {})
|
51
|
+
request_options =
|
52
|
+
if WssAgent::Configure['force_check_all_dependencies'] || options['force']
|
53
|
+
{ type: POLICY_TYPES[:compliance], forceCheckAllDependencies: true }
|
54
|
+
else
|
55
|
+
{ type: POLICY_TYPES[:basic], forceCheckAllDependencies: false }
|
56
|
+
end
|
57
|
+
|
58
|
+
ResponsePolicies.new(request(gem_list, request_options))
|
49
59
|
end
|
50
60
|
|
51
61
|
def request(gem_list, options = {})
|
@@ -55,5 +65,24 @@ module WssAgent
|
|
55
65
|
rescue Faraday::Error::ClientError => ex
|
56
66
|
ex
|
57
67
|
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def connection_options
|
72
|
+
@connection_options ||
|
73
|
+
begin
|
74
|
+
|
75
|
+
@connection_options = {
|
76
|
+
url: Configure.url,
|
77
|
+
request: { timeout: REQUEST_TIMEOUT }
|
78
|
+
}
|
79
|
+
if Configure.ssl?
|
80
|
+
@connection_options[:ssl] = {
|
81
|
+
ca_file: WssAgent::DEFAULT_CA_BUNDLE_PATH
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@connection_options
|
86
|
+
end
|
58
87
|
end
|
59
88
|
end
|
data/lib/wss_agent/configure.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
module WssAgent
|
2
2
|
class Configure
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
API_PATH = '/agent'
|
3
|
+
DEFAULT_CONFIG_FILE = 'default.yml'.freeze
|
4
|
+
CUSTOM_DEFAULT_CONFIG_FILE = 'custom_default.yml'.freeze
|
5
|
+
CURRENT_CONFIG_FILE = 'wss_agent.yml'.freeze
|
6
|
+
API_PATH = '/agent'.freeze
|
8
7
|
|
9
8
|
extend SingleForwardable
|
10
9
|
def_delegator :current, :[]
|
11
10
|
|
12
11
|
class << self
|
13
|
-
|
14
12
|
def default_path
|
15
13
|
File.join(File.expand_path('../..', __FILE__), 'config', DEFAULT_CONFIG_FILE)
|
16
14
|
end
|
@@ -38,8 +36,8 @@ module WssAgent
|
|
38
36
|
|
39
37
|
@current_config = YAML.load(File.read(current_path))
|
40
38
|
|
41
|
-
|
42
|
-
return raise InvalidConfigFile,
|
39
|
+
unless !!@current_config
|
40
|
+
return raise InvalidConfigFile, 'Problem reading wss_agent.yml, please check the file is a valid YAML'
|
43
41
|
end
|
44
42
|
|
45
43
|
default.merge(@current_config)
|
@@ -53,7 +51,7 @@ module WssAgent
|
|
53
51
|
URI(@url)
|
54
52
|
|
55
53
|
rescue URI::Error
|
56
|
-
raise ApiUrlInvalid,
|
54
|
+
raise ApiUrlInvalid, 'Api url is invalid. Could you please check url in wss_agent.yml'
|
57
55
|
end
|
58
56
|
|
59
57
|
def port
|
@@ -65,10 +63,14 @@ module WssAgent
|
|
65
63
|
[@uri.scheme, @uri.host].join('://')
|
66
64
|
end
|
67
65
|
|
66
|
+
def ssl?
|
67
|
+
uri.scheme == 'https'
|
68
|
+
end
|
69
|
+
|
68
70
|
def api_path
|
69
71
|
@uri = uri
|
70
72
|
@url_path = @uri.path
|
71
|
-
@url_path ==
|
73
|
+
@url_path == '' ? API_PATH : @url_path
|
72
74
|
end
|
73
75
|
|
74
76
|
def token
|
data/lib/wss_agent/gem_sha1.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'digest'
|
2
2
|
|
3
3
|
module WssAgent
|
4
4
|
class GemSha1
|
@@ -19,7 +19,7 @@ module WssAgent
|
|
19
19
|
h.adapter :excon
|
20
20
|
end
|
21
21
|
response = conn.get("/api/v1/versions/#{spec.name}.json")
|
22
|
-
versions =
|
22
|
+
versions = MultiJson.load(response.body)
|
23
23
|
unless versions.detect { |j| j['number'] == spec.version }
|
24
24
|
spec.version = versions.first['number']
|
25
25
|
end
|
@@ -61,9 +61,7 @@ module WssAgent
|
|
61
61
|
|
62
62
|
when '302' # redirect
|
63
63
|
response = Net::HTTP.get_response(URI(response['location']))
|
64
|
-
if response.code == '200'
|
65
|
-
return Digest::SHA1.hexdigest(response.body)
|
66
|
-
end
|
64
|
+
return Digest::SHA1.hexdigest(response.body) if response.code == '200'
|
67
65
|
else # gem isn't found
|
68
66
|
''
|
69
67
|
end
|
@@ -71,6 +69,5 @@ module WssAgent
|
|
71
69
|
rescue Timeout::Error
|
72
70
|
retry_request ? nil : remote_file(true)
|
73
71
|
end
|
74
|
-
|
75
72
|
end
|
76
73
|
end
|
data/lib/wss_agent/project.rb
CHANGED
@@ -2,43 +2,39 @@ module WssAgent
|
|
2
2
|
class Project
|
3
3
|
|
4
4
|
def project_name
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
when is_rails_app?
|
9
|
-
rails_app_name
|
10
|
-
else
|
11
|
-
folder_name
|
12
|
-
end
|
5
|
+
return gem.name if gem?
|
6
|
+
return rails_app_name if rails?
|
7
|
+
folder_name
|
13
8
|
end
|
14
9
|
|
15
10
|
def project_version
|
16
|
-
|
17
|
-
gem.version.to_s
|
18
|
-
else
|
19
|
-
''
|
20
|
-
end
|
11
|
+
gem? ? gem.version.to_s : ''
|
21
12
|
end
|
22
13
|
|
23
14
|
def folder_name
|
24
15
|
Bundler.root.split.last.to_s
|
25
16
|
end
|
26
17
|
|
27
|
-
def
|
18
|
+
def gem?
|
28
19
|
!Dir.glob(Bundler.root.join('*.gemspec')).last.nil?
|
29
20
|
end
|
30
21
|
|
31
22
|
def gem
|
32
|
-
@gem ||= Gem::Specification.load(
|
23
|
+
@gem ||= Gem::Specification.load(
|
24
|
+
Dir.glob(Bundler.root.join('*.gemspec')).last
|
25
|
+
)
|
33
26
|
end
|
34
27
|
|
35
|
-
def
|
36
|
-
|
28
|
+
def rails?
|
29
|
+
File.exist?(rails_app_path)
|
37
30
|
end
|
38
31
|
|
39
32
|
def rails_app_name
|
40
|
-
|
41
|
-
|
33
|
+
File.read(rails_app_path).match(/module (\w*)/)[1]
|
34
|
+
end
|
35
|
+
|
36
|
+
def rails_app_path
|
37
|
+
Bundler.root.join('config', 'application.rb')
|
42
38
|
end
|
43
39
|
end
|
44
40
|
end
|
data/lib/wss_agent/response.rb
CHANGED
@@ -23,7 +23,7 @@ module WssAgent
|
|
23
23
|
def parse_response
|
24
24
|
if response.success?
|
25
25
|
begin
|
26
|
-
@response_data =
|
26
|
+
@response_data = MultiJson.load(response.body)
|
27
27
|
@status = @response_data['status'].to_i
|
28
28
|
@message = @response_data['message']
|
29
29
|
rescue
|
@@ -49,9 +49,9 @@ module WssAgent
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def data
|
52
|
-
@data ||=
|
52
|
+
@data ||= MultiJson.load(response_data['data'])
|
53
53
|
rescue
|
54
|
-
response_data && response_data.key?('data') ?
|
54
|
+
response_data && response_data.key?('data') ? response_data['data'] : nil
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -6,19 +6,20 @@ module WssAgent
|
|
6
6
|
@message = "White Source update results: \n"
|
7
7
|
@message << " White Source organization: #{data['organization']} \n"
|
8
8
|
|
9
|
-
|
9
|
+
if data['createdProjects'].empty?
|
10
|
+
@message << " No new projects found \n"
|
11
|
+
else
|
10
12
|
@message << " #{data['createdProjects'].size} newly created projects: "
|
11
13
|
@message << data['createdProjects'].join(' ')
|
12
|
-
else
|
13
|
-
@message << " No new projects found \n"
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
if data['updatedProjects'].empty?
|
17
|
+
@message << "\n No projects were updated \n"
|
18
|
+
else
|
17
19
|
@message << " #{data['updatedProjects'].size} existing projects were updated: "
|
18
20
|
@message << data['updatedProjects'].join(' ')
|
19
|
-
else
|
20
|
-
@message << "\n No projects were updated \n"
|
21
21
|
end
|
22
|
+
|
22
23
|
@message
|
23
24
|
else
|
24
25
|
super
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module WssAgent
|
2
2
|
class ResponsePolicies < Response
|
3
|
-
REJECT_ACTION = 'Reject'
|
3
|
+
REJECT_ACTION = 'Reject'.freeze
|
4
4
|
|
5
5
|
def parse_response
|
6
6
|
if response.success?
|
7
7
|
begin
|
8
|
-
@response_data =
|
8
|
+
@response_data = MultiJson.load(response.body)
|
9
9
|
@status = @response_data['status'].to_i
|
10
10
|
@message = @response_data['message']
|
11
11
|
check_new_projects
|
@@ -20,7 +20,6 @@ module WssAgent
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
23
|
def message
|
25
24
|
if success?
|
26
25
|
if policy_violations?
|
@@ -33,7 +32,7 @@ module WssAgent
|
|
33
32
|
}.join("\n")
|
34
33
|
@message.join("\n")
|
35
34
|
else
|
36
|
-
|
35
|
+
'All dependencies conform with open source policies'
|
37
36
|
end
|
38
37
|
end
|
39
38
|
end
|
@@ -64,8 +63,10 @@ module WssAgent
|
|
64
63
|
def check(resource)
|
65
64
|
if resource.key?('resource') && resource.key?('policy') &&
|
66
65
|
(resource['policy']['actionType'] == REJECT_ACTION)
|
67
|
-
add_resource(
|
68
|
-
|
66
|
+
add_resource(
|
67
|
+
'resource' => resource['resource'],
|
68
|
+
'policy' => resource['policy']
|
69
|
+
)
|
69
70
|
end
|
70
71
|
|
71
72
|
if resource.key?('children') && resource['children'].is_a?(Array)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'digest'
|
2
2
|
|
3
3
|
module WssAgent
|
4
4
|
class Specifications
|
@@ -10,7 +10,11 @@ module WssAgent
|
|
10
10
|
# @option options [Boolean] 'all' if true then get all dependencies (include development dependencies)
|
11
11
|
# @option options [String] 'excludes' list gem name which need to exclude from end list
|
12
12
|
def specs(options = {})
|
13
|
-
list_gems = Bundler::Definition.build(
|
13
|
+
list_gems = Bundler::Definition.build(
|
14
|
+
Bundler.default_gemfile,
|
15
|
+
Bundler.default_lockfile,
|
16
|
+
false
|
17
|
+
).specs.to_a
|
14
18
|
if options['all']
|
15
19
|
# get all gems
|
16
20
|
list = {}
|
@@ -31,26 +35,38 @@ module WssAgent
|
|
31
35
|
new(specs(options)).call
|
32
36
|
end
|
33
37
|
|
38
|
+
def check_policy?(options = {})
|
39
|
+
options['force'] ||
|
40
|
+
WssAgent::Configure['check_policies'] ||
|
41
|
+
WssAgent::Configure['force_check_all_dependencies']
|
42
|
+
end
|
43
|
+
private :check_policy?
|
44
|
+
|
34
45
|
# Send gem list to server
|
35
46
|
#
|
36
47
|
# @param (see Specifications#specs)
|
37
48
|
def update(options = {})
|
38
49
|
wss_client = WssAgent::Client.new
|
39
|
-
|
40
|
-
|
50
|
+
|
51
|
+
if check_policy?(options)
|
52
|
+
policy_results = wss_client.check_policies(
|
53
|
+
WssAgent::Specifications.list(options),
|
54
|
+
options
|
55
|
+
)
|
41
56
|
if policy_results.success? && policy_results.policy_violations?
|
42
57
|
puts policy_results.message
|
43
58
|
return false
|
44
59
|
end
|
45
60
|
end
|
46
61
|
|
62
|
+
|
47
63
|
result = wss_client.update(WssAgent::Specifications.list(options))
|
48
64
|
if result.success?
|
49
65
|
WssAgent.logger.debug result.data
|
50
66
|
puts result.message
|
51
67
|
else
|
52
68
|
WssAgent.logger.debug "synchronization errors occur: status: #{result.status}, message: #{result.message}, data: #{result.data}"
|
53
|
-
ap "error: #{result.status}/#{result.data}", color: {string: :red }
|
69
|
+
ap "error: #{result.status}/#{result.data}", color: { string: :red }
|
54
70
|
end
|
55
71
|
|
56
72
|
result.success?
|
@@ -61,13 +77,16 @@ module WssAgent
|
|
61
77
|
# @param (see Specifications#specs)
|
62
78
|
def check_policies(options = {})
|
63
79
|
wss_client = WssAgent::Client.new
|
64
|
-
result = wss_client.check_policies(
|
80
|
+
result = wss_client.check_policies(
|
81
|
+
WssAgent::Specifications.list(options),
|
82
|
+
options
|
83
|
+
)
|
65
84
|
if result.success?
|
66
85
|
WssAgent.logger.debug result.data
|
67
86
|
puts result.message
|
68
87
|
else
|
69
88
|
WssAgent.logger.debug "check policies errors occur: #{result.status}, message: #{result.message}, data: #{result.data}"
|
70
|
-
ap "error: #{result.status}/#{result.data}", color: {string: :red }
|
89
|
+
ap "error: #{result.status}/#{result.data}", color: { string: :red }
|
71
90
|
end
|
72
91
|
end
|
73
92
|
|
@@ -84,6 +103,7 @@ module WssAgent
|
|
84
103
|
if options['excludes'] && options['excludes'].to_s.split(',').include?(gd.name)
|
85
104
|
next
|
86
105
|
end
|
106
|
+
|
87
107
|
gs = gd.matching_specs.first
|
88
108
|
if gs
|
89
109
|
unless list[gs.name]
|
@@ -94,8 +114,10 @@ module WssAgent
|
|
94
114
|
end
|
95
115
|
else
|
96
116
|
unless list[gd.name]
|
97
|
-
list[gd.name] = Gem::Specification.new(
|
98
|
-
|
117
|
+
list[gd.name] = Gem::Specification.new(
|
118
|
+
gd.name,
|
119
|
+
gd.requirements_list.last.scan(/[\d\.\w]+/).first
|
120
|
+
)
|
99
121
|
rm_dep = remote_dependencies(gd.name, gd.requirements_list.last)
|
100
122
|
unless rm_dep.empty?
|
101
123
|
list = gem_dependencies(list, rm_dep, options)
|
@@ -113,16 +135,20 @@ module WssAgent
|
|
113
135
|
# @params version [String] version gem
|
114
136
|
#
|
115
137
|
# @return [Array<Gem::Dependency>] list gem dependencies
|
116
|
-
def remote_dependencies(gem_name,
|
138
|
+
def remote_dependencies(gem_name, _version)
|
117
139
|
conn = Faraday.new(url: 'https://rubygems.org') do |h|
|
118
140
|
h.headers[:content_type] = 'application/x-www-form-urlencoded'
|
119
141
|
h.request :url_encoded
|
120
142
|
h.adapter :excon
|
121
143
|
end
|
122
144
|
response = conn.get("/api/v1/gems/#{gem_name}.json")
|
123
|
-
dep_list =
|
124
|
-
dep_list['dependencies'].values.flatten.
|
125
|
-
|
145
|
+
dep_list = MultiJson.load(response.body)
|
146
|
+
dep_list['dependencies'].values.flatten.map do |j|
|
147
|
+
Gem::Dependency.new(
|
148
|
+
j['name'],
|
149
|
+
Gem::Requirement.new(j['requirements'].split(','))
|
150
|
+
)
|
151
|
+
end
|
126
152
|
end
|
127
153
|
end # end class << self
|
128
154
|
|
@@ -140,8 +166,8 @@ module WssAgent
|
|
140
166
|
def gem_item(spec)
|
141
167
|
{
|
142
168
|
'groupId' => spec.name,
|
143
|
-
|
144
|
-
|
169
|
+
'artifactId' => spec.file_name,
|
170
|
+
'version' => spec.version.to_s,
|
145
171
|
'sha1' => GemSha1.new(spec).sha1,
|
146
172
|
'optional' => false,
|
147
173
|
'children' => [],
|