arr-pm 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
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:
|