blob: 3683d11596ff0efc9c0790782d14a0109bdabbc1 [file] [edit]
import fs from 'node:fs/promises';
import stringify from '../lib/stringify-and-order-properties.js';
import {
createOrUpdateBrowserEntry,
getRSSItems,
RSSItem,
updateBrowserEntry,
} from './utils';
interface Release {
version: string;
date: string;
releaseNote: string;
channel: 'current';
engine: 'Blink';
engineVersion: string;
}
/**
* Extracts the latest release from the items.
* @param items the RSS items.
* @param titleVersionPattern the pattern to match the title and extract the version.
* @param descriptionEngineVersionPattern the pattern to match the description and extract the engine version.
* @returns the latest release, if found, otherwise null.
*/
const findRelease = (
items: RSSItem[],
titleVersionPattern: RegExp,
descriptionEngineVersionPattern: RegExp,
): Release | null => {
const item = items.find(
(item) =>
titleVersionPattern.test(item.title) &&
descriptionEngineVersionPattern.test(item.description),
);
if (!item) {
return null;
}
const version = (
item.title.match(titleVersionPattern) as RegExpMatchArray
)[1];
const date = new Date(item.pubDate).toISOString().split('T')[0];
const releaseNote = item.link;
const engineVersion = (
item.description.match(descriptionEngineVersionPattern) as RegExpMatchArray
)[1];
return {
version,
date,
releaseNote,
channel: 'current',
engine: 'Blink',
engineVersion,
};
};
/**
* Converts a message into a GFM noteblock.
* @param type the type of the noteblock.
* @param message the message of the noteblock.
* @returns the message as a GFM noteblock.
*/
const gfmNoteblock = (type: 'INFO' | 'WARN', message: string) =>
`> [!${type}]\n${message
.split('\n')
.map((line) => `> ${line}`)
.join('\n')}`;
/**
* Updates the JSON files listing the Opera browser releases.
* @param options The list of options for this type of Safari.
* @returns The log of what has been generated (empty if nothing)
*/
export const updateOperaReleases = async (options) => {
const browser = options.bcdBrowserName;
const isDesktop = browser === 'opera';
let result = '';
const items = await getRSSItems(options.releaseFeedURL);
const release = findRelease(
items.filter((item) =>
options.releaseFilterCreator?.includes(item['dc:creator'] ?? true),
),
options.titleVersionPattern,
options.descriptionEngineVersionPattern,
);
if (!release) {
return gfmNoteblock(
'INFO',
`**${options.browserName}**: No release announcement found among ${items.length} items in [this RSS feed](<${options.releaseFeedURL}>).`,
);
}
const file = await fs.readFile(`${options.bcdFile}`, 'utf-8');
const data = JSON.parse(file.toString());
const current = structuredClone(
data.browsers[browser].releases[release.version],
);
if (isDesktop && !current) {
return gfmNoteblock(
'WARN',
`Latest stable **${options.browserName}** release **${release.version}** not yet tracked.`,
);
}
result += createOrUpdateBrowserEntry(
data,
browser,
release.version,
release.channel,
release.engine,
release.engineVersion,
release.date,
release.releaseNote,
);
// Set previous release to "retired".
const previousVersion = String(Number(release.version) - 1);
result += updateBrowserEntry(
data,
browser,
previousVersion,
undefined,
'retired',
undefined,
undefined,
);
if (isDesktop) {
// 1. Set next release to "beta".
result += createOrUpdateBrowserEntry(
data,
browser,
String(Number(release.version) + 1),
'beta',
release.engine,
String(Number(release.engineVersion) + 1),
);
// 2. Add another release as "nightly".
result += createOrUpdateBrowserEntry(
data,
browser,
String(Number(release.version) + 2),
'nightly',
release.engine,
String(Number(release.engineVersion) + 2),
);
}
await fs.writeFile(`./${options.bcdFile}`, stringify(data) + '\n');
// Returns the log
if (result) {
result = `### Updates for ${options.browserName}${result}`;
}
return result;
};