Непрекъснатата интеграция (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. В рамките на етапите единични задачи, наречени „стъпки“, казват на Дженкинс какво всъщност да прави. Етапите може да са малки, но критично трябва да са логически различниедни от други (напр. „Codestyle“ и „Unit Test“, не само „Test“), тъй като това позволява по-лесно отстраняване на проблеми и намиране на грешки.

За по-лесно четене и за да „разделим нашите опасения“, действителните подробности за стъпките обикновено се съхраняват в отделни скриптове, вместо да принуждаваме всички наши подробности за изпълнение в един файл.

Стъпката checkout scm определя вида на инструмента за управление на изходния код, който използвате (в този случай Git), клона за плащане, както и действителния URL адрес, към който да отидете, и необходимите идентификационни данни (ако има такива). Стъпката sh ще изпълни дадената шел команда, която в нашия случай изпълнява някои шел скриптове, съхранени в директория, наречена jenkins.

Етапът Подготовка трябва да се използва за изпълнение на всякакви предварителни условия за стартиране на вашето приложение, като например инсталация на npm или инсталация на bower, и може също да се използва за изпълнение на всички необходими задачи за почистване, като например изрязване на npm.

Ако работите в големи екипи или ако обичате последователността, можете да добавите етап Codestyle във вашия процес, за да гарантирате, че стандартите се поддържат автоматично, и да отхвърлите всичко, което не отговаря на стандартите. Инструменти като JSHint и ESLint могат да направят това за JavaScript и могат да бъдат координирани да се изпълняват с помощта на програма за изпълнение на задачи, след което да се изпълняват във вашия конвейер.

След като средата ви бъде настроена и всички правила за линтинг са преминали, можете да изпълните своя етап Unit Test. Това, разбира се, ще се различава в зависимост от вашето приложение, тъй като може да използвате произволна комбинация от програма за изпълнение на задачи и Mocha, Jasmine, Karma (между много други), за да изпълнявате тестовете, но концепцията е същата и тестовете трябва да се изпълняват от скрипт, в този случай `unit.test.sh`. Отчетите, създадени от вашите модулни тестове, могат да бъдат публикувани, а в случая на Jenkins плъгинът junit може да публикува XML отчети вместо вас.

Пускането на този конвейер спрямо вашия код при всеки ангажимент към вашия клон за разработка (както и вашите клонове за функции и корекции на грешки, в зависимост от модела на разклоняване на вашите екипи) ще гарантира, че всеки код в тези клонове има ниво на качество, което дефинирате във вашия модулен тест спецификация — където можете да дефинирате показатели като нива на покритие, на които да не успеете. Можете също така да настроите вашата локална среда да изпълнява тези тестове автоматично за всяко записване на файл, улавяне на проблеми дори по-рано в цикъла на разработка. Всички неуспешни модулни тестове трябва да доведат до неуспех на тръбопровода, което е критично, когато става въпрос за изпълнение на следващите стъпки, тъй като не искате да внедрявате код с неуспешни тестове...

„Част 2“ ще обхване как да разширите този основен конвейер, за да внедрите вашето приложение, и ще разгледа как конвейерът може да е малко по-различен, когато работите с Node.JS услуги, а не с приложения от предния край.