Непрерывная интеграция (CI) и непрерывное развертывание (CD) - это методы, используемые разработчиками во всем мире для повышения качества своего программного обеспечения и сокращения времени вывода на рынок функций и исправлений ошибок. Но в мире JavaScript, где время жизни фреймворка или стиля, являющегося «хорошим», часто можно измерить днями, как вы последовательно добиваетесь этих практик?

В этой статье будет рассмотрен базовый шаблон конвейера для реализации CI и CD и рассмотрено, как его можно применить как к веб-приложениям, так и к службам, а также способы расширения этого шаблона в зависимости от варианта использования. Показанный код будет написан на Groovy и предназначен для использования с подключаемым модулем Pipeline для Jenkins, но эти концепции могут быть применены к выбранной вами платформе. Цель также состоит в том, чтобы не зависеть от фреймворка, поскольку описанные здесь концепции должны применяться к любому приложению, которое вы создаете на JavaScript. В этой части основное внимание будет уделено одностраничному приложению, построенному на чем-то вроде Angular или React, а во второй части речь пойдет о приложениях Node.JS.

Непрерывная интеграция

Непрерывная интеграция (CI) - это практика разработки, при которой разработчики должны интегрировать код в общий репозиторий несколько раз в день. Затем каждая регистрация проверяется автоматизированной сборкой, что позволяет командам обнаруживать проблемы на раннем этапе. -« Thoughtworks

Невозможно переоценить значение CI для выявления проблем по мере их возникновения, а не для выявления этих проблем пользователями. Основным методом тестирования в приложении JavaScript будет линтинг и модульные тесты, и именно здесь мы начнем с нашего конвейера. Инструменты, которые вы используете для запуска ваших тестов, могут отличаться, но в целом вам понадобится какой-нибудь исполнитель задач (Gulp, Grunt, npm), который сможет выполнять ваши тесты из командной строки. Команды для их запуска будут затем сохранены в сценариях и выполнены Jenkins (или вашей платформой CI по выбору). Однако, чтобы иметь возможность протестировать приложение, мы должны сначала получить исходный код и подготовить нашу среду.

node() {
  stage("Checkout") {
    checkout scm: [$class: 'GitSCM',
                   branches: [[name: 'origin/develop']],
                   userRemoteConfigs: [[
                     credentialsId: '<credentials>', 
                     url: '<gitURL>']]
       ]
  }
  stage("Prepare") {
    sh './jenkins/prepare.sh'
  }
  stage("Codestyle") {
    sh './jenkins/codestyle.sh'
  }
  stage("Unit Test") {
    
    sh './jenkins/unit.test.sh'
    junit healthScaleFactor: 5, testResults: 'reports/**/*.xml'
  }
}

В приведенном выше примере кода представлен ряд концепций конвейера. Первый - это «узел», который в Jenkins означает контекст, внутри которого нужно выполнять работу, путем планирования шагов внутри него и создания рабочей области (каталога). Для распараллеливания работы и перемещения контекста выполнения между виртуальными машинами или средами можно использовать несколько узлов, но для наших основных целей с чисто последовательными задачами мы заключим всю нашу работу в один узел.

«Этапы» используются для определения подмножеств конвейера, которые визуализируются плагинами на панели инструментов Jenkins. Внутри этапов отдельные задачи, называемые «шагами», говорят Дженкинсу, что на самом деле делать. Этапы могут быть небольшими, но они должны быть логически отделены друг от друга (например, «Стиль кода» и «Модульный тест», а не только «Тест»), поскольку это упрощает устранение неполадок и поиск ошибок.

Для простоты чтения и для того, чтобы «разделить наши опасения», фактические детали шагов обычно хранятся в отдельных сценариях, вместо того, чтобы помещать все детали нашей реализации в один файл.

Шаг checkout scm определяет тип используемого инструмента управления исходным кодом (в данном случае Git), ветку для оформления заказа, а также фактический URL-адрес, на который нужно перейти, и требуемые учетные данные (если есть). Шаг sh выполнит указанную команду оболочки, которая в нашем случае запускает некоторые сценарии оболочки, хранящиеся в каталоге с именем jenkins.

Этап Prepare должен использоваться для выполнения любых предварительных условий для запуска вашего приложения, таких как установка npm или bower, а также может использоваться для выполнения любых необходимых задач очистки, таких как npm prune.

Если вы работаете в больших командах или вам нравится последовательность, вы можете добавить этап Стиль кода в свой конвейер, чтобы обеспечить автоматическое поддержание стандартов и отклонить все, что не соответствует стандартам. Такие инструменты, как JSHint и ESLint, могут делать это для JavaScript и могут быть скоординированы для запуска с помощью средства выполнения задач, а затем выполняются в вашем конвейере.

После того, как ваша среда настроена и все правила линтинга пройдены, вы можете запустить этап Модульное тестирование. Это, конечно, будет отличаться в зависимости от вашего приложения, так как вы можете использовать любую комбинацию бегуна задач и Mocha, Jasmine, Karma (среди многих других) для запуска тестов, но концепция такая же, и тесты должны выполняться из скрипт, в данном случае unit.test.sh. Отчеты, созданные вашими модульными тестами, могут быть опубликованы, а в случае Jenkins плагин junit может публиковать отчеты XML за вас.

Запуск этого конвейера для вашего кода при каждой фиксации в вашей ветке разработки (а также в ветвях функций и исправлений ошибок, в зависимости от модели ветвления вашей команды) гарантирует, что любой код в этих ветвях будет иметь уровень качества, который вы определяете в своем модульном тесте. спецификация - где вы можете определить такие метрики, как уровни покрытия для сбоя. Вы также можете настроить свою локальную среду на автоматический запуск этих тестов при каждом сохранении файла, выявляя проблемы еще раньше в цикле разработки. Любые неудачные модульные тесты должны привести к сбою конвейера, что очень важно, когда дело доходит до реализации следующих шагов, поскольку вы не хотите развертывать код с ошибочными тестами ...

Часть 2 расскажет, как расширить этот базовый конвейер для развертывания вашего приложения, и посмотрим, как конвейер может немного отличаться при работе со службами Node.JS, а не с интерфейсными приложениями.