Fabric как новый Bash: проверка ДЗ в OTUS

WebDev_Deep_26.7_site-5020-cb21df.png

Чаще всего Fabric вспоминают, когда речь идёт об автоматизации деплоя и работы с удалёнными нодами. Но на самом деле он так же прекрасно справляется и с автоматизацией локальных задач. Он удобнее обычного баша тем, что в нём есть вся гибкость Python, и круче чистого Python множеством готовых админских функций.

Лично я пользуюсь им для локальной автоматизации довольно часто. Один из примеров — проверка домашних заданий в OTUS. С его помощью я ускоряю проверку стилей и статического анализа кода.

Давайте посмотрим, как это выглядит

Прежде всего, у меня создана папка ~/otus/homeworks/, в которой лежит fabfile.py и виртуальное окружение otus с установленными и настроенными линтерами.

Работаем мы локально, так что нам нужна функция local из состава Fabric.

import os
from fabric.api import local, lcd, task, settings

Поскольку ДЗ студенты сдают в виде git-репозиториев, мне нужна функция, которая будет клонировать их в локальные папки.

def prepare_repo(repo, pull=True):
   project_path = repo.split(':')[1]
   if project_path.endswith('.git'):
       project_path = project_path[:-4]
   if not os.path.exists(project_path):
       os.makedirs(project_path)
       local('git clone {} {}'.format(repo, project_path))
   elif pull:
       with lcd(project_path):
           local('git pull')
   return project_path

Часто проекты проходят несколько этапов проверок, функция может проверять, есть ли уже репозиторий, и если есть — вытягивать обновления.

А дальше у меня есть несколько тасков: flake, pylint, mccabe и т.д. Они все примерно одинаковые, так что приведу в пример только одну:

@task
@task
def flake8(repo, pull=True):
   project_path = prepare_repo(repo, pull)
   with lcd(project_path):
       with settings(warn_only=True):
           result = local('/path/to/venv/otus/bin/flake8 .')
   return result.return_code == 0

Получив из функции подготовки репозитория путь до папки, мы переключаемся в неё, используя lcd. Это аналог cd, только для работы локально. После мы включаем режим warn_only. Он нужен, чтобы выполнение скрипта не прекратилось, если flake8 вернёт код, отличный от нуля. А он именно так и сделает, если в коде есть хоть одно замечание. Как результат, функция вернёт булево значение — были ли замечания.

Аналогично устроены и остальные проверки. Но на практике я редко запускаю их отдельно. Кроме отдельных проверок, у меня есть одна общая:

@task
def all(repo):
   prepare_repo(repo, True)
   checks = []
   checks.append(flake8(repo, False))
   checks.append(pylint(repo, False))
   checks.append(mccabe(repo, False))
   … 
   if all(checks):
       print('Perfect!')

Таким образом, чаще всего я запускаю именно одну эту задачу, которая проверяет весь код, выводя основные нарушения, если они есть.

Разумеется, проверка кода на этом не заканчивается. Стилистические замечания лишь малая часть процесса. Но благодаря автоматизации, эта часть делается быстро и оставляет больше времени на остальной анализ.

Автор
0 комментариев
Для комментирования необходимо авторизоваться