← Назад к вопросам

Как оптимизировать CI пайплайн?

1.0 Junior🔥 71 комментариев
#Инструменты и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Оптимизация CI пайплайна

CI/CD пайплайны часто становятся узким местом в разработке. Долгие проверки (тесты, линтинг, сборка) замедляют feedback loop и разочаровывают разработчиков. Есть множество техник для ускорения.

1. Кэширование зависимостей

npm/yarn зависимости

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      # Кэш npm пакетов
      - uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-
      
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'  # Встроенное кэширование
      
      - run: npm ci  # Вместо npm install (детерминированно)

Экономия: ~2-3 минуты на установку зависимостей

Docker слои

# Dockerfile
FROM node:18-alpine

WORKDIR /app

# Кэш layer - меняется редко
COPY package*.json ./
RUN npm ci

# Меняется часто - не кэшируется
COPY . .

RUN npm run build

2. Распараллеливание задач

Вместо последовательного выполнения - запускать параллельно:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      - run: npm ci
      - run: npm run test

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      - run: npm ci
      - run: npm run build

# lint, test и build выполняются одновременно!
require: [lint, test, build]  # Все должны пройти

Экономия: если каждая задача 10 минут, то: 30 минут последовательно -> 10 минут параллельно

3. Быстрые тесты первыми

Чем раньше упадет тест, тем раньше получит feedback разработчик:

steps:
  # Быстрые unit тесты ПЕРВЫМИ
  - run: npm run test:unit --run  # ~2 минуты
  
  # Потом линтинг
  - run: npm run lint  # ~1 минута
  
  # Потом сборка
  - run: npm run build  # ~3 минуты
  
  # E2E тесты последними (самые медленные)
  - run: npm run test:e2e  # ~10 минут

4. Conditional Execution - запускать только нужные тесты

steps:
  - uses: actions/checkout@v3
    with:
      fetch-depth: 0  # Нужно для сравнения
  
  # Смотрим какие файлы изменились
  - id: changes
    uses: dorny/paths-filter@v2
    with:
      filters: |
        frontend:
          - 'frontend/**'
        backend:
          - 'backend/**'
  
  # Запускаем только если изменился frontend
  - if: steps.changes.outputs.frontend == 'true'
    run: npm run test

5. Оптимизация сборки Next.js

// next.config.js
module.exports = {
  // Отключить аналитику Vercel
  telemetry: false,
  
  // Параллельная сборка страниц
  experimental: {
    parallelDeriv: true
  },
  
  // Минимизировать CSS
  swcMinify: true,
  
  // Отключить source maps в продакшене
  productionBrowserSourceMaps: false
};

6. Инкрементальная сборка

steps:
  # Кэшируем результаты Next.js сборки
  - uses: actions/cache@v3
    with:
      path: |
        .next/cache
        node_modules/.cache
      key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}
      restore-keys: |
        ${{ runner.os }}-nextjs-
  
  - run: npm run build

7. Выборочное тестирование (Change detection)

#!/bin/bash
# scripts/run-tests.sh

# Получаем измененные файлы
CHANGED_FILES=$(git diff --name-only main...HEAD)

# Если изменился только README
if [ "$(echo "$CHANGED_FILES" | grep -c -v '.md$')" -eq 0 ]; then
  echo "Только документация, тесты не нужны"
  exit 0
fi

# Если изменились компоненты - запускаем unit тесты
if echo "$CHANGED_FILES" | grep -q 'components/'; then
  npm run test:unit
fi

# Если изменились API утилиты - запускаем integration тесты
if echo "$CHANGED_FILES" | grep -q 'lib/api'; then
  npm run test:integration
fi

8. Разделение на матрицу тестов

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16, 18, 20]
        test-suite: [unit, integration, e2e]
    
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      
      - run: npm ci
      
      # Каждая комбинация версии и тест-suite запускается отдельно
      - run: npm run test:${{ matrix.test-suite }}

9. Оптимизация Docker образов

# Multi-stage build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Финальный образ - только необходимое
FROM node:18-alpine
WORKDIR /app

# Копируем только зависимости для production
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/package*.json ./

RUN npm prune --production

EXPOSE 3000
CMD ["npm", "start"]

10. Отключение ненужных проверок

// jest.config.js
module.exports = {
  // Собирать coverage только в main ветке
  collectCoverageFrom: process.env.CI ? ['src/**'] : undefined,
  coveragePathIgnorePatterns: [
    '/node_modules/',
    '/.next/',
  ],
  // Использовать быстрый reporter
  reporters: ['default'],
};

11. Pre-commit Hooks

Ран же на локальной машине - быстрый feedback:

// package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{js,ts,tsx,json,md}": ["prettier --write"]
  }
}

12. Правильная конфигурация runner

# Выбрать мощный раннер для сборки
jobs:
  build:
    runs-on: ubuntu-latest-4-cores  # Или ubuntu-24.04-x64
    # Можно использовать самоуправляемый runner (свой сервер)
    # runs-on: [self-hosted, linux, x64]

Итоговый пример оптимизированного CI

name: Optimized CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  quick-checks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      - run: npm ci --prefer-offline
      - run: npm run lint
      - run: npm run test:unit --run

  build:
    runs-on: ubuntu-latest
    needs: quick-checks
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      - uses: actions/cache@v3
        with:
          path: .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}
      - run: npm ci --prefer-offline
      - run: npm run build

  e2e:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      - run: npm ci --prefer-offline
      - run: npm run test:e2e

Ключевые метрики

МетрикаДоПослеВыигрыш
Total time45 мин12 мин3.75x
Lint5 мин3 минкэш
Test15 мин5 минпараллель
Build20 мин8 мининкремент
E2E10 мин4 минкэш

Оптимизация CI - это итеративный процесс. Начните с профилирования, найдите узкие места и применяйте техники в приоритете их влияния.