Непрерывная интеграция является необходимостью в наши дни. А для социальных проектов с открытым исходным кодом это имеет решающее значение. Наш любимый инструмент для автоматизированного тестирования — Travis CI. Как и большинство инструментов, Travis делает то, что делает хорошо. К сожалению, это не очень умно. Да поможет вам небо, если у вас есть большой или модульный проект с множеством тестов — вы будете ждать вечность между сборками.

И это именно то, с чем мы столкнулись. У нас есть репозиторий, содержащий 30 модулей NPM, каждый со своими спецификациями (тестами). Эти модули являются частью конвейера стареющих активов, о котором я кратко упомянул на прошлой неделе. Таким образом, каждый модуль подвергается большому количеству задач каждый раз, когда вносятся изменения. Travis CI подключается к репозиторию и для каждого запроса на включение будет запускать спецификации для каждого модуля, гарантируя отсутствие ошибок в изменениях кода, содержащихся в PR. Когда вы работаете только над одним или двумя модулями, время выполнения задач относительно невелико; обычно 1-2 минуты. Это, конечно, зависит от таких вещей, как npm install время, так как каждый модуль требует установки для тестирования. Умножьте это на 30, и вы начнете видеть, где возникает проблема.

Ожидание отстой

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

Вот фрагмент нашей структуры репо для справки:

ui-tracking
|--src
   |--common
      |--event_registry
      |--tracking_metadata
   |--tracking
      |--cart
      |--etc..

Цель получена

Чтобы ориентироваться на модули, нам нужно знать, каковы их нормализованные имена; имена, под которыми мы публикуем их в NPM. Из-за некоторых устаревших вещей, встроенных в наш конвейер, мы храним модули в group/name, но публикуем их как group.name. Итак, давайте запустим travis-target (имейте в виду, что мы используем синтаксис ES6, который поддерживает Node v7).

const target = require('travis-target');
const pattern = /^src\//;
let targets = await target();

Давайте представим, что оба модуля common.event_registry и tracking.cart были изменены в одном запросе на слияние (общий для нас шаблон) — наши результаты могли бы выглядеть так:

[
  'README.md',
  'src/common/event_registry/js/event_registry.js',
  'src/common/event_registry/js/registry.js',
  'src/common/event_registry/package.json',
  'src/tracking/cart/js/cart.js',
  'src/tracking/cart/package.json'
]

Но это просто глупо, так что давайте travis-target дадим несколько вариантов для работы:

const target = require('travis-target');
const pattern = /^src\//;
let targets = await target({
  pattern: pattern,
  map: (result) => {
    let parts;
    result = result.replace(pattern, '');
    parts = result.split('/');
    return parts.slice(0, 2).join('.');
  }
});

Передавая pattern в параметрах, мы говорим travis-target фильтровать (или возвращать только те результаты, которые соответствуют) шаблону регулярного выражения. Это дает нам исходный набор каталогов, начинающихся с src/.

[
  'src/common/event_registry/js',
  'src/tracking/cart/js'
]

Вы заметите, что первоначальный пример результатов содержал несколько повторяющихся каталогов; travis-target убирает это за вас.

Далее мы указываем функцию map в опциях. Это позволит нам преобразовать каждый элемент в Array результатов, чтобы он был готов к использованию. Теперь наши результаты будут выглядеть так:

[
  'common.event_registry',
  'tracking.cart'
]

Великая справедливость

Используя этот последний набор результатов, мы теперь знаем, какие модули были затронуты в PR, и мы знаем, для каких модулей нужно запускать спецификации. Наши следующие шаги — запустить последовательность команд оболочки с использованием @exponent/spawn-async, которая хорошо работает с шаблонами async/await, которые теперь поддерживаются в Node v7.1 с флагом --harmony-async-await.

Поскольку мы внедрили этот шаблон, время сборки наших PR находится в диапазоне 1–2 минут; значительное улучшение, которое в какой-то степени принесет разработчику удовольствие.

Ваше здоровье!

Первоначально опубликовано на shellscape.org 16 ноября 2016 г.