reaper-man 0.1.4 → 0.1.6
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/CHANGELOG.md +4 -0
- data/CONTRIBUTING.md +2 -2
- data/README.md +4 -5
- data/lib/reaper-man/generator.rb +1 -1
- data/lib/reaper-man/generator/rubygems.rb +5 -1
- data/lib/reaper-man/generator/yum.rb +239 -0
- data/lib/reaper-man/package_list.rb +3 -0
- data/lib/reaper-man/package_list/gem.rb +2 -2
- data/lib/reaper-man/package_list/rpm.rb +234 -0
- data/lib/reaper-man/signer/rpm.rb +42 -0
- data/lib/reaper-man/version.rb +1 -1
- data/reaper-man.gemspec +2 -1
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 632a3a30795b366dec8638fdd49b437ee9672f33
|
4
|
+
data.tar.gz: 38261b77a034dd4024da01794451266a7e6698cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 726a868ca539dd913f79393df783df70f0967e281280994751ccd9bc080cca7f909121a17c251d62cfca01452ad023852227f26a0e46186fc4f34b1af68977ed
|
7
|
+
data.tar.gz: b73b0dc046dbb1fa02b129e5fc7fbe7294984c7149a5dd088169b5f4c96d95bc69f5d18e5a93d601e58613c10e7ffec2c32696b37e52ad2ab3e465d3da63b328
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# v0.1.6
|
2
|
+
* [fix] update gem spec loading and requirement building for package list
|
3
|
+
* [fix] deflate contents of quick specs on gem repositories
|
4
|
+
|
1
5
|
# v0.1.4
|
2
6
|
* [fix] fix package and repository signing for deb/apt
|
3
7
|
* [enhancement] allow passing mix of files/directories for signing
|
data/CONTRIBUTING.md
CHANGED
@@ -12,7 +12,7 @@ The develop branch is the current edge of development.
|
|
12
12
|
|
13
13
|
## Pull requests
|
14
14
|
|
15
|
-
* https://github.com/
|
15
|
+
* https://github.com/spox/reaper-man
|
16
16
|
|
17
17
|
Please base all pull requests of the `develop` branch. Merges to
|
18
18
|
`master` only occur through the `develop` branch. Pull requests
|
@@ -22,4 +22,4 @@ based on `master` will likely be cherry picked.
|
|
22
22
|
|
23
23
|
Need to report an issue? Use the github issues:
|
24
24
|
|
25
|
-
* https://github.com/
|
25
|
+
* https://github.com/spox/reaper-man/issues
|
data/README.md
CHANGED
@@ -19,9 +19,6 @@ assets (and the delivery of said assets) is left to the reader.
|
|
19
19
|
|
20
20
|
* deb/apt
|
21
21
|
* gem/rubygems
|
22
|
-
|
23
|
-
#### In Progress
|
24
|
-
|
25
22
|
* rpm/yum
|
26
23
|
|
27
24
|
### Usage
|
@@ -58,8 +55,10 @@ Commands that must be available within the path:
|
|
58
55
|
|
59
56
|
* `gpg`
|
60
57
|
* `debsigs`
|
58
|
+
* `dpkg-deb`
|
59
|
+
* `rpm`
|
60
|
+
* `rpmsign`
|
61
61
|
* `expect`
|
62
62
|
|
63
63
|
## Infos
|
64
|
-
* Repository: https://github.com/
|
65
|
-
* IRC: Freenode @ #heavywater
|
64
|
+
* Repository: https://github.com/spox/reaper-man
|
data/lib/reaper-man/generator.rb
CHANGED
@@ -56,6 +56,8 @@ module ReaperMan
|
|
56
56
|
info.each do |var, value|
|
57
57
|
if(spec.respond_to?("#{var}="))
|
58
58
|
begin
|
59
|
+
# Ensure we convert Smash instances
|
60
|
+
value = value.to_hash if value.is_a?(Hash)
|
59
61
|
spec.send("#{var}=", value)
|
60
62
|
rescue Gem::InvalidSpecificationException => e
|
61
63
|
# TODO: Do we have a logger in this project?
|
@@ -67,8 +69,10 @@ module ReaperMan
|
|
67
69
|
info[:dependencies].each do |dep|
|
68
70
|
spec.add_dependency(*dep)
|
69
71
|
end
|
72
|
+
deflator = Zlib::Deflate.new
|
70
73
|
create_file('quick', marshal_path, "#{name}-#{version}.gemspec.rz") do |file|
|
71
|
-
file.write(Marshal.dump(spec))
|
74
|
+
file.write(deflator.deflate(Marshal.dump(spec), Zlib::SYNC_FLUSH))
|
75
|
+
file.write(deflator.finish)
|
72
76
|
end
|
73
77
|
end
|
74
78
|
end
|
@@ -1,10 +1,249 @@
|
|
1
1
|
require 'reaper-man'
|
2
|
+
require 'xmlsimple'
|
2
3
|
require 'time'
|
3
4
|
|
4
5
|
module ReaperMan
|
5
6
|
class Generator
|
6
7
|
# Generator methods for yum
|
7
8
|
module Yum
|
9
|
+
|
10
|
+
# Version flag mappings
|
11
|
+
VERSION_FLAGS = {
|
12
|
+
'2' => 'LT',
|
13
|
+
'4' => 'GT',
|
14
|
+
'8' => 'EQ',
|
15
|
+
'10' => 'LE',
|
16
|
+
'12' => 'GE'
|
17
|
+
}
|
18
|
+
|
19
|
+
# Location of xmlns (though all are now defunct)
|
20
|
+
XMLNS_MAP = {
|
21
|
+
:repo => 'http://linux.duke.edu/metadata/repo',
|
22
|
+
:common => 'http://linux.duke.edu/metadata/common',
|
23
|
+
:rpm => 'http://linux.duke.edu/metadata/rpm',
|
24
|
+
:filelists => 'http://linux.duke.edu/metadata/filelists',
|
25
|
+
:other => 'http://linux.duke.edu/metadata/other'
|
26
|
+
}
|
27
|
+
|
28
|
+
# Generate the repository
|
29
|
+
def generate!
|
30
|
+
generate_dists(package_config[:yum])
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate the repository dists
|
34
|
+
#
|
35
|
+
# @param pkg_hash [Hash] repository description
|
36
|
+
# @return [TrueClass]
|
37
|
+
def generate_dists(pkg_hash)
|
38
|
+
pkg_hash.each do |origin_name, dists|
|
39
|
+
dists.each do |dist_name, dist_args|
|
40
|
+
dist_args[:components].each do |component_name, arches|
|
41
|
+
packages = arches.values.flatten.compact.map(&:values).flatten.compact.map(&:values).flatten.compact
|
42
|
+
p_file = primary_file(origin_name, dist_name, component_name, packages)
|
43
|
+
f_file = filelist_file(origin_name, dist_name, component_name, packages)
|
44
|
+
sign_file_if_setup do
|
45
|
+
repomd_file(origin_name, dist_name, component_name,
|
46
|
+
:primary => p_file,
|
47
|
+
:filelists => f_file
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
# Sign file if configured for signing
|
57
|
+
#
|
58
|
+
# @yield block returning file path
|
59
|
+
# @return [String] file path
|
60
|
+
def sign_file_if_setup(opts=nil)
|
61
|
+
path = yield
|
62
|
+
if(signer && options[:sign])
|
63
|
+
signer.file(path, nil, opts)
|
64
|
+
end
|
65
|
+
path
|
66
|
+
end
|
67
|
+
|
68
|
+
# Create a primary file
|
69
|
+
#
|
70
|
+
# @param origin_name [String]
|
71
|
+
# @param dist_name [String]
|
72
|
+
# @param component_name [String]
|
73
|
+
# @param packages [Hash]
|
74
|
+
# @return [Array<String>] path to file, path to compressed file
|
75
|
+
def primary_file(origin_name, dist_name, component_name, packages)
|
76
|
+
content = {
|
77
|
+
:metadata => {
|
78
|
+
:@xmlns => XMLNS_MAP[:common],
|
79
|
+
'@xmlns:rpm' => XMLNS_MAP[:rpm],
|
80
|
+
:@packages => packages.size,
|
81
|
+
:package => packages.map{ |package|
|
82
|
+
{
|
83
|
+
:@type => 'rpm',
|
84
|
+
:name => package['NAME'],
|
85
|
+
:arch => package['ARCH'],
|
86
|
+
:version => {
|
87
|
+
:@epoch => package['EPOCHNUM'],
|
88
|
+
:@ver => package['VERSION'],
|
89
|
+
:@rel => package['RELEASE'].split('.').first
|
90
|
+
},
|
91
|
+
:checksum => [
|
92
|
+
{
|
93
|
+
:@type => 'sha',
|
94
|
+
:@pkgid => 'YES'
|
95
|
+
},
|
96
|
+
package[:generated_sha]
|
97
|
+
],
|
98
|
+
:summary => package['SUMMARY'],
|
99
|
+
:description => [package['DESCRIPTION']].flatten.compact.join(' '),
|
100
|
+
:packager => package['PACKAGER'],
|
101
|
+
:url => package['URL'],
|
102
|
+
:time => {
|
103
|
+
:@file => Time.now.to_i,
|
104
|
+
:@build => package['BUILDTIME']
|
105
|
+
},
|
106
|
+
:size => {
|
107
|
+
:@archive => package['ARCHIVESIZE'],
|
108
|
+
:@package => package[:generated_size],
|
109
|
+
:@installed => package['LONGSIZE']
|
110
|
+
},
|
111
|
+
:location => package[:generated_path],
|
112
|
+
:format => {
|
113
|
+
'rpm:license' => package['LICENSE'],
|
114
|
+
'rpm:vendor' => package['VENDOR'],
|
115
|
+
'rpm:group' => package['GROUP'],
|
116
|
+
'rpm:buildhost' => package['BUILDHOST'],
|
117
|
+
'rpm:header-range' => {
|
118
|
+
:@start => package[:generated_header][:start],
|
119
|
+
:@end => package[:generated_header][:end]
|
120
|
+
},
|
121
|
+
'rpm:provides' => {
|
122
|
+
'rpm:entry' => Array.new.tap{ |entries|
|
123
|
+
pro_ver = package['PROVIDEVERSION'].dup
|
124
|
+
package['PROVIDENAME'].each_with_index do |p_name, p_idx|
|
125
|
+
item = {:@name => p_name}
|
126
|
+
if(p_flag = VERSION_FLAGS[package['PROVIDEFLAGS'][p_idx]])
|
127
|
+
p_ver, p_rel = pro_ver.shift.split('-', 2)
|
128
|
+
item.merge!(:@flags => p_flag, :@ver => p_ver, :@rel => p_rel, :@epoch => package['EPOCHNUM'])
|
129
|
+
end
|
130
|
+
entries.push(item)
|
131
|
+
end
|
132
|
+
}
|
133
|
+
},
|
134
|
+
'rpm:requires' => {
|
135
|
+
'rpm:entry' => Array.new.tap{ |entries|
|
136
|
+
req_ver = package['REQUIREVERSION'].dup
|
137
|
+
package['REQUIRENAME'].each_with_index do |r_name, r_idx|
|
138
|
+
item = {:@name => r_name}
|
139
|
+
if(r_flag = VERSION_FLAGS[package['REQUIREFLAGS'][r_idx]])
|
140
|
+
r_ver, r_rel = req_ver.shift.split('-', 2)
|
141
|
+
item.merge!(:@flags => r_flag, :@ver => r_ver, :@rel => r_rel, :@epoch => package['EPOCHNUM'])
|
142
|
+
end
|
143
|
+
entries.push(item)
|
144
|
+
end
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
args = [origin_name, dist_name, component_name, 'repodata', 'primary.xml']
|
153
|
+
[
|
154
|
+
create_file(*args) do |file|
|
155
|
+
file.puts generate_xml(content)
|
156
|
+
end,
|
157
|
+
compress_file(*args)
|
158
|
+
]
|
159
|
+
end
|
160
|
+
|
161
|
+
# Create a filelist file
|
162
|
+
#
|
163
|
+
# @param origin_name [String]
|
164
|
+
# @param dist_name [String]
|
165
|
+
# @param component_name [String]
|
166
|
+
# @param packages [Hash]
|
167
|
+
# @return [Array<String>] path to file, path to compressed file
|
168
|
+
def filelist_file(origin_name, dist_name, component_name, packages)
|
169
|
+
content = {
|
170
|
+
'filelists' => {
|
171
|
+
:@xmlns => XMLNS_MAP[:filelists],
|
172
|
+
:@packages => packages.size,
|
173
|
+
:package => packages.map{ |package|
|
174
|
+
{
|
175
|
+
:@pkgid => package[:generated_sha],
|
176
|
+
:@name => package['NAME'],
|
177
|
+
:@arch => package['ARCH'],
|
178
|
+
:version => {
|
179
|
+
:@epoch => package['EPOCHNUM'],
|
180
|
+
:@ver => package['VERSION'],
|
181
|
+
:@rel => package['RELEASE'].split('.').first
|
182
|
+
},
|
183
|
+
:file => (package['FILENAMES'] + package['DIRNAMES']).map{ |dir|
|
184
|
+
{:@type => 'dir', :_content_ => dir}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
args = [origin_name, dist_name, component_name, 'repodata', 'filelists.xml']
|
191
|
+
[
|
192
|
+
create_file(*args) do |file|
|
193
|
+
file.puts generate_xml(content)
|
194
|
+
end,
|
195
|
+
compress_file(*args)
|
196
|
+
]
|
197
|
+
end
|
198
|
+
|
199
|
+
# Create a repomd file
|
200
|
+
#
|
201
|
+
# @param origin_name [String]
|
202
|
+
# @param dist_name [String]
|
203
|
+
# @param component_name [String]
|
204
|
+
# @param packages [Hash]
|
205
|
+
# @return [String] path to file
|
206
|
+
def repomd_file(origin_name, dist_name, component_name, files)
|
207
|
+
content = {
|
208
|
+
:repomd => {
|
209
|
+
:@xmlns => XMLNS_MAP[:repo],
|
210
|
+
:data => Hash.new.tap{ |data|
|
211
|
+
files.each do |f_name, f_paths|
|
212
|
+
data[f_name] = {
|
213
|
+
:location => File.join('repodata', File.basename(f_paths.first)),
|
214
|
+
'open-checksum' => {
|
215
|
+
:@type => 'sha',
|
216
|
+
:_content_ => checksum(File.open(f_paths.first), :sha1)
|
217
|
+
},
|
218
|
+
:checksum => {
|
219
|
+
:@type => 'sha',
|
220
|
+
:_content_ => checksum(File.open(f_paths.last), :sha1)
|
221
|
+
},
|
222
|
+
:timestamp => File.mtime(f_paths.first).to_i
|
223
|
+
}
|
224
|
+
end
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
args = [origin_name, dist_name, component_name, 'repodata', 'repomd.xml']
|
229
|
+
create_file(*args) do |file|
|
230
|
+
file.puts generate_xml(content)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Generate XML document
|
235
|
+
#
|
236
|
+
# @param hash [Hash]
|
237
|
+
# @return [String]
|
238
|
+
def generate_xml(hash)
|
239
|
+
XmlSimple.xml_out(hash,
|
240
|
+
'AttrPrefix' => true,
|
241
|
+
'KeepRoot' => true,
|
242
|
+
'ContentKey' => :_content_,
|
243
|
+
'XmlDeclaration' => '<?xml version="1.0" encoding="UTF-8" ?>'
|
244
|
+
)
|
245
|
+
end
|
246
|
+
|
8
247
|
end
|
9
248
|
end
|
10
249
|
end
|
@@ -38,14 +38,14 @@ module ReaperMan
|
|
38
38
|
# @param package [String] path to package
|
39
39
|
# @return [Hash]
|
40
40
|
def extract_fields(package)
|
41
|
-
spec = ::Gem::Package.
|
41
|
+
spec = ::Gem::Package.new(package).spec
|
42
42
|
fields = Smash[
|
43
43
|
spec.to_yaml_properties.map do |var_name|
|
44
44
|
[var_name.to_s.tr('@', ''), spec.instance_variable_get(var_name)]
|
45
45
|
end
|
46
46
|
]
|
47
47
|
fields['dependencies'] = fields['dependencies'].map do |dep|
|
48
|
-
[dep.name, dep.requirement.to_s]
|
48
|
+
[dep.name, dep.requirement.to_s.split(',').map(&:strip)]
|
49
49
|
end
|
50
50
|
fields
|
51
51
|
end
|
@@ -3,8 +3,242 @@ require 'reaper-man'
|
|
3
3
|
module ReaperMan
|
4
4
|
class PackageList
|
5
5
|
class Processor
|
6
|
+
# Package list process for RPM packages
|
6
7
|
class Rpm < Processor
|
7
8
|
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :origin
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :dist
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :component
|
15
|
+
# @return [Array<String>] architectures
|
16
|
+
attr_reader :all_map
|
17
|
+
# @return [String] prefix for package file location
|
18
|
+
attr_reader :package_root
|
19
|
+
# @return [String] namespace for packages
|
20
|
+
attr_reader :package_bucket
|
21
|
+
|
22
|
+
# default package root prefix
|
23
|
+
DEFAULT_ROOT = 'pool'
|
24
|
+
# default namespace for packages
|
25
|
+
DEFAULT_BUCKET = 'public'
|
26
|
+
# default architectures to define
|
27
|
+
DEFAULT_ALL_MAP = ['amd64', 'i386']
|
28
|
+
|
29
|
+
# Create new instance
|
30
|
+
#
|
31
|
+
# @param args [Hash]
|
32
|
+
# @option args [String] :origin
|
33
|
+
# @option args [String] :codename
|
34
|
+
# @option args [String] :component
|
35
|
+
# @option args [String] :package_root
|
36
|
+
# @option args [String] :package_bucket
|
37
|
+
# @option args [Array<String>] :all_map
|
38
|
+
def initialize(args={})
|
39
|
+
@origin = args[:origin].to_s
|
40
|
+
@dist = args[:codename].to_s
|
41
|
+
@component = args[:component].to_s
|
42
|
+
@package_root = args.fetch(:package_root, DEFAULT_ROOT)
|
43
|
+
@package_bucket = args.fetch(:package_bucket, DEFAULT_BUCKET)
|
44
|
+
if(dist.empty? || component.empty?)
|
45
|
+
raise 'Both `codename` and `component` must contain valid values'
|
46
|
+
end
|
47
|
+
@all_map = args.fetch(:all_map, DEFAULT_ALL_MAP)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Add a package to the list
|
51
|
+
#
|
52
|
+
# @param conf [Hash]
|
53
|
+
# @param package [String] path to package
|
54
|
+
def add(hash, package)
|
55
|
+
info = extract_fields(package)
|
56
|
+
info.merge!(generate_checksums(package))
|
57
|
+
filenames = inject_package(hash, info, package)
|
58
|
+
filenames
|
59
|
+
end
|
60
|
+
|
61
|
+
# Remove package from the list
|
62
|
+
#
|
63
|
+
# @param conf [Hash] configuration hash
|
64
|
+
# @param package_name [String] name
|
65
|
+
# @param version [String]
|
66
|
+
def remove(hash, package_name, version, args={})
|
67
|
+
hash = hash.to_smash
|
68
|
+
arch = [args.fetch(:arch, all_map)].flatten.compact
|
69
|
+
deleted = false
|
70
|
+
arch.each do |arch_name|
|
71
|
+
arch_name = "binary-#{arch_name}"
|
72
|
+
if(hash.get(:yum, origin, dist, :components, component, arch_name, package_name))
|
73
|
+
if(version)
|
74
|
+
deleted = hash[:yum][origin][dist][:components][component][arch_name][package_name].delete(version)
|
75
|
+
else
|
76
|
+
deleted = hash[:yum][origin][dist][:components][component][arch_name].delete(package_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
!!deleted
|
81
|
+
end
|
82
|
+
|
83
|
+
# Extract package metadata
|
84
|
+
#
|
85
|
+
# @param package [String] path to package
|
86
|
+
# @return [Hash]
|
87
|
+
def extract_fields(package)
|
88
|
+
fields = shellout('rpm --querytags').stdout.split("\n").map do |line|
|
89
|
+
line.strip!
|
90
|
+
unless(line.empty? || line.start_with?('HEADER'))
|
91
|
+
line
|
92
|
+
end
|
93
|
+
end.compact
|
94
|
+
|
95
|
+
fmt = fields.map do |k|
|
96
|
+
["\\[#{k}\\]", "[%{#{k}}\n]"]
|
97
|
+
end.flatten.join("\n")
|
98
|
+
|
99
|
+
cmd = "rpm -q -p #{package} --queryformat 'output:\n#{fmt}'"
|
100
|
+
result = shellout(cmd).stdout.sub(/.*output:/, '')
|
101
|
+
|
102
|
+
data = Smash.new
|
103
|
+
key = nil
|
104
|
+
result.split("\n").each do |item|
|
105
|
+
item.strip!
|
106
|
+
next if item.empty?
|
107
|
+
if(item.start_with?('[') && item.end_with?(']'))
|
108
|
+
key = item.tr('[]', '')
|
109
|
+
else
|
110
|
+
if(data[key])
|
111
|
+
if(!data[key].is_a?(Array))
|
112
|
+
data[key] = [data[key]]
|
113
|
+
end
|
114
|
+
data[key] << item
|
115
|
+
else
|
116
|
+
data[key] = item
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
data[:generated_sha] = checksum(File.open(package, 'r'), :sha1)
|
121
|
+
data[:generated_size] = File.size(package)
|
122
|
+
data[:generated_header] = extract_header_information(package)
|
123
|
+
data
|
124
|
+
end
|
125
|
+
|
126
|
+
# Insert package information into package list
|
127
|
+
#
|
128
|
+
# @param hash [Hash] package list contents
|
129
|
+
# @param info [Hash] package information
|
130
|
+
# @param package [String] path to package file
|
131
|
+
# @return [Array<String>] package paths within package list contents
|
132
|
+
def inject_package(hash, info, package)
|
133
|
+
arch = info['ARCH']
|
134
|
+
arch = arch == 'all' ? all_map : [arch]
|
135
|
+
arch.map do |arch|
|
136
|
+
package_file_name = File.join(
|
137
|
+
package_root, package_bucket, origin,
|
138
|
+
dist, component, File.basename(package)
|
139
|
+
)
|
140
|
+
hash.deep_merge!(
|
141
|
+
'yum' => {
|
142
|
+
origin => {
|
143
|
+
dist => {
|
144
|
+
'components' => {
|
145
|
+
component => {
|
146
|
+
arch => {
|
147
|
+
info['NAME'] => {
|
148
|
+
info['NEVR'] => info.merge(:generated_path => package_file_name)
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
)
|
157
|
+
File.join('yum', origin, package_file_name)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Generate required checksums for given package
|
162
|
+
#
|
163
|
+
# @param package [String] path to package file
|
164
|
+
# @return [Hash] checksums
|
165
|
+
def generate_checksums(package)
|
166
|
+
File.open(package, 'r') do |pkg|
|
167
|
+
{
|
168
|
+
'MD5sum' => checksum(pkg.rewind && pkg, :md5),
|
169
|
+
'SHA1' => checksum(pkg.rewind && pkg, :sha1),
|
170
|
+
'SHA256' => checksum(pkg.rewind && pkg, :sha256)
|
171
|
+
}
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Extract the start and end points of the header within the
|
176
|
+
# package
|
177
|
+
#
|
178
|
+
# @param package [String] path to package
|
179
|
+
# @return [Smash<start,end>]
|
180
|
+
# @note ported from: http://yum.baseurl.org/gitweb?p=yum.git;a=blob;f=yum/packages.py;h=eebeb9dfd264b887b054187276cea12ced3a0bc2;hb=HEAD#l2212
|
181
|
+
def extract_header_information(package)
|
182
|
+
io = File.open(package, 'rb')
|
183
|
+
|
184
|
+
# read past lead and 8 bytes of signature header
|
185
|
+
io.seek(104)
|
186
|
+
binindex = io.read(4)
|
187
|
+
sigindex, _ = binindex.unpack('I>')
|
188
|
+
|
189
|
+
bindata = io.read(4)
|
190
|
+
sigdata, _ = bindata.unpack('I>')
|
191
|
+
|
192
|
+
# seeked in to 112 bytes
|
193
|
+
|
194
|
+
# each index is 4 32bit segments == 16 bytes
|
195
|
+
|
196
|
+
sigindexsize = sigindex * 16
|
197
|
+
sigsize = sigdata + sigindexsize
|
198
|
+
|
199
|
+
# Round to next 8 byte boundary
|
200
|
+
|
201
|
+
disttoboundary = (sigsize % 8)
|
202
|
+
unless(disttoboundary == 0)
|
203
|
+
disttoboundary = 8 - disttoboundary
|
204
|
+
end
|
205
|
+
|
206
|
+
# 112 bytes - 96 == lead
|
207
|
+
# 8 == magic and reserved
|
208
|
+
# 8 == signature header data
|
209
|
+
|
210
|
+
hdrstart = 112 + sigsize + disttoboundary
|
211
|
+
|
212
|
+
# seek to start of header
|
213
|
+
io.seek(hdrstart)
|
214
|
+
# seek past magic
|
215
|
+
io.seek(8, IO::SEEK_CUR)
|
216
|
+
|
217
|
+
binindex = io.read(4)
|
218
|
+
|
219
|
+
hdrindex, _ = binindex.unpack('I>')
|
220
|
+
bindata = io.read(4)
|
221
|
+
hdrdata, _ = bindata.unpack('I>')
|
222
|
+
|
223
|
+
# each index is 4 32bit segments - so each is 16 bytes
|
224
|
+
|
225
|
+
hdrindexsize = hdrindex * 16
|
226
|
+
|
227
|
+
# add 16 to the hdrsize to account for 16 bytes of misc data
|
228
|
+
# between the end of the sig and the header
|
229
|
+
|
230
|
+
hdrsize = hdrdata + hdrindexsize + 16
|
231
|
+
|
232
|
+
# header end is hdrstart + hdrsize
|
233
|
+
|
234
|
+
hdrend = hdrstart + hdrsize
|
235
|
+
|
236
|
+
Smash.new(
|
237
|
+
:start => hdrstart,
|
238
|
+
:end => hdrend
|
239
|
+
)
|
240
|
+
end
|
241
|
+
|
8
242
|
end
|
9
243
|
end
|
10
244
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'reaper-man'
|
2
|
+
|
3
|
+
module ReaperMan
|
4
|
+
class Signer
|
5
|
+
# Signing methods for rpm files
|
6
|
+
module Rpm
|
7
|
+
|
8
|
+
# Sign given files
|
9
|
+
#
|
10
|
+
# @param pkgs [String] list of file paths
|
11
|
+
# @return [TrueClass]
|
12
|
+
def package(*pkgs)
|
13
|
+
pkgs = valid_packages(*pkgs)
|
14
|
+
pkgs.each_slice(sign_chunk_size) do |pkgs|
|
15
|
+
cmd = %(rpmsign --resign --key-id="#{key_id}" #{pkgs.join(' ')})
|
16
|
+
if(key_password)
|
17
|
+
shellout(
|
18
|
+
"#{Signer::HELPER_COMMAND} #{cmd}",
|
19
|
+
:environment => {
|
20
|
+
'REAPER_KEY_PASSWORD' => key_password
|
21
|
+
}
|
22
|
+
)
|
23
|
+
else
|
24
|
+
shellout(cmd)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
# Filter only valid paths for signing (.rpm extensions)
|
31
|
+
#
|
32
|
+
# @param pkgs [String] list of file paths
|
33
|
+
# @return [Array<String>]
|
34
|
+
def valid_packages(*pkgs)
|
35
|
+
pkgs.find_all do |pkg|
|
36
|
+
File.extname(pkg) == '.rpm'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/reaper-man/version.rb
CHANGED
data/reaper-man.gemspec
CHANGED
@@ -6,11 +6,12 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.summary = 'Reap packages'
|
7
7
|
s.author = 'Chris Roberts'
|
8
8
|
s.email = 'code@chrisroberts.org'
|
9
|
-
s.homepage = 'https://github.com/
|
9
|
+
s.homepage = 'https://github.com/spox/reaper-man'
|
10
10
|
s.description = 'Grow code, reap packages'
|
11
11
|
s.require_path = 'lib'
|
12
12
|
s.add_runtime_dependency 'bogo-cli'
|
13
13
|
s.add_runtime_dependency 'childprocess'
|
14
|
+
s.add_runtime_dependency 'xml-simple'
|
14
15
|
s.add_development_dependency 'minitest'
|
15
16
|
s.executables << 'reaper-man'
|
16
17
|
s.files = Dir['lib/**/*'] + %w(reaper-man.gemspec README.md CHANGELOG.md CONTRIBUTING.md LICENSE)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reaper-man
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Roberts
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bogo-cli
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: xml-simple
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: minitest
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,13 +97,14 @@ files:
|
|
83
97
|
- lib/reaper-man/package_list/rpm.rb
|
84
98
|
- lib/reaper-man/signer.rb
|
85
99
|
- lib/reaper-man/signer/deb.rb
|
100
|
+
- lib/reaper-man/signer/rpm.rb
|
86
101
|
- lib/reaper-man/signer/rubygems.rb
|
87
102
|
- lib/reaper-man/util-scripts/auto-helper
|
88
103
|
- lib/reaper-man/utils.rb
|
89
104
|
- lib/reaper-man/utils/process.rb
|
90
105
|
- lib/reaper-man/version.rb
|
91
106
|
- reaper-man.gemspec
|
92
|
-
homepage: https://github.com/
|
107
|
+
homepage: https://github.com/spox/reaper-man
|
93
108
|
licenses: []
|
94
109
|
metadata: {}
|
95
110
|
post_install_message:
|
@@ -108,9 +123,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
123
|
version: '0'
|
109
124
|
requirements: []
|
110
125
|
rubyforge_project:
|
111
|
-
rubygems_version: 2.
|
126
|
+
rubygems_version: 2.4.8
|
112
127
|
signing_key:
|
113
128
|
specification_version: 4
|
114
129
|
summary: Reap packages
|
115
130
|
test_files: []
|
116
|
-
has_rdoc:
|