arr-pm 0.0.2
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.
Potentially problematic release.
This version of arr-pm might be problematic. Click here for more details.
- data/.batcave/manifest +5 -0
- data/.gitignore +13 -0
- data/Gemfile +1 -0
- data/LICENSE +14 -0
- data/Makefile +46 -0
- data/README.md +72 -0
- data/header-verify.rb +71 -0
- data/lib/arr-pm.rb +61 -0
- data/lib/arr-pm/conflicts.rb +5 -0
- data/lib/arr-pm/file.rb +145 -0
- data/lib/arr-pm/file/header.rb +89 -0
- data/lib/arr-pm/file/lead.rb +52 -0
- data/lib/arr-pm/file/tag.rb +307 -0
- data/lib/arr-pm/namespace.rb +3 -0
- data/lib/arr-pm/requires.rb +23 -0
- data/notify-failure.sh +15 -0
- data/printrpm.rb +22 -0
- data/rpm.gemspec +21 -0
- data/test/all.rb +24 -0
- data/test/docs.rb +40 -0
- data/test/testing.rb +14 -0
- metadata +81 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
source :rubygems
|
data/LICENSE
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Copyright 2012 Jordan Sissel
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
14
|
+
|
data/Makefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
GEMSPEC=$(shell ls *.gemspec)
|
2
|
+
VERSION=$(shell awk -F\" '/spec.version/ { print $$2 }' $(GEMSPEC))
|
3
|
+
NAME=$(shell awk -F\" '/spec.name/ { print $$2 }' $(GEMSPEC))
|
4
|
+
GEM=$(NAME)-$(VERSION).gem
|
5
|
+
|
6
|
+
.PHONY: test
|
7
|
+
test:
|
8
|
+
sh notify-failure.sh ruby test/all.rb
|
9
|
+
|
10
|
+
.PHONY: testloop
|
11
|
+
testloop:
|
12
|
+
while true; do \
|
13
|
+
$(MAKE) test; \
|
14
|
+
$(MAKE) wait-for-changes; \
|
15
|
+
done
|
16
|
+
|
17
|
+
.PHONY: serve-coverage
|
18
|
+
serve-coverage:
|
19
|
+
cd coverage; python -mSimpleHTTPServer
|
20
|
+
|
21
|
+
.PHONY: wait-for-changes
|
22
|
+
wait-for-changes:
|
23
|
+
-inotifywait --exclude '\.swp' -e modify $$(find $(DIRS) -name '*.rb'; find $(DIRS) -type d)
|
24
|
+
|
25
|
+
.PHONY: package
|
26
|
+
package: | $(GEM)
|
27
|
+
|
28
|
+
.PHONY: gem
|
29
|
+
gem: $(GEM)
|
30
|
+
|
31
|
+
$(GEM):
|
32
|
+
gem build $(GEMSPEC)
|
33
|
+
|
34
|
+
.PHONY: test-package
|
35
|
+
test-package: $(GEM)
|
36
|
+
# Sometimes 'gem build' makes a faulty gem.
|
37
|
+
gem unpack $(GEM)
|
38
|
+
rm -rf ftw-$(VERSION)/
|
39
|
+
|
40
|
+
.PHONY: publish
|
41
|
+
publish: test-package
|
42
|
+
gem push $(GEM)
|
43
|
+
|
44
|
+
.PHONY: install
|
45
|
+
install: $(GEM)
|
46
|
+
gem install $(GEM)
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# ARRRRRRRRRR PM
|
2
|
+
|
3
|
+
RPM reader/writer library written in Ruby.
|
4
|
+
|
5
|
+
It aims to provide [fpm](https://github.com/jordansissel/fpm) with a way to
|
6
|
+
read and write rpms.
|
7
|
+
|
8
|
+
## Why not use librpm?
|
9
|
+
|
10
|
+
The API is quite confusing in many places, poorly documented in most. I have
|
11
|
+
reached out to some CentOS/rpm folks to see what we can do about improving that
|
12
|
+
API.
|
13
|
+
|
14
|
+
Even still, librpm has dependencies of its own. I want fpm to be able to read
|
15
|
+
and write RPMs without requiring and endless chain of dependencies and most
|
16
|
+
especially without requiring root access to get things going.
|
17
|
+
|
18
|
+
Mainly, if I try to build librpm myself, I get this: "configure: error: missing
|
19
|
+
required NSPR / NSS header" and I'm not the burden of dependency resolution on
|
20
|
+
fpm users.
|
21
|
+
|
22
|
+
## API ideas
|
23
|
+
|
24
|
+
It should be possible to do a read+modify+write on an RPM.
|
25
|
+
|
26
|
+
### Creating an RPM (proposed API)
|
27
|
+
|
28
|
+
rpm = RPM.new
|
29
|
+
|
30
|
+
# requires and conflicts
|
31
|
+
rpm.requires("name") <= "version" # Something fun-dsl-likef
|
32
|
+
rpm.requires("name", :<=, "version") # Or maybe something like this
|
33
|
+
|
34
|
+
# provides
|
35
|
+
rpm.provides("name")
|
36
|
+
|
37
|
+
# Add some files
|
38
|
+
rpm.files << path
|
39
|
+
rpm.files << path2
|
40
|
+
rpm.files << path3
|
41
|
+
|
42
|
+
# Scripts?
|
43
|
+
rpm.scripts[:preinstall](path_or_script_string)
|
44
|
+
rpm.scripts[:postinstall](path_or_script_string)
|
45
|
+
rpm.scripts[:preuninstall](path_or_script_string)
|
46
|
+
rpm.scripts[:postuninstall](path_or_script_string)
|
47
|
+
|
48
|
+
rpm.write(output_file_name)
|
49
|
+
|
50
|
+
### Reading an RPM (proposed API)
|
51
|
+
|
52
|
+
rpm = RPM.read(file)
|
53
|
+
|
54
|
+
rpm.requires # Array of RPM::Requires ?
|
55
|
+
rpm.provides # Array of 'Provides' strings?
|
56
|
+
rpm.conflicts # Array of RPM::Conflicts ?
|
57
|
+
rpm.files # Array of RPM::File ?
|
58
|
+
|
59
|
+
Maybe something like:
|
60
|
+
|
61
|
+
rpm.files.each do |file|
|
62
|
+
# Tags that are defined in rpm header tags
|
63
|
+
# fileclass filecolors filecontexts filedependsn filedependsx filedevices
|
64
|
+
# filedigests fileflags filegroupname fileinodes filelangs filelinktos
|
65
|
+
# filemodes filemtimes filerdevs filesizes fileusername fileverifyflags
|
66
|
+
#
|
67
|
+
# frankly, we don't care about most of these. Meaningful ones:
|
68
|
+
# username, groupname, size, mtime, mode, linkto
|
69
|
+
|
70
|
+
# file.io could give a nice IO-like thing that let you read the file out
|
71
|
+
# of the rpm
|
72
|
+
end
|
data/header-verify.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
HEADER_MAGIC = "\x8e\xad\xe8\x01\x00\x00\x00\x00"
|
2
|
+
TAG_ENTRY_SIZE = 16 # tag id, type, offset, count == 16 bytes
|
3
|
+
|
4
|
+
def read_header(io)
|
5
|
+
# RPM 'header' section looks like:
|
6
|
+
#
|
7
|
+
# MAGIC (8 bytes) index_count (4 bytes), data_length (4 bytes )
|
8
|
+
#
|
9
|
+
# * index_count is the number of 'tags' in this header.
|
10
|
+
# * data_length is a blob containing all the values for the tags
|
11
|
+
#
|
12
|
+
# Header has a header of 'magic' + index_count (4 bytes) + data_length (4 bytes)
|
13
|
+
#p "start of header" => io.pos
|
14
|
+
data = io.read(16).unpack("a8NN")
|
15
|
+
|
16
|
+
# TODO(sissel): @index_count is really a count, rename?
|
17
|
+
@magic, @index_count, @data_length = data
|
18
|
+
if @magic != HEADER_MAGIC
|
19
|
+
puts "Magic value in header was wrong. Expected #{HEADER_MAGIC.inspect}, but got #{@magic.inspect}"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
@index_size = @index_count * TAG_ENTRY_SIZE
|
24
|
+
tag_data = io.read(@index_size)
|
25
|
+
data = io.read(@data_length)
|
26
|
+
#p "end of header" => io.pos
|
27
|
+
|
28
|
+
(0 ... @index_count).each do |i|
|
29
|
+
offset = i * TAG_ENTRY_SIZE
|
30
|
+
entry_data = tag_data[i * TAG_ENTRY_SIZE, TAG_ENTRY_SIZE]
|
31
|
+
tag, tag_type, offset, count = entry_data.unpack("NNNN")
|
32
|
+
if block_given?
|
33
|
+
yield :tag => tag, :type => tag_type, :offset => offset, :count => count
|
34
|
+
end
|
35
|
+
#entry << data
|
36
|
+
end # each index
|
37
|
+
return 16 + @index_size + @data_length
|
38
|
+
end
|
39
|
+
|
40
|
+
if ARGV.length != 1
|
41
|
+
puts "Usage: #{$0} blah.rpm"
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
|
45
|
+
rpm = File.new(ARGV[0])
|
46
|
+
|
47
|
+
# Read the 'lead' - it's mostly an ignored part of the rpm file.
|
48
|
+
lead = rpm.read(96)
|
49
|
+
magic, major, minor, type, archnum, name, osnum, signature_type, reserved = lead.unpack("A4CCnnA66nnA16")
|
50
|
+
|
51
|
+
puts "RPM file version #{major}.#{minor} (#{signature_type == 5 ? "signed" : "unsigned"})"
|
52
|
+
|
53
|
+
if signature_type == 5
|
54
|
+
# Read a header for the rpm signature. This has the same format as a normal
|
55
|
+
# rpm header
|
56
|
+
puts "Checking signature"
|
57
|
+
length = read_header(rpm) do |tag|
|
58
|
+
# print each tag in this header
|
59
|
+
p tag
|
60
|
+
end
|
61
|
+
|
62
|
+
# signature headers are padded up to an 8-byte boundar, details here:
|
63
|
+
# http://rpm.org/gitweb?p=rpm.git;a=blob;f=lib/signature.c;h=63e59c00f255a538e48cbc8b0cf3b9bd4a4dbd56;hb=HEAD#l204
|
64
|
+
# Throw away the pad.
|
65
|
+
rpm.read((length % 8))
|
66
|
+
end
|
67
|
+
|
68
|
+
# Read the rpm header
|
69
|
+
puts "Checking header"
|
70
|
+
read_header(rpm)
|
71
|
+
p rpm.read(4)
|
data/lib/arr-pm.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require "rpm/namespace"
|
2
|
+
|
3
|
+
class RPM
|
4
|
+
private
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
#@requires = []
|
8
|
+
#@conflicts = []
|
9
|
+
#@provides = []
|
10
|
+
#@files = []
|
11
|
+
#@scripts = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def requires(name, operator=nil, version=nil)
|
15
|
+
@requires << [name, operator, version]
|
16
|
+
end # def requires
|
17
|
+
|
18
|
+
def conflicts(name, operator=nil, version=nil)
|
19
|
+
@conflicts << [name, operator, version]
|
20
|
+
end
|
21
|
+
|
22
|
+
def provides(name)
|
23
|
+
@provides << name
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.read(path_or_io)
|
27
|
+
rpmfile = RPM::File.new(path_or_io)
|
28
|
+
signature = rpmfile.signature
|
29
|
+
header = rpmfile.header
|
30
|
+
|
31
|
+
header.tags.each do |tag|
|
32
|
+
p tag
|
33
|
+
end
|
34
|
+
|
35
|
+
# Things we care about in the header:
|
36
|
+
# * name, version, release, epoch, summary, description
|
37
|
+
# *
|
38
|
+
# * requires, provides, conflicts
|
39
|
+
# * scripts
|
40
|
+
# * payload format
|
41
|
+
|
42
|
+
#payload = rpmfile.payload
|
43
|
+
# Parse the payload, check the rpmfile.header to find tags describing the
|
44
|
+
# format of the payload.
|
45
|
+
# Initial target should be gzipped cpio.
|
46
|
+
end # def self.read
|
47
|
+
|
48
|
+
def files
|
49
|
+
return @files
|
50
|
+
end
|
51
|
+
|
52
|
+
# Write this RPM to an IO-like object (must respond to 'write')
|
53
|
+
def write(io)
|
54
|
+
# write the lead
|
55
|
+
# write the signature?
|
56
|
+
# write the header
|
57
|
+
# write the payload
|
58
|
+
end
|
59
|
+
|
60
|
+
public(:files, :requires, :conflicts, :provides, :write)
|
61
|
+
end # class RPM
|
data/lib/arr-pm/file.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "namespace")
|
2
|
+
require File.join(File.dirname(__FILE__), "file", "header")
|
3
|
+
require File.join(File.dirname(__FILE__), "file", "lead")
|
4
|
+
require File.join(File.dirname(__FILE__), "file", "tag")
|
5
|
+
|
6
|
+
# Much of the code here is derived from knowledge gained by reading the rpm
|
7
|
+
# source code, but mostly it started making more sense after reading this site:
|
8
|
+
# http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html
|
9
|
+
|
10
|
+
class RPM::File
|
11
|
+
attr_reader :file
|
12
|
+
|
13
|
+
FLAG_LESS = (1 << 1) # RPMSENSE_LESS = (1 << 1),
|
14
|
+
FLAG_GREATER = (1 << 2) # RPMSENSE_GREATER = (1 << 2),
|
15
|
+
FLAG_EQUAL = (1 << 3) # RPMSENSE_EQUAL = (1 << 3),
|
16
|
+
|
17
|
+
def initialize(file)
|
18
|
+
if file.is_a?(String)
|
19
|
+
file = File.new(file, "r")
|
20
|
+
end
|
21
|
+
@file = file
|
22
|
+
|
23
|
+
end # def initialize
|
24
|
+
|
25
|
+
# Return the lead for this rpm
|
26
|
+
#
|
27
|
+
# This 'lead' structure is almost entirely deprecated in the RPM file format.
|
28
|
+
def lead
|
29
|
+
if @lead.nil?
|
30
|
+
# Make sure we're at the beginning of the file.
|
31
|
+
@file.seek(0, IO::SEEK_SET)
|
32
|
+
@lead = ::RPM::File::Lead.new(@file)
|
33
|
+
|
34
|
+
# TODO(sissel): have 'read' return number of bytes read?
|
35
|
+
@lead.read
|
36
|
+
end
|
37
|
+
return @lead
|
38
|
+
end # def lead
|
39
|
+
|
40
|
+
# Return the signature header for this rpm
|
41
|
+
def signature
|
42
|
+
lead # Make sure we've parsed the lead...
|
43
|
+
|
44
|
+
# If signature_type is not 5 (HEADER_SIGNED_TYPE), no signature.
|
45
|
+
if @lead.signature_type != Header::HEADER_SIGNED_TYPE
|
46
|
+
@signature = false
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
50
|
+
if @signature.nil?
|
51
|
+
@signature = ::RPM::File::Header.new(@file)
|
52
|
+
@signature.read
|
53
|
+
|
54
|
+
# signature headers are padded up to an 8-byte boundar, details here:
|
55
|
+
# http://rpm.org/gitweb?p=rpm.git;a=blob;f=lib/signature.c;h=63e59c00f255a538e48cbc8b0cf3b9bd4a4dbd56;hb=HEAD#l204
|
56
|
+
# Throw away the pad.
|
57
|
+
@file.read(@signature.length % 8)
|
58
|
+
end
|
59
|
+
|
60
|
+
return @signature
|
61
|
+
end # def signature
|
62
|
+
|
63
|
+
# Return the header for this rpm.
|
64
|
+
def header
|
65
|
+
signature
|
66
|
+
|
67
|
+
if @header.nil?
|
68
|
+
@header = ::RPM::File::Header.new(@file)
|
69
|
+
@header.read
|
70
|
+
end
|
71
|
+
return @header
|
72
|
+
end # def header
|
73
|
+
|
74
|
+
# Returns a file descriptor for the payload. On first invocation, it seeks to
|
75
|
+
# the start of the payload
|
76
|
+
def payload
|
77
|
+
header
|
78
|
+
if @payload.nil?
|
79
|
+
@payload = @file.clone
|
80
|
+
# The payload starts after the lead, signature, and header. Remember the signature has an
|
81
|
+
# 8-byte boundary-rounding.
|
82
|
+
@payload.seek(@lead.length + @signature.length + @signature.length % 8 + @header.length, IO::SEEK_SET)
|
83
|
+
end
|
84
|
+
|
85
|
+
return @payload
|
86
|
+
end # def payload
|
87
|
+
|
88
|
+
# Extract this RPM to a target directory.
|
89
|
+
#
|
90
|
+
# This should have roughly the same effect as:
|
91
|
+
#
|
92
|
+
# % rpm2cpio blah.rpm | (cd {target}; cpio -i --make-directories)
|
93
|
+
def extract(target)
|
94
|
+
if !File.directory?(target)
|
95
|
+
raise Errno::ENOENT.new(target)
|
96
|
+
end
|
97
|
+
|
98
|
+
tags = {}
|
99
|
+
header.tags.each do |tag|
|
100
|
+
tags[tag.tag] = tag.value
|
101
|
+
end
|
102
|
+
|
103
|
+
# Extract to the 'target' path
|
104
|
+
#tags[:payloadcompressor] # "xz" or "gzip" or ..?
|
105
|
+
#tags[:payloadformat] # "cpio"
|
106
|
+
|
107
|
+
extractor = IO.popen("#{tags[:payloadcompressor]} -d | (cd #{target}; cpio -i --make-directories)", "w")
|
108
|
+
buffer = ""
|
109
|
+
buffer.force_encoding("BINARY")
|
110
|
+
payload_fd = payload
|
111
|
+
loop do
|
112
|
+
data = payload.read(16384, buffer)
|
113
|
+
break if data.nil? # eof
|
114
|
+
extractor.write(data)
|
115
|
+
end
|
116
|
+
extractor.close
|
117
|
+
end # def extract
|
118
|
+
|
119
|
+
def requires
|
120
|
+
tags = {}
|
121
|
+
header.tags.each do |tag|
|
122
|
+
tags[tag.tag] = tag.value
|
123
|
+
end
|
124
|
+
|
125
|
+
result = []
|
126
|
+
reqs = tags[:requirename].zip(tags[:requireflags], tags[:requireversion])
|
127
|
+
reqs.each do |name, flag, version|
|
128
|
+
result << [name, operator(flag), version]
|
129
|
+
end
|
130
|
+
return result
|
131
|
+
end # def requires
|
132
|
+
|
133
|
+
def operator(flag)
|
134
|
+
have = lambda do |mask|
|
135
|
+
return flag & (mask) == mask
|
136
|
+
end
|
137
|
+
return "=" if have.call(FLAG_EQUAL)
|
138
|
+
return "<=" if have.call(FLAG_LESS | FLAG_EQUAL)
|
139
|
+
return "<<" if have.call(FLAG_LESS)
|
140
|
+
return ">=" if have.call(FLAG_GREATER | FLAG_EQUAL)
|
141
|
+
return ">>" if have.call(FLAG_GREATER)
|
142
|
+
end # def operator
|
143
|
+
|
144
|
+
public(:extract, :payload, :header, :lead, :signature, :initialize, :requires)
|
145
|
+
end # class RPM::File
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "namespace")
|
2
|
+
require File.join(File.dirname(__FILE__), "tag")
|
3
|
+
require "cabin"
|
4
|
+
|
5
|
+
class RPM::File::Header
|
6
|
+
include Cabin::Inspectable
|
7
|
+
attr_reader :tags
|
8
|
+
attr_reader :length
|
9
|
+
|
10
|
+
attr_accessor :magic # 8-byte string magic
|
11
|
+
attr_accessor :index_count # rpmlib calls this field 'il' unhelpfully
|
12
|
+
attr_accessor :data_length # rpmlib calls this field 'dl' unhelpfully
|
13
|
+
|
14
|
+
HEADER_SIGNED_TYPE = 5
|
15
|
+
HEADER_MAGIC = "\x8e\xad\xe8\x01\x00\x00\x00\x00"
|
16
|
+
# magic + index_count + data_length
|
17
|
+
HEADER_HEADER_LENGTH = HEADER_MAGIC.length + 4 + 4
|
18
|
+
TAG_ENTRY_SIZE = 16 # tag id, type, offset, count == 16 bytes
|
19
|
+
|
20
|
+
def initialize(file)
|
21
|
+
@file = file
|
22
|
+
|
23
|
+
@inspectables = [:@length, :@index_count, :@data_length]
|
24
|
+
@tags = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def read
|
28
|
+
# TODO(sissel): update the comments here to reflect learnings about rpm
|
29
|
+
# internals
|
30
|
+
# At this point assume we've read and consumed the lead and signature.
|
31
|
+
#len = @rpm.signature.index_length + @rpm.signature
|
32
|
+
#
|
33
|
+
# header size is
|
34
|
+
# ( @rpm.signature.index_length * size of a header entry )
|
35
|
+
# + @rpm.signature.data_length
|
36
|
+
#
|
37
|
+
# header 'entries' are an
|
38
|
+
# int32 (tag id), int32 (tag type), int32 (offset), uint32 (count)
|
39
|
+
#
|
40
|
+
# len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo_s)) + dl;
|
41
|
+
# See rpm's header.c, the headerLoad method function for reference.
|
42
|
+
|
43
|
+
# Header always starts with HEADER_MAGIC + index_count(2bytes) +
|
44
|
+
# data_length(2bytes)
|
45
|
+
data = @file.read(HEADER_HEADER_LENGTH).unpack("a8NN")
|
46
|
+
# TODO(sissel): @index_count is really a count, rename?
|
47
|
+
@magic, @index_count, @data_length = data
|
48
|
+
validate
|
49
|
+
|
50
|
+
@index_size = @index_count * TAG_ENTRY_SIZE
|
51
|
+
tag_data = @file.read(@index_size)
|
52
|
+
data = @file.read(@data_length)
|
53
|
+
|
54
|
+
(0 ... @index_count).each do |i|
|
55
|
+
offset = i * TAG_ENTRY_SIZE
|
56
|
+
entry_data = tag_data[i * TAG_ENTRY_SIZE, TAG_ENTRY_SIZE]
|
57
|
+
entry = entry_data.unpack("NNNN")
|
58
|
+
entry << data
|
59
|
+
tag = ::RPM::File::Tag.new(*entry)
|
60
|
+
@tags << tag
|
61
|
+
end # each index
|
62
|
+
|
63
|
+
@length = HEADER_HEADER_LENGTH + @index_size + @data_length
|
64
|
+
end # def read
|
65
|
+
|
66
|
+
def write
|
67
|
+
raise "not implemented yet"
|
68
|
+
# Sort tags by type (integer value)
|
69
|
+
# emit all tags in order
|
70
|
+
# then emit all data segments in same order
|
71
|
+
end # def write
|
72
|
+
|
73
|
+
def validate
|
74
|
+
# TODO(sissel): raise real exceptions
|
75
|
+
if @magic != ::RPM::File::Header::HEADER_MAGIC
|
76
|
+
raise "Header magic did not match; got #{@magic.inspect}, " \
|
77
|
+
"expected #{::RPM::File::Header::HEADER_MAGIC.inspect}"
|
78
|
+
end
|
79
|
+
|
80
|
+
#if !(0..32).include?(@index_count)
|
81
|
+
#raise "Invalid 'index_count' value #{@index_count}, expected to be in range [0..32]"
|
82
|
+
#end
|
83
|
+
|
84
|
+
#if !(0..8192).include?(@data_length)
|
85
|
+
#raise "Invalid 'data_length' value #{@data_length}, expected to be in range [0..8192]"
|
86
|
+
#end
|
87
|
+
end # def validate
|
88
|
+
|
89
|
+
end # class RPM::File::Header
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "namespace")
|
2
|
+
require "cabin"
|
3
|
+
|
4
|
+
class RPM::File::Lead
|
5
|
+
include Cabin::Inspectable
|
6
|
+
|
7
|
+
#struct rpmlead {
|
8
|
+
attr_accessor :magic #unsigned char magic[4];
|
9
|
+
attr_accessor :major #unsigned char major;
|
10
|
+
attr_accessor :minor #unsigned char minor;
|
11
|
+
attr_accessor :type #short type;
|
12
|
+
attr_accessor :archnum #short archnum;
|
13
|
+
attr_accessor :name #char name[66];
|
14
|
+
attr_accessor :osnum #short osnum;
|
15
|
+
attr_accessor :signature_type #short signature_type;
|
16
|
+
attr_accessor :reserved #char reserved[16];
|
17
|
+
#}
|
18
|
+
|
19
|
+
attr_accessor :length
|
20
|
+
|
21
|
+
def initialize(file)
|
22
|
+
@file = file
|
23
|
+
@inspectables = [:@major, :@minor, :@length, :@type, :@archnum, :@signature_type, :@reserved, :@osnum]
|
24
|
+
end
|
25
|
+
|
26
|
+
def type
|
27
|
+
case @type
|
28
|
+
when 0
|
29
|
+
return :binary
|
30
|
+
when 1
|
31
|
+
return :source
|
32
|
+
else
|
33
|
+
raise "Unknown package 'type' value #{@type}"
|
34
|
+
end
|
35
|
+
end # def type
|
36
|
+
|
37
|
+
def read
|
38
|
+
# Use 'A' here instead of 'a' to trim nulls.
|
39
|
+
@length = 96
|
40
|
+
data = @file.read(@length).unpack("A4CCnnA66nnA16")
|
41
|
+
@magic, @major, @minor, @type, @archnum, @name, \
|
42
|
+
@osnum, @signature_type, @reserved = data
|
43
|
+
|
44
|
+
return nil
|
45
|
+
end # def read
|
46
|
+
|
47
|
+
def write(file)
|
48
|
+
data = [ @magic, @major, @minor, @type, @archnum, @name, \
|
49
|
+
@osnum, @signature_type, @reserved ].pack("a4CCnna66nna16")
|
50
|
+
file.write(data)
|
51
|
+
end # def write
|
52
|
+
end # class RPM::File::Lead
|
@@ -0,0 +1,307 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "namespace")
|
2
|
+
require "cabin"
|
3
|
+
|
4
|
+
class RPM::File::Tag
|
5
|
+
include Cabin::Inspectable
|
6
|
+
|
7
|
+
attr_accessor :tag
|
8
|
+
attr_accessor :type
|
9
|
+
attr_accessor :offset
|
10
|
+
attr_accessor :count
|
11
|
+
attr_accessor :value
|
12
|
+
|
13
|
+
# This data can be found mostly in rpmtag.h
|
14
|
+
TAG = {
|
15
|
+
61 => :headerimage,
|
16
|
+
62 => :headersignatures,
|
17
|
+
63 => :headerimmutable,
|
18
|
+
64 => :headerregions,
|
19
|
+
100 => :headeri18ntable,
|
20
|
+
256 => :sig_base,
|
21
|
+
|
22
|
+
257 => :sigsize,
|
23
|
+
258 => :siglemd5_1,
|
24
|
+
259 => :sigpgp,
|
25
|
+
260 => :siglemd5_2,
|
26
|
+
261 => :sigmd5,
|
27
|
+
262 => :siggpg,
|
28
|
+
263 => :sigpgp5,
|
29
|
+
264 => :badsha1_1,
|
30
|
+
265 => :badsha1_2,
|
31
|
+
266 => :pubkeys,
|
32
|
+
267 => :dsaheader,
|
33
|
+
268 => :rsaheader,
|
34
|
+
269 => :sha1header,
|
35
|
+
270 => :longsigsize,
|
36
|
+
271 => :longarchivesize,
|
37
|
+
|
38
|
+
1000 => :name,
|
39
|
+
1001 => :version,
|
40
|
+
1002 => :release,
|
41
|
+
1003 => :epoch,
|
42
|
+
1004 => :summary,
|
43
|
+
1005 => :description,
|
44
|
+
1006 => :buildtime,
|
45
|
+
1007 => :buildhost,
|
46
|
+
1008 => :installtime,
|
47
|
+
1009 => :size,
|
48
|
+
1010 => :distribution,
|
49
|
+
1011 => :vendor,
|
50
|
+
1012 => :gif,
|
51
|
+
1013 => :xpm,
|
52
|
+
1014 => :license,
|
53
|
+
1015 => :packager,
|
54
|
+
1016 => :group,
|
55
|
+
1017 => :changelog,
|
56
|
+
1018 => :source,
|
57
|
+
1019 => :patch,
|
58
|
+
1020 => :url,
|
59
|
+
1021 => :os,
|
60
|
+
1022 => :arch,
|
61
|
+
1023 => :prein,
|
62
|
+
1024 => :postin,
|
63
|
+
1025 => :preun,
|
64
|
+
1026 => :postun,
|
65
|
+
1027 => :oldfilenames,
|
66
|
+
1028 => :filesizes,
|
67
|
+
1029 => :filestates,
|
68
|
+
1030 => :filemodes,
|
69
|
+
1031 => :fileuids,
|
70
|
+
1032 => :filegids,
|
71
|
+
1033 => :filerdevs,
|
72
|
+
1034 => :filemtimes,
|
73
|
+
1035 => :filedigests,
|
74
|
+
1036 => :filelinktos,
|
75
|
+
1037 => :fileflags,
|
76
|
+
1038 => :root,
|
77
|
+
1039 => :fileusername,
|
78
|
+
1040 => :filegroupname,
|
79
|
+
1041 => :exclude,
|
80
|
+
1042 => :exclusive,
|
81
|
+
1043 => :icon,
|
82
|
+
1044 => :sourcerpm,
|
83
|
+
1045 => :fileverifyflags,
|
84
|
+
1046 => :archivesize,
|
85
|
+
1047 => :providename,
|
86
|
+
1048 => :requireflags,
|
87
|
+
1049 => :requirename,
|
88
|
+
1050 => :requireversion,
|
89
|
+
1051 => :nosource,
|
90
|
+
1052 => :nopatch,
|
91
|
+
1053 => :conflictflags,
|
92
|
+
1054 => :conflictname,
|
93
|
+
1055 => :conflictversion,
|
94
|
+
1056 => :defaultprefix,
|
95
|
+
1057 => :buildroot,
|
96
|
+
1058 => :installprefix,
|
97
|
+
1059 => :excludearch,
|
98
|
+
1060 => :excludeos,
|
99
|
+
1061 => :exclusivearch,
|
100
|
+
1062 => :exclusiveos,
|
101
|
+
1063 => :autoreqprov,
|
102
|
+
1064 => :rpmversion,
|
103
|
+
1065 => :triggerscripts,
|
104
|
+
1066 => :triggername,
|
105
|
+
1067 => :triggerversion,
|
106
|
+
1068 => :triggerflags,
|
107
|
+
1069 => :triggerindex,
|
108
|
+
1079 => :verifyscript,
|
109
|
+
1080 => :changelogtime,
|
110
|
+
1081 => :changelogname,
|
111
|
+
1082 => :changelogtext,
|
112
|
+
1083 => :brokenmd5,
|
113
|
+
1084 => :prereq,
|
114
|
+
1085 => :preinprog,
|
115
|
+
1086 => :postinprog,
|
116
|
+
1087 => :preunprog,
|
117
|
+
1088 => :postunprog,
|
118
|
+
1089 => :buildarchs,
|
119
|
+
1090 => :obsoletename,
|
120
|
+
1091 => :verifyscriptprog,
|
121
|
+
1092 => :triggerscriptprog,
|
122
|
+
1093 => :docdir,
|
123
|
+
1094 => :cookie,
|
124
|
+
1095 => :filedevices,
|
125
|
+
1096 => :fileinodes,
|
126
|
+
1097 => :filelangs,
|
127
|
+
1098 => :prefixes,
|
128
|
+
1099 => :instprefixes,
|
129
|
+
1100 => :triggerin,
|
130
|
+
1101 => :triggerun,
|
131
|
+
1102 => :triggerpostun,
|
132
|
+
1103 => :autoreq,
|
133
|
+
1104 => :autoprov,
|
134
|
+
1105 => :capability,
|
135
|
+
1106 => :sourcepackage,
|
136
|
+
1107 => :oldorigfilenames,
|
137
|
+
1108 => :buildprereq,
|
138
|
+
1109 => :buildrequires,
|
139
|
+
1110 => :buildconflicts,
|
140
|
+
1111 => :buildmacros,
|
141
|
+
1112 => :provideflags,
|
142
|
+
1113 => :provideversion,
|
143
|
+
1114 => :obsoleteflags,
|
144
|
+
1115 => :obsoleteversion,
|
145
|
+
1116 => :dirindexes,
|
146
|
+
1117 => :basenames,
|
147
|
+
1118 => :dirnames,
|
148
|
+
1119 => :origdirindexes,
|
149
|
+
1120 => :origbasenames,
|
150
|
+
1121 => :origdirnames,
|
151
|
+
1122 => :optflags,
|
152
|
+
1123 => :disturl,
|
153
|
+
1124 => :payloadformat,
|
154
|
+
1125 => :payloadcompressor,
|
155
|
+
1126 => :payloadflags,
|
156
|
+
1127 => :installcolor,
|
157
|
+
1128 => :installtid,
|
158
|
+
1129 => :removetid,
|
159
|
+
1130 => :sha1rhn,
|
160
|
+
1131 => :rhnplatform,
|
161
|
+
1132 => :platform,
|
162
|
+
1133 => :patchesname,
|
163
|
+
1134 => :patchesflags,
|
164
|
+
1135 => :patchesversion,
|
165
|
+
1136 => :cachectime,
|
166
|
+
1137 => :cachepkgpath,
|
167
|
+
1138 => :cachepkgsize,
|
168
|
+
1139 => :cachepkgmtime,
|
169
|
+
1140 => :filecolors,
|
170
|
+
1141 => :fileclass,
|
171
|
+
1142 => :classdict,
|
172
|
+
1143 => :filedependsx,
|
173
|
+
1144 => :filedependsn,
|
174
|
+
1145 => :dependsdict,
|
175
|
+
1146 => :sourcepkgid,
|
176
|
+
1147 => :filecontexts,
|
177
|
+
1148 => :fscontexts,
|
178
|
+
1149 => :recontexts,
|
179
|
+
1150 => :policies,
|
180
|
+
1151 => :pretrans,
|
181
|
+
1152 => :posttrans,
|
182
|
+
1153 => :pretransprog,
|
183
|
+
1154 => :posttransprog,
|
184
|
+
1155 => :disttag,
|
185
|
+
1156 => :suggestsname,
|
186
|
+
1157 => :suggestsversion,
|
187
|
+
1158 => :suggestsflags,
|
188
|
+
1159 => :enhancesname,
|
189
|
+
1160 => :enhancesversion,
|
190
|
+
1161 => :enhancesflags,
|
191
|
+
1162 => :priority,
|
192
|
+
1163 => :cvsid,
|
193
|
+
1164 => :blinkpkgid,
|
194
|
+
1165 => :blinkhdrid,
|
195
|
+
1166 => :blinknevra,
|
196
|
+
1167 => :flinkpkgid,
|
197
|
+
1168 => :flinkhdrid,
|
198
|
+
1169 => :flinknevra,
|
199
|
+
1170 => :packageorigin,
|
200
|
+
1171 => :triggerprein,
|
201
|
+
1172 => :buildsuggests,
|
202
|
+
1173 => :buildenhances,
|
203
|
+
1174 => :scriptstates,
|
204
|
+
1175 => :scriptmetrics,
|
205
|
+
1176 => :buildcpuclock,
|
206
|
+
1177 => :filedigestalgos,
|
207
|
+
1178 => :variants,
|
208
|
+
1179 => :xmajor,
|
209
|
+
1180 => :xminor,
|
210
|
+
1181 => :repotag,
|
211
|
+
1182 => :keywords,
|
212
|
+
1183 => :buildplatforms,
|
213
|
+
1184 => :packagecolor,
|
214
|
+
1185 => :packageprefcolor,
|
215
|
+
1186 => :xattrsdict,
|
216
|
+
1187 => :filexattrsx,
|
217
|
+
1188 => :depattrsdict,
|
218
|
+
1189 => :conflictattrsx,
|
219
|
+
1190 => :obsoleteattrsx,
|
220
|
+
1191 => :provideattrsx,
|
221
|
+
1192 => :requireattrsx,
|
222
|
+
1193 => :buildprovides,
|
223
|
+
1194 => :buildobsoletes,
|
224
|
+
1195 => :dbinstance,
|
225
|
+
1196 => :nvra,
|
226
|
+
5000 => :filenames,
|
227
|
+
5001 => :fileprovide,
|
228
|
+
5002 => :filerequire,
|
229
|
+
5003 => :fsnames,
|
230
|
+
5004 => :fssizes,
|
231
|
+
5005 => :triggerconds,
|
232
|
+
5006 => :triggertype,
|
233
|
+
5007 => :origfilenames,
|
234
|
+
5008 => :longfilesizes,
|
235
|
+
5009 => :longsize,
|
236
|
+
5010 => :filecaps,
|
237
|
+
5011 => :filedigestalgo,
|
238
|
+
5012 => :bugurl,
|
239
|
+
5013 => :evr,
|
240
|
+
5014 => :nvr,
|
241
|
+
5015 => :nevr,
|
242
|
+
5016 => :nevra,
|
243
|
+
5017 => :headercolor,
|
244
|
+
5018 => :verbose,
|
245
|
+
5019 => :epochnum,
|
246
|
+
}
|
247
|
+
|
248
|
+
# See 'rpmTagType' enum in rpmtag.h
|
249
|
+
TYPE = {
|
250
|
+
0 => :null,
|
251
|
+
1 => :char,
|
252
|
+
2 => :int8,
|
253
|
+
3 => :int16,
|
254
|
+
4 => :int32,
|
255
|
+
5 => :int64,
|
256
|
+
6 => :string,
|
257
|
+
7 => :binary,
|
258
|
+
8 => :string_array,
|
259
|
+
9 => :i18nstring,
|
260
|
+
}
|
261
|
+
|
262
|
+
def initialize(tag_id, type, offset, count, data)
|
263
|
+
@tag = tag_id
|
264
|
+
@type = type
|
265
|
+
@offset = offset
|
266
|
+
@count = count
|
267
|
+
|
268
|
+
@data = data
|
269
|
+
|
270
|
+
@inspectables = [:@tag, :@type, :@offset, :@count, :@value]
|
271
|
+
end # def initialize
|
272
|
+
|
273
|
+
def tag
|
274
|
+
TAG.fetch(@tag, @tag)
|
275
|
+
end # def tag
|
276
|
+
|
277
|
+
def tag_as_int
|
278
|
+
@tag
|
279
|
+
end
|
280
|
+
|
281
|
+
def type
|
282
|
+
TYPE.fetch(@type, @type)
|
283
|
+
end # def type
|
284
|
+
|
285
|
+
def value
|
286
|
+
if !@value
|
287
|
+
case type
|
288
|
+
when :string
|
289
|
+
# string at offset up to first null
|
290
|
+
@value = @data[@offset .. -1][/^[^\0]+/]
|
291
|
+
when :i18nstring
|
292
|
+
# string at offset up to first null
|
293
|
+
@value = @data[@offset .. -1][/^[^\0]+/]
|
294
|
+
when :string_array
|
295
|
+
@value = @data[@offset .. -1].split("\0")[0 ... @count]
|
296
|
+
when :binary
|
297
|
+
@value = @data[@offset, @count]
|
298
|
+
when :int32
|
299
|
+
@value = @data[@offset, 4 * count].unpack("N" * count)
|
300
|
+
when :int16
|
301
|
+
@value = @data[@offset, 2 * count].unpack("n" * count)
|
302
|
+
end # case type
|
303
|
+
end # if !@value
|
304
|
+
|
305
|
+
return @value
|
306
|
+
end # def value
|
307
|
+
end # class RPM::File::Tag
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "namespace")
|
2
|
+
|
3
|
+
class RPM::Requires
|
4
|
+
private
|
5
|
+
def initialize(name)
|
6
|
+
@name = name
|
7
|
+
@version = "0"
|
8
|
+
@operator = ">="
|
9
|
+
end # def initialize
|
10
|
+
|
11
|
+
def condition(operator, version)
|
12
|
+
@operator = operator
|
13
|
+
@version = version
|
14
|
+
end # def condition
|
15
|
+
|
16
|
+
def <=(version); condition(:<=, version) end
|
17
|
+
def >=(version); condition(:>=, version) end
|
18
|
+
def <(version); condition(:<, version) end
|
19
|
+
def >(version); condition(:>, version) end
|
20
|
+
def ==(version); condition(:==, version) end
|
21
|
+
|
22
|
+
public(:initialize, :<=, :>=, :<, :>, :==)
|
23
|
+
end
|
data/notify-failure.sh
ADDED
data/printrpm.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$: << "lib"
|
2
|
+
|
3
|
+
require "rpm/file"
|
4
|
+
|
5
|
+
rpm = RPM::File.new(ARGV[0])
|
6
|
+
|
7
|
+
#p rpm.lead
|
8
|
+
rpm.signature.tags.each do |tag|
|
9
|
+
#p :tag => [tag.tag, tag.type, tag.count, tag.value]
|
10
|
+
end
|
11
|
+
|
12
|
+
rpm.header.tags.each do |tag|
|
13
|
+
#next unless tag.tag.to_s =~ /(payload|sig)/
|
14
|
+
# p :tag => [tag.tag, tag.type, tag.count, tag.value]
|
15
|
+
end
|
16
|
+
|
17
|
+
require "awesome_print"
|
18
|
+
ap rpm.requires
|
19
|
+
|
20
|
+
#payload = rpm.payload
|
21
|
+
#fd = File.new("/tmp/rpm.payload", "w")
|
22
|
+
#fd.write(rpm.payload.read)
|
data/rpm.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
files = %x{git ls-files}.split("\n")
|
3
|
+
|
4
|
+
spec.name = "arr-pm"
|
5
|
+
spec.version = "0.0.2"
|
6
|
+
spec.summary = "RPM reader and writer library"
|
7
|
+
spec.description = "This library allows to you to read and write rpm " \
|
8
|
+
"packages. Written in pure ruby because librpm is not available " \
|
9
|
+
"on all systems"
|
10
|
+
spec.license = "Apache 2"
|
11
|
+
|
12
|
+
spec.add_dependency "cabin", ">0" # for logging. apache 2 license
|
13
|
+
spec.files = files
|
14
|
+
spec.require_paths << "lib"
|
15
|
+
spec.bindir = "bin"
|
16
|
+
|
17
|
+
spec.authors = ["Jordan Sissel"]
|
18
|
+
spec.email = ["jls@semicomplete.com"]
|
19
|
+
#spec.homepage = "..."
|
20
|
+
end
|
21
|
+
|
data/test/all.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "minitest/spec"
|
3
|
+
require "minitest/autorun"
|
4
|
+
|
5
|
+
# Get coverage report
|
6
|
+
require "simplecov"
|
7
|
+
SimpleCov.start
|
8
|
+
|
9
|
+
# Add '../lib' to the require path.
|
10
|
+
$: << File.join(File.dirname(__FILE__), "..", "lib")
|
11
|
+
|
12
|
+
def use(path)
|
13
|
+
puts "Loading tests from #{path}"
|
14
|
+
require File.expand_path(path)
|
15
|
+
end
|
16
|
+
|
17
|
+
dirname = File.dirname(__FILE__)
|
18
|
+
use File.join(dirname, "docs.rb")
|
19
|
+
|
20
|
+
# Load tests from ./*/**/*.rb (usually ./libraryname/....)
|
21
|
+
glob = File.join(dirname, "*", "**", "*.rb")
|
22
|
+
Dir.glob(glob).each do |path|
|
23
|
+
use path
|
24
|
+
end
|
data/test/docs.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "yard"
|
3
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "testing")
|
4
|
+
|
5
|
+
describe "documentation tests" do
|
6
|
+
before do
|
7
|
+
# Use YARD to parse all ruby files found in '../lib'
|
8
|
+
libdir = File.join(File.dirname(__FILE__), "..", "lib")
|
9
|
+
YARD::Registry.load(Dir.glob(File.join(libdir, "**", "*.rb")))
|
10
|
+
@registry = YARD::Registry.all
|
11
|
+
end
|
12
|
+
|
13
|
+
test "All classes, methods, modules, and constants must be documented" do
|
14
|
+
# Note, the 'find the undocumented things' code here is
|
15
|
+
# copied mostly from: YARD 0.7.5's lib/yard/cli/stats.rb
|
16
|
+
#
|
17
|
+
# Find all undocumented classes, modules, and constants
|
18
|
+
undocumented = @registry.select do |o|
|
19
|
+
[:class, :module, :constant].include?(o.type) && o.docstring.blank?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Find all undocumented methods
|
23
|
+
methods = @registry.select { |m| m.type == :method }
|
24
|
+
methods.reject! { |m| m.is_alias? || !m.is_explicit? }
|
25
|
+
undocumented += methods.select do |m|
|
26
|
+
m.docstring.blank? && !m.overridden_method
|
27
|
+
end
|
28
|
+
|
29
|
+
if (undocumented.length > 0)
|
30
|
+
message = ["The following are not documented"]
|
31
|
+
undocumented.each do |o|
|
32
|
+
message << "* #{o.type.to_s} #{o.to_s} <#{o.file}:#{o.line}>"
|
33
|
+
end
|
34
|
+
|
35
|
+
flunk(message.join("\n"))
|
36
|
+
else
|
37
|
+
pass
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/test/testing.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "minitest/spec"
|
3
|
+
|
4
|
+
# Add '../lib' to the require path.
|
5
|
+
$: << File.join(File.dirname(__FILE__), "..", "lib")
|
6
|
+
|
7
|
+
# I don't really like monkeypatching, but whatever, this is probably better
|
8
|
+
# than overriding the 'describe' method.
|
9
|
+
class MiniTest::Spec
|
10
|
+
class << self
|
11
|
+
# 'it' sounds wrong, call it 'test'
|
12
|
+
alias :test :it
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: arr-pm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jordan Sissel
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: cabin
|
16
|
+
requirement: &18435880 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>'
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *18435880
|
25
|
+
description: This library allows to you to read and write rpm packages. Written in
|
26
|
+
pure ruby because librpm is not available on all systems
|
27
|
+
email:
|
28
|
+
- jls@semicomplete.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- .batcave/manifest
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- LICENSE
|
37
|
+
- Makefile
|
38
|
+
- README.md
|
39
|
+
- header-verify.rb
|
40
|
+
- lib/arr-pm.rb
|
41
|
+
- lib/arr-pm/conflicts.rb
|
42
|
+
- lib/arr-pm/file.rb
|
43
|
+
- lib/arr-pm/file/header.rb
|
44
|
+
- lib/arr-pm/file/lead.rb
|
45
|
+
- lib/arr-pm/file/tag.rb
|
46
|
+
- lib/arr-pm/namespace.rb
|
47
|
+
- lib/arr-pm/requires.rb
|
48
|
+
- notify-failure.sh
|
49
|
+
- printrpm.rb
|
50
|
+
- rpm.gemspec
|
51
|
+
- test/all.rb
|
52
|
+
- test/docs.rb
|
53
|
+
- test/testing.rb
|
54
|
+
homepage:
|
55
|
+
licenses:
|
56
|
+
- Apache 2
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.8.16
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: RPM reader and writer library
|
80
|
+
test_files: []
|
81
|
+
has_rdoc:
|