Авторинг Perl модулей

  • Tutorial
При разработке перл-модулей приходится делать много работы, которая практически не связана с задачами и кодом модуля — начиная от создания десятка типовых файлов/каталогов и заканчивая выполнением десятка одинаковых операций необходимых для выпуска новой версии.

Помимо того, что это скучно, лениво, и часто приводит к ошибкам (из-за необходимости обновлять мета-информацию — вроде номера версии — в нескольких файлах или нечаянного пропуска части команд при релизе) всё дополнительно осложняется перловским TIMTOWTDI — существует несколько разных систем сборки, у всех есть свои достоинства и недостатки (но нет простой таблички с их перечислением), и ни одна из них не является рекомендуемой ни официально ни сообществом.

Кроме того, многие из нас пишут на перле очень много лет, и последний раз читали perlnewmod когда изучали перл. В результате, когда создаются новые модули это нередко делается в стиле 15-ти летней давности, причём система сборки выбирается практически случайным образом — либо древний, но знакомый и точно умеющий что угодно EUMM, либо одна из других (не потому, что нужна именно она, а просто в надежде что она окажется проще и удобнее EUMM, не создав при этом новых проблем… которые она всё-таки со временем создаёт).

Далее кратко описаны имеющиеся на начало 2015 года средства, которые могут облегчить процесс разработки перл-модулей, сделать ваши модули более современными, и упростить другим разработчикам доработку ваших модулей. Я постарался перечислить их основные плюсы и минусы, но т.к. сам пользовался не всеми то буду дополнять/исправлять этот список в соответствии с вашими комментариями.


Задачи


Итак, давайте составим список задач, которые обычно входят в процесс авторинга Perl модулей.

Задачи при создании нового модуля:
  1. задать имя/email автора
  2. определить лицензию
  3. выбрать систему сборки
  4. выбрать дополнительные особенности:
    • поддержка XS
    • базовый набор тестов (проверка документации, правописания, качества  кода, зависимостей, файлов системы сборки, etc.)
    • автоматическая генерация README из документации модуля
    • использование VCS
    • интеграция с GitHub, Travis CI
  5. создать скелет модуля — каталог с кучкой файлов необходимых для его  разработки и релиза:
    • скелет самого модуля и документации
    • базовый набор тестов
    • Changes
    • README
    • LICENSE
    • файлы системы сборки
    • список зависимостей (иногда входит в файлы системы сборки)
    • при использовании VCS: репозиторий и его настройки

Задачи при релизе новой версии:
  1. обновить список зависимостей
  2. собрать модуль
  3. запустить тесты
  4. выбрать новый номер версии и изменить её в нескольких файлах (в  некоторых файлах — в нескольких местах)
  5. описать изменения в Changes
  6. добавить в Changes текущую дату и версию релиза
  7. обновить README
  8. при использовании VCS:
    1. commit изменений версии, Changes, и всех остальных изменённых файлов
    2. добавить tag для новой версии
    3. при использовании центрального репо (GitHub) — отправить в него  изменения
  9. создать архив с модулем
  10. залить его на CPAN

В идеале, все решения необходимые при создании нового модуля (кроме необходимости в XS) нужно принять один раз, и в дальнейшем создавать новые модули одной командой. А при релизе, в идеале, одна команда должна выполнить все шаги кроме выбора нового номера версии и описания изменений в Changes.

Update: если вы дочитали сюда, и не поняли что за «авторинг» такой и чем он отличается от обычного использования Makefile.PL/Build.PL — прочитайте эти два комментария.

$VERSION


In a perfect world, I would never blog about version numbers in Perl.
 «Version numbers should be boring» — David Golden
Перед тем как перейти к сути вопроса, необходимо описать одну проблему: выбор формата для версии модуля. Хотя это и не связано напрямую с темой статьи, этот выбор необходимо делать в процессе авторинга, и он не так прост, как может показаться.

Есть очень много способов неправильно объявить версию своего модуля, поэтому я перечислю только правильные — их намного меньше.

Старая десятичная / Old-style decimal


Стабильные релизы (две/три цифры, кому как нравится):

our $VERSION = '0.08';
our $VERSION = '0.008';
package MyModule 0.08;                  # need perl-5.12
package MyModule 0.008;                 # need perl-5.12

Альфа-версии для тестирования CPAN Testers перед выпуском 0.09 / 0.009:

our $VERSION = '0.08_01';
our $VERSION = '0.008_001';

Либо, учитывая что альфа-версии пользователи всё-равно не увидят, вы можете использовать две независимые последовательности для стабильных и нестабильных релизов: пример.

Точечно-числовая / Dotted-decimal


Стабильные релизы (можно использовать 3 и более чисел в версии):

use version; our $VERSION = 'v0.8.0';   # need perl-5.8.1
our $VERSION = 'v0.8.0';                # need perl-5.10
package MyModule v0.8.0;                # need perl-5.12

Альфа-версии для тестирования CPAN Testers перед выпуском v0.8.1:

use version; our $VERSION = 'v0.8.0_1'; # need perl-5.8.1
our $VERSION = 'v0.8.0_1';              # need perl-5.10

Хотя "v0.8_1" тоже можно использовать как альфа-версию между стабильными "v0.8.0" и "v0.8.1" но такая версия может быть только одна — если понадобится выпустить несколько альфа-версий то выпустить стабильную с номером "v0.8.1" уже не получится.

Семантическая / Semantic


На данный момент нет возможности полностью соответствовать спецификации семантического версионирования - pre-release версии определённые как "1.2.3-alpha1" применить к перл-модулям нельзя. Самым близким вариантом будет вышеописанная 3-х элементная точечно-числовая версия — вы можете следовать правилам определения следующей стабильной версии по спецификации, а вместо текстовых pre-release версий выпускать числовые альфа-версии.

TRIAL


Вы уже заметили, что альфа версии это, как говорят наши зарубежные коллеги, «боль в заднице». Фактически ведь речь идёт не о настоящих «альфа» — настоящие альфа, бета и прочие pre-release версии описаны в спецификации семантического версионирования и не поддерживаются перлом. Речь идёт о том, чтобы дать CPAN команду «не индексировать» данную версию. Таким образом мы смешиваем в одной переменной два типа данных (номер версии и флаг для CPAN), что является плохим стилем, уродливо выглядит, запутывает (что выбрать перед выпуском "v0.8.1""v0.8_1" или "v0.8.0_1"?) и создаёт другие проблемы: альфа версии нельзя задать в package, в некоторых старых (до 5.8.1) версиях perl они работают не корректно.

Не так давно на CPAN добавили новый способ дать команду «не индексировать» данную версию — если в имени архива с модулем есть слово "TRIAL". Таким образом, вы можете больше не использовать «альфа» версии. Некоторые утилиты (Dist::Zillashipit, …) поддерживают параметр для добавления в имя архива с модулем "-TRIAL" перед заливанием на CPAN. Учтите, что "v1.2.3-TRIAL" (в отличие от "v1.2_3") это обычная версия "v1.2.3", так что следующая после неё должна быть "v1.2.4" или "v1.2.4-TRIAL".

Скелет модуля / Boilerplate


Эти утилиты создают каталог с новым модулем, наполняя его базовым набором необходимых файлов по какому-то шаблону. Это самый старый, и, до сих пор, основной способ создать новый модуль. Проблема этого подхода в том, что значительную часть этих файлов мало просто однократно создать, их нужно постоянно обновлять при выпуске новых версий модуля. Поэтому многие постепенно начинают использовать вместо этих утилит более комплексные решения (обычно на базе Dist::Zilla).

h2xs, Module::Starter


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

На CPAN можно найти ещё некоторое количество аналогичных модулей, но все, которые видел я, были заточены под потребности их авторов и не очень гибко настраивались — по сути, задача генерации каталога с новым модулем по шаблону настолько проста, что почти каждый пишет свой велосипед (мой — это скрипт на 20 строк в ~/bin/, тоже абсолютно не настраиваемый).

Сборка / Build


С 95% модулей содержащих несколько pm-файлов, тесты и стандартно собирающийся небольшой кусок XS — справится любая система сборки.

Начиная с 5.10.1 появилась поддержка configure_requires — т.е. теперь можно в META.{json,yml} указать какие модули должны быть установлены до запуска perl Makefile.PL или perl Build.PL. Иными словами теперь не важно, установлен ли у пользователя, например, Module::Build::Tiny — вы можете использовать его для сборки своего модуля. А можете написать свою систему сборки для своих модулей.

ExtUtils::MakeMaker (a.k.a. EUMM)


Особенности:
  • требует наличия make
  • есть поддержка графа зависимостей (благодаря использованию make)

Недостатки:
  • для изменения процесса сборки обычно приходится менять Makefile (либо  дописывая к нему кусок, либо как-то его преобразуя; более того, это  необходимо делать портабельно как в плане формата Makefile так и в  плане используемых в нём шелл-команд) — что очень сильно всё усложняет
    • при внесении изменений нужно писать портабельный код
  • слишком большой и сложный,  никто не хочет его поддерживать  и развивать
  • к нему сложно писать плагины и сложно подключать одновременно несколько  плагинов

Достоинства:
  • самый гибкий из всех альтернатив, поэтому в сложных случаях приходится  использовать исключительно EUMM

Module::Build (a.k.a. MB)


Особенности:
  • для работы достаточно perl, не нужен ни make ни знание как писать  портабельный Makefile
  • нет поддержки графа зависимостей (что от чего зависит при сборке)
  • убирают из core (модулей распространяемых вместе с perl) — что ни на что  не повлияет (спасибо configure_requires), но вызвало волну  FUD; на самом деле никаких новых  причин его избегать не появилось, и если старые причины вас не  останавливали — то продолжайте спокойно им пользоваться и дальше

Недостатки:
  • слишком большой и сложный, никто не хочет его поддерживать и развивать
  • из-за архитектуры схожей с EUMM (для расширения нужно писать код,  который будет использоваться для генерации другого кода, который уже  будет выполняться) у него та же проблема — сложно писать плагины и  сложно подключать одновременно несколько плагинов (недавно появился  Module::Build::Pluggable,  который пытается решить вторую половину этой проблемы)

Достоинства:
  • использовать и, в небольших объёмах, расширять значительно проще, чем  EUMM

Module::Install (a.k.a. MI)


Особенности:
  • обёртка над EUMM

Недостатки:
  • пытался решить проблему отсутствия в то время configure_requires копируя  себя и все необходимые ему для сборки текущего модуля плагины в inc/ -  что, в свою очередь, создало новые проблемы (а сейчас и вовсе потеряло  смысл) — от необходимости перевыпускать свой модуль для исправления бага  в идущей в комплекте с ним версии MI до неудобств при использовании VCS  из-за того, что в репозитории постоянно обновляется код относящийся к MI

Достоинства:
  • простой и наглядный интерфейс

Несмотря на единодушную нелюбовь сообщества к EUMM и Module::Build, у меня сложилось впечатление что Module::Install в последнее время вообще перестали воспринимать всерьёз — создаваемые им время от времени проблемы перевесили его достоинства.

Module::Build::Tiny (a.k.a. MBT)


Особенности:
  • эксперимент, показавший что можно сделать систему сборки на перле  достаточную для большинства модулей всего в 120 строчках кода

Ваш Build.PL выглядит так:

use 5.008001;               # only if you need it
use Module::Build::Tiny;
Build_PL();

Как ни странно, но этим даже можно пользоваться — используя файл cpanfile для управления зависимостями и вспомогательную утилиту mbtiny для авторинга (генерации Build.PL, MANIFEST и META.{json,yml}, подготовки архива с модулем — того, что делал Module::Build и что не относится к процессу сборки модуля). Либо использовать Dist::Zilla вместо mbtiny (кстати, Dist::Milla и Minilla используют MBT — для таких навороченных систем как раз очень удобно, когда система сборки делает только своё дело и не берёт на себя «лишние» задачи).

Управление зависимостями / Dependencies


cpanfile


Это подход, при котором зависимости указываются отдельно от системы сборки, в файле cpanfile. Есть несколько причин это делать:

  • Если вы используете разные системы сборки в разных модулях, то  использование cpanfile позволит забыть об отличиях синтаксиса (и  ограничениях — EUMM и Module::Install пока не полностью поддерживают  CPAN::Meta::Spec 2.0) разных  систем сборки и использовать один формат для всех (кстати,  совместимый с форматом Module::Install).
  • Этим же способом можно задавать зависимости не только для перл-модулей,  но и для обычных приложений (у которых обычно нет Makefile.PL или  Build.PL и зависимости указывать просто негде) — собственно, именно  ради этой возможности cpanfile и был разработан. Причём можно задать для  отдельной зависимости альтернативный источник, откуда её брать — из  приватного CPAN mirror, из git, etc.
  • Можно устанавливать все зависимости через cpanm --installdeps . или  carton без META.{json,yml} файлов.
  • При использовании Module::Install можно не держать в репозитории inc/  и META.{json,yml} и корректно указывать зависимость от Module::Install  как «develop» (author_requires) а не «configure» (configure_requires).  При этом другие разработчики, у которых не установлен Module::Install,  смогут его установить через cpanm --with-develop --installdeps . или  carton.

Управление версиями / VCS


В случае перл-модулей нужно иметь в виду, что в репозитории нужно, с одной стороны, хранить все необходимые для сборки модуля файлы (чтобы его можно было устанавливать прямо из репозитория через cpanm и чтобы после форка другие разработчики получили рабочую версию проекта), а с другой избежать замусоривания его лишними авто-генерируемыми файлами (например, файлами Module::Install в inc/) — вам ведь их commit-ить постоянно, плюс они будут замусоривать diff, etc. Особенно это касается пользователей Dist::Zilla — если вы хотите получать pull-request-ы то не стоит требовать чтобы желающие пофиксить вам какую-то мелочь были вынуждены устанавливать 150-200 дополнительных модулей для запуска сборки проекта.

GitHub


При использовании GitHub вам скорее всего придётся либо писать дополнительное описание модуля в README.md, либо настроить автоматическую генерацию этого файла из POD-документации модуля. И во втором случае может потребоваться добавлять дополнительные элементы - например, статус сборки проекта в Travis CI.

Непрерывная интеграция / CI


CPAN Testers


До апреля 2013 CPAN Testers не поддерживал отдельное указание test_requires (зависимостей необходимых только для запуска тестов). При этом системы сборки уже давно давали возможность их указывать… но это не работало. В результате некоторые разработчики модулей сильно огорчались, выпускали новую версию без всяких умных test_requires и забывали про эту фичу. Так вот, уже можноПодробности поддержки test_requires разными версиями систем сборки.

В принципе, сервис CPAN Testers покрывает основные потребности, но у него есть один недостаток: тестирование происходит уже после релиза. Чтобы прогнать модуль через CPAN Testers до релиза нужно выпускать специальные alpha-версии — и всё-равно это релиз, да и работает CPAN Testers не так уж быстро.

GitHub + Travis CI


Подключив к репозиторию с модулем на GitHub Travis CI можно автоматизировать тестирование текущей версии модуля до релиза на нескольких версиях perl (хоть это и не так круто в плане разных платформ как CPAN Testers, но всё-таки лучше, чем запускать тесты только у себя на машине).

Выпуск / Release


App::scan_prereqs_cpanfile


Предоставляет команду scan-prereqs-cpanfile для анализа зависимостей модуля и генерирования cpanfile или вывода отличий от текущего cpanfile (если он модифицировался вручную и просто сгенерировать его заново это не лучшая идея).

Perl::Version


Предоставляет команду perl-reversion для изменения номера версии в (почти) всех файлах модуля.

Поддерживает README, но не README.md.

CPAN::Uploader


Предоставляет команду cpan-upload для заливания модулей на CPAN из командной строки. Конфиг-файл ~/.pause с логином/паролем для PAUSE может быть зашифрован GnuPG.

GitHub


К сожалению, GitHub пока не умеет автоматически заливать перл-модули на CPAN (хотя, наверное, технически правильнее будет сказать, что это CPAN не умеет выкачивать модули с GitHub) при добавлении тега для новой версии (как это происходит, например, с плагинами для jQuery). Но я всё-равно оставлю этот пункт здесь, вдруг его увидят нужные люди и добавят фичу.

Поддержка / Support


CPAN RT


Баг-трекер CPAN много лет был единственным доступным вариантом. Учитывая его неудобный интерфейс это было очень печально. С другой стороны, им можно пользоваться даже если вы не используете VCS при разработке модуля.

К счастью, сейчас есть возможность указать в META.{json,yml} (не ручками, конечно, а через используемую систему сборки) альтернативный баг-трекер (например на GitHub). К сожалению, хотя при этом изменятся ссылки на баг-трекер на сайтах CPAN и MetaCPAN, это не отключит возможность добавлять тикеты для вашего модуля на CPAN RT (но там показывается уведомление, что предпочитаемый баг-трекер в другом месте). Разумеется, после изменения баг-трекера текущие баги остаются в RT.

GitHub


Не буду описывать преимущества и удобство поддержки проектов на GitHub (особенно бросающиеся в глаза по сравнению с CPAN RT). Тем более, что даже если не нравится Git, то можно без проблем локально работать с Mercurial и всё-равно держать проект на GitHub (через плагин hg-git).

Если вы захотите переместить текущие тикеты из CPAN RT в GitHub Issues - можете попробовать воспользоваться rt-to-github.pl или этой модификацией его старой версии.

Авторинг / Authoring


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

У меня есть два сервера, один только что установлен, там всего 27 CPAN-модулей, на втором много лет разрабатывается много перл-проектов, там установлено 248 CPAN-модулей. Я подсчитал, сколько приходится установить дополнительных CPAN-модулей на каждом из серверов для описанных в этом разделе утилит:

  • Dist::Zilla (без дополнительных плагинов) требует 162 модуля на первом и  83 на втором сервере.
  • Dist::Milla (фактически, подборка плагинов для Dist::Zilla) требует 257  и 166 модулей.
  • Minilla (которая декларирует минимум зависимостей как одно из основных  преимуществ) — 126 и 41 модуль.
  • ShipIt — 1 и 1.
  • App::ModuleBuildTiny — 8 и 6.

Dist::Zilla


Это настоящий монстр. Он делает всё! На CPAN сейчас порядка 900 модулей входящих в 480 дистрибутивов расширяющих возможности Dist::Zilla. Из этих 480 дистрибутивов 315 это плагины (Dist::Zilla::Plugin::*), и ещё 100 - разные подборки этих плагинов (Dist::Zilla::PluginBundle::*).

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

По первой проблеме — ничего не могу сказать. Я не рискнул связываться и пытаться его настроить под себя. Кто это сделал — поделитесь впечатлениями в комментариях.

Вторая проблема тоже достаточно важна — если вы хотите получать патчи и pull-request-ы к своему модулю, необходимо, чтобы желающие внести в него изменения могли сделать это достаточно просто. Если им для исправления пары строк нужно будет установить половину CPAN (автор утверждает что не половину, а всего 0.6%) и разбираться с нестандартным процессом сборки - патч вы не дождётесь.

Dist::Milla


Особенности:
  • просто подборка плагинов для Dist::Zilla и утилита-обёртка
  • используется лицензия Perl, для изменения замените её другой в POD
  • нет автоматического определения и обновления зависимостей, их нужно  описывать вручную в cpanfile
    • можно воспользоваться scan-prereqs-cpanfile --diff=cpanfile чтобы  быстро найти все новые подключенные модули, которые ещё не добавлены в  cpanfile
  • можно выбрать какая система сборки будет использоваться:  Module::Build::Tiny (по умолчанию), Module::Build или EUMM, но я не  уверен, что можно воспользоваться всеми возможностями выбранной системы  сборки — Build.PL редактировать смысла нет, он при каждой сборке  генерируется заново

Недостатки (по сравнению с Dist::Zilla):
  • ещё большее количество зависимостей, которые нужно установить… но это, в  значительной мере, компенсируется тем, что можно сэкономить несколько  дней на составление своей подборки плагинов, которая ещё не факт что  оказалась бы меньше
    • впрочем, как только захочется добавить новую фичу, например вывод  badge с отчётом Travis CI в README.md — всё-равно нужно будет искать и  подключать  дополнительный  плагин Dist::Zilla
  • вроде бы XS-модули не поддерживаются (вероятно, нужно будет подключать  дополнительный плагин)

Достоинства:
  • многие популярные плагины Dist::Zilla модифицируют код и/или  документацию модуля (или зависят от плагинов, которые это делают),  заставляя вас изменять свой рабочий процесс — таких плагинов в  Dist::Milla принципиально нет: ваш код и документацию изменяете только  вы сами (исключая обновление $VERSION при релизе)
    • вместо того, чтобы изменять код/документацию на основании заданных  мета-данных, Dist::Milla работает в обратном направлении, вычисляя  мета-данные на основании кода, документации и репозитория — как это  всегда делали системы сборки вроде Module::Install или Module::Build
  • в отличие от большинства модулей использующих Dist::Zilla, в разработке  модулей использующих Dist::Milla могут легко участвовать сторонние  разработчики — им не нужно устанавливать себе Dist::Zilla или  разбираться с нестандартным процессом сборки, ваш модуль выглядит,  собирается, тестируется и устанавливается как все обычные модули
    • в частности, ваш модуль можно устанавливать прямо из GitHub
  • нет необходимости тратить несколько дней на подбор и настройку своего  набора плагинов для Dist::Zilla, можно начать сразу использовать  достаточно неплохой стартовый набор, и в дальнейшем его модифицировать  по мере необходимости
  • поддержка миграции существующего модуля использующего другую систему  сборки или авторинга на Dist::Milla

Minilla


Особенности:
  • работает практически идентично Dist::Milla с настройками по умолчанию

Недостатки:
  • практически не настраивается

Достоинства:
  • совместим со стандартным процессом сборки/установки модуля
  • не использует Dist::Zilla
  • значительно меньше зависимостей, чем у Dist::Milla
  • поддерживает XS
  • поддержка миграции существующего модуля использующего другую систему  сборки или авторинга на Minilla

Если вы делаете авторинг практически так же, как Tokuhiro Matsuno (автор Minilla), либо если вам всё-равно как именно делается авторинг, главное чтобы всё работало само из коробки достаточно современным образом (т.е. с использованием git и GitHub) и не требовало установки дикого количества модулей — Minilla вам идеально подойдёт. Если же потребуется что-то делать иначе — придётся искать ему замену (вероятнее всего ей окажется Dist::Milla).

ShipIt


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

В каталоге модуля создаётся файл .shipit, примерно такой:

steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist, UploadCPAN

и теперь при запуске shipit будут выполнены указанные в .shipit операции:

  1. у вас спросят номер новой версии
  2. новая версия пропишется в коде модуля
  3. проверит наличие записи в Changes и предложит её добавить
  4. запустит тесты (поддерживает Makefile.PL и Build.PL)
  5. сделает commit (поддерживает Git/Mercurial/SVN)
  6. добавит tag для новой версии
  7. подготовит архив с модулем
  8. зальёт архив на CPAN

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

Не понятно, будет ли ShipIt поддерживаться — он уже пару лет не обновлялся, а его автор сейчас активно работает над Dist::Milla и описывает миграцию с ShipIt на Dist::Milla. На мой взгляд ShipIt делает практически всё необходимое, и при этом он очень маленький, простой, расширяемый и без зависимостей. Не знаю, что в нём не устроило автора, и почему он решил делать обёртку для Dist::Zilla вместо дописывания недостающей функциональности плагинами для ShipIt. По мнению автора Dist::Zilla (который активно использовал ShipIt до того как разработать Dist::Zilla) основная проблема ShipIt — сложность и недостаточная гибкость расширения его плагинами. Надо признать, эту проблему Dist::Zilla действительно решил более чем основательно.

App::ModuleBuildTiny


Долго думал, стоит ли вообще включать его в статью. Функциональность у него рудиментарная, текущая версия 0.005, год его никто не обновлял… но буквально на днях автор им активно занялся, и кроме того он необходим для использования Module::Build::Tiny напрямую, не через Dist::Zilla или Minilla.

Это не похоже на утилиту для авторинга, по крайней мере если сравнить его возможности с вышеописанными утилитами. Но автор его назвал «A standalone authoring tool», значит будем рассматривать её в этом разделе. С учётом его крайне ограниченных возможностей попробуем использовать вместе с ним другие мелкие утилиты (описанные в следующих разделах) в попытке получить полную функциональность авторинга без Dist::Zilla.

Для начала — самый минималистичный способ создать модуль для CPAN:

mkdir -p Example-MBtiny/lib/Example/
cd Example-MBtiny
vi lib/Example/MBtiny.pm
mbtiny dist

В результате получаем Example-MBtiny-$VERSION.tar.gz который можно заливать на CPAN. Никаких других файлов в каталоге с модулем больше нет - только созданный нами lib/Example/MBtiny.pm и этот архив.

Теперь создадим (или сгенерируем какой-нибудь утилитой) все стандартные файлы, которые обычно присутствуют в каждом современном модуле выложенном на GitHub: README.md, LICENSE, Changes, t/*, .gitignore.travis.yml а так же создадим репозиторий, добавим туда все эти файлы и зальём на GitHub.

Следующий вопрос — в каком стиле использовать mbtiny:

  • Если вызывать только mbtiny test и mbtiny dist, то необходимые для  сборки, тестирования и установки файлы Build.PL, MANIFEST и  META.{json,yml} будут генерироваться на лету и удаляться по завершению  операции. Безусловно, они будут присутствовать в созданном архиве для  заливания на CPAN, но их не будет в репозитории — что сделает  невозможным установку модуля через cpanm прямо из репозитория и может  озадачить сторонних разработчиков которые хотели бы что-то изменить и  прислать pull-request.
  • Альтернативный вариант — вызвать mbtiny regenerate для создания  Build.PL, MANIFEST и META.{json,yml} и добавить их в репозиторий.  При изменении версии модуля, добавлении новых файлов, или изменении  зависимостей — будет необходимо снова вызывать эту команду.

При использовании GitHub нужно добавить в проект metamerge.json, его содержимое будет учитываться при генерации META.{json,yml}:

{
   "resources" : {
      "bugtracker" : {
         "web" : "https://github.com/powerman/Example-MBtiny/issues"
      },
      "homepage" : "https://github.com/powerman/Example-MBtiny",
      "repository" : {
         "type" : "git",
         "url" : "git://github.com/powerman/Example-MBtiny.git",
         "web" : "https://github.com/powerman/Example-MBtiny"
      }
   }
}

При релизе новой версии пригодятся дополнительные утилиты:

# update dependencies
scan-prereqs-cpanfile >cpanfile
# ... update META.* from cpanfile if you added META.* into the repo
mbtiny regenerate
# build & test
mbtiny test
# update version everywhere
ver=1.2.3
perl-reversion -set $ver
# don't forget to update version & date
vi Changes
# regenerate README.md with badges
cp BADGES.md README.md
pod2markdown lib/Example/MBtiny.pm >> README.md
# release
git commit -a -m "release $ver"
git tag $ver
git push
mbtiny dist
cpan-upload Example-MBtiny-$ver.tar.gz

Как видите, выполнять всё это каждый раз вручную — не лучшая идея, что-то забудешь, где-то ошибёшься. Но оформить это скриптом не сложно. Двух таких простых скриптов (этого, и создающего скелет нового модуля по шаблону) вполне достаточно для авторинга современных модулей без Dist::Zilla.

Резюме


  • если нет острой необходимости поддерживать старые версии перла, на мой  взгляд, в модулях стоит по умолчанию использовать use 5.010001;
  • версию задавать используя формат our $VERSION="v1.2.3";
    • соответствовать спецификации семантического версионирования настолько,  насколько возможно в перле
    • для альфа-версий добавлять после версии в имени архива с модулем  "-TRIAL" вместо использования подчёркиваний в номере версии
  • в качестве системы сборки использовать Module::Build::Tiny или  Module::Build (только если не хватает возможностей MBT)
  • для управления зависимостями использовать cpanfile
  • держать модуль на GitHub, указать ссылки на GitHub в META.json
    • использовать Travis CI

Что же касается выбора утилиты для авторинга — здесь сложно дать однозначную рекомендацию. Если выбирать среди не заброшенных:

  • Minilla, если работа по умолчанию устраивает и нет потребности как-то  его настраивать
  • Dist::Milla, если нравится Minilla но хочется что-то настроить, либо  хочется начать использовать Dist::Zilla с достаточно адекватной и  документированной подборки плагинов
  • App::ModuleBuildTiny, если хочется быстро сделать свой велосипед,  простой и достаточно гибко настраиваемый

Разные полезности для авторов модулей



______________________
Текст конвертирован используя habrahabr backend для AsciiDoc.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

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

    0
    Небольшое примечание для тех, кого раздражает префикс «v» перед номером версии. Если соблюдать некоторые ограничения (минимальный перл 5.10, минимум 3 числа в версии, без альфа-версий), можно этот префикс не указывать:
    use 5.010;
    our $VERSION = '1.2.3';  # NOT ALLOWED: '1.2', '1.2_3'
    
    Но, по большому счёту, абсолютно не важно, как записана версия внутри модуля, если она корректно работает — её никто кроме автора не видит. А вот как выглядит версия модуля в тегах гита и в имени архива с модулем уже может иметь значение — но обеспечить отсутствие «v» в них это уже задача для утилит авторинга.
      +1
      Хотелось бы вводную часть, где написано, о чем всё это вообще.

      Сборка, зависимости, авторинг — что это? Зачем? Кто на ком сидит?
        0
        Раньше был один сплошной комок — часть описанных в статье задач брала на себя система сборки (Module::Build, например), часть делалась вручную, остальное делать ленились вообще. Сейчас всё становится более цивилизованно, для разных задач появились специализированные инструменты… но разобраться какие инструменты вообще есть, какие заброшены а какие актуальны, чем отличаются и какими лучше пользоваться — очень не просто. Я попытался в статье ответить именно на этот вопрос.

        Что до вводной части — предполагалось что это раздел Задачи, в котором описано что обычно требуется делать автору модуля (помимо написания собственно самого модуля, тестов и документации).
          0
          В каком месте раздела «Задачи» применяется, к примеру, Dist::Zilla? А авторинг?

          Что вообще такое авторинг?
            0
            Речь идет о создании дистрибутива софта.

            Dist::Zilla применяется для автоматизации различных операций с дистрибутивом (создание, упаковка, тестирование, отправка на cpan).

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

            В общем Dist::Zilla сводит все к выполнению одной команды.
              0
              Вот опять — «Дистзилла крута и может всякое». Как бы объяснить… я не про то, что дистзилла плоха или хороша. Я про то, что в этой статье нет взаимосвязи между всеми этими упомянутыми модулями.

              На каком этапе мне понадобится дистзилла? А почему я не могу использовать вместо неё мейкмейкер? Что? Это разные инструменты? А в чем разница? А авторинг — это что вообще такое и почему он не может заменить собой и дистзиллу и мейкмейкер?

              Кто на ком сидит в этой мешанине?
                0
                На каком этапе мне понадобится дистзилла?
                Понадобится — неправильный термин. Её можно не использовать вообще. Но если её использовать — то она будет использоваться на всех этапах.
                А почему я не могу использовать вместо неё мейкмейкер? Что? Это разные инструменты? А в чем разница?
                Можете. Просто EUMM решает только часть задач (причём довольно сложным и запутанным образом), поэтому остальные задачи придётся либо выполнять вручную, либо использовать какие-то дополнительные из описанных в статье утилит (например, cpan-uploader для автоматического заливания дистрибутива с модулей созданного EUMM на PAUSE).
                А авторинг — это что вообще такое
                Уже ответил.
                Кто на ком сидит в этой мешанине?
                Сложно сказать. Есть много достаточно разных задач. Есть куча утилит, многие из которых пытаются с переменным успехом решать некоторые из этих задач. Естественно, функциональность многих утилит пересекается. Плюс нередко одни из них используют внутри себя другие.

                По большому счёту, Вам не должно быть важно кто на ком сидит. Вам нужно сначала сделать выбор между использованием одной утилиты для решения всех (или практически всех — кроме создания скелета нового модуля) задач, или использованием отдельных утилит для разных задач. В первом случае вопрос «кто на ком сидит» отпадёт сам т.к. останется только одна утилита. Во втором просто идёте последовательно по списку всех задач и для каждого следующего шага выбираете подходящую утилиту — которая может заодно решать и другие задачи плюс к текущей, таким образом закрыв собой не одну а несколько задач. Продолжать пока либо не найдёте утилиты для всех задач либо не решите, что оставшиеся без утилит задачи проще делать вручную.
                  0
                  Вот, вот эти два последних Ваших комментария — это и есть примерно та самая вводная часть, про которую я написал изначально. было бы здорово свести это в один параграф в статье, это сделает статью гораздо понятнее.
              0
              В контексте перл-модулей утилита для авторинга — это программа помогающая автору создавать и подготавливать к распространению перл-модули. Все задачи перечисленные в том разделе относятся именно к процессу авторинга.

              Утилиты, описываемые в статье до раздела Авторинг / Authoring решают только отдельные подзадачи процесса авторинга, а описанные в этом разделе Dist::Zilla/Dist::Milla/Minilla — все сразу. ShipIt решает вторую половину задач — всё, связанное с выпуском новой версии, но создавать новый модуль он не умеет. Описание App::ModuleBuildTiny демонстрирует, как решит ту же вторую половину задач используя сборную солянку из описанных в статье мелких утилит.

              В каком месте раздела «Задачи» применяется, к примеру, Dist::Zilla?
              Во всех местах. Dist::Zilla решает все перечисленные в нём задачи.
              А авторинг?
              Авторинг — это общее название для всех перечисленных в этом разделе задач.
              Что вообще такое авторинг?
              Это всё то, что Вам нужно делать для того, чтобы сделать доступным свой новый перл-модуль для других (не важно, через выкладывание его на CPAN/DarkPAN или отправку его другому разработчику по email) — всё, кроме собственно написания кода, тестов и документации этого модуля.
          0
          Dist::Zilla — самый гибкий и расширяемый.

          Недостаток — использует монстрообразный Moose c миллионом зависимостей. Я бы вообще перенес Moose в разряд deprecated.

          Разобраться не так сложно, как кажется. По сути, все, что он делает — последовательно запускает плагины.
            0
            Еще могу добавить, что лучше всего сразу начинать описывать зависимости в cpanfile, из которого они будут экспортироваться в META файлы дистрибутива.
            Это самый практичный подход, т.к. не каждый проект — это CPAN дистрибутив и часто надо просто поставить зависимости для кода из репозитория, не собирая и устанавливая его.
              0
              Dist:::Milla — это просто сконфигурированный профиль Миагавы для Dist::Zilla.
              Никакой особой документации для него нет.
              В него зашито использование git и github, кому-то может не подойти.
                0
                Dist:::Milla — это просто сконфигурированный профиль Миагавы для Dist::Zilla.
                Да, но… это достаточно популярный профиль, плюс он построен на нетипичном для большинства профилей Dist::Zilla принципе — работать привычным для авторов не использовавших Dist::Zilla способом, получая нужные ему метаданные из кода/документации модуля вместо того, чтобы брать их из конфига Dist::Zilla и генерировать на их базе часть кода и большую часть документации модуля. Помимо того, что, на мой взгляд, это более разумный подход в принципе, как минимум он сильно упрощает порог входа при попытке начать использовать Dist::Zilla.
                Никакой особой документации для него нет.
                Ну, например, есть screencast. Но ещё важнее то, что я описал выше — привычный стиль работы с модулем, который не нужно специально документировать.
                В него зашито использование git и github, кому-то может не подойти.
                Его наверняка можно переключить на использование Mercurial. А что касается GitHub — по большому счёту ради него всё и затевалось. Один только перенос багтрекера с RT стоит того, чтобы всем этим заморочиться. Плюс намного легче отправлять и принимать pull-request-ы, чем вручную сделанные патчи. Плюс возможность устанавливать любую версию прямо из репозитория через cpanm, без заливания на CPAN. Автоматический прогон тестов через Travis CI до выкладывания на CPAN. Контроль покрытия тестами через Coveralls. … В общем, кому-то он, конечно, не подойдёт, но для многих он сильно упрощает работу.
                  0
                  Отключить гит там — это сделать свой профиль. Это будет уже не дистмилла.
                  Кстати, популярных профилей много, посмотрите на cpan Dist::Zilla::PluginBundle::
                    0
                    Много — это сколько? Если посмотреть по «плюсикам» на metacpan, то список 162 бандлов вырождается в:
                    1. 32 — Dist::Zilla::PluginBundle::Git
                    2. 16 — Dist::Zilla::PluginBundle::Milla
                    3. 12 — Dist::Zilla::PluginBundle::GitHub
                    4. 6 — Dist::Zilla::PluginBundle::DAGOLDEN
                    5. 6 — Dist::Zilla::PluginBundle::TestingMania
                    При этом Git полностью входит в Milla, функциональность GitHub тоже в Milla реализована, хотя и немного иначе. Что касается подборки Голдена — я его очень уважаю, но он как раз использует зиллу совершенно типичным для неё образом, генерируя половину модуля автоматически из мета-данных — что противоположно подходу миллы. А вот к последнему стоит присмотреться внимательнее — возможно его стоит использовать вместе с миллой, спасибо за наводку. :)

                    Что касается «уже не милла» — не согласен. Милла — это не конкретная подборка плагинов, высеченная в граните. Это определённая идеология, подход. Плюс базовый набор достаточно неплохо настроенный по умолчанию. Если миллу не менять под себя — в чём тогда вообще смысл с ней связываться если намного проще поставить вместо неё миниллу?
                      0
                      Минилла не настраивается, гит вшит.

                      Мне гит не нужен, например, я меркуриал использую.

                      У каждого серьезного контрибъютора свой профиль.

                      Переписал бы кто-то Dist::Zilla на Moo. Сразу бы кол-во зависимостей упало в 2 раза.
                      Кроме этого в ней есть архитектурный баг — при старте загружаются сразу все плагины, которые установлены, а не только те, которые необходимы для выполнения текущей команды. Такого я от Ricardo Signes не ожидал.
                        0
                        Минилла не настраивается, гит вшит.
                        В неё для этой цели входит PluginRemover, так что можно отключить плагины Git и подключить аналогичные для Mercurial. Теоретически — на практике я этого не проверял, возможно будут какие-то нюансы с порядком вызова плагинов.

                        Вообще я Вас хорошо понимаю — сам до недавнего момента использовал исключительно Mercurial. Но суровая правда жизни в том, что Git победил. :( Так что можно продолжать любить Mercurial, но Git всё-равно необходимо хорошо знать и уметь использовать. А учитывая, что главное достоинство Mercurial — это юзабилити: его можно вполне полноценно использовать интуитивно, не тратя время на его изучение, вникание во внутреннее устройство репозитория, etc. — то после того, как потрачено время на полноценное изучение Git, основная причина продолжать использовать Mercurial как-то теряет актуальность. Когда разберёшься с Git и привыкнешь думать в терминах операций над репозиторием, а не над своим кодом, он становится вполне удобен.
                          0
                          Авторитет Торвальдса вывел Git в топ.

                          Жаль, mercurial, объективно, удачнее.

                          Вообще, гит нужен только из-за популярности гитхаба. Если атлассиан вломит денег в bitbucket — победит меркуриал.
                            0
                            А при чём тут гитхаб? С ним абсолютно без проблем можно работать через hg-git. И не думаю, что деньги теперь уже помогут — последний опрос на хабре показал порядка 70% гит и 15% меркуриал вроде бы.

                            Плюс, если с гитом уже разобрался, то формат репо у него всё-таки более здравый, и это тоже имеет определённое значение (я немного работал напрямую с репо меркуриала когда добавлял его поддержку в vcprompt).
                              0
                              Популярность гита напрямую связана с популярностью гитхаба.

                              Если бы битбакет появился раньше — сейчас бы пропорция была обратной.

                              hg-git — не всё поддерживает, лучше ставить полноценный гит клиент.

                              Репо в меркуриале лучше — он хранит диффы, гит — полные копии, его репо растет в размере гораздо быстрее.
                        0
                        В Milla автоматически генерируется только README, Changes и зависимости из cpanfile.

                        Это делается стандартными плагинами.

                        В общем это только профиль, никакой там особой философии нет.

                        В милле нет особой надобности, т.к. вы наверняка захотите расширить функционал рано или поздно и сделаете свой профиль.
                      0
                      Да, я свой профиль скопировал из Milla в начале. ;-)
                  0
                  > Этим же способом можно задавать зависимости не только для перл-модулей, но и для обычных приложений (у которых обычно нет Makefile.PL или Build.PL и зависимости указывать просто негде) — собственно, именно ради этой возможности cpanfile и был разработан. Причём можно задать для отдельной зависимости альтернативный источник, откуда её брать — из приватного CPAN mirror, из git, etc.

                  А как задать источник?
                    0

                    По-моему просто указать url в git-репо вместо имени модуля. Погуглите… вот, например: https://github.com/perl-carton/carton/issues/132

                      0
                      Там как-раз и обсуждается, что такой способ не работает. Миягава дальше пишет, мол, установка из гита — это геморрой и ему недосуг.
                      0
                      alaska332, Вы не в курсе?
                        0
                        Нужно, чтобы cpan клиент поддерживал другие репозитории.
                        cpanm поддерживает только cpan и развиваться больше не будет, на смену ему пришла библиотека Menlo.
                        На базе Menlo есть многопоточный App::cpm, который может ставить модули с гитаба.
                        Посмотрите документацию к нему, там все написано.
                          0
                          Ого, спасибо! App::cpm я как-то пропустил. Почитал доку — вроде бы оно делает ровно то, что мне и требуется.

                          Я только один момент не понял — cpm умеет использовать cpanfile.snapshot, а создавать он его умеет? Я просто еще попробовать не успел, может Вы пробовали и сможете сходу сказать?
                            0
                            Отвечаю сам себе: нет, cpm не умеет создавать cpanfile.snapshot.
                            0
                            Попробовал cpm. У сожалению, cpm не умеет читать cpanfile рекурсивно. Если устанавливаемый модуль X зависит от Y, а тот от Z, то cpm поставит только Y, а дальше не пойдет.

                            Беда.
                              0
                              Разве?
                              У меня он ставит большое кол-во модулей со всеми зависимостями, большая часть из которых — рекурсивные.
                              Без рекурсивных зависимостей он бесполезен.

                              Он сейчас активно разрабатывается, можно создать issue на гитхабе.
                              https://github.com/skaji/cpm/issues
                                0
                                А Вы проверьте, откуда он у вас читает зависимости. Скорее всего из метафайла. А я говорю именно о спанфайле, тут совсем другое кино начинается…

                                Написал тикет — https://github.com/skaji/cpm/issues/50
                                  0
                                  Ну вообще-то cpanfile это частный случай хранения зависимостей.
                                  Зависимости надо экспортировать в meta, чтобы работало везде.
                                    0
                                    Вот только мета не поддерживает установку из гита.

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

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