简体   繁体   中英

How to deploy multiple apps (monorepo) with Azure and NX

I'm using NX tools to manage a monorepo with multiple apps and I'm struggling to understand how to deploy using Azure and release pipelines.

Disclaimer : I'm very new to Azure and devops in general.

My understanding is this: I create a pipeline (not a release pipeline, just a "regular one" if that make any sense) and plug a yml to it. Also, the pipeline is linked to a repo on Azure Repos, which means that every time I push to this repo, it will trigger the pipeline and run the yaml commands. On this commands, I run lint, test and builds.

This is what I can do and can understand, the following become more obscure :

The build job is supposed to create an artifact if I'm pusing/merging on master which I can conditioned. Now I can create a release pipeline which will be trigger when to repo it is linked to will create an artifact. This release pipeline can then send this artifact to an app service which is a slot where an app will live.

Okay, but I'm using a monorepo, which means the build will produce multiple applications and each one of these apps should be deploy to the correct app service.

After some research, I found that the general idea is to create one release pipeline for each app. These release pipelines are all linked to the same monorepo, but they have a filter which is a build tag. The build tag is added while building the apps using the yml file.

So these are basically my understanding of all of this. Now here are the questions:

  1. What exactly is a build tag and where does it live? Is it somehow linked to an artifact?
  2. The idea is to create one build tag per artifact, right?
  3. I failed to create a build tag, how can I do so?
  4. What is the correct way to zip and publish the artifacts?

    Here is the yaml I'm using:

jobs:
  - job: Lint
    steps:
      - task: NodeTool@0
        inputs:
          versionSpec: '12.x'
        displayName: 'Install Node.js'
      - task: Npm@1
        displayName: 'Npm install'
      - pwsh: 'npm run nx affected -- --target=lint --parallel --base=origin/master --maxParallel=4'
        displayName: 'Running lint'

  - job: Test
    steps:
      - task: NodeTool@0
        inputs:
          versionSpec: '12.x'
        displayName: 'Install Node.js'
      - task: Npm@1
        displayName: 'npm install'
      - pwsh: 'npm run nx affected -- --target=test --parallel --code-coverage --base=origin/master --maxParallel=4'
        displayName: 'Running tests'

  - job: Build
    steps:
      - task: NodeTool@0
        inputs:
          versionSpec: '12.x'
        displayName: 'Install Node.js'
      - task: Npm@1
        displayName: 'npm install'
      - pwsh: 'npm run nx affected -- --target=build --parallel --base=origin/master --prod'
        displayName: 'Running build'
      - pwsh: |
          npm run nx affected:apps -- --base=HEAD~1  --head=HEAD | grep -E '( - )(\w|-|\d|_)+' | sed -E 's/ - /##vso[build.addbuildtag]/g'
        displayName: 'Adding build tags'

When running this, test, lint and builds are working, but I don't think it adds a build tag, here is the log:

在此处输入图像描述

It appears like nothing happens... How can I correctly add the tag and make the release pipeline to be triggered?

I've also found this snippet to zip and publish the artifact, but I don't know if I can use that since in a monorepo we should - I think - create multiple artifacts.

5) So the last question is: how can I create multiple artifacts and is it even the good thing to do?

Many many thanks in advance for the help, I know this is a long boring post and helping a noob can be boring but I'm stuck with this for way to long...

1, You can add tags for a build from the UI page(Build summary page, see below) or using Rest api . It can be used to filter between builds. If your build pipeline generated multiple artifacts, build tags will not be able to filter the artifacts which generated in one build.

在此处输入图像描述

2, So you can think about how to separate the build artifacts for each app in your pipeline.

You can use Archive files task to zip your build artifacts and publish using publish build artifacts task .

See below yaml example: I used two archive files task to package the build artifacts for app1 and app2 separately. And save the zipped artifacts in folder $(Build.ArtifactStagingDirectory) . Then the publish build artifacts task will publish the artifacts to azure devops cloud. (Release pipeline will download the artifacts to deploy to your app server)

- task: ArchiveFiles@2
      inputs:
        rootFolderOrFile: $(system.defaultworkingdirectory)/app1/dist
        archiveType: 'zip'
        archiveFile: '$(Build.ArtifactStagingDirectory)/app1/dist1.zip'
        includeRootFolder: false
      enabled: true
    - task: ArchiveFiles@2
      inputs:
        rootFolderOrFile: $(system.defaultworkingdirectory)/app2/dist
        archiveType: 'zip'
        archiveFile: '$(Build.ArtifactStagingDirectory)/app2/dist2.zip'
        includeRootFolder: false
      enabled: true


    - task: PublishBuildArtifacts@1
      inputs:
        PathtoPublish: $(Build.ArtifactStagingDirectory)/
        artifactName: build

3, Then you can use multiple stages in your release pipeline and use Azure App Service Deploy task . For below example:

In stage one add Azure App Service Deploy task and set the package to $(System.DefaultWorkingDirectory)/**/app1/dist1.zip to deploy app1. And in stage two set it to $(System.DefaultWorkingDirectory)/**/app2/dist2.zip to deploy app2. 在此处输入图像描述

Another workaround to deploy multiple apps in monorepo is to create multiple build/release pipeline, one for each app. And in the build pipeline using path filter to let the build pipeline only be triggered when it corresponding app is updated.

trigger:
  paths:
    include:
    - root/app1/*

Hope above helps!

@levi Lu-MSFT answered helped me a ton and is basically how we are doing it, but they didn't really give full yaml code.

The way we have the release pipeline set up is one release pipeline per application, looking at the build tags for the release trigger. We are currently bundling all apps together inside one artifact, so the downside is when an app's main branch is triggered, the release process will download all the apps.

The way I added my build tags dynamically started with this nx command:

npx nx affected:apps --base=origin/main --plain

which prints out a list of apps that have changed. From there, I loop through the apps and create the build tags dynamically.

Here I have two jobs, one that runs on PR's and one that runs on main branch.

trigger:
  branches:
    include:
      - main
pool:
  name: My-Pool
  demands: Agent.OS -equals Windows_NT

jobs:
  - job: NX_AFFECTED_PR
    pool:
      name: WBOmega-Pool
      demands: Agent.OS -equals Windows_NT
    condition: eq(variables['Build.Reason'], 'PullRequest')
    steps:
      - task: Npm@1
        displayName: 'npm install'
        inputs:
          command: 'install'
          verbose: true

      - powershell: |
          npx nx affected:apps --base=origin/main --plain | Tee-Object -Variable output

          [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
          foreach ($appName in ($output -replace '\s+', ' ').Split()) {
                  Write-Host "this is the app-name: $appName"
                  if($appName.Trim() -eq ""){
                    Write-Host "App name is blank"
                  } else {
                    $url="https://dev.azure.com/YOUR_ORG/$(System.TeamProject)/_apis/build/builds/$(build.buildid)/tags/" + $appName + "?api-version=6.0"
                    Write-Host $url
                    $result = Invoke-RestMethod -Uri $url -Headers @{Authorization = "Bearer $(System.AccessToken)"} -Method Put
                  }
          }
        name: set_build_tags
      - powershell: |
          npx nx affected --target=build --base=origin/main --parallel --max-parallel=3
        name: build
      - powershell: |
          npx nx affected --target=lint --base=origin/main --parallel --max-parallel=3
        name: lint
      - powershell: |
          npx nx affected --target=test --base=origin/main --parallel --max-parallel=3
        name: test

  - job: NX_AFFECTED_MAIN
    pool:
      name: My-Pool
      demands: Agent.OS -equals Windows_NT
    condition: ne(variables['Build.Reason'], 'PullRequest')
    steps:
      - checkout: self
        persistCredentials: true
      - powershell: |
          npx nx affected:apps --base=HEAD~1 --plain | Tee-Object -Variable output
          [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
          foreach ($appName in ($output -replace '\s+', ' ').Split()) {
                  Write-Host "this is the app-name: $appName"
                  if($appName.Trim() -eq ""){
                    Write-Host "App name is blank"
                  } else {
                    $url="https://dev.azure.com/YOUR_ORG/$(System.TeamProject)/_apis/build/builds/$(build.buildid)/tags/" + $appName + "?api-version=6.0"
                    Write-Host $url
                    $result = Invoke-RestMethod -Uri $url -Headers @{Authorization = "Bearer $(System.AccessToken)"} -Method Put
                  }
          }
        name: set_build_tags
      - powershell: |
          npx nx affected --target=build --base=HEAD~1 --parallel --max-parallel=3
        name: build
      - powershell: |
          npx nx affected --target=lint --base=HEAD~1 --parallel --max-parallel=3
        name: lint
      - powershell: |
          npx nx affected --target=test --base=HEAD~1 --parallel --max-parallel=3
        name: test
      - task: CopyFiles@2
        displayName: 'Copy Files to: $(build.artifactstagingdirectory)\apps\'
        inputs:
          SourceFolder: '$(Build.SourcesDirectory)\dist\apps\'
          TargetFolder: '$(build.artifactstagingdirectory)\apps\'
          CleanTargetFolder: true
          OverWrite: true

      - task: PublishBuildArtifacts@1
        displayName: 'Publish Artifact'
        inputs:
          PathtoPublish: '$(Build.ArtifactStagingDirectory)\apps'
          ArtifactName: 'Apps'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM