Use Npm scopes in Azure Artifacts
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
Npm scopes serve as a means to categorize related packages into groups. These scopes enable you to create packages with identical names to those created by different users without encountering conflicts. By using scopes, you have the ability to segregate public and private packages by adding the scope prefix @scopeName and configuring the .npmrc file to exclusively use a feed with that particular scope.
Azure Artifacts provides the capability to publish and download both scoped and nonscoped packages from feeds or public registries. Npm scopes are particularly valuable when working with self-hosted on-premises servers lacking internet access, as configuring upstream sources in such scenarios isn't feasible. In summary, when 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.
Project setup
Sign in to your Azure DevOps organization, and then navigate to your project.
Select Artifacts, and then select Connect to feed.
Select npm, and then select Other.
Add a
.npmrc
file in the same directory as your package.json, and paste the following snippet into your file.Organization-scoped feed:
registry=https://pkgs.dev.azure.com/<ORGANIZATION_NAME/_packaging/<FEED_NAME>/npm/registry/ always-auth=true
Project-scoped feed:
registry=https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/registry/ always-auth=true
Set up credentials
Copy the following snippet into your user-level
.npmrc
file, (Example: C:\Users\FabrikamUser.npmrc). Make sure you don't paste it into the .npmrc file within your source repository.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 token
Project-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 & write scopes.
Run the following command to encode your newly generated personal access token. When prompted, paste your personal access token and then copy the resulting Base64 encoded value.
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(); })"
Open your
.npmrc
file and replace the placeholder[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
with your encoded personal access token you just created.
Scope setup
In your .npmrc file, replace registry=<YOUR_SOURCE_URL>
with @ScopeName:registry=<YOUR_SOURCE_URL>
.
Make sure to include both the scope and package names in your package.json file as follows: { "name": "@ScopeName/PackageName" }
. See the examples below:
Organization-scoped feed:
@ScopeName:registry=https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/registry/ always-auth=true
{ "name": "@ScopeName/PackageName" }
Project-scoped feed:
@ScopeName:registry=https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/registry/ always-auth=true
{ "name": "@ScopeName/PackageName" }
Example:
@local:registry=https://pkgs.dev.azure.com/FabrikamOrg/NpmDemo/_packaging/FabrikamFeed/npm/registry/ always-auth=true
{ "name": "@demo/js-e2e-express-server", "version": "2.0.0", "description": "JavaScript server written with Express.js", "main": "index.js", "directories": { "doc": "docs", "test": "test" }
Publish scoped packages
Open a command prompt window, navigate to your project directory, and run the following command to publish your scoped package. In our example, our package is listed under the @local view.
npm publish
Upstream sources vs scopes
Upstream sources give you the most flexibility to use a combination of scoped and nonscoped packages in your feed, as well as both scoped and nonscoped packages from public registries like npmjs.com.
Scopes, however, impose a naming restriction on 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 file. With that in mind, scopes can serve as a viable alternative to upstream sources.