From af9aa49b748f56bf81f5bfc711723fdb4ea50e7a Mon Sep 17 00:00:00 2001 From: Sonali Deshpande <48232592+sonalideshpandemsft@users.noreply.github.com> Date: Sun, 21 Jul 2024 19:31:56 -0700 Subject: [PATCH 1/3] Move upload release reports step (#21887) The release reports are currently uploaded at the very beginning of the CI run, which is problematic because build or test failures could occur, causing an incorrect release report to be published to the azure blob. This update relocates the upload step under the publish section, ensuring that reports are only uploaded after a successful build and test run. [Test Run](https://dev.azure.com/fluidframework/internal/_build/results?buildId=280522&view=logs&j=c719ff29-64ed-5818-044d-14407c6a65d1) [AB#8886](https://dev.azure.com/fluidframework/internal/_workitems/edit/8886) --- .../src/commands/release/report-unreleased.ts | 33 +++++-- tools/pipelines/server-gitrest.yml | 2 +- tools/pipelines/server-historian.yml | 1 - tools/pipelines/server-routerlicious.yml | 1 - .../pipelines/templates/build-npm-package.yml | 13 +-- .../templates/include-install-build-tools.yml | 70 ++++++++++++++ .../templates/include-publish-npm-package.yml | 11 +++ .../templates/include-set-package-version.yml | 70 +------------- .../templates/upload-dev-manifest.yml | 92 +++++++++++-------- 9 files changed, 167 insertions(+), 126 deletions(-) create mode 100644 tools/pipelines/templates/include-install-build-tools.yml diff --git a/build-tools/packages/build-cli/src/commands/release/report-unreleased.ts b/build-tools/packages/build-cli/src/commands/release/report-unreleased.ts index c0b718000686..bf48b72012ec 100644 --- a/build-tools/packages/build-cli/src/commands/release/report-unreleased.ts +++ b/build-tools/packages/build-cli/src/commands/release/report-unreleased.ts @@ -5,7 +5,6 @@ import * as fs from "node:fs/promises"; import path from "node:path"; -import { isInternalVersionRange, isInternalVersionScheme } from "@fluid-tools/version-tools"; import type { Logger } from "@fluidframework/build-tools"; import { Flags } from "@oclif/core"; import { formatISO } from "date-fns"; @@ -154,19 +153,41 @@ async function updateReportVersions( version: string, log: Logger, ): Promise { + const clientPackageName = "fluid-framework"; + + const packageReleaseDetails = report[clientPackageName]; + + if (packageReleaseDetails === undefined) { + throw new Error(`Client package ${clientPackageName} is not defined in the report.`); + } + + if (packageReleaseDetails.ranges?.caret === undefined) { + throw new Error(`Caret version for ${clientPackageName} is not defined in the report.`); + } + + if (packageReleaseDetails.version === undefined) { + throw new Error(`Simple version for ${clientPackageName} is not defined in the report.`); + } + + const clientVersionCaret = report[clientPackageName].ranges.caret; + const clientVersionSimple = report[clientPackageName].version; + + log.log(`Caret version: ${clientVersionCaret}`); + log.log(`Simple version: ${clientVersionSimple}`); + for (const packageName of Object.keys(report)) { if (ignorePackageList.has(packageName)) { continue; } - // updates caret ranges - if (isInternalVersionRange(report[packageName].ranges.caret, true)) { - // If the caret range is a range, reset it to an exact version. - // Note: Post 2.0 release, the versions will no longer be internal versions so another condition will be required that will work after 2.0. + const packageInfo = report[packageName]; + + // todo: add better checks + if (packageInfo.ranges.caret && packageInfo.ranges.caret === clientVersionCaret) { report[packageName].ranges.caret = version; } - if (isInternalVersionScheme(report[packageName].version)) { + if (packageInfo.version && packageInfo.version === clientVersionSimple) { report[packageName].version = version; } } diff --git a/tools/pipelines/server-gitrest.yml b/tools/pipelines/server-gitrest.yml index 7aa33a030d89..d3daf806f969 100644 --- a/tools/pipelines/server-gitrest.yml +++ b/tools/pipelines/server-gitrest.yml @@ -80,7 +80,7 @@ pr: - tools/pipelines/templates/include-vars.yml - tools/pipelines/templates/include-install-pnpm.yml - tools/pipelines/templates/include-use-node-version.yml - - tools/pipelines/templates/upload-dev-manifest.yml + variables: - template: /tools/pipelines/templates/include-vars.yml@self diff --git a/tools/pipelines/server-historian.yml b/tools/pipelines/server-historian.yml index 9f4d90896b24..52bad8f49bae 100644 --- a/tools/pipelines/server-historian.yml +++ b/tools/pipelines/server-historian.yml @@ -80,7 +80,6 @@ pr: - tools/pipelines/templates/include-vars.yml - tools/pipelines/templates/include-install-pnpm.yml - tools/pipelines/templates/include-use-node-version.yml - - tools/pipelines/templates/upload-dev-manifest.yml variables: - template: /tools/pipelines/templates/include-vars.yml@self diff --git a/tools/pipelines/server-routerlicious.yml b/tools/pipelines/server-routerlicious.yml index 9f244a390021..9e019303915a 100644 --- a/tools/pipelines/server-routerlicious.yml +++ b/tools/pipelines/server-routerlicious.yml @@ -94,7 +94,6 @@ pr: - tools/pipelines/templates/include-vars.yml - tools/pipelines/templates/include-install-pnpm.yml - tools/pipelines/templates/include-use-node-version.yml - - tools/pipelines/templates/upload-dev-manifest.yml exclude: - server/routerlicious/kubernetes/routerlicious diff --git a/tools/pipelines/templates/build-npm-package.yml b/tools/pipelines/templates/build-npm-package.yml index 899bbd05f0eb..1eab86eeff01 100644 --- a/tools/pipelines/templates/build-npm-package.yml +++ b/tools/pipelines/templates/build-npm-package.yml @@ -334,8 +334,6 @@ extends: tagName: '${{ parameters.tagName }}' interdependencyRange: '${{ parameters.interdependencyRange }}' packageTypesOverride: '${{ parameters.packageTypesOverride }}' - STORAGE_ACCOUNT: $(STORAGE_ACCOUNT) - STORAGE_KEY: $(STORAGE_KEY) # Build and Lint - template: /tools/pipelines/templates/include-build-lint.yml@self @@ -582,16 +580,6 @@ extends: sbomEnabled: false publishLocation: pipeline - # This condition should be kept in sync with the one in include-set-package-version, which decides if - # upload-dev-manifest.yml should be included or not. - - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.tagName, 'client'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))) }}: - - output: pipelineArtifact - displayName: Publish Artifact - release_reports - targetPath: $(Build.ArtifactStagingDirectory)/release_reports - artifactName: release_reports - sbomEnabled: false - publishLocation: pipeline - # Job - Component detection - ${{ if eq(parameters.publish, true) }}: - job: CG @@ -617,6 +605,7 @@ extends: parameters: tagName: ${{ parameters.tagName }} isReleaseGroup: ${{ parameters.isReleaseGroup }} + buildDirectory: ${{ parameters.buildDirectory }} # Capture pipeline stage results - ${{ if eq(parameters.telemetry, true) }}: diff --git a/tools/pipelines/templates/include-install-build-tools.yml b/tools/pipelines/templates/include-install-build-tools.yml new file mode 100644 index 000000000000..ef81196a2dc7 --- /dev/null +++ b/tools/pipelines/templates/include-install-build-tools.yml @@ -0,0 +1,70 @@ +# Copyright (c) Microsoft Corporation and contributors. All rights reserved. +# Licensed under the MIT License. + +# include-install-build-tools +# +# This template can be included in pipelines to install the Fluid build-tools locally so flub can be used in the +# pipeline. + +parameters: +- name: buildDirectory + type: string + +- name: buildToolsVersionToInstall + type: string + default: repo + +# The path to the pnpm store. +- name: pnpmStorePath + type: string + default: $(Pipeline.Workspace)/.pnpm-store + +steps: + + # These steps should ONLY run if we're using the repo version of the build tools. These steps are mutually exclusive + # with the next group of steps. + - ${{ if eq(parameters.buildToolsVersionToInstall, 'repo') }}: + - template: include-install-pnpm.yml + parameters: + buildDirectory: $(Build.SourcesDirectory)/build-tools + pnpmStorePath: ${{ parameters.pnpmStorePath }} + enableCache: false + + - task: Bash@3 + name: InstallBuildTools + displayName: Install Fluid Build Tools (from repo) + inputs: + targetType: 'inline' + workingDirectory: $(Build.SourcesDirectory)/build-tools + script: | + pnpm i --frozen-lockfile + pnpm build:compile + cd packages/build-cli + # Use npm link instead of pnpm link because it handles bins better + npm link + echo "which flub: $(which flub)" + + # These steps install a version of build-tools from the npm registry. As noted above, these steps are mutually exclusive + # with the previous group of steps. + - ${{ if ne(parameters.buildToolsVersionToInstall, 'repo') }}: + - task: Bash@3 + name: InstallBuildTools + displayName: Install Fluid Build Tools (from npm) + inputs: + targetType: 'inline' + workingDirectory: ${{ parameters.buildDirectory }} + script: | + echo "${{ parameters.buildToolsVersionToInstall }}" + npm install --global "@fluid-tools/build-cli@${{ parameters.buildToolsVersionToInstall }}" + + - task: Bash@3 + name: BuildToolsInstallCheck + displayName: Check Build Tools Installation + inputs: + targetType: 'inline' + workingDirectory: ${{ parameters.buildDirectory }} + script: | + # Output the help and full command list for debugging purposes + echo "which flub: $(which flub)" + flub --help + flub commands diff --git a/tools/pipelines/templates/include-publish-npm-package.yml b/tools/pipelines/templates/include-publish-npm-package.yml index 0dcc0bfa6007..cd61468f31c4 100644 --- a/tools/pipelines/templates/include-publish-npm-package.yml +++ b/tools/pipelines/templates/include-publish-npm-package.yml @@ -15,6 +15,9 @@ parameters: type: boolean default: false +- name: buildDirectory + type: string + stages: # This stage only runs for test-branch builds @@ -50,6 +53,14 @@ stages: environment: package-build-feed pool: ${{ parameters.pool }} + # Only generate manifest files for runs in the internal project (i.e. CI runs, not PR run), for the main and release branch, and + # for the build of the client release group (we don't need manifests for anything else). + # Enabling this template for every PR run risks overwriting existing manifest files uploaded to Azure blobs. Therefore, it's crucial to restrict this template to commits merged into the main and release branch. + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.tagName, 'client'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))) }}: + - template: /tools/pipelines/templates/upload-dev-manifest.yml@self + parameters: + buildDirectory: '${{ parameters.buildDirectory }}' + # Only add a stage to publish packages to the dev feed for release groups, which have separate lists of packages to # publish to each feed. Non-release-group builds (for an independent package) only need to publish to the build feed. - ${{ if eq(parameters.isReleaseGroup, true) }}: diff --git a/tools/pipelines/templates/include-set-package-version.yml b/tools/pipelines/templates/include-set-package-version.yml index 335d9a87b998..b754e14e70d6 100644 --- a/tools/pipelines/templates/include-set-package-version.yml +++ b/tools/pipelines/templates/include-set-package-version.yml @@ -32,64 +32,14 @@ parameters: type: string default: none -- name: STORAGE_ACCOUNT - type: string - default: none - -- name: STORAGE_KEY - type: string - default: none - # Set version steps: -# These steps should ONLY run if we're using the repo version of the build tools. These steps are mutually exclusive -# with the next group of steps. -- ${{ if eq(parameters.buildToolsVersionToInstall, 'repo') }}: - - template: include-install-pnpm.yml - parameters: - buildDirectory: $(Build.SourcesDirectory)/build-tools - pnpmStorePath: ${{ parameters.pnpmStorePath }} - enableCache: false - - - task: Bash@3 - name: InstallBuildTools - displayName: Install Fluid Build Tools (from repo) - inputs: - targetType: 'inline' - workingDirectory: $(Build.SourcesDirectory)/build-tools - script: | - pnpm i --frozen-lockfile - pnpm build:compile - cd packages/build-cli - # Use npm link instead of pnpm link because it handles bins better - npm link - echo "which flub: $(which flub)" - -# These steps install a version of build-tools from the npm registry. As noted above, these steps are mutually exclusive -# with the previous group of steps. -- ${{ if ne(parameters.buildToolsVersionToInstall, 'repo') }}: - - task: Bash@3 - name: InstallBuildTools - displayName: Install Fluid Build Tools (from npm) - inputs: - targetType: 'inline' - workingDirectory: ${{ parameters.buildDirectory }} - script: | - echo "${{ parameters.buildToolsVersionToInstall }}" - npm install --global "@fluid-tools/build-cli@${{ parameters.buildToolsVersionToInstall }}" - -- task: Bash@3 - name: BuildToolsInstallCheck - displayName: Check Build Tools Installation - inputs: - targetType: 'inline' - workingDirectory: ${{ parameters.buildDirectory }} - script: | - # Output the help and full command list for debugging purposes - echo "which flub: $(which flub)" - flub --help - flub commands +- template: include-install-build-tools.yml + parameters: + buildDirectory: ${{ parameters.buildDirectory }} + buildToolsVersionToInstall: ${{ parameters.buildToolsVersionToInstall }} + pnpmStorePath: ${{ parameters.pnpmStorePath }} - task: Bash@3 name: SetVersion @@ -142,16 +92,6 @@ steps: workingDirectory: ${{ parameters.buildDirectory }} filePath: $(Build.SourcesDirectory)/scripts/update-package-version.sh -# Only generate manifest files for runs in the internal project (i.e. CI runs, not PR run), for the main and release branch, and -# for the build of the client release group (we don't need manifests for anything else). -# Enabling this template for every PR run risks overwriting existing manifest files uploaded to Azure blobs. Therefore, it's crucial to restrict this template to commits merged into the main and release branch. -- ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.tagName, 'client'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))) }}: - - template: /tools/pipelines/templates/upload-dev-manifest.yml - parameters: - buildDirectory: '${{ parameters.buildDirectory }}' - STORAGE_ACCOUNT: '${{ parameters.STORAGE_ACCOUNT }}' - STORAGE_KEY: '${{ parameters.STORAGE_KEY }}' - # This task is a last-minute verification that no Fluid internal versions show up with caret dependencies. This is to # help find and prevent bugs in the version bumping tools. - task: Bash@3 diff --git a/tools/pipelines/templates/upload-dev-manifest.yml b/tools/pipelines/templates/upload-dev-manifest.yml index 6112ddca532a..5366fd1ad849 100644 --- a/tools/pipelines/templates/upload-dev-manifest.yml +++ b/tools/pipelines/templates/upload-dev-manifest.yml @@ -5,48 +5,60 @@ parameters: - name: buildDirectory type: string -- name: STORAGE_ACCOUNT - type: string +jobs: + - job: + displayName: Upload Release Reports + variables: + - group: storage-vars + - name: version + value: $[stageDependencies.build.build.outputs['SetVersion.version']] + steps: + - template: include-install-build-tools.yml + parameters: + buildDirectory: ${{ parameters.buildDirectory }} -- name: STORAGE_KEY - type: string + - task: Bash@3 + displayName: Generate release reports + inputs: + targetType: 'inline' + workingDirectory: ${{ parameters.buildDirectory }} + script: | + mkdir generate_release_reports + flub release report -g client -o generate_release_reports --baseFileName manifest -steps: -- task: Bash@3 - displayName: Generate release reports - inputs: - targetType: 'inline' - workingDirectory: ${{ parameters.buildDirectory }} - script: | - mkdir generate_release_reports - flub release report -g client -o generate_release_reports --baseFileName manifest + - task: Bash@3 + displayName: Update release report version + inputs: + targetType: 'inline' + workingDirectory: ${{ parameters.buildDirectory }} + script: | + mkdir upload_release_reports + flub release report-unreleased --version $(version) --fullReportFilePath generate_release_reports/manifest.full.json --outDir upload_release_reports --branchName '$(Build.SourceBranch)' -- task: Bash@3 - displayName: Update release report version - inputs: - targetType: 'inline' - workingDirectory: ${{ parameters.buildDirectory }} - script: | - mkdir upload_release_reports - flub release report-unreleased --version $(SetVersion.version) --fullReportFilePath generate_release_reports/manifest.full.json --outDir upload_release_reports --branchName '$(Build.SourceBranch)' + - task: CopyFiles@2 + displayName: Copy release reports + inputs: + SourceFolder: ${{ parameters.buildDirectory }}/upload_release_reports + TargetFolder: $(Build.ArtifactStagingDirectory)/release_reports -- task: CopyFiles@2 - displayName: Copy release reports - inputs: - SourceFolder: ${{ parameters.buildDirectory }}/upload_release_reports - TargetFolder: $(Build.ArtifactStagingDirectory)/release_reports + - task: AzureCLI@2 + displayName: Upload release reports + continueOnError: true + inputs: + azureSubscription: 'fluid-docs' + scriptType: bash + workingDirectory: ${{ parameters.buildDirectory }} + scriptLocation: inlineScript + inlineScript: | + for file in upload_release_reports/*; do + az storage blob upload -f "$file" -c 'manifest-files' -n "$(basename "$file")" --account-name $(STORAGE_ACCOUNT) --account-key $(STORAGE_KEY) --overwrite true --verbose + done + # Delete generate_release_reports and upload_release_reports folder + rm -r generate_release_reports upload_release_reports -- task: AzureCLI@2 - displayName: Upload release reports - continueOnError: true - inputs: - azureSubscription: 'fluid-docs' - scriptType: bash - workingDirectory: ${{ parameters.buildDirectory }} - scriptLocation: inlineScript - inlineScript: | - for file in upload_release_reports/*; do - az storage blob upload -f "$file" -c 'manifest-files' -n "$(basename "$file")" --account-name ${{ parameters.STORAGE_ACCOUNT }} --account-key ${{ parameters.STORAGE_KEY }} --overwrite true --verbose - done - # Delete generate_release_reports and upload_release_reports folder - rm -r generate_release_reports upload_release_reports + - task: 1ES.PublishPipelineArtifact@1 + displayName: Publish Artifact - release_reports + inputs: + targetPath: $(Build.ArtifactStagingDirectory)/release_reports + artifactName: release_reports + publishLocation: pipeline From a40f31337b87d26211d9f8e2e154e4b69556a051 Mon Sep 17 00:00:00 2001 From: Sonali Deshpande <48232592+sonalideshpandemsft@users.noreply.github.com> Date: Mon, 22 Jul 2024 09:03:28 -0700 Subject: [PATCH 2/3] Fix server pipeline (#21969) The server pipeline started to fail due to this commit: https://github.com/microsoft/FluidFramework/pull/21887. The follow-up should fix the server pipeline. [Test Run](https://dev.azure.com/fluidframework/internal/_build/results?buildId=281718&view=results) --- tools/pipelines/templates/build-docker-service.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/pipelines/templates/build-docker-service.yml b/tools/pipelines/templates/build-docker-service.yml index c96a50099eb9..6ffdeee4d3a9 100644 --- a/tools/pipelines/templates/build-docker-service.yml +++ b/tools/pipelines/templates/build-docker-service.yml @@ -548,6 +548,7 @@ extends: parameters: tagName: ${{ parameters.tagName }} isReleaseGroup: ${{ parameters.isReleaseGroup }} + buildDirectory: ${{ parameters.buildDirectory }} - ${{ if eq(parameters.shouldReleaseDockerImage, true) }}: - template: /tools/pipelines/templates/include-publish-docker-service.yml@self From 2d29185d8bdb161c5b1f107c07260a0bfec43a82 Mon Sep 17 00:00:00 2001 From: Sonali Deshpande <48232592+sonalideshpandemsft@users.noreply.github.com> Date: Wed, 7 Aug 2024 20:04:50 -0700 Subject: [PATCH 3/3] Generate release report for test branches (#22125) With the merge of https://github.com/microsoft/FluidFramework/pull/22030, test branches now pass the client pipeline successfully. As a result, release reports can also be generated for test versions published to the test feed. [Test Run](https://dev.azure.com/fluidframework/internal/_build/results?buildId=285541&view=results) [AB#8042](https://dev.azure.com/fluidframework/internal/_workitems/edit/8042) --- .../src/commands/release/report-unreleased.ts | 8 ++- .../api-report/version-tools.api.md | 3 + .../packages/version-tools/src/index.ts | 1 + .../src/internalVersionScheme.ts | 41 +++++++++++++ .../src/test/internalVersionScheme.test.ts | 57 +++++++++++++++++++ .../templates/include-publish-npm-package.yml | 12 +++- 6 files changed, 119 insertions(+), 3 deletions(-) diff --git a/build-tools/packages/build-cli/src/commands/release/report-unreleased.ts b/build-tools/packages/build-cli/src/commands/release/report-unreleased.ts index bf48b72012ec..c569e4b6d04e 100644 --- a/build-tools/packages/build-cli/src/commands/release/report-unreleased.ts +++ b/build-tools/packages/build-cli/src/commands/release/report-unreleased.ts @@ -5,6 +5,7 @@ import * as fs from "node:fs/promises"; import path from "node:path"; +import { isInternalTestVersion } from "@fluid-tools/version-tools"; import type { Logger } from "@fluidframework/build-tools"; import { Flags } from "@oclif/core"; import { formatISO } from "date-fns"; @@ -206,7 +207,12 @@ async function updateReportVersions( */ function extractBuildNumber(version: string): number { - const versionParts: string[] = version.split("."); + const versionParts: string[] = version.split("-"); + + if (isInternalTestVersion(version)) { + return Number.parseInt(versionParts[1], 10); + } + // Extract the last part of the version, which is the number you're looking for return Number.parseInt(versionParts[versionParts.length - 1], 10); } diff --git a/build-tools/packages/version-tools/api-report/version-tools.api.md b/build-tools/packages/version-tools/api-report/version-tools.api.md index 07b23b698253..268cdd6f2014 100644 --- a/build-tools/packages/version-tools/api-report/version-tools.api.md +++ b/build-tools/packages/version-tools/api-report/version-tools.api.md @@ -60,6 +60,9 @@ export type InterdependencyRange = WorkspaceRange | RangeOperator | RangeOperato // @public export function isInterdependencyRange(r: unknown): r is InterdependencyRange; +// @public +export function isInternalTestVersion(version: semver.SemVer | string): boolean; + // @public export function isInternalVersionRange(range: string, allowAnyPrereleaseId?: boolean): boolean; diff --git a/build-tools/packages/version-tools/src/index.ts b/build-tools/packages/version-tools/src/index.ts index bd089025e5a3..35fdc0fbb3fb 100644 --- a/build-tools/packages/version-tools/src/index.ts +++ b/build-tools/packages/version-tools/src/index.ts @@ -27,6 +27,7 @@ export { changePreReleaseIdentifier, getVersionRange, fromInternalScheme, + isInternalTestVersion, isInternalVersionRange, isInternalVersionScheme, toInternalScheme, diff --git a/build-tools/packages/version-tools/src/internalVersionScheme.ts b/build-tools/packages/version-tools/src/internalVersionScheme.ts index c0a3336c0169..875f9012e038 100644 --- a/build-tools/packages/version-tools/src/internalVersionScheme.ts +++ b/build-tools/packages/version-tools/src/internalVersionScheme.ts @@ -490,3 +490,44 @@ export function detectInternalVersionConstraintType( const maxSatisfying = semver.maxSatisfying([patch, minor], range); return maxSatisfying === patch ? "patch" : maxSatisfying === minor ? "minor" : "exact"; } + +/** + * Checks if the provided version is a test version. + * + * Test versions are generated from test/ branches and are published to the test feed. + * + * @param version - The version to check + * @returns - True if the version string is a test version, otherwise false + * + * @example + * returns true + * isInternalTestVersion("0.0.0-260312-test"); + * + * @example + * returns false + * isInternalTestVersion("2.1.0-260312"); + * + * @throws error - If the version string cannot be parsed as a valid semantic version. + */ +export function isInternalTestVersion(version: semver.SemVer | string): boolean { + const parsedVersion = semver.parse(version); + + if (parsedVersion === null) { + throw new Error(`Couldn't parse ${version} as a semver.`); + } + + if ( + parsedVersion.prerelease.length === 0 || + typeof parsedVersion.prerelease[0] !== "string" + ) { + return false; + } + + const isTestVersion = + parsedVersion.minor === 0 && + parsedVersion.major === 0 && + parsedVersion.patch === 0 && + parsedVersion.prerelease[0].endsWith("-test"); + + return isTestVersion; +} diff --git a/build-tools/packages/version-tools/src/test/internalVersionScheme.test.ts b/build-tools/packages/version-tools/src/test/internalVersionScheme.test.ts index 96c677c4e046..ed8ff122ffa6 100644 --- a/build-tools/packages/version-tools/src/test/internalVersionScheme.test.ts +++ b/build-tools/packages/version-tools/src/test/internalVersionScheme.test.ts @@ -10,6 +10,7 @@ import { detectInternalVersionConstraintType, fromInternalScheme, getVersionRange, + isInternalTestVersion, isInternalVersionRange, isInternalVersionScheme, toInternalScheme, @@ -387,4 +388,60 @@ describe("internalScheme", () => { ); }); }); + + describe("checking test version scheme", () => { + it("0.0.0-285010-test is a valid test version", () => { + const input = `0.0.0-285010-test`; + const result = isInternalTestVersion(input); + assert.isTrue(result); + }); + + it("0.0.0-test-285010 is not a valid test version", () => { + const input = `0.0.0-test-285010`; + const result = isInternalTestVersion(input); + assert.isFalse(result); + }); + + it("2.1.0-test-285010 is not a valid test version", () => { + const input = `2.1.0-test-285010`; + const result = isInternalTestVersion(input); + assert.isFalse(result); + }); + + it("2.1.0-285010-test is not a valid test version", () => { + const input = `2.1.0-285010-test`; + const result = isInternalTestVersion(input); + assert.isFalse(result); + }); + + it("2.1.0-285010 is a prerelease version but not a test version", () => { + const input = `2.1.0-285010`; + const result = isInternalTestVersion(input); + assert.isFalse(result); + }); + + it("2.1.0 is a release version but not a test version", () => { + const input = `2.1.0`; + const result = isInternalTestVersion(input); + assert.isFalse(result); + }); + + it("2.0.0-internal.1.0.0 is internal scheme but not a test version", () => { + const input = `2.0.0-internal.1.0.0`; + const result = isInternalTestVersion(input); + assert.isFalse(result); + }); + + it("2.0.0-rc.1.0.0 is internal scheme but not a test version", () => { + const input = `2.0.0-rc.1.0.0`; + const result = isInternalTestVersion(input); + assert.isFalse(result); + }); + + it("2.0.0-dev.1.1.0.123 is an internal dev version but not a test version", () => { + const input = `2.0.0-dev.1.1.0.123`; + const result = isInternalTestVersion(input); + assert.isFalse(result); + }); + }); }); diff --git a/tools/pipelines/templates/include-publish-npm-package.yml b/tools/pipelines/templates/include-publish-npm-package.yml index cd61468f31c4..c046259fc18d 100644 --- a/tools/pipelines/templates/include-publish-npm-package.yml +++ b/tools/pipelines/templates/include-publish-npm-package.yml @@ -35,6 +35,14 @@ stages: environment: test-package-build-feed pool: ${{ parameters.pool }} + # Only generate manifest files for runs in the internal project (i.e. CI runs, not PR run), for the main, test and release branch, and + # for the build of the client release group (we don't need manifests for anything else). + # Enabling this template for every PR run risks overwriting existing manifest files uploaded to Azure blobs. Therefore, it's crucial to restrict this template to commits merged into the main and release branch. + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.tagName, 'client'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/test/'))) }}: + - template: /tools/pipelines/templates/upload-dev-manifest.yml@self + parameters: + buildDirectory: '${{ parameters.buildDirectory }}' + # This stage and the following stage both run for non-test builds. The build feed is equivalent to the public npmjs.org # feed except it contains builds for every main- or next-branch build. - stage: publish_npm_internal_build @@ -53,10 +61,10 @@ stages: environment: package-build-feed pool: ${{ parameters.pool }} - # Only generate manifest files for runs in the internal project (i.e. CI runs, not PR run), for the main and release branch, and + # Only generate manifest files for runs in the internal project (i.e. CI runs, not PR run), for the main, test and release branch, and # for the build of the client release group (we don't need manifests for anything else). # Enabling this template for every PR run risks overwriting existing manifest files uploaded to Azure blobs. Therefore, it's crucial to restrict this template to commits merged into the main and release branch. - - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.tagName, 'client'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))) }}: + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.tagName, 'client'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/test/'))) }}: - template: /tools/pipelines/templates/upload-dev-manifest.yml@self parameters: buildDirectory: '${{ parameters.buildDirectory }}'