Build Node docker image with Azure Devops Npm Registry and Azure Devops Pipelines

Build Node docker image with Azure Devops Npm Registry and Azure Devops Pipelines

This page will explain how to build a docker image of a Node.Js application, that uses a private dependency stored in Azure Devops Artifact Npm Registry.

Note : I will not explain here how to publish a library to an Azure devops Artifact NPM Registry.

Source Code

As explained in the Azure Documentation, in order to use a dependency from Azure Devops Npm Registry, you will need to add an .npmrc file with the url of the registry.

This Documentation Get started with npm packages in Azure Artifacts will detail how to authenticate and configure Azure Devops NPM Registry.

Build Pipeline

Of course you will have to authenticate from your build execution in order to download the private dependencies.

If you use the Npm@1 task, your task will be automatically authenticated. Azure Pipeline will inject credentials automatically.

- task: Npm@1
    displayName: Install NPM Dependencies
    inputs:
      command: install
      workingDir: $(System.DefaultWorkingDirectory)/path/to/node-application

After the execution of this task, you can use npm script and it will be authenticated (as long as you run it in the same job, ie, the same agent).

Pipeline example :

variables:
  node_version: 16.19.1

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: NodeTool@0
    inputs:
      versionSpec: $(node_version)

  - task: Npm@1
    displayName: Install NPM Dependencies
    inputs:
      command: install
      workingDir: $(System.DefaultWorkingDirectory)/path/to/node-application

  - script: npm run test
    workingDirectory: $(System.DefaultWorkingDirectory)/path/to/node-application
    displayName: 'Execute unit tests'

Docker Build

Now imagine you want to build a docker image of your code. As you will have to run an npm install task during the docker build, you will need to have credentials inside the docker image.

Here is the Dockerfile of my application (this is a typescript application)

FROM node:18.12-alpine As BUILD
WORKDIR /app
COPY tsconfig.json .
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./ 
# Copy Authentication
COPY .npmrc .
RUN npm ci
COPY . .
RUN npm run build

FROM node:18.12-alpine
WORKDIR /app
COPY --from=BUILD /app/package*.json ./ 
COPY --from=BUILD /app/dist dist
# Copy Authentication
COPY --from=BUILD /app/.npmrc .
RUN npm ci --production
# Remove Authentication as it should not be in the final docker image
RUN rm -f .npmrc
EXPOSE 3000
CMD ["npm","start"]

As you can see, we specifically inject the .npmrc file inside the image (then remove it).

It is important to remove the .npmrc file from the image published.

In order to create the .npmrc file you can inject you will need to inject credentials inside the file.

For that you can use the npmAuthenticate@0 task jsut before the docker build :

- task: npmAuthenticate@0
    inputs:
      workingFile: $(System.DefaultWorkingDirectory)/path/to/node-application/.npmrc

- task: Docker@1
    name: docker_build_image
    displayName: 'Docker: Build image'
    inputs:
        azureSubscriptionEndpoint: 'Your Service Connetion'
        azureContainerRegistry: 'my-acr.azurecr.io'
        buildContext: path/to/node-application
        dockerFile: 'path/to/node-application/Dockerfile'
        imageName: my-application:$(Build.BuildNumber)
        includeLatestTag: true

I hope this quick article was helpfull, feel free to comment, or react, and if you like … say and share 😃