Skip to content

Commit

Permalink
feat(build-cli): add check latestVersions command to build-tools (mic…
Browse files Browse the repository at this point in the history
…rosoft#22252)

added check latestVersions command to build-tools.
Usage for build-docs pipeline scenario where we want to check if
triggering branch is the latest minor version of a major version.
  • Loading branch information
zhenmichael authored Sep 30, 2024
1 parent 542021f commit fc486fe
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 0 deletions.
28 changes: 28 additions & 0 deletions build-tools/packages/build-cli/docs/check.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Check commands are used to verify repo state, apply policy, etc.

* [`flub check buildVersion`](#flub-check-buildversion)
* [`flub check changeset`](#flub-check-changeset)
* [`flub check latestVersions VERSION PACKAGE_OR_RELEASE_GROUP`](#flub-check-latestversions-version-package_or_release_group)
* [`flub check layers`](#flub-check-layers)
* [`flub check policy`](#flub-check-policy)
* [`flub check prApproval`](#flub-check-prapproval)
Expand Down Expand Up @@ -97,6 +98,33 @@ EXAMPLES

_See code: [src/commands/check/changeset.ts](https://github.com/microsoft/FluidFramework/blob/main/build-tools/packages/build-cli/src/commands/check/changeset.ts)_

## `flub check latestVersions VERSION PACKAGE_OR_RELEASE_GROUP`

Determines if an input version matches a latest minor release version. Intended to be used in the Fluid Framework CI pipeline only.

```
USAGE
$ flub check latestVersions VERSION PACKAGE_OR_RELEASE_GROUP [-v | --quiet]
ARGUMENTS
VERSION The version to check. When running in CI, this value corresponds to the pipeline trigger
branch.
PACKAGE_OR_RELEASE_GROUP The name of a package or a release group.
LOGGING FLAGS
-v, --verbose Enable verbose logging.
--quiet Disable all logging.
DESCRIPTION
Determines if an input version matches a latest minor release version. Intended to be used in the Fluid Framework CI
pipeline only.
This command is used in CI to determine if a pipeline was triggered by a release branch with the latest minor version
of a major version.
```

_See code: [src/commands/check/latestVersions.ts](https://github.com/microsoft/FluidFramework/blob/main/build-tools/packages/build-cli/src/commands/check/latestVersions.ts)_

## `flub check layers`

Checks that the dependencies between Fluid Framework packages are properly layered.
Expand Down
18 changes: 18 additions & 0 deletions build-tools/packages/build-cli/src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { MonoRepo, Package } from "@fluidframework/build-tools";
import { Args } from "@oclif/core";
import { PackageName } from "@rushstack/node-core-library";
import * as semver from "semver";
// eslint-disable-next-line import/no-deprecated
import { Context, isMonoRepoKind } from "./library/index.js";

Expand Down Expand Up @@ -37,3 +38,20 @@ export const findPackageOrReleaseGroup = (
context.independentPackages.find((pkg) => PackageName.getUnscopedName(pkg.name) === name)
);
};

/**
* Creates a CLI argument for semver versions. It's a factory function so that commands can override the properties more
* easily when using the argument.
*/
export const semverArg = Args.custom<semver.SemVer, { loose?: boolean }>({
required: true,
description:
"A semantic versioning (semver) version string. Values are verified to be valid semvers during argument parsing.",
parse: async (input, _, opts) => {
const parsed = semver.parse(input, opts.loose);
if (parsed === null) {
throw new Error(`Invalid semver: ${input}`);
}
return parsed;
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

import { isInternalVersionScheme } from "@fluid-tools/version-tools";
import * as semver from "semver";
import { findPackageOrReleaseGroup, packageOrReleaseGroupArg, semverArg } from "../../args.js";
import { BaseCommand, sortVersions } from "../../library/index.js";

type MajorVersion = number;

export default class LatestVersionsCommand extends BaseCommand<typeof LatestVersionsCommand> {
static readonly summary =
"Determines if an input version matches a latest minor release version. Intended to be used in the Fluid Framework CI pipeline only.";

static readonly description =
"This command is used in CI to determine if a pipeline was triggered by a release branch with the latest minor version of a major version.";

static readonly args = {
version: semverArg({
required: true,
description:
"The version to check. When running in CI, this value corresponds to the pipeline trigger branch.",
}),
package_or_release_group: packageOrReleaseGroupArg({ required: true }),
} as const;

public async run(): Promise<void> {
const { args } = this;
const context = await this.getContext();
const versionInput = this.args.version;

const rgOrPackage = findPackageOrReleaseGroup(args.package_or_release_group, context);
if (rgOrPackage === undefined) {
this.error(`Package not found: ${args.package_or_release_group}`);
}

const versions = await context.getAllVersions(rgOrPackage.name);

if (!versions) {
this.error(`No versions found for ${rgOrPackage.name}`);
}

// Filter out pre-release versions
const stableVersions = versions.filter((v) => {
return !isInternalVersionScheme(v.version);
});

// Sort the semver versions ordered from highest to lowest
const sortedByVersion = sortVersions(stableVersions, "version");

const inputMajorVersion: MajorVersion = semver.major(versionInput.version);

for (const v of sortedByVersion) {
const majorVersion: MajorVersion = semver.major(v.version);

// Since sortedByVersion is sorted, the first encountered version is the highest one
if (majorVersion === inputMajorVersion) {
if (v.version === versionInput.version) {
// Check if the input version is the latest version for the major version
this.log(
`Version ${versionInput.version} is the latest version for major version ${majorVersion}`,
);
return;
}

// If versions do not match on first major version encounter, then the input version is not the latest
this.error(
`skipping deployment stage. input version ${versionInput.version} does not match the latest version ${v.version}`,
{ exit: 1 },
);
}
}

// Error if no major version corresponds to input version
this.error(
`No major version found corresponding to input version ${versionInput.version}`,
{ exit: 1 },
);
}
}

0 comments on commit fc486fe

Please sign in to comment.