arr-pm 0.0.6 → 0.0.11

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.

checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ceb08ab00b24c659ee4f0fd42d8f5c8ad77416ffff3f89e8ee18ec46ddc58383
4
+ data.tar.gz: b53d4d64c24362fb2cf27606842d8352e9f7d184f9ba3b76eb1e33b3e13b2775
5
+ SHA512:
6
+ metadata.gz: 441b7c7a0d0851c3c4cadaf7e2e0f20d32388ba4df722ee3f76bfb3a34bb072739daf63739cf19e9d8f221a40a80f999ca47b2825043ded493d1f3363dec3e54
7
+ data.tar.gz: 815d8fff66bc8672b7e212f6c138795a9029b5e78032d26051ac114e306084f97d01519060427aeb50e9bca29dd49af5d5567370904d8881fc99179184811cd3
data/.rubocop.yml ADDED
@@ -0,0 +1,73 @@
1
+ # Let's not argue over this...
2
+ StringLiterals:
3
+ Enabled: false
4
+
5
+ # I can't find a reason for raise vs fail.
6
+ SignalException:
7
+ Enabled: false
8
+
9
+ # I can't find a reason to prefer 'map' when 'collect' is what I mean.
10
+ # I'm collecting things from a list. Maybe someone can help me understand the
11
+ # semantics here.
12
+ CollectionMethods:
13
+ Enabled: false
14
+
15
+ # Why do you even *SEE* trailing whitespace? Because your editor was
16
+ # misconfigured to highlight trailing whitespace, right? Maybe turn that off?
17
+ # ;)
18
+ TrailingWhitespace:
19
+ Enabled: false
20
+
21
+ # Line length is another weird problem that somehow in the past 40 years of
22
+ # computing we don't seem to have solved. It's a display problem :(
23
+ LineLength:
24
+ Max: 9000
25
+
26
+ # %w() vs [ "x", "y", ... ]
27
+ # The complaint is on lib/pleaserun/detector.rb's map of OS=>Runner,
28
+ # i'll ignore it.
29
+ WordArray:
30
+ MinSize: 5
31
+
32
+ # A 20-line method isn't too bad.
33
+ MethodLength:
34
+ Max: 20
35
+
36
+ # Hash rockets (=>) forever. Why? Not all of my hash keys are static symbols.
37
+ HashSyntax:
38
+ EnforcedStyle: hash_rockets
39
+
40
+ # I prefer explicit return. It makes it clear in the code that the
41
+ # code author intended to return a value from a method.
42
+ RedundantReturn:
43
+ Enabled: false
44
+
45
+ # My view on a readable case statement seems to disagree with
46
+ # what rubocop wants and it doesn't let me configure it other than
47
+ # enable/disable.
48
+ CaseIndentation:
49
+ Enabled: false
50
+
51
+ # module This::Module::Definition is good.
52
+ Style/ClassAndModuleChildren:
53
+ Enabled: true
54
+ EnforcedStyle: compact
55
+
56
+ # "in interpolation #{use.some("double quotes is ok")}"
57
+ Style/StringLiteralsInInterpolation:
58
+ Enabled: true
59
+ EnforcedStyle: double_quotes
60
+
61
+ # Long-block `if !something ... end` are more readable to me than `unless something ... end`
62
+ Style/NegatedIf:
63
+ Enabled: false
64
+
65
+ Style/NumericLiterals:
66
+ MinDigits: 6
67
+
68
+ # This kind of style "useless use of %x" assumes code is write-once.
69
+ Style/UnneededPercentX:
70
+ Enabled: false
71
+
72
+ Style/FileName:
73
+ Enabled: false
data/Gemfile CHANGED
@@ -1 +1,3 @@
1
- source :rubygems
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,77 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features)
6
+
7
+ ## Uncomment to clear the screen before every task
8
+ # clearing :on
9
+
10
+ ## Guard internally checks for changes in the Guardfile and exits.
11
+ ## If you want Guard to automatically start up again, run guard in a
12
+ ## shell loop, e.g.:
13
+ ##
14
+ ## $ while bundle exec guard; do echo "Restarting Guard..."; done
15
+ ##
16
+ ## Note: if you are using the `directories` clause above and you are not
17
+ ## watching the project directory ('.'), then you will want to move
18
+ ## the Guardfile to a watched dir and symlink it back, e.g.
19
+ #
20
+ # $ mkdir config
21
+ # $ mv Guardfile config/
22
+ # $ ln -s config/Guardfile .
23
+ #
24
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
25
+
26
+ # Note: The cmd option is now required due to the increasing number of ways
27
+ # rspec may be run, below are examples of the most common uses.
28
+ # * bundler: 'bundle exec rspec'
29
+ # * bundler binstubs: 'bin/rspec'
30
+ # * spring: 'bin/rspec' (This will use spring if running and you have
31
+ # installed the spring binstubs per the docs)
32
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
33
+ # * 'just' rspec: 'rspec'
34
+
35
+ guard :rspec, cmd: "bundle exec rspec" do
36
+ require "guard/rspec/dsl"
37
+ dsl = Guard::RSpec::Dsl.new(self)
38
+
39
+ # Feel free to open issues for suggestions and improvements
40
+
41
+ # RSpec files
42
+ rspec = dsl.rspec
43
+ watch(rspec.spec_helper) { rspec.spec_dir }
44
+ watch(rspec.spec_support) { rspec.spec_dir }
45
+ watch(rspec.spec_files)
46
+
47
+ # Ruby files
48
+ ruby = dsl.ruby
49
+ dsl.watch_spec_files_for(ruby.lib_files)
50
+
51
+ # Rails files
52
+ rails = dsl.rails(view_extensions: %w(erb haml slim))
53
+ dsl.watch_spec_files_for(rails.app_files)
54
+ dsl.watch_spec_files_for(rails.views)
55
+
56
+ watch(rails.controllers) do |m|
57
+ [
58
+ rspec.spec.("routing/#{m[1]}_routing"),
59
+ rspec.spec.("controllers/#{m[1]}_controller"),
60
+ rspec.spec.("acceptance/#{m[1]}")
61
+ ]
62
+ end
63
+
64
+ # Rails config changes
65
+ watch(rails.spec_helper) { rspec.spec_dir }
66
+ watch(rails.routes) { "#{rspec.spec_dir}/routing" }
67
+ watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
68
+
69
+ # Capybara features specs
70
+ watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
71
+
72
+ # Turnip features and steps
73
+ watch(%r{^spec/acceptance/(.+)\.feature$})
74
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
75
+ Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
76
+ end
77
+ end
data/Makefile CHANGED
@@ -3,25 +3,6 @@ VERSION=$(shell awk -F\" '/spec.version/ { print $$2 }' $(GEMSPEC))
3
3
  NAME=$(shell awk -F\" '/spec.name/ { print $$2 }' $(GEMSPEC))
4
4
  GEM=$(NAME)-$(VERSION).gem
5
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
6
  .PHONY: package
26
7
  package: | $(GEM)
27
8
 
data/arr-pm.gemspec CHANGED
@@ -2,7 +2,7 @@ Gem::Specification.new do |spec|
2
2
  files = %x{git ls-files}.split("\n")
3
3
 
4
4
  spec.name = "arr-pm"
5
- spec.version = "0.0.6"
5
+ spec.version = "0.0.11"
6
6
  spec.summary = "RPM reader and writer library"
7
7
  spec.description = "This library allows to you to read and write rpm " \
8
8
  "packages. Written in pure ruby because librpm is not available " \
@@ -16,6 +16,8 @@ Gem::Specification.new do |spec|
16
16
 
17
17
  spec.authors = ["Jordan Sissel"]
18
18
  spec.email = ["jls@semicomplete.com"]
19
+
20
+ spec.add_development_dependency "flores", ">0"
19
21
  #spec.homepage = "..."
20
22
  end
21
23
 
data/cpio.rb ADDED
@@ -0,0 +1,202 @@
1
+ class BoundedIO
2
+ attr_reader :length
3
+ attr_reader :remaining
4
+
5
+ def initialize(io, length, &eof_callback)
6
+ @io = io
7
+ @length = length
8
+ @remaining = length
9
+
10
+ @eof_callback = eof_callback
11
+ @eof = false
12
+ end
13
+
14
+ def read(size=nil)
15
+ return nil if eof?
16
+ size = @remaining if size.nil?
17
+ data = @io.read(size)
18
+ @remaining -= data.bytesize
19
+ eof?
20
+ data
21
+ end
22
+
23
+ def sysread(size)
24
+ raise EOFError, "end of file reached" if eof?
25
+ read(size)
26
+ end
27
+
28
+ def eof?
29
+ return false if @remaining > 0
30
+ return @eof if @eof
31
+
32
+ @eof_callback.call
33
+ @eof = true
34
+ end
35
+ end
36
+
37
+ module CPIO
38
+ FIELDS = [
39
+ :magic, :ino, :mode, :uid, :gid, :nlink, :mtime, :filesize, :devmajor,
40
+ :devminor, :rdevmajor, :rdevminor, :namesize, :check
41
+ ]
42
+ end
43
+
44
+ class CPIO::ASCIIReader
45
+ FIELD_SIZES = {
46
+ :magic => 6,
47
+ :ino => 8,
48
+ :mode => 8,
49
+ :uid => 8,
50
+ :gid => 8,
51
+ :nlink => 8,
52
+ :mtime => 8,
53
+ :filesize => 8,
54
+ :devmajor => 8,
55
+ :devminor => 8,
56
+ :rdevmajor => 8,
57
+ :rdevminor => 8,
58
+ :namesize => 8,
59
+ :check => 8
60
+ }
61
+ HEADER_LENGTH = FIELD_SIZES.reduce(0) { |m, (_, v)| m + v }
62
+ HEADER_PACK = FIELD_SIZES.collect { |_, v| "A#{v}" }.join
63
+
64
+ FIELD_ORDER = [
65
+ :magic, :ino, :mode, :uid, :gid, :nlink, :mtime, :filesize, :devmajor,
66
+ :devminor, :rdevmajor, :rdevminor, :namesize, :check
67
+ ]
68
+
69
+ def initialize(io)
70
+ @io = io
71
+ end
72
+
73
+ private
74
+
75
+ def io
76
+ @io
77
+ end
78
+
79
+ def each(&block)
80
+ while true
81
+ entry = read
82
+ break if entry.nil?
83
+ # The CPIO format has the end-of-stream marker as a file called "TRAILER!!!"
84
+ break if entry.name == "TRAILER!!!"
85
+ block.call(entry, entry.file)
86
+ verify_correct_read(entry) unless entry.directory?
87
+ end
88
+ end
89
+
90
+ def verify_correct_read(entry)
91
+ # Read and throw away the whole file if not read at all.
92
+ entry.file.tap do |file|
93
+ if file.nil? || file.remaining == 0
94
+ # All OK! :)
95
+ elsif file.remaining == file.length
96
+ file.read(16384) while !file.eof?
97
+ else
98
+ # The file was only partially read? This should be an error by the
99
+ # user.
100
+ consumed = file.length - file.remaining
101
+ raise BadState, "Only #{consumed} bytes were read of the #{file.length} byte file: #{entry.name}"
102
+ end
103
+ end
104
+ end
105
+
106
+ def read
107
+ entry = CPIOEntry.new
108
+ header = io.read(HEADER_LENGTH)
109
+ return nil if header.nil?
110
+ FIELD_ORDER.zip(header.unpack(HEADER_PACK)).each do |field, value|
111
+ entry.send("#{field}=", value.to_i(16))
112
+ end
113
+
114
+ entry.validate
115
+ entry.mtime = Time.at(entry.mtime)
116
+ read_name(entry, @io)
117
+ read_file(entry, @io)
118
+ entry
119
+ end
120
+
121
+ def read_name(entry, io)
122
+ entry.name = io.read(entry.namesize - 1) # - 1 for null terminator
123
+ nul = io.read(1)
124
+ raise ArgumentError, "Corrupt CPIO or bug? Name null terminator was not null: #{nul.inspect}" if nul != "\0"
125
+ padding_data = io.read(padding_name(entry))
126
+ # Padding should be all null bytes
127
+ if padding_data != ("\0" * padding_data.bytesize)
128
+ raise ArgumentError, "Corrupt CPIO or bug? Name null padding was #{padding_name(entry)} bytes: #{padding_data.inspect}"
129
+ end
130
+ end
131
+
132
+ def read_file(entry, io)
133
+ if entry.directory?
134
+ entry.file = nil
135
+ #read_file_padding(entry, io)
136
+ nil
137
+ else
138
+ entry.file = BoundedIO.new(io, entry.filesize) do
139
+ read_file_padding(entry, io)
140
+ end
141
+ end
142
+ end
143
+
144
+ def read_file_padding(entry, io)
145
+ padding_data = io.read(padding_file(entry))
146
+ if padding_data != ("\0" * padding_data.bytesize)
147
+ raise ArgumentError, "Corrupt CPIO or bug? File null padding was #{padding_file(entry)} bytes: #{padding_data.inspect}"
148
+ end
149
+ end
150
+
151
+ def padding_name(entry)
152
+ # name padding is padding up to a multiple of 4 after header+namesize
153
+ -(HEADER_LENGTH + entry.namesize) % 4
154
+ end
155
+
156
+ def padding_file(entry)
157
+ (-(HEADER_LENGTH + entry.filesize + 2) % 4)
158
+ end
159
+ public(:each)
160
+ end
161
+
162
+ class CPIOEntry
163
+ CPIO::FIELDS.each do |field|
164
+ attr_accessor field
165
+ end
166
+
167
+ attr_accessor :name
168
+ attr_accessor :file
169
+
170
+ DIRECTORY_FLAG = 0040000
171
+
172
+ def validate
173
+ raise "Invalid magic #{magic.inspect}" if magic != 0x070701
174
+ raise "Invalid ino #{ino.inspect}" if ino < 0
175
+ raise "Invalid mode #{mode.inspect}" if mode < 0
176
+ raise "Invalid uid #{uid.inspect}" if uid < 0
177
+ raise "Invalid gid #{gid.inspect}" if gid < 0
178
+ raise "Invalid nlink #{nlink.inspect}" if nlink < 0
179
+ raise "Invalid mtime #{mtime.inspect}" if mtime < 0
180
+ raise "Invalid filesize #{filesize.inspect}" if filesize < 0
181
+ raise "Invalid devmajor #{devmajor.inspect}" if devmajor < 0
182
+ raise "Invalid devminor #{devminor.inspect}" if devminor < 0
183
+ raise "Invalid rdevmajor #{rdevmajor.inspect}" if rdevmajor < 0
184
+ raise "Invalid rdevminor #{rdevminor.inspect}" if rdevminor < 0
185
+ raise "Invalid namesize #{namesize.inspect}" if namesize < 0
186
+ raise "Invalid check #{check.inspect}" if check < 0
187
+ end # def validate
188
+
189
+ def read(*args)
190
+ return nil if directory?
191
+ file.read(*args)
192
+ end
193
+
194
+ def directory?
195
+ mode & DIRECTORY_FLAG > 0
196
+ end
197
+ end
198
+
199
+ CPIO::ASCIIReader.new(STDIN).each do |entry, file|
200
+ puts entry.name
201
+ file.read unless entry.directory?
202
+ end
data/lib/arr-pm/file.rb CHANGED
@@ -2,6 +2,7 @@ require File.join(File.dirname(__FILE__), "namespace")
2
2
  require File.join(File.dirname(__FILE__), "file", "header")
3
3
  require File.join(File.dirname(__FILE__), "file", "lead")
4
4
  require File.join(File.dirname(__FILE__), "file", "tag")
5
+ require "fcntl"
5
6
 
6
7
  # Much of the code here is derived from knowledge gained by reading the rpm
7
8
  # source code, but mostly it started making more sense after reading this site:
@@ -99,7 +100,11 @@ class RPM::File
99
100
 
100
101
  extractor = IO.popen("#{tags[:payloadcompressor]} -d | (cd #{target}; cpio -i --quiet --make-directories)", "w")
101
102
  buffer = ""
102
- buffer.force_encoding("BINARY")
103
+ begin
104
+ buffer.force_encoding("BINARY")
105
+ rescue NoMethodError
106
+ # Do Nothing
107
+ end
103
108
  payload_fd = payload.clone
104
109
  loop do
105
110
  data = payload_fd.read(16384, buffer)
@@ -158,24 +163,28 @@ class RPM::File
158
163
  #
159
164
  # @return Array of [ [name, operator, version], ... ]
160
165
  def conflicts
161
- return relation(:conflicts)
166
+ return relation(:conflict)
162
167
  end # def conflicts
163
168
 
164
169
  # Get an array of provides defined in this package.
165
170
  #
166
171
  # @return Array of [ [name, operator, version], ... ]
167
172
  def provides
168
- return relation(:provides)
173
+ return relation(:provide)
169
174
  end # def provides
170
175
 
171
176
  # Get an array of config files
172
177
  def config_files
173
178
  # this stuff seems to be in the 'enum rpmfileAttrs_e' from rpm/rpmfi.h
174
179
  results = []
175
- tags[:fileflags].each_with_index do |flag, i|
176
- # The :fileflags (and other :file... tags) are an array, in order of
177
- # files in the rpm payload, we want a list of paths of config files.
178
- results << files[i] if mask?(flag, FLAG_CONFIG_FILE)
180
+ # short-circuit if there's no :fileflags tag
181
+ return results unless tags.include?(:fileflags)
182
+ if !tags[:fileflags].nil?
183
+ tags[:fileflags].each_with_index do |flag, i|
184
+ # The :fileflags (and other :file... tags) are an array, in order of
185
+ # files in the rpm payload, we want a list of paths of config files.
186
+ results << files[i] if mask?(flag, FLAG_CONFIG_FILE)
187
+ end
179
188
  end
180
189
  return results
181
190
  end # def config_files
@@ -190,7 +199,11 @@ class RPM::File
190
199
 
191
200
  lister = IO.popen("#{tags[:payloadcompressor]} -d | cpio -it --quiet", "r+")
192
201
  buffer = ""
193
- buffer.force_encoding("BINARY")
202
+ begin
203
+ buffer.force_encoding("BINARY")
204
+ rescue NoMethodError
205
+ # Do Nothing
206
+ end
194
207
  payload_fd = payload.clone
195
208
  output = ""
196
209
  loop do
@@ -202,12 +215,23 @@ class RPM::File
202
215
  begin
203
216
  output << lister.read_nonblock(16384)
204
217
  rescue Errno::EAGAIN
205
- # do nothing
218
+ # Nothing to read, move on!
206
219
  end
207
220
  end
208
221
  lister.close_write
222
+
209
223
  # Read remaining output
210
- output << lister.read
224
+ begin
225
+ output << lister.read
226
+ rescue Errno::EAGAIN
227
+ # Because read_nonblock enables NONBLOCK the 'lister' fd,
228
+ # and we may have invoked a read *before* cpio has started
229
+ # writing, let's keep retrying this read until we get an EOF
230
+ retry
231
+ rescue EOFError
232
+ # At EOF, hurray! We're done reading.
233
+ end
234
+
211
235
  # Split output by newline and strip leading "."
212
236
  @files = output.split("\n").collect { |s| s.gsub(/^\./, "") }
213
237
  return @files