zypper-upgraderepo 1.0.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.
@@ -0,0 +1,47 @@
1
+ # Zypper-Upgraderepo
2
+
3
+ Zypper-Upgraderepo helps to check and upgrade the repositories used in your system
4
+ without have to do it manually.
5
+
6
+ It can be used stand-alone or installed as _zypper_ plugin.
7
+
8
+ ## Installation
9
+
10
+ Install it as:
11
+
12
+ $ gem install zypper-upgraderepo
13
+
14
+ If you want to install it as zypper plugin watch the _zypper-upgraderepo-plugin_ project linked below.
15
+
16
+ ## Usage
17
+
18
+ To check the availability of the current repositories:
19
+
20
+ $ zypper-upgraderepo -c
21
+
22
+ To check the availability of the next version repositories:
23
+
24
+ $ zypper-upgraderepo -n
25
+
26
+ To upgrade the repositories to the next version:
27
+
28
+ $ sudo zypper-upgraderepo -u
29
+
30
+ ## Get help
31
+
32
+ Where to start:
33
+
34
+ $ zypper-upgraderepo --help
35
+
36
+ More Help:
37
+
38
+ - The wiki page: https://github.com/fabiomux/zypper-upgraderepo
39
+ - zypper-upgraderepo-plugin project: https://github.com/fabiomux/zypper-upgraderepo-plugin
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fabiomux/zypper-upgraderepo. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
44
+
45
+ ## Code of Conduct
46
+
47
+ Everyone interacting in the Zypper::Upgraderepo project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fabiomux/zypper-upgraderepo/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "zypper/upgraderepo"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "zypper/upgraderepo/cli"
4
+
5
+ Zypper::Upgraderepo::CLI.start
@@ -0,0 +1,72 @@
1
+ require 'zypper/upgraderepo/repository'
2
+ require 'zypper/upgraderepo/os_release'
3
+ require 'zypper/upgraderepo/utils'
4
+
5
+
6
+ module Zypper
7
+ module Upgraderepo
8
+
9
+ class Builder
10
+ def initialize(options)
11
+ @os_release = OsRelease.new(options)
12
+ @repos = RepositoryList.new(options)
13
+ @print_hint = options.hint
14
+ end
15
+
16
+ def backup
17
+ @repos.backup
18
+ Messages.ok 'Repository backup executed!'
19
+ end
20
+
21
+ def check_current
22
+ check_repos(@os_release.current)
23
+ end
24
+
25
+ def check_next
26
+ @repos.upgrade(@os_release.next) unless @os_release.last?
27
+ check_repos(@os_release.next)
28
+ end
29
+
30
+ def check_to
31
+ @repos.upgrade(@os_release.custom)
32
+ check_repos(@os_release.custom)
33
+ end
34
+
35
+ def upgrade
36
+ @repos.upgrade(@os_release.next) unless @os_release.last?
37
+ @repos.save
38
+ Messages.ok 'Repositories upgraded!'
39
+ end
40
+
41
+ def upgrade_to
42
+ @repos.upgrade(@os_release.custom)
43
+ @repos.save
44
+ Messages.ok 'Repositories upgraded!'
45
+ end
46
+
47
+
48
+ private
49
+
50
+ def check_repos(version)
51
+ Messages.header(@repos.max_col)
52
+ @repos.list.each_with_index do |r, i|
53
+ Messages.separator
54
+ if r.available?
55
+ Messages.available i.next, r.name, r.url, @repos.max_col
56
+ elsif r.redirected?
57
+ Messages.redirect i.next, r.name, r.url, @repos.max_col, r.redirected_to
58
+ elsif r.not_found?
59
+ if @print_hint
60
+ Messages.alternative i.next, r.name, r.url, @repos.max_col, r.evaluate_alternative(version)
61
+ else
62
+ Messages.not_found i.next, r.name, r.url, @repos.max_col
63
+ end
64
+ end
65
+ end
66
+ Messages.footer
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,120 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'zypper/upgraderepo'
4
+ require 'zypper/upgraderepo/version'
5
+
6
+ module Zypper
7
+
8
+ module Upgraderepo
9
+
10
+ class OptParseMain
11
+
12
+ def self.parse(args)
13
+ options = OpenStruct.new
14
+ options.operation = :check_current
15
+ options.backup_path = ENV['HOME']
16
+ options.only_enabled = true
17
+ options.alias = true
18
+ options.name = true
19
+ options.hint = true
20
+ options.overrides = {}
21
+ options.version = nil
22
+
23
+ OptionParser.new do |opt|
24
+
25
+ if ENV['ZYPPER_UPGRADEREPO']
26
+ opt.banner = 'Usage: zypper upgraderepo [OPTIONS] [OPERATION]'
27
+ else
28
+ opt.banner = 'Usage: zypper-upgraderepo [OPTIONS] [OPERATION]'
29
+ end
30
+
31
+ opt.separator ''
32
+ opt.separator 'Operations:'
33
+
34
+ opt.on('-b', '--backup <PATH>', 'Create a Tar backup of all the repositories under PATH') do |o|
35
+ options.operation = :backup
36
+ options.only_enabled = false
37
+ options.backup_path = o
38
+ end
39
+
40
+ opt.on('-c', '--check-current', 'Check the repositories for the current version (Default)') do |o|
41
+ options.operation = :check_current
42
+ end
43
+
44
+ opt.on('-N', '--check-next', 'Check the repositories for the next version') do |o|
45
+ options.operation = :check_next
46
+ end
47
+
48
+ opt.on('-C', '--check-to <VERSION>', 'Check for a custom VERSION') do |v|
49
+ options.version = v
50
+ options.operation = :check_to
51
+ end
52
+
53
+ opt.on('-u', '--upgrade', 'Upgrade to the last version available') do |o|
54
+ options.operation = :upgrade
55
+ end
56
+
57
+ opt.on('-U', '--upgrade-to <VERSION>', 'Upgrade to a specific VERSION') do |v|
58
+ options.version = v
59
+ options.operation = :upgrade_to
60
+ end
61
+
62
+ opt.separator ''
63
+ opt.separator 'Options:'
64
+
65
+ opt.on('--[no-]only-enabled', 'Include or not the disabled repositories') do |o|
66
+ options.only_enabled = o
67
+ end
68
+
69
+ opt.on('--[no-]name', 'Upgrade or not the name') do |o|
70
+ options.name = o
71
+ end
72
+
73
+ opt.on('--[no-]alias', 'Upgrade or not the alias') do |o|
74
+ options.alias = o
75
+ end
76
+
77
+ opt.on('--[no-]hint', 'Suggest a new url when the current is not found') do |o|
78
+ options.hint = o
79
+ end
80
+
81
+ opt.on('--override-url <NUMBER>,<URL>', Array, 'Overwrite the repository\'s url NUMBER with URL') do |r|
82
+ options.overrides[r[0]] = r[1]
83
+ end
84
+
85
+ unless ENV['ZYPPER_UPGRADEREPO']
86
+ opt.separator ''
87
+ opt.separator 'Other:'
88
+
89
+ opt.on_tail('-h', '--help', 'Show this message') do |o|
90
+ puts opt
91
+ exit
92
+ end
93
+
94
+ opt.on_tail('-v', '--version', 'Show version') do |o|
95
+ puts VERSION
96
+ exit
97
+ end
98
+ end
99
+
100
+ end.parse!(ARGV)
101
+
102
+ options
103
+ end
104
+
105
+ end
106
+
107
+
108
+ class CLI
109
+ def self.start
110
+ begin
111
+ options = OptParseMain.parse(ARGV)
112
+ Upgraderepo::Builder.new(options).send(options.operation)
113
+ rescue => e
114
+ Messages.error e
115
+ exit 1
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,67 @@
1
+ require 'iniparse'
2
+
3
+ module Zypper
4
+ module Upgraderepo
5
+
6
+
7
+ class OsRelease
8
+
9
+ attr_reader :custom
10
+
11
+ OS_VERSIONS = ['13.1', '13.2', '42.1', '42.2', '42.3', '15.0']
12
+
13
+
14
+ def initialize(options)
15
+ fname = if File.exist? '/etc/os-release'
16
+ '/etc/os-release'
17
+ elsif File.exist? '/etc/SuSE-release'
18
+ '/etc/SuSE-release'
19
+ else
20
+ raise ReleaseFileNotFound
21
+ end
22
+ @release = IniParse.parse(File.read(fname))
23
+ @current_idx = OS_VERSIONS.index(@release['__anonymous__']['VERSION'].delete('"'))
24
+
25
+ if options.version
26
+ raise InvalidVersion, options.version unless OS_VERSIONS.include?(options.version)
27
+ raise AlreadyUpgraded, options.version unless OS_VERSIONS.index(options.version) != @current_idx
28
+ @custom = options.version
29
+ end
30
+ end
31
+
32
+ def current
33
+ OS_VERSIONS[@current_idx]
34
+ end
35
+
36
+ def next
37
+ unless last?
38
+ OS_VERSIONS[@current_idx.next]
39
+ else
40
+ nil
41
+ end
42
+ end
43
+
44
+ def previous
45
+ unless first?
46
+ OS_VERSIONS[@current_idx.pred]
47
+ else
48
+ nil
49
+ end
50
+ end
51
+
52
+ def last?
53
+ @current_idx == (OS_VERSIONS.count - 1)
54
+ end
55
+
56
+ def first?
57
+ @current_idx == 0
58
+ end
59
+
60
+ def valid?(version)
61
+ OS_VERSIONS.include? version
62
+ end
63
+ end
64
+
65
+
66
+ end
67
+ end
@@ -0,0 +1,171 @@
1
+ require 'iniparse'
2
+ require 'net/http'
3
+ require 'zlib'
4
+ require 'minitar'
5
+
6
+ module Zypper
7
+ module Upgraderepo
8
+
9
+
10
+ class RepositoryList
11
+ attr_reader :list, :max_col
12
+
13
+ def initialize(options)
14
+ @alias = options.alias
15
+ @name = options.name
16
+ @overrides = options.overrides
17
+ @list = []
18
+ @backup_path = options.backup_path
19
+
20
+ Dir.glob('/etc/zypp/repos.d/*.repo').sort.each do |i|
21
+ r = Repository.new(i)
22
+ next if options.only_enabled && (!r.enabled?)
23
+ @list << r
24
+ end
25
+ @max_col = @list.max_by { |x| x.name.length }.name.length
26
+ end
27
+
28
+ def backup
29
+ filename = File.join(@backup_path, "repos-backup-#{Time.now.to_s.delete(': +-')[0..-5]}.tgz")
30
+ raise InvalidPermissions, filename unless File.writable? @backup_path
31
+ Minitar.pack('/etc/zypp/repos.d',
32
+ Zlib::GzipWriter.new(File.open(filename, 'wb')))
33
+ end
34
+
35
+ def upgrade(version)
36
+ @list.each_with_index do |repo, i|
37
+ if @overrides.has_key? i.next.to_s
38
+ repo.url = @overrides[i.next.to_s]
39
+ else
40
+ repo.url = repo.url.gsub(/\d\d\.\d/, version)
41
+ end
42
+ repo.alias = repo.alias.gsub(/\d\d\.\d/, version) if @alias
43
+ repo.name = repo.name.gsub(/\d\d\.\d/, version) if @name
44
+ end
45
+ end
46
+
47
+ def save
48
+ @list.each do |i|
49
+ i.save
50
+ end
51
+ end
52
+ end
53
+
54
+
55
+ class Repository
56
+ attr_reader :filename
57
+
58
+ def initialize(filename)
59
+ @filename = filename
60
+ @repo = IniParse.parse(File.read(filename))
61
+ @key = get_key
62
+ @res = nil
63
+ end
64
+
65
+ def enabled?
66
+ @repo[@key]['enabled'].to_i == 1
67
+ end
68
+
69
+ def type
70
+ @repo[@key]['type']
71
+ end
72
+
73
+ def name
74
+ @repo[@key]['name'] || @key
75
+ end
76
+
77
+ def name=(value)
78
+ @repo[@key]['name'] = value
79
+ end
80
+
81
+ def url
82
+ @repo[@key]['baseurl']
83
+ end
84
+
85
+ def url=(value)
86
+ @repo[@key]['baseurl'] = value
87
+ end
88
+
89
+ def alias
90
+ @key
91
+ end
92
+
93
+ def alias=(value)
94
+ @repo = IniParse.parse(@repo.to_ini.sub(/\[[^\]]+\]/, "[#{value}]"))
95
+ @key = get_key
96
+ end
97
+
98
+ def available?
99
+ ping.is_a?(Net::HTTPSuccess)
100
+ end
101
+
102
+ def redirected?
103
+ ping.is_a?(Net::HTTPRedirection)
104
+ end
105
+
106
+ def redirected_to
107
+ ping['location']
108
+ end
109
+
110
+ def not_found?
111
+ ping.is_a?(Net::HTTPNotFound)
112
+ end
113
+
114
+ def save
115
+ raise InvalidPermissions, @filename unless File.writable? @filename
116
+ @repo.save(@filename)
117
+ end
118
+
119
+ def evaluate_alternative(version)
120
+ if url =~ /dl\.google\.com/
121
+ return { url: '', message: 'Just Google security, use this repo anyway ;)'}
122
+ elsif not_found?
123
+ return traverse_url(URI(url.clone), version)
124
+ elsif redirected?
125
+ return { url: redirected_to, message: 'Redirected to:' }
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def ping(uri = URI(url), force = false)
132
+ begin
133
+ @res = Net::HTTP.get_response(uri) if @res.nil? || force
134
+ rescue SocketError
135
+ raise NoConnection
136
+ end
137
+ @res
138
+ end
139
+
140
+ def get_key
141
+ @repo.to_hash.keys.delete_if {|k| k == '0'}.pop
142
+ end
143
+
144
+ def traverse_url(uri, version)
145
+ uri.path = File.dirname(uri.path)
146
+
147
+ return {url: '', message: 'None, try to find it manually'} if uri.path == '/'
148
+
149
+ uri.path += '/'
150
+ ping(uri, true)
151
+
152
+ if not_found?
153
+ return traverse_url(uri, version)
154
+ elsif available?
155
+ return {url: uri.to_s, message: 'Override with this one' } if uri.path =~ Regexp.new(version)
156
+
157
+ path = ping.body.to_s.scan(Regexp.new("href=\"[^\"]*#{version}[^\"]*\"")).uniq
158
+ unless path.empty?
159
+ uri.path += "#{path.pop.scan(/href="(.*)"/).pop.pop }"
160
+ return {url: uri.to_s, message: 'Override with this one' }
161
+ end
162
+
163
+ return {url: url, message: 'Can\'t find anything similar, try manually!' }
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+
170
+ end
171
+ end