262 lines
6.9 KiB
JavaScript
262 lines
6.9 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2023 Google Inc.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import {execSync, exec} from 'child_process';
|
|
import {writeFile, readFile} from 'fs/promises';
|
|
import {promisify} from 'util';
|
|
|
|
import actions from '@actions/core';
|
|
import {resolveBuildId} from '@puppeteer/browsers';
|
|
import {SemVer} from 'semver';
|
|
|
|
import packageJson from '../packages/puppeteer-core/package.json' with {type: 'json'};
|
|
import versionData from '../versions.json' with {type: 'json'};
|
|
|
|
import {PUPPETEER_REVISIONS} from 'puppeteer-core/internal/revisions.js';
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
const BROWSER = process.env.BROWSER_TO_UPDATE;
|
|
|
|
if (!BROWSER) {
|
|
console.error('No BROWSER_TO_UPDATE env variable supplied!');
|
|
process.exit(1);
|
|
}
|
|
|
|
const BROWSER_CURRENT_VERSION = PUPPETEER_REVISIONS[BROWSER];
|
|
|
|
const touchedFiles = [];
|
|
|
|
function getCapitalize(text) {
|
|
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} version
|
|
* @returns {string}
|
|
*/
|
|
function normalizeVersionForCommit(browser, version) {
|
|
switch (browser) {
|
|
case 'firefox':
|
|
// Splits the prefix of `stable_` for Firefox
|
|
return version.split('_').at(-1);
|
|
case 'chrome':
|
|
return version;
|
|
}
|
|
|
|
throw new Error(`Unrecognized browser ${browser}`);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} version
|
|
* @returns {string}
|
|
*/
|
|
function normalizeVersionToSemVer(browser, version) {
|
|
switch (browser) {
|
|
case 'firefox':
|
|
// Splits the prefix of `stable_` for Firefox
|
|
version = version.split('_').at(-1);
|
|
// Firefox reports 129.0 instead of 129.0.0
|
|
// Patch have the correct number 128.0.2
|
|
if (version.split('.').length <= 2) {
|
|
return `${version}.0`;
|
|
}
|
|
|
|
return version;
|
|
case 'chrome':
|
|
// For Chrome (example: 127.0.6533.99) is allowed as SemVer
|
|
// as long as we use the loose option.
|
|
return version;
|
|
}
|
|
|
|
throw new Error(`Unrecognized browser ${browser}`);
|
|
}
|
|
|
|
function checkIfNeedsUpdate(browser, oldVersion, newVersion) {
|
|
const oldSemVer = new SemVer(
|
|
normalizeVersionToSemVer(browser, oldVersion),
|
|
true,
|
|
);
|
|
const newSemVer = new SemVer(
|
|
normalizeVersionToSemVer(browser, newVersion),
|
|
true,
|
|
);
|
|
let message = `roll to ${getCapitalize(browser)} ${normalizeVersionForCommit(browser, newVersion)}`;
|
|
|
|
if (newSemVer.compare(oldSemVer) <= 0) {
|
|
// Exit the process without setting up version
|
|
console.warn(
|
|
`Version ${newVersion} is older or the same as the current ${oldVersion}`,
|
|
);
|
|
process.exit(0);
|
|
} else if (newSemVer.major === oldSemVer.major) {
|
|
message = `fix: ${message}`;
|
|
} else {
|
|
message = `feat: ${message}`;
|
|
}
|
|
actions.setOutput('commit', message);
|
|
}
|
|
|
|
/**
|
|
* We cant use `npm run format` as it's too slow
|
|
* so we only scope the files we updated
|
|
*/
|
|
async function formatUpdateFiles() {
|
|
await Promise.all(
|
|
touchedFiles.map(file => {
|
|
return execAsync(`npx eslint --fix ${file}`);
|
|
}),
|
|
);
|
|
await Promise.all(
|
|
touchedFiles.map(file => {
|
|
return execAsync(`npx prettier --write ${file}`);
|
|
}),
|
|
);
|
|
}
|
|
|
|
async function replaceInFile(filePath, search, replace) {
|
|
const buffer = await readFile(filePath);
|
|
const update = buffer.toString().replaceAll(search, replace);
|
|
|
|
await writeFile(filePath, update);
|
|
|
|
touchedFiles.push(filePath);
|
|
}
|
|
|
|
async function getVersionForStable(browser) {
|
|
return await resolveBuildId(browser, 'linux', 'stable');
|
|
}
|
|
|
|
async function updateDevToolsProtocolVersion(browserVersion) {
|
|
const result = await fetch(
|
|
'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions.json',
|
|
).then(response => {
|
|
return response.json();
|
|
});
|
|
|
|
const {version, revision} = result.channels['Stable'];
|
|
if (browserVersion !== version) {
|
|
console.error(
|
|
'The version from CfT website and @puppeteer/browser mismatch.',
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
const currentProtocol = packageJson.dependencies['devtools-protocol'];
|
|
const command = `npm view "devtools-protocol@<=0.0.${revision}" version | tail -1`;
|
|
|
|
const bestNewProtocol = execSync(command, {
|
|
encoding: 'utf8',
|
|
})
|
|
.split(' ')[1]
|
|
.replace(/'|\n/g, '');
|
|
|
|
await replaceInFile(
|
|
'./packages/puppeteer-core/package.json',
|
|
`"devtools-protocol": "${currentProtocol}"`,
|
|
`"devtools-protocol": "${bestNewProtocol}"`,
|
|
);
|
|
|
|
await replaceInFile(
|
|
'./packages/puppeteer/package.json',
|
|
`"devtools-protocol": "${currentProtocol}"`,
|
|
`"devtools-protocol": "${bestNewProtocol}"`,
|
|
);
|
|
}
|
|
|
|
async function saveVersionData() {
|
|
await writeFile('./versions.json', JSON.stringify(versionData, null, 2));
|
|
touchedFiles.push('./versions.json');
|
|
}
|
|
|
|
async function updateVersionData(browser, oldVersion, newVersion) {
|
|
const browserVersions = versionData.versions.map(
|
|
([_puppeteerVersion, browserVersions]) => {
|
|
return browserVersions[browser];
|
|
},
|
|
);
|
|
if (browserVersions.indexOf(newVersion) !== -1) {
|
|
// Already updated.
|
|
return;
|
|
}
|
|
|
|
const nextVersionConfig = versionData.versions.find(([puppeteerVersion]) => {
|
|
return puppeteerVersion === 'NEXT';
|
|
});
|
|
|
|
// If we have manually rolled Chrome but not yet released
|
|
// We will have NEXT as value in the Map
|
|
if (nextVersionConfig) {
|
|
nextVersionConfig[1][browser] = newVersion;
|
|
return;
|
|
}
|
|
|
|
versionData.versions.unshift([
|
|
'NEXT',
|
|
{
|
|
...versionData.versions.at(0).at(1),
|
|
[browser]: newVersion,
|
|
},
|
|
]);
|
|
}
|
|
|
|
async function updateLastMaintainedChromeVersion(oldVersion, newVersion) {
|
|
const browserVersions = versionData.versions.map(
|
|
([_puppeteerVersion, browserVersions]) => {
|
|
return browserVersions['chrome'];
|
|
},
|
|
);
|
|
if (browserVersions.indexOf(newVersion) !== -1) {
|
|
// Already updated.
|
|
return;
|
|
}
|
|
|
|
const oldSemVer = new SemVer(oldVersion, true);
|
|
const newSemVer = new SemVer(newVersion, true);
|
|
|
|
if (newSemVer.compareMain(oldSemVer) !== 0) {
|
|
const lastMaintainedSemVer = new SemVer(
|
|
versionData.lastMaintainedChromeVersion,
|
|
true,
|
|
);
|
|
const newLastMaintainedMajor = lastMaintainedSemVer.major + 1;
|
|
|
|
const nextMaintainedVersion = browserVersions.find(version => {
|
|
return new SemVer(version, true).major === newLastMaintainedMajor;
|
|
});
|
|
|
|
versionData.lastMaintainedChromeVersion = nextMaintainedVersion;
|
|
}
|
|
}
|
|
|
|
const version = await getVersionForStable(BROWSER);
|
|
|
|
checkIfNeedsUpdate(BROWSER, BROWSER_CURRENT_VERSION, version);
|
|
|
|
await replaceInFile(
|
|
'./packages/puppeteer-core/src/revisions.ts',
|
|
BROWSER_CURRENT_VERSION,
|
|
version,
|
|
);
|
|
|
|
await updateVersionData(BROWSER, BROWSER_CURRENT_VERSION, version);
|
|
|
|
if (BROWSER === 'chrome') {
|
|
await updateLastMaintainedChromeVersion(BROWSER_CURRENT_VERSION, version);
|
|
await updateDevToolsProtocolVersion(version);
|
|
// Create new `package-lock.json` as we update devtools-protocol
|
|
execSync('npm install --ignore-scripts');
|
|
}
|
|
|
|
await saveVersionData();
|
|
|
|
// Make sure we pass CI formatter check by running all the new files though it
|
|
await formatUpdateFiles();
|
|
|
|
// Keep this as they can be used to debug GitHub Actions if needed
|
|
actions.setOutput('version', version);
|