Skip to content

Commit 3638075

Browse files
committed
feat: handle alpha versions with minimal tags
1 parent 43463fd commit 3638075

2 files changed

Lines changed: 126 additions & 120 deletions

File tree

‎stackbrew.mjs‎

Lines changed: 126 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
#!/usr/bin/env node
22

33
import { readFileSync } from 'node:fs';
4-
import { basename, join } from 'node:path';
4+
import { basename } from 'node:path';
55
import { execSync } from 'node:child_process';
66

77
// Grab last git commit
88
function getCommitHasForPath(path) {
99
return execSync(`git log -1 --format=%H HEAD -- ${path}`).toString().trim();
1010
}
1111

12+
// Grab nodejs/Release values as the authoritative start/maintenance/end dates
13+
const releaseTimes = await fetch(
14+
'https://raw.githubusercontent.com/nodejs/Release/refs/heads/main/schedule.json',
15+
);
16+
const releaseJson = await releaseTimes.json();
17+
18+
// Grab the nodejs.org index.json as the authoritative Codename values, since that can lag the Release repo JSON
19+
const availableVersions = await fetch(
20+
'https://nodejs.org/download/release/index.json',
21+
);
22+
const officialIndexJson = await availableVersions.json();
23+
1224
const stackbrewPath = basename(import.meta.filename);
1325

1426
// Header
@@ -21,7 +33,7 @@ GitFetch: refs/heads/main\n`;
2133
// Loop versions
2234
const config = JSON.parse(readFileSync('versions.json'));
2335

24-
const versions = Object.keys(config).reverse();
36+
const localVersions = Object.keys(config).reverse();
2537

2638
let midnight = new Date();
2739
midnight.setHours(0, 0, 0, 0);
@@ -31,126 +43,140 @@ const slimRE = new RegExp(/\*-slim/);
3143
let foundLTS = false;
3244
let foundCurrent = false;
3345

34-
for (const version of versions) {
35-
let lts = new Date(`${config[version].lts}T00:00:00.00`).getTime();
36-
let isCurrent = foundCurrent ? false : isNaN(lts) || lts >= now;
46+
for (const majorVersion of localVersions) {
47+
let releaseRepoInfo = releaseJson[`v${majorVersion}`];
48+
let nodejsOrgReleaseInfo = officialIndexJson.find((indexVersion) =>
49+
indexVersion.version.startsWith(`v${majorVersion}`),
50+
);
51+
let lts = new Date(`${releaseRepoInfo.lts}T00:00:00.00`).getTime();
52+
// First Alpha build is the only active one, and when the "start" of the release hasn't cut over
53+
let isAlpha = nodejsOrgReleaseInfo.version.includes('alpha');
54+
// The Current/Latest release is the first non-Alpha major
55+
let isCurrent = foundCurrent ? false : !isAlpha;
3756
foundCurrent = isCurrent || foundCurrent;
57+
// The LTS tag is only set on the most recent LTS release
3858
let isLTS = foundLTS ? false : now >= lts;
3959
foundLTS = isLTS || foundLTS;
40-
let codename = config[version].codename;
41-
let defaultAlpine = config[version]['alpine-default'];
42-
let defaultDebian = config[version]['debian-default'];
43-
let variants = config[version].variants;
60+
let codename = nodejsOrgReleaseInfo.lts
61+
? nodejsOrgReleaseInfo.lts.toLowerCase()
62+
: '';
63+
let defaultAlpine = config[majorVersion]['alpine-default'];
64+
let defaultDebian = config[majorVersion]['debian-default'];
65+
let variants = config[majorVersion].variants;
4466
for (const variant in variants) {
45-
let dockerfilePath = join(version, variant, 'Dockerfile');
46-
let isAlpine = alpineRE.test(variant);
47-
let isSlim = slimRE.test(variant);
48-
let isDefaultSlim = new RegExp(`${defaultDebian}-slim`).test(variant);
49-
50-
// Get full version from the Dockerfile
51-
let dockerfile = readFileSync(dockerfilePath, 'utf-8');
52-
let fullVersion = dockerfile.match(
53-
/ENV NODE_VERSION=(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/,
54-
);
55-
56-
let tags = [
57-
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-${variant}`,
58-
`${fullVersion.groups.major}.${fullVersion.groups.minor}-${variant}`,
59-
`${fullVersion.groups.major}-${variant}`,
60-
];
61-
62-
if (codename) {
63-
tags.push(`${codename}-${variant}`);
64-
}
65-
66-
if (variant === defaultAlpine) {
67-
tags.push(
68-
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-alpine`,
67+
let tags;
68+
69+
if (isAlpha) {
70+
// Only push explicit variants during the Alpha phase
71+
tags = [`${nodejsOrgReleaseInfo.version.substring(1)}-${variant}`];
72+
} else {
73+
let isAlpine = alpineRE.test(variant);
74+
let isSlim = slimRE.test(variant);
75+
let isDefaultSlim = new RegExp(`${defaultDebian}-slim`).test(variant);
76+
let fullVersion = nodejsOrgReleaseInfo.version.match(
77+
/v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/,
6978
);
70-
tags.push(
71-
`${fullVersion.groups.major}.${fullVersion.groups.minor}-alpine`,
72-
);
73-
tags.push(`${fullVersion.groups.major}-alpine`);
74-
if (codename) {
75-
tags.push(`${codename}-alpine`);
76-
}
77-
}
79+
tags = [
80+
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-${variant}`,
81+
`${fullVersion.groups.major}.${fullVersion.groups.minor}-${variant}`,
82+
`${fullVersion.groups.major}-${variant}`,
83+
];
7884

79-
if (variant === defaultDebian) {
80-
tags.push(
81-
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}`,
82-
);
83-
tags.push(`${fullVersion.groups.major}.${fullVersion.groups.minor}`);
84-
tags.push(`${fullVersion.groups.major}`);
85-
if (isSlim) {
86-
tags.push(
87-
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-slim`,
88-
);
89-
tags.push(
90-
`${fullVersion.groups.major}.${fullVersion.groups.minor}-slim`,
91-
);
92-
tags.push(`${fullVersion.groups.major}-slim`);
93-
}
94-
if (codename) {
95-
tags.push(`${codename}`);
96-
}
97-
}
98-
if (isDefaultSlim) {
99-
tags.push(
100-
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-slim`,
101-
);
102-
tags.push(`${fullVersion.groups.major}.${fullVersion.groups.minor}-slim`);
103-
tags.push(`${fullVersion.groups.major}-slim`);
10485
if (codename) {
105-
tags.push(`${codename}-slim`);
86+
tags.push(`${codename}-${variant}`);
10687
}
107-
}
10888

109-
if (isCurrent) {
11089
if (variant === defaultAlpine) {
111-
tags.push(variant);
11290
tags.push(
11391
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-alpine`,
11492
);
11593
tags.push(
11694
`${fullVersion.groups.major}.${fullVersion.groups.minor}-alpine`,
11795
);
11896
tags.push(`${fullVersion.groups.major}-alpine`);
119-
tags.push('alpine');
120-
tags.push('current-alpine');
121-
}
122-
if (variant === defaultDebian) {
123-
tags.push(variant);
124-
tags.push('latest');
125-
tags.push('current');
126-
}
127-
if (isAlpine) {
128-
tags.push(`${variant}`);
129-
tags.push(`current-${variant}`);
130-
}
131-
if (!isAlpine) {
132-
tags.push(`${variant}`);
133-
tags.push(`current-${variant}`);
134-
}
135-
if (isDefaultSlim) {
136-
tags.push('slim');
137-
tags.push('current-slim');
97+
if (codename) {
98+
tags.push(`${codename}-alpine`);
99+
}
138100
}
139-
}
140101

141-
if (isLTS) {
142-
tags.push(`lts-${variant}`);
143102
if (variant === defaultDebian) {
144-
tags.push('lts');
103+
tags.push(
104+
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}`,
105+
);
106+
tags.push(`${fullVersion.groups.major}.${fullVersion.groups.minor}`);
107+
tags.push(`${fullVersion.groups.major}`);
108+
if (isSlim) {
109+
tags.push(
110+
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-slim`,
111+
);
112+
tags.push(
113+
`${fullVersion.groups.major}.${fullVersion.groups.minor}-slim`,
114+
);
115+
tags.push(`${fullVersion.groups.major}-slim`);
116+
}
145117
if (codename) {
146-
tags.push(`lts-${codename}`);
118+
tags.push(`${codename}`);
147119
}
148120
}
149121
if (isDefaultSlim) {
150-
tags.push(`lts-slim`);
122+
tags.push(
123+
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-slim`,
124+
);
125+
tags.push(
126+
`${fullVersion.groups.major}.${fullVersion.groups.minor}-slim`,
127+
);
128+
tags.push(`${fullVersion.groups.major}-slim`);
129+
if (codename) {
130+
tags.push(`${codename}-slim`);
131+
}
151132
}
152-
if (variant === defaultAlpine) {
153-
tags.push(`lts-alpine`);
133+
134+
if (isCurrent) {
135+
if (variant === defaultAlpine) {
136+
tags.push(variant);
137+
tags.push(
138+
`${fullVersion.groups.major}.${fullVersion.groups.minor}.${fullVersion.groups.patch}-alpine`,
139+
);
140+
tags.push(
141+
`${fullVersion.groups.major}.${fullVersion.groups.minor}-alpine`,
142+
);
143+
tags.push(`${fullVersion.groups.major}-alpine`);
144+
tags.push('alpine');
145+
tags.push('current-alpine');
146+
}
147+
if (variant === defaultDebian) {
148+
tags.push(variant);
149+
tags.push('latest');
150+
tags.push('current');
151+
}
152+
if (isAlpine) {
153+
tags.push(`${variant}`);
154+
tags.push(`current-${variant}`);
155+
}
156+
if (!isAlpine) {
157+
tags.push(`${variant}`);
158+
tags.push(`current-${variant}`);
159+
}
160+
if (isDefaultSlim) {
161+
tags.push('slim');
162+
tags.push('current-slim');
163+
}
164+
}
165+
166+
if (isLTS) {
167+
tags.push(`lts-${variant}`);
168+
if (variant === defaultDebian) {
169+
tags.push('lts');
170+
if (codename) {
171+
tags.push(`lts-${codename}`);
172+
}
173+
}
174+
if (isDefaultSlim) {
175+
tags.push(`lts-slim`);
176+
}
177+
if (variant === defaultAlpine) {
178+
tags.push(`lts-alpine`);
179+
}
154180
}
155181
}
156182

@@ -159,9 +185,9 @@ Tags: ${tags
159185
.filter((x, i, a) => a.indexOf(x) == i)
160186
.sort()
161187
.join(', ')}
162-
Architectures: ${config[version].variants[variant].join(', ')}
163-
GitCommit: ${getCommitHasForPath(`${version}/${variant}`)}
164-
Directory: ${`${version}/${variant}`}\n`;
188+
Architectures: ${config[majorVersion].variants[variant].join(', ')}
189+
GitCommit: ${getCommitHasForPath(`${majorVersion}/${variant}`)}
190+
Directory: ${`${majorVersion}/${variant}`}\n`;
165191
}
166192
}
167193

‎versions.json‎

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
{
22
"27": {
3-
"alpha": "2026-10-28",
4-
"lts": "2027-10-20",
5-
"start": "2027-04-22",
6-
"end": "2030-04-30",
7-
"codename": "",
83
"alpine-default": "alpine3.24",
94
"debian-default": "trixie",
105
"variants": {
@@ -41,11 +36,6 @@
4136
}
4237
},
4338
"26": {
44-
"start": "2026-05-05",
45-
"lts": "2026-10-28",
46-
"maintenance": "2027-10-20",
47-
"end": "2029-04-30",
48-
"codename": "lithium",
4939
"alpine-default": "alpine3.24",
5040
"debian-default": "trixie",
5141
"variants": {
@@ -82,11 +72,6 @@
8272
}
8373
},
8474
"24": {
85-
"start": "2025-05-06",
86-
"lts": "2025-10-28",
87-
"maintenance": "2026-10-20",
88-
"end": "2028-04-30",
89-
"codename": "krypton",
9075
"alpine-default": "alpine3.24",
9176
"debian-default": "bookworm",
9277
"variants": {
@@ -125,11 +110,6 @@
125110
}
126111
},
127112
"22": {
128-
"start": "2024-04-23",
129-
"lts": "2024-10-29",
130-
"maintenance": "2025-10-21",
131-
"end": "2027-04-30",
132-
"codename": "jod",
133113
"alpine-default": "alpine3.24",
134114
"debian-default": "bookworm",
135115
"variants": {

0 commit comments

Comments
 (0)