Это перевод статьи Кента Додса  «Why I don’t commit generated files to master».

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

Что такое «сгенерированные файлы»?

В контексте библиотеки, сгенерированные файлы — это собранные, скомпилированные и минифицированные версии, которые пользователь подключает на свою страницу, например через <script>.

Для библиотеки из одного файла сборка не нужна, но для работы модульной библиотеки в старых браузерах, без поддержки модулей ES6, необходимо эти модули собрать в один файл. Такую сборку я и называю «генерацией файлов».

Зачем нужна сборка?

Вы возможно испытываете искушение сделать коммит созданных файлов в репозиторий по нескольким причинам:

  1. Они необходимы для bower (перестаньте использовать bower).
  2. Это проще для людей, которые хотят просто скачать файлы и не используют пакетные менеджеры типа npm или bower.
  3. Используя rawgit, собранные файлы можно легко подключить напрямую из репозитория библиотеки на GitHub в свой проект или в демонстрации на jsbin или plunker.

Довольно долго я тоже коммитил свои снегенированные файлы в репозиторий библиотек, и это работало отлично! Пока я не столкнулся с проблемами.

Проблемы

Вот несколько проблем.

Обманчивые скачки изменений кода в статистике

Скачки изменений кода в статистике
Скачки изменений кода в статистике

Выглядит как большое количество новых изменений, не так ли? Нет! Этот скачок произошёл, когда сборка была добавлена в ng-stats (было и несколько других существенных изменений). Последующие скачки происходили всякий раз, когда выпускалась новая версия (и следовательно, повторная сборка заново коммитилась). Становится сложно понять, сколько кода изменилось на самом деле в проекте.

Ужасный git diff

Ужасный diff
Ужасный diff

При разработке библиотеки нужно убедиться, что коммит сгенерированных файлов делается только при выпуске релиза (но не при других изменениях файлов), в противном случае будет намного сложнее определить в git diff (утилита для просмотра отличия в коде между коммитами), когда и какие изменения были внесены в код.

Но даже при коммите собранных файлов только при выпуске версии diff двух коммитов, разделённых сборкой, превращается в кошмар.

Сложность совместной разработки

Для open source кода важно обеспечить лёгкость совместной разработки и привлечения новых программистов. Я часто получал пулреквесты, с изменениями в сгенерированных файлах, а не в исходных — и эти изменения будут перезаписаны при следующем запуске сборки! В других пулреквестах в код включены собранные файлы, из-за чего сделать ревью кода значительно сложнее (смотрите пункт «Ужасный git diff»).

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

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

Моё решение

Можно хранить собранные файлы в репозитории без вышеперечисленных недостатков. Для этого отправляйте сгенерированные файлы в специальную ветку. Я называю такую ветку «latest», и автоматизирую этот процесс с помощью пакета publish-latest.

Вы запускаете сборку вашей библиотеки, затем пакет publish-latest. С помощью скрипта, он создаёт коммит из сгенерированных файлов и добавляет его в ветку latest. Запускать publish-latest можно и локально, но будет лучше использовать semantic-release и TravisCI (или любой другой CI-сервис) для подготовки релизов вашей библиотеки.

Преимущества моего метода

  1. Для bower отметьте тегом коммит, сделанный при помощи publish-release (semantic-release делает это за вас).
  2. Для прямого скачивания сгенерированных файлов используйте созданную ветку «latest».
  3. Для rawgit тоже укажите ветку «latest».

Как этот метод решает проблемы?

  1. Большие скачки изменений в гите — просто игнорируйте ветку latest, сфокусируйтесь на master (с помощью графика изменений от контрибьюторов в GitHub).
  2. Огромные git diffs — вам никогда не понадобится git diff на ветке «latest». Это просто сгенерированный (бесполезный для гита) код.
  3. Пулреквесты в ваш код — напишите в руководстве по внесению изменений (Contributing.md), что ветка latest только для хранения сборок и пулреквест следует делать от других веток.

В качестве примера работы с publish-latest посмотрите мои репозитории и проекты других людей.

Заключение

Существует множество инструментов, которые помогут вам в развитии ваших open source библиотек.

Мои примеры на Egghead.io демонстрируют процесс с самых азов до строительства большой библиотеки. Я рекомендую вам к просмотру статью «Как писать JavaScript библиотеку с открытым исходным кодом».

FAQ

  • Работает этот метод с CDNJS? С angular-formly — очень хорошо.
  • Как насчёт того, чтобы хранить ветку latest в отдельном репозитории? Это, кстати, неплохая идея. Используя флаг url в publish-latest, вы сможете легко это сделать.