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