Выпуск 3. Май 2013

Три правила тестирования кода, написанного с использованием ORM-фреймворка | Содержание | Введение в Perl XS

Pinto — собственный CPAN из коробки

Pinto — многообещающий инструмент для построения собственного CPAN-репозитория. На данный момент автор собирает средства (собрано уже более 80%) на реализацию новых интересных задач. Поучаствуйте и вы! http://tinyurl.com/gopinto

Одной из лучших вещей в мире Perl являются открытые модули, которые доступны на CPAN. Но следить за ними сложно. Каждую неделю появляются сотни новых релизов, и никогда не знаешь когда новая версия модуля привнесет ошибку, которая сломает твое приложение.

Одна из стратегий решения этой проблемы — сделать свой CPAN-репозиторий, который содержит только те версии модулей, которые нужны. Затем можно использовать CPAN-инструментарий для сборки своего приложения из модулей своего репозитория, неподверженного вихрю изменений публичного CPAN.

За прошедшие годы я построил несколько CPAN-репозиториев, используя CPAN::Mini и CPAN::Site. Но они всегда казались мне неуклюжими и никогда не удовлетворяли моим потребностям. Пару лет назад один клиент нанял меня для построения очередного CPAN. Но в этот раз у меня была возможность начать с нуля. Pinto — результат этой работы.

Pinto — это надежный инструмент для создания и управления своим CPAN-репозиторием. У него несколько мощных свойств, которые помогут безопасно управлять всеми нужными для приложения Perl-модулями. Эта статья расскажет как создать свой CPAN с помощью Pinto и продемонстрирует некоторые особенности.

Установка Pinto

Pinto доступен из CPAN и может быть уставлен как любой другой модуль, используя cpan или cpanm утилиты. Но Pinto это больше приложение, чем библиотека. Это инструмент, которые используется для управления кодом приложения, не будучи частью самого приложения. Поэтому я рекомендую устанавливать Pinto как самодостаточную программу следующими двумя командами:

curl -L http://getpinto.stratopan.com | bash
source ~/opt/local/pinto/etc/bashrc

Это установит Pinto в ~/opt/local/pinto, а также добавит необходимые директории в PATH и MANPATH. Все автономно, поэтому установка Pinto не меняет окружение для разработки, а также окружение никак не может повлиять на Pinto.

Изучаем Pinto

Первое, что нужно знать при работе с новым инструментом, это как получить помощь:

pinto commands            # Show a list of available commands
pinto help <COMMAND>      # Show a summary of options and arguments for <COMMAND>
pinto manual <COMMAND>    # Show the complete manual for <COMMAND>

Pinto также поставляется с другой документацией, включая урок и короткий справочник команд. Можно всегда посмотреть эту документацию одной из следующих команд:

man Pinto::Manual::Introduction  # Explains basic Pinto conecpts
man Pinto::Manual::Installing    # Suggestions for installing Pinto
man Pinto::Manual::Tutorial      # A narrative guide to Pinto
man Pinto::Manual::QuickStart    # A summary of common commands

Создание репозитория

Первым шагом в использовании Pinto является создание репозитория с использованием команды init:

pinto -r ~/repo init

Что создаст новый репозиторий в директории ~/repo. Если директория не существует, она будет создана. Если она уже существует, то должна быть пустой.

Опция -r (или --root) указывает где находится репозиторий. Она обязательна для каждой команды pinto. Если это достаточно утомительно, можно установить переменную окружения PINTO_REPOSITORY_ROOT и опустить опцию -r.

Инспектирование репозитория

Теперь, когда репозиторий создан, посмотрим что в нем находится. Для того, чтобы узнать что в репозитории, воспользуемся командой list:

pinto -r ~/repo list

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

Добавление CPAN-модулей

Предположим идет работа над приложением My-App, которое содержит модуль My::App и он зависит от модуля URI. Добавим модуль URI в репозиторий используя команду pull:

pinto -r ~/repo pull URI

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

Теперь в Pinto-репозитории находится модуль URI. Посмотрим, что же действительно получилось. Воспользуемся командой list для просмотра содержимого репозитория:

pinto -r ~/repo list

На это раз, список будет выглядеть похожим образом:

rf  URI              1.60  GAAS/URI-1.60.tar.gz
rf  URI::Escape      3.31  GAAS/URI-1.60.tar.gz
rf  URI::Heuristic   4.20  GAAS/URI-1.60.tar.gz
...

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

Добавление приватных модулей

Предположим работа над My-App завершена, осталось выпустить первый релиз. Используя предпочитаемую систему сборки (например, ExtUtils::MakeMaker, Module::Build, Module::Install и т.п.) собирается релиз My-App-1.0.tar.gz. Его можно добавить в репозиторий с помощью команды add:

$> pinto -r ~/repo add path/to/My-App-1.0.tar.gz

На этот раз снова будет предложено ввести сообщение, описывающее изменение. Сейчас, при просмотре содержимого репозитория, будет показан модуль My::App, а в качестве автора будет указан пользователь, добавивший модуль.

rl  My::App           1.0  JEFF/My-App-1.0.tar.gz
rf  URI              1.60  GAAS/URI-1.60.tar.gz
rf  URI::Escape      3.31  GAAS/URI-1.60.tar.gz
rf  URI::Heuristic   4.20  GAAS/URI-1.60.tar.gz
...

Установка модулей

Теперь когда в Pinto-репозитории есть модули, следующим шагом является сборка и установка. Под капотом Pinto-репозиторий организован практически как и CPAN-репозиторий, поэтому он полностью совместим с cpanm или любым другим инсталлятором Perl-модулей. Все, что нужно сделать, это указать инсталлятору на Pinto-репозиторий:

cpanm --mirror file://$HOME/repo --mirror-only My::App

Это команда соберет и установит My::App, используя только модули из Pinto-репозитория. Поэтому на выходе будут те же версии модулей, даже если они будут удалены или обновлены на публичном CPAN.

С cpanm опция --mirror-only крайне важна, потому что это предотвращает cpanm от обращения к публичному CPAN в случае, когда он не может найти модуль в репозитории. Когда такое происходит, обычно это говорит о том, что в каком-то дистрибутиве неправильно указаны зависимости в файле META. Чтобы починить, достаточно воспользоваться командой pull и добавить все нужные модули.

Обновление модулей

Предположим прошло несколько недель после первого релиза My-App и теперь у модуля URI появилась на CPAN новая версия 1.62. В ней появились критические исправления, которые тоже хотелось бы получить. Опять же, для обновления воспользуемся командой pull. Но в силу того, что в репозитории уже существует версия URI следует точно указать, что необходимо установить новую, указав минимальную версию:

pinto -r ~/repo pull URI~1.62

При просмотре содержимого репозитория в этот раз, видна новая версия URI (и, возможно, также других модулей):

rl  My::App           1.0  JEFF/My-App-1.0.tar.gz
rf  URI              1.62  GAAS/URI-1.62.tar.gz
rf  URI::Escape      3.38  GAAS/URI-1.62.tar.gz
rf  URI::Heuristic   4.20  GAAS/URI-1.62.tar.gz
...

Если новая версия URI требует каких-нибудь обновлений или дополнительных зависимостей, они тоже попадут в репозиторий. И при установке My::App будет использована версия 1.62.

Работа со стеками

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

Все CPAN-репозитории имеют индекс, который связывает последнюю версию каждого модуля с архивом, в котором он содержится. Обычно есть только один индекс в репозитории. Но в Pinto-репозитории может быть несколько индексов. Каждый такой индекс называется стек. Это позволяет создавать разные стеки зависимостей в одном репозитории. Поэтому можно иметь стек для разработки и стек для боевой машины, или стек perl-5.8 и стек perl-5.16. Добавление или обновление модуля всегда затрагивает только один стек.

Перед тем как идти дальше, стоит узнать про стек по умолчанию. Для большинства команд имя стека это опциональный параметр. Поэтому если стек не указан, используется указанный по умолчанию.

В репозитории никогда нет больше одного стека по умолчанию. Когда был создан репозиторий, был создан стек master и он был назначен стеком по умолчанию. Можно изменить стек по умолчанию или изменить его название, но пока не будем в это вдаваться. Стоит запомнить, что master — это название стека по умолчанию при создании репозитория.

Создание стека

Предположим, в репозитории находится версия 1.60 модуля URI, но недавно вышла версия 1.62, как и в предыдущих примерах. Необходимо обновиться, но в этот раз воспользуемся отдельным стеком.

До сих пор все, что добавлялось в репозиторий, добавлялось в стек master. Поэтому создадим копию этого стека, используя команду copy:

pinto -r ~/repo copy master uri_upgrade

Это создаст новый стек с названием uri_upgrade. Чтобы посмотреть содержимое этого стека, воспользуемся командой list с опцией --stack:

pinto -r ~/repo list --stack uri_upgrade

Будет выдан список идентичный стеку master:

rl  My::App           1.0  JEFF/My-App-1.0.tar.gz
rf  URI              1.60  GAAS/URI-1.60.tar.gz
...

Обновление стека

Теперь когда есть отдельный стек, попробуем обновить URI. Как и раньше, воспользуемся командой pull. Но в этот раз скажем Pinto добавить модули в стек uri_upgrade:

pinto -r ~/repo pull --stack uri_upgrade URI~1.62

Можно сравнить стеки master и uri_upgrade, используя команду diff:

pinto -r ~/repo diff master uri_upgrade

+rf URI              1.62 GAAS/URI-1.62.tar.gz
+rf URI::Escape      3.31 GAAS/URI-1.62.tar.gz
+rf URI::Heuristic   4.20 GAAS/URI-1.62.tar.gz
...
-rf URI              1.60 GAAS/URI-1.60.tar.gz
-rf URI::Escape      3.31 GAAS/URI-1.60.tar.gz
-rf URI::Heuristic   4.20 GAAS/URI-1.60.tar.gz

Вывод похож на вывод команды diff(1). Записи, начинающиеся с +, были добавлены, а начинающиеся с -, были удалены. Можно увидеть, что модули из дистрибутива URI-1.60 были заменены модулями из дистрибутива URI-1.62.

Установка из стека

Как только новые модули появились в стеке uri_upgrade, можно попробовать собрать приложение, указав cpanm на этот стек. У каждого стека есть поддиректория внутри репозитория, поэтому достаточно просто добавить название к адресу:

cpanm --mirror file://$HOME/repo/stacks/uri_upgrade --mirror-only My::App

Если все тесты проходят, можно уверенно обновить URI до версии 1.62 также в стеке master, воспользовавшись командой pull. Так как master это стек по умолчанию, его можно не указывать в качестве параметра:

pinto -r ~/repo pull URI~1.62

Замораживание версий

Стеки это отличный способ для тестирования эффекта изменения зависимостей на приложении. Но что, если тесты не проходят? Если проблема в My-App, то можно ее быстро исправить, изменив код, выпустив версию 2.0 и затем обновить URI на стеке master.

Но что, если ошибка в модуле URI или исправление в My-App займет много времени, тут возникает проблема. Не хочется, чтобы кто-то обновил URI, также не хочется, чтобы модуль обновился у других зависимостей My-App. Пока неизвестно, что проблема решена, необходимо предотвратить обновление URI. Для этого есть замораживание версий.

Замораживание модуля

Когда модуль замораживается, версия этого модуля фиксируется в стеке. Любая попытка обновить его (напрямую или через другую зависимость) будет неудачной. Для замораживания модуля используется команда pin (от англ. закрепить — прим. перев.):

pinto -r ~/repo pin URI

Если снова просмотреть стек master, будет примерно такой вывод:

...
rl  My::App           1.0  JEFF/My-App-1.0.tar.gz
rf! URI              1.60  GAAS/URI-1.60.tar.gz
rf! URI::Escape      3.31  GAAS/URI-1.60.tar.gz
...

Символ ! в начале записи означает, что модуль заморожен. Если кто-нибудь попытается обновить URI или добавить дистрибутив, который требует новую версию URI, Pinto выдаст предупреждение и откажется принимать новые дистрибутивы. Стоит отметить, что заморожены все модули внутри дистрибутива URI-1.60, поэтому невозможно частично обновить дистрибутив (такая ситуация возможна, когда модуль перемещается в другой дистрибутив).

Размораживание модуля

Через некоторое время, допустим, решается проблема в My-App или выходит новая версия URI с исправлением ошибки. Когда такое происходит, можно разморозить URI в стеке, воспользовавшись командой unpin:

pinto -r ~/repo unpin URI

С этого времени можно без проблем обновить URI до последней версии при необходимости. Также как и с заморозкой, разморозка освобождает все модули внутри дистрибутива.

Использование замораживания и стеков одновременно

Замораживание и стеки часто используются сообща для упрощения цикла разработки. Например, можно создать стек под названием prod, который содержит все известные надежные зависимости. В то же самое время можно создать стек dev, который содержит экспериментальные зависимости для следующего релиза. Изначально, стек dev это просто копия стека prod.

В ходе разработки возможно обновление или добавление нескольких модулей в стек dev. Если обновленный модуль ломает приложение, этот модуль замораживается в стеке prod, сигнализируя о том, что он не должен быть обновлен.

Замораживание и патчи

Иногда, когда у модуля в CPAN-дистрибутиве есть ошибка, автор не может или не хочет ее исправлять (во всяком случае до релиза вашего приложения). В этой ситуации можно сделать локальный патч CPAN-дистрибутива.

Предположим создается копия модуля URI с локальной версий дистрибутива с названием URI-1.60_PATCHED.tar.gz. Его можно добавить с репозиторий командой add:

pinto -r ~/repo add path/to/URI-1.60_PATCHED.tar.gz

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

pinto -r ~/repo pin URI

Когда автор URI выпускает версию 1.62, стоит протестировать ее перед тем, как размораживать локальную исправленную версию. Как и прежде, это может быть достигнуто клонированием стека с помощью команды copy. На этот раз назовем стек trial.

pinto -r ~/repo copy master trial

Но перед обновлением URI в стеке trial необходимо там его разморозить:

pinto -r ~/repo unpin --stack trial URI

Теперь можно попробовать обновить URI в стеке и собрать My::App следующим образом:

pinto -r ~/repo pull --stack trial URI~1.62
cpanm --mirror file://$HOME/repo/stacks/trial --mirror-only My::App

Если все прошло успешно, размораживаем в стеке master и обновляем версию URI.

pinto -r ~/repo unpin URI
pinto -r ~/repo pull URI~1.62

Обзор изменений

Как, наверное, уже можно было заметить, каждая команда, которая изменяет состояние стека, требует сообщение для описания действия. Эти сообщения могут быть просмотрены командой log:

pinto -r ~/repo log

На выходе должно получиться примерно следующее:

revision 4a62d7ce-245c-45d4-89f8-987080a90112
Date: Mar 15, 2013 1:58:05 PM
User: jeff

     Pin GAAS/URI-1.59.tar.gz

     Pinning URI because it is not causes our foo.t script to fail

revision 4a62d7ce-245c-45d4-89f8-987080a90112
Date: Mar 15, 2013 1:58:05 PM
User: jeff

     Pull GAAS/URI-1.59.tar.gz

     URI is required for HTTP support in our application

...

Заголовок каждого сообщения показывает кто и когда сделал изменение. Также у сообщений есть уникальный идентификатор похожий на SHA-1 слепок в git. Можно использовать эти идентификаторы для просмотра изменений между разными версиями или же для сброса состояния стека до предыдущей версии [NB: правда, это еще не до конца реализовано].

Завершение

В этой статей были приведены основные команды для создания Pinto-репозитория и наполнения его модулями. А также как использовать стеки и замораживание зависимостей при различных препятствиях во время разработки.

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

Jeffrey Thalhammerперевел Вячеслав Тихановский


Три правила тестирования кода, написанного с использованием ORM-фреймворка | Содержание | Введение в Perl XS
Нас уже 1010. Больше подписчиков — лучше выпуски!

Комментарии к статье