Pull to refresh

Зачем и как мы бэкапим github

Reading time3 min
Views19K


Позвольте мне для начала немного пофилософствовать на тему технологий. Технологии позволяют нам концентрироваться на результате, на конечной цели, дают ощущение контроля. Вот вы в белоснежном кителе на мостике своего технологичного лайнера выходите в очередной плавание. Ваш лайнер снаряжен всем необходимым, чтобы противостоять любой проблеме. Вам не страшны волны, айсберги и даже пьяные боцманы.

Вообще лирическое вступление было навеяно вполне конкретной историей про сломанный гитхаб. Сделанная на заре одного из проектов синхронизация домашнего репо в гитхаб решила проблему переезда. Потом про костыль забыли. Древнее зло уснуло и терпеливо ждало своего часа. В один прекрасный день скайнет новый сотрудник решил привести в порядок тот самый домашний репозиторий. И самым популярным вопросом среди программистов в тот день было «коллега, а вы не видели мою ветку 0022? ну такая, с багфиксами». Руководство опс-тим было спокойно как никогда: гит — это распределенная система, версия кода хранится на персональном компьютере каждого разработчика. Давайте уже как-нибудь разберитесь между собой и не отвлекайте нас от сборки наших ядер и тюнингов сетевых стеков.

И все же зачем..?

Действительно, можно нафантазировать большое количество количество возможных проблем, например:

  • Недоступность удаленного репозитория и отсутсвие актуальной локальной копии
  • Злоумышленники с помощью украденного пароля испортили/удалили репозитории
  • Ошибки в манипуляциях с репозиториями


Итак, что и как бэкапить...

Вот вкратце наш хитрый план действий:

  1. Получаем список репозиториев для организации
  2. Клонируем репозитории поз полученного списка
  3. Архивируем
  4. Кладем в AWS S3


Немного больше конкретики в случае использования github.com


Разумно завести для процедуры бэкапа отдельного readonly-пользователя. Так же необходимо сгенерировать для него token(Settings -> Personal Access Tokens -> Generate new token).

Для начала с помощью pygithub3 получаем репозитории, которые мы впоследствии собираемся бэкапить:
from pygithub3 import Github

def get_repos(args):
    config = {'token': args.token}
    gh = Github(**config)

    return gh.repos.list_by_org(args.organization).all()


Для клонирования будем использовать консольный git:
def clone_repo(repo_list,args):

    if os.path.isdir(args.directory):
        shutil.rmtree(args.directory)

    os.mkdir(args.directory)

    if args.mirror is True:
        args.git += " --mirror"

    for repo in repo_list:
        repo_url = "https://%(token)s:x-oauth-basic@github.com/%(organization)s/%(repo)s.git" % {'token': args.token,
                                                                                                 'organization': args.organization,
                                                                                                 'repo': repo.name}

        os.system('git clone %(arguments)s %(repo_url)s %(directory)s/%(repo)s' % {'arguments': args.git,
                                                                                   'repo_url': repo_url,
                                                                                   'directory': args.directory, 'repo': repo.name})

Обратите внимание на опцию "--mirror" — с помощью нее создается зеркальная копия удаленного репозитория.

Кстати, в случае использования bitbucket.org...


Получаем список репозиториев:
def _get_repositories(owner, username, password):
    auth_value = ('%s:%s' % (username, password)).encode('base64').strip()
    headers = {'Authorization': 'Basic %s' % auth_value}
    url = 'https://bitbucket.org/api/2.0/repositories/%s?role=member' % owner
    values = []

    while url is not None:
        request = urllib2.Request(url, None, headers)
        data = json.loads(urllib2.urlopen(request).read())
        values = values + data['values']
        url = data.get('next')

    return values


И клонируем:
def _git_clone(username, password, directory, sub_dir_name, owner, slug, verbose=False):
    os.chdir(directory)
    cmd = 'git clone --mirror https://%s:%s@bitbucket.org/%s/%s.git %s' % (username, password,
                                                                  owner, slug, sub_dir_name)
    proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
    ret_value = proc.wait()
    msg = proc.stdout.read()
    sys.stdout.write('%s%s%s%s' % (sub_dir_name, os.linesep,
                                   '=' * len(sub_dir_name), os.linesep))
    sys.stdout.write("%s%s" % (msg, os.linesep))
    return ret_value

Кстати, slug — это url-friendly название вашего репо в bitbucket.

Готовый скрипт для github можно найти тут.
Tags:
Hubs:
Total votes 17: ↑12 and ↓5+7
Comments24

Articles