Как объединить интерфейс и серверную часть в одном репозитории в мире CI/CD.

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

Разве не было бы здорово, если бы и клиент, и сервер находились в одном репозитории, используя одно и то же развертывание и тестирование? Полный стек разработчика будет иметь гораздо более быстрый обзор потока данных от серверной части к интерфейсу и обратно.

В классическом CI/CD нам нужны разные общие функции:
build: сборка/компиляция кода и создание исполняемого кода.
test:проверка все условия тестирования для только что сгенерированного кода по-прежнему работают должным образом.
run :запускает соответствующий компонент во время выполнения модуля.

Связанный файл package.json будет выглядеть примерно так…

{
 …
 “scripts”: {
 …
 “start”: “…command for starting the frontend in local context…”,
 “build”: “…run a local build, result would be in /build subfolder …”,
 “test”: “…running tests…”,
 …
 },
 …
 }

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

Затем во время сборки на верхнем уровне добавляется некоторая магия, чтобы объединить интерфейс и сервер непосредственно в одном контейнере / модуле.

Чтобы понять эту магию, мы сначала исследуем природу двух подкомпонентов нашего приложения.

Внешний интерфейс

В настоящее время интерфейс в основном состоит из одностраничного приложения Javascript, созданного фреймворком (React, Angular, Vue, …). В конечном итоге сборка сводит всю работу фронтенд-разработчиков к 3 статическим файлам: волшебство!

index.html
bundle.css
bundle.js

Бэкэнд

Бэкэнд обычно представляет собой простой сервер отдыха / GraphQL, который также имеет возможность доставки статических файлов. Основная идея состоит в том, чтобы просто скопировать 3 файла, упомянутых выше, в папку static/public серверной службы и доставить их в браузер клиента с помощью соответствующих вызовов URL: Готово!

Соединяем все это изоморфным образом

Связанный "общий" файл package.json на корневом уровне будет выглядеть примерно так…

{
 …
 “scripts”: {
 …
 “run”: “cd backend;npm start”,
 “build:frontend”: “cd frontend;npm run build;cd ..”,
 “build:backend”: “cd backend;npm run build;cd ..”,
 “build:copyfe2be”: “cp frontend/build/ backend/build/static/”,
 “build”: “npm run build:frontend;npm run build:backend;npm run build:copyfe2be”,
 “test:frontend”: “frontend/npm run test”,
 “test:backend”: “backend/npm run test”,
 “test:e2e”: “…run e2er tests…”,
 “test”: “npm run test:frontend;npm run test:backend;npm run test:e2e”,
 …
 },
 …
 }

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

Надеюсь, мне удалось донести до вас общую идею такого стека и надеюсь, вам понравится ваша первая реализация.