Cache NodeJS tool in DevOps

1.4k Views Asked by At

Is it possible to use Cache with the NodeTool task on order to skip downloading a specific version of tools and use a version shared across multiple pipelines? Right now NodeTool task occasionally fails to download the tool which causes intermittent errors. I'm a little unclear on how the Cache task works to allow another task to conditionally run the first time, and also where/how to get the cache to restore the Node JS tool to the appropriate path consistent with what NodeTool would do. This is a pipeline hosted in DevOps.

Note I'm not asking about catching NPM packages. Just a version of NodeJS tool supplied by NodeTool task.

2

There are 2 best solutions below

0
jessehouwing On

You can't use the Cache task, because it will only cache things within the working directory of the job.

But it's relatively easy to re-populate the hosted tools cache at the start of the job.

The agent keeps a toolcache folder under which each tools installer puts the contents and then updates the path. The path to the tools cache is stored in a predefined variable: Agent.ToolsDirectory.

This folder uses a predefined structure:

{cache root}
    {tool name}
        {semantic version}
            {platform}
                 {tool files}

In the case of Node 16.19.1 this ends up here: $(Agent.ToolsDirectory)/node/16.19.1/x64

If you zip up the folder $(Agent.ToolsDirectory)/node/16.19.1/ and store it somewhere (Azure DevOps Universal Artifacts comes to mind), you can restore the folder at the start of the job. The UseNode task will detect the presence of the restored files and it will automatically use it.

Note: In case of a linux or mac agent, make sure you restore the executable bits

You can run a pipeline once to create the zip and upload it to universal artifacts. You can then use this artefact from any other workflow that needs to restore this specific tool.

Note: The thing I can't understand is how the pipeline would default to a different Node version. The UseNode task is setup to fail the pipeline when it can't give you the version of node you want.

Something like:

name: Backup Node

pool:
  vmImage: ubuntu-latest

steps:
- task: UseNode@1
  inputs:
    version: 14.x

- script: |
    nodeversion=$(node --version)
    echo "##vso[task.setvariable variable=nodeversion]${nodeversion:1}"

- task: ArchiveFiles@2
  inputs:
    rootFolderOrFile: '$(Agent.ToolsDirectory)/node/$(nodeversion)/'
    includeRootFolder: true
    archiveType: 'zip'
    archiveFile: '$(Build.ArtifactStagingDirectory)/node.zip)'
    replaceExistingArchive: true

- task: UniversalPackages@0
  inputs:
    command: 'publish'
    publishDirectory: '$(Build.ArtifactStagingDirectory)/node.zip)'
    feedsToUsePublish: 'internal'
    vstsFeedPublish: '6484ebc3-af16-4af9-aa66-6b3398db7214/72f9ef5d-b5b8-445b-87b9-90b7a3fa4305'
    vstsFeedPackagePublish: 'node'
    versionOption: 'custom'
    versionPublish: '$(nodeversion)'

And then to restore:

- task: UniversalPackages@0
  inputs:
    command: 'download'
    downloadDirectory: '$(Agent.ToolsDirectory)/node/'
    feedsToUse: 'internal'
    vstsFeed: '6484ebc3-af16-4af9-aa66-6b3398db7214/72f9ef5d-b5b8-445b-87b9-90b7a3fa4305'
    vstsFeedPackage: 'node'
    vstsPackageVersion: '14.*'

- script: |
    # Fix the permissions on node and npm etc.
    chmod +x ...

- task: UseNode@1
  inputs:
    version: 14.x

Make sure you grant the Project Collection Build Service contributor permissions on the package feed.

0
Markus Schulte On

We are using Cache@2 for caching the Node.js runtime.

We are using Node Version Manager

$ cat .nvmrc
18.18.0

Based on this, the caching:

steps:
  - powershell: Write-Host "##vso[task.setvariable variable=NODEJS_RUNTIME_DIRECTORY]$(Agent.ToolsDirectory)/node/$(cat .nvmrc)"
    displayName: Set Node.js runtime directory variable
  - task: Cache@2
    inputs:
      key: nodejs | $(Agent.OS) | .nvmrc
      path: $(NODEJS_RUNTIME_DIRECTORY)
      cacheHitVar: NODEJS_RUNTIME_CACHE_HIT
    displayName: Cache Node.js runtime
  - task: NodeTool@0
    condition: and(succeeded(), ne(variables.NODEJS_RUNTIME_CACHE_HIT, 'true'))
    inputs:
      versionSource: fromFile
      versionFilePath: .nvmrc
    displayName: Install Node.js
  - powershell: |
      Write-Host '##vso[task.prependpath]$(NODEJS_RUNTIME_DIRECTORY)/x64/bin'
      Write-Host "Prepending PATH environment variable with directory: $(NODEJS_RUNTIME_DIRECTORY)/x64/bin"
    condition: and(succeeded(), ne(variables.NODEJS_RUNTIME_CACHE_HIT, 'false'))
    displayName: Prepend Node.js runtime to PATH
  - powershell: |
      Write-Host "##[debug] Node.js version: $(node -v)"
      Write-Host "##[debug] NPM version: $(npm -v)"
      Write-Host "##[debug] `$NODEJS_RUNTIME_DIRECTORY: $(NODEJS_RUNTIME_DIRECTORY)"
    displayName: Print Node.js version