open atlas
↑ К треку
CI/CD-пайплайны CICD · 12 · 04

Capstone: чтение реального deploy.yml как кейс-стади по release engineering

Прочитай один реальный деплой-workflow от и до — concurrency cancel-in-progress, artifact-free build-deploy джоб, cache-keyed state, ~12-минутную сборку — и оцени против планки release engineering: версионирование, changelog, гейты, обратимость. Критика, а не recall.

CICD Senior ◷ 18 min
Уровень
ОсновыJuniorMiddleSenior

Пайплайн, что ты сейчас прочитаешь, реален — это форма workflow, что деплоит большой статический учебный сайт, и он заработал свою текущую форму тяжёлым путём. Более ранняя версия шардила сборку по шести параллельным джобам и передавала ~420 МБ артефактов между ними на каждом ране; это разнесло квоту artifact-storage free-tier, и раз квота была достигнута, она блокировала каждый upload — даже файл 163 КБ — пока платформа не пересчитывала использование шесть-двенадцать часов спустя, заклинивая каждый деплой. Фикс был не большей квотой; а структурным переписыванием на единственный artifact-free джоб build-deploy, что держит state лишь в build-кэше. Эта история — суть этого capstone: релиз-пайплайн судится не по тому, зелён ли он сегодня, а по тому, как он падает, как восстанавливается и можешь ли ты ответить «что отгрузилось и как откатить» в 3 ночи. Прочитай этот workflow так, как senior ревьюит пайплайн коллеги — найди, что он делает хорошо, что оставляет на столе и что бы ты изменил, прежде чем он понесёт что-то, двигающее деньги.

Workflow на ревью

Вот форма пайплайна, сжатая до несущих частей:

name: build-deploy
on:
  push:
    branches: [main]

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: true        # новый push отменяет деплой в полёте

permissions:
  contents: read

jobs:
  build-deploy:                    # один джоб: plan → build → lint → deploy
    runs-on: ubuntu-latest
    timeout-minutes: 90
    steps:
      - uses: actions/checkout@v4
      - uses: actions/cache@v4      # state живёт здесь, не в артефактах
        with:
          key: build-${{ hashFiles('content-hash') }}
          path: dist/
      - run: bun install --frozen-lockfile
      - run: bun run build          # полный рендер ~12 мин (инкрементальный короче)
      - run: cat dist/lint-report.json   # build-time linter-гейт
      - run: ./deploy.sh dist/       # upload статики на CDN/Pages-хост

Что он делает хорошо, в словаре этого юнита:

  • concurrency с cancel-in-progress: true, ключеванный по ref, означает, что новый push в main отменяет более старый, ещё бегущий деплой той же ветки. Ты никогда не получаешь два деплоя, гоняющихся за публикацию, и никогда не тратишь 12-минутную сборку на коммит, что уже вытеснен. Это верный дефолт для деплоя статики на единую цель, где важно лишь последнее состояние.
  • Единственный artifact-free джоб — рубцовая ткань из Hook: plan, build, lint и deploy бегут в одном джобе и передают state через actions/cache (отдельная от артефактов квота), поэтому нет передачи 420 МБ, что заклинит. Держание всего пайплайна в одном джобе также означает, что нет меж-джобного артефакта, что истечёт или повредится — цена в том, что шаги не могут масштабироваться вширь, что для ~12-минутной сборки приемлемый размен.
  • permissions: contents: read — least privilege: токен джоба может читать репо и ничего больше, поэтому скомпрометированный шаг не может пушить коммиты или публиковать пакеты.
  • Build-time linter (lint-report.json) — релиз-гейт внутри сборки: контент, нарушающий правила, валит ран до того, как что-либо отгрузится.
Викторина

Почему `concurrency: { group: deploy-${{ github.ref }}, cancel-in-progress: true }` — верный выбор для этого деплоя статики на единую цель, и где он был бы опасен?

Оцени против планки release engineering

Читая любой пайплайн, попробуй разделить два вопроса: «это корректно деплоит?» и «это управляет релизом?» Большинство пайплайнов отвечают да на первый и нет на второй. Workflow операционно крепок, но прочитай его против четырёх столпов этого юнита, и дыры ясны — и поучительны, потому что они разница между «деплоит статику» и «управляет релизом».

  • Версионирование и теги: этот пайплайн деплоит на каждый push в main без версии, без тега и без неизменяемого ref. Для статики, что публикует latest-wins, это защитимый выбор — но он означает, что нет именованного релиза, на который указать, а «откатить на последнюю хорошую версию» нечего именовать. Митигация, что подходит этому дизайну — собственная неизменяемая история деплоев хоста (каждый upload — отдельный, адресуемый деплой, что можно мгновенно пере-промоутить), которая заменяет git-тег как якорь обратимости.
  • Changelog и релизная запись: нет генерируемого changelog и нет релизного объекта, поэтому «что изменилось в этом деплое» отвечаемо лишь чтением коммита, что его триггернул. Приемлемо для внутреннего сайта; дыра в момент, когда внешним потребителям или аудиторам нужна запись на релиз.
  • Окружения и гейты: деплой бежит прямо в production на push без окружения, без required reviewer и без wait-таймера. Каждый смерженный коммит отгружается мгновенно без человека на момент релиза и без выдержки. Это самая большая дыра против юнита: для чего угодно сверх низко-ставочной статики ты бы поставил деплой за окружение production с required reviewer и коротким wait-таймером, и вероятно разделил build (на push) и deploy (гейтированный).
  • Обратимость: нет явного пути отката в workflow. Восстановление целиком зависит от способности хоста деплоя пере-промоутить предыдущий деплой. Это работает для статического CDN-хоста, но это неявно — senior сделал бы «передеплой предыдущий известно-хороший деплой» явным, протестированным, одно-командным путём, а не свойством, что он предполагает у платформы.
Викторина

Ревьюер говорит, что у этого пайплайна 'нет истории отката'. Учитывая, что он деплоит статику на CDN-хост с неизменяемой историей деплоев на upload, что самая точная критика и фикс?

Почему это работает

Почему идеально зелёный, хорошо собранный пайплайн всё равно не дотягивает до планки юнита? Потому что «собирает и деплоит корректно» и «управляет релизом» — разные работы. Этот workflow прибивает операционный слой — конкурентность, least privilege, lint-гейт и закалённая-сбоем структура одного джоба, заработанная из реальной аварии. Но release engineering добавляет слой управления поверх: именованную, неизменяемую вещь, что каждый деплой производит (версию/тег или адресуемый деплой), человекочитаемую запись о том, что изменилось, намеренный человеческий гейт перед production и явный, протестированный путь назад. Контекст статики делает некоторые из них опциональными — публикация latest-wins по-настоящему не нуждается в semver — но в момент, когда тот же пайплайн несёт что-то, от чего зависит пользователь или что инспектирует аудитор, каждый опущенный столп становится дырой, что находит инцидент. Хорошо читать реальный пайплайн значит называть, какие упущения оправданы контекстом, а какие — латентные инциденты.

Вспомните перед уходом
  1. 01
    Резюмируй, что этот пайплайн делает хорошо операционно и почему каждый выбор крепок для деплоя статики на единую цель.
  2. 02
    Пройди четыре столпа release engineering и оцени этот пайплайн по каждому, отличая оправданные контекстом упущения от латентных инцидентов.
Итог

Capstone читает один реальный деплой-workflow как кейс-стади и судит его так, как senior судит пайплайн коллеги. Операционно он крепок, и его форма заработана: ключеванный по ref concurrency с cancel-in-progress отменяет вытесненный деплой, чтобы два не гонялись и ни одна долгая сборка не тратилась; единственный artifact-free джоб build-deploy, передающий state через actions/cache — структурный фикс реальной аварии, где 420 МБ меж-джобных артефактов исчерпали квоту хранилища и заклинили каждый upload на часы; permissions: contents: read — least privilege; а build-time linter гейтит несоответствующий контент до отгрузки. Судимый против четырёх столпов этого юнита, однако, слой управления тонок. Нет версии, тега или неизменяемого ref — приемлемо для latest-wins публикации статики, с неизменяемой историей деплоев на upload хоста как якорем обратимости. Нет генерируемого changelog или релизного объекта, поэтому «что изменилось» — это лишь триггерящий коммит. Самая большая дыра — гейты: пайплайн отгружает прямо в production на push без окружения, required reviewer или wait-таймера, поэтому каждый merge релизит мгновенно без человека на момент релиза и без выдержки — для чего-то сверх низко-ставочного сайта ты бы загейтил деплой за окружение production и вероятно разделил build и deploy. А откат неявен, опираясь на фичу пере-промоута платформы, а не явный, протестированный одно-командный путь. Урок чтения реального пайплайна в том, что быть зелёным сегодня — не планка: релиз-пайплайн судится по тому, как он падает, как откатывается и можешь ли ты ответить «что отгрузилось и как откатить» под давлением — а честное ревью называет, какие упущения контекст оправдывает, а какие просто следующий ждущий инцидент. Теперь, когда откроешь любой deploy.yml, ты знаешь, какие два вопроса задать первыми — и как отличить оправданное упущение от латентного инцидента.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 6 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.