zypper 0.2.2 → 0.3.0

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ 0.3.0
2
+ -----
3
+ * Extended testsuites
4
+ * Project moved to openSUSE/rubygem-zypper at GitHub
5
+ * Using Nori (with Nokogiri) to parse the XML output
6
+ * Speedup improvements
7
+ * New Package::updates method
8
+
1
9
  0.2.2
2
10
  -----
3
11
  * Fixed last_exit_status method and thus all methods returning boolean
@@ -121,8 +121,8 @@ zypper.repositories.all
121
121
 
122
122
  # returns
123
123
  [
124
- { "enabled"=>"1", "autorefresh"=>"1", "name"=>"SLES11-SP1-x68_64", "url"=>["http://repo/URI"],
125
- "type"=>"rpm-md", "alias"=>"repository_alias", "gpgcheck"=>"1" },
124
+ { "enabled"=>true, "autorefresh"=>true, "name"=>"SLES11-SP1-x68_64", "url"=>["http://repo/URI"],
125
+ "type"=>"rpm-md", "alias"=>"repository_alias", "gpgcheck"=>true },
126
126
  { ... },
127
127
  ...
128
128
  ]
@@ -329,6 +329,26 @@ zypper.package.installed?(:package => 'package')
329
329
  true or false
330
330
  ```
331
331
 
332
+ #### Package Updates ####
333
+
334
+ Returns list of packages that could be updated (with higher version available).
335
+
336
+ ```ruby
337
+ zypper.packages.updates
338
+
339
+ # returns e.g.
340
+ [
341
+ ...
342
+ {:source=>{:url=>"http://download.opensuse.org/update/12.1/", :alias=>"openSUSE_12.1_Updates"},
343
+ :summary=>"Utilities to query and test DNS ", :license=>nil, :description=>"This package
344
+ includes the utilities host, dig, and nslookup used to\ntest and query the Domain Name System
345
+ (DNS). The Berkeley Internet\nName Domain (BIND) DNS server is found in the package named bind.",
346
+ :edition=>"9.8.3P1-4.14.1", :name=>"bind-utils", :kind=>"package", :arch=>"x86_64"
347
+ },
348
+ ...
349
+ ]
350
+ ```
351
+
332
352
  ### Patches ###
333
353
 
334
354
  You can access the patches class either with
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.2
1
+ 0.3.0
@@ -1,8 +1,7 @@
1
- require 'zypper/utils'
1
+ require 'zypper/update'
2
2
 
3
3
  class Zypper
4
- class Package
5
- include ZypperUtils
4
+ class Package < Update
6
5
 
7
6
  class Status
8
7
  INSTALLED = :installed
@@ -63,6 +62,11 @@ class Zypper
63
62
  find(options.merge(:status => Status::AVAILABLE))
64
63
  end
65
64
 
65
+ # Finds all package updates
66
+ def updates(options = {})
67
+ find_updates(options.merge(:type => :package))
68
+ end
69
+
66
70
  private
67
71
 
68
72
  def status(status)
@@ -71,30 +75,29 @@ class Zypper
71
75
  raise "Unknown package status '#{status}'"
72
76
  end
73
77
 
74
- # SLE11 zypper doesn't support XML output for packages
75
- # FIXME: merge with 'convert_patches'
78
+ # SLE11 zypper doesn't support XML output for packages yet
76
79
  def convert_packages(packages)
77
80
  out = []
78
- table_index = 0
81
+ table_header_lines = 3
79
82
  package = {}
80
83
 
81
- packages.split("\n").each {|line|
82
- table_index = table_index + 1
83
- # Skip the first two - table header
84
- next if table_index < 3
84
+ for line in packages.split("\n")
85
+ # Skip the table header
86
+ if table_header_lines > 0
87
+ table_header_lines = table_header_lines - 1
88
+ next
89
+ end
85
90
 
86
- line.gsub!(/ +\| +/, '|')
87
- line.gsub!(/^ +/, '')
88
- line.gsub!(/ +$/, '')
89
- package = line.split '|'
91
+ line.strip!
92
+ package = line.split(%r{ *\| *})
90
93
 
91
- out.push(
94
+ out << {
92
95
  :status => status(package[0]),
93
96
  :name => package[1],
94
97
  :summary => package[2],
95
98
  :type => package[3]
96
- )
97
- }
99
+ }
100
+ end
98
101
 
99
102
  out
100
103
  end
@@ -1,11 +1,12 @@
1
- require 'zypper/utils'
1
+ require 'zypper/update'
2
2
 
3
3
  class Zypper
4
- class Patch
4
+ class Patch < Update
5
+
5
6
  class Status
6
- NEEDED = 'Needed'
7
- INSTALLED = 'Installed'
8
- NOT_APPLICABLE = 'Not Applicable'
7
+ NEEDED = 'needed'
8
+ INSTALLED = 'applied'
9
+ NOT_APPLICABLE = 'not-needed'
9
10
  end
10
11
 
11
12
  class Category
@@ -15,9 +16,8 @@ class Zypper
15
16
  OPTIONAL = 'optional'
16
17
  end
17
18
 
18
- include ZypperUtils
19
-
20
- FILTER_OPTIONS = [:repository, :name, :version, :category, :status]
19
+ FILTER_OPTIONS = [:name, :edition, :arch, :category, :status, :pkgmanager,
20
+ :restart, :interactive, :repository_url, :repository_alias]
21
21
 
22
22
  # Lists all patches
23
23
  #
@@ -27,18 +27,14 @@ class Zypper
27
27
  # Logical AND is always applied for all the options present
28
28
  #
29
29
  # @example
30
- # all(:status => 'Installed')
30
+ # find(:status => Zypper::Patch::Status::INSTALLED)
31
31
  def find(options = {})
32
- additional_options = {:quiet => true}
33
-
34
- if (run(build_command('patches', options.merge(additional_options))))
35
- apply_filters(convert_patches(last_message), options)
36
- end
32
+ apply_filters(find_updates(options.merge(:type => :patch)), options)
37
33
  end
38
34
 
39
35
  # Lists all known patches
40
36
  def all(options = {})
41
- find
37
+ find(options)
42
38
  end
43
39
 
44
40
  # All applicable patches
@@ -64,47 +60,27 @@ class Zypper
64
60
 
65
61
  private
66
62
 
67
- # Current libzypp doesn't support XML output for patches
68
- def convert_patches(patches)
69
- out = []
70
- table_index = 0
71
- patch = {}
72
-
73
- patches.split("\n").each {|line|
74
- table_index = table_index + 1
75
- # Skip the first two - table header
76
- next if table_index < 3
77
-
78
- line.gsub!(/ +\| +/, '|')
79
- line.gsub!(/^ +/, '')
80
- line.gsub!(/ +$/, '')
81
- patch = line.split '|'
82
-
83
- out.push(
84
- :repository => patch[0],
85
- :name => patch[1],
86
- :version => patch[2],
87
- :category => patch[3],
88
- :status => patch[4]
89
- )
90
- }
91
-
92
- out
93
- end
94
-
95
63
  # Filters patches according to given parameters
96
64
  #
97
65
  # @param (Array) patches
98
- # @param (Hash) filters criteria, possible keys are :repository, :name, :version, :category and :status
66
+ # @param (Hash) filters criteria, possible keys are :name, :edition, :arch, :category, :status,
67
+ # :pkgmanager, :restart, :interactive, :repository_url, :repository_alias
99
68
  #
100
69
  # @example
101
- # apply_patch_filters(patches, { :status => 'Needed' })
102
- # apply_patch_filters(patches, { :version' => '1887', :repository => 'SLES11-SP1-Update' })
70
+ # apply_patch_filters(patches, { :status => 'needed' })
71
+ # apply_patch_filters(patches, { :edition => '1887', ... })
103
72
  def apply_filters(patches = [], filters = {})
104
73
  filters.each {|filter_key, filter_value|
105
74
  raise "Unknown filter parameter '#{filter_key}'" unless FILTER_OPTIONS.include? filter_key
106
75
 
107
- patches = patches.select{|patch| patch[filter_key] == filter_value}
76
+ case filter_key
77
+ when :repository_url
78
+ patches = patches.select{|patch| patch[:source][:url] == filter_value}
79
+ when :repository_alias
80
+ patches = patches.select{|patch| patch[:source][:alias] == filter_value}
81
+ else
82
+ patches = patches.select{|patch| patch[filter_key] == filter_value}
83
+ end
108
84
  }
109
85
  patches
110
86
  end
@@ -15,7 +15,7 @@ class Zypper
15
15
  # Lists all known repositories
16
16
  def all(options = {})
17
17
  out = xml_run build_command('repos', options.merge(:get => XML_COMMANDS_GET))
18
- out.fetch('repo-list', []).fetch(0, {}).fetch('repo', [])
18
+ convert_output(out.fetch(:stream, {}).fetch(:repo_list, {}).fetch(:repo, []), :repo)
19
19
  end
20
20
 
21
21
  # Adds a new repository defined by options
@@ -12,7 +12,7 @@ class Zypper
12
12
  # Lists all known services
13
13
  def all(options = {})
14
14
  out = xml_run build_command('services', options.merge(:get => XML_COMMANDS_GET))
15
- out.fetch('service-list', []).fetch(0, {}).fetch('service', [])
15
+ convert_output(out.fetch(:stream, {}).fetch(:service_list, {}).fetch(:service, []), :service)
16
16
  end
17
17
 
18
18
  end
@@ -0,0 +1,25 @@
1
+ require 'zypper/utils'
2
+
3
+ class Zypper
4
+ class Update
5
+ include ZypperUtils
6
+
7
+ KNOWN_TYPES = [:patch, :package, :pattern, :product]
8
+
9
+ DEFALUT_TYPE = :patch
10
+
11
+ # Lists all known updates
12
+ def find_updates(options = {})
13
+ options[:type] = DEFALUT_TYPE if options[:type].nil?
14
+ # FIXME: check allowed types
15
+
16
+ additional_options = {:quiet => true, :get => XML_COMMANDS_GET, :type => options[:type].to_s}
17
+
18
+ out = xml_run build_command('list-updates', options.merge(additional_options))
19
+
20
+ convert_output(out.fetch(:stream, {}).fetch(:update_status, {}).fetch(:update_list, {}).fetch(:update, []), options[:type])
21
+ # FIXME: implement filters
22
+ end
23
+
24
+ end
25
+ end
@@ -2,12 +2,57 @@ module ZypperUtils
2
2
  require 'rubygems'
3
3
  require 'shellwords'
4
4
  require 'popen4'
5
- require 'xmlsimple'
5
+ require 'nori'
6
6
 
7
7
  require 'zypper/config'
8
8
 
9
9
  XML_COMMANDS_GET = 'xml'
10
10
 
11
+ PARAMS_FOR_TYPES = {
12
+ :patch => [
13
+ # ['attribute_key', :type_to_convert_to],
14
+ [:interactive, :boolean],
15
+ [:pkgmanager, :boolean],
16
+ [:restart, :boolean],
17
+ ],
18
+ :repo => [
19
+ [:autorefresh, :boolean],
20
+ [:gpgcheck, :boolean],
21
+ [:enabled, :boolean],
22
+ ],
23
+ :service => [
24
+ [:autorefresh, :boolean],
25
+ [:enabled, :boolean],
26
+ # Treat :repo entry as :repo subitem
27
+ [:repo, :subitem],
28
+ ],
29
+ }
30
+
31
+ Nori.parser = :nokogiri
32
+ Nori.advanced_typecasting = false
33
+
34
+ ATTRIBUTE_STARTS_WITH = '@'[0]
35
+
36
+ tag_to_sym = {}
37
+
38
+ Nori.configure do |config|
39
+ config.convert_tags_to { |tag|
40
+ if (tag_to_sym.has_key?(tag))
41
+ tag_to_sym[tag]
42
+ else
43
+ old_tag = tag.dup
44
+
45
+ if (tag[0] == ATTRIBUTE_STARTS_WITH)
46
+ # cut the '@' from the beginning of the string
47
+ tag.slice!(0)
48
+ end
49
+
50
+ tag_to_sym.store(old_tag, tag.to_sym)
51
+ tag_to_sym[old_tag]
52
+ end
53
+ }
54
+ end
55
+
11
56
  # Only getters are public
12
57
  attr_reader :last_message, :last_error_message, :last_exit_status, :config
13
58
 
@@ -99,53 +144,53 @@ module ZypperUtils
99
144
  # Returns string of command options depending on a given zypper command
100
145
  # combined with provided options
101
146
  def zypper_command_options(zypper_action, options = {})
102
- ret_options = []
103
-
104
- # Additional command-line options for a command
105
- if options[:cmd_options]
106
- ret_options = options[:cmd_options]
107
- end
147
+ ret_options = options.fetch(:cmd_options, [])
108
148
 
109
149
  case zypper_action
110
150
  when 'refresh'
111
- ret_options = [
151
+ ret_options = ret_options | [
112
152
  options[:force] ? '--force' : '',
113
153
  options[:force_build] ? '--force-build' : '',
114
154
  ]
115
155
  when 'addrepo'
116
- ret_options = [
156
+ ret_options = ret_options | [
117
157
  config.refresh_repo? ? '--refresh':'',
118
158
  options[:url],
119
159
  options[:alias],
120
160
  ]
121
161
  when 'removerepo'
122
- ret_options = [
162
+ ret_options = ret_options | [
123
163
  options[:alias],
124
164
  ]
125
165
  when 'install'
126
- ret_options = [
166
+ ret_options = ret_options | [
127
167
  config.auto_agree_with_licenses? ? '--auto-agree-with-licenses' : '',
128
168
  escape_items(options[:packages]),
129
169
  ]
130
170
  when 'remove'
131
- ret_options = [
171
+ ret_options = ret_options | [
132
172
  escape_items(options[:packages]),
133
173
  ]
134
174
  when 'version'
135
- ret_options = [
175
+ ret_options = ret_options | [
136
176
  '--version',
137
177
  ]
138
178
  when 'info'
139
- ret_options = [
179
+ ret_options = ret_options | [
140
180
  escape(options[:package]),
141
181
  ]
142
182
  when 'search'
143
- ret_options = [
183
+ ret_options = ret_options | [
144
184
  options[:status] == Zypper::Package::Status::INSTALLED ? '--installed-only' : '',
145
185
  options[:status] == Zypper::Package::Status::AVAILABLE ? '--uninstalled-only' : '',
146
186
 
147
187
  options[:name] ? '--match-exact ' + escape(options[:name]) : '',
148
188
  ]
189
+ when 'list-updates'
190
+ ret_options = ret_options | [
191
+ !options[:type].nil? ? "--type #{escape(options[:type])}" : '',
192
+ '--all',
193
+ ]
149
194
  end
150
195
 
151
196
  ret_options.join(' ')
@@ -164,14 +209,7 @@ module ZypperUtils
164
209
 
165
210
  def xml_run(command)
166
211
  xml = run(command, {:get => XML_COMMANDS_GET})
167
- out = XmlSimple.xml_in(xml)
168
-
169
- if !out["message"].nil?
170
- errors = out["message"].select{|hash| hash["type"] == "error"}
171
- self.last_error = errors.collect{|hash| hash["content"]}.join("\n")
172
- end
173
-
174
- out
212
+ Nori.parse xml
175
213
  end
176
214
 
177
215
  # Runs a command given as argument and returns the full output
@@ -191,4 +229,60 @@ module ZypperUtils
191
229
  end
192
230
  end
193
231
 
232
+ # Whatever it gets, returns an Array
233
+ # Sometimes even with the only Array item if not got an Array
234
+ def return_array ret
235
+ if ret.kind_of? Array
236
+ ret
237
+ else
238
+ [ret]
239
+ end
240
+ end
241
+
242
+ def convert_output(parsed_stream, type)
243
+ out = []
244
+
245
+ params = PARAMS_FOR_TYPES.fetch(type, [])
246
+
247
+ for item in return_array(parsed_stream)
248
+ one_item = item
249
+
250
+ params.each do |param|
251
+ one_item[param[0]] = convert_entry(item[param[0]], param[0], param[1])
252
+ end
253
+
254
+ out << one_item
255
+ end
256
+
257
+ out
258
+ end
259
+
260
+ def convert_entry(entry, key, to_type = nil)
261
+ return entry unless to_type
262
+
263
+ case to_type
264
+ when :boolean
265
+ Boolean(entry)
266
+ when :subitem
267
+ convert_output(entry, key)
268
+ else
269
+ entry
270
+ end
271
+ end
272
+
273
+ def boolean_nocache(string)
274
+ return false unless string
275
+ return true if string == true || string =~ (/(true|t|yes|y|1)$/i)
276
+ return false if string == false || string.nil? || string =~ (/(false|f|no|n|0)$/i)
277
+ raise ArgumentError.new("invalid value for Boolean: '#{string}'")
278
+ end
279
+
280
+ @@boolean_cache = {}
281
+
282
+ def Boolean(string)
283
+ return @@boolean_cache[string] if @@boolean_cache.has_key?(string)
284
+
285
+ @@boolean_cache[string] = boolean_nocache(string)
286
+ @@boolean_cache[string]
287
+ end
194
288
  end
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 2
10
- version: 0.2.2
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Lukas Ocilka
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-06-26 00:00:00 Z
18
+ date: 2012-07-30 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: popen4
@@ -32,7 +32,7 @@ dependencies:
32
32
  type: :runtime
33
33
  version_requirements: *id001
34
34
  - !ruby/object:Gem::Dependency
35
- name: xml-simple
35
+ name: nori
36
36
  prerelease: false
37
37
  requirement: &id002 !ruby/object:Gem::Requirement
38
38
  none: false
@@ -46,7 +46,7 @@ dependencies:
46
46
  type: :runtime
47
47
  version_requirements: *id002
48
48
  - !ruby/object:Gem::Dependency
49
- name: mocha
49
+ name: nokogiri
50
50
  prerelease: false
51
51
  requirement: &id003 !ruby/object:Gem::Requirement
52
52
  none: false
@@ -57,10 +57,10 @@ dependencies:
57
57
  segments:
58
58
  - 0
59
59
  version: "0"
60
- type: :development
60
+ type: :runtime
61
61
  version_requirements: *id003
62
62
  - !ruby/object:Gem::Dependency
63
- name: rake
63
+ name: mocha
64
64
  prerelease: false
65
65
  requirement: &id004 !ruby/object:Gem::Requirement
66
66
  none: false
@@ -73,10 +73,26 @@ dependencies:
73
73
  version: "0"
74
74
  type: :development
75
75
  version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: rake
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ type: :development
89
+ version_requirements: *id005
76
90
  description: |-
77
91
  Library for accessing zypper functions such as searching and
78
- installing packages, adding and removing repositories and services. Supports
79
- calling zypper in changed root (both with local zypper and zypper in chroot).
92
+ installing packages, adding and removing repositories and services, filtering
93
+ and applying patches.
94
+ Supports calling zypper in changed root (both with local zypper and zypper in
95
+ chroot).
80
96
  email: lukas.ocilka@gmail.com
81
97
  executables: []
82
98
 
@@ -90,6 +106,7 @@ files:
90
106
  - lib/zypper/repository.rb
91
107
  - lib/zypper/utils.rb
92
108
  - lib/zypper/package.rb
109
+ - lib/zypper/update.rb
93
110
  - lib/zypper/patch.rb
94
111
  - lib/zypper/service.rb
95
112
  - lib/zypper/version.rb
@@ -97,7 +114,7 @@ files:
97
114
  - CHANGELOG
98
115
  - README.markdown
99
116
  - VERSION
100
- homepage: https://github.com/kobliha/zypper
117
+ homepage: https://github.com/openSUSE/rubygem-zypper
101
118
  licenses:
102
119
  - MIT
103
120
  post_install_message: