Статьи

Написание сценария cURL для Python для среды разработки Django

Вставь и помолись

Google часто сталкивается с технической проблемой и предлагает блог или статью StackOverflow, которая обещает решить проблему путем копирования и вставки простого однострочного текста в терминал. Сначала это просто быстро chmodили просто apt-get. Это совершенно небезопасно, если вы не знаете точно, что делает команда, но это чертовски эффективно. Затем вы переходите к тому, чтобы делать несколько строк одновременно, не слишком задумываясь об этом, как вредная привычка к наркотикам. Повторение медленно убеждает вас, что это не безумие.

За последние пару лет я видел несколько крайних версий этой же идеи, но перенесенных на следующий уровень. Homebrew можно установить, запустив:

ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

Heroku Toolbelt для Linux — это просто

wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh

прочь. Я называю эти инсталляторы Paste and Pray .

Звучит весело, правда? Я так и думал, поэтому придумал версию, которая с нуля устанавливает довольно ванильную среду разработки Django . Он не предполагает ничего, кроме cURL и Python, оба из которых предварительно установлены на OSX и Ubuntu. Он полностью автоматизирует установку Homebrew (включая его XCode для OSX), а также pip, virtualenv, ваш код из GitHub, Django и любые другие ваши требования Python. Он также устанавливает некоторые переменные окружения и обновляет ваш файл hosts.

О, я упоминал, что некоторые из этих шагов требуют root-доступа? Это правильно! Это больше похоже на Super Paste и Pray ™.

Примечание: этот код не предназначен для повторного использования напрямую. Вместо этого я думал, что поделюсь тем, что я узнал, когда писал это.

Код

Вы можете просмотреть код и документацию . Сам код может быть запущен с

curl -fsS https://raw.github.com/chase-seibert/paste-and-pray/go/run.py |sudo -E python

Вы сможете легко изменить его для любого приложения Django.

Sh Stuff

Хотя я не хотел никаких зависимостей, необходимых для самого установщика, я хотел воспользоваться этой возможностью, чтобы поиграть с sh , хорошим Python API для взаимодействия с оболочкой. Поскольку я не мог рассчитывать на установку pip, я просто скачал эту зависимость прямо в Python:

urllib.urlretrieve("https://raw.github.com/amoffat/sh/master/sh.py", "sh.py")
sh = imp.load_source("sh", "sh.py")

Работать с shоказалось немного сложнее, чем я себе представлял. Во-первых, многим моим командам требовался root-доступ. Самый простой способ состоял в том, чтобы запустить скрипт sudoи обернуть любые шаги, которые не требуют root, в sudo -E -u usernameпрефиксе. Это именно то, для чего предназначен sh.bake .

Я нашел полезным перенаправить _outв sys.stdoutцелях устранения неполадок. Точно так же, если вам нужно принять пользовательский ввод, вам нужно перенаправить стандартный ввод с чем-то вроде:

def raw_input_tty(name, prompt):
    ''' the main use case for this script has the user piping in the results from
    a curl; which over-rides stdin. But we also want to interactively prompt the user
    for some input, so dynamcially switch back to tty. '''
    sys.stdin = open('/dev/tty')
    return raw_input(prompt)

Sudo Madness

Сначала я пытался вызвать setuid для перехода на пользователя без полномочий root. Но я не смог вернуться к root, а это значит, что вам нужно будет выполнить все шаги root в одном чанке, а затем делать все, что не root. Это был показательный стопор, так как шаг установки Homebrew должен выполняться от имени обычного пользователя, но для последующей установки pipи virtualenvустановки инструмента требуется root.

Другая морщина получала оригинальное имя пользователя изнутри sudo. Это было просто, хотя мне нужно было поохотиться, прежде чем я обнаружил, что и OSX, и Linux установили SUDO_USERпеременную окружения именно для этой цели.

OSX Stuff

Ручная установка Homebrew проста, но для этого необходимо сначала установить инструменты командной строки XCode. Как правило, это головная боль, потому что вы должны создать Apple ID, а также искать и проверять их веб-сайт разработчика для бинарного установщика.

Оказывается, там есть прямые ссылки для скачивания, они просто не разглашаются. Вот код Python для загрузки и установки правильной версии.

def install_xcode(osx_version):
    # see: https://devimages.apple.com.edgekey.net/downloads/xcode/simulators/index-3905972D-B609-49CE-8D06-51ADC78E07BC.dvtdownloadableindex
    downloads = {
        10.7: "http://devimages.apple.com/downloads/xcode/command_line_tools_for_xcode_os_x_lion_april_2013.dmg",
        10.8: "http://devimages.apple.com/downloads/xcode/command_line_tools_for_xcode_os_x_mountain_lion_april_2013.dmg",
    }
    if osx_version not in downloads:
        raise NotImplementedError("Could not locate XCode download for OSX %s" % osx_version)
    download_file = downloads.get(osx_version)
    # save this OUTSIDE the normal tmp dir; in case we need to restart install
    dmg_file = "/tmp/xcode.dmg"
    if not os.path.exists(dmg_file):
        urllib.urlretrieve(download_file, dmg_file)
    volume_dir = "/tmp/xcode"
    if not os.path.exists(volume_dir):
        sh.hdiutil("attach", "-mountpoint", volume_dir, dmg_file)
    mpkg_file = [f for f in os.listdir(volume_dir) if f.endswith(".mpkg")][0]
    try:
        sh.installer("-pkg", os.path.join(volume_dir, mpkg_file), "-target", "/")
    except sh.ErrorReturnCode as e:
        print e.stderr
    finally:
        sh.hdiutil("detach", volume_dir)

Как я уже сказал, установить Homebrew просто. Мне пришлось включить небольшой список исправлений, чтобы он работал на одной из моих тестовых машин.

Питон Материал

Работа с Python значительно облегчила работу сценария. В частности, shдовольно просто работать с такими задачами, как получение списка элементов, установленных в настоящее время с помощью brew:

installed = self.sh.brew("list", "-1").split("\n")
not_installed = list(set(self.dependencies) - set(installed))

Он также позволяет создавать цепочки, как в оболочке bash:

sh.grep(sh.cat("/etc/hosts"), HOST_NAME)

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

activate_this = '/path/to/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))

GitHub Stuff

Для кратчайшего возможного URL вы можете установить в GitHub ветку по умолчанию , например, «go». Глядя на установщик homebrew, я заметил, что можно и имя файла не указывать. Необработанный файловый сервис GitHub, по-видимому, выбирает первый файл в алфавитном порядке, если вы делаете это. Однако я не хотел связываться с попытками заставить мой код отображаться в алфавитном порядке .gitignore, поэтому я пропустил эту оптимизацию.

Последние мысли

Случайные другие мысли по написанию установщиков:

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