-
-
Notifications
You must be signed in to change notification settings - Fork 671
Expand file tree
/
Copy pathgenerateReleasePlan.mjs
More file actions
executable file
·85 lines (72 loc) · 3.46 KB
/
Copy pathgenerateReleasePlan.mjs
File metadata and controls
executable file
·85 lines (72 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/usr/bin/env node
// Generate Release Plan table for a given release line.
// Usage:
// node generateReleasePlan.mjs xx
// or
// node generateReleasePlan.mjs path/to/CHANGELOG_Vxx.md
import { open, readFile } from 'node:fs/promises';
const schedule = JSON.parse(await readFile(new URL('./schedule.json', import.meta.url), 'utf-8'));
let [, , majorVersion] = process.argv;
const tableHeader = `
Version | Release Date | Releaser(s)
------- | ------------ | -----------`;
console.log(`_Draft schedule - all dates subject to change_\n${tableHeader}`);
function outputFutureReleaseSchedule(majorVersion, lastReleaseDate) {
const versionKey = `v${majorVersion}`;
if (!Object.hasOwn(schedule, versionKey)) {
throw new Error(`Unknown version ${majorVersion}, accepted values are ${Object.keys(schedule)}`);
}
const { start, maintenance, lts } = schedule[versionKey];
const ltsDateTime = lts && Temporal.PlainDate.from(lts);
const maintenanceDateTime = Temporal.PlainDate.from(maintenance);
const isNowDuringLTS = lts && Temporal.PlainDate.compare(Temporal.Now.plainDateTimeISO(), ltsDateTime) > 0;
const weeks = isNowDuringLTS ? 4 : 2;
const startOfCycle =
lastReleaseDate?.add({ weeks }) ?? (isNowDuringLTS ? ltsDateTime : Temporal.PlainDate.from(start));
const endOfCycle = isNowDuringLTS ? maintenanceDateTime : ltsDateTime || maintenanceDateTime;
for (let i = startOfCycle; Temporal.PlainDate.compare(i, endOfCycle) === -1; i = i.add({ weeks })) {
console.log(` ${majorVersion}.x.x | ${i} | `);
}
if (lts && (!lastReleaseDate || Temporal.PlainDate.compare(maintenance, lastReleaseDate) > 0)) {
console.log(
`${majorVersion}.x.x (${isNowDuringLTS ? 'Maintenance' : 'LTS'} transition) | ${
isNowDuringLTS ? maintenance : lts
} | ${isNowDuringLTS ? '_No release_' : ''}`,
);
}
}
if (isNaN(majorVersion)) {
let hasOutputFutureScheduleYet = false;
const existingReleases = [];
const releaseTitle = /^## (\d{4}-\d{2}-\d{2}), Version (\d+\.\d+\.\d+) ([^,]+), (.+)$/;
const securityRelease = /^This is a security release\.$/;
const fd = await open(majorVersion, 'r');
let releaseType, lastReleaseDate;
for await (const line of fd.readLines()) {
const releaseTitleMatch = releaseTitle.exec(line);
if (releaseTitleMatch) {
const [, date, version, type, releasers] = releaseTitleMatch;
lastReleaseDate ??= Temporal.PlainDate.from(date);
const wasLTSTransition = releaseType && type === '(Current)' && type !== releaseType;
if (wasLTSTransition) {
const majorVersion = existingReleases[0].slice(1, existingReleases[0].indexOf('.'));
existingReleases[0] = existingReleases[0].replace('|', '(LTS transition) |');
console.log(existingReleases.splice(0, Infinity, '', '</details>').join('\n'));
outputFutureReleaseSchedule(majorVersion, lastReleaseDate);
hasOutputFutureScheduleYet = true;
console.log('\n\n<details><summary>Current</summary>\n\n' + tableHeader);
}
existingReleases.unshift(` ${version} | ${date} | ${releasers}`);
releaseType = type;
} else if (securityRelease.test(line)) {
existingReleases[0] = existingReleases[0].replace('|', '(Security) |');
}
}
console.log(existingReleases.join('\n'));
if (!hasOutputFutureScheduleYet) {
const majorVersion = existingReleases[0].slice(1, existingReleases[0].indexOf('.'));
outputFutureReleaseSchedule(majorVersion, lastReleaseDate);
}
} else {
outputFutureReleaseSchedule(majorVersion);
}