Використання Lerna з GitHub Actions для розгортання версійованого повноцінного застосунку на Elastic Beanstalk

Перекладаючи статтю "Автоматизація (CI/CD) та зворотний проксі для Docker Elastic Beanstalk з GitHub Actions" (/@wellington.grisa/automate-ci-cd-and-reverse-proxy-a-docker-elastic-beanstalk-up-with-github-actions-346f15e28180), я хотів поглибити розуміння, щоб отримати переваги від використання як Lerna, так і GitHub Actions.

Ця стаття буде поділена на:

  • налаштування Lerna
  • налаштування commitlint та husky для забезпечення використання конвенційних комітів (ми побачимо переваги цього в статті)
  • підготовка різних робочих процесів для повного використання GitHub Actions (при відкритті pull request, при створенні тегу, після злиття, після завершення одного з робочих процесів)
  • використання тегів GitHub разом з Lerna для версій в ECR
  • включення нових скриптів для динамічного розгортання пакунків

Налаштування Lerna

Це дуже просто: npx lerna init — independent. Це створить файл lerna.json і встановить пакет Lerna в ваші залежності для розробки.

Налаштування commitlint, husky та використання конвенційних комітів

Ідея commitlint полягає в тому, щоб забезпечити використання конвенційних комітів, і ми побачимо переваги цього пізніше.

Для цього виконайте команду: yarn add @commitlint/config-lerna-scopes @commitlint/cli @commitlint/config-conventional -D -W.

Створіть файл у кореневій папці commitlint.config.ts і помістіть у нього наступний вміст (якщо ви використовуєте TypeScript):

import type { UserConfig } from "@commitlint/types";  

const Configuration: UserConfig = {  
 extends: [  
 "@commitlint/config-conventional",  
 "@commitlint/config-lerna-scopes",  
 ],  
};  

module.exports = Configuration;

Наступний крок - налаштування husky, щоб забезпечити виконання комітів відповідно до конвенцій. Виконайте:

  • yarn add husky -D -W
  • npx husky add .husky/commit-msg ‘npx — no — commitlint — edit ${1}’

Це створить папку.huskyз файломcommit-msg, який налаштований для використання](https://github.com/well-doing/docker-elastic-beanstalk-up/blob/main/.husky/commit-msg)commitlint для ваших комітів.

Це означає, що відтепер ваші коміти повинні бути виконані згідно з конвенцією, наприклад: fix(api): fix bug. Використання Lerna scopes також дозволяє перевіряти пакунки в Lerna. Коли у вас є коміт для кількох пакунків, ви можете використати, наприклад, fix(api,ui,models): update buggy package version. Згодом ми побачимо цікаву функцію Lerna при використанні таких конвенційних комітів.

Додаткові GitHub Workflows

checks.yml

Тепер, коли ми налаштували Lerna і забезпечили виконання комітів, час почати використовувати Lerna. Одна з особливостей Lerna полягає в тому, що вона може визначити, які пакунки змінилися з моменту, що дозволяє скористатися цією перевагою разом із checks.yml (назва не має значення, важливий момент - робочий процес, який працює при відкритті pull request) у GitHub workflows.

on:  
 pull_request:  
 types: [opened, synchronize]

Ідея полягає в тому, щоб запускати тести щоразу, коли відкривається або синхронізується pull request. У нашому репозиторії ми створили просто echo, який імітує тести для кожного пакету, ось так:

"scripts": {  
 // ...інші команди  
 "test": "echo api TESTS PASSED"  
 },

Цього достатньо, щоб перевірити функцію лише запуску пакунків, які змінилися з моменту останнього разу. Створіть файл checks.yml в .github/workflows, який відповідатиме за кожен pull request.

Це цікаво, оскільки ви можете налаштувати гілку так, щоб її можна було зливати лише після успішного проходження перевірки.
Зображення нижче показує, як це буде виглядати у вашому PR.

pic

Як зазначено в коміті, були запущені тільки тести для ui

pic

Ви не можете злитись, поки перевірки не будуть успішно завершені

version.yml

Перейдемо до наступної функції Lerna, version. Ця команда визначає зміни та запитує, яка версія - patch, minor або major. З цього моменту ця команда почне створювати нові теги у вашому GitHub репозиторії. Однак з цього моменту ми почнемо отримувати переваги від наших конвенційних комітів, semantic-release, Lerna та yarn workspaces разом. Як згадувалося раніше, ідея полягає в тому, що як тільки ви почнете комітити свій код, скажімо, ви комітували виправлення для ui, ви повинні вказати це:

fix(ui): fix ui's bug

Зробивши це, Lerna виявляє, що для api є оновлення, яке потрібно опублікувати, і може обробити це автоматично. Ось чому давайте додамо наш скрипт версії для запуску в робочому процесі при злитті PR. Важливі частини цього робочого процесу:

name: Version  

on:  
 pull_request:  
 types: [closed]  
 branches: ["main"]  

jobs:  
 version:  
 if: ${{ github.event.pull_request.merged == true }}  
 # ...  
 steps:  
 - name: Version  
 uses: actions/checkout@v4  
 with:  
 token: ${{ secrets.DOCKER_ELASTIC_BEANSTALK_VERSION_UP }}  
 fetch-depth: 0  
 - run: |  
 yarn  
 # ... other commands  
 yarn lerna version --conventional-commits --no-commit-hooks --yes

deploy.yml

Це дійсно цікавий робочий процес, який буде виконуватись під час створення тегу, тому важливі частини:

name: Build  

on:  
 push:  
 tags: "**"  

jobs:  
 build:  
 if: ${{ (contains(github.ref, 'ui') || contains(github.ref, 'api')) }}  
 steps:  
 # ...

- name: Відправка Docker образу для API  
 env:  
 REGISTRY: ${{ steps.configure-aws.outputs.ecr-registry }}  
 REPOSITORY: docker-elastic-beanstalk-up  
 run: yarn push-docker
  • назва робочого процесу буде використовуватись пізніше, у цьому випадку ми називаємо його Build
  • параметр on налаштовує кожен тег, який буде відправлений, тому кожного разу, коли тег відправляється (у нашому випадку після команди yarn lerna version), цей робочий процес буде виконуватись
  • оскільки в нашому проекті є файли Docker для ui і api, ми забезпечуємо, що цей робочий процес буде виконуватись лише тоді, коли ці пакунки будуть теговані, а не коли тегується models
  • однак найважливішим кроком в цьому робочому процесі є скрипт, який викликається через yarn push-docker

Цей скрипт містить версію застосунку, і його буде легко реалізувати кількома рядками, використовуючи змінні середовища:

PACKAGE_NAME="${GITHUB_REF_NAME%%@*}" # отримує api або ui  
VERSION="${GITHUB_REF_NAME##*@}" # отримує версію  

TAG=$PACKAGE_NAME-$VERSION # будуємо щось на зразок, api-0.0.1  

build_docker_image() {  
 echo "Будую docker образ $REGISTRY/$REPOSITORY:$TAG"  

 docker build -t $REGISTRY/$REPOSITORY:$TAG -f packages/$PACKAGE_NAME.Dockerfile .  
}  

push_docker_image_to_ecr() {  
 echo "Відправка Docker образу - $REGISTRY/$REPOSITORY:$TAG"  

 docker push $REGISTRY/$REPOSITORY:$TAG  
}  

build_docker_image  
push_docker_image_to_ecr

Змінна GITHUBREFNAME є посиланням на робочий процес, що було викликано, і в цьому випадку це буде версія тегу, тобто: [email protected]. Ми будемо мати робочий процес, викликаний для кожного створеного тегу, тому якщо у нас є зміни в ui та api, ми отримаємо два робочих процеси: один для [email protected], а інший для [email protected]. Таким чином, ми можемо використовувати ці теги для побудови наших зображень ECR, і Voilá, ми маємо версійовані образи в ECR.

pic

deploy.yml

Це робочий процес, який відповідає за обробку останньої версії, яку ви щойно побудували. Важливі частини:

name: Deploy  

on:  
 workflow_run:  
 workflows: [Build] # це назва вашого робочого процесу, який був виконаний на кроці тегування  
 types: [completed] # виконується лише після завершення  

jobs:  
 deploy:  
 if: ${{ github.event.workflow_run.conclusion == 'success' }}  
 # це необхідно, щоб забезпечити виконання лише після успішного завершення  
 name: Deploy  

 # ...інші кроки  

 - name: Розгортання на EB  
 env:  
 REGISTRY: ${{ steps.configure-aws.outputs.ecr-registry }}  
 REPOSITORY: docker-elastic-beanstalk-up  
 run: yarn deploy

І знову ж таки, найважливішим є скрипт, який обробляє логіку розгортання наших застосунків:

  • він отримує останні теги, доступні на GitHub, які будуть використані для створення docker-compose.yml для розгортання застосунку
  • використовує ці теги для побудови зображень ECR
  • оскільки наш застосунок має два взаємозалежні сервіси і тегування відбувається для кожного з них, ми перевіряємо чи існує вже образ іншого пакунку в ECR перед створенням його docker-compose.yml
  • після цього він намагається відправити на eb deploy, якщо є помилка, перевіряє статус, оскільки застосунок може бути не готовий або в черзі, тому ми викликаємо eb deploy тільки коли це можливо

Одне важливе зауваження: оскільки наше розгортання виконується після робочого процесу Build, якщо ми змінюємо і api, і ui, це означає, що ми будемо мати два різних розгортання, що виконуються одночасно.
Проблема в тому, що оскільки наша структура використовує лише один docker-compose.yml, нам потрібно трохи налаштувати скрипт eb deploy, оскільки один з пакунків може вже виконувати розгортання середовища.

api зміна >   
 api образ > розгортання > eb виконує розгортання  
ui зміна >   
 ui образ > розгортання > eb помилка, eb оновлює (через розгортання іншого пакунку)
if ! eb deploy docker-elastic-beanstalk-up-dev; then  
 STATUS=$(eb status docker-elastic-beanstalk-up-dev | grep -i status | awk '{print $2}')  

 echo "Статус Elastic Beanstalk: $STATUS"  

 if ["$STATUS" == "Ready"]; then  
 echo "Elastic Beanstalk готовий!"  

 if ! eb deploy docker-elastic-beanstalk-up-dev; then  
 echo "Розгортання виконується іншим пакунком, але це нормально (теоретично), оскільки він використовує останні теги."  
 fi  

 exit 0  
 elif ["$STATUS" == "Updating"] || ["$STATUS" == "Pending"]; then  
 echo "Розгортання виконується іншим пакунком, але це нормально (теоретично), оскільки він використовує останні теги."  
 exit 0  
 else  
 echo "Невідомий статус: $STATUS. Вихід з помилкою."  
 exit 1  
 fi  
 fi

Але як ви можете побачити в скрипті, це нормально, оскільки ми використовуємо останні теги з Git.

Ще одна річ, яку ми отримали безкоштовно після того, як вирішили використовувати semantic releases за допомогою конвенційних комітів, це те, що Lerna автоматично створює та оновлює журнали змін для кожного зміненого пакунку, наприклад:

pic

Як ви можете побачити, в цьому випадку розділ Bug Fixes міститиме всі коміти, зроблені в цій ітерації/оновленні пакунку з посиланням на його зміни. Так, це все автоматично виконується в поточному налаштуванні CI/CD.

Перекладено з: Using Lerna with GitHub Actions to Deploy a versioned Full-stack Application to Elastic Beanstalk

Leave a Reply

Your email address will not be published. Required fields are marked *