Интеграция с CI/CD

Общая роль Rollup в CI/CD процессе

В современных фронтенд-проектах сборка становится неотъемлемой частью автоматизированного жизненного цикла доставки кода. Rollup в этом контексте выступает как инструмент, обеспечивающий детерминированную компиляцию модулей, tree-shaking и генерацию оптимизированных бандлов для различных окружений.

В CI/CD-процессах Rollup обычно выполняется как отдельный этап после установки зависимостей и перед тестированием или деплоем. Его задача — преобразовать исходный модульный код в готовые артефакты, которые можно безопасно публиковать в staging или production.

Ключевая особенность интеграции Rollup в CI/CD заключается в том, что он должен работать без интерактивного ввода, быть воспроизводимым и одинаково выполняться в разных средах.


Базовые требования к Rollup-сборке в CI

При использовании Rollup в автоматизированных пайплайнах необходимо учитывать ряд требований:

  • Полная детерминированность сборки
  • Отсутствие зависимостей от локального окружения
  • Явное управление версиями Node.js
  • Минимизация побочных эффектов плагинов
  • Корректная обработка переменных окружения

Особенно важно избегать ситуаций, когда результат сборки зависит от времени выполнения или скрытых системных переменных. CI должен всегда воспроизводить одинаковый бандл при одинаковом входном коде.


Структура типичного CI-процесса с Rollup

Обычно пайплайн включает следующие этапы:

  1. Установка зависимостей
  2. Линтинг и статический анализ
  3. Сборка через Rollup
  4. Прогон тестов
  5. Публикация артефактов

Rollup занимает центральное место между анализом кода и тестированием, так как формирует финальную версию модулей.


Конфигурация Rollup для CI

Конфигурационный файл Rollup должен быть максимально предсказуемым. Пример базовой структуры:

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  output: [
    {
      file: 'dist/bundle.esm.js',
      format: 'esm',
      sourcemap: true
    },
    {
      file: 'dist/bundle.cjs.js',
      format: 'cjs',
      sourcemap: true
    }
  ],
  plugins: [
    resolve(),
    commonjs(),
    babel({ babelHelpers: 'bundled' })
  ]
};

В CI важно фиксировать:

  • версии плагинов
  • версии Node.js
  • порядок подключения плагинов

Даже незначительное изменение порядка может привести к различиям в выходных артефактах.


Переменные окружения и режимы сборки

CI/CD часто требует разных режимов сборки:

  • development
  • staging
  • production

Rollup обычно управляет этим через process.env.NODE_ENV или отдельные флаги.

Пример:

const production = process.env.NODE_ENV === 'production';

export default {
  plugins: [
    production && terser()
  ]
};

В CI важно явно задавать переменные окружения, а не полагаться на значения по умолчанию.


Использование кэширования в CI

Один из ключевых способов ускорения сборки — кэширование зависимостей и промежуточных результатов.

В контексте Rollup можно кэшировать:

  • node_modules
  • кеш npm/yarn/pnpm
  • директории .rollup.cache (если используется кастомная реализация)
  • результат dist, если сборка инкрементальная

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


Интеграция с GitHub Actions

Типичный workflow для Rollup-сборки:

name: build

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

В данном случае npm run build должен запускать Rollup без дополнительных интерактивных шагов.


Интеграция с GitLab CI

Пример .gitlab-ci.yml:

stages:
  - build

build:
  image: node:20
  stage: build
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/

Особенность GitLab CI — удобная работа с артефактами. Rollup-выход обычно сохраняется как артефакт для последующих стадий (тестирование, деплой).


Управление версиями артефактов

В CI/CD часто требуется связывать билд с конкретным коммитом или тегом.

Подходы:

  • добавление hash коммита в имя файла
  • генерация версии через package.json
  • использование CI переменных (например, GITHUB_SHA)

Пример:

import { execSync } from 'child_process';

const commitHash = execSync('git rev-parse --short HEAD')
  .toString()
  .trim();

export default {
  output: {
    file: `dist/bundle.${commitHash}.js`,
    format: 'esm'
  }
};

Это позволяет точно идентифицировать, какой код был развернут.


Параллельная сборка и оптимизация времени CI

Rollup сам по себе не является многопоточным сборщиком, но ускорение возможно через:

  • разделение входных точек (multi-entry build)
  • параллельные jobs в CI
  • использование разных процессов для разных форматов (esm/cjs/iife)

Пример стратегии:

  • job 1: ESM сборка
  • job 2: CJS сборка
  • job 3: типы TypeScript (если применимо)

Обработка ошибок в CI

В CI любая ошибка сборки должна приводить к остановке pipeline.

Типичные источники ошибок:

  • несовместимые версии плагинов
  • отсутствие peer dependencies
  • неправильные пути входных файлов
  • ошибки Babel трансформации

Важно, чтобы Rollup возвращал корректный exit code. В скриптах npm это достигается автоматически, но при кастомных Node-скриптах нужно явно пробрасывать ошибки.


Использование Rollup через программный API в CI

Иногда Rollup запускается не через CLI, а через API:

import { rollup } from 'rollup';
import config from './rollup.config.js';

async function build() {
  const bundle = await rollup(config);

  await Promise.all(
    config.output.map(output =>
      bundle.write(output)
    )
  );
}

build().catch(err => {
  console.error(err);
  process.exit(1);
});

Такой подход удобен в сложных CI-сценариях, где требуется динамическая сборка конфигурации.


Многоокруженческая сборка в CI/CD

Часто один pipeline должен собирать несколько окружений:

  • staging
  • production
  • legacy browser support

Rollup позволяет формировать разные конфигурации:

export default [
  {
    input: 'src/index.js',
    output: {
      file: 'dist/app.esm.js',
      format: 'esm'
    }
  },
  {
    input: 'src/index.js',
    output: {
      file: 'dist/app.legacy.js',
      format: 'iife'
    }
  }
];

CI может запускать их последовательно или параллельно.


Безопасность и контроль зависимостей

В CI важно контролировать:

  • lock-файлы (package-lock.json, yarn.lock, pnpm-lock.yaml)
  • неизменяемость зависимостей между сборками
  • аудит npm пакетов перед сборкой

Rollup в этом контексте выступает как конечный этап проверки целостности цепочки зависимостей.


Артефакты и деплой

После успешной сборки Rollup-артефакты обычно:

  • публикуются в npm registry
  • загружаются в CDN
  • передаются в docker-образ
  • деплоятся на static hosting

Ключевой принцип — отделение сборки от деплоя. CI генерирует неизменяемый результат, CD только распространяет его.


Мониторинг стабильности сборки

В зрелых CI/CD системах отслеживаются:

  • время сборки Rollup
  • размер бандла
  • количество модулей
  • частота ошибок

Рост размера бандла может сигнализировать о проблемах tree-shaking или утечках зависимостей.


Типичные проблемы интеграции

На практике часто встречаются следующие проблемы:

  • различие Node.js версий между локальной средой и CI
  • плагины, зависящие от файловой системы
  • некорректная работа с ESM/CJS гибридом
  • отсутствие кэша и чрезмерно долгие сборки
  • различия в пути (Windows vs Linux)

Решение обычно сводится к фиксации окружения и упрощению конфигурации Rollup.


Масштабирование CI для больших проектов

В крупных монорепозиториях Rollup используется:

  • по пакетам (package-level builds)
  • с зависимостями между пакетами
  • с инкрементальной пересборкой

Часто добавляются инструменты orchestration, которые решают, какие пакеты нужно пересобирать при изменении кода.


Интеграция с quality gates

Rollup-сборка может быть частью проверок качества:

  • максимальный размер бандла
  • запрет на определённые зависимости
  • контроль tree-shaking эффективности
  • проверка наличия sourcemaps

Эти проверки выполняются после сборки и могут блокировать деплой.