blob: ae777ce81d35d8f374f4be547e3f6b1da90ecbc3 [file] [log] [blame] [edit]
/* This file is a part of @mdn/browser-compat-data
* See LICENSE file for more information. */
import * as fs from 'node:fs';
import chalk from 'chalk-template';
import stringify from '../lib/stringify-and-order-properties.js';
import { newBrowserEntry, updateBrowserEntry } from './utils.js';
//
// Content of the two release note files
//
let releaseNotesText;
let archivedReleaseNotesText;
/**
* initReleaseNoteFiles - Fetch both release notes file and store them
* @returns Logs (error messages)
*/
const initReleaseNoteFiles = async () => {
let result = '';
// Fetch the regular page1
const releaseNote = await fetch(
'https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel',
);
// Check that it exists, if not exit.
if (releaseNote.status != 200) {
// File not found -> log a warning
result = chalk`{red \nRelease note files not found for Edge (${releaseNote.status}).`;
} else {
releaseNotesText = await releaseNote.text();
}
// Fetch the archived page
const archivedReleaseNotes = await fetch(
'https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-archive-stable-channel',
);
if (archivedReleaseNotes.status != 200) {
// File not found -> log a warning
result += chalk`{red \nArchive release note files not found for Edge (${archivedReleaseNotes.status}).`;
} else {
archivedReleaseNotesText = await archivedReleaseNotes.text();
}
return result;
};
/**
* updateReleaseNotesIfArchived - Return the new release notes URL to use
* @param originalURL The URL that is currently used
* @returns The new URL to use (eventually the same)
*/
const updateReleaseNotesIfArchived = (originalURL) => {
const id = originalURL.split('#')[1];
// Test if the original URL still works
// If the files doesn't exist or the id not found in the archive
// Keep the original file
if (
!id ||
!releaseNotesText ||
releaseNotesText.indexOf(`<h2 id="${id}">`) != -1 ||
!archivedReleaseNotesText ||
archivedReleaseNotesText.indexOf(`<h2 id="${id}">`) == -1
) {
return originalURL; // We keep the original URL
}
// Return the archived entry.
return `https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-archive-stable-channel#${id}`;
};
/**
* getFutureReleaseDate - Read the future release date
* @param release The release number of the version
* @param releaseScheduleURL The url of the MD file having it
* @returns The date in the YYYY-MM-DD format
* Throws a string in case of an error
*/
const getFutureReleaseDate = async (release, releaseScheduleURL) => {
// Fetch the MD of the release schedule
const scheduleMD = await fetch(releaseScheduleURL);
const text = await scheduleMD.text();
if (!text) {
throw chalk`{red \nRelease file not found.}`;
}
// Find the line
//const regexp = new RegExp(`| ${release} |\\w*|\\w*| ?Week of (\\w*) ?|\\w*|`, 'i');
const regexp = new RegExp(
`\\| ${release} \\|.*\\|.*\\| ?Week of (.*) ?\\|.*\\|`,
'i',
);
const result = text.match(regexp);
if (!result) {
throw chalk`{yellow \nRelease date not found for Edge ${release}.}`;
}
const releaseDateText = result[1];
// Get the date from the file
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
const year = releaseDateText.substring(7, 11);
const month = months.indexOf(releaseDateText.substring(3, 6)) + 1;
const day = releaseDateText.substring(0, 2);
const releaseDate = new Date(`${year}-${month}-${day}Z`);
return releaseDate.toISOString().substring(0, 10); // Remove the time part
};
/**
* getReleaseNotesURL - Guess the URL of the release notes
* @param status The status of the release
* @param fullRelease The release of the release
* @param date The date of the release
* @returns The URL of the release notes or the empty string if not found
* Throws a string in case of error
*/
const getReleaseNotesURL = async (status, fullRelease, date) => {
// If the status isn't stable, do not give back any release notes.
if (status !== 'Stable') {
return '';
}
// Calculate the URL
const releaseStr = fullRelease.replace(/\./g, '');
const month = [
'january',
'february',
'march',
'april',
'may',
'june',
'july',
'august',
'september',
'october',
'november',
'december',
];
const dateObj = new Date(date);
const dateStr = `${
month[dateObj.getMonth()]
}-${dateObj.getDate()}-${dateObj.getFullYear()}`;
// Check if the id exists
if (
releaseNotesText.indexOf(`<h2 id="version-${releaseStr}-${dateStr}">`) == -1
) {
// Section not found -> log a warning
throw chalk`{red \nSection not found in official release notes for Edge ${fullRelease}}`;
}
return `https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel#version-${releaseStr}-${dateStr}`;
};
/**
* updateEdgeReleases - Update the json file listing the browser releases of an Edge browser
* @param options The list of options for Edge.
* @returns The log of what has been generated (empty if nothing)
*/
export const updateEdgeReleases = async (options) => {
//
// Read the release note files
//
let result = await initReleaseNoteFiles();
//
// Get the JSON with the versions from edge releases
//
const buffer = await fetch(options.edgeupdatesURL);
const edgeVersions = JSON.parse(await buffer.text());
//
// Get the edge.json from the local BCD
//
const file = fs.readFileSync(`${options.bcdFile}`);
const edgeBCD = JSON.parse(file.toString());
//
// Update the three channels
//
const channels = new Map([
['current', options.releaseBranch],
['beta', options.betaBranch],
['nightly', options.nightlyBranch],
]);
// Information store to be used outside the for loop
const data = {};
for (const [key, value] of channels) {
//
// Extract the useful data
//
// Find the right entry
const entry = edgeVersions.find((e) => e['Product'] === value);
// Get the last version
const version = parseFloat(entry['Releases'][0]['ProductVersion']);
// We need to find the first release with this version.
// As, they are from the newer to the older in the JSON object,
// we are looking for the first that is not the latest version
// and get the one immediately above (the first release of that
// version)
//
// The full version number is needed for the release note URL
// as well as the date
let id = 1;
while (
id < entry['Releases'].length &&
parseFloat(entry['Releases'][id]['ProductVersion']) === version
) {
id++;
}
id--;
data[value] = {};
data[value].version = parseFloat(entry['Releases'][id]['ProductVersion']);
data[value].fullVersion = entry['Releases'][id]['ProductVersion'];
//
// Update the JSON in memory
//
// We skip beta and nightly versions if they are of the same version as the released one
if (
key === 'current' ||
(key !== 'current' &&
data[value].version !== data[channels.get('current')].version)
) {
// NOTE: the published date is the one of the actual release
// (being the Stable, Beta, or Nightly),
// and not the one of the future release like we would like
// So we only get it if we are on the 'current' channel.
// Get published date
if (key === 'current') {
data[value].versionDate = entry['Releases'][id][
'PublishedTime'
].substring(0, 10); // Remove the time part;
} else {
try {
data[value].versionDate = await getFutureReleaseDate(
data[value].version,
options.releaseScheduleURL,
);
} catch (str) {
result += str;
}
}
// Get the release notes
let releaseNotesURL = '';
try {
releaseNotesURL = await getReleaseNotesURL(
value,
data[value].fullVersion,
data[value].versionDate,
);
} catch (s) {
result += s;
}
if (
edgeBCD.browsers[options.bcdBrowserName].releases[data[value].version]
) {
// The entry already exists
result += updateBrowserEntry(
edgeBCD,
options.bcdBrowserName,
data[value].version,
data[value]?.versionDate,
key,
releaseNotesURL,
'',
);
} else {
// New entry
result += newBrowserEntry(
edgeBCD,
options.bcdBrowserName,
data[value].version,
key,
options.browserEngine,
data[value]?.versionDate,
releaseNotesURL,
data[value].version,
);
}
}
}
//
// Check that all older releases are 'retired'
//
for (
let i = options.firstRelease;
i < data[options.releaseBranch].version;
i++
) {
if (!options.skippedReleases.includes(i)) {
if (edgeBCD.browsers[options.bcdBrowserName].releases[i.toString()]) {
result += updateBrowserEntry(
edgeBCD,
options.bcdBrowserName,
i.toString(),
edgeBCD.browsers[options.bcdBrowserName].releases[i.toString()]
.release_date,
'retired',
updateReleaseNotesIfArchived(
edgeBCD.browsers[options.bcdBrowserName].releases[i.toString()]
.release_notes,
),
'',
);
} else {
// There is a retired version missing. Edgeupdates doesn't list them.
// There is an oddity: the version is not skipped but not in edgeupdates
result += chalk`{yellow \nEdge ${i} not found in Edgeupdates! Add it manually or add an exception.}`;
}
}
}
//
// Add a planned version entry
//
const planned = (data[options.nightlyBranch].version + 1).toString();
let releaseDate;
try {
releaseDate = await getFutureReleaseDate(
planned,
options.releaseScheduleURL,
);
} catch (s) {
result += s;
}
if (edgeBCD.browsers[options.bcdBrowserName].releases[planned]) {
result += updateBrowserEntry(
edgeBCD,
options.bcdBrowserName,
planned,
releaseDate,
'planned',
'',
planned,
);
} else {
// New entry
result += newBrowserEntry(
edgeBCD,
options.bcdBrowserName,
planned.toString(),
'planned',
options.browserEngine,
releaseDate,
'',
planned.toString(),
);
}
//
// Write the update browser's json to file
//
fs.writeFileSync(`./${options.bcdFile}`, stringify(edgeBCD) + '\n');
// Returns the log
if (result) {
result = `### Updates for ${options.browserName}${result}`;
}
return result;
};