Skip to content

Commit f016220

Browse files
authored
Merge pull request #254 from SolidOS/release
Update Release Orchestrator
2 parents 4e068ff + 22abc5f commit f016220

3 files changed

Lines changed: 283 additions & 28 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"defaultBranch": "main",
3+
"skipIfNoDiff": true,
4+
"defaultInstall": "npm install",
5+
"defaultTest": "npm test",
6+
"defaultBuild": "npm run build",
7+
"modes": {
8+
"stable": {
9+
"branch": "main",
10+
"versionBump": "patch",
11+
"npmTag": "latest",
12+
"gitTag": true,
13+
"gitPush": true
14+
},
15+
"test": {
16+
"branch": "dev",
17+
"versionBump": "prerelease",
18+
"preid": "test",
19+
"npmTag": "test",
20+
"gitTag": false,
21+
"gitPush": false
22+
}
23+
},
24+
"repos": [
25+
{
26+
"name": "solid-ui",
27+
"path": "workspaces/solid-ui",
28+
"repo": "https://github.com/SolidOS/solid-ui.git",
29+
"afterInstall": [
30+
"npm install pane-registry@latest solid-logic@latest rdflib@latest solid-namespace@latest"
31+
]
32+
},
33+
{
34+
"name": "contacts-pane",
35+
"path": "workspaces/contacts-pane",
36+
"repo": "https://github.com/SolidOS/contacts-pane.git",
37+
"afterInstall": [
38+
"npm install solid-ui solid-logic@latest rdflib@latest"
39+
]
40+
},
41+
{
42+
"name": "profile-pane",
43+
"path": "workspaces/profile-pane",
44+
"repo": "https://github.com/SolidOS/profile-pane.git",
45+
"afterInstall": [
46+
"npm install chat-pane@latest contacts-pane solid-ui pane-registry@latest solid-logic@latest rdflib@latest"
47+
]
48+
},
49+
{
50+
"name": "solid-panes",
51+
"path": "workspaces/solid-panes",
52+
"repo": "https://github.com/SolidOS/solid-panes.git",
53+
"afterInstall": [
54+
"npm install activitystreams-pane@latest chat-pane@latest contacts-pane folder-pane@latest issue-pane@latest meeting-pane@latest pane-registry@latest profile-pane source-pane@latest solid-ui solid-logic@latest solid-namespace@latest rdflib@latest"
55+
]
56+
},
57+
{
58+
"name": "mashlib",
59+
"path": "workspaces/mashlib",
60+
"repo": "https://github.com/SolidOS/mashlib.git",
61+
"afterInstall": [
62+
"npm install solid-panes solid-ui solid-logic@latest rdflib@latest"
63+
]
64+
}
65+
]
66+
}

scripts/release

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set -e
33

44
unset PREFIX npm_config_prefix
5-
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
5+
export NVM_DIR="$HOME/.nvm"
66
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
77

88
function gitCheckoutMainOf {
@@ -81,8 +81,9 @@ updateRepo folder-pane # folder-pane (solid-ui, solid-logic)
8181
updateRepo issue-pane # issue-pane (solid-ui, pane-registry, rdflib)
8282
updateRepo meeting-pane # meeting-pane (solid-ui, solid-logic, rdflib)
8383
updateRepo contacts-pane # contacts-pane (solid-ui, pane-registry, solid-logic, rdflib)
84-
updateRepo profile-pane # profile-pane (chat-pane, solid-ui, pane-registry, solid-logic, rdflib)
84+
updateRepo profile-pane # profile-pane (contacts-pane, solid-ui, pane-registry, solid-logic, rdflib)
8585
updateRepo source-pane # source-pane (solid-ui)
86+
updateRepo folder-pane # folder-pane (solid-ui, solid-logic)
8687
updateRepo solid-panes # solid-panes (chat-pane, contacts-pane, folder-pane, issue-pane, meeting-pane, pane-registry, rdflib, solid-ui, source-pane)
8788

8889
updateRepo mashlib # mashlib (rdflib, solid-panes, solid-ui)

scripts/release-orchestrator.js

Lines changed: 214 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -223,34 +223,219 @@ function publishStable(repoDir, modeConfig, dryRun) {
223223

224224
function publishTest(repoDir, modeConfig, dryRun) {
225225
const preid = modeConfig.preid || 'test';
226-
const originalScripts = disableVersionScripts(repoDir);
227-
try {
228-
run(`npm version prerelease --preid ${preid} --no-git-tag-version`, repoDir, dryRun);
229-
} finally {
230-
restoreVersionScripts(repoDir, originalScripts);
231-
}
232-
233226
const pkg = getPackageJson(repoDir);
234227
const name = pkg ? pkg.name : null;
235-
let version = getPackageVersion(repoDir);
236-
let attempts = 0;
237-
const maxAttempts = 5;
238-
239-
if (!dryRun && name) {
240-
while (attempts < maxAttempts && packageVersionExists(name, version, repoDir)) {
241-
console.log(`Version ${version} already published. Bumping prerelease...`);
242-
const retryOriginalScripts = disableVersionScripts(repoDir);
243-
try {
244-
run(`npm version prerelease --preid ${preid} --no-git-tag-version`, repoDir, dryRun);
245-
} finally {
246-
restoreVersionScripts(repoDir, retryOriginalScripts);
228+
let localVersion = pkg ? pkg.version : null;
229+
230+
console.log(`Local package.json version: ${localVersion}`);
231+
232+
// Get the latest @test version from npm FIRST to understand what's already published
233+
let latestTestVersion = null;
234+
if (name) {
235+
try {
236+
const result = runQuiet(`npm view ${name}@${preid} version`, repoDir);
237+
if (result && result.trim()) {
238+
latestTestVersion = result.trim();
239+
console.log(`Latest published @${preid} version: ${latestTestVersion}`);
240+
}
241+
} catch (err) {
242+
// No @test version published yet, that's fine
243+
console.log(`No @${preid} version published yet`);
244+
}
245+
}
246+
247+
// Get the latest stable version from npm
248+
let latestStableVersion = null;
249+
if (name) {
250+
try {
251+
const result = runQuiet(`npm view ${name}@latest version`, repoDir);
252+
if (result && result.trim()) {
253+
latestStableVersion = result.trim();
254+
console.log(`Latest published @latest version: ${latestStableVersion}`);
247255
}
248-
version = getPackageVersion(repoDir);
249-
attempts += 1;
256+
} catch (err) {
257+
console.log(`No @latest version published yet`);
250258
}
259+
}
251260

252-
if (attempts === maxAttempts && packageVersionExists(name, version, repoDir)) {
253-
throw new Error(`Unable to find an unpublished prerelease version after ${maxAttempts} attempts.`);
261+
let version;
262+
263+
// Strategy: Always compute baseVersion from the latest published @test version first
264+
// This ensures we're incrementing from what npm sees, not what our local checkout has
265+
if (latestTestVersion) {
266+
// Extract the base version and counter from the latest published @test version
267+
// Match pattern 1: X.Y.Z-preid.N (standard test version with counter)
268+
// Match pattern 2: X.Y.Z (just a version, no test suffix yet)
269+
const regexPattern = `^(\\d+\\.\\d+\\.\\d+)(?:.*)?-${preid.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.(\\d+)$`;
270+
console.log(`[DEBUG] Testing regex: ${regexPattern}`);
271+
console.log(`[DEBUG] Against version: ${latestTestVersion}`);
272+
const counterMatch = latestTestVersion.match(new RegExp(regexPattern));
273+
console.log(`[DEBUG] Regex match result:`, counterMatch);
274+
275+
if (counterMatch && counterMatch[1]) {
276+
// Found a version with -test.N pattern
277+
const publishedBaseVersion = counterMatch[1];
278+
const publishedCounter = parseInt(counterMatch[2], 10);
279+
console.log(`Base version from published @${preid}: ${publishedBaseVersion}, counter: ${publishedCounter}`);
280+
281+
// Check if the base version from @test matches the latest stable
282+
// If yes, we need to bump to the next patch version for the test
283+
if (latestStableVersion && publishedBaseVersion === latestStableVersion) {
284+
console.log(`Base version ${publishedBaseVersion} matches latest stable. Bumping to next patch for test...`);
285+
// Increment patch version and start at -test.0
286+
const versionParts = publishedBaseVersion.split('.');
287+
if (versionParts.length >= 3) {
288+
versionParts[2] = String(parseInt(versionParts[2], 10) + 1);
289+
const newBaseVersion = versionParts.join('.');
290+
version = `${newBaseVersion}-${preid}.0`;
291+
console.log(`Bumping to ${version}...`);
292+
} else {
293+
// Fallback to normal bump if version format is unexpected
294+
console.log(`Unexpected version format. Doing normal prerelease bump...`);
295+
const originalScripts = disableVersionScripts(repoDir);
296+
try {
297+
run(`npm version prerelease --preid ${preid} --no-git-tag-version`, repoDir, dryRun);
298+
} finally {
299+
restoreVersionScripts(repoDir, originalScripts);
300+
}
301+
version = getPackageVersion(repoDir);
302+
}
303+
304+
// Update package.json manually with this version
305+
if (version && !dryRun) {
306+
const pkgPath = path.join(repoDir, 'package.json');
307+
const pkgData = readJson(pkgPath);
308+
pkgData.version = version;
309+
fs.writeFileSync(pkgPath, JSON.stringify(pkgData, null, 2) + '\n');
310+
}
311+
} else {
312+
// Base version doesn't match stable, increment the test counter
313+
const nextCounter = parseInt(counterMatch[2], 10) + 1;
314+
version = `${publishedBaseVersion}-${preid}.${nextCounter}`;
315+
console.log(`Latest @${preid} is ${latestTestVersion}. Incrementing to ${version}...`);
316+
317+
// Update package.json manually with this version
318+
const pkgPath = path.join(repoDir, 'package.json');
319+
const pkgData = readJson(pkgPath);
320+
pkgData.version = version;
321+
fs.writeFileSync(pkgPath, JSON.stringify(pkgData, null, 2) + '\n');
322+
}
323+
} else {
324+
// Couldn't find exact -test.N pattern, but we do have a @test version
325+
// Extract just the X.Y.Z base from latestTestVersion
326+
const baseVersionMatch = latestTestVersion.match(/^(\d+\.\d+\.\d+)/);
327+
if (baseVersionMatch && baseVersionMatch[1]) {
328+
const publishedBaseVersion = baseVersionMatch[1];
329+
console.log(`Found @${preid} version but no -${preid}.N pattern. Extracted base: ${publishedBaseVersion}`);
330+
331+
// Check if this base version matches the latest stable
332+
if (latestStableVersion && publishedBaseVersion === latestStableVersion) {
333+
console.log(`Base version ${publishedBaseVersion} matches latest stable. Bumping to next patch for test...`);
334+
const versionParts = publishedBaseVersion.split('.');
335+
if (versionParts.length >= 3) {
336+
versionParts[2] = String(parseInt(versionParts[2], 10) + 1);
337+
const newBaseVersion = versionParts.join('.');
338+
version = `${newBaseVersion}-${preid}.0`;
339+
console.log(`Bumping to ${version}...`);
340+
} else {
341+
console.log(`Unexpected version format. Doing normal prerelease bump...`);
342+
const originalScripts = disableVersionScripts(repoDir);
343+
try {
344+
run(`npm version prerelease --preid ${preid} --no-git-tag-version`, repoDir, dryRun);
345+
} finally {
346+
restoreVersionScripts(repoDir, originalScripts);
347+
}
348+
version = getPackageVersion(repoDir);
349+
}
350+
351+
// Update package.json manually with this version
352+
if (version && !dryRun) {
353+
const pkgPath = path.join(repoDir, 'package.json');
354+
const pkgData = readJson(pkgPath);
355+
pkgData.version = version;
356+
fs.writeFileSync(pkgPath, JSON.stringify(pkgData, null, 2) + '\n');
357+
}
358+
} else {
359+
// Base version doesn't match stable, use this version as starting point for -test
360+
version = `${publishedBaseVersion}-${preid}.0`;
361+
console.log(`Base version ${publishedBaseVersion} differs from stable. Starting test version at ${version}...`);
362+
363+
// Update package.json manually with this version
364+
if (!dryRun) {
365+
const pkgPath = path.join(repoDir, 'package.json');
366+
const pkgData = readJson(pkgPath);
367+
pkgData.version = version;
368+
fs.writeFileSync(pkgPath, JSON.stringify(pkgData, null, 2) + '\n');
369+
}
370+
}
371+
} else {
372+
// Couldn't parse base version at all, do normal prerelease bump
373+
console.log(`Found @${preid} (${latestTestVersion}) but couldn't parse base version. Doing normal prerelease bump...`);
374+
const originalScripts = disableVersionScripts(repoDir);
375+
try {
376+
run(`npm version prerelease --preid ${preid} --no-git-tag-version`, repoDir, dryRun);
377+
} finally {
378+
restoreVersionScripts(repoDir, originalScripts);
379+
}
380+
version = getPackageVersion(repoDir);
381+
if (dryRun) {
382+
console.log(`[dry-run simulation] Prerelease version would be: ${version}`);
383+
} else {
384+
console.log(`After prerelease bump: ${version}`);
385+
}
386+
}
387+
}
388+
} else {
389+
// No existing @test version, but we can be smart about versioning
390+
console.log(`No @${preid} version found. Determining strategy for first test release...`);
391+
392+
// Extract base version from local version (just X.Y.Z, strip any pre-release identifiers)
393+
const localBaseMatch = localVersion.match(/^(\d+\.\d+\.\d+)/);
394+
const localBaseVersion = localBaseMatch ? localBaseMatch[1] : localVersion;
395+
console.log(`Local base version: ${localBaseVersion}`);
396+
397+
if (latestStableVersion && localBaseVersion === latestStableVersion) {
398+
// Local version base matches latest stable, so bump patch and start at -test.0
399+
console.log(`Local base ${localBaseVersion} matches latest stable. Bumping to next patch for test...`);
400+
const versionParts = localBaseVersion.split('.');
401+
if (versionParts.length >= 3) {
402+
versionParts[2] = String(parseInt(versionParts[2], 10) + 1);
403+
const newBaseVersion = versionParts.join('.');
404+
version = `${newBaseVersion}-${preid}.0`;
405+
console.log(`Using ${version} for test release...`);
406+
407+
if (!dryRun) {
408+
const pkgPath = path.join(repoDir, 'package.json');
409+
const pkgData = readJson(pkgPath);
410+
pkgData.version = version;
411+
fs.writeFileSync(pkgPath, JSON.stringify(pkgData, null, 2) + '\n');
412+
}
413+
} else {
414+
// Fallback to normal prerelease bump if format is unexpected
415+
console.log(`Unexpected version format. Doing normal prerelease bump...`);
416+
const originalScripts = disableVersionScripts(repoDir);
417+
try {
418+
run(`npm version prerelease --preid ${preid} --no-git-tag-version`, repoDir, dryRun);
419+
} finally {
420+
restoreVersionScripts(repoDir, originalScripts);
421+
}
422+
version = getPackageVersion(repoDir);
423+
}
424+
} else {
425+
// Local version is newer than stable (or no stable exists), use it with -test.0
426+
version = `${localBaseVersion}-${preid}.0`;
427+
console.log(`Local base ${localBaseVersion} is ahead of stable. Starting test at ${version}...`);
428+
429+
if (!dryRun) {
430+
const pkgPath = path.join(repoDir, 'package.json');
431+
const pkgData = readJson(pkgPath);
432+
pkgData.version = version;
433+
fs.writeFileSync(pkgPath, JSON.stringify(pkgData, null, 2) + '\n');
434+
}
435+
}
436+
437+
if (dryRun) {
438+
console.log(`[dry-run simulation] Test version would be: ${version}`);
254439
}
255440
}
256441

@@ -325,9 +510,12 @@ function main() {
325510
continue;
326511
}
327512

328-
const { behind, ahead } = getAheadBehind(repoDir, branch);
329-
if (behind > 0) {
330-
throw new Error(`Local branch behind origin/${branch}. Pull first.`);
513+
// Skip ahead/behind check in dry-run since git commands don't actually execute
514+
if (!dryRun) {
515+
const { behind, ahead } = getAheadBehind(repoDir, branch);
516+
if (behind > 0) {
517+
throw new Error(`Local branch behind origin/${branch}. Pull first.`);
518+
}
331519
}
332520

333521
const pkg = getPackageJson(repoDir);

0 commit comments

Comments
 (0)