diff --git a/tools/pipelines/build-performance-observability.yml b/tools/pipelines/build-performance-observability.yml index dc3177e44ca8..4d7dd7c272e3 100644 --- a/tools/pipelines/build-performance-observability.yml +++ b/tools/pipelines/build-performance-observability.yml @@ -98,44 +98,9 @@ extends: - template: /tools/pipelines/templates/include-use-node-version.yml@self - - task: Bash@3 - displayName: Configure npm registry - inputs: - targetType: 'inline' - workingDirectory: $(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }} - script: | - set -eu -o pipefail - echo "registry=$(ado-feeds-primary-registry)" > .npmrc - - - task: npmAuthenticate@0 - displayName: Npm authenticate - retryCountOnTaskFailure: 1 - inputs: - workingFile: $(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }}/.npmrc - - - task: Bash@3 - displayName: Install pnpm - inputs: - targetType: 'inline' - workingDirectory: $(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }} - script: | - set -eu -o pipefail - PNPM_VERSION=$(node -e "const p=require('./package.json');console.log(p.packageManager.split('+')[0])") - npm install -g "$PNPM_VERSION" --userconfig "$(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }}/.npmrc" - - - task: Bash@3 - displayName: Configure pnpm - inputs: - targetType: 'inline' - workingDirectory: $(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }} - script: | - set -eu -o pipefail - echo "Using pnpm $(pnpm -v)" - # Point all npm/pnpm commands at the authenticated .npmrc - echo "##vso[task.setvariable variable=NPM_CONFIG_USERCONFIG]$(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }}/.npmrc" - pnpm config set store-dir $(Pipeline.Workspace)/.pnpm-store - pnpm config set -g workspace-concurrency 0 - pnpm config set registry "$(ado-feeds-primary-registry)" + - template: /tools/pipelines/templates/include-install-pnpm.yml@self + parameters: + buildDirectory: ${{ variables.FluidFrameworkDirectory }} - task: Bash@3 displayName: Install root dependencies diff --git a/tools/pipelines/templates/include-install-pnpm.yml b/tools/pipelines/templates/include-install-pnpm.yml index 6207eb48abe4..0b926ec9d422 100644 --- a/tools/pipelines/templates/include-install-pnpm.yml +++ b/tools/pipelines/templates/include-install-pnpm.yml @@ -62,46 +62,57 @@ steps: restoreKeys: | "pnpm-store" | "$(Agent.OS)" +# Seed the userconfig .npmrc with the primary registry. This propagates to subsequent tasks so +# that npmAuthenticate (below) and any later pnpm/npm invocation in the job reuse the same file. - task: Bash@3 - displayName: Install and configure pnpm + displayName: Configure npm registry # The previous task (cache restoration) can timeout, which is classified as canceled, but since it's just cache # restoration, we want to continue even if it timed out. condition: or(succeeded(), canceled()) inputs: targetType: 'inline' workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' - # workspace-concurrency 0 means use use the CPU core count. This is better than the default (4) for larger agents. script: | set -eu -o pipefail echo "Using node $(node --version)" - sudo corepack enable - echo "Using pnpm $(pnpm -v)" - # This ensures all subsequent tasks in this job will use the pnpm configuration set here. echo "##vso[task.setvariable variable=NPM_CONFIG_USERCONFIG]$NPM_CONFIG_USERCONFIG" - echo "Pnpm user config location: $(pnpm config get userconfig)" - pnpm config set store-dir ${{ parameters.pnpmStorePath }} - echo "Pnpm store: ${{ parameters.pnpmStorePath }}" echo "Primary registry: ${NPM_REGISTRY}" - pnpm config set -g workspace-concurrency 0 - pnpm config set registry "${NPM_REGISTRY}" - if [ ${NPM_REGISTRY} == "https://registry.npmjs.org/" ]; then + echo "registry=${NPM_REGISTRY}" > "$NPM_CONFIG_USERCONFIG" + if [ "${NPM_REGISTRY}" == "https://registry.npmjs.org/" ]; then echo "##vso[task.setvariable variable=registryType]public" else echo "##vso[task.setvariable variable=registryType]private" fi env: NPM_REGISTRY: ${{ parameters.primaryRegistry }} - NPM_CONFIG_USERCONFIG: ${{ parameters.userNpmrcPath}} - # We should leverage the primary npm registry to install pnpm as well. However, ADO artifacts feeds do not implement - # the full npm registry API including a route that corepack uses to get package metadata--the version route here: - # https://github.com/nodejs/corepack/blob/bc13d40037d0b1bfd386e260ae741f55505b5c7c/sources/npmRegistryUtils.ts#L32 - # Thus installing pnpm from an ADO feed using corepack is not possible at time of writing. - # COREPACK_NPM_REGISTRY: ${{ parameters.primaryRegistry }} + NPM_CONFIG_USERCONFIG: ${{ parameters.userNpmrcPath }} -# Authenticate to npm feed if required +# Authenticate to the npm feed if required. Runs before pnpm install so that, +# `npm install -g pnpm@` can fetch pnpm itself from the authenticated feed. - task: npmAuthenticate@0 displayName: 'Npm authenticate' condition: and(succeeded(), eq(variables['registryType'], 'private')) retryCountOnTaskFailure: 1 inputs: workingFile: ${{ parameters.userNpmrcPath }} + +# Install and configure pnpm +- task: Bash@3 + displayName: Install pnpm + inputs: + targetType: 'inline' + workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' + script: | + set -eu -o pipefail + # Parse the pnpm version pinned in package.json's "packageManager" field + # (e.g. "pnpm@10.33.0+sha512..." -> "pnpm@10.33.0"). + # This discards the integrity hash suffix, which npm does not provide a practical way to use, + # and trusts the package repository to serve the correct version. + PNPM_VERSION=$(node -e "const p=require('./package.json');console.log(p.packageManager.split('+')[0])") + npm install -g "$PNPM_VERSION" --userconfig "${{ parameters.userNpmrcPath }}" + echo "Using pnpm $(pnpm -v)" + echo "Pnpm user config location: $(pnpm config get userconfig)" + pnpm config set store-dir ${{ parameters.pnpmStorePath }} + echo "Pnpm store: ${{ parameters.pnpmStorePath }}" + # workspace-concurrency 0 means use the CPU core count. This is better than the default (4) for larger agents. + pnpm config set -g workspace-concurrency 0