zypper 0.2.2 → 0.3.0

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