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

Стадии и изоляция заданий

Курс «Лаборатория GitLab CI/CD»

Первый job лаборатории course-card только сообщает о среде выполнения. Добавим настоящую сборку и отдельное задание для проверки. На этом шаге важно увидеть две связи: порядок работы и передачу результата. Их нельзя заменить друг другом. Следующая стадия может начать работу после предыдущей и всё равно не получить созданные там файлы.

Будем использовать готовые программы из архива. build.mjs читает courses.json, вставляет карточки в шаблон и создаёт dist/. verify.mjs проверяет структуру метаданных и соответствие хэшей. Обе программы используют встроенные модули Node.js, поэтому npm-зависимости здесь не устанавливаются. Их команды в уроке не выполнялись.

Стадии и задания

Замените первый вариант .gitlab-ci.yml следующим полным файлом. Список stages задаёт порядок групп; jobs указывают принадлежность к группе через stage. Имена build и verify в списке могут быть другими, если ссылки заданий согласованы. В нашем примере названия отражают результат каждого шага.

default:
  image: node:24-bookworm-slim

stages:
  - build
  - verify

build-site:
  stage: build
  script:
    - node scripts/build.mjs
  artifacts:
    paths:
      - dist/
    expire_in: 7 days

verify-site:
  stage: verify
  script:
    - node scripts/verify.mjs

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

build-site создаёт файлы, после чего runner сохраняет указанную папку как артефакт. В стандартной схеме последующее задание получает артефакты предыдущих стадий. В журнале самостоятельного запуска ожидается отдельный этап скачивания перед script. Этот этап объясняет появление dist/ в новом job; общая история коммитов сама по себе её не создаёт.

Почему одинаковая папка не общая

В рабочем каталоге первого задания появился dist/index.html. После завершения контейнер или временная область может быть очищена. Второе задание получает исходники того же commit, но сгенерированный dist/ не является частью этих исходников. Без artifacts оно начинает с репозитория, в котором такой папки нет.

Именно этот ошибочный случай показан в доступной вводной демонстрации Валентина Деспа: test не находит созданный build-файл, пока автор не объявляет передачу артефакта. Наша сборка меняет содержание, но причина сохраняется. Этот механизм также раскрывается в официальном tutorial построения сложного pipeline.

Попробуйте мысленно убрать artifacts из первого job. Порядок стадий сохранится, однако verify.mjs завершится с сообщением об отсутствующих файлах. Добавить перед проверкой ещё одну сборку означало бы получить новый результат вместо проверки первого. Такая конфигурация могла бы стать зелёной, скрыв ошибку передачи. Нам нужен договор «проверяем тот результат, который построен раньше».

Не полагайтесь на случайно оставшиеся файлы shell runner. Даже если два задания физически выполняются на одном компьютере, конфигурация должна явно выражать передачу результата. Временная папка или совпавшее имя рабочей области не является устойчивым интерфейсом между jobs. Такая зависимость проявится после смены исполнителя и затруднит поиск причины.

Пути и срок хранения

artifacts:paths задаётся относительно рабочей области проекта. В нашем случае dist/ — результат build.mjs. Если программа записывает output/, а конфигурация сохраняет dist/, runner не сможет сохранить ожидаемую папку. Поэтому имя результата должно совпадать в программе, YAML и тексте урока.

Срок expire_in: 7 days описывает выбранный срок хранения учебного артефакта. Реальные правила удержания и доступность могут зависеть от настроек GitLab, включая сохранение результатов последнего успешного pipeline. Не рассматривайте это значение как обещание, что файл исчезнет ровно в заданный момент или будет доступен при любых правах. Подробности находятся в документации job artifacts.

Не сохраняйте весь рабочий каталог через слишком широкую маску. В нём могут находиться конфигурации, отчёты и временные сведения, которые не относятся к выпуску. Для course-card договор конкретен: строим статический каталог и сохраняем dist/. Позднее отчёты проверки получат отдельные paths и особый режим сохранения при ошибке.

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

Параллельные задания одной стадии не должны обмениваться файлами через случайную общую папку. Если одному из них нужен результат другого, это уже содержательная зависимость, которую требуется выразить отдельно. Иначе порядок запуска будет зависеть от доступности исполнителей и появится редкая ошибка, которую трудно воспроизвести. В нашей первой схеме build расположен раньше verify и сохраняет выход. При добавлении новых jobs сначала определите их входы, затем выберите stage или needs. Такой порядок проектирования позволяет объяснить процесс ещё до первого запуска и не требует угадывать, почему одна машина случайно дала зелёный результат.

Относительный путь артефакта читается от рабочей директории проекта в job. Если генератор записал dist в другой вложенной папке, декларация paths: dist/ её не найдёт. Уточняйте пару «место записи — место сохранения» вместе, затем отдельно проверяйте место чтения в verify. Совпадение названий jobs здесь ничего не меняет: GitLab передаёт указанные файлы, а программа открывает собственный относительный путь. В архиве все команды рассчитаны на корень лаборатории, что позволяет проследить эту связь без дополнительных переходов между директориями.

Чтение состояния pipeline

Пример стадий pipeline из официальной документации GitLab

Реальное изображение из документации GitLab показывает группировку заданий по стадиям. Имена jobs отличаются от course-card; изображение не является результатом запуска нашей лаборатории.

Зелёный build и красный verify означают разные результаты. В первом создан файл, во втором не выполнено условие проверки. Начинайте разбор с первого ошибочного job и его журнала. Сообщение «не найден dist/release.json» говорит о файле и передаче, а неверный хэш — о содержимом. Цвет всей строки pipeline не заменяет чтение конкретной причины.

Если build завершился ошибкой, verify при обычной схеме не должен создавать впечатление проверенного результата. Не назначайте allow_failure: true основной проверке только ради зелёного pipeline. Такой параметр выражает допустимую необязательную ошибку, а наша проверка является условием качества артефакта. Состояние должно соответствовать договору.

Ожидаемый результат — отдельная сборка и отдельная проверка одного dist/. Файлы передаются явно, а причины отсутствия и несоответствия можно различить. В следующем уроке разберём программы и коды завершения, чтобы сообщение в журнале не скрывало настоящую ошибку.