chrome.ts (6459B)
1 /** 2 * @license 3 * Copyright 2023 Google Inc. 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 import path from 'node:path'; 8 9 import semver from 'semver'; 10 11 import {getJSON} from '../httpUtil.js'; 12 13 import {BrowserPlatform, ChromeReleaseChannel} from './types.js'; 14 15 function folder(platform: BrowserPlatform): string { 16 switch (platform) { 17 case BrowserPlatform.LINUX_ARM: 18 case BrowserPlatform.LINUX: 19 return 'linux64'; 20 case BrowserPlatform.MAC_ARM: 21 return 'mac-arm64'; 22 case BrowserPlatform.MAC: 23 return 'mac-x64'; 24 case BrowserPlatform.WIN32: 25 return 'win32'; 26 case BrowserPlatform.WIN64: 27 return 'win64'; 28 } 29 } 30 31 export function resolveDownloadUrl( 32 platform: BrowserPlatform, 33 buildId: string, 34 baseUrl = 'https://storage.googleapis.com/chrome-for-testing-public', 35 ): string { 36 return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`; 37 } 38 39 export function resolveDownloadPath( 40 platform: BrowserPlatform, 41 buildId: string, 42 ): string[] { 43 return [buildId, folder(platform), `chrome-${folder(platform)}.zip`]; 44 } 45 46 export function relativeExecutablePath( 47 platform: BrowserPlatform, 48 _buildId: string, 49 ): string { 50 switch (platform) { 51 case BrowserPlatform.MAC: 52 case BrowserPlatform.MAC_ARM: 53 return path.join( 54 'chrome-' + folder(platform), 55 'Google Chrome for Testing.app', 56 'Contents', 57 'MacOS', 58 'Google Chrome for Testing', 59 ); 60 case BrowserPlatform.LINUX_ARM: 61 case BrowserPlatform.LINUX: 62 return path.join('chrome-linux64', 'chrome'); 63 case BrowserPlatform.WIN32: 64 case BrowserPlatform.WIN64: 65 return path.join('chrome-' + folder(platform), 'chrome.exe'); 66 } 67 } 68 69 export async function getLastKnownGoodReleaseForChannel( 70 channel: ChromeReleaseChannel, 71 ): Promise<{version: string; revision: string}> { 72 const data = (await getJSON( 73 new URL( 74 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions.json', 75 ), 76 )) as { 77 channels: Record<string, {version: string}>; 78 }; 79 80 for (const channel of Object.keys(data.channels)) { 81 data.channels[channel.toLowerCase()] = data.channels[channel]!; 82 delete data.channels[channel]; 83 } 84 85 return ( 86 data as { 87 channels: Record< 88 ChromeReleaseChannel, 89 {version: string; revision: string} 90 >; 91 } 92 ).channels[channel]; 93 } 94 95 export async function getLastKnownGoodReleaseForMilestone( 96 milestone: string, 97 ): Promise<{version: string; revision: string} | undefined> { 98 const data = (await getJSON( 99 new URL( 100 'https://googlechromelabs.github.io/chrome-for-testing/latest-versions-per-milestone.json', 101 ), 102 )) as { 103 milestones: Record<string, {version: string; revision: string}>; 104 }; 105 return data.milestones[milestone] as 106 | {version: string; revision: string} 107 | undefined; 108 } 109 110 export async function getLastKnownGoodReleaseForBuild( 111 /** 112 * @example `112.0.23`, 113 */ 114 buildPrefix: string, 115 ): Promise<{version: string; revision: string} | undefined> { 116 const data = (await getJSON( 117 new URL( 118 'https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build.json', 119 ), 120 )) as { 121 builds: Record<string, {version: string; revision: string}>; 122 }; 123 return data.builds[buildPrefix] as 124 | {version: string; revision: string} 125 | undefined; 126 } 127 128 export async function resolveBuildId( 129 channel: ChromeReleaseChannel, 130 ): Promise<string>; 131 export async function resolveBuildId( 132 channel: string, 133 ): Promise<string | undefined>; 134 export async function resolveBuildId( 135 channel: ChromeReleaseChannel | string, 136 ): Promise<string | undefined> { 137 if ( 138 Object.values(ChromeReleaseChannel).includes( 139 channel as ChromeReleaseChannel, 140 ) 141 ) { 142 return ( 143 await getLastKnownGoodReleaseForChannel(channel as ChromeReleaseChannel) 144 ).version; 145 } 146 if (channel.match(/^\d+$/)) { 147 // Potentially a milestone. 148 return (await getLastKnownGoodReleaseForMilestone(channel))?.version; 149 } 150 if (channel.match(/^\d+\.\d+\.\d+$/)) { 151 // Potentially a build prefix without the patch version. 152 return (await getLastKnownGoodReleaseForBuild(channel))?.version; 153 } 154 return; 155 } 156 157 export function resolveSystemExecutablePath( 158 platform: BrowserPlatform, 159 channel: ChromeReleaseChannel, 160 ): string { 161 switch (platform) { 162 case BrowserPlatform.WIN64: 163 case BrowserPlatform.WIN32: 164 switch (channel) { 165 case ChromeReleaseChannel.STABLE: 166 return `${process.env['PROGRAMFILES']}\\Google\\Chrome\\Application\\chrome.exe`; 167 case ChromeReleaseChannel.BETA: 168 return `${process.env['PROGRAMFILES']}\\Google\\Chrome Beta\\Application\\chrome.exe`; 169 case ChromeReleaseChannel.CANARY: 170 return `${process.env['PROGRAMFILES']}\\Google\\Chrome SxS\\Application\\chrome.exe`; 171 case ChromeReleaseChannel.DEV: 172 return `${process.env['PROGRAMFILES']}\\Google\\Chrome Dev\\Application\\chrome.exe`; 173 } 174 case BrowserPlatform.MAC_ARM: 175 case BrowserPlatform.MAC: 176 switch (channel) { 177 case ChromeReleaseChannel.STABLE: 178 return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; 179 case ChromeReleaseChannel.BETA: 180 return '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta'; 181 case ChromeReleaseChannel.CANARY: 182 return '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'; 183 case ChromeReleaseChannel.DEV: 184 return '/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev'; 185 } 186 case BrowserPlatform.LINUX_ARM: 187 case BrowserPlatform.LINUX: 188 switch (channel) { 189 case ChromeReleaseChannel.STABLE: 190 return '/opt/google/chrome/chrome'; 191 case ChromeReleaseChannel.BETA: 192 return '/opt/google/chrome-beta/chrome'; 193 case ChromeReleaseChannel.CANARY: 194 return '/opt/google/chrome-canary/chrome'; 195 case ChromeReleaseChannel.DEV: 196 return '/opt/google/chrome-unstable/chrome'; 197 } 198 } 199 } 200 201 export function compareVersions(a: string, b: string): number { 202 if (!semver.valid(a)) { 203 throw new Error(`Version ${a} is not a valid semver version`); 204 } 205 if (!semver.valid(b)) { 206 throw new Error(`Version ${b} is not a valid semver version`); 207 } 208 if (semver.gt(a, b)) { 209 return 1; 210 } else if (semver.lt(a, b)) { 211 return -1; 212 } else { 213 return 0; 214 } 215 }