Перейти к содержанию

Изоляция установки и сборки в CI

Граница доверия становится полезной, когда разные стадии не получают все полномочия сразу. В этом уроке составим архитектурный план CI для установки, сборки и выпуска. Он описывает ресурсы и передачи, а не запускает pipeline.

«Контур выпуска» остаётся локальной лабораторией. Файл ci-contract.json — модель требований, не конфигурация рабочего GitLab ProfessorWeb. Секреты, аккаунты, сетевые установки и публикация не используются.

Разделение по полномочиям

Первая стадия получает необходимые зависимости и записывает состояние дерева. Для неё не нужен токен публикации сайта или пакета. Если в ней выполняется чужой код, область доступа должна соответствовать задаче установки. В план записываем read-only доступ там, где он достаточен, и отсутствие полномочий выпуска. Это требование будущей среды, а не эффект одного JSON.

Вторая стадия читает выбранные исходники и создаёт артефакт. Она может выполнять код сборщика, поэтому запрет install-скриптов не делает её безопасной автоматически. Основной npm run способен исполнить проектный скрипт даже при ignore-scripts. В договоре сборка является самостоятельной границей доверия и получает только необходимые источники, рабочую папку и выход.

Третья стадия принимает утверждённый артефакт, а не пересобирает случайную текущую ветку. Иначе между ревью и выпуском может измениться источник. В ci-contract.json у передачи есть source_revision, artifact_id, policy_version и verification_state. Для нашей подготовки фактические идентификаторы сборки неизвестны, поэтому значения остаются planned/null.

Разные job сами по себе не обеспечивают изоляцию. Они могут использовать общую файловую систему, кеш, сеть или постоянный runner. Реальная среда должна реализовать описанные границы и доказать, что права не перетекают. В лаборатории это список требований: чистое окружение, определённые входы, ограниченный доступ и понятный выход. Мы не заявляем, что контейнер автоматически решает все эти задачи.

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

Из проекта в проверяемую передачу

Маленький build.mjs показывает ожидаемое преобразование собственной страницы. Он не требует чужих пакетов. Если читатель позднее запустит его в выбранной среде, сможет сохранить реальные выходные файлы и отпечатки. В этой подготовке функция только прочитана как исходник, поэтому передача артефакта остаётся планом.

Для изменения pull request полномочия выпуска не должны появляться только потому, что файл похож на CI-конфигурацию. Сначала определяется, какая ветка, какой источник и какой workflow разрешены. Конкретные правила платформы необходимо сверить при реальном внедрении. У нас нет собственного запущенного runner, поэтому JSON не изображает готовую конфигурацию прав GitLab.

Рассмотрим отказ стадии. Если проверка соответствия источника не завершена, выпуск не продолжает работу автоматически. Статус verification_pending отличается от ошибки установки и от отрицательного ревью. В договоре указано, кто принимает решение и какое свидетельство позволяет двигаться дальше. Это делает остановку объяснимой, а не случайным красным индикатором.

Результат — три стадии с ограниченными ресурсами и явной передачей. Следующий урок опишет краткоживущие полномочия npm trusted publishing, сохранив важную границу: правильная аутентификация CI не делает вредоносную сборку доброкачественной.

Если проект только публикует статические файлы на хостинг, npm trusted publishing может вообще не участвовать в выпуске. Для сайта нужен свой механизм передачи, а npm относится к публикации пакетов. Наш учебный маршрут объясняет общий принцип границ и затем конкретный пример реестра. Не следует настраивать лишнюю внешнюю публикацию только ради использования модного механизма.

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

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

Чтение передачи между стадиями

Откройте ci-contract и найдите границу build → release. Передаётся ожидаемый артефакт, а не полномочия установки. У release собственное назначение и условие принятия. Если добавить ему исходную рабочую папку с произвольными скриптами, появляется новый ввод, который нужно объяснить. Простая последовательность трёх job не препятствует этому изменению.

Для каждого ресурса спросите, какая операция действительно его требует. Read-доступ к частному пакету нужен получению, write-доступ к месту выпуска — размещению. Постоянный токен всей организации не становится необходимым сборщику только потому, что ранее был установлен общей переменной. Такое рассуждение помогает сузить область до реализации конкретной платформы.

Установка и сборка могут иметь сетевой доступ, но его причины отличаются. Пакет может скачивать дополнительный бинарный файл, а сборщик — подтягивать внешний контент. Эти механизмы следует записать, иначе воспроизводимость зависит от неизвестного внешнего состояния. Наш маленький build.mjs читает только локальный JSON, поэтому граница примера значительно уже.

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

Критерий готовности плана — возможность объяснить каждый переход. Реальное доказательство изоляции появится только после настройки и отдельного разрешённого выполнения. Не изменяйте поле verification_state на passed после редакционного чтения текста: это другой вид работы. Сохранение такого различия позволит позднее внедрять инфраструктуру без путаницы между планом и фактическим выпуском.

Полный основной артефакт ci-contract.json выбранного состояния:

{
  "fictional": true,
  "not_live_ci_configuration": true,
  "stages": [
    {
      "id": "fetch",
      "publish_permissions": false,
      "private_dependency_read": "only if required",
      "execution": "not_run"
    },
    {
      "id": "build",
      "publish_permissions": false,
      "inputs": [
        "selected source",
        "selected dependencies"
      ],
      "output": "planned artifact",
      "execution": "not_run"
    },
    {
      "id": "release",
      "inputs": [
        "approved artifact"
      ],
      "destination": "explicit future release target",
      "execution": "not_run"
    }
  ],
  "transfer": {
    "source_revision": null,
    "artifact_id": null,
    "policy_version": "policy-v1",
    "verification_state": "pending"
  },
  "isolation_implemented": false
}

Все исходники серии: папка lesson-07 содержит полный самостоятельный снимок, собственный проект и авторские модели. Исполнение примеров, npm, установки, аудит, тесты, CI, настройки и публикация не выполнялись.

Первичные источники: ci, config.