diff --git a/tools/pipelines/deploy-website.yml b/tools/pipelines/deploy-website.yml index 3551f072562..2158fbd1a5c 100644 --- a/tools/pipelines/deploy-website.yml +++ b/tools/pipelines/deploy-website.yml @@ -130,22 +130,10 @@ stages: - template: /tools/pipelines/templates/include-use-node-version.yml@self - - template: /tools/pipelines/templates/include-install-pnpm.yml@self + - template: /tools/pipelines/templates/include-install.yml@self parameters: buildDirectory: ${{ variables.FluidFrameworkDirectory }}/docs - - task: Bash@3 - displayName: Install dependencies - retryCountOnTaskFailure: 4 - inputs: - targetType: 'inline' - workingDirectory: $(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }}/docs - script: | - set -eu -o pipefail - # Ensure it's easy to tell which workspace this is running in by inspecting the logs - pwd - pnpm i --frozen-lockfile - - task: Npm@1 displayName: npm run build inputs: @@ -269,22 +257,10 @@ stages: - template: /tools/pipelines/templates/include-use-node-version.yml@self - - template: /tools/pipelines/templates/include-install-pnpm.yml@self + - template: /tools/pipelines/templates/include-install.yml@self parameters: buildDirectory: ${{ variables.FluidFrameworkDirectory }}/docs - - task: Bash@3 - displayName: Install dependencies - retryCountOnTaskFailure: 4 - inputs: - targetType: 'inline' - workingDirectory: $(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }}/docs - script: | - set -eu -o pipefail - # Ensure it's easy to tell which workspace this is running in by inspecting the logs - pwd - pnpm i --frozen-lockfile - - task: Npm@1 displayName: Build inputs: diff --git a/tools/pipelines/server-gitrest.yml b/tools/pipelines/server-gitrest.yml index e5b44c3d264..f1836d77bca 100644 --- a/tools/pipelines/server-gitrest.yml +++ b/tools/pipelines/server-gitrest.yml @@ -102,7 +102,7 @@ extends: dockerBuildBumpsVersion: true # Root-only install: gitrest has native deps that don't install in CI; the real build runs in Docker. # Root deps are still needed for the version-setting step. - packageManagerInstallCommand: 'pnpm i --frozen-lockfile --workspace-root' + installWorkspaceRootOnly: true # The --build-context is needed because the Dockerfile copies files from the repo root. additionalBuildArguments: --build-context root=$(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }} buildDirectory: ${{ variables.FluidFrameworkDirectory }}/server/gitrest diff --git a/tools/pipelines/server-gitssh.yml b/tools/pipelines/server-gitssh.yml index 7c388fe7093..c141296efa9 100644 --- a/tools/pipelines/server-gitssh.yml +++ b/tools/pipelines/server-gitssh.yml @@ -84,6 +84,6 @@ extends: containerName: fluidframework/routerlicious/gitssh setVersion: false # No package.json; skip package-manager setup. - packageManager: none + skipPackageInstall: true enableDockerImagePull: false tagName: gitssh diff --git a/tools/pipelines/server-historian.yml b/tools/pipelines/server-historian.yml index b72d1c1638f..cf4e6eca8e5 100644 --- a/tools/pipelines/server-historian.yml +++ b/tools/pipelines/server-historian.yml @@ -103,7 +103,7 @@ extends: dockerBuildBumpsVersion: true # Root-only install: historian has native deps that don't install in CI; the real build runs in Docker. # Root deps are still needed for the version-setting step. - packageManagerInstallCommand: 'pnpm i --frozen-lockfile --workspace-root' + installWorkspaceRootOnly: true # The --build-context is needed because the Dockerfile copies files from the repo root. additionalBuildArguments: --build-context root=$(Pipeline.Workspace)/${{ variables.FluidFrameworkDirectory }} test: test diff --git a/tools/pipelines/server-routerlicious.yml b/tools/pipelines/server-routerlicious.yml index 5162d853f65..958f7c01ad1 100644 --- a/tools/pipelines/server-routerlicious.yml +++ b/tools/pipelines/server-routerlicious.yml @@ -123,7 +123,7 @@ extends: dockerBuildBumpsVersion: true # Root-only install: r11s has native deps that don't install in CI; the real build runs in Docker. # Root deps are still needed for the version-setting step. - packageManagerInstallCommand: 'pnpm i --frozen-lockfile --workspace-root' + installWorkspaceRootOnly: true tagName: server isReleaseGroup: true pool: Large-eastus2 diff --git a/tools/pipelines/templates/build-docker-service.yml b/tools/pipelines/templates/build-docker-service.yml index dc441c819ab..6326865afbe 100644 --- a/tools/pipelines/templates/build-docker-service.yml +++ b/tools/pipelines/templates/build-docker-service.yml @@ -65,17 +65,15 @@ parameters: type: string default: repo - # The package manager (if any) used by this pipeline. - - name: packageManager - type: string - default: pnpm - values: - - pnpm - - none + # If true, skip package-manager setup/install entirely (used by packages without package.json, e.g. gitssh). + - name: skipPackageInstall + type: boolean + default: false - - name: packageManagerInstallCommand - type: string - default: 'pnpm i --frozen-lockfile' + # If true, install only root dependencies for version-setting scenarios in packages with native deps. + - name: installWorkspaceRootOnly + type: boolean + default: false # The semver range constraint to use for interdependencies; that is, dependencies on other packages within the release # group @@ -239,8 +237,8 @@ extends: containerBaseDir=${{ parameters.containerBaseDir }} interdependencyRange='${{ parameters.interdependencyRange }}' isReleaseGroup=${{ parameters.isReleaseGroup }} - packageManager=${{ parameters.packageManager }} - packageManagerInstallCommand=${{ parameters.packageManagerInstallCommand }} + skipPackageInstall=${{ parameters.skipPackageInstall }} + installWorkspaceRootOnly=${{ parameters.installWorkspaceRootOnly }} releaseKind=${{ parameters.releaseKind }} setVersion=${{ parameters.setVersion }} shouldPublishNpmPackages=${{ parameters.shouldPublishNpmPackages }} @@ -301,8 +299,8 @@ extends: fi fi - if [[ "${{ parameters.packageManager }}" == "none" ]] && [[ "${{ parameters.setVersion }}" == "True" ]]; then - echo "##vso[task.logissue type=error]packageManager: 'none' is incompatible with setVersion: true (version-setting transitively requires a package manager)." + if [[ "${{ parameters.skipPackageInstall }}" == "true" ]] && [[ "${{ parameters.setVersion }}" == "True" ]]; then + echo "##vso[task.logissue type=error]skipPackageInstall: true is incompatible with setVersion: true (version-setting requires dependencies to be installed)." exit -1; fi @@ -316,22 +314,27 @@ extends: buildDirectory: ${{ parameters.buildDirectory }} requireNotice: ${{ parameters.shouldReleaseDockerImage }} - - ${{ if eq(parameters.packageManager, 'pnpm') }}: + - ${{ if ne(parameters.skipPackageInstall, true) }}: - template: /tools/pipelines/templates/include-install-pnpm.yml@self parameters: buildDirectory: ${{ parameters.buildDirectory }} # Set version - - ${{ if eq(parameters.setVersion, true) }}: + - ${{ if and(eq(parameters.setVersion, true), ne(parameters.skipPackageInstall, true)) }}: - task: Bash@3 displayName: Install dependencies retryCountOnTaskFailure: 4 inputs: targetType: 'inline' workingDirectory: $(Pipeline.Workspace)/${{ parameters.buildDirectory }} - script: | - set -eu -o pipefail - ${{ parameters.packageManagerInstallCommand }} + ${{ if eq(parameters.installWorkspaceRootOnly, true) }}: + script: | + set -eu -o pipefail + pnpm i --frozen-lockfile --workspace-root + ${{ else }}: + script: | + set -eu -o pipefail + pnpm i --frozen-lockfile - template: /tools/pipelines/templates/include-set-package-version.yml@self parameters: @@ -402,7 +405,7 @@ extends: # as a BuildKit secret so the Dockerfile's npm/pnpm steps can install from our # ADO npm mirror. Only emit --secret for pnpm release groups; non-pnpm # Dockerfiles don't consume it. (gitssh is already excluded one level up.) - ${{ if eq(parameters.packageManager, 'pnpm') }}: + ${{ if ne(parameters.skipPackageInstall, true) }}: buildArguments: --target base $(DockerBuildArgs.output) --secret id=npmrc,src=$(NPM_CONFIG_USERCONFIG) ${{ else }}: buildArguments: --target base $(DockerBuildArgs.output) @@ -548,7 +551,7 @@ extends: image: $(containerTag) # See the base-image build above for the rationale. Passed here too because # a cache miss could re-execute the base stage layers during this build. - ${{ if eq(parameters.packageManager, 'pnpm') }}: + ${{ if ne(parameters.skipPackageInstall, true) }}: buildArguments: $(DockerBuildArgs.output) --secret id=npmrc,src=$(NPM_CONFIG_USERCONFIG) ${{ else }}: buildArguments: $(DockerBuildArgs.output) @@ -588,7 +591,7 @@ extends: localImage: $(containerTag) remoteImage: $(ComputeFinalTagList.FinalTagList) - - ${{ if eq(parameters.packageManager, 'pnpm') }}: + - ${{ if ne(parameters.skipPackageInstall, true) }}: # Prune the pnpm store before it's cached. This removes any deps that are not used by the current build. - task: Bash@3 displayName: Prune pnpm store diff --git a/tools/pipelines/templates/build-npm-client-package.yml b/tools/pipelines/templates/build-npm-client-package.yml index 4bbbde15eb1..7266046a8d3 100644 --- a/tools/pipelines/templates/build-npm-client-package.yml +++ b/tools/pipelines/templates/build-npm-client-package.yml @@ -1,7 +1,22 @@ # Copyright (c) Microsoft Corporation and contributors. All rights reserved. # Licensed under the MIT License. -# build-npm-package template to build NPM packages/projects +# build-npm-client-package template +# +# This template exists as a specialized variant of build-npm-package for the +# client release group pipeline (Build - client packages). It was split from +# the generic template because the client pipeline has additional behavior and +# artifact contracts that other npm package pipelines do not require. +# +# Key differences from build-npm-package: +# - Captures and publishes build output archive (build_output_archive) for +# downstream test pipelines. +# - Runs tests in dedicated jobs (coverage + per-task fan-out) rather than only +# inline in the main build job. +# - Builds and publishes the devtools browser extension artifact. +# - Includes client-specific telemetry and artifact shaping used by internal +# CI and follow-on workflows. +# parameters: - name: buildDirectory @@ -37,10 +52,6 @@ parameters: type: boolean default: false -- name: isBundleSizeArtifactsPipeline - type: boolean - default: false - - name: taskPack type: boolean default: true @@ -74,20 +85,12 @@ parameters: type: string default: repo -- name: packageManager - type: string - default: pnpm - # Parameter for modifying the 'types' field in the package.json. # If the value 'none' is provided, the 'types' field in package.json will remain unchanged. - name: packageTypesOverride type: string default: none -- name: packageManagerInstallCommand - type: string - default: 'pnpm i --frozen-lockfile' - # The semver range constraint to use for interdependencies; that is, dependencies on other packages within the release # group - name: interdependencyRange @@ -331,21 +334,16 @@ extends: - template: /tools/pipelines/templates/include-install.yml@self parameters: - packageManager: '${{ parameters.packageManager }}' buildDirectory: '${{ parameters.buildDirectory }}' - packageManagerInstallCommand: '${{ parameters.packageManagerInstallCommand }}' - - # The bundle-size-artifacts pipeline runs a client build but doesn't publish packages, - # so we skip version setting for it. - - ${{ if eq(parameters.isBundleSizeArtifactsPipeline, false) }}: - - template: /tools/pipelines/templates/include-set-package-version.yml@self - parameters: - buildDirectory: '${{ parameters.buildDirectory }}' - buildNumberInPatch: ${{ parameters.buildNumberInPatch }} - buildToolsVersionToInstall: '${{ parameters.buildToolsVersionToInstall }}' - tagName: '${{ parameters.tagName }}' - interdependencyRange: '${{ parameters.interdependencyRange }}' - packageTypesOverride: '${{ parameters.packageTypesOverride }}' + + - template: /tools/pipelines/templates/include-set-package-version.yml@self + parameters: + buildDirectory: '${{ parameters.buildDirectory }}' + buildNumberInPatch: ${{ parameters.buildNumberInPatch }} + buildToolsVersionToInstall: '${{ parameters.buildToolsVersionToInstall }}' + tagName: '${{ parameters.tagName }}' + interdependencyRange: '${{ parameters.interdependencyRange }}' + packageTypesOverride: '${{ parameters.packageTypesOverride }}' # Build and Lint - template: /tools/pipelines/templates/include-build-lint.yml@self @@ -382,7 +380,7 @@ extends: - task: Bash@3 displayName: npm pack env: - PACKAGE_MANAGER: '${{ parameters.packageManager }}' + PACKAGE_MANAGER: 'pnpm' RELEASE_GROUP: '${{ parameters.tagName }}' STAGING_PATH: $(Build.ArtifactStagingDirectory) inputs: @@ -449,29 +447,28 @@ extends: # At this point we want to publish the artifact with the _api-extractor-temp folder, # but as part of 1ES migration that's now part of templateContext.outputs below. - - ${{ if eq(parameters.packageManager, 'pnpm') }}: - # Reset the pnpm-lock.yaml file since it's been modified by the versioning. But for dependency caching we want - # the cache key (which is based on the contents of the lockfile) to be the unmodified file. So we reset the - # lockfile as the last step so that when the dependency cache is uploaded, the cache key matches what it was - # at the beginning of the CI job. - - task: Bash@3 - displayName: Reset lockfile - inputs: - targetType: inline - workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' - script: | - set -eu -o pipefail - git checkout HEAD -- pnpm-lock.yaml - - # Prune the pnpm store before it's cached. This removes any deps that are not used by the current build. - - task: Bash@3 - displayName: Prune pnpm store - inputs: - targetType: inline - workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' - script: | - set -eu -o pipefail - pnpm store prune + # Reset the pnpm-lock.yaml file since it's been modified by the versioning. But for dependency caching we want + # the cache key (which is based on the contents of the lockfile) to be the unmodified file. So we reset the + # lockfile as the last step so that when the dependency cache is uploaded, the cache key matches what it was + # at the beginning of the CI job. + - task: Bash@3 + displayName: Reset lockfile + inputs: + targetType: inline + workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' + script: | + set -eu -o pipefail + git checkout HEAD -- pnpm-lock.yaml + + # Prune the pnpm store before it's cached. This removes any deps that are not used by the current build. + - task: Bash@3 + displayName: Prune pnpm store + inputs: + targetType: inline + workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' + script: | + set -eu -o pipefail + pnpm store prune - task: Bash@3 displayName: Check for extraneous modified files @@ -600,9 +597,7 @@ extends: - template: /tools/pipelines/templates/include-install.yml@self parameters: - packageManager: '${{ parameters.packageManager }}' buildDirectory: '${{ parameters.buildDirectory }}' - packageManagerInstallCommand: '${{ parameters.packageManagerInstallCommand }}' # We need it in order to run flub where the code coverage comparison logic calls for it - template: /tools/pipelines/templates/include-install-build-tools.yml@self @@ -764,9 +759,7 @@ extends: - template: /tools/pipelines/templates/include-install.yml@self parameters: - packageManager: '${{ parameters.packageManager }}' buildDirectory: '${{ parameters.buildDirectory }}' - packageManagerInstallCommand: '${{ parameters.packageManagerInstallCommand }}' - task: DownloadPipelineArtifact@2 inputs: diff --git a/tools/pipelines/templates/build-npm-package.yml b/tools/pipelines/templates/build-npm-package.yml index d8591dbfedd..39f4e2e6e07 100644 --- a/tools/pipelines/templates/build-npm-package.yml +++ b/tools/pipelines/templates/build-npm-package.yml @@ -2,6 +2,8 @@ # Licensed under the MIT License. # build-npm-package template to build NPM packages/projects +# Note that the build-npm-client-package is a modified copy of this template, modified for use in the main client pipeline with modifications like a different testing strategy. +# Other client specific pipelines like build-bundle-size-artifacts still use this template, so this template still needs to support client. parameters: - name: buildDirectory @@ -73,20 +75,12 @@ parameters: type: string default: repo -- name: packageManager - type: string - default: pnpm - # Parameter for modifying the 'types' field in the package.json. # If the value 'none' is provided, the 'types' field in package.json will remain unchanged. - name: packageTypesOverride type: string default: none -- name: packageManagerInstallCommand - type: string - default: 'pnpm i --frozen-lockfile' - # The semver range constraint to use for interdependencies; that is, dependencies on other packages within the release # group - name: interdependencyRange @@ -314,9 +308,7 @@ extends: - template: /tools/pipelines/templates/include-install.yml@self parameters: - packageManager: '${{ parameters.packageManager }}' buildDirectory: '${{ parameters.buildDirectory }}' - packageManagerInstallCommand: '${{ parameters.packageManagerInstallCommand }}' # The bundle-size-artifacts pipeline runs a client build but doesn't publish packages, # so we skip version setting for it. @@ -454,7 +446,7 @@ extends: - task: Bash@3 displayName: npm pack env: - PACKAGE_MANAGER: '${{ parameters.packageManager }}' + PACKAGE_MANAGER: 'pnpm' RELEASE_GROUP: '${{ parameters.tagName }}' STAGING_PATH: $(Build.ArtifactStagingDirectory) inputs: @@ -533,29 +525,28 @@ extends: # At this point we want to publish the artifact with the _api-extractor-temp folder, # but as part of 1ES migration that's now part of templateContext.outputs below. - - ${{ if eq(parameters.packageManager, 'pnpm') }}: - # Reset the pnpm-lock.yaml file since it's been modified by the versioning. But for dependency caching we want - # the cache key (which is based on the contents of the lockfile) to be the unmodified file. So we reset the - # lockfile as the last step so that when the dependency cache is uploaded, the cache key matches what it was - # at the beginning of the CI job. - - task: Bash@3 - displayName: Reset lockfile - inputs: - targetType: inline - workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' - script: | - set -eu -o pipefail - git checkout HEAD -- pnpm-lock.yaml - - # Prune the pnpm store before it's cached. This removes any deps that are not used by the current build. - - task: Bash@3 - displayName: Prune pnpm store - inputs: - targetType: inline - workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' - script: | - set -eu -o pipefail - pnpm store prune + # Reset the pnpm-lock.yaml file since it's been modified by the versioning. But for dependency caching we want + # the cache key (which is based on the contents of the lockfile) to be the unmodified file. So we reset the + # lockfile as the last step so that when the dependency cache is uploaded, the cache key matches what it was + # at the beginning of the CI job. + - task: Bash@3 + displayName: Reset lockfile + inputs: + targetType: inline + workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' + script: | + set -eu -o pipefail + git checkout HEAD -- pnpm-lock.yaml + + # Prune the pnpm store before it's cached. This removes any deps that are not used by the current build. + - task: Bash@3 + displayName: Prune pnpm store + inputs: + targetType: inline + workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' + script: | + set -eu -o pipefail + pnpm store prune - task: Bash@3 displayName: Check for extraneous modified files diff --git a/tools/pipelines/templates/include-install.yml b/tools/pipelines/templates/include-install.yml index cb1ebd07d01..2d16f0c4b5a 100644 --- a/tools/pipelines/templates/include-install.yml +++ b/tools/pipelines/templates/include-install.yml @@ -4,17 +4,9 @@ # include-install template for the install step in client build and test stability pipeline parameters: -- name: packageManager - type: string - default: pnpm - - name: buildDirectory type: string -- name: packageManagerInstallCommand - type: string - default: 'pnpm i --frozen-lockfile' - - name: primaryRegistry type: string default: $(ado-feeds-primary-registry) @@ -24,12 +16,11 @@ parameters: default: $(Agent.TempDirectory)/.npmrc steps: - - ${{ if eq(parameters.packageManager, 'pnpm') }}: - - template: /tools/pipelines/templates/include-install-pnpm.yml@self - parameters: - buildDirectory: ${{ parameters.buildDirectory }} - primaryRegistry: ${{ parameters.primaryRegistry }} - userNpmrcPath: ${{ parameters.userNpmrcPath }} + - template: /tools/pipelines/templates/include-install-pnpm.yml@self + parameters: + buildDirectory: ${{ parameters.buildDirectory }} + primaryRegistry: ${{ parameters.primaryRegistry }} + userNpmrcPath: ${{ parameters.userNpmrcPath }} - task: Bash@3 displayName: Install dependencies @@ -39,4 +30,4 @@ steps: workingDirectory: '$(Pipeline.Workspace)/${{ parameters.buildDirectory }}' script: | set -eu -o pipefail - ${{ parameters.packageManagerInstallCommand }} + pnpm i --frozen-lockfile