In this requirement. We are using CI / CD from the GitHub repository and creating an artifact and Deploying Next Js on Azure App Service, which is using “Strapi – Open source Node.js Headless CMS” from an Azure App Service using Azure DevOps Releases. We are using staging slots, and upon Approval, it will deploy to the production slot.
Good to know – CI/CD are Continuous Integration, Continuous Delivery, and Continuous Deployment
Pipelines are integrated with GitHub Repo for CI / CD and are deployed to UAT (Azure App Service Staging Slot ), where they can be tested. If it gets approved (Approval Gates), After testing, it will be deployed to the Production Slot in the same App Service. Otherwise, the change can be rejected.
Ran the node js locally using [ npm run dev / npm run build ], which went through successfully.
A similar article for CI / CD is done for Azure App Service using Azure DevOps.
Azure DevOps Pipelines for App Service with GitHub – Azure365Pro.com
Azure DevOps Pipelines for Static Web Apps with GitHub – Azure365Pro.com
As you see below, we are using a Git hub source repository. First Deploys to a Standard UAT (Azure App Service Staging Slot ). If an approver approves changes, It’s deployed to a Production Azure App Service.
Now you can see the releases happening on UAT and Production environments without any manual steps. Let’s see how to implement the same.
Create New Pipeline – Choose Github (YAML) as I use GitHub in my case
Choosing Node.js Express Web App on Linux on Azure
Using below YAML – azure-pipelines.yml
# Node.js Express Web App to Linux on Azure # Build a Node.js Express app and deploy it to Azure as a Linux web app. # Add steps that analyze code, save build artifacts, deploy, and more: # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript trigger: - main variables: # Azure Resource Manager connection created during pipeline creation azureSubscription: 'd4200000-0000-0000-0000-9bb00007d505' # Web app name webAppName: 'az-az365pro-pr-fc-web-rg' # Environment name environmentName: 'az-az365pro-pr-fc-web-rg' # Agent VM image name vmImageName: 'ubuntu-latest' stages: - stage: Build displayName: Build stage jobs: - job: Build displayName: Build pool: vmImage: $(vmImageName) steps: - task: NodeTool@0 inputs: versionSpec: '18.x' displayName: 'Install Node.js' - script: | npm install npm run build --if-present # npm run test --if-present displayName: 'npm install, build and test' - task: ArchiveFiles@2 displayName: 'Archive files' inputs: rootFolderOrFile: '$(System.DefaultWorkingDirectory)' includeRootFolder: false archiveType: zip archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip replaceExistingArchive: true - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip artifact: drop
Having two files in my repo
Added ecosystem.config.js. We need to start this separately
module.exports = { apps: [ { name: "az-az365pro-web", script: "./node_modules/next/dist/bin/next", args: "start -p " + (process.env.PORT || 3000), watch: false, autorestart: true, }, ], };
if bin location is in different place
ecosystem.config.js.
module.exports = { apps: [ { name: "az-az365pro-web", script: "./node_modules/.bin/next", args: "start -p " + (process.env.PORT || 3000), watch: false, autorestart: true, }, ], };
As you can see below, running this store my build to Azure Artifact where Azure Releases can use them.
Now as by build is released with the zip sent to my drop location
Now my source in releases is artifact
The startup command set to
pm2 start /home/site/wwwroot/ecosystem.config.js --no-daemon
if pm2 start command doesn’t exist – build will fail
root@fff533200d57:/home/site/wwwroot# npm run dev
npm info using npm@9.6.4
npm info using node@v18.16.1
> fulcrum-dashboard@0.1.0 dev
> next dev
node:internal/modules/cjs/loader:1080
throw err;
^
Error: Cannot find module ‘../build/output/log’
Require stack:
– /home/site/wwwroot/node_modules/.bin/next
at Module._resolveFilename (node:internal/modules/cjs/loader:1077:15)
at Module._load (node:internal/modules/cjs/loader:922:27)
at Module.require (node:internal/modules/cjs/loader:1143:19)
at require (node:internal/modules/cjs/helpers:110:18)
at Object.
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) {
code: ‘MODULE_NOT_FOUND’,
requireStack: [ ‘/home/site/wwwroot/node_modules/.bin/next’ ]
}
Staging is set to a Staging slot.
Staging slot is created for use – Add Slot – Staging
In my next stage Production Slot is chosen with the same settings
Now my App Service URLs are ready with below
Staging – (Upon Approval, Goes to Prod)
https://azure365pro-statging.azurewebsites.net/en-US
Production –
https://azure365pro.azurewebsites.net/en-US
Continous Deployment Trigger is enabled so that UAT is pushing on every new build.