add configure_tuna
This commit is contained in:
parent
d3dc6a5860
commit
82222fd071
816
files/oh-my-tuna.py
Normal file
816
files/oh-my-tuna.py
Normal file
@ -0,0 +1,816 @@
|
|||||||
|
#!/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()
|
||||||
13
init_root.sh
13
init_root.sh
@ -38,7 +38,7 @@ check_google_access() {
|
|||||||
configure_ssh() {
|
configure_ssh() {
|
||||||
[ -f ~/.ssh/authorized_keys ] && return
|
[ -f ~/.ssh/authorized_keys ] && return
|
||||||
|
|
||||||
read -p "要配置 .ssh 吗?[Y]: " response
|
read -p "要配置 authorized_keys 吗?[Y]: " response
|
||||||
|
|
||||||
if [[ -z "$response" ]] || [[ $response =~ ^[Yy]$ ]]; then
|
if [[ -z "$response" ]] || [[ $response =~ ^[Yy]$ ]]; then
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
@ -80,6 +80,16 @@ configure_vim() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure_tuna() {
|
||||||
|
[ $abroad -eq 1 ] && return
|
||||||
|
|
||||||
|
read -p "要切换 Tuna 源吗?[N]: " response
|
||||||
|
|
||||||
|
if [[ $response =~ ^[Yy]$ ]]; then
|
||||||
|
python3 $scriptdir/files/oh-my-tuna.py
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
configure_apt_upgrade() {
|
configure_apt_upgrade() {
|
||||||
read -p "要运行 apt upgrade 吗?[N]: " response
|
read -p "要运行 apt upgrade 吗?[N]: " response
|
||||||
|
|
||||||
@ -200,6 +210,7 @@ configure_ssh
|
|||||||
configure_ssh_keygen
|
configure_ssh_keygen
|
||||||
configure_nano
|
configure_nano
|
||||||
configure_vim
|
configure_vim
|
||||||
|
configure_tuna
|
||||||
configure_apt_upgrade
|
configure_apt_upgrade
|
||||||
configure_apt_install
|
configure_apt_install
|
||||||
configure_zsh
|
configure_zsh
|
||||||
|
|||||||
14
init_sudo.sh
14
init_sudo.sh
@ -38,7 +38,7 @@ check_google_access() {
|
|||||||
configure_ssh() {
|
configure_ssh() {
|
||||||
[ -f ~/.ssh/authorized_keys ] && return
|
[ -f ~/.ssh/authorized_keys ] && return
|
||||||
|
|
||||||
read -p "要配置 .ssh 吗?[Y]: " response
|
read -p "要配置 authorized_keys 吗?[Y]: " response
|
||||||
|
|
||||||
if [[ -z "$response" ]] || [[ $response =~ ^[Yy]$ ]]; then
|
if [[ -z "$response" ]] || [[ $response =~ ^[Yy]$ ]]; then
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
@ -80,6 +80,17 @@ configure_vim() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure_tuna() {
|
||||||
|
[ $abroad -eq 1 ] && return
|
||||||
|
|
||||||
|
read -p "要切换 Tuna 源吗?[N]: " response
|
||||||
|
|
||||||
|
if [[ $response =~ ^[Yy]$ ]]; then
|
||||||
|
python3 $scriptdir/files/oh-my-tuna.py
|
||||||
|
sudo python3 $scriptdir/files/oh-my-tuna.py
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
configure_apt_upgrade() {
|
configure_apt_upgrade() {
|
||||||
read -p "要运行 apt upgrade 吗?[N]: " response
|
read -p "要运行 apt upgrade 吗?[N]: " response
|
||||||
|
|
||||||
@ -200,6 +211,7 @@ configure_ssh
|
|||||||
configure_ssh_keygen
|
configure_ssh_keygen
|
||||||
configure_nano
|
configure_nano
|
||||||
configure_vim
|
configure_vim
|
||||||
|
configure_tuna
|
||||||
configure_apt_upgrade
|
configure_apt_upgrade
|
||||||
configure_apt_install
|
configure_apt_install
|
||||||
configure_zsh
|
configure_zsh
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user