Работа с Composer (PHP)

CodeScoring.Save работает как Composer-репозиторий (тип composer) и совместим с Composer 2.x. Можно создать:

  • proxy-репозиторий — кэширующий прокси к Packagist или к другому Composer-репозиторию (Private Packagist, Satis, Nexus, Artifactory, GitLab, другой Save);
  • hosted-репозиторий — хранилище для внутренних пакетов компании.

Composer 1 не поддерживается.

Создание репозитория в веб-интерфейсе

  1. В боковом меню выберите Projects и откройте проект.

  2. Нажмите Create repository.

  3. Заполните общие поля Name, Key, Description и Color — так же, как для других форматов (см. Управление репозиториями).

  4. В поле Format выберите PHP / Composer.

  5. В поле Type выберите Proxy или Hosted.

  6. Для Proxy заполните дополнительные поля:

    • Proxy URL — адрес Composer-репозитория, по которому доступен его packages.json:

      UpstreamПример адреса
      Packagisthttps://repo.packagist.org
      Nexushttps://nexus.example.com/repository/composer-proxy
      Artifactoryhttps://artifactory.example.com/artifactory/api/composer/php-remote
      Другой Savehttps://save2.example.com/cs-save/php/<project>/<repository>
    • Cache TTL, seconds — обязательное поле: сколько секунд Save хранит полученные от upstream данные, прежде чем перепроверить их. Для Packagist подойдёт 86400 (сутки).

    • Authentication — как Save авторизуется в upstream. Для Packagist оставьте none.

      ЗначениеПоляЧто отправляется в upstreamКогда выбирать
      none—Запросы без авторизацииПубличный upstream, например Packagist
      basicUsername, PasswordAuthorization: Basic ...Upstream принимает логин и пароль или токен вместо пароля — это случай Nexus, Artifactory и другого Save. Composer называет такой способ http-basic
      bearerTokenAuthorization: Bearer <token>Upstream принимает токен в заголовке Bearer — в терминах Composer это способ bearer

      Пароль и токен после сохранения не показываются ни в интерфейсе, ни в API: на форме редактирования сохранённое значение отображается как ******** — оставьте его, чтобы не менять. Учётные данные Save отправляет только на хост из Proxy URL (подробнее — Доступ proxy-репозиториев к upstream).

  7. Нажмите Create repository.

На странице созданного репозитория кнопка со значком «звено цепи» показывает готовые команды для подключения Composer именно к этому репозиторию.

Создание через API

То же самое можно сделать запросом к API. В format указывается php, учётные данные upstream передаются в блоке auth:

curl -X POST https://save.example.com/api/v1/repos \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "project": "web",
    "name": "nexus-composer-proxy",
    "format": "php",
    "repository_type": "proxy",
    "remote_url": "https://nexus.example.com/repository/composer-proxy",
    "auth": {"auth_type": "basic", "username": "<upstream-user>", "password": "<upstream-password>"},
    "cache_ttl": 86400
  }'

Для hosted-репозитория достаточно "repository_type": "hosted" без remote_url и auth.

Особенности proxy-репозитория
  • Архивы пакетов кэшируются при первой загрузке, следующие установки берут их из Save.
  • Метаданные пакетов Save пока не кэширует и при каждом запросе спрашивает у upstream. Если upstream недоступен, Composer сможет поставить только то, что есть в его собственном кэше.
  • Архивы пакетов Packagist хранятся на GitHub, поэтому Save нужен исходящий доступ к api.github.com и codeload.github.com.
  • Версии, для которых в upstream нет zip-архива (только исходный код в git), через proxy установить нельзя.

URL-схема

Все пути ниже указаны относительно адреса репозитория https://save.example.com/cs-save/php/<project>/<repository>:

ПутьЧто это
packages.jsonКорень репозитория
p2/<vendor>/<package>.jsonРелизные версии пакета
p2/<vendor>/<package>~dev.jsondev-ветки пакета
dist/<vendor>/<package>/<version>.zipАрхив пакета
packages/list.jsonСписок пакетов
search.json?q=<текст>Поиск

Все ссылки внутри packages.json и p2/... абсолютные: Save строит их по адресу, с которым к нему пришёл клиент (с учётом заголовков X-Forwarded-Host и X-Forwarded-Proto).

Подключение Composer

Адрес репозитория имеет вид https://save.example.com/cs-save/php/<project>/<repository>. Подключите его к проекту как репозиторий типа composer и отключите packagist.org, чтобы все пакеты шли через Save:

# Подключить репозиторий к текущему проекту
composer config repositories.save composer https://save.example.com/cs-save/php/<project>/packagist-proxy

# Отключить packagist.org, чтобы все запросы шли через Save
composer config repo.packagist false

То же самое в composer.json:

{
  "repositories": [
    { "type": "composer", "url": "https://save.example.com/cs-save/php/<project>/packagist-proxy" },
    { "packagist.org": false }
  ]
}

Save принимает http-basic — логин и пароль пользователя Save:

composer config [--global] http-basic.save.example.com <username> <password>

Дальше Composer работает как обычно: composer require, composer install, composer update.

Robot-аккаунты в CI

Для CI/CD можно использовать robot-аккаунт: composer config http-basic.save.example.com 'sa$<robot-login>' '<api-key>'. В CI логин и пароль удобно передавать переменной окружения COMPOSER_AUTH, не сохраняя auth.json в репозитории.

export COMPOSER_AUTH='{"http-basic": {"save.example.com": {"username": "sa$ci-builder", "password": "<api-key>"}}}'

Подробнее о robot-аккаунтах — в разделе Аутентификация.

Тестовые стенды без TLS

По умолчанию Composer работает только по https. Для стенда без TLS выполните composer config secure-http false.

Публикация пакетов в hosted-репозиторий

В Composer нет встроенной команды публикации: обычно пакеты попадают в Packagist или Satis из git-репозитория. Приватные хранилища (Nexus, Artifactory, GitLab) вместо этого принимают zip-архив пакета по HTTP. Save принимает архив так же, как Nexus, поэтому подходят инструменты, которые умеют публиковать в Nexus.

Командой composer push

Плагин elendev/composer-push добавляет в Composer команду composer push: она собирает zip-архив из текущего проекта (без vendor/, каталогов VCS и скрытых файлов) и загружает его.

  1. Установите плагин в проект пакета:

    composer require --dev elendev/composer-push
  2. Укажите адрес hosted-репозитория в composer.json:

    {
      "extra": {
        "push": {
          "url": "https://save.example.com/cs-save/php/<project>/php-hosted",
          "type": "nexus",
          "ignore": ["tests/", "docs/"]
        }
      }
    }
  3. Логин и пароль плагин берёт из auth.json для хоста Save — тех же, что вы задали командой composer config http-basic... выше.

  4. Опубликуйте версию:

    composer push 1.0.0

    Если версия указана в composer.json, её можно не передавать.

Не используйте

--src-type, --src-url и --src-ref С этими параметрами плагин отправляет архив в другом формате, и Save ответит 400. Ссылка на исходный код в git для пакетов из Save не нужна: Composer скачивает их архивом.

Из CI без PHP

Если в CI нет PHP, загрузите архив через curl. Параметр --upload-file отправляет файл запросом PUT. Имя пакета и версия указываются в адресе:

curl -u "<username>:<password>" \
  --upload-file acme-demo-1.0.0.zip \
  https://save.example.com/cs-save/php/<project>/php-hosted/packages/upload/acme/demo/1.0.0

Можно загрузить архив и на адрес самого репозитория — тогда имя и версия берутся из composer.json в архиве. Если версии в composer.json нет, передайте её параметром ?version=:

curl -u "<username>:<password>" \
  --upload-file acme-demo-1.0.0.zip \
  "https://save.example.com/cs-save/php/<project>/php-hosted?version=1.0.0"

Как собрать архив

Удобнее всего — командой composer archive. Она учитывает archive.exclude и export-ignore из .gitattributes:

composer archive --format=zip --file=acme-demo-1.0.0

Подойдёт и git archive --format=zip -o acme-demo-1.0.0.zip v1.0.0. При публикации через composer push архив собирать не нужно.

Требования к пакету

Save проверяет архив до сохранения, поэтому при ошибке ничего не записывается:

  • архив — zip, а composer.json лежит в его корне или в одной папке верхнего уровня (как в архивах GitHub и GitLab);
  • name в composer.json задан в формате vendor/package. Если имя указано и в адресе загрузки, оно должно совпадать с composer.json;
  • версия — по правилам Composer. Её можно указать в version в composer.json или передать при загрузке. Если она есть в обоих местах, значения должны совпадать (v1.0.0 и 1.0.0 считаются одной версией). Ветку со слешем в имени (dev-feature/x) загрузить нельзя;
  • размер архива — не больше HOST_PHP_MAX_FILE_SIZE (по умолчанию 256 МБ), иначе Save ответит 413.

Опубликованную версию нельзя перезаписать: повторная загрузка вернёт 409. Чтобы заменить версию, сначала удалите её.

Удаление версии (hosted)

Версию можно удалить в веб-интерфейсе, как любой артефакт. Есть два способа:

  • отметьте артефакт флажком слева в дереве репозитория и нажмите Delete на панели, которая появится над деревом. Так можно удалить сразу несколько версий;
  • нажмите на артефакт в дереве репозитория и в правой панели выберите Delete artifact в меню с тремя точками (рядом с Download).

Или через API:

curl -u "<username>:<password>" \
  -X DELETE https://save.example.com/cs-save/php/<project>/php-hosted/packages/acme/demo/1.0.0

Изменения сразу видны в метаданных.

Миграция URL репозитория

Сценарий использования: миграция Composer-репозитория с Nexus / Artifactory на CodeScoring.Save.

Источникurl в composer.json до миграцииurl в composer.json после миграции
Nexushttps://nexus.host.ru/repository/composer-proxy/https://save.example.com/cs-save/php/<project>/composer-proxy
Artifactoryhttps://jfrog.host.ru/artifactory/api/composer/php-remotehttps://save.example.com/cs-save/php/<project>/php-remote
Официальный репозиторийhttps://repo.packagist.orghttps://save.example.com/cs-save/php/<project>/packagist-proxy

Адрес загрузки packages/upload/<vendor>/<package>/<version> совпадает с Nexus, поэтому в composer push и в скриптах публикации достаточно поменять адрес. После смены адреса выполните composer update, чтобы в composer.lock записались новые ссылки на архивы.

Устранение неполадок

Какие запросы делает Composer

Запустите Composer с ключом -vvv — он покажет каждый адрес, к которому обращается:

composer update -vvv

Все запросы должны идти на адрес Save. Если ссылки начинаются с http://, хотя Save открыт по https, проверьте, что ingress передаёт заголовки X-Forwarded-Proto и X-Forwarded-Host.

Проверка метаданных пакета

curl -u "<username>:<password>" \
  https://save.example.com/cs-save/php/<project>/packagist-proxy/p2/monolog/monolog.json

404 означает, что такого пакета нет ни в Save, ни в upstream.

Состояние сервиса

curl https://save.example.com/health

Аудит по репозиторию

curl -u "<username>:<password>" \
  "https://save.example.com/api/v1/admin/audit?resource_type=repository&q=packagist-proxy&limit=50"
Страница была полезна?