Blog author's portrait
Artur Tagisow
Vue dev from Eastern Poland

Azure DevOps - rolling release private npm package

In this tutorial we’ll cover how to create:

  1. a dummy package we’ll deploy

  2. an Azure Artifacts feed - where our package will be stored privately and available only to our co-workers - instead of the public npm package registry

  3. (NOTE: NOT DONE YET) an Azure Pipeline that will release the package whenever a PR is merged to the dummy package

  4. (NOTE: NOT DONE YET) mention that when you create a org-scoped azure artifacts feed you need to remember to give permissions to the right teams and those teams’ build services, otherwise only you and admins will be able to download pkgs - ie. mortal users will get a 403 Forbidden error during npm install, even if they are logged in correctly

TODO:

  1. Rewrite this so it shows one of my private packages, not some censored screenshots from work :P With a private package, I guess I could make a devops project public so this is more interactive?
  2. Add some npm run build command to the dummy package - right now the pipeline just uses npm run build even though in step 1 I don’t mention it (I use Vue/Vite but not everyone needs to)

1. Creating a dummy package to deploy

I’m going to assume you already have a repository on Azure DevOps.

In your repo:

npm init -y
git add -A
git commit -m "add dummy package"
git push

If have a pre-existing project with package.json, make sure to remove private: true from that file. It’s required because a private package can’t be published. The below change is what I’m asking you to do:

package.json
{
-  "private": true,
  "scripts": {
    "build": "vite build",
    "dev": "vite --port 3333 --open"
  },
  "dependencies": {

2. Creating an Azure Artifacts feed

The most popular source of javascript packages is the NPM registry. The packages there are publicly available - eg. webpack on NPM.

BUT - we want to publish a package that’s closed source and owned by our organization/employer. The solution is to use Azure Artifacts - it’s similar to the NPM registry, but it’s controlled by you. Only you can decide who can see and download packages uploaded to it.

Below are step by step instructions for creating an Azure Artifacts feed (aka “your own private NPM registry”):

  1. Azure Artifacts has its own section in the sidebar on the Azure DevOps website. Click its icon:
    The sidebar seen on the Azure DevOps website is shown. Azure Artifacts is the last button on the sidebar and is marked with a red arrow

  2. Next, click the “Create Feed” button:
    The Azure Artifacts main view is shown. The "Create feed" button is marked with a red square at the top

  3. A side menu will apear on the right. In the “Name” field at the top, type in a name for your feed (can be anything you want). After that, click the “Create” button at the bottom:
    The "Create a feed" screen is visible. The input box labelled "Name" is marked as the first step. The input box has the contents "my_feed". The "Create feed" button is marked as the second step

  4. You’ve finished creating an Azure Artifacts feed!
    You’ll see a “Connect to the feed to get started” screen. We won’t be connecting to the feed ourselves at all, because Azure Devops will do that for us, so we’ll ignore this screen.
    We’ll be releasing the package purely through Azure Pipelines and its Releases feature.
    The view of an empty Azure Aritfacts feed is shown. It's telling the user to connect to the feed to add packages

In the next step (step 3) we’ll build our package and store its build result (artifacts). Even later - in step 4 - we’ll finally release the package to the feed we created in this step (step 2).

3. Creating an Azure Pipeline that will build our package and store its dist

We’ll:

3.1. npm run build our package

3.2. set the package’s version number based on the unique Azure Devops “Build ID”

3.3. publish the built artifact (that we’ll add a relase for later)

For the pipeline, use this azure-pipelines.yml:

# We don't want to run certain tasks in PR code-checks 
# (eg. prevent publishing artifacts in unapproved pull requests).
# This is used later in `condition: `
variables:
  isNonPrBuild: eq(variables['Build.SourceBranch'], 'refs/heads/master')

steps:
- task: Npm@1
  displayName: 'npm install'
  inputs:
    command: 'custom'
    # i'm using --ignore-scripts to disable husky, and npm ci instead of npm install
    customCommand: 'ci --ignore-scripts' 
    verbose: false

# 3.1. "`npm run build` our package"
- task: Npm@1
  displayName: 'Build dummy package'
  inputs:
    command: 'custom'
    customCommand: 'run build'

# 3.2. "set the package's version number based on the unique Azure Devops "Build ID""
  # Bumps "version" property in package.json before publishing
  # --force to suppress "Git working directory not clean" error (Azure Pipelines makes .npmrc dirty)
  # --no-git-tag-version to avoid having to set git user.name user.email
- script: npm version 1.0.$(Build.BuildID) --force --no-git-tag-version
  displayName: Set npm package version
  condition:
    eq(variables.isNonPrBuild, true)

# part 1 - "3.3 publish the built artifact (that we'll add a relase for later)"
- task: CopyFiles@2
  displayName: 'Copy lib files'
  inputs:
    targetFolder: '$(Build.ArtifactStagingDirectory)/npm'  
    Contents: |
      **/**
      !node_modules/**
  condition:
    eq(variables.isNonPrBuild, true)

# part 2 - "3.3 publish the built artifact (that we'll add a relase for later)"
- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact: drop'
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
  condition:
    eq(variables.isNonPrBuild, true)

TODO: Add instructions on creating a pipeline, where to put this yaml etc.

4. Add release stage

The pipeline will build your package whenever a pull request is merged to the master branch but the package won’t be published anywhere yet. For that, we need to create release definition.

  1. Adding a release stage. First, click the "Releases" menu item in the "Pipelines" submenu. Then, click "+ Add" near the "Stages" text. Then, in the dropdown menu that appears - press "New stage"
  2. In the sidebar that appears on the right, click the "Or start with an:Empty job" job

    1. In the “Command” dropdown, select the option custom
    2. In the “Working folder that contains package.json” field, press the button with 3 dots and select the artifact name published in step 3.3
    3. In the “Command and arguments” text field, write publish --ignore-scripts, which will make Azure Devops run npm publish --ignore-scripts. publish is a standard command in npm if you’re familiar with publishing packages manually. Here we let DevOps do that for us :)
      I’m also using --ignore-scipts to prevent husky from running its precommit hooks. If you don’t use husky, you can skip that argument and just write publish in the text field.
    4. In the “Registries to use” radio button list, select “Registry I select here”.
    5. In the “Use packages from this Azure Artifacts/TFS registry” dropdown, select the name of the Azure Artifacts feed you created in step 2.

5. Releasing for the first time

  1. You’ll need to make the pipeline from step 3 run at least once, so that an artifact is created.

  2. After that, go to the Pipelines>Releases screen and hit the “Create a release” button.

  3. Select an artifact, then release it.

  4. Hope it runs successfully!

  5. It should appear in the feed also