Единый репозиторий Git содержит исходный код нескольких приложений и библиотек.

Зачем использовать монорепо?

Monorepo в организации поможет вам получить следующие преимущества:

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

Что я должен знать перед использованием Monorepo?

Если вы уже разработали пару проектов на JavaScript, тогда вам пора изучать Monorepo. Для этой статьи, если у вас есть базовые знания Angular, это будет хорошо, так как я буду создавать проекты Angular в примере монорепозитория.

Где исходный код для Nx Monorepo?

Вот пакет Nx npm, который вы можете использовать из npm. В этой статье я продемонстрирую код, размещенный на github по адресу ниже:

Проблемы с Монорепо?

Проблемы заключаются в том, как определить, какое приложение зависит от скольких проектов? Как ограничить зависимость, чтобы уменьшить проблемы циклической зависимости и организовать структуру кода? Как скомпилировать только тот проект, который только изменился? Как запускать тесты только для измененных проектов? Как увеличить скорость инструментов для большого набора проектов в одном монорепозитории? Как управлять версиями? Все это очень важные требования. Платформа монорепозитория 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 и библиотеками из пустого пресета.

  1. Мы будем использовать кэширование Azure Pipeline для повышения скорости сборки за счет кэширования пакетов npm.
  2. Мы опубликуем наше приложение в npm из пайплайна в автоматическом режиме.

В Монорепо мы проделаем нижеприведенную работу

  1. Мне нужна карма в качестве тестировщика
  2. Мне не нужен e2e-тест для приложений
  3. Я хочу, чтобы мои приложения можно было публиковать
  4. Я хочу, чтобы мои библиотеки нельзя было публиковать
  5. Создайте конвейеры Azure CI/CD
  6. Разверните приложение в реестре 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

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

  • 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 с пользовательскими настройками

  1. Выполнить npx create-nx-workspace@latest
  2. Укажите название организации: curise
  3. Выберите угловое приложение для рабочей области. angular

Это установит плагин @nrwl/angular, чтобы дать вам инструменты для создания angular lib и приложений.

  1. Затем введите имя углового приложения: admin

  1. Затем выберите формат стиля как SASS.
  2. Линтер по умолчанию: выберите TSLINT, так как у меня есть угловое приложение
  3. Nx Cloud предоставляет кэширование через облако: Нет (требуется платный план подписки)

Запустите скрипт:

Понимание рабочего пространства Nx Monorepo

  1. Приложения
  2. библиотеки
  3. tools В папке Tools есть файл tsconfig, что означает, что вы можете писать машинописные файлы и компилировать их в JS.
  4. decorate-angular-cli.js
  5. jest.preset.js
  6. angular.json Создает файл angular.json для управления рабочим пространством. Однако, если вы хотите создать пустой проект монорепозитория, он создает файл workspace.json.

Создание проектов в Nx Monorepo

Беги nx build

Показана папка dist.

Преимущества и недостатки пустого Nx Monorepo

После создания пустого рабочего пространства.

Преимущества пустой рабочей области

  1. Вы можете использовать средство запуска юнит-тестов karma
  2. Вы можете использовать workspace.json для настройки любых приложений и библиотек.

Недостатки пустого рабочего пространства:

  1. Вы должны написать угловой декоратор cli для Nx CLI

Независимо от того, пусто или предварительно заполнено рабочее пространство, вы должны создать свои собственные CI/CD конвейеры и команды Nx для своих конвейеров.

Создание нового пустого рабочего пространства Nx Monorepo

Теперь мы создадим наше рабочее пространство монорепозитория, которое мы будем продолжать использовать. Мы начнем с нуля пустой монорепозиторий.

Создание пустой рабочей области Nx Monorepo — наш последний вариант для этого примера. Итак, давайте создадим нужную рабочую папку и из этого места откроем powershell на компьютере с Windows, который, как я обнаружил, работал на git bash commands, но работал не очень хорошо.

Запустите приведенный ниже скрипт, чтобы создать пустое рабочее пространство.

npx create-nx-workspace@latest --preset=empty

Вышеупомянутый скрипт спросит вас

  1. название организации и
  2. Хотите использовать 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, выполните следующие действия:

  1. Замените вхождения nx на ng в вашем package.json
  2. Удалите скрипт из постустановочного скрипта в package.json.
  3. Удалите и переустановите ваши 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.

  1. Перейдите к артефактам Azure.

  1. Выберите Создать ленту.

  1. Дайте своему каналу имя и выберите его видимость, исходные источники и область действия.

Создание конвейера Azure CI

Выполните шаги, чтобы создать определение сборки

Шаг 1: Создайте файл шаблона yaml

Затем перейдите в проект и создайте файл install-node-module.yml:

  1. Мы установим node.js
  2. Запишем задачу на кеширование папки node_modules в лазурную ленту.
  3. Затем мы установим пакеты узлов, если кеш не существует.
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 Он автоматически запустит тест администратора и клиента.

Шаг: Давайте нажмем код и заметим наши сборки

Смотрите на этот раз в машине сборки, а также определили

  1. Только 2 проекта для анализа и тестирования, и они
  2. «брендинг-логгер», «милые щенки-админ»
  3. Только 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, обновите файл рабочей области.

административный проект

клиентский проект

Шаг: Сборка в производственном режиме

Я хочу создать свое угловое приложение ниже

  1. производственный режим
  2. нет хеширования выходного файла
  3. Мне нужен исходный файл карты, чтобы я мог отлаживать свой исходный файл.

Ниже приведена команда сборки 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 для разработчиков

Что делают умные компоненты?

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

Что может сделать немой / презентационный компонент?

  • Нет внедрения зависимостей для службы и хранилища
  • Только ввод и вывод
  • Отвечает за рендеринг данных
  • Нет публикации/отправки сообщения
  • Нет запроса к хранилищу, нет изменения состояния модели

Что такое услуга?

  1. Он имеет бизнес-логику и
  2. Он может воспользоваться услугой API.
  3. Большинство служб являются обработчиками сообщений.

Тест: где мне написать код для создания виджета mcq?

Что такое модель?

  • Это структура данных, которая имеет состояние.
  • В нем не должно быть бизнес-логики.
  • Он может иметь геттеры и сеттеры.

Что такое магазин?

Представляет собой коллекцию моделей. Если у вас есть несколько моделей одного типа, создайте хранилище, чтобы получить их и поместить в коллекцию.

  1. Не пишите бизнес-логику для изменения состояния модели в магазине.
  2. Бизнес-логика для изменения состояния модели используется только в СЛУЖБАХ.
  3. Все запросы, связанные с моделью (фильтрация, сортировка и т. д.), должны быть записаны в хранилище, к которому может обращаться компонент.
  4. Store мы можем написать код, который не входит в одну модель, и вы хотите охватить несколько экземпляров модели.
  5. Пример: сортировка моделей внутри магазина, утверждение перед добавлением модели в коллекцию.

Что такое API-сервис?

  1. Совершает сетевые http-вызовы и возвращает обещания.
  2. Он имеет зависимость от httpClient и расширяет базовую службу API.
  3. Служба 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 — это ветки для монорепозитория.
  • Когда мы создаем ветки? Ответ: При переносе кода в новую среду рекомендуется создать отдельную ветку в монорепозитории.

Рекомендации

Спасибо, что дочитали мою статью до конца. Надеюсь, сегодня вы узнали что-то особенное. Если вам понравилась эта статья, поделитесь ею с друзьями, а если у вас есть предложения или мысли, которыми можно поделиться со мной, напишите в поле для комментариев.

💖 Скажи мне 👋!
Рупеш Тивари
Основатель Fullstack Master
Электронная почта: [email protected]
Веб-сайт: RupeshTiwari.com