Skip to content

Commit 581e662

Browse files
committed
Chore: refactor
- splits code into command classes - executes `do_it` as a default task - refactor status code handling - updates bundler/platform/nokogiri - handle encoding everywhere - adds test coverage (a few "online" tests)
1 parent df2cf19 commit 581e662

16 files changed

+289
-178
lines changed

.github/workflows/pr.yml

+1
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,6 @@ jobs:
4545
- run: gem install pkg/apt-spy2*
4646
- run: apt-spy2 check --strict
4747
- run: sudo env "PATH=$PATH" apt-spy2 fix --commit --strict
48+
- run: sudo env "PATH=$PATH" apt-spy2 check --launchpad --country=us --strict
4849

4950

Gemfile.lock

+10-9
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ GEM
1414
colored (1.2)
1515
docile (1.4.0)
1616
json (2.6.3)
17-
mini_portile2 (2.8.1)
1817
minitest (5.15.0)
19-
nokogiri (1.14.1)
20-
mini_portile2 (~> 2.8.0)
18+
nokogiri (1.14.2-x86_64-darwin)
19+
racc (~> 1.4)
20+
nokogiri (1.14.2-x86_64-linux)
2121
racc (~> 1.4)
2222
parallel (1.22.1)
2323
parser (3.2.1.0)
@@ -37,21 +37,22 @@ GEM
3737
rubocop-ast (>= 1.24.1, < 2.0)
3838
ruby-progressbar (~> 1.7)
3939
unicode-display_width (>= 2.4.0, < 3.0)
40-
rubocop-ast (1.24.1)
41-
parser (>= 3.1.1.0)
40+
rubocop-ast (1.26.0)
41+
parser (>= 3.2.1.0)
4242
ruby-progressbar (1.11.0)
43-
simplecov (0.21.2)
43+
simplecov (0.22.0)
4444
docile (~> 1.1)
4545
simplecov-html (~> 0.11)
4646
simplecov_json_formatter (~> 0.1)
4747
simplecov-html (0.12.3)
4848
simplecov-lcov (0.8.0)
49-
simplecov_json_formatter (0.1.3)
49+
simplecov_json_formatter (0.1.4)
5050
thor (1.2.1)
5151
unicode-display_width (2.4.2)
5252

5353
PLATFORMS
54-
ruby
54+
x86_64-darwin-22
55+
x86_64-linux
5556

5657
DEPENDENCIES
5758
apt-spy2!
@@ -63,4 +64,4 @@ DEPENDENCIES
6364
simplecov-lcov (~> 0.8.0)
6465

6566
BUNDLED WITH
66-
2.2.32
67+
2.3.15

Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ container:=apt-spy2
66

77
clean:
88
docker rm -f $(container) || true
9+
rm -rf vendor
910

1011
install:
11-
bundle install --path ./vendor/bundle
12+
bundle config set --local path './vendor/bundle'
13+
bundle install
1214

1315
release:
1416
bundle exec rake release

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ gem install apt-spy2
1717
## Usage
1818

1919
```sh
20-
$ apt-spy2 [21:03:52]
20+
$ apt-spy2
2121
apt-spy2 commands:
2222
apt-spy2 check # Evaluate mirrors
2323
apt-spy2 fix # Set the closest/fastest mirror

lib/apt/spy2.rb

+9-142
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,29 @@
11
# frozen_string_literal: true
22

33
require 'thor'
4-
require 'colored'
5-
require 'fileutils'
6-
require 'apt/spy2/writer'
7-
require 'apt/spy2/country'
8-
require 'apt/spy2/downloader'
9-
require 'apt/spy2/ubuntu_mirrors'
10-
require 'apt/spy2/launchpad'
11-
require 'apt/spy2/request'
12-
require 'apt/spy2/url'
4+
require 'apt/spy2/command/fix'
5+
require 'apt/spy2/command/list'
6+
require 'apt/spy2/command/check'
7+
require 'apt/spy2/version'
138

149
# apt-spy2 command interface
1510
class AptSpy2 < Thor
1611
package_name 'apt-spy2'
1712
class_option :country, default: 'mirrors'
18-
class_option :launchpad, type: :boolean, banner: "Use launchpad's mirror list"
19-
20-
desc 'fix', 'Set the closest/fastest mirror'
21-
option :commit, type: :boolean
22-
option :strict, type: :boolean
23-
def fix
24-
mirrors = retrieve(options[:country], use_launchpad?(options))
25-
working = filter(mirrors, options[:strict], false)
26-
print 'The closest mirror is: '
27-
puts (working[0]).to_s.bold.magenta
28-
unless options[:commit]
29-
puts 'Run with --commit to adjust /etc/apt/sources.list'.yellow
30-
return
31-
end
32-
33-
puts 'Updating /etc/apt/sources.list'.yellow
34-
update(working[0])
35-
end
13+
class_option :launchpad, type: :boolean, banner: 'Use launchpad\'s mirror list'
3614

3715
desc 'check', 'Evaluate mirrors'
38-
option :output, type: :boolean, default: true
39-
option :format, default: 'shell'
40-
option :strict, type: :boolean
41-
def check
42-
@writer = Apt::Spy2::Writer.new(options[:format])
16+
subcommand 'check', Apt::Spy2::Command::Check
4317

44-
mirrors = retrieve(options[:country], use_launchpad?(options))
45-
filter(mirrors, options[:strict], options[:output])
46-
47-
puts @writer.to_json if @writer.json?
48-
end
18+
desc 'fix', 'Update sources'
19+
subcommand 'fix', Apt::Spy2::Command::Fix
4920

5021
desc 'list', 'List the currently available mirrors'
51-
option :format, default: 'shell'
52-
def list
53-
mirrors = retrieve(options[:country], use_launchpad?(options))
54-
55-
@writer = Apt::Spy2::Writer.new(options[:format])
56-
57-
@writer.complete(mirrors)
58-
59-
puts @writer.to_json if @writer.json?
60-
puts mirrors unless @writer.json?
61-
end
22+
subcommand 'list', Apt::Spy2::Command::List
6223

6324
desc 'version', 'Show which version of apt-spy2 is installed'
6425
def version
6526
puts Apt::Spy2::VERSION
6627
exit
6728
end
68-
69-
private
70-
71-
def retrieve(country = 'mirrors', launchpad = false)
72-
downloader = Apt::Spy2::Downloader.new
73-
74-
if launchpad
75-
csv_path = File.expand_path("#{File.dirname(__FILE__)}/../../var/country-names.txt")
76-
country = Apt::Spy2::Country.new(csv_path)
77-
name = country.to_country_name(options[:country])
78-
79-
launchpad = Apt::Spy2::Launchpad.new(downloader.do_download('https://launchpad.net/ubuntu/+archivemirrors'))
80-
return launchpad.mirrors(name)
81-
end
82-
83-
country.upcase! if country.length == 2
84-
85-
ubuntu_mirrors = Apt::Spy2::UbuntuMirrors.new(downloader.do_download("http://mirrors.ubuntu.com/#{country}.txt"))
86-
ubuntu_mirrors.mirrors(country)
87-
end
88-
89-
def filter(mirrors, strict = false, output = true)
90-
# f me :)
91-
92-
working_mirrors = []
93-
94-
url = Apt::Spy2::Url.new(strict)
95-
96-
mirrors.each do |mirror|
97-
data = { 'mirror' => mirror }
98-
99-
check = url.adjust!(mirror)
100-
101-
status = broken?(check)
102-
103-
data['status'] = status
104-
105-
working_mirrors << mirror if status == 'up'
106-
107-
@writer.echo(data) if output
108-
end
109-
110-
working_mirrors
111-
end
112-
113-
def broken?(url)
114-
begin
115-
req = Apt::Spy2::Request.new(url)
116-
response = req.head
117-
return 'up' if response.code == '200'
118-
119-
return 'broken' if response.code == '404'
120-
rescue StandardError
121-
# connection errors, ssl errors, etc.
122-
end
123-
124-
'down'
125-
end
126-
127-
def update(mirror)
128-
t = Time.now
129-
r = `lsb_release -c`.split(' ')[1]
130-
sources = "## Updated on #{t} by apt-spy2\n"
131-
sources += "deb #{mirror} #{r} main restricted universe multiverse\n"
132-
sources += "deb #{mirror} #{r}-updates main restricted universe multiverse\n"
133-
sources += "deb #{mirror} #{r}-backports main restricted universe multiverse\n"
134-
sources += "deb #{mirror} #{r}-security main restricted universe multiverse\n"
135-
136-
apt_sources = '/etc/apt/sources.list'
137-
138-
begin
139-
File.rename apt_sources, "#{apt_sources}.#{t.to_i}"
140-
File.open(apt_sources, 'w') do |f|
141-
f.write(sources)
142-
end
143-
rescue StandardError
144-
msg = "Failed updating #{apt_sources}!"
145-
msg += 'You probably need sudo!'
146-
raise msg
147-
end
148-
149-
puts "Updated '#{apt_sources}' with #{mirror}".green
150-
puts 'Run `apt-get update` to update'.black_on_yellow
151-
end
152-
153-
def use_launchpad?(options)
154-
return false unless options[:launchpad]
155-
156-
if options[:country] && options[:country] == 'mirrors'
157-
raise 'Please supply a `--country=foo`. Launchpad cannot guess!'
158-
end
159-
160-
true
161-
end
16229
end

lib/apt/spy2/command/check.rb

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# frozen_string_literal: true
2+
3+
require 'apt/spy2/command/command'
4+
require 'apt/spy2/writer'
5+
6+
module Apt
7+
module Spy2
8+
module Command
9+
# runs `apt-spy2 check`
10+
class Check < BaseCommand
11+
option :output, type: :boolean, default: true
12+
option :format, default: 'shell'
13+
option :strict, type: :boolean
14+
15+
desc 'do_it', ''
16+
def do_it
17+
@writer = Apt::Spy2::Writer.new(options[:format])
18+
19+
mirrors = retrieve(launchpad: use_launchpad?(options))
20+
filter(mirrors, strict: options[:strict], output: options[:output])
21+
22+
puts @writer.to_json if @writer.json?
23+
end
24+
25+
default_task :do_it
26+
end
27+
end
28+
end
29+
end

lib/apt/spy2/command/command.rb

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# frozen_string_literal: true
2+
3+
require 'thor'
4+
require 'colored'
5+
require 'apt/spy2/country'
6+
require 'apt/spy2/downloader'
7+
require 'apt/spy2/launchpad'
8+
require 'apt/spy2/request'
9+
require 'apt/spy2/status'
10+
require 'apt/spy2/ubuntu_mirrors'
11+
require 'apt/spy2/url'
12+
require 'apt/spy2/writer'
13+
14+
module Apt
15+
module Spy2
16+
module Command
17+
# BaseCommmand for all others
18+
class BaseCommand < Thor
19+
# rubocop:disable Metrics/BlockLength
20+
no_commands do
21+
def use_launchpad?(options)
22+
return false unless options[:launchpad]
23+
24+
if options[:country] && options[:country] == 'mirrors'
25+
raise 'Please supply a `--country=foo`. Launchpad cannot guess!'
26+
end
27+
28+
true
29+
end
30+
31+
def country_names
32+
File.expand_path("#{File.dirname(__FILE__)}/../../../../var/country-names.txt")
33+
end
34+
35+
def retrieve(launchpad: false)
36+
downloader = Apt::Spy2::Downloader.new
37+
38+
if launchpad
39+
name = Apt::Spy2::Country.new(country_names).to_country_name(options[:country])
40+
launchpad = Apt::Spy2::Launchpad.new(downloader.do_download('https://launchpad.net/ubuntu/+archivemirrors'))
41+
return launchpad.mirrors(name)
42+
end
43+
44+
country = options[:country]
45+
country.upcase! if country.length == 2
46+
47+
ubuntu_mirrors = Apt::Spy2::UbuntuMirrors.new(downloader.do_download("http://mirrors.ubuntu.com/#{country}.txt"))
48+
ubuntu_mirrors.mirrors(country)
49+
end
50+
51+
def filter(mirrors, strict: false, output: true)
52+
# f me :)
53+
working_mirrors = []
54+
url = Apt::Spy2::Url.new(strict)
55+
56+
mirrors.each do |mirror|
57+
data = { 'mirror' => mirror }
58+
data['status'] = broken?(url.adjust!(mirror))
59+
working_mirrors << mirror if data['status'] == Apt::Spy2::Status::UP
60+
@writer.echo(data) if output
61+
end
62+
63+
working_mirrors
64+
end
65+
66+
def broken?(url)
67+
begin
68+
req = Apt::Spy2::Request.new(url)
69+
return Apt::Spy2::Status.status?(req.head.code)
70+
rescue StandardError
71+
# connection errors, ssl errors, etc.
72+
end
73+
74+
Apt::Spy2::Status::DOWN
75+
end
76+
end
77+
# rubocop:enable Metrics/BlockLength
78+
end
79+
end
80+
end
81+
end

0 commit comments

Comments
 (0)