From 052e6df88557be4cfdb17c4110e031b639ca9911 Mon Sep 17 00:00:00 2001 From: Navin Agarwal Date: Fri, 10 Oct 2025 14:38:55 -0700 Subject: [PATCH] (layer compat) Add auto generation update logic to release tools --- .../build-cli/src/handlers/doFunctions.ts | 107 ++ .../src/handlers/fluidReleaseStateHandler.ts | 7 +- .../build-cli/src/machines/FluidRelease.fsl | 1 + .../src/machines/FluidRelease.fsl.svg | 1025 +++++++++-------- 4 files changed, 633 insertions(+), 507 deletions(-) diff --git a/build-tools/packages/build-cli/src/handlers/doFunctions.ts b/build-tools/packages/build-cli/src/handlers/doFunctions.ts index c9478acb6f34..757d0a63e462 100644 --- a/build-tools/packages/build-cli/src/handlers/doFunctions.ts +++ b/build-tools/packages/build-cli/src/handlers/doFunctions.ts @@ -4,6 +4,7 @@ */ import { strict as assert } from "node:assert"; +import { readFile, writeFile } from "node:fs/promises"; import type { Machine } from "jssm"; import chalk from "picocolors"; @@ -191,3 +192,109 @@ export const doReleaseGroupBump: StateHandlerFunction = async ( BaseStateHandler.signalSuccess(machine, state); return true; }; + +/** + * Updates the generation used for layer compatibility. + * + * @param state - The current state machine state. + * @param machine - The state machine. + * @param testMode - Set to true to run function in test mode. + * @param log - A logger that the function can use for logging. + * @param data - An object with handler-specific contextual data. + * @returns True if the state was handled; false otherwise. + */ +export const doLayerGenerationUpdate: StateHandlerFunction = async ( + state: MachineState, + machine: Machine, + testMode: boolean, + log: CommandLogger, + data: FluidReleaseStateHandlerData, +): Promise => { + if (testMode) return true; + + const { bumpType } = data; + if (bumpType === "patch") { + log.info(`Generation update is not needed for patch release`); + BaseStateHandler.signalSuccess(machine, state); + return true; + } + + log.info(`Updating layerGeneration.ts for a ${bumpType} release`); + + // eslint-disable-next-line no-warning-comments + // TODO: Is it okay to read a file from this folder? + // The file that stores information of the current generation and release date. + const filename = "packages/common/client-utils/src/layerGeneration.ts"; + + // eslint-disable-next-line no-warning-comments + // TODO: This should ideally be read from a common location in client utils. What is the best way to do this? + // The minimum compatibility window in months that is supported across all layers. + const minimumCompatWindowMonths = 3; + + const today = new Date(); + let previousGeneration = -1; + let newGeneration = 1; + + let fileContents: string | undefined; + try { + fileContents = await readFile(filename, "utf8"); + } catch { + log.info(`File ${filename} doesn't exist yet`); + } + + if (fileContents !== undefined) { + const match = fileContents.match( + /.*\nexport const generation = (\d+);[\n\r]*export const releaseDate = "((0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2})";.*/m, + ); + if (match === null) { + log.errorLog(`${filename} content not as expected`); + BaseStateHandler.signalFailure(machine, state); + return false; + } + + previousGeneration = Number(match[1]); + const previousReleaseDateString = match[2]; + const previousReleaseDate = new Date(previousReleaseDateString); + const daysBetweenReleases = Math.round( + (today.getTime() - previousReleaseDate.getTime()) / (1000 * 60 * 60 * 24), + ); + + const monthsBetweenReleases = Math.floor(daysBetweenReleases / 30); + newGeneration = + previousGeneration + Math.min(monthsBetweenReleases, minimumCompatWindowMonths - 1); + + log.info(`Previous release date: ${previousReleaseDate}, Today: ${today}`); + log.info(`Days between releases: ${daysBetweenReleases}`); + log.info(`Months between releases: ${monthsBetweenReleases}`); + } + + if (newGeneration === previousGeneration) { + log.info(`Generation does not need to be bumped`); + BaseStateHandler.signalSuccess(machine, state); + return true; + } + + const yyyy = today.getFullYear(); + const mmNumber = today.getMonth() + 1; // Months start at 0! + const ddNumber = today.getDate(); + + const dd = ddNumber < 10 ? `0${ddNumber}` : ddNumber.toString(); + const mm = mmNumber < 10 ? `0${mmNumber}` : mmNumber.toString(); + const releaseDateFormatted = `${mm}/${dd}/${yyyy}`; + + const output = `/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY + */ + +export const generation = ${newGeneration}; +export const releaseDate = "${releaseDateFormatted}"; +`; + + log.info(`Bumping generation from ${previousGeneration} to ${newGeneration}`); + await writeFile(filename, output); + + BaseStateHandler.signalSuccess(machine, state); + return true; +}; diff --git a/build-tools/packages/build-cli/src/handlers/fluidReleaseStateHandler.ts b/build-tools/packages/build-cli/src/handlers/fluidReleaseStateHandler.ts index 7253cc5946a8..9ebe8cccbc7c 100644 --- a/build-tools/packages/build-cli/src/handlers/fluidReleaseStateHandler.ts +++ b/build-tools/packages/build-cli/src/handlers/fluidReleaseStateHandler.ts @@ -41,7 +41,11 @@ import { checkTypeTestPrepare, checkValidReleaseGroup, } from "./checkFunctions.js"; -import { doBumpReleasedDependencies, doReleaseGroupBump } from "./doFunctions.js"; +import { + doBumpReleasedDependencies, + doLayerGenerationUpdate, + doReleaseGroupBump, +} from "./doFunctions.js"; import { InitFailedStateHandler } from "./initFailedStateHandler.js"; import { promptToCommitChanges, @@ -191,6 +195,7 @@ export class FluidReleaseStateHandler extends InitFailedStateHandler { ["CheckTypeTestPrepare2", checkTypeTestPrepare], ["CheckValidReleaseGroup", checkValidReleaseGroup], ["DoBumpReleasedDependencies", doBumpReleasedDependencies], + ["DoLayerGenerationUpdate", doLayerGenerationUpdate], ["DoMajorRelease", handleBumpType], ["DoMinorRelease", handleBumpType], ["DoPatchRelease", handleBumpType], diff --git a/build-tools/packages/build-cli/src/machines/FluidRelease.fsl b/build-tools/packages/build-cli/src/machines/FluidRelease.fsl index 16781f5d4159..ab971c38f480 100644 --- a/build-tools/packages/build-cli/src/machines/FluidRelease.fsl +++ b/build-tools/packages/build-cli/src/machines/FluidRelease.fsl @@ -137,6 +137,7 @@ CheckShouldCommitDeps 'failure' // Bumps a release group and prompts to push/PR or commit changes DoReleaseGroupBump 'success' +=> DoLayerGenerationUpdate 'success' => CheckShouldCommitBump 'success' => PromptToPRBump; diff --git a/build-tools/packages/build-cli/src/machines/FluidRelease.fsl.svg b/build-tools/packages/build-cli/src/machines/FluidRelease.fsl.svg index f0a4f8b70b17..7f528241d15e 100644 --- a/build-tools/packages/build-cli/src/machines/FluidRelease.fsl.svg +++ b/build-tools/packages/build-cli/src/machines/FluidRelease.fsl.svg @@ -4,8 +4,8 @@ - + - + G - + n0 - -Init + +Init n1 - -CheckShouldRunOptionalChecks + +CheckShouldRunOptionalChecks n0->n1 - - -success + + +success n13 - -Failed + +Failed n0->n13 - - -failure + + +failure n2 - -CheckValidReleaseGroup + +CheckValidReleaseGroup n1->n2 - - -success + + +success n10 - -CheckNoPrereleaseDependencies + +CheckNoPrereleaseDependencies n1->n10 - - -failure + + +failure n3 - -CheckDependenciesInstalled + +CheckDependenciesInstalled n2->n3 - - -success + + +success n2->n13 - - -failure + + +failure n4 - -CheckPolicy + +CheckPolicy n3->n4 - - -success + + +success n3->n13 - - -failure + + +failure n5 - -CheckAssertTagging + +CheckAssertTagging n4->n5 - - -success + + +success n14 - -PromptToCommitPolicy + +PromptToCommitPolicy n4->n14 - - -failure + + +failure n6 - -CheckReleaseNotes + +CheckReleaseNotes n5->n6 - - -success + + +success n5->n14 - - -failure + + +failure n7 - -CheckChangelogs + +CheckChangelogs n6->n7 - - -success + + +success n15 - -PromptToGenerateReleaseNotes + +PromptToGenerateReleaseNotes n6->n15 - - -failure + + +failure n8 - -CheckHasRemote + +CheckHasRemote n7->n8 - - -success + + +success n16 - -PromptToGenerateChangelogs + +PromptToGenerateChangelogs n7->n16 - - -failure + + +failure n9 - -CheckBranchUpToDate + +CheckBranchUpToDate n8->n9 - - -success + + +success n8->n13 - - -failure + + +failure n9->n10 - - -success + + +success n9->n13 - - -failure + + +failure n11 - -AskForReleaseType + +AskForReleaseType n10->n11 - - -success + + +success n12 - -DoBumpReleasedDependencies + +DoBumpReleasedDependencies n10->n12 - - -failure + + +failure - - -n30 - -DoPatchRelease + + +n31 + +DoPatchRelease - + -n11->n30 - - -patch +n11->n31 + + +patch - - -n42 - -DoMinorRelease + + +n43 + +DoMinorRelease - + -n11->n42 - - -minor +n11->n43 + + +minor - - -n51 - -DoMajorRelease + + +n52 + +DoMajorRelease - + -n11->n51 - - -major +n11->n52 + + +major n17 - -CheckNoPrereleaseDependencies2 + +CheckNoPrereleaseDependencies2 n12->n17 - - -success + + +success n20 - -CheckNoPrereleaseDependencies3 + +CheckNoPrereleaseDependencies3 n12->n20 - - -failure + + +failure n18 - -CheckShouldCommitDeps + +CheckShouldCommitDeps n17->n18 - - -success + + +success n21 - -PromptToReleaseDeps + +PromptToReleaseDeps n17->n21 - - -failure + + +failure n19 - -PromptToPRDeps + +PromptToPRDeps n18->n19 - - -success + + +success n25 - -PromptToCommitDeps + +PromptToCommitDeps n18->n25 - - -failure + + +failure n20->n21 - - -failure + + +failure n22 - -CheckShouldCommitReleasedDepsBump + +CheckShouldCommitReleasedDepsBump n20->n22 - - -success + + +success n23 - -PromptToPRReleasedDepsBump + +PromptToPRReleasedDepsBump n22->n23 - - -success + + +success n24 - -PromptToCommitReleasedDepsBump + +PromptToCommitReleasedDepsBump n22->n24 - - -failure + + +failure n26 - -DoReleaseGroupBump + +DoReleaseGroupBump n27 - -CheckShouldCommitBump + +DoLayerGenerationUpdate n26->n27 - - -success + + +success n28 - -PromptToPRBump + +CheckShouldCommitBump n27->n28 - - -success + + +success n29 - -PromptToCommitBump + +PromptToPRBump - + -n27->n29 - - -failure +n28->n29 + + +success - - -n31 - -CheckDoesReleaseFromReleaseBranch + + +n30 + +PromptToCommitBump - + -n30->n31 - - -success +n28->n30 + + +failure n32 - -CheckOnReleaseBranch + +CheckDoesReleaseFromReleaseBranch n31->n32 - - -success + + +success n33 - -CheckReleaseIsDone - - - -n31->n33 - - -failure - - - -n32->n13 - - -failure + +CheckOnReleaseBranch - + n32->n33 - - -success + + +success n34 - -CheckReleaseGroupIsBumpedPatch - - - -n33->n34 - - -success + +CheckReleaseIsDone - - -n38 - -PromptToRelease + + +n32->n34 + + +failure - - -n33->n38 - - -failure + + +n33->n13 + + +failure - - -n34->n26 - - -failure + + +n33->n34 + + +success n35 - -CheckTypeTestPrepare + +CheckReleaseGroupIsBumpedPatch - + n34->n35 - - -success + + +success + + + +n39 + +PromptToRelease + + + +n34->n39 + + +failure + + + +n35->n26 + + +failure n36 - -CheckTypeTestGenerate + +CheckTypeTestPrepare - + n35->n36 - - -success - - - -n39 - -PromptToRunTypeTests - - - -n35->n39 - - -failure + + +success n37 - -ReleaseComplete + +CheckTypeTestGenerate - + n36->n37 - - -success - - - -n36->n39 - - -failure + + +success n40 - -CheckTypeTestPrepare2 + +PromptToRunTypeTests + + + +n36->n40 + + +failure - + + +n38 + +ReleaseComplete + + + +n37->n38 + + +success + + -n40->n39 - - -failure +n37->n40 + + +failure n41 - -CheckTypeTestGenerate2 + +CheckTypeTestPrepare2 - + -n40->n41 - - -success +n41->n40 + + +failure - - -n41->n37 - - -success + + +n42 + +CheckTypeTestGenerate2 - + -n41->n39 - - -failure - - - -n43 - -CheckDoesReleaseFromReleaseBranch2 +n41->n42 + + +success - + -n42->n43 - - -success +n42->n38 + + +success + + + +n42->n40 + + +failure n44 - -CheckOnReleaseBranch2 + +CheckDoesReleaseFromReleaseBranch2 n43->n44 - - -success - - - -n46 - -CheckReleaseIsDone2 - - - -n43->n46 - - -failure + + +success n45 - -CheckReleaseGroupIsBumpedPatch2 + +CheckOnReleaseBranch2 - + n44->n45 - - -success + + +success - - -n48 - -CheckReleaseGroupIsBumpedMinor + + +n47 + +CheckReleaseIsDone2 - - -n44->n48 - - -failure + + +n44->n47 + + +failure - - -n45->n26 - - -failure + + +n46 + +CheckReleaseGroupIsBumpedPatch2 - + n45->n46 - - -success + + +success - - -n46->n38 - - -failure + + +n49 + +CheckReleaseGroupIsBumpedMinor - - -n47 - -CheckReleaseGroupIsBumpedMinor2 + + +n45->n49 + + +failure - + -n46->n47 - - -success +n46->n26 + + +failure - - -n47->n26 - - -failure + + +n46->n47 + + +success - + -n47->n40 - - -success +n47->n39 + + +failure - - -n48->n26 - - -failure + + +n48 + +CheckReleaseGroupIsBumpedMinor2 - - -n49 - -CheckReleaseBranchExists + + +n47->n48 + + +success - + -n48->n49 - - -success +n48->n26 + + +failure - + + +n48->n41 + + +success + + -n49->n33 - - -success +n49->n26 + + +failure n50 - -PromptToCreateReleaseBranch + +CheckReleaseBranchExists - + n49->n50 - - -failure + + +success - - -n52 - -CheckDoesReleaseFromReleaseBranch3 + + +n50->n34 + + +success + + + +n51 + +PromptToCreateReleaseBranch - + -n51->n52 - - -success +n50->n51 + + +failure n53 - -CheckMainNextIntegrated + +CheckDoesReleaseFromReleaseBranch3 n52->n53 - - -success + + +success n54 - -CheckReleaseIsDone3 - - - -n52->n54 - - -failure + +CheckMainNextIntegrated - + n53->n54 - - -success + + +success n55 - -CheckOnReleaseBranch3 + +CheckReleaseIsDone3 - + n53->n55 - - -failure + + +failure - - -n54->n38 - - -success - - - -n54->n42 - - -failure - - - -n55->n13 - - -success + + +n54->n55 + + +success n56 - -CheckBranchName + +CheckOnReleaseBranch3 - - -n55->n56 - - -failure + + +n54->n56 + + +failure + + + +n55->n39 + + +success + + + +n55->n43 + + +failure - + n56->n13 - - -failure + + +success n57 - -PromptToIntegrateNext + +CheckBranchName n56->n57 - - -success + + +failure + + + +n57->n13 + + +failure + + + +n58 + +PromptToIntegrateNext + + + +n57->n58 + + +success