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.

@@ -0,0 +1,5 @@
1
+ ---
2
+ things:
3
+ ruby:
4
+ args:
5
+ - rpm
@@ -0,0 +1,13 @@
1
+ # vim
2
+ .*.sw[a-z]
3
+
4
+ # build byproducts
5
+ build-*/*
6
+ fpm.wiki
7
+ *.gem
8
+
9
+ # python
10
+ *.pyc
11
+
12
+ # RVM
13
+ .rvmrc
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
+
@@ -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)
@@ -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
@@ -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)
@@ -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
@@ -0,0 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), "namespace")
2
+ require File.join(File.dirname(__FILE__), "requires")
3
+
4
+ class RPM::Conflicts < RPM::Requires
5
+ end
@@ -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,3 @@
1
+ class RPM
2
+ class File; end
3
+ end
@@ -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
@@ -0,0 +1,15 @@
1
+ #!/bin/sh
2
+
3
+ "$@"
4
+ status=$?
5
+
6
+ if [ ! -z "$TMUX" ] ; then
7
+ if [ "$status" -ne 0 ] ; then
8
+ tmux display-message "Tests Fail"
9
+ else
10
+ tmux display-message "Tests OK"
11
+ fi
12
+ fi
13
+
14
+ exit $status
15
+
@@ -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)
@@ -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
+
@@ -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
@@ -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
@@ -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: