Сайт Романа ПарпалакаЗаметкиТехнологииВеб-разработкаУправление зависимостями на примере composer

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

28 августа 2016 года

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

Я спрашиваю, зачем это, пытаюсь увидеть пользу. Я вижу, как это усложняет систему, но не вижу, как помогает […] Я пытаюсь разобраться.

Объясняю на примере Эгеи — движка блогов, который разрабатывает сам Илья.

Суть проблемы

У Эгеи есть несколько зависимостей. Ты разрабатываешь Лайкли, Жуэль, Нисден — каждый продукт в своем репозитории. А еще есть jQuery, плагины для календаря, растягивания полей ввода текста по содержимому и т. д.

В «традиционном» подходе ты скачал компоненты, положил рядом с кодом. Может быть, даже добавил в гит, чтобы не потерять.

Прошло полгода. Пора делать новый релиз. И если нет смысла торопиться обновлять jQuery, наверняка ты захочешь обновить свои же зависимости. Опять копируешь файлики, делаешь коммит Эгеи. Кто-то сообщает об ошибке. Воспроизводишь. Ошибка оказывается в Лайкли. Исправляешь. Делаешь коммит в Лайкли. Копируешь файлики. Делаешь коммит Эгеи.

В чем недостатки этого подхода?

  1. История в репозитории Эгеи оказывается забитой посторонними изменениями. Кому интересно видеть в git log, что добавилось в jQuery между версиями 2.1.3 и 2.1.4?
  2. На рутинную работу уходит всё больше и больше времени.
  3. Более того, рутинная работа — потенциальный источник ошибок. Когда нет скрипта, который собирает дистрибутив по одной команде, приходится собирать архив вручную. Смотреть, чтобы в нем были все нужные файлы и не попало что-нибудь лишнее.

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

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

В каждой области существуют свои инструменты управления зависимостями. В серверном яваскрипте — npm. В браузерных js- и css-библиотеках — bower. Последний скачивает их из своего репозитория или из любого гит-репозитория, например, с гитхаба.

В мире PHP ту же работу выполняет composer. Мне больше нравится его модель работы с фиксацией версий в lock-файле.

Composer

Composer работает с двумя файлами: composer.json и composer.lock. В первом мы перечисляем пакеты и версии, от которых зависит наш проект, например, "jquery": "2.1.*". Команду composer update запускаем в ходе разработки при обновлении версии имеющейся зависимости или добавлении новой. composer update скачивает пакеты в папку vendor и запишет точные версии пакетов (например, 2.1.4) во второй файл composer.lock.

$$\text{\tt\large composer update:}\vspace{0.3cm} \tikzstyle{block} = [rectangle, fill=red!15, text width=7em, text centered, minimum height=3em] \begin{tikzpicture}[node distance = 3.6cm] \node [block] (json) {\tt composer.json\\ 2.1.*}; \node [block, right of=json] (lock) {\tt composer.lock\\ 2.1.4}; \node [block, right of=lock] (vendor) {\tt vendor/}; \draw [->] (json) to[out=-30, in=210] node[below] {\text{\small определяет версию}} (lock); \draw [->] (lock) to[out=-30, in=210] node[below] {\text{\small скачивает}} (vendor); models}; \end{tikzpicture}$$

Оба файла composer.json и composer.lock добавляются в гит. Тем самым автор коммита гарантирует, что код заработает с указанными конкретными версиями зависимостей (2.1.4). А вот папка vendor в гит не добавляется. В итоге гит-репозиторий содержит чистую историю изменений нашего кода без технических коммитов с обновлением зависимостей.

Команда composer install разворачивает код на сервере или у другого разработчика. Она не проверяет существование новых версий пакетов, а восстанавливает зависимости на момент коммита из файла composer.lock.

$$\text{\tt\large composer install:}\vspace{0.3cm} \tikzstyle{block} = [rectangle, fill=green!18, text width=7em, text centered, minimum height=3em] \begin{tikzpicture}[node distance = 3.6cm] \node [block, right of=json] (lock) {\tt composer.lock\\ 2.1.4}; \node [block, right of=lock] (vendor) {\tt vendor/}; \draw [->] (lock) to[out=-30, in=210] node[below]{\text{\small скачивает}} (vendor); models}; \end{tikzpicture}$$

Пакеты композера хранятся на packagist.org. Но есть возможность подключить любой гит-репозиторий как зависимость, не добавляя его на packagist. Пишем в composer.json

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/ilyabirman/Jouele"
        }
    ],
    "require": {
        "ilyabirman/jouele": "dev-master"
    }
}

и composer update всегда будет обновлять библиотеку до последнего коммита в ветке master.

Composer делает еще одну важную вещь — настраивает автоматическую загрузку библиотек. Подключаем сгенерированный файл autoload.php:

require __DIR__ . '/vendor/autoload.php';

и используем классы из зависимостей без дополнительных инструкций include/require.

Особенности bower и npm

Недостаток менеджеров bower и npm в том, что у них нет аналогов команды composer update и файла composer.lock. Они не гарантируют, что восстановят все зависимости до того состояния, на котором ты проверил, что всё работает. Например, если в bower.json написали "jquery": "2.1.*", то команда bower install будет скачивать новые версии jquery по мере их выхода, даже если ты этого не хочешь.

Как это всё использовать?

Для разработки: ты добавляешь к своим зависимостям composer.json и делаешь их composer-пакетами. Затем добавляешь их в основной проект в composer.json. И composer update скачивает в папку vendor последние версии зависимостей.

Для сборки: ты не хранишь в репозитории саму папку vendor. Пишешь bash-скрипт, который делает git clone и composer install --no-dev, а потом архивирует результат.

Инструменты управления зависимостями обеспечивают масштабируемый подход. Они одинаково хорошо работают и с несколькими зависимостями, и с десятками или даже сотнями. Последняя ситуация — не редкость при использовании php-фреймворков. Например, команда composer create-project symfony/framework-standard-edition my_project_name скачивает типовое приложение на symfony и устанавливает 35 зависимостей суммарным объемом 33 мегабайта.

Поделиться
Посмотрите в блоге

Комментарии

#1. 30 августа 2016 года, 01:55. Дима Семьюшкин пишет:
В npm есть npm update и есть возможность указывать конкретные версии пакетов, это заменяет lock. Зачем отдельный файл?
#2. 30 августа 2016 года, 14:09. пишет:
Я как раз об этом писал. json-файл описывает множество версий, с которыми проект в принципе должен заработать. lock-файл — конкретные версии, на которых проект был протестирован, и которые уходят в production.

Добавил картинок, чтобы было понятнее.
#3. 26 ноября 2016 года, 21:44. Игорь М. пишет:
Здравствуйте Роман!
Я так понимаю движок S2 начал использовать composer? :)

P.S. Что-то совсем мало статей по веб-разработке, за два года одна статься с тегом PHP, печально это.
#4. 27 ноября 2016 года, 22:29. пишет:
Промежуточная неопубликованная версия — да.

А какие именно темы интересуют? Мне кажется, что любая статья будет тривиальным повторением того, о чем уже и так много раз везде писали.
#5. 27 ноября 2016 года, 22:39. Игорь М. пишет:
Было бы интересно узнать ваше личное мнение о чем-то или взгляд под другим углом.
Например:
— «Я не понимаю зачем этот Dependency injection когда есть.»
— Как я использовал Service Locator для своего проекта
— Memcached vs Redis
— А вот как я отбился от DoS
— Появился PHP7 — Ruby умирает (холиварчик)
#6. 28 ноября 2016 года, 00:19. пишет:
Попробую что-нибудь по первой теме написать. Хотя вот на хабре есть хороший материал: https://habrahabr.ru/post/183658/
#7. 28 ноября 2016 года, 00:22. Игорь М. пишет:
Отлично! Ждем :)
Статья понравилась, спасибо!
#8. 21 января 2017 года, 16:18. Кирилл Садовник пишет:
У npm есть лок-файлы: https://docs.npmjs.com/cli/shrinkwrap

Но с появлением yarn пользоваться npm больше нет смысла.
#9. 21 января 2017 года, 21:42. пишет:
Кирилл, спасибо за ссылку. Обязательно попробую yarn при случае. Тормознутый npm достал :)

Оставьте свой комментарий

Ваше имя:

Комментарий:

Для выделения используйте следующий код: [i]курсив[/i], [b]жирный[/b].
Цитату оформляйте так: [q = имя автора]цитата[/q] или [q]еще цитата[/q].
Ссылку начните с http://. Других команд или HTML-тегов здесь нет.

Сколько будет 28+2?