817 lines
24 KiB
Python
817 lines
24 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# This file is part of oh-my-tuna
|
|
# Copyright (c) 2018 oh-my-tuna's authors
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
import subprocess
|
|
import os
|
|
import errno
|
|
import argparse
|
|
import re
|
|
import platform
|
|
from contextlib import contextmanager
|
|
|
|
try:
|
|
input = raw_input
|
|
except NameError:
|
|
pass
|
|
|
|
try:
|
|
import configparser
|
|
except ImportError:
|
|
import ConfigParser as configparser
|
|
|
|
|
|
mirror_root = "mirrors.tuna.tsinghua.edu.cn"
|
|
host_name = "tuna.tsinghua.edu.cn"
|
|
always_yes = False
|
|
verbose = False
|
|
is_global = True
|
|
|
|
os_release_regex = re.compile(r"^ID=\"?([^\"\n]+)\"?$", re.M)
|
|
|
|
|
|
@contextmanager
|
|
def cd(path):
|
|
old_cwd = os.getcwd()
|
|
os.chdir(path)
|
|
try:
|
|
yield
|
|
finally:
|
|
os.chdir(old_cwd)
|
|
|
|
|
|
def sh(command):
|
|
try:
|
|
if verbose:
|
|
print('$ %s' % command)
|
|
if isinstance(command, list):
|
|
command = ' '.join(command)
|
|
return subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT).decode('utf-8').rstrip()
|
|
except Exception as e:
|
|
return None
|
|
|
|
|
|
def user_prompt():
|
|
global always_yes
|
|
if always_yes:
|
|
return True
|
|
|
|
ans = input('Do you wish to proceed(y/n/a):')
|
|
if ans == 'a':
|
|
always_yes = True
|
|
return ans != 'n'
|
|
|
|
|
|
def ask_if_change(name, expected, command_read, command_set):
|
|
current = sh(command_read)
|
|
if current != expected:
|
|
print('%s Before:' % name)
|
|
print(current)
|
|
print('%s After:' % name)
|
|
print(expected)
|
|
if user_prompt():
|
|
sh(command_set)
|
|
print('Command %s succeeded' % command_set)
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
print('%s is already configured to TUNA mirrors' % name)
|
|
return True
|
|
|
|
|
|
def get_linux_distro():
|
|
os_release = sh('cat /etc/os-release')
|
|
if not os_release:
|
|
return None
|
|
match = re.findall(os_release_regex, os_release)
|
|
if len(match) != 1:
|
|
return None
|
|
return match[0]
|
|
|
|
|
|
def set_env(key, value):
|
|
shell = os.environ.get('SHELL').split('/')[-1]
|
|
if shell == 'bash' or shell == 'sh':
|
|
with open(os.path.expanduser('~/.profile'), 'a') as f:
|
|
f.write('export %s=%s\n' % (key, value))
|
|
elif shell == 'zsh':
|
|
with open(os.path.expanduser('~/.zprofile'), 'a') as f:
|
|
f.write('export %s=%s\n' % (key, value))
|
|
else:
|
|
print('Please set %s=%s' % (key, value))
|
|
|
|
|
|
def remove_env(key):
|
|
shell = os.environ.get('SHELL').split('/')[-1]
|
|
if shell == 'bash' or shell == 'sh':
|
|
pattern = "^export %s=" % key
|
|
profile = "~/.profile"
|
|
elif shell == 'zsh':
|
|
pattern = "^export %s=" % key
|
|
profile = "~/.zprofile"
|
|
if pattern:
|
|
profile = os.path.expanduser(profile)
|
|
if platform.system() == 'Darwin': # TODO: More BSD systems
|
|
sed = ['sed', '-i', "", "/%s/d" % pattern, profile]
|
|
else:
|
|
sed = ['sed', '-i', "/%s/d" % pattern, profile]
|
|
sh(sed)
|
|
return True
|
|
else:
|
|
print('Please remove environment variable %s' % key)
|
|
return False
|
|
|
|
|
|
def mkdir_p(path):
|
|
try:
|
|
os.makedirs(path)
|
|
except OSError as exc:
|
|
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
|
pass
|
|
else:
|
|
raise
|
|
|
|
|
|
|
|
class Base(object):
|
|
"""
|
|
Name of this mirror/module
|
|
"""
|
|
|
|
@staticmethod
|
|
def name():
|
|
raise NotImplementedError
|
|
|
|
"""
|
|
Returns whether this mirror is applicable
|
|
"""
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
return False
|
|
|
|
"""
|
|
Returns whether this mirror is already up
|
|
"""
|
|
|
|
@staticmethod
|
|
def is_online():
|
|
raise NotImplementedError
|
|
|
|
"""
|
|
Activate this mirror
|
|
Returns True if this operation is completed, False otherwise
|
|
Caller should never invoke this method when is_online returns True
|
|
"""
|
|
|
|
@staticmethod
|
|
def up():
|
|
raise NotImplementedError
|
|
|
|
"""
|
|
Deactivate this mirror
|
|
Returns True if this operation is completed, False otherwise
|
|
Caller should never invoke this method when is_online returns False
|
|
"""
|
|
|
|
@staticmethod
|
|
def down():
|
|
raise NotImplementedError
|
|
|
|
"""
|
|
Print a log entry with the name of this mirror/module
|
|
"""
|
|
|
|
@classmethod
|
|
def log(cls, msg, level='i'):
|
|
levels = "viodwe" # verbose, info, ok, debug, warning, error
|
|
assert level in levels
|
|
|
|
global verbose
|
|
if level == 'v' and verbose:
|
|
return
|
|
|
|
color_prefix = {
|
|
'v': '',
|
|
'i': '',
|
|
'o': '\033[32m',
|
|
'd': '\033[34m',
|
|
'w': '\033[33m',
|
|
'e': '\033[31m'
|
|
}
|
|
if color_prefix[level]:
|
|
color_suffix = '\033[0m'
|
|
else:
|
|
color_suffix = ''
|
|
|
|
print('%s[%s]: %s%s' % (color_prefix[level], cls.name(), msg, color_suffix))
|
|
|
|
|
|
class Pypi(Base):
|
|
mirror_url = 'https://pypi.%s/simple' % host_name
|
|
|
|
"""
|
|
Reference: https://pip.pypa.io/en/stable/user_guide/#configuration
|
|
"""
|
|
@staticmethod
|
|
def config_files():
|
|
system = platform.system()
|
|
if system == 'Darwin':
|
|
return ('$HOME/Library/Application Support/pip/pip.conf', '$HOME/.pip/pip.conf')
|
|
elif system == 'Windows':
|
|
return (r'%APPDATA%\pip\pip.ini', r'~\pip\pip.ini')
|
|
elif system == 'Linux':
|
|
return ('$HOME/.config/pip/pip.conf', '$HOME/.pip/pip.conf')
|
|
|
|
|
|
@staticmethod
|
|
def name():
|
|
return "pypi"
|
|
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
global is_global
|
|
if is_global:
|
|
# Skip if in global mode
|
|
return False
|
|
return sh('pip') is not None or sh('pip3') is not None
|
|
|
|
|
|
@staticmethod
|
|
def is_online():
|
|
pattern = re.compile(r' *index-url *= *%s' % Pypi.mirror_url)
|
|
config_files = Pypi.config_files()
|
|
for conf_file in config_files:
|
|
if not os.path.exists(os.path.expandvars(conf_file)):
|
|
continue
|
|
with open(os.path.expandvars(conf_file)) as f:
|
|
for line in f:
|
|
if pattern.match(line):
|
|
return True
|
|
return False
|
|
|
|
|
|
@staticmethod
|
|
def up():
|
|
config_file = os.path.expandvars(Pypi.config_files()[0])
|
|
config = configparser.ConfigParser()
|
|
if os.path.exists(config_file):
|
|
config.read(config_file)
|
|
if not config.has_section('global'):
|
|
config.add_section('global')
|
|
if not os.path.isdir(os.path.dirname(config_file)):
|
|
mkdir_p(os.path.dirname(config_file))
|
|
config.set('global', 'index-url', Pypi.mirror_url)
|
|
with open(config_file, 'w') as f:
|
|
config.write(f)
|
|
return True
|
|
|
|
|
|
@staticmethod
|
|
def down():
|
|
config_files = map(os.path.expandvars, Pypi.config_files())
|
|
config = configparser.ConfigParser()
|
|
for path in config_files:
|
|
if not os.path.exists(path):
|
|
continue
|
|
config.read(path)
|
|
try:
|
|
if config.get('global', 'index-url') == Pypi.mirror_url:
|
|
config.remove_option('global', 'index-url')
|
|
with open(path, 'w') as f:
|
|
config.write(f)
|
|
except (configparser.NoOptionError, configparser.NoSectionError):
|
|
pass
|
|
return True
|
|
|
|
|
|
class ArchLinux(Base):
|
|
@staticmethod
|
|
def name():
|
|
return 'Arch Linux'
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
global is_global
|
|
if not is_global:
|
|
return False
|
|
return os.path.isfile(
|
|
'/etc/pacman.d/mirrorlist') and get_linux_distro() == 'arch'
|
|
|
|
@staticmethod
|
|
def is_online():
|
|
mirror_re = re.compile(
|
|
r" *Server *= *(http|https)://%s/archlinux/\$repo/os/\$arch\n" %
|
|
mirror_root, re.M)
|
|
ml = open('/etc/pacman.d/mirrorlist', 'r')
|
|
lines = ml.readlines()
|
|
result = map(lambda l: re.match(mirror_re, l), lines)
|
|
result = any(result)
|
|
ml.close()
|
|
return result
|
|
|
|
@staticmethod
|
|
def up():
|
|
# Match commented or not
|
|
mirror_re = re.compile(
|
|
r" *(# *)?Server *= *(http|https)://%s/archlinux/\$repo/os/\$arch\n"
|
|
% mirror_root, re.M)
|
|
banner = '# Generated and managed by the awesome oh-my-tuna\n'
|
|
target = "Server = https://%s/archlinux/$repo/os/$arch\n\n" % mirror_root
|
|
|
|
print(
|
|
'This operation will insert the following line into the beginning of your pacman mirrorlist:\n%s'
|
|
% target[:-2])
|
|
if not user_prompt():
|
|
return False
|
|
|
|
ml = open('/etc/pacman.d/mirrorlist', 'r')
|
|
lines = ml.readlines()
|
|
|
|
# Remove all
|
|
lines = filter(lambda l: re.match(mirror_re, l) is None, lines)
|
|
|
|
# Remove banner
|
|
lines = filter(lambda l: l != banner, lines)
|
|
|
|
# Finish reading
|
|
lines = list(lines)
|
|
|
|
# Remove padding newlines
|
|
k = 0
|
|
while k < len(lines) and lines[k] == '\n':
|
|
k += 1
|
|
|
|
ml.close()
|
|
ml = open('/etc/pacman.d/mirrorlist', 'w')
|
|
# Add target
|
|
ml.write(banner)
|
|
ml.write(target)
|
|
ml.writelines(lines[k:])
|
|
ml.close()
|
|
return True
|
|
|
|
@staticmethod
|
|
def down():
|
|
print(
|
|
'This action will comment out TUNA mirrors from your pacman mirrorlist, if there is any.'
|
|
)
|
|
if not user_prompt():
|
|
return False
|
|
|
|
# Simply remove all matched lines
|
|
mirror_re = re.compile(
|
|
r" *Server *= *(http|https)://%s/archlinux/\$repo/os/\$arch\n" %
|
|
mirror_root, re.M)
|
|
|
|
ml = open('/etc/pacman.d/mirrorlist', 'r')
|
|
lines = ml.readlines()
|
|
lines = list(
|
|
map(lambda l: l if re.match(mirror_re, l) is None else '# ' + l,
|
|
lines))
|
|
ml.close()
|
|
ml = open('/etc/pacman.d/mirrorlist', 'w')
|
|
ml.writelines(lines)
|
|
ml.close()
|
|
return True
|
|
|
|
|
|
class Homebrew(Base):
|
|
@staticmethod
|
|
def name():
|
|
return 'Homebrew'
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
global is_global
|
|
if not is_global:
|
|
return False
|
|
return sh('brew --repo') is not None
|
|
|
|
@staticmethod
|
|
def is_online():
|
|
repo = sh('brew --repo')
|
|
with cd(repo):
|
|
repo_online = sh('git remote get-url origin'
|
|
) == 'https://%s/git/homebrew/brew.git' % mirror_root
|
|
if repo_online:
|
|
return os.environ.get('HOMEBREW_BOTTLE_DOMAIN') == 'https://%s/homebrew-bottles' % mirror_root
|
|
return False
|
|
|
|
|
|
@staticmethod
|
|
def up():
|
|
repo = sh('brew --repo')
|
|
with cd(repo):
|
|
ask_if_change(
|
|
'Homebrew repo',
|
|
'https://%s/git/homebrew/brew.git' % mirror_root,
|
|
'git remote get-url origin',
|
|
'git remote set-url origin https://%s/git/homebrew/brew.git' %
|
|
mirror_root)
|
|
for tap in ('homebrew-core', 'homebrew-python', 'homebrew-science'):
|
|
tap_path = '%s/Library/Taps/homebrew/%s' % (repo, tap)
|
|
if os.path.isdir(tap_path):
|
|
with cd(tap_path):
|
|
ask_if_change(
|
|
'Homebrew tap %s' % tap,
|
|
'https://%s/git/homebrew/%s.git' % (mirror_root, tap),
|
|
'git remote get-url origin',
|
|
'git remote set-url origin https://%s/git/homebrew/%s.git'
|
|
% (mirror_root, tap))
|
|
set_env('HOMEBREW_BOTTLE_DOMAIN', 'https://%s/homebrew-bottles' % mirror_root)
|
|
return True
|
|
|
|
@staticmethod
|
|
def down():
|
|
repo = sh('brew --repo')
|
|
with cd(repo):
|
|
sh('git remote set-url origin https://github.com/homebrew/brew.git'
|
|
)
|
|
for tap in ('homebrew-core', 'homebrew-python',
|
|
'homebrew-science'):
|
|
tap_path = '%s/Library/Taps/homebrew/%s' % (repo, tap)
|
|
if os.path.isdir(tap_path):
|
|
with cd(tap_path):
|
|
sh('git remote set-url origin https://github.com/homebrew/%s.git'
|
|
% tap)
|
|
sh('git remote get-url origin'
|
|
) == 'https://github.com/homebrew/brew.git'
|
|
return remove_env('HOMEBREW_BOTTLE_DOMAIN')
|
|
|
|
|
|
class CTAN(Base):
|
|
@staticmethod
|
|
def name():
|
|
return 'CTAN'
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
# Works both in global mode or local mode
|
|
return sh('tlmgr --version') is not None
|
|
|
|
@staticmethod
|
|
def is_online():
|
|
global is_global
|
|
base = "tlmgr"
|
|
if not is_global:
|
|
# Setup usertree first
|
|
sh("tlmgr init-usertree")
|
|
base += " --usermode"
|
|
|
|
return sh(
|
|
'%s option repository' % base
|
|
) == 'Default package repository (repository): https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/tlnet'
|
|
|
|
@staticmethod
|
|
def up():
|
|
global is_global
|
|
base = "tlmgr"
|
|
if not is_global:
|
|
base += " --usermode"
|
|
|
|
return ask_if_change(
|
|
'CTAN mirror',
|
|
'Default package repository (repository): https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/tlnet',
|
|
'%s option repository' % base,
|
|
'%s option repository https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/tlnet' % base
|
|
)
|
|
|
|
|
|
class Anaconda(Base):
|
|
url_free = 'https://%s/anaconda/pkgs/free/' % mirror_root
|
|
url_main = 'https://%s/anaconda/pkgs/main/' % mirror_root
|
|
|
|
|
|
@staticmethod
|
|
def name():
|
|
return "Anaconda"
|
|
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
# Works both in global mode and local mode
|
|
return sh('conda -V') is not None
|
|
|
|
|
|
@staticmethod
|
|
def is_online():
|
|
cmd = 'conda config --get channels'
|
|
global is_global
|
|
if is_global:
|
|
cmd += ' --system'
|
|
|
|
channels = sh(cmd).split('\n')
|
|
in_channels = 0
|
|
for line in channels:
|
|
if Anaconda.url_free in line:
|
|
in_channels += 1
|
|
elif Anaconda.url_main in line:
|
|
in_channels += 1
|
|
return in_channels == 2
|
|
|
|
|
|
@staticmethod
|
|
def up():
|
|
basecmd = 'conda config'
|
|
global is_global
|
|
if is_global:
|
|
basecmd += ' --system'
|
|
sh ("%s --add channels %s" % (basecmd, Anaconda.url_free))
|
|
sh ("%s --add channels %s" % (basecmd, Anaconda.url_main))
|
|
return True
|
|
|
|
@staticmethod
|
|
def down():
|
|
basecmd = 'conda config'
|
|
global is_global
|
|
if is_global:
|
|
basecmd += ' --system'
|
|
sh ("%s --remove channels %s" % (basecmd, Anaconda.url_free))
|
|
sh ("%s --remove channels %s" % (basecmd, Anaconda.url_main))
|
|
return True
|
|
|
|
|
|
class Debian(Base):
|
|
pools = "main contrib non-free"
|
|
default_sources = {
|
|
'http://deb.debian.org/debian': ['', '-updates'],
|
|
'http://security.debian.org/debian-security': ['main', 'contrib', 'non-free'],
|
|
}
|
|
|
|
@staticmethod
|
|
def build_mirrorspec():
|
|
return {
|
|
'https://' + mirror_root + '/debian': ['', '-updates'],
|
|
'https://' + mirror_root + '/debian-security': ['/updates'],
|
|
}
|
|
|
|
@classmethod
|
|
def build_template(cls, mirrorspecs):
|
|
release = sh('lsb_release -sc')
|
|
lines = ['%s %s %s%s %s\n' % (repoType, mirror, release, repo, cls.pools)
|
|
for mirror in mirrorspecs
|
|
for repo in mirrorspecs[mirror]
|
|
for repoType in ['deb', 'deb-src']]
|
|
tmpl = ''.join(lines)
|
|
return tmpl
|
|
|
|
@staticmethod
|
|
def name():
|
|
return 'Debian'
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
global is_global
|
|
if not is_global:
|
|
return False
|
|
return os.path.isfile(
|
|
'/etc/apt/sources.list') and get_linux_distro() == 'debian'
|
|
|
|
@classmethod
|
|
def is_online(cls):
|
|
with open('/etc/apt/sources.list', 'r') as sl:
|
|
content = sl.read();
|
|
return content == cls.build_template(cls.build_mirrorspec())
|
|
|
|
@classmethod
|
|
def up(cls):
|
|
print('This operation will move your current sources.list to sources.oh-my-tuna.bak.list,\n' + \
|
|
'and use TUNA apt source instead.')
|
|
if not user_prompt():
|
|
return False
|
|
if os.path.isfile('/etc/apt/sources.list'):
|
|
sh('cp /etc/apt/sources.list /etc/apt/sources.oh-my-tuna.bak.list')
|
|
with open('/etc/apt/sources.list', 'w') as sl:
|
|
sl.write(cls.build_template(cls.build_mirrorspec()))
|
|
return True
|
|
|
|
@classmethod
|
|
def down(cls):
|
|
print('This operation will copy sources.oh-my-tuna.bak.list to sources.list if there is one,\n' + \
|
|
'otherwise build a new sources.list with archive.ubuntu.com as its mirror root.')
|
|
if not user_prompt():
|
|
return False
|
|
if os.path.isfile('/etc/apt/sources.oh-my-tuna.bak.list'):
|
|
if sh('cp /etc/apt/sources.oh-my-tuna.bak.list /etc/apt/sources.list') is not None:
|
|
return True
|
|
with open('/etc/apt/sources.list', 'w') as sl:
|
|
sl.write(cls.build_template(cls.default_sources))
|
|
return True
|
|
|
|
|
|
def _get_mirror_suffix():
|
|
uname = sh('uname -m')
|
|
no_suffix_list = ['i386', 'i586', 'i686', 'x86_64', 'amd64']
|
|
if any(map(lambda x: x in uname, no_suffix_list)):
|
|
return ''
|
|
else:
|
|
return '-ports'
|
|
|
|
|
|
class Ubuntu(Debian):
|
|
default_sources = { 'http://archive.ubuntu.com/ubuntu' + _get_mirror_suffix(): ['', '-updates', '-security', '-backports'] }
|
|
pools = "main multiverse universe restricted"
|
|
|
|
@staticmethod
|
|
def build_mirrorspec():
|
|
return {
|
|
'https://' + mirror_root + '/ubuntu' + _get_mirror_suffix(): ['', '-updates', '-security', '-backports'],
|
|
}
|
|
|
|
@staticmethod
|
|
def name():
|
|
return 'Ubuntu'
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
global is_global
|
|
if not is_global:
|
|
return False
|
|
return os.path.isfile(
|
|
'/etc/apt/sources.list') and get_linux_distro() == 'ubuntu'
|
|
|
|
|
|
class CentOS(Base):
|
|
@staticmethod
|
|
def name():
|
|
return 'CentOS'
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
global is_global
|
|
if not is_global:
|
|
return False
|
|
return os.path.isfile(
|
|
'/etc/yum.repos.d/CentOS-Base.repo') and get_linux_distro() == 'centos'
|
|
|
|
@staticmethod
|
|
def is_online():
|
|
mirror_re = re.compile(
|
|
r"baseurl=https://%s/centos/\$releasever/os/\$basearch/\n" %
|
|
mirror_root, re.M)
|
|
ml = open('/etc/yum.repos.d/CentOS-Base.repo', 'r')
|
|
lines = ml.readlines()
|
|
result = map(lambda l: re.match(mirror_re, l), lines)
|
|
result = any(result)
|
|
ml.close()
|
|
return result
|
|
|
|
@staticmethod
|
|
def up():
|
|
sh('cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak')
|
|
sh(r'sed -i -E s/^#?baseurl=https?:\/\/[^\/]+\/(.*)$/baseurl=https:\/\/%s\/\1/g /etc/yum.repos.d/CentOS-Base.repo' % mirror_root.replace('/', r'\/'))
|
|
sh(r'sed -i -E s/^(mirrorlist=.*)$/#\1/g /etc/yum.repos.d/CentOS-Base.repo')
|
|
|
|
return True
|
|
|
|
@staticmethod
|
|
def down():
|
|
if os.path.isfile('/etc/yum.repos.d/CentOS-Base.repo.bak'):
|
|
sh('cp /etc/yum.repos.d/CentOS-Base.repo.bak /etc/yum.repos.d/CentOS-Base.repo')
|
|
return True
|
|
|
|
|
|
sh(r'sed -i -E s/^#(mirrorlist=.*)$/\1/g /etc/yum.repos.d/CentOS-Base.repo')
|
|
sh(r'sed -i -E s/^(baseurl=.*)$/#\1/g /etc/yum.repos.d/CentOS-Base.repo')
|
|
return True
|
|
|
|
|
|
class AOSCOS(Base):
|
|
@staticmethod
|
|
def name():
|
|
return 'AOSC OS'
|
|
|
|
@staticmethod
|
|
def is_applicable():
|
|
global is_global
|
|
if not is_global:
|
|
return False
|
|
return os.path.isfile(
|
|
'/var/lib/apt/gen/status.json') and get_linux_distro() == 'aosc'
|
|
|
|
@staticmethod
|
|
def is_online():
|
|
agl_result = sh('env LC_ALL=C apt-gen-list now')
|
|
if not agl_result:
|
|
return None
|
|
out_re = re.compile(r"mirrors:.* tuna.*", re.M)
|
|
match = re.findall(out_re, agl_result)
|
|
return len(match) > 0
|
|
|
|
@staticmethod
|
|
def up():
|
|
agl_result = sh('env LC_ALL=C apt-gen-list now')
|
|
if not agl_result:
|
|
return False
|
|
out_re = re.compile(r"mirrors: origin$", re.M)
|
|
match = re.findall(out_re, agl_result)
|
|
if len(match) > 0:
|
|
if not sh('env LC_ALL=C apt-gen-list m tuna'):
|
|
return False
|
|
else:
|
|
if not sh('env LC_ALL=C apt-gen-list m +tuna'):
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def down():
|
|
if not sh('env LC_ALL=C apt-gen-list m -tuna'):
|
|
return False
|
|
return True
|
|
|
|
|
|
MODULES = [ArchLinux, Homebrew, CTAN, Pypi, Anaconda, Debian, Ubuntu, CentOS, AOSCOS]
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description='Use TUNA mirrors everywhere when applicable')
|
|
parser.add_argument(
|
|
'subcommand',
|
|
nargs='?',
|
|
metavar='SUBCOMMAND',
|
|
choices=['up', 'down', 'status'],
|
|
default='up')
|
|
parser.add_argument(
|
|
'-v', '--verbose', help='verbose output', action='store_true')
|
|
parser.add_argument(
|
|
'-y',
|
|
'--yes',
|
|
help='always answer yes to questions',
|
|
action='store_true')
|
|
parser.add_argument(
|
|
'-g',
|
|
'--global',
|
|
dest='is_global',
|
|
help='apply system-wide changes. This option may affect applicability of some modules.',
|
|
action='store_true')
|
|
|
|
args = parser.parse_args()
|
|
global verbose
|
|
verbose = args.verbose
|
|
global always_yes
|
|
always_yes = args.yes
|
|
global is_global
|
|
is_global = args.is_global
|
|
|
|
if args.subcommand == 'up':
|
|
for m in MODULES:
|
|
if m.is_applicable():
|
|
if not m.is_online():
|
|
m.log('Activating...')
|
|
try:
|
|
result = m.up()
|
|
if not result:
|
|
m.log('Operation canceled', 'w')
|
|
else:
|
|
m.log('Mirror has been activated', 'o')
|
|
except NotImplementedError:
|
|
m.log(
|
|
'Mirror doesn\'t support activation. Please activate manually'
|
|
, 'e')
|
|
|
|
if args.subcommand == 'down':
|
|
for m in MODULES:
|
|
if m.is_applicable():
|
|
if m.is_online():
|
|
m.log('Deactivating...')
|
|
try:
|
|
result = m.down()
|
|
if not result:
|
|
m.log('Operation canceled', 'w')
|
|
else:
|
|
m.log('Mirror has been deactivated', 'o')
|
|
except NotImplementedError:
|
|
m.log(
|
|
'Mirror doesn\'t support deactivation. Please deactivate manually'
|
|
, 'e')
|
|
|
|
if args.subcommand == 'status':
|
|
for m in MODULES:
|
|
if m.is_applicable():
|
|
if m.is_online():
|
|
m.log('Online', 'o')
|
|
else:
|
|
m.log('Offline')
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|