npmazure-devopsazure-pipelines.npmrc

How to set the registry URL in a project-level .npmrc file from an environment variable in an Azure Pipeline


I have a project level .npmrc with private npm registry url

registry=https://blabla.pkgs.visualstudio.com/_packaging/BlaBla/npm/registry/  
                        
always-auth=true

and my Azure pipeline has this stage for npm install using their built in task

          - task: Npm@1
            displayName: "NPM Install"
            inputs:
              command: custom
              workingDir: ./project/  #this is the project folder, has package.json, npmrc files
              customCommand: install
              verbose: true

Now this works fine and packages are installed. I think the authentication token is stored in some global .npmrc file

Problem

Due to compliance issues I cannot store the URL hardcoded in the .npmrc since it is committed and scanned and complained by code analysis tools.

What I tried

I tried to somehow set it like an environment variable

Approach 1

I edited my project level npmrc

registry=$REGISTRY 
always-auth=true

and wrote a bash stage before the install

          - bash: |
              export NPM_CONFIG_REGISTRY=https://blabla.pkgs.visualstudio.com/_packaging/BlaBla/npm/registry/
              echo $NPM_CONFIG_REGISTRY  #hardcoded in pipeline for testing
            displayName: "Set NPM Registry"
            workingDir: $(Build.SourcesDirectory)/project/

This didn't work in the pipeline and gave npm ERR! code ERR_INVALID_URL. When I tried this locally also it seems that it adds the registry URL globally because when I tested with npm config get registry it printed the URL I just set both in the project folder level (has project level npmrc) and outside the project folder.

I also tested while the url was hardcoded and npm config get registry printed the correct URL in the project folder and outside it printed https://registry.npmjs.org/. But locally the npm install worked but probably for the wrong reasons since it also gave this

npm WARN invalid config registry="$REGISTRY" set in C:\BlaBla\project\.npmrc
npm WARN invalid config Must be full url with "http://"

So in conclusion I think the project level registry URL was never set even locally but I don't know why the Azure pipeline didn't even come this close. My guess is the NPM task runs in a different terminal/session so it doesn't have an idea of the env variable I just set

Approach 2

Instead of setting an env I edited my install custom command customCommand: install --registry https://blabla.pkgs.visualstudio.com/_packaging/BlaBla/npm/registry/

now also the I got the same npm WARN but instead of the invalid URL errors this time I got

npm ERR! code E401
npm ERR! Unable to authenticate, your authentication token seems to be invalid.

What can I try next to resolve this?

Resources used: https://stackoverflow.com/a/55441931/13583510


Solution

  • How to set the registry URL in a project-level .npmrc file from an environment variable in an Azure Pipeline

    Your guess is correct. When you use export command in bash to set the environment variable, it can only work on current session. And NPM task is working on different session. So it can not read the correct environemnt.

    To meet your requirement, you can directly define the Pipeline Variable with the feed url and set the format: ${REGISTRY} to use environment variable in npmrc file.

    Here is an example:

    .npmrc file:

    registry=${REGISTRY}                        
    always-auth=true
    

    Pipeline Sample:

    variables:
      REGISTRY: 'https://blabla.pkgs.visualstudio.com/_packaging/BlaBla/npm/registry'
    
    steps:
    .... Other steps
    
    - task: Npm@1
      displayName: "NPM Install"
      inputs:
        command: custom
        workingDir: ./project/  #this is the project folder, has package.json, npmrc files
        customCommand: install
        verbose: true
    

    Result: We can see valid NPM registry url in NPM task.

    enter image description here

    On the other hand, I would like to share another easier method to use private feed. If your pipeline and feed are in the same organization, you can directly set the customRegistry and customFeed in NPM task.

    For example:

    - task: Npm@1
      displayName: "NPM Install"
      inputs:
        command: 'custom'
        workingDir: './project/'
        customCommand: 'install'
        customRegistry: 'useFeed'
        customFeed: 'Your Feed Name'
    

    In this case, you don't need to set any additional configurations in project .npmrc file.

    Update:

    When we use Pipeline Environment variable in NPM task to replace the registry value in .npmrc file, it may not automatically set the credentials for the registry and cause 401 error.

    If you run into this issue, you can consider using sed command to replace the registry url before the npm task.

    Here is an example:

    .npmrc file:

    registry=InputREGISTRY          
    always-auth=true
    

    Pipeline sample:

    variables:
      REGISTRY: https://blabla.pkgs.visualstudio.com/_packaging/BlaBla/npm/registry/ 
    
    steps:
    - bash: sed -i 's,registry=InputREGISTRY,registry=$(REGISTRY),g' $(Build.SourcesDirectory)/project/.npmrc 
    - task: Npm@1
      displayName: "NPM Install"
      inputs:
        command: custom
        workingDir: ./project/  
        customCommand: install
        verbose: true