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

    Сегодня неожиданно понял, что скрипты — это сила (спустя несколько месяцев использования 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(c_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 c_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 уж очень вскользь упоминается на страницах нашего любимого журнала…
    • +24
    • 21,3k
    • 7
    Поделиться публикацией

    Комментарии 7

      +1
      >git add.
      иногда нужен ключик --all (-A)
        0
        Ну это по потребностям — каждый подгонит под себя. Можно вообще через командную строку все аргументы принимать с дефолтом под наиболее частое применение. Я сейчас делаю так: если что-то пришлось выполнить более трёх раз — добавляю возможность делать это через скрипт, а штучные команды пишу в консоли. Ключик --all пока мне не приходилось использовать ни разу — вот его и нет…
        0
        Прежде всего поставьте fabric и, впредь, ставьте её везде (если, вдруг, Вы ещё так не делаете)!

        А еще, ставьте везде MS-DOS даже если он вам не нужен.
        Ну правда, аргументируйте как-то, почему все Python-разработчики должны уставить этот модуль.
          0
          Нижеследующая статья как бы и есть аргумент…
          0
          Еще для подобных задач хорошо зарекомендовал себя pexpect
            0
            Спасибо за ссылку. На первый взгляд pexpect более «низкоуровневый» (что, несомненно, также может пригодиться), fabric же скрывает множество нюансов позволяя просто писать строчки команд, определяя на какой машине они будут исполняться (локально или где-то на сервере через SSH). Т.е. Если нужно просто выполнить скрипт из набора команд — fabric позволяет не писать рутинных функций.
            0
            Комментировать/раскомментировать строки в settings.py — скорее антипаттерн.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое