Use npm scopes
Azure DevOps Services | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018 - TFS 2017
Npm scopes are a way of grouping related packages together. A scope allows you to create a package with the same name as a package created by another user or Org without conflict. They allow the user to separate public and private packages by prefixing their packages with a scope @SCOPE_NAME and configuring the .npmrc file to only use an Azure Artifacts feed for that scope.
With Azure Artifacts, you can publish and download both scoped and non-scoped packages to/from your Artifacts feeds or public registries. Using npm scopes is also useful with self-hosted on-premise servers that do not have internet access because setting up upstream sources in that case is not possible. Using scopes:
- We don't have to worry about name collisions.
- No need to change the npm registry in order to install or publish our packages.
- Each npm organization/user has their own scope, and only the owner or the scope members can publish packages to their scope.
Note
You need npm version 2 or greater to use npm scopes. Run npm install npm@latest -g to upgrade to the latest version.
Project setup
To use an Azure Artifacts feed with a specific scope, we will need to set up our .npmrc file and then set up credentials to authenticate with our feed.
Project setup
From within your project, select Azure Artifacts, and then select Connect to feed.
Select npm.
Select the Other tab.
Add a .npmrc file in the same directory as your package.json, and paste the following snippet into your file.
registry=https://pkgs.dev.azure.com/<yourOrganization>/_packaging/<yourFeed>/npm/registry/ always-auth=true
Credentials setup
Copy the following snippet into your .npmrc file.
Organization-scoped feed:
; begin auth token //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/registry/:username=[ENTER_ANY_VALUE_BUT_NOT_AN_EMPTY_STRING] //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/registry/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN] //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/registry/:email=npm requires email to be set but doesn't use the value //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/:username=[ANY_VALUE_BUT_NOT_AN_EMPTY_STRING] //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN] //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/:email=npm requires email to be set but doesn't use the value ; end auth tokenProject-scoped feed:
; begin auth token //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/registry/:username=[ENTER_ANY_VALUE_BUT_NOT_AN_EMPTY_STRING] //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/registry/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN] //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/registry/:email=npm requires email to be set but doesn't use the value //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/:username=[ENTER_ANY_VALUE_BUT_NOT_AN_EMPTY_STRING] //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN] //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/:email=npm requires email to be set but doesn't use the value ; end auth token
Generate a personal access token with packaging read and write scopes.
Encode your newly generated personal access token as follows:
Run the following command in an elevated command prompt window, and then paste your personal access token when prompted:
node -e "require('readline') .createInterface({input:process.stdin,output:process.stdout,historySize:0}) .question('PAT> ',p => { b64=Buffer.from(p.trim()).toString('base64');console.log(b64);process.exit(); })"You can also use the following command to convert your personal access token to Base64 depending on your operating system:
Windows:
[Convert]::ToBase64String([system.Text.Encoding]::UTF8.GetBytes("YOUR_PAT_GOES_HERE"))LinuxMac:
echo -n "YOUR_PAT_GOES_HERE" | base64
Copy the Base64 encoded value.
Open your .npmrc file and replace the placeholder
[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]with your user encoded personal access token you just created.
From Packages, select Connect to feed.
Select npm.
Select Generate npm credentials, and then copy the credentials to add them to your user .npmrc file manually:
In your .npmrc file, replace registry=<YOUR_SOURCE_URL> with @SCOPE_NAME:registry=<YOUR_SOURCE_URL>.
Note
Make sure you add the scope and package names to your package.json file: { "name": "@SCOPE_NAME/PACKAGE_NAME" }.
Upstream sources or scopes?
Upstream sources give you the most flexibility to use a combination of scoped and non-scoped packages in your feed, as well as scoped and non-scoped packages from public registries such as npmjs.com.
Scopes add another restriction when naming your packages: each package name must start with @<scope>. If you want to publish your private packages to public registries, you must do so with the scopes intact. If you remove package scopes when deploying your packages, you'll need to update all the references in your package.json. With that in mind, scopes can be a viable alternative to upstream sources.