gjp 0.5.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/.gitignore +21 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +40 -0
- data/README.md +30 -0
- data/Rakefile +7 -0
- data/bin/gjp +25 -0
- data/gjp.gemspec +30 -0
- data/lib/gjp.rb +7 -0
- data/lib/gjp/cli.rb +51 -0
- data/lib/gjp/get_pom.rb +158 -0
- data/lib/gjp/get_source.rb +29 -0
- data/lib/gjp/get_source_address.rb +48 -0
- data/lib/gjp/logger.rb +24 -0
- data/lib/gjp/pom.rb +30 -0
- data/lib/gjp/version.rb +5 -0
- data/spec/data/antlr/antlr-2.7.2.jar +0 -0
- data/spec/data/antlr/pom.xml +6 -0
- data/spec/data/commons-logging/commons-logging-1.1.1.jar +0 -0
- data/spec/data/commons-logging/pom.xml +504 -0
- data/spec/data/nailgun/nailgun-0.7.1.jar +0 -0
- data/spec/data/nailgun/pom.xml +153 -0
- data/spec/data/tomcat/pom.xml +33 -0
- data/spec/lib/get_pom_spec.rb +80 -0
- data/spec/lib/get_source_address_spec.rb +18 -0
- data/spec/lib/get_source_spec.rb +23 -0
- data/spec/lib/pom_spec.rb +32 -0
- data/spec/spec_helper.rb +6 -0
- metadata +213 -0
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
coverage
|
6
|
+
InstalledFiles
|
7
|
+
lib/bundler/man
|
8
|
+
pkg
|
9
|
+
rdoc
|
10
|
+
spec/reports
|
11
|
+
test/tmp
|
12
|
+
test/version_tmp
|
13
|
+
tmp
|
14
|
+
|
15
|
+
# SPEC generated files
|
16
|
+
spec/data/tomcat/org.apache.tomcat\:tomcat\:7.0.40
|
17
|
+
|
18
|
+
# YARD artifacts
|
19
|
+
.yardoc
|
20
|
+
_yardoc
|
21
|
+
doc/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gjp (0.5.0)
|
5
|
+
clamp
|
6
|
+
json
|
7
|
+
nokogiri
|
8
|
+
rest-client
|
9
|
+
rubyzip
|
10
|
+
text
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: http://rubygems.org/
|
14
|
+
specs:
|
15
|
+
clamp (0.6.1)
|
16
|
+
diff-lcs (1.2.4)
|
17
|
+
json (1.8.0)
|
18
|
+
mime-types (1.23)
|
19
|
+
nokogiri (1.5.9)
|
20
|
+
rake (10.0.4)
|
21
|
+
rest-client (1.6.7)
|
22
|
+
mime-types (>= 1.16)
|
23
|
+
rspec (2.13.0)
|
24
|
+
rspec-core (~> 2.13.0)
|
25
|
+
rspec-expectations (~> 2.13.0)
|
26
|
+
rspec-mocks (~> 2.13.0)
|
27
|
+
rspec-core (2.13.1)
|
28
|
+
rspec-expectations (2.13.0)
|
29
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
30
|
+
rspec-mocks (2.13.1)
|
31
|
+
rubyzip (0.9.9)
|
32
|
+
text (1.2.1)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
gjp!
|
39
|
+
rake
|
40
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
gjp – Green Java Packager's tools
|
2
|
+
===
|
3
|
+
|
4
|
+
`gjp` (pronounced _/ˈdʒiː ˈaɪ ˈd͡ʒəʊ/_) is a set of tools to ease and partially automate Linux packaging of Java projects.
|
5
|
+
|
6
|
+
The project focus is on producing rpm packages for SUSE distributions, but it is general enough to be useful even for other distributions.
|
7
|
+
|
8
|
+
|
9
|
+
## Install
|
10
|
+
|
11
|
+
Easiest install is via RubyGems:
|
12
|
+
|
13
|
+
$ gem install gjp
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
Currently available tools:
|
18
|
+
* `gjp get-pom PATH` will attempt to find an artifact's pom.xml, if it exists (from the package itself or through search.maven.org)
|
19
|
+
* `gjp get-source-address PATH` will attempt to find the SCM address of an artifact from its pom.xml (from the pom.xml itself or through api.github.com)
|
20
|
+
* `gjp get-source PATH ADDRESS` downloads the source of a pom.xml's project from its SCM at ADDRESS
|
21
|
+
|
22
|
+
## Source
|
23
|
+
|
24
|
+
`gjp`'s Git repo is available on GitHub, which can be browsed at:
|
25
|
+
|
26
|
+
https://github.com/SilvioMoioli/gjp
|
27
|
+
|
28
|
+
and cloned with:
|
29
|
+
|
30
|
+
git clone git@github.com:SilvioMoioli/gjp.git
|
data/Rakefile
ADDED
data/bin/gjp
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# The (open)?SUSE ((Linux Enterprise) (Server|Desktop))?'s
|
5
|
+
# packager Swiss Army Knife.
|
6
|
+
#
|
7
|
+
# Copyright 2013 Silvio Moioli <smoioli@suse.de>
|
8
|
+
#
|
9
|
+
# Inspired mainly by pom2spec by:
|
10
|
+
# Pascal Bleser <pascal.bleser@opensuse.org>
|
11
|
+
# Duncan Mac-Vicar Prett <dmacvicar@suse.de>
|
12
|
+
#
|
13
|
+
# This file is licensed under the
|
14
|
+
# GNU Lesser General Public License version 2.1 or later:
|
15
|
+
# http://www.gnu.org/licenses/lgpl-2.1.html
|
16
|
+
#
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'gjp'
|
20
|
+
rescue LoadError
|
21
|
+
require 'rubygems'
|
22
|
+
require 'gjp'
|
23
|
+
end
|
24
|
+
|
25
|
+
MainCommand.run
|
data/gjp.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "gjp/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "gjp"
|
7
|
+
s.version = Gjp::VERSION
|
8
|
+
s.authors = ["Silvio Moioli"]
|
9
|
+
s.email = ["smoioli@suse.de"]
|
10
|
+
s.homepage = "https://github.com/SilvioMoioli/gjp"
|
11
|
+
s.summary = "Green Java Packager's Tools"
|
12
|
+
s.description = "A suite of tools to ease Java packaging in SUSE systems"
|
13
|
+
|
14
|
+
s.rubyforge_project = "gjp"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency "rake"
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
|
24
|
+
s.add_runtime_dependency "clamp"
|
25
|
+
s.add_runtime_dependency "rubyzip"
|
26
|
+
s.add_runtime_dependency "rest-client"
|
27
|
+
s.add_runtime_dependency "json"
|
28
|
+
s.add_runtime_dependency "text"
|
29
|
+
s.add_runtime_dependency "nokogiri"
|
30
|
+
end
|
data/lib/gjp.rb
ADDED
data/lib/gjp/cli.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "clamp"
|
4
|
+
|
5
|
+
class MainCommand < Clamp::Command
|
6
|
+
subcommand "get-pom", "Retrieves a pom corresponding to a jar" do
|
7
|
+
parameter "JAR", "jar file path"
|
8
|
+
option ["-v", "--verbose"], :flag, "verbose output"
|
9
|
+
option ["--very-verbose"], :flag, "very verbose output"
|
10
|
+
option ["--very-very-verbose"], :flag, "very very verbose output"
|
11
|
+
|
12
|
+
def execute
|
13
|
+
begin
|
14
|
+
init_logger
|
15
|
+
puts PomGetter.get_pom(jar)
|
16
|
+
rescue Zip::ZipError
|
17
|
+
$stderr.puts "#{jar} does not seem to be a valid jar archive, skipping"
|
18
|
+
rescue TypeError
|
19
|
+
$stderr.puts "#{jar} seems to be a valid jar archive but is corrupt, skipping"
|
20
|
+
rescue RestClient::ResourceNotFound
|
21
|
+
$stderr.puts "Got an error while looking for #{jar} in search.maven.org"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
subcommand "get-source-address", "Retrieves a project's SCM Internet address" do
|
27
|
+
parameter "POM", "project's pom file path"
|
28
|
+
option ["-v", "--verbose"], :flag, "verbose output"
|
29
|
+
option ["--very-verbose"], :flag, "very verbose output"
|
30
|
+
option ["--very-very-verbose"], :flag, "very very verbose output"
|
31
|
+
|
32
|
+
def execute
|
33
|
+
init_logger
|
34
|
+
puts SourceAddressGetter.get_source_address(pom)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
subcommand "get-source", "Retrieves a project's source code directory" do
|
39
|
+
parameter "ADDRESS", "project's SCM Internet address"
|
40
|
+
parameter "POM", "project's pom file path"
|
41
|
+
parameter "[DIRECTORY]", "directory in which to save the source code", :default => "."
|
42
|
+
option ["-v", "--verbose"], :flag, "verbose output"
|
43
|
+
option ["--very-verbose"], :flag, "very verbose output"
|
44
|
+
option ["--very-very-verbose"], :flag, "very very verbose output"
|
45
|
+
|
46
|
+
def execute
|
47
|
+
init_logger
|
48
|
+
puts SourceGetter.get_source(address, pom, directory)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/gjp/get_pom.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "digest/sha1"
|
4
|
+
require "zip/zip"
|
5
|
+
require "rest_client"
|
6
|
+
require "json"
|
7
|
+
require "text"
|
8
|
+
require "pathname"
|
9
|
+
|
10
|
+
# implements the get-pom subcommand
|
11
|
+
class PomGetter
|
12
|
+
|
13
|
+
# returns the pom corresponding to a file or directory, if it can be found
|
14
|
+
def self.get_pom(file)
|
15
|
+
(get_pom_from_dir(file) or get_pom_from_jar(file) or get_pom_from_sha1(file) or get_pom_from_heuristic(file))
|
16
|
+
end
|
17
|
+
|
18
|
+
# returns the pom in a project directory
|
19
|
+
def self.get_pom_from_dir(dir)
|
20
|
+
if File.directory?(dir)
|
21
|
+
pom_path = File.join(dir, "pom.xml")
|
22
|
+
if File.file?(pom_path)
|
23
|
+
$log.info("pom.xml found in #{dir}/pom.xml")
|
24
|
+
return File.read(pom_path)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# returns a pom embedded in a jar file
|
30
|
+
def self.get_pom_from_jar(file)
|
31
|
+
Zip::ZipFile.foreach(file) do |entry|
|
32
|
+
if entry.name =~ /\/pom.xml$/
|
33
|
+
$log.info("pom.xml found in #{file}##{entry.name}")
|
34
|
+
return entry.get_input_stream.read
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# returns a pom from search.maven.org with a jar sha1 search
|
41
|
+
def self.get_pom_from_sha1(file)
|
42
|
+
sha1 = Digest::SHA1.hexdigest File.read(file)
|
43
|
+
results = repository_search({:q => "1:\"#{sha1}\""}).select {|result| result["ec"].include?(".pom")}
|
44
|
+
result = results.first
|
45
|
+
if result != nil
|
46
|
+
$log.info("pom.xml for #{file} found on search.maven.org for sha1 #{sha1} (#{result["g"]}:#{result["a"]}:#{result["v"]})")
|
47
|
+
return repository_download(result)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# returns a pom from search.maven.org with a heuristic name search
|
52
|
+
def self.get_pom_from_heuristic(file)
|
53
|
+
filename = Pathname.new(file).basename.to_s
|
54
|
+
matches = filename.match(/([^\/]*?)(?:[\.\-\_ ~,]([0-9].*))?\.jar$/)
|
55
|
+
if matches != nil and matches.length > 1
|
56
|
+
my_artifact_id = matches[1]
|
57
|
+
my_version = matches[2]
|
58
|
+
result = repository_search({:q => my_artifact_id}).first
|
59
|
+
if result != nil
|
60
|
+
results = repository_search({:q => "g:\"#{result["g"]}\" AND a:\"#{result["a"]}\"", :core => "gav"})
|
61
|
+
their_versions = results.map {|doc| doc["v"]}
|
62
|
+
best_matched_version = if my_version != nil then best_match(my_version, their_versions) else their_versions.max end
|
63
|
+
best_matched_result = (results.select{|result| result["v"] == best_matched_version}).first
|
64
|
+
|
65
|
+
$log.warn("pom.xml for #{file} found on search.maven.org with heuristic search (#{best_matched_result["g"]}:#{best_matched_result["a"]}:#{best_matched_result["v"]})")
|
66
|
+
|
67
|
+
return repository_download(best_matched_result)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# returns a JSON result from search.maven.com
|
73
|
+
def self.repository_search(params)
|
74
|
+
response = RestClient.get "http://search.maven.org/solrsearch/select", {:params => params.merge({"rows" => "100", "wt" => "json"})}
|
75
|
+
json = JSON.parse(response.to_s)
|
76
|
+
return json["response"]["docs"]
|
77
|
+
end
|
78
|
+
|
79
|
+
# downloads a POM from a search.maven.com search result
|
80
|
+
def self.repository_download(result)
|
81
|
+
if result != nil
|
82
|
+
path = "#{result["g"].gsub(".", "/")}/#{result["a"]}/#{result["v"]}/#{result["a"]}-#{result["v"]}.pom"
|
83
|
+
return (RestClient.get "http://search.maven.org/remotecontent", {:params => {:filepath => path}}).to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# returns the "best match" between a version number and a set of available version numbers
|
88
|
+
# using a heuristic criterion. Idea:
|
89
|
+
# - split the version number in chunks divided by ., - etc.
|
90
|
+
# - every chunk with same index is "compared", differences make up a score
|
91
|
+
# - "comparison" is a subtraction if the chunk is an integer, a string distance measure otherwise
|
92
|
+
# - score weighs differently on chunk index (first chunks are most important)
|
93
|
+
# - lowest score wins
|
94
|
+
def self.best_match(my_version, their_versions)
|
95
|
+
$log.debug("version comparison: #{my_version} vs #{their_versions.join(', ')}")
|
96
|
+
|
97
|
+
my_chunks = my_version.split /[\.\-\_ ~,]/
|
98
|
+
their_chunks_hash = Hash[
|
99
|
+
their_versions.map do |their_version|
|
100
|
+
their_chunks_for_version = their_version.split /[\.\-\_ ~,]/
|
101
|
+
their_chunks_for_version += [nil]*[my_chunks.length - their_chunks_for_version.length, 0].max
|
102
|
+
[their_version, their_chunks_for_version]
|
103
|
+
end
|
104
|
+
]
|
105
|
+
|
106
|
+
max_chunks_length = ([my_chunks.length] + their_chunks_hash.values.map {|chunk| chunk.length}).max
|
107
|
+
|
108
|
+
scoreboard = []
|
109
|
+
their_versions.each do |their_version|
|
110
|
+
their_chunks = their_chunks_hash[their_version]
|
111
|
+
score = 0
|
112
|
+
their_chunks.each_with_index do |their_chunk, i|
|
113
|
+
score_multiplier = 100**(max_chunks_length -i -1)
|
114
|
+
my_chunk = my_chunks[i]
|
115
|
+
score += chunk_distance(my_chunk, their_chunk) * score_multiplier
|
116
|
+
end
|
117
|
+
scoreboard << {:version => their_version, :score => score}
|
118
|
+
end
|
119
|
+
|
120
|
+
scoreboard = scoreboard.sort_by {|element| element[:score]}
|
121
|
+
|
122
|
+
$log.debug("scoreboard: ")
|
123
|
+
scoreboard.each_with_index do |element, i|
|
124
|
+
$log.debug(" #{i+1}. #{element[:version]} (score: #{element[:score]})")
|
125
|
+
end
|
126
|
+
|
127
|
+
winner = scoreboard.first
|
128
|
+
|
129
|
+
if winner != nil
|
130
|
+
return winner[:version]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# returns a score representing the distance between two version chunks
|
135
|
+
# for integers, the score is the difference between their values
|
136
|
+
# for strings, the score is the Levenshtein distance
|
137
|
+
# in any case score is normalized between 0 (identical) and 99 (very different/uncomparable)
|
138
|
+
def self.chunk_distance(my_chunk, their_chunk)
|
139
|
+
if my_chunk == nil
|
140
|
+
my_chunk = "0"
|
141
|
+
end
|
142
|
+
if their_chunk == nil
|
143
|
+
their_chunk = "0"
|
144
|
+
end
|
145
|
+
if my_chunk.is_i? and their_chunk.is_i?
|
146
|
+
return [(my_chunk.to_i - their_chunk.to_i).abs, 99].min
|
147
|
+
else
|
148
|
+
return [Text::Levenshtein.distance(my_chunk.upcase, their_chunk.upcase), 99].min
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class String
|
154
|
+
def is_i?
|
155
|
+
!!(self =~ /^[0-9]+$/)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "rest_client"
|
4
|
+
|
5
|
+
# implements the get-source subcommand
|
6
|
+
class SourceGetter
|
7
|
+
|
8
|
+
# downloads a project's source into a specified directory
|
9
|
+
def self.get_source(address, pomfile, directory)
|
10
|
+
$log.info("downloading: #{address} in #{directory}, pomfile: #{pomfile}")
|
11
|
+
|
12
|
+
dummy, prefix, scm_address = address.split(/^([^:]+):(.*)$/)
|
13
|
+
|
14
|
+
$log.info("prefix: #{prefix}, scm_address: #{scm_address}")
|
15
|
+
|
16
|
+
if prefix == "git"
|
17
|
+
get_source_from_git(scm_address, pomfile, directory)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# checks out from git
|
22
|
+
def self.get_source_from_git(scm_address, pomfile, directory)
|
23
|
+
pom = Pom.new(pomfile)
|
24
|
+
dir = File.join(directory, "#{pom.group_id}:#{pom.artifact_id}:#{pom.version}")
|
25
|
+
|
26
|
+
Dir::mkdir(dir)
|
27
|
+
`git clone #{scm_address} #{dir}`
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "rest_client"
|
4
|
+
require "json"
|
5
|
+
require "open-uri"
|
6
|
+
|
7
|
+
# implements the get-source-address subcommand
|
8
|
+
class SourceAddressGetter
|
9
|
+
|
10
|
+
# returns the pom corresponding to a file or directory, if it can be found
|
11
|
+
def self.get_source_address(file)
|
12
|
+
$log.info("looking for source address for: #{file}")
|
13
|
+
(get_source_address_from_pom(file) or get_source_address_from_github(file))
|
14
|
+
end
|
15
|
+
|
16
|
+
# returns an scm address in a pom file
|
17
|
+
def self.get_source_address_from_pom(file)
|
18
|
+
pom = Pom.new(file)
|
19
|
+
result = pom.connection_address
|
20
|
+
|
21
|
+
if result != nil
|
22
|
+
$log.info("address found in pom")
|
23
|
+
result
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# returns an scm address looking for it on github
|
28
|
+
def self.get_source_address_from_github(file)
|
29
|
+
pom = Pom.new(file)
|
30
|
+
|
31
|
+
result = (github_search(pom.artifact_id) or github_search(pom.artifact_id.split("-").first) or github_search(pom.group_id))
|
32
|
+
|
33
|
+
if result != nil
|
34
|
+
$log.info("address found on Github: #{result}")
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# returns a Giuthub repo address based on the keyword
|
40
|
+
def self.github_search(keyword)
|
41
|
+
if keyword != "" and keyword != nil
|
42
|
+
response = RestClient.get "https://api.github.com/legacy/repos/search/" + CGI::escape(keyword), :user_agent => "gjp/" + Gjp::VERSION, :language => "java", :sort => "forks"
|
43
|
+
json = JSON.parse(response.to_s)
|
44
|
+
|
45
|
+
(json["repositories"].map {|repository| "git:" + repository["url"]}).first
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|