Введение: Зачем нужен продвинутый CI/CD для TypeScript?
Базовый пайплайн CI/CD, который просто запускает npm test и npm build, перестаёт быть эффективным, как только проект TypeScript начинает расти. Время сборки увеличивается, тесты занимают часы, а ручное управление версиями приводит к ошибкам. Продвинутый уровень настройки GitHub Actions позволяет решить эти проблемы: ускорить пайплайн за счёт кэширования, выполнять тесты параллельно для разных версий Node.js и ОС, а также автоматизировать деплой на продакшен.
В этой статье мы разберём три ключевых приёма для TypeScript проектов: умное кэширование node_modules, матричные сборки для проверки совместимости, и автоматический деплой с использованием actions/upload-pages-artifact. Все примеры написаны на TypeScript и готовы к интеграции в ваш репозиторий.
1. Умное кэширование зависимостей и компиляции
Первая проблема больших TypeScript проектов — долгая установка зависимостей и компиляция. GitHub Actions предоставляет механизм кэширования, но его нужно настроить правильно. Вместо того чтобы кэшировать всю папку node_modules (что может привести к устаревшим версиям), лучше кэшировать package-lock.json и папку .cache компилятора.
Пример продвинутого кэширования для TypeScript:
name: TypeScript CI (Advanced)
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Cache TypeScript cache
uses: actions/cache@v4
with:
path: |
**/node_modules
**/.tsbuildinfo
key: ${{ runner.os }}-ts-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-ts-
- run: npm ci
- run: npm run build --if-present
- run: npm test
Ключевая оптимизация здесь — использование hashFiles('**/package-lock.json') для генерации уникального ключа кэша. Если package-lock.json не менялся, кэш восстанавливается за секунды. Флаг --if-present позволяет пропустить скрипт сборки, если он не определён.
2. Матричные сборки: тестирование на разных версиях Node.js и ОС
TypeScript компилируется в JavaScript, но поведение кода может отличаться в разных средах исполнения. Матричная стратегия GitHub Actions позволяет запускать один и тот же пайплайн на нескольких комбинациях ОС и версий Node.js. Это критично для библиотек и серверных приложений.
Пример настройки матрицы для TypeScript проекта:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
exclude:
- os: windows-latest
node-version: 22
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npx tsc --noEmit
- run: npm test
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.os }}-${{ matrix.node-version }}
path: coverage/
Ключевые моменты:
fail-fast: false— не останавливает остальные джобы при ошибке в одной из них. Позволяет увидеть все проблемы сразу.exclude— позволяет исключить заведомо нерабочие комбинации (например, Node 22 на Windows, если есть известная проблема).- Параллельный запуск — все 8 комбинаций (3 ОС × 3 версии минус 1 исключение) выполняются одновременно, что экономит время.
npx tsc --noEmit— проверка типов без генерации JS-файлов. Быстрее полной сборки.
3. Автоматический деплой с условными триггерами
Продвинутый CI/CD подразумевает не только тестирование, но и деплой. Для TypeScript проектов часто нужно собрать статику (если это фронтенд) или скомпилировать серверный код. Используем условные триггеры на основе веток и тегов.
Пример деплоя на GitHub Pages с проверкой типа ветки:
deploy:
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './dist'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Почему это продвинутый уровень?
needs: test— деплой запускается только после успешного прохождения всех матричных тестов.if: github.ref == 'refs/heads/main'— защита от случайного деплоя из других веток.permissions— явное указание прав на запись в Pages и получение токена. Без этого деплой не сработает.upload-pages-artifact— оптимизированный артефакт для Pages, который автоматически сжимает и загружает файлы.
4. Оптимизация времени: параллельные джобы и кэширование артефактов
Финальный приём — разделение пайплайна на независимые джобы, которые могут выполняться параллельно. Например, линтинг, проверка типов и юнит-тесты не зависят друг от друга и могут идти одновременно.
Пример струк