Единый репозиторий Git содержит исходный код нескольких приложений и библиотек.
Зачем использовать монорепо?
Monorepo в организации поможет вам получить следующие преимущества:
- Общий код. Повторно используйте проверку кода, код инструментов и код библиотек в приложениях в одном репозитории.
- Атомарные изменения: измените компонент кнопки в общей библиотеке и обновите приложение, использующее эту кнопку, в той же фиксации.
- Мобильность разработчиков: разработчики могут легко вносить свой вклад в любые приложения или библиотеки, просто перейдя в один репозиторий исходного кода.
Что я должен знать перед использованием Monorepo?
Если вы уже разработали пару проектов на JavaScript, тогда вам пора изучать Monorepo. Для этой статьи, если у вас есть базовые знания Angular, это будет хорошо, так как я буду создавать проекты Angular в примере монорепозитория.
Где исходный код для Nx Monorepo?
Вот пакет Nx npm, который вы можете использовать из npm. В этой статье я продемонстрирую код, размещенный на github по адресу ниже:
- Образец Angular Monorepo Nrwl.NX
- Образец Angular Monorepo Nrwl.NX с передовым конвейером Microsoft Azure CI/CD
Проблемы с Монорепо?
Проблемы заключаются в том, как определить, какое приложение зависит от скольких проектов? Как ограничить зависимость, чтобы уменьшить проблемы циклической зависимости и организовать структуру кода? Как скомпилировать только тот проект, который только изменился? Как запускать тесты только для измененных проектов? Как увеличить скорость инструментов для большого набора проектов в одном монорепозитории? Как управлять версиями? Все это очень важные требования. Платформа монорепозитория Nx.Nrwl — отличная платформа для управления любым количеством проектов JavaScript всего в одном репозитории GitHub. С Nx.Nrwl Monorepo вы получаете возможность визуализировать, какой проект зависит от других. Все проблемы, которые я упомянул, могут быть решены с помощью Nx Monorepo. Оставайтесь с нами и прочитайте эту статью до конца, чтобы создать монорепозиторий angular с нуля с помощью фреймворка Nx Monorepo.
Как управлять версиями и параллельной разработкой?
У вас может быть своя собственная политика в соответствии с потребностями вашей организации для управления параллельной разработкой, выпуском, исправлением, разработкой, слиянием, основным выпуском, работами, связанными с обслуживанием. Однако я предлагаю один вариант. Посмотрим, имеет ли это для вас смысл.
Управление релизами с помощью Monorepo
Создайте ветку для номера выпуска. Пример releases/11.0
рассматривает эту ветку как активную рабочую ветку. Вы можете рассматривать это как свою ветку dev
.
Работа над Feature Branch с Monorepo
Для добавления новой функции или работы над новым PBI или вариантом использования. Создайте папку функций и поместите номер PBI в качестве новой ветки в папку функций, работающую над разработкой, и объедините их с веткой выпуска. Пример: features/PBI123/appendOrder
Управление основными выпусками с помощью Monorepo
Чтобы сделать основной выпуск, создайте новую ветку 11.1
как ветку основного выпуска. И если вы хотите применить исправление к 11.1
, создайте другую ветку 11.1.1
для работы над исправлениями и объедините их обратно в выпускную ветку 11.0
для будущих выпусков.
Что разработчик узнает из этой статьи?
В этой статье я расскажу вам, как создать одно рабочее пространство nx monorepo с приложением Angular и библиотеками из пустого пресета.
- Мы будем использовать кэширование Azure Pipeline для повышения скорости сборки за счет кэширования пакетов npm.
- Мы опубликуем наше приложение в npm из пайплайна в автоматическом режиме.
В Монорепо мы проделаем нижеприведенную работу
- Мне нужна карма в качестве тестировщика
- Мне не нужен e2e-тест для приложений
- Я хочу, чтобы мои приложения можно было публиковать
- Я хочу, чтобы мои библиотеки нельзя было публиковать
- Создайте конвейеры Azure CI/CD
- Разверните приложение в реестре NPM
Настройка среды разработки
Привет, разработчик, убедитесь, что у вас установлено node.js
, что больше, чем V10
. Я установил node.js v14
.
Код Visual Studio (VsCode)
Я использую Vs Code и установил расширение консоли nx, чтобы получить встроенный способ создания приложений/библиотек только из консоли nx.
Расширения кода Visual Studio
Установите ниже расширения VsCode для лучшего опыта разработки:
- Консоль Nx в вашем vs-коде для создания компонентов без написания кода.
- Material Theme VS Code Extension: чтобы увидеть значки для каждого файла, вы должны обновить настройки vscode. Возьмите пользовательские настройки Vs Code для VsCode из этой статьи.
- Azure Pipelines для проверки файла yml
- Установите расширение визуальной студии yaml
Ниже приведены необязательные, но рекомендуемые расширения
- TODO Highlighter: необязательно, помогает выделять комментарии к задачам и исправлениям.
Комментарии FixMe FIXME: комментарии — это некоторые вещи, которые вы планируете исправить в текущем спринте.
Пример: исправление службы API для вызова веб-API сервера только в рамках этого спринта. Истории с FIXME вызывают у вас тревогу. Убедитесь, что все FIXME адресованы до того, как будут отмечены истории. 📓 Обратите внимание: как только вы исправите код, удалите комментарий 😄
Комментарии к задаче TODO: комментарии — это то, над чем вы планируете работать в будущих спринтах. Пример: работа над сервисом API MCQ для вызова API на стороне сервера, который необходимо выполнить в будущем спринте.
Проверить все комментарии к задачам CTRL + SHIFT + P
Настройки VScode
Если вы хотите получить пользовательские значки для своих файлов, убедитесь, что вы установили VSCode Materials, скопируйте приведенные ниже настройки и вставьте в свой локальный файл VSCode settings.json, расположенный по адресу %appdata%\Code\User\settings.json
.
"material-icon-theme.folders.associations": {
"itops": "Vm",
"presentation":"Views",
"aggregate-score":"Sublime",
"aspects": "Apollo",
"contracts": "Scala",
"message-handlers": "Serverless",
"base": "core",
"branding": "Shared",
"main": "Stack",
"composition": "include",
"modules": "Cluster",
"partials": "Mock",
"filters": "Terraform",
"sharedviews": "Svelte",
"dashboard": "Gulp",
"ui-core": "Review",
"dev": "Intellij",
"ngmaterial": "Theme",
"ckeditor5": "Mobile",
"launch": "Docker",
"editor": "Custom",
"toolbar": "Tools",
"pubsub": "Event",
"message-handler": "Delta",
"api-composition": "Mock",
"dto": "Gradle",
"interfaces": "Animation",
"global-error-handler":"Error",
"root":"Svg"
},
"material-icon-theme.files.associations": {
"*.state.ts": "Silverstripe",
"*.dto.ts": "Gradle",
"*.module.ts": "Racket",
"*.routing.module.ts": "R",
"*.store.ts": "Storybook",
"*.tests.ts": "Test-ts",
"*.controller.ts": "Angular-component",
"*.enum.ts": "Jinja",
"*.service.ts": "Quasar",
"*.model.ts": "Shaderlab",
"*.viewmodel.ts": "Pug",
"*.command.ts": "Email",
"*.event.ts": "Stencil",
"*.ts":"Idris"
},
Вы получите значки ниже после обновления файла настроек.
Включить Karma Test Runner в Nx Monorepo
Когда вы создаете монорепозиторий nx с Angular
или React
или Next.js
или Gatsby
или next.js
или Web Components
, он даст вам JEST
в качестве исполнителя юнит-тестов. Если вы хотите получить средство запуска тестов karma
, вам нужно создать пустое рабочее пространство, а затем добавить @nrwl/angular
, а затем добавить новое приложение или библиотеку с исполнителем модульных тестов кармы.
Создание первого монорепозитория Nx с помощью CLI
Чтобы создать свое первое рабочее пространство Nx Monorepo, запустите следующий скрипт:
npx create-nx-workspace@latest
- Дайте имя рабочей области:
myorg
- Выберите макет: я выбираю приложение Angular (рабочее пространство с одним приложением Angular)
- Имя приложения: cutepuppies-admin
- Формат таблицы стилей: SASS
- Линтер: ТСЛИНТ
- Nx Cloud: Нет (по умолчанию)
Это создаст рабочую область с угловым приложением, которое мы определили.
Когда вы будете использовать их шаблон, они добавят сквозной тест для приложения. Они также добавят тест-бегун.
В моем рабочем пространстве я не хочу Jest и не хочу тестов e2e. Итак, я создаю свое рабочее пространство с пустым пресетом.
Создание монорепозитория Nx с пользовательскими настройками
- Выполнить
npx create-nx-workspace@latest
- Укажите название организации:
curise
- Выберите угловое приложение для рабочей области.
angular
Это установит плагин @nrwl/angular
, чтобы дать вам инструменты для создания angular lib и приложений.
- Затем введите имя углового приложения: admin
- Затем выберите формат стиля как SASS.
- Линтер по умолчанию: выберите TSLINT, так как у меня есть угловое приложение
- Nx Cloud предоставляет кэширование через облако: Нет (требуется платный план подписки)
Запустите скрипт:
Понимание рабочего пространства Nx Monorepo
- Приложения
- библиотеки
- tools В папке Tools есть файл tsconfig, что означает, что вы можете писать машинописные файлы и компилировать их в JS.
- decorate-angular-cli.js
- jest.preset.js
- angular.json Создает файл angular.json для управления рабочим пространством. Однако, если вы хотите создать пустой проект монорепозитория, он создает файл workspace.json.
Создание проектов в Nx Monorepo
Беги nx build
Показана папка dist
.
Преимущества и недостатки пустого Nx Monorepo
После создания пустого рабочего пространства.
Преимущества пустой рабочей области
- Вы можете использовать средство запуска юнит-тестов
karma
- Вы можете использовать
workspace.json
для настройки любых приложений и библиотек.
Недостатки пустого рабочего пространства:
- Вы должны написать угловой декоратор cli для
Nx CLI
Независимо от того, пусто или предварительно заполнено рабочее пространство, вы должны создать свои собственные CI/CD
конвейеры и команды Nx для своих конвейеров.
Создание нового пустого рабочего пространства Nx Monorepo
Теперь мы создадим наше рабочее пространство монорепозитория, которое мы будем продолжать использовать. Мы начнем с нуля пустой монорепозиторий.
Создание пустой рабочей области Nx Monorepo — наш последний вариант для этого примера. Итак, давайте создадим нужную рабочую папку и из этого места откроем powershell
на компьютере с Windows, который, как я обнаружил, работал на git bash commands
, но работал не очень хорошо.
Запустите приведенный ниже скрипт, чтобы создать пустое рабочее пространство.
npx create-nx-workspace@latest --preset=empty
Вышеупомянутый скрипт спросит вас
- название организации и
- Хотите использовать Nx Cloud или нет.
Обратите внимание, что он создал рабочее пространство
Итак, теперь я получаю пустые папки приложений и библиотек.
Это мои зависимости, которые я получаю в package.json
Что делает интерфейс командной строки NX?
Nx CLI
команды вызовут Angular CLI
. Nx CLI
просто выполняет некоторые оптимизации перед вызовом Angular CLI
. Nx Cli
может выполнять все команды ng cli
. Преимущество, которое вы получаете с Nx CLI
, заключается в кэшировании вычислений, что ускоряет выполнение задач. Кроме того, Nx CLI не зависит от технологии. Вы можете использовать Nx CLI
для запуска angular
, react
, JavaScript
или любых сценариев проекта. Таким образом, ваша команда всегда работает с Nx CLI
в своей рабочей области, независимо от того, какие интерфейсные технологии вы используете.
Использование Nx CLI поверх Angular CLI
Чтобы указать ng cli
на Nx CLI
, нам нужно запустить файл decorate-angular-cli.js. Из-за symlinking
вы по-прежнему можете набирать ng build
, ng test
или ng lint
в терминале. Команда ng в этом случае укажет на nx, который выполнит оптимизацию перед вызовом ng
.
После установки мы можем выполнить этот файл декоратора.
"postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
Как отказаться от NX CLI
Хотя я не рекомендую этот вариант :-1:
Однако, если вы хотите отказаться от NX CLI
, выполните следующие действия:
- Замените вхождения nx на ng в вашем package.json
- Удалите скрипт из постустановочного скрипта в package.json.
- Удалите и переустановите ваши node_modules
Хотя мы этого не делаем.
Общие сведения о сценариях рабочей области NX
"scripts": {
"nx": "nx",
"start": "nx serve",
"build": "nx build",
"test": "nx test",
"lint": "nx workspace-lint && nx lint",
"e2e": "nx e2e",
"affected:apps": "nx affected:apps",
"affected:libs": "nx affected:libs",
"affected:build": "nx affected:build",
"affected:e2e": "nx affected:e2e",
"affected:test": "nx affected:test",
"affected:lint": "nx affected:lint",
"affected:dep-graph": "nx affected:dep-graph",
"affected": "nx affected",
"format": "nx format:write",
"format:write": "nx format:write",
"format:check": "nx format:check",
"update": "nx migrate latest",
"workspace-generator": "nx workspace-generator",
"dep-graph": "nx dep-graph",
"help": "nx help"
},
Создание библиотеки Angular с помощью консоли Nx
Я буду использовать nx console
для создания приложения.
Поскольку я хочу создавать приложения и библиотеки Angular, мне нужно установить пакет узла @nrwl/angular
.
пожалуйста, запустите приведенный ниже скрипт, чтобы установить @nrwl/angular
npm i -D @nrwl/angular
Выберите Nx, затем выберите «Создать», а затем выберите «Библиотека Angular».
Заполните необходимые поля. Я хочу создать библиотеку angular logger в папке lib/branding с фреймворком для тестирования кармы.
Это мой результат пробного запуска
Проект Logger успешно создан.
Как остановить dryRun при использовании консоли Nx?
В настоящее время вы не можете остановить пробный запуск, поэтому используйте скрипт для создания приложения или библиотеки.
nx generate @nrwl/angular:library --name=utils --directory=branding --importPath=@myor/branding-utils --prefix=myorg-branding-utils --tags=scope:branding,type:branding-utils --no-interactive --dry-run
Создание проекта JS с помощью консоли NX
NX Monorepo поддерживает только средство выполнения модульных тестов JEST
или NONE
для проекта Javascript.
nx generate @nrwl/workspace:library --name=ckeditor5 --directory=branding --importPath=@myorg/branding-ckeditor5 --prefix=myorg-branding-ckeditor5 --tags=scope:branding,type:branding-ckeditor5 --no-interactive --js --unitTestRunner=none --dry-run
Как создать команду «Выполнить» в NX Monorepo
Nx Run Command помогает создавать собственные команды
Имя цели: проклятие цели, например build
Имя проекта: имя проекта в файле workspace.json: branding-ckeditor
Команда: npm run build
cwd расположение папки src проекта
выходные данные, где будут храниться артефакты сборки.
nx generate @nrwl/workspace:run-commands --name=build --command='npm run build' --project=branding-ckeditor5 --cwd=libs/branding/ckeditor5/src --outputs=libs/branding/ckeditor5 --no-interactive --dry-run
Это обновит workspace.json
и nx.json
Теперь вы можете запустить nx build branding-ckeditor5
Шаг: создайте приложение Angular с помощью консоли Nx
На этот раз давайте выберем угловое приложение, чтобы создать приложения для администратора cutepuppies.
Имя: cutepuppies-admin
e2eTestRunner: нет (мне не нужен этот тест)
Маршрутизация: Генерирует модуль маршрутизации, выберите это.
Теги: scope:itops,type:itops-cutepuppies-admin Модуль запуска тестов: Karma
Нажмите кнопку «Выполнить» и, наконец, выберите SASS, пока он спрашивает, какой формат стиля. Смотрите, как мое приложение создано
Добавление Nx CLI Decorator для пустого рабочего пространства Nx
Шаг: Давайте создадим службу ведения журнала и тесты
Давайте создадим loggingService внутри нашего проекта регистратора. Используйте консоль Nx для создания службы angular.
Это пробный результат создания сервиса angular.
Заполните форму, добавьте ниже информацию
Имя: logging проект: branding-logger Служба регистрации уведомлений создана
Давайте запустим тест и посмотрим, проходит тест или нет. Поскольку я использую основную ветку вместо основной ветки, я обновлю свои скрипты, чтобы использовать только основную ветку.
Поскольку добавляется только служба ведения журнала, если я запускаю тест, будет запущен только один тест проекта. Запустить npm, запустить затронуто: тест
Шаг: Давайте возьмем зависимость регистратора от нашего приложения.
Далее, чтобы запустить сборку
Затронутый запуск npm: сборка
Обратите внимание, что приложение создается, и папка dist создается с одним приложением.
Теперь давайте воспользуемся службой ведения журналов, чтобы что-то записать, и затем запустим наше приложение.
Перейдите в libs/branding/logger/src/index.ts и экспортируйте службу регистратора.
Затем перейдите в apps\cutepuppies-admin\src\app\app.component.ts и добавьте службу ведения журнала и конструктор входа.
Давайте обслуживаем наше приложение, запускаемое ниже скрипта
nx обслуживать милых щенков-администратора
Перейдите по адресу https://localhost:4200 и в журнале консоли увидите, что hello world идет.
Теперь давайте снова создадим приложение, перейдем в папку dist и запустим приведенный ниже скрипт из папки dist.
cd dist/apps/cutepuppies-admin && npx http-server -o
Обратите внимание, что наше приложение работает, и я также вижу журнал.
Создание клиента милых щенков
Создан Sales/PuppyEditor
Создание продаж/щенков
Добавление библиотеки пользователей в службу поддержки клиентов
Добавление маршрута продаж щенков в клиентское приложение.
Добавление маршрута редактора щенков в приложение администратора
Создание компонента Add-Puppy в проекте Puppy Editor
Объявление модуля: libs/sales/puppy-editor/src/lib/sales-puppy-editor.module.ts
Проверьте результат пробного прогона и подтвердите
и выберите Выполнить
Компонент создан
Компонент хороший
Добавьте компонент «Puppies» в «Customers/Puppies» с помощью консоли Nx.
Чтобы получить путь компонента, скопируйте относительный путь папки lib продаж/щенков /libs/sales/puppies/src/lib
Выберите «Выполнить», чтобы создать компонент.
Позволяет обновить маршрут для щенков
Добавить маршрут для добавления щенка в редакторе щенков
Добавление маршрутов для приложения администратора
Добавить маршрут в приложение администратора: добавьте маршрут ниже, чтобы перейти к компоненту по умолчанию редактора щенков.
Добавление маршрутов для клиентского приложения
Добавить маршрут в клиентское приложение: добавьте маршрут ниже, чтобы перейти к компоненту модуля щенков по умолчанию.
Компонент обслуживающего администратора
Давайте добавим скрипты для обслуживания как административного, так и клиентского приложения.
"start-admin":"nx serve cutepuppies-admin --port=4210 -o",
"start-client":"nx serve cutepuppies-client --port=4211 -o",
Позволяет очистить HTML-код компонента приложения
Давайте запустим приложение администратора npm run start-admin
Сайт администратора запущен и работает https://localhost:4210/puppies-editor
Обслуживание клиентского приложения
компонент приложения
Выполнить npm run start-client
Сайт клиента запущен: https://localhost:4211/puppies
Архитектура рабочей области MonoRepo
Приложения
- Приложения — это системы IT-Ops
- Это может зависеть от чего угодно
- Только приложения можно создавать и публиковать в репозиториях npm.
Библиотеки
- Библиотеки — это сервисы
- Сервисы должны иметь несколько проектов
- Сервисы не должны зависеть друг от друга
- Проекты сервисов не должны зависеть друг от друга. Если только он не настроен на это.
- Сервисные проекты могут зависеть от брендинга
- Проекты сервисов нельзя создавать и публиковать.
- В проектах служб есть папка libs, которая эквивалентна папке модулей.
График зависимостей рабочего пространства MonoRepo
Беги nx dep-graph --base=main
Создание конвейера сборки CI
Установите расширение визуальной студии yaml
Установите Azure Pipelines для проверки файла yml
Кэширование Azure Pipeline
Мы установим модули узла и кешируем их, чтобы для будущей сборки они считывались из расположения кеша. Если package-lock.json изменен, он обновит кеш.
Мы собираемся использовать Восстанавливает и сохраняет артефакты конвейера с универсальными пакетами для кэширования конвейеров ci. Итак, установите задачу в свой лазурный devops из Marketplace.
Восстанавливает и сохраняет артефакты пайплайна с помощью универсальных пакетов
Поскольку мы используем кеш конвейера, нам нужно создать фид артефактов в Azure devops для хранения папок node_modules. Подробнее о ленте артефактов
Создание фида артефактов в Azure devops
Каналы артефактов — это организационные конструкции, которые позволяют вам хранить, управлять и группировать ваши пакеты, а также контролировать, с кем ими делиться. Фиды не зависят от типа пакета. В одном веб-канале можно хранить пакеты всех следующих типов: пакеты npm, NuGet, Maven, Python и Universal.
- Перейдите к артефактам Azure.
- Выберите Создать ленту.
- Дайте своему каналу имя и выберите его видимость, исходные источники и область действия.
Создание конвейера Azure CI
Выполните шаги, чтобы создать определение сборки
Шаг 1: Создайте файл шаблона yaml
Затем перейдите в проект и создайте файл install-node-module.yml:
- Мы установим node.js
- Запишем задачу на кеширование папки node_modules в лазурную ленту.
- Затем мы установим пакеты узлов, если кеш не существует.
steps:
- task: NodeTool@0
inputs:
versionSpec: '14.x'
displayName: 'Install Node.js'
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreAndSaveCacheV1.RestoreAndSaveCache@1
inputs:
keyfile: '**/package-lock.json, !**/node_modules/**/package-lock.json, !**/.*/**/package-lock.json'
targetfolder: '$(Build.SourcesDirectory)/node_modules'
vstsFeed: 'nx_azure_node_modules_cache_feed'
- script: |
npm ci --ignore-engines
displayName: Install dependencies only if there is no cache available
condition: ne(variables['CacheRestored'], 'true')
Шаг 2: Динамически сгенерируйте ci-скрипт
/**
* # Generate Ci command
* 1. It will identify the affected projects
* 2. Group the projects in 3 groups for each command.
* ## Example:
* If 3 projects are affected it will create below group for lint, test and build command
* lint1:[admin], lint2:[logger], lint3:[client]
* build1:[admin], build2:[logger], build3:[client]( if you made buildable for each projects)
* test1:[admin], test2:[logger], test3:[client]( if you have tests for each projects)
* If 4 projects are affected it will create below group for lint, test and build command
* lint1:[admin,ngmaterial], lint2:[logger], lint3:[client]
* build1:[admin,ngmaterial], build2:[logger], build3:[client]( if you made buildable for each projects)
* test1:[admin,ngmaterial], test2:[logger], test3:[client] ( if you have tests for each projects)
*/
const execSync = require('child_process').execSync;
const isMaster = process.argv[2] === 'False';
const baseSha = isMaster ? 'origin/main~1' : 'origin/main';
console.log(
JSON.stringify({
...commands('lint'),
...commands('test'),
...commands('build'),
})
);
function commands(target) {
const array = JSON.parse(
execSync(`npx nx print-affected --base=${baseSha} --target=${target}`)
.toString()
.trim()
).tasks.map((t) => t.target.project);
array.sort(() => 0.5 - Math.random());
const third = Math.floor(array.length / 3);
const a1 = array.slice(0, third);
const a2 = array.slice(third, third * 2);
const a3 = array.slice(third * 2);
return {
[target + '1']: a1,
[target + '2']: a2,
[target + '3']: a3,
};
}
Шаг : Создайте файл azure-pipelines.yml
trigger:
- main # Trigger CI automatically whenever main branch is changed
jobs:
- job: initial_setup # Install Node.js & Node Packages & Generate Commands for Affected Projects.
pool:
vmImage: 'ubuntu-latest'
variables:
IS_PR: $[ eq(variables['Build.Reason'], 'PullRequest') ] # Findout Is this pull request?
steps:
- template: .azure-pipelines/steps/install-node-modules.yml # Base Template
- powershell: echo "##vso[task.setvariable variable=COMMANDS;isOutput=true]$(node ./tools/scripts/generate-ci-commands.js $(IS_PR))" # Create COMMANDS build time variable and assign ci commands.
name: setCommands
- script: echo $(setCommands.COMMANDS) # Echo the command for log purpose.
name: echoCommands
Шаг: Давайте запустим сборку
Выберите запуск
На этот раз мы не изменили код, поэтому команда сборки не создавалась.
Шаг : Создайте Run Many JavaScript
Чтобы запустить анализ, тестирование и сборку, нам нужно создать файл run-many.js, в который мы поместим наш скрипт.
/**
* # Run Many
* It will run the script using nx command line to run them in parellel.
*/
const execSync = require('child_process').execSync;
const commands = JSON.parse(process.argv[2]);
const projects = commands[process.argv[3]];
const target = process.argv[4];
execSync(
`npx nx run-many --target=${target} --projects=${projects.join(
','
)} --parallel`,
{
stdio: [0, 1, 2],
}
);
Шаг : Добавьте скрипты lint, test и build.
trigger:
- main # Trigger CI automatically whenever main branch is changed
jobs:
- job: initial_setup # Install Node.js & Node Packages & Generate Commands for Affected Projects.
pool:
vmImage: 'ubuntu-latest'
variables:
IS_PR: $[ eq(variables['Build.Reason'], 'PullRequest') ] # Findout Is this pull request?
steps:
CAMTASIA_19_MEDIA_FORMAT*C:\Users\Rupesh\Documents\Camtasia\Rec 01-21-21_003.trec|Product - template: .azure-pipelines/steps/install-node-modules.yml # Base Template
- powershell: echo "##vso[task.setvariable variable=COMMANDS;isOutput=true]$(node ./tools/scripts/generate-ci-commands.js $(IS_PR))" # Create COMMANDS build time variable and assign ci commands.
name: setCommands
- script: echo $(setCommands.COMMANDS) # Echo the command for log purpose.
name: echoCommands
- job: lint1 # Lets Run the Linting command for lint1 array.
dependsOn: initial_setup
condition:
| # Make sure this is passing and Don't Run this if we have empty array in nx commands from intial_setup step.
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"lint1":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' lint1 lint # Run the lint command in parallel
- job: lint2
dependsOn: initial_setup
condition: |
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"lint2":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' lint2 lint
- job: lint3
dependsOn: initial_setup
condition: |
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"lint2":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' lint3 lint
- job: test1
dependsOn: initial_setup
condition: |
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"test1":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' test1 test
- job: test2
dependsOn: initial_setup
condition: |
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"test2":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' test2 test
- job: test3
dependsOn: initial_setup
condition: |
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"test3":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' test3 test
- job: build1
dependsOn: initial_setup
condition: |
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"build1":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' build1 build`
- job: build2
dependsOn: initial_setup
condition: |
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"build2":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' build2 build
- job: build3
dependsOn: initial_setup
condition: |
and(
succeeded(),
not(contains(
dependencies.initial_setup.outputs['setCommands.COMMANDS'],
'"build3":[]'
))
)
pool:
vmImage: 'ubuntu-latest'
variables:
COMMANDS: $[ dependencies.initial_setup.outputs['setCommands.COMMANDS'] ]
steps:
- template: .azure-pipelines/steps/install-node-modules.yml
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' build3 build
Step : Lets Run the build Again
Notice since we have not changed any code we will notice only initial_setup and rest all jobs will be skipped.
Step : Lets Change the Logger and find out affected dependency graph
Go libs\branding\logger\src\lib\logging.service.ts
Run npm run affected:dep-graph
подтвердите, что изменения работают. Запустите npm run start-admin
Шаг: исправьте спецификации администратора
Давайте создадим скрипт для тестирования и просмотра
Теперь давайте исправим тест для проекта администратора, исправьте название заголовка. Теперь запустите npm run affected:test
Он автоматически запустит тест администратора.
Шаг: исправьте спецификации клиента
Теперь, чтобы исправить тест для клиентского проекта, исправьте название заголовка. Теперь запустите npm run affected:test
Он автоматически запустит тест администратора и клиента.
Шаг: Давайте нажмем код и заметим наши сборки
Смотрите на этот раз в машине сборки, а также определили
- Только 2 проекта для анализа и тестирования, и они
- «брендинг-логгер», «милые щенки-админ»
- Только 1 проект для сборки, и это: «cutepuppies-admin»
Обратите внимание, что тест и сборка проходят
Шаг: Запуск всей сборки и тестирования lint в качестве ЗАДАЧИ.
Мы не хотим запускать столько заданий, так как наш монорепозиторий не такой большой. Поэтому я решил создать только одно задание, поэтому давайте выполним следующие шаги.
Шаг: Обновите скрипт генерации команды
Давайте обновим определение сборки, чтобы запускать их как задачу в одном задании.
Переименуйте текущий файл в generate-ci-batch-commands.js и создайте новый файл generate-ci-commands.js.
/**
* # Generate Ci command
* 1. It will identify the affected projects
* 2. Group the projects in 3 groups for each command.
* ## Example:
* If 3 projects are affected it will create below group for lint, test and build command
* lint1:[admin], lint2:[logger], lint3:[client]
* build1:[admin], build2:[logger], build3:[client]( if you made buildable for each projects)
* test1:[admin], test2:[logger], test3:[client]( if you have tests for each projects)
* If 4 projects are affected it will create below group for lint, test and build command
* lint1:[admin,ngmaterial], lint2:[logger], lint3:[client]
* build1:[admin,ngmaterial], build2:[logger], build3:[client]( if you made buildable for each projects)
* test1:[admin,ngmaterial], test2:[logger], test3:[client] ( if you have tests for each projects)
*/
debugger;
const execSync = require('child_process').execSync;
const isMaster = process.argv[2] === 'False';
const baseSha = isMaster ? 'origin/main~1' : 'origin/main';
console.log(
JSON.stringify({
...commands('lint'),
...commands('test'),
...commands('build'),
})
);
function commands(target) {
const array = JSON.parse(
execSync(`npx nx print-affected --base=${baseSha} --target=${target}`)
.toString()
.trim()
).tasks.map((t) => t.target.project);
return { [target]: array };
}
Если вы запустите этот скрипт, после изменения проекта logger. Он сгенерирует команды для затронутых проектов. Как ниже
{
"lint": ["cutepuppies-admin", "branding-logger"],
"test": ["cutepuppies-admin", "branding-logger"],
"build": ["cutepuppies-admin"]
}
Шаг: Обновите yml azure-pipeline, чтобы иметь шаги, а не задания.
Переименуйте текущий yml azure-pipeline.
и переместите в папку .azure-pipeline/steps для будущего использования или резервного копирования.
лазурные-трубопроводы.yml
trigger:
- main # Trigger CI automatically whenever main branch is changed
variables:
IS_PULLREQUEST: $[eq(variables['Build.Reason'], 'PullRequest')]
steps:
- task: Cache@2
displayName: 'Cache Npm Dependencies'
inputs:
key: '**/package-lock.json, !**/node_modules/**/package-lock.json, !**/.*/**/package-lock.json'
path: '$(Build.SourcesDirectory)/node_modules'
cacheHitVar: 'CacheRestored'
- task: Npm@1
displayName: 'npm ci'
inputs:
command: ci
verbose: false
condition: ne(variables['CacheRestored'], 'true')
- powershell: |
echo "##vso[task.setvariable variable=COMMANDS]$(node ./tools/scripts/generate-ci-commands.js $(IS_PULLREQUEST))"
name: setCommands
displayName: setting commands
- script: echo $(COMMANDS)
name: echoCommands
displayName: commands to run
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' lint
displayName: linting
condition: |
and(
succeeded(),
not(contains(
variables['COMMANDS'],
'"lint":[]'
))
)
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' test
displayName: testing
condition: |
and(
succeeded(),
not(contains(
variables['COMMANDS'],
'"test":[]'
))
)
- script: node ./tools/scripts/run-many.js '$(COMMANDS)' build
displayName: building
condition: |
and(
succeeded(),
not(contains(
variables['COMMANDS'],
'"build":[]'
))
)
Запустить сборку
Шаг: Измените только службу ведения журнала и запустите сборку.
Запустить сборку
Добивайтесь успеха
Шаг : Поиск уязвимых приложений
Давайте обновим команды, чтобы также предоставить список приложений для публикации в репозитории npm.
Когда мы меняем регистратор, мы также хотим видеть массив публикации с затронутым приложением.
{
"lint": ["branding-logger", "cutepuppies-admin"],
"test": ["branding-logger", "cutepuppies-admin"],
"build": ["cutepuppies-admin"],
"publish": ["cutepuppies-admin"]
}
Если я также изменю клиентское приложение, я должен увидеть оба приложения в массиве публикаций.
{
"lint": ["cutepuppies-client", "branding-logger", "cutepuppies-admin"],
"test": ["cutepuppies-client", "branding-logger", "cutepuppies-admin"],
"build": ["cutepuppies-client", "cutepuppies-admin"],
"publish": ["cutepuppies-client", "cutepuppies-admin"]
}
Я добавлю ниже метод для публикации приложений
function affectedApps(command) {
const x = execSync(`npx nx affected:apps --base=${baseSha}`)
.toString()
.trim();
let apps = x ? x.split('\n\n')[1].split(' - ').slice(1) : [];
apps = apps.map((t) => t.replace('\n', '').trim());
return { [command]: apps };
}
Новый скрипт для generate-ci-command.js
const execSync = require('child_process').execSync;
const isMaster = process.argv[2] === 'False';
const baseSha = isMaster ? 'origin/main~1' : 'origin/main';
console.log(
JSON.stringify({
...commands('lint'),
...commands('test'),
...commands('build'),
...affectedApps('publish'), // publish command for the apps
})
);
function commands(target) {
const array = JSON.parse(
execSync(`npx nx print-affected --base=${baseSha} --target=${target}`)
.toString()
.trim()
).tasks.map((t) => t.target.project);
return { [target]: array };
}
function affectedApps(command) {
const x = execSync(`npx nx affected:apps --base=${baseSha}`)
.toString()
.trim();
let apps = x ? x.split('\n\n')[1].split(' - ').slice(1) : [];
apps = apps.map((t) => t.replace('\n', '').trim());
return { [command]: apps };
}
Шаг: Давайте добавим Package.json в приложения
Пакет приложения администратора.json
Пакет клиентского приложения.json
Шаг : Обновите workspace.json для приложения.
Чтобы скопировать файл package.json после сборки в папку dist, обновите файл рабочей области.
административный проект
клиентский проект
Шаг: Сборка в производственном режиме
Я хочу создать свое угловое приложение ниже
- производственный режим
- нет хеширования выходного файла
- Мне нужен исходный файл карты, чтобы я мог отлаживать свой исходный файл.
Ниже приведена команда сборки nx для выполнения вышеуказанных требований.
nx run-many --target=build --projects=cutepuppies-client,cutepuppies-admin --parallel --configuration=production --outputHashing=none --sourceMap=true
Запустите выше команду сборки nx
Обратите внимание, что в папке dist в папке администратора мы получили файл карты и package.json.
клиентский проект
Шаг: Публикация результатов тестирования в Azure Pipeline Build
Шаг: публикация покрытия кода в Azure Pipeline Build
Шаг: добавьте файл .npmrc в приложение, которое мы хотим опубликовать npm.
Получите токен доступа из (npm.js)[https://www.npmjs.com/]
Создание нового токена доступа в npmjs
Добавление файла npmrc с авторизацией
Шаг: Обновление версии Package.json и публикация пакета Npm
/**
* . bump the version
* . add npmrc file (if required)
* . publish to npm package repository
*
* Example: node ./publish-npm.js $(COMMANDS) $(Build.BuildNumber)
*
*/
const execSync = require('child_process').execSync;
const fs = require('fs');
const path = require('path');
var basePackageJson = require('../../package.json');
const args = {
commands: process.argv[2],
buildNumber: process.argv[3],
};
console.log('Received Args: ', args, '\n');
if (!args.commands) {
throw new Error('Commands are required');
}
const commands = JSON.parse(args.commands);
/*
let commands = {
publish: ['cutepuppies-admin', 'cutepuppies-client'],
};
*/
if (!args.buildNumber) {
throw new Error('Build Number is required');
}
const buildNumber = args.buildNumber.toString();
// let buildNumber = '3243';
const projects = commands['publish'];
const newVersion = getNewVesrion();
console.log(`new npm version would be: ${newVersion}`, '\n');
updatePublishingPackageJsonVersion();
publishNpmPackage();
return newVersion;
function getNewVesrion() {
let currentVersion = basePackageJson.version;
return currentVersion
.split('.')
.map((x, i) => (i == 2 ? buildNumber : x))
.join('.');
}
function updatePublishingPackageJsonVersion() {
projects.forEach((project) => {
updateVersion(
path.resolve(__dirname, '../../', `dist/apps/${project}/package.json`)
);
});
}
function updateVersion(packageJsonFilePath) {
var package = require(packageJsonFilePath);
package.version = newVersion;
fs.writeFileSync(packageJsonFilePath, JSON.stringify(package, null, 2));
console.log(
`Version updated for the app ${package.name}: ${newVersion}`,
'\n'
);
}
function publishNpmPackage() {
projects.forEach((app) => {
const cwd = path.resolve(__dirname, '../../', `dist/apps/${app}`);
console.log(`publishing to npm from: `, cwd, '\n');
execSync(
`npm publish --access public`,
{ cwd, stdio: [0, 1, 2] },
function (error) {
console.log(error, '\n');
throw error;
}
);
console.log(`${app} is published`, newVersion, '\n');
});
}
Шаг : Отправьте изменения и запустите сборку
Смотрите, как админ и клиент опубликованы.
Шаг: измените только администратора и обратите внимание, что только администратор опубликован в NPM
Карма терпит неудачу, когда нет теста
Тест на карму провалится, если у вас нет тестов. Поэтому, если вы хотите остановить это поведение, вам нужно перейти к karma.conf.js
в корневом каталоге, где вы видите package.json
, и добавить ниже флаг как false.
failOnEmptyTestSuite: false
Руководство по архитектуре Monorepo для разработчиков
Что делают умные компоненты?
- Он может выполнять внедрение зависимостей и внедрять сервисные хранилища.
- Он может только отправить команду
- Он может вызывать только методы запроса для хранения, чтобы получить некоторый результат/данные.
- Он не может написать какой-либо фильтр, своего рода логика перемещает их в хранилище.
- Он не может опубликовать событие
- Он не может иметь логику бизнес-запросов или логику обновления модели.
- Он может передавать некоторую структуру данных во вложенные немые/презентационные компоненты.
Что может сделать немой / презентационный компонент?
- Нет внедрения зависимостей для службы и хранилища
- Только ввод и вывод
- Отвечает за рендеринг данных
- Нет публикации/отправки сообщения
- Нет запроса к хранилищу, нет изменения состояния модели
Что такое услуга?
- Он имеет бизнес-логику и
- Он может воспользоваться услугой API.
- Большинство служб являются обработчиками сообщений.
Тест: где мне написать код для создания виджета mcq?
Что такое модель?
- Это структура данных, которая имеет состояние.
- В нем не должно быть бизнес-логики.
- Он может иметь геттеры и сеттеры.
Что такое магазин?
Представляет собой коллекцию моделей. Если у вас есть несколько моделей одного типа, создайте хранилище, чтобы получить их и поместить в коллекцию.
- Не пишите бизнес-логику для изменения состояния модели в магазине.
- Бизнес-логика для изменения состояния модели используется только в СЛУЖБАХ.
- Все запросы, связанные с моделью (фильтрация, сортировка и т. д.), должны быть записаны в хранилище, к которому может обращаться компонент.
- Store мы можем написать код, который не входит в одну модель, и вы хотите охватить несколько экземпляров модели.
- Пример: сортировка моделей внутри магазина, утверждение перед добавлением модели в коллекцию.
Что такое API-сервис?
- Совершает сетевые http-вызовы и возвращает обещания.
- Он имеет зависимость от httpClient и расширяет базовую службу API.
- Служба API в основном возвращает объект модели на стороне клиента, а сервер должен возвращать клиенту ту же структуру данных модели. Однако в случае несоответствия между результатом, полученным от сервера и клиента. Затем используйте DTO для структуры данных на стороне сервера. DTO должен оставаться в папке DATA проекта.
Кто от кого зависит в библиотеке?
У нас есть компонентный уровень, обработчики сообщений и сервисные уровни API.
- Компонент может зависеть от модели и службы сообщений.
- Обработчик сообщений может зависеть от модели и API
- Служба API может зависеть от httpclient и dto
Рекомендации перед отправкой на Git
✅ RUN TEST: затронут npm run: test ✅ RUN LINT: затронут npm run: lint
Вы всегда можете запустить затронутые проекты в своей функциональной ветке, указав ее имя.
npm run affected:test --base=origin/features/PBI23/MessageHandlers
npm run affected:lint --base=origin/features/PBI23/MessageHandlers
Соглашения об именах
Не стесняйтесь следовать любым соглашениям об именовании, принятым в вашей организации. Вот что я рекомендую:
Вопросы, связанные с Nx
- Есть ли у проектов lib версии npm? Ответ: Нет, потому что их нельзя собирать и публиковать.
- Как мы должны ветвиться в монорепозитории? Ответ: V1, V2, V3 — это ветки для монорепозитория.
- Когда мы создаем ветки? Ответ: При переносе кода в новую среду рекомендуется создать отдельную ветку в монорепозитории.
Рекомендации
- Исходный код: https://github.com/rupeshtiwari/coding-examples-angular-monorepo-with-nx
- Исходный код: https://github.com/rupeshtiwari/coding-examples-angular-monorepo-nx-azure-ci-cd
- Указатель A-Z командной строки Windows CMD
- https://nx.dev/latest/react/getting-started/getting-started
Спасибо, что дочитали мою статью до конца. Надеюсь, сегодня вы узнали что-то особенное. Если вам понравилась эта статья, поделитесь ею с друзьями, а если у вас есть предложения или мысли, которыми можно поделиться со мной, напишите в поле для комментариев.
💖 Скажи мне 👋!
Рупеш Тивари
Основатель Fullstack Master
Электронная почта: [email protected]
Веб-сайт: RupeshTiwari.com