Fabric — пара прикладных рецептов

в 18:08, , рубрики: development, fabric, Git, python, метки: , , ,

Сегодня неожиданно понял, что скрипты — это сила (спустя несколько месяцев использования fabric). На самом деле 30 минут потраченные на написание адекватного сценария избавляют от многих совокупных часов повторения ненужных действий. Для упрощения жизни адептов python'а существует такой прекрасный модуль как fabric. И я хочу поделиться парой кусков своего fab-файла как пример упрощения жизни девелопера.

Это будут функции: «умный» комментатор локальных файлов и git-коммитер.

Прежде всего поставьте fabric и, впредь, ставьте её везде (если, вдруг, Вы ещё так не делаете)!

pip install fabric

Коммитер

Ничего сверхъестественного — просто удобный рецепт:

# -*- coding: utf-8 -*-

from fabric.api import local, prompt, settings

env.test_branch = 'test'
env.dev_branch = 'dev'


def commit():
    """
    Коммит изменений.
    """
    with settings(warn_only=True):
        local('git status')
        prompt('Press <Enter> to continue or <Ctrl+C> to cancel.')  # тут можно прервать коммит, 
            # если в него попали ненужные файлы или наоборот
        local('git add .')
        local('git commit')  # тут вылазит консольный редактор и можно ввести комментарий


def merge_dev_to_test(with_return=True):
    """
    Слияние изменений из разработки в тестовую ветку.
    """
    local('git checkout %s' % env.test_branch)  # переход в тестовую ветку
    local('git merge --no-ff %s' % env.dev_branch)  # слияние с веткой разработки

    if with_return:
        local('git checkout %s' % env.dev_branch)  # опциональное возвращение назад


def to_test()
    """
    Коммит изменений в тестовую ветку.
    """
    commit()
    merge_dev_to_test()

Функция merge_dev_to_test очень прикладная и её можно легко универсализировать.

Собственно этот скриптик заменяет ввод 6 команд, на ввод одной fab to_test и ввод комментария в редакторе. При этом он даёт предпросмотр — какие изменения произойдут в бранче и сливает наработки из разработческой ветки в тестовую. Если слияние не нужно — просто выполняем fab commit.

Комментер

Я использую его как переключатель вариантов настроек Django.

Отвлекаясь, сразу хочу сказать, что это не единственно верный способ использования разных настроек django для разных ситуаций. Это просто один из них. Другие варианты можно посмотреть здесь.

# -*- coding: utf-8 -*-

from os import path
from fabric.api import env, local


env.settings_files = (
    path.join('projcet_root', 'settings.py'),
    path.join('projcet_root', 'module', 'settings.py'),
)
env.settings_versions = {
    'develop': '#-D',
    'test': '#-T',
    'production': '#-P',
}

def _commenter(type, filename, regex, use_sudo=False, char='#', backup='.bak'):
    """
    Обработка комментариев в локальных файлах.
    """
    if use_sudo:
        sudoer = 'sudo '
    else:
        sudoer = ''

    if regex.startswith('^'):
        regex = regex[1:]

    if regex.endswith('$'):
        regex = regex[:-1]

    if type == 'comment':
        replacement = '%s ' % char
        char = '[^%s ]' % char
        regex = '(%s.+%s.*)' % (char, regex)
    else:
        replacement = ''
        regex = r'%s ?(.+%s.*)' % (char, regex)

    local(r"{sudo}sed -i{backup} -r -e 's/^([[:space:]]*){regex}$/"
        r"1{replacement}2/g' {filename}".format(**{
        'sudo': sudoer,
        'backup': backup,
        'replacement': replacement,
        'regex': regex,
        'filename': filename,
    }))


def lcomment(*args, **kwargs):
    """
    Комментирование.
    """
    _commenter('comment', *args, **kwargs)


def luncomment(*args, **kwargs):
    """
    Разкомментирование.
    """
    _commenter('uncomment', *args, **kwargs)


def update_settings(mode):
    """
    Изменение настроек.
    """
    for filename in env.settings_files:
        for version in env.settings_versions:
            if mode == version:
                luncomment(filename, versions[version])
            elif:
                lcomment(filename, versions[version])

Код также доступен в гисте.

В самом fabric есть модуль contrib.files с функцией comment которую, по большому счёту, и повторяет «комментер» с той разницей, что работает локально, и комментарии ставит не в начало строки, а перед текстом вставляя между символом комментария и текстом пробел (как это делает sublime text) и ищет маркер только в конце строки.

Что он умеет. По команде fab update_settings:mode=<режим> он у меет обходить файлы из списка env.settings_files по дороге раскомментируя строки с маркером соответствующим режиму и комментируя с маркером не соответствующим.

Т.е. вызов fab update_settings:mode=test пробежится по файлам /project_root/settings.py и /project_root/module/settings.py комментируя строки, заканчивающиеся на '#-D' и '#-P' и раскомментируя заканчивающиеся на '#-T'.

Что позволяет писать конфигурации типа:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dvl',  # #-D
        # 'NAME': 'prd',  # #-P
        # 'NAME': 'tst',  # #-T
        'USER': 'ruutt',
        'PASSWORD': 'gigopasswort',
        'CHARSET': 'UTF8',
    },
}

…разделяя их на стадии коммита:

def deploy(branch):
    env.test_branch =branch  # заменили тестовый бранч на нужный
    commit(False)  # коммитим и мержим в нужный бранч, оставаясь в нём
    update_settings(branch)  # в нужном бранче меняем настройки на нужные
    # тут можно ещё покольдовать, закоммитить, запушить и т.д.
    local('git checkout %s' % env.dev_branch)  # возвращаемся в бранч разработки
    prompt("OK. Press any <Enter> to exit.")  # если вдруг из IDE раскрывается окно с консолью -
    # эта строчка не даст ему закрыться и унести с собой результаты успешного исполнения.

А воспользоваться всей это мешаниной можно так fab deploy:branch=test. Считать сколько команд в консоли и времени на них потраченного будет сэкономлено я оставляю Вам.

Вот такой вот наработкой решил поделиться. А то как-то fabric уж очень вскользь упоминается на страницах нашего любимого журнала…

Автор: qnub

Поделиться

* - обязательные к заполнению поля