Skip to content

Commit

Permalink
Chore: refactor
Browse files Browse the repository at this point in the history
- 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)
  • Loading branch information
till committed Feb 14, 2023
1 parent df2cf19 commit 581e662
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 178 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ jobs:
- run: gem install pkg/apt-spy2*
- run: apt-spy2 check --strict
- run: sudo env "PATH=$PATH" apt-spy2 fix --commit --strict
- run: sudo env "PATH=$PATH" apt-spy2 check --launchpad --country=us --strict


19 changes: 10 additions & 9 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ GEM
colored (1.2)
docile (1.4.0)
json (2.6.3)
mini_portile2 (2.8.1)
minitest (5.15.0)
nokogiri (1.14.1)
mini_portile2 (~> 2.8.0)
nokogiri (1.14.2-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.14.2-x86_64-linux)
racc (~> 1.4)
parallel (1.22.1)
parser (3.2.1.0)
Expand All @@ -37,21 +37,22 @@ GEM
rubocop-ast (>= 1.24.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.24.1)
parser (>= 3.1.1.0)
rubocop-ast (1.26.0)
parser (>= 3.2.1.0)
ruby-progressbar (1.11.0)
simplecov (0.21.2)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov-lcov (0.8.0)
simplecov_json_formatter (0.1.3)
simplecov_json_formatter (0.1.4)
thor (1.2.1)
unicode-display_width (2.4.2)

PLATFORMS
ruby
x86_64-darwin-22
x86_64-linux

DEPENDENCIES
apt-spy2!
Expand All @@ -63,4 +64,4 @@ DEPENDENCIES
simplecov-lcov (~> 0.8.0)

BUNDLED WITH
2.2.32
2.3.15
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ container:=apt-spy2

clean:
docker rm -f $(container) || true
rm -rf vendor

install:
bundle install --path ./vendor/bundle
bundle config set --local path './vendor/bundle'
bundle install

release:
bundle exec rake release
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ gem install apt-spy2
## Usage

```sh
$ apt-spy2 [21:03:52]
$ apt-spy2
apt-spy2 commands:
apt-spy2 check # Evaluate mirrors
apt-spy2 fix # Set the closest/fastest mirror
Expand Down
151 changes: 9 additions & 142 deletions lib/apt/spy2.rb
Original file line number Diff line number Diff line change
@@ -1,162 +1,29 @@
# frozen_string_literal: true

require 'thor'
require 'colored'
require 'fileutils'
require 'apt/spy2/writer'
require 'apt/spy2/country'
require 'apt/spy2/downloader'
require 'apt/spy2/ubuntu_mirrors'
require 'apt/spy2/launchpad'
require 'apt/spy2/request'
require 'apt/spy2/url'
require 'apt/spy2/command/fix'
require 'apt/spy2/command/list'
require 'apt/spy2/command/check'
require 'apt/spy2/version'

# apt-spy2 command interface
class AptSpy2 < Thor
package_name 'apt-spy2'
class_option :country, default: 'mirrors'
class_option :launchpad, type: :boolean, banner: "Use launchpad's mirror list"

desc 'fix', 'Set the closest/fastest mirror'
option :commit, type: :boolean
option :strict, type: :boolean
def fix
mirrors = retrieve(options[:country], use_launchpad?(options))
working = filter(mirrors, options[:strict], false)
print 'The closest mirror is: '
puts (working[0]).to_s.bold.magenta
unless options[:commit]
puts 'Run with --commit to adjust /etc/apt/sources.list'.yellow
return
end

puts 'Updating /etc/apt/sources.list'.yellow
update(working[0])
end
class_option :launchpad, type: :boolean, banner: 'Use launchpad\'s mirror list'

desc 'check', 'Evaluate mirrors'
option :output, type: :boolean, default: true
option :format, default: 'shell'
option :strict, type: :boolean
def check
@writer = Apt::Spy2::Writer.new(options[:format])
subcommand 'check', Apt::Spy2::Command::Check

mirrors = retrieve(options[:country], use_launchpad?(options))
filter(mirrors, options[:strict], options[:output])

puts @writer.to_json if @writer.json?
end
desc 'fix', 'Update sources'
subcommand 'fix', Apt::Spy2::Command::Fix

desc 'list', 'List the currently available mirrors'
option :format, default: 'shell'
def list
mirrors = retrieve(options[:country], use_launchpad?(options))

@writer = Apt::Spy2::Writer.new(options[:format])

@writer.complete(mirrors)

puts @writer.to_json if @writer.json?
puts mirrors unless @writer.json?
end
subcommand 'list', Apt::Spy2::Command::List

desc 'version', 'Show which version of apt-spy2 is installed'
def version
puts Apt::Spy2::VERSION
exit
end

private

def retrieve(country = 'mirrors', launchpad = false)
downloader = Apt::Spy2::Downloader.new

if launchpad
csv_path = File.expand_path("#{File.dirname(__FILE__)}/../../var/country-names.txt")
country = Apt::Spy2::Country.new(csv_path)
name = country.to_country_name(options[:country])

launchpad = Apt::Spy2::Launchpad.new(downloader.do_download('https://launchpad.net/ubuntu/+archivemirrors'))
return launchpad.mirrors(name)
end

country.upcase! if country.length == 2

ubuntu_mirrors = Apt::Spy2::UbuntuMirrors.new(downloader.do_download("http://mirrors.ubuntu.com/#{country}.txt"))
ubuntu_mirrors.mirrors(country)
end

def filter(mirrors, strict = false, output = true)
# f me :)

working_mirrors = []

url = Apt::Spy2::Url.new(strict)

mirrors.each do |mirror|
data = { 'mirror' => mirror }

check = url.adjust!(mirror)

status = broken?(check)

data['status'] = status

working_mirrors << mirror if status == 'up'

@writer.echo(data) if output
end

working_mirrors
end

def broken?(url)
begin
req = Apt::Spy2::Request.new(url)
response = req.head
return 'up' if response.code == '200'

return 'broken' if response.code == '404'
rescue StandardError
# connection errors, ssl errors, etc.
end

'down'
end

def update(mirror)
t = Time.now
r = `lsb_release -c`.split(' ')[1]
sources = "## Updated on #{t} by apt-spy2\n"
sources += "deb #{mirror} #{r} main restricted universe multiverse\n"
sources += "deb #{mirror} #{r}-updates main restricted universe multiverse\n"
sources += "deb #{mirror} #{r}-backports main restricted universe multiverse\n"
sources += "deb #{mirror} #{r}-security main restricted universe multiverse\n"

apt_sources = '/etc/apt/sources.list'

begin
File.rename apt_sources, "#{apt_sources}.#{t.to_i}"
File.open(apt_sources, 'w') do |f|
f.write(sources)
end
rescue StandardError
msg = "Failed updating #{apt_sources}!"
msg += 'You probably need sudo!'
raise msg
end

puts "Updated '#{apt_sources}' with #{mirror}".green
puts 'Run `apt-get update` to update'.black_on_yellow
end

def use_launchpad?(options)
return false unless options[:launchpad]

if options[:country] && options[:country] == 'mirrors'
raise 'Please supply a `--country=foo`. Launchpad cannot guess!'
end

true
end
end
29 changes: 29 additions & 0 deletions lib/apt/spy2/command/check.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

require 'apt/spy2/command/command'
require 'apt/spy2/writer'

module Apt
module Spy2
module Command
# runs `apt-spy2 check`
class Check < BaseCommand
option :output, type: :boolean, default: true
option :format, default: 'shell'
option :strict, type: :boolean

desc 'do_it', ''
def do_it
@writer = Apt::Spy2::Writer.new(options[:format])

mirrors = retrieve(launchpad: use_launchpad?(options))
filter(mirrors, strict: options[:strict], output: options[:output])

puts @writer.to_json if @writer.json?
end

default_task :do_it
end
end
end
end
81 changes: 81 additions & 0 deletions lib/apt/spy2/command/command.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

require 'thor'
require 'colored'
require 'apt/spy2/country'
require 'apt/spy2/downloader'
require 'apt/spy2/launchpad'
require 'apt/spy2/request'
require 'apt/spy2/status'
require 'apt/spy2/ubuntu_mirrors'
require 'apt/spy2/url'
require 'apt/spy2/writer'

module Apt
module Spy2
module Command
# BaseCommmand for all others
class BaseCommand < Thor
# rubocop:disable Metrics/BlockLength
no_commands do
def use_launchpad?(options)
return false unless options[:launchpad]

if options[:country] && options[:country] == 'mirrors'
raise 'Please supply a `--country=foo`. Launchpad cannot guess!'
end

true
end

def country_names
File.expand_path("#{File.dirname(__FILE__)}/../../../../var/country-names.txt")
end

def retrieve(launchpad: false)
downloader = Apt::Spy2::Downloader.new

if launchpad
name = Apt::Spy2::Country.new(country_names).to_country_name(options[:country])
launchpad = Apt::Spy2::Launchpad.new(downloader.do_download('https://launchpad.net/ubuntu/+archivemirrors'))
return launchpad.mirrors(name)
end

country = options[:country]
country.upcase! if country.length == 2

ubuntu_mirrors = Apt::Spy2::UbuntuMirrors.new(downloader.do_download("http://mirrors.ubuntu.com/#{country}.txt"))
ubuntu_mirrors.mirrors(country)
end

def filter(mirrors, strict: false, output: true)
# f me :)
working_mirrors = []
url = Apt::Spy2::Url.new(strict)

mirrors.each do |mirror|
data = { 'mirror' => mirror }
data['status'] = broken?(url.adjust!(mirror))
working_mirrors << mirror if data['status'] == Apt::Spy2::Status::UP
@writer.echo(data) if output
end

working_mirrors
end

def broken?(url)
begin
req = Apt::Spy2::Request.new(url)
return Apt::Spy2::Status.status?(req.head.code)
rescue StandardError
# connection errors, ssl errors, etc.
end

Apt::Spy2::Status::DOWN
end
end
# rubocop:enable Metrics/BlockLength
end
end
end
end
Loading

0 comments on commit 581e662

Please sign in to comment.