1*fe5cfb17STomasz Sapeta/** 2*fe5cfb17STomasz Sapeta * Copyright (c) Meta Platforms, Inc. and affiliates. 3*fe5cfb17STomasz Sapeta * 4*fe5cfb17STomasz Sapeta * This source code is licensed under the MIT license found in the 5*fe5cfb17STomasz Sapeta * LICENSE file in the root directory of this source tree. 6*fe5cfb17STomasz Sapeta * 7*fe5cfb17STomasz Sapeta * @format 8*fe5cfb17STomasz Sapeta */ 9*fe5cfb17STomasz Sapeta 10*fe5cfb17STomasz Sapetaconst VERSION_REGEX = /^v?((\d+)\.(\d+)\.(\d+)(?:-(.+))?)$/; 11*fe5cfb17STomasz Sapeta 12*fe5cfb17STomasz Sapeta/** 13*fe5cfb17STomasz Sapeta * Parses a version string and performs some checks to verify its validity. 14*fe5cfb17STomasz Sapeta * A valid version is in the format vX.Y.Z[-KKK] where X, Y, Z are numbers and KKK can be something else. 15*fe5cfb17STomasz Sapeta * The `builtType` is used to enforce that the major version can assume only specific 16*fe5cfb17STomasz Sapeta * values. 17*fe5cfb17STomasz Sapeta * 18*fe5cfb17STomasz Sapeta * Some examples of valid versions are: 19*fe5cfb17STomasz Sapeta * - stable: 0.68.1 20*fe5cfb17STomasz Sapeta * - stable prerelease: 0.70.0-rc.0 21*fe5cfb17STomasz Sapeta * - e2e-test: X.Y.Z-20221116-2018 22*fe5cfb17STomasz Sapeta * - nightly: 0.0.0-20221116-2018-0bc4547fc 23*fe5cfb17STomasz Sapeta * - dryrun: 1000.0.0 24*fe5cfb17STomasz Sapeta * 25*fe5cfb17STomasz Sapeta * Parameters: 26*fe5cfb17STomasz Sapeta * - @versionStr the string representing a version 27*fe5cfb17STomasz Sapeta * - @buildType the build type. It can be of values: `dry-run`, `release`, `nightly` 28*fe5cfb17STomasz Sapeta * 29*fe5cfb17STomasz Sapeta * Returns: an object with the shape: 30*fe5cfb17STomasz Sapeta * ``` 31*fe5cfb17STomasz Sapeta * { 32*fe5cfb17STomasz Sapeta * version: string, 33*fe5cfb17STomasz Sapeta * major: number, 34*fe5cfb17STomasz Sapeta * minor: number, 35*fe5cfb17STomasz Sapeta * patch: number, 36*fe5cfb17STomasz Sapeta * prerelease: string 37*fe5cfb17STomasz Sapeta * } 38*fe5cfb17STomasz Sapeta * ``` 39*fe5cfb17STomasz Sapeta * 40*fe5cfb17STomasz Sapeta */ 41*fe5cfb17STomasz Sapetafunction parseVersion(versionStr, buildType) { 42*fe5cfb17STomasz Sapeta try { 43*fe5cfb17STomasz Sapeta validateBuildType(buildType); 44*fe5cfb17STomasz Sapeta } catch (e) { 45*fe5cfb17STomasz Sapeta throw e; 46*fe5cfb17STomasz Sapeta } 47*fe5cfb17STomasz Sapeta 48*fe5cfb17STomasz Sapeta const match = extractMatchIfValid(versionStr); 49*fe5cfb17STomasz Sapeta const [, version, major, minor, patch, prerelease] = match; 50*fe5cfb17STomasz Sapeta 51*fe5cfb17STomasz Sapeta const versionObject = { 52*fe5cfb17STomasz Sapeta version, 53*fe5cfb17STomasz Sapeta major, 54*fe5cfb17STomasz Sapeta minor, 55*fe5cfb17STomasz Sapeta patch, 56*fe5cfb17STomasz Sapeta prerelease, 57*fe5cfb17STomasz Sapeta }; 58*fe5cfb17STomasz Sapeta 59*fe5cfb17STomasz Sapeta try { 60*fe5cfb17STomasz Sapeta validateVersion(versionObject, buildType); 61*fe5cfb17STomasz Sapeta } catch (e) { 62*fe5cfb17STomasz Sapeta throw e; 63*fe5cfb17STomasz Sapeta } 64*fe5cfb17STomasz Sapeta 65*fe5cfb17STomasz Sapeta return versionObject; 66*fe5cfb17STomasz Sapeta} 67*fe5cfb17STomasz Sapeta 68*fe5cfb17STomasz Sapetafunction validateBuildType(buildType) { 69*fe5cfb17STomasz Sapeta const validBuildTypes = new Set(['release', 'dry-run', 'nightly']); 70*fe5cfb17STomasz Sapeta if (!validBuildTypes.has(buildType)) { 71*fe5cfb17STomasz Sapeta throw new Error(`Unsupported build type: ${buildType}`); 72*fe5cfb17STomasz Sapeta } 73*fe5cfb17STomasz Sapeta} 74*fe5cfb17STomasz Sapeta 75*fe5cfb17STomasz Sapetafunction extractMatchIfValid(versionStr) { 76*fe5cfb17STomasz Sapeta const match = versionStr.match(VERSION_REGEX); 77*fe5cfb17STomasz Sapeta if (!match) { 78*fe5cfb17STomasz Sapeta throw new Error( 79*fe5cfb17STomasz Sapeta `You must pass a correctly formatted version; couldn't parse ${versionStr}`, 80*fe5cfb17STomasz Sapeta ); 81*fe5cfb17STomasz Sapeta } 82*fe5cfb17STomasz Sapeta return match; 83*fe5cfb17STomasz Sapeta} 84*fe5cfb17STomasz Sapeta 85*fe5cfb17STomasz Sapetafunction validateVersion(versionObject, buildType) { 86*fe5cfb17STomasz Sapeta const map = { 87*fe5cfb17STomasz Sapeta release: validateRelease, 88*fe5cfb17STomasz Sapeta 'dry-run': validateDryRun, 89*fe5cfb17STomasz Sapeta nightly: validateNightly, 90*fe5cfb17STomasz Sapeta }; 91*fe5cfb17STomasz Sapeta 92*fe5cfb17STomasz Sapeta const validationFunction = map[buildType]; 93*fe5cfb17STomasz Sapeta validationFunction(versionObject); 94*fe5cfb17STomasz Sapeta} 95*fe5cfb17STomasz Sapeta 96*fe5cfb17STomasz Sapeta/** 97*fe5cfb17STomasz Sapeta * Releases are in the form of 0.Y.Z[-RC.0] 98*fe5cfb17STomasz Sapeta */ 99*fe5cfb17STomasz Sapetafunction validateRelease(version) { 100*fe5cfb17STomasz Sapeta const validRelease = isStableRelease(version) || isStablePrerelease(version); 101*fe5cfb17STomasz Sapeta if (!validRelease) { 102*fe5cfb17STomasz Sapeta throw new Error(`Version ${version.version} is not valid for Release`); 103*fe5cfb17STomasz Sapeta } 104*fe5cfb17STomasz Sapeta} 105*fe5cfb17STomasz Sapeta 106*fe5cfb17STomasz Sapetafunction validateDryRun(version) { 107*fe5cfb17STomasz Sapeta const isNightly = isNightlyBuild(version) && version.prerelease != null; 108*fe5cfb17STomasz Sapeta 109*fe5cfb17STomasz Sapeta if ( 110*fe5cfb17STomasz Sapeta !isMain(version) && 111*fe5cfb17STomasz Sapeta !isNightly && 112*fe5cfb17STomasz Sapeta !isStableRelease(version) && 113*fe5cfb17STomasz Sapeta !isStablePrerelease(version) 114*fe5cfb17STomasz Sapeta ) { 115*fe5cfb17STomasz Sapeta throw new Error(`Version ${version.version} is not valid for dry-runs`); 116*fe5cfb17STomasz Sapeta } 117*fe5cfb17STomasz Sapeta} 118*fe5cfb17STomasz Sapeta 119*fe5cfb17STomasz Sapetafunction validateNightly(version) { 120*fe5cfb17STomasz Sapeta // a valid nightly is a prerelease 121*fe5cfb17STomasz Sapeta const isPrerelease = version.prerelease != null; 122*fe5cfb17STomasz Sapeta const isValidNightly = isNightlyBuild(version) && isPrerelease; 123*fe5cfb17STomasz Sapeta if (!isValidNightly) { 124*fe5cfb17STomasz Sapeta throw new Error(`Version ${version.version} is not valid for nightlies`); 125*fe5cfb17STomasz Sapeta } 126*fe5cfb17STomasz Sapeta} 127*fe5cfb17STomasz Sapeta 128*fe5cfb17STomasz Sapetafunction isStableRelease(version) { 129*fe5cfb17STomasz Sapeta return ( 130*fe5cfb17STomasz Sapeta version.major === '0' && version.minor !== '0' && version.prerelease == null 131*fe5cfb17STomasz Sapeta ); 132*fe5cfb17STomasz Sapeta} 133*fe5cfb17STomasz Sapeta 134*fe5cfb17STomasz Sapetafunction isStablePrerelease(version) { 135*fe5cfb17STomasz Sapeta return ( 136*fe5cfb17STomasz Sapeta version.major === '0' && 137*fe5cfb17STomasz Sapeta version.minor !== '0' && 138*fe5cfb17STomasz Sapeta version.patch.match(/^\d+$/) && 139*fe5cfb17STomasz Sapeta version.prerelease != null && 140*fe5cfb17STomasz Sapeta (version.prerelease.startsWith('rc.') || 141*fe5cfb17STomasz Sapeta version.prerelease.startsWith('rc-') || 142*fe5cfb17STomasz Sapeta version.prerelease.match(/^(\d{8})-(\d{4})$/)) 143*fe5cfb17STomasz Sapeta ); 144*fe5cfb17STomasz Sapeta} 145*fe5cfb17STomasz Sapeta 146*fe5cfb17STomasz Sapetafunction isNightlyBuild(version) { 147*fe5cfb17STomasz Sapeta return ( 148*fe5cfb17STomasz Sapeta version.major === '0' && version.minor === '0' && version.patch === '0' 149*fe5cfb17STomasz Sapeta ); 150*fe5cfb17STomasz Sapeta} 151*fe5cfb17STomasz Sapeta 152*fe5cfb17STomasz Sapetafunction isMain(version) { 153*fe5cfb17STomasz Sapeta return ( 154*fe5cfb17STomasz Sapeta version.major === '1000' && version.minor === '0' && version.patch === '0' 155*fe5cfb17STomasz Sapeta ); 156*fe5cfb17STomasz Sapeta} 157*fe5cfb17STomasz Sapeta 158*fe5cfb17STomasz Sapetafunction isReleaseBranch(branch) { 159*fe5cfb17STomasz Sapeta return branch.endsWith('-stable'); 160*fe5cfb17STomasz Sapeta} 161*fe5cfb17STomasz Sapeta 162*fe5cfb17STomasz Sapetamodule.exports = { 163*fe5cfb17STomasz Sapeta validateBuildType, 164*fe5cfb17STomasz Sapeta parseVersion, 165*fe5cfb17STomasz Sapeta isReleaseBranch, 166*fe5cfb17STomasz Sapeta isMain, 167*fe5cfb17STomasz Sapeta isStableRelease, 168*fe5cfb17STomasz Sapeta isStablePrerelease, 169*fe5cfb17STomasz Sapeta}; 170