From 2ee75c79426f4d374f11795e7d3bbac5cb65ff42 Mon Sep 17 00:00:00 2001 From: Bob Lee Date: Fri, 18 Sep 2026 20:31:57 +0800 Subject: [PATCH 1/2] Follow the release-root symlink when pruning old versions The production mirror lives at /srv/openbitfun-release, which is a symlink to /srv/bitfun-release. GNU find does not descend a command-line symlink unless -H is set, so KEEP_VERSIONS never deleted anything on the host. --- scripts/linux-binaries-manifest.test.mjs | 1 + scripts/openbitfun-release-sync.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/linux-binaries-manifest.test.mjs b/scripts/linux-binaries-manifest.test.mjs index bf66fc342f..a4f8a1d2a5 100644 --- a/scripts/linux-binaries-manifest.test.mjs +++ b/scripts/linux-binaries-manifest.test.mjs @@ -448,6 +448,7 @@ test('the mirror keeps only the two newest versions and skips an unchanged lates assert.ok(keep, 'KEEP_VERSIONS must be set'); assert.equal(Number(keep[1]), 2); assert.match(syncScript, /Already mirroring \$VERSION; nothing to fetch/); + assert.match(syncScript, /find -H "\$WEBSITE_RELEASE_DIR"/); assert.doesNotMatch(syncScript, /! -name '0\.2\.\*'/); }); diff --git a/scripts/openbitfun-release-sync.sh b/scripts/openbitfun-release-sync.sh index 8f1866268c..3341234cda 100755 --- a/scripts/openbitfun-release-sync.sh +++ b/scripts/openbitfun-release-sync.sh @@ -683,7 +683,7 @@ prune_old_versions() { local dirs=() total remove_count i while IFS= read -r d; do dirs+=("$d") - done < <(find "$WEBSITE_RELEASE_DIR" -mindepth 1 -maxdepth 1 -type d | sort -V) + done < <(find -H "$WEBSITE_RELEASE_DIR" -mindepth 1 -maxdepth 1 -type d | sort -V) total=${#dirs[@]} if [ "$total" -le "$KEEP_VERSIONS" ]; then return 0 From fa9c369bbcc5872a8b4cfe2162fac9d7dad40364 Mon Sep 17 00:00:00 2001 From: Bob Lee Date: Fri, 18 Sep 2026 20:32:43 +0800 Subject: [PATCH 2/2] Order mirrored versions by SemVer so a beta is not kept over its release GNU sort -V treats 1.0.0-beta as newer than 1.0.0. The production prune then deleted the previous stable directory and left the beta. Compare the version directory names as SemVer instead, and walk the tree through Python so a symlinked release root is still visible. --- scripts/linux-binaries-manifest.test.mjs | 29 +++++++++++++++++++++++- scripts/openbitfun-release-sync.sh | 17 +++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/scripts/linux-binaries-manifest.test.mjs b/scripts/linux-binaries-manifest.test.mjs index a4f8a1d2a5..be980d0e7c 100644 --- a/scripts/linux-binaries-manifest.test.mjs +++ b/scripts/linux-binaries-manifest.test.mjs @@ -448,10 +448,37 @@ test('the mirror keeps only the two newest versions and skips an unchanged lates assert.ok(keep, 'KEEP_VERSIONS must be set'); assert.equal(Number(keep[1]), 2); assert.match(syncScript, /Already mirroring \$VERSION; nothing to fetch/); - assert.match(syncScript, /find -H "\$WEBSITE_RELEASE_DIR"/); assert.doesNotMatch(syncScript, /! -name '0\.2\.\*'/); }); +test('pruning keeps the two newest SemVer releases and drops a newer-looking beta', () => { + const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'openbitfun-prune-')); + for (const name of ['0.2.19', '1.0.0-beta', '1.0.0', '1.0.1']) { + fs.mkdirSync(path.join(temp, name)); + } + const result = spawnSync( + 'bash', + ['-c', ` + source "$SYNC_SCRIPT" + WEBSITE_RELEASE_DIR="$TEST_DIR" + KEEP_VERSIONS=2 + PYTHON=python3 + prune_old_versions + `], + { + encoding: 'utf8', + env: { + ...process.env, + SYNC_SCRIPT: path.join(repoRoot, 'scripts/openbitfun-release-sync.sh'), + TEST_DIR: temp, + }, + } + ); + assert.equal(result.status, 0, result.stderr); + assert.deepEqual(fs.readdirSync(temp).sort(), ['1.0.0', '1.0.1']); + fs.rmSync(temp, { recursive: true, force: true }); +}); + test('openbitfun sync lock and cron use the in-repo script, not a server-only copy', () => { const syncScript = fs.readFileSync( path.join(repoRoot, 'scripts/openbitfun-release-sync.sh'), diff --git a/scripts/openbitfun-release-sync.sh b/scripts/openbitfun-release-sync.sh index 3341234cda..4ff569e177 100755 --- a/scripts/openbitfun-release-sync.sh +++ b/scripts/openbitfun-release-sync.sh @@ -683,7 +683,22 @@ prune_old_versions() { local dirs=() total remove_count i while IFS= read -r d; do dirs+=("$d") - done < <(find -H "$WEBSITE_RELEASE_DIR" -mindepth 1 -maxdepth 1 -type d | sort -V) + done < <("$PYTHON" - "$WEBSITE_RELEASE_DIR" <<'PY' +import os, re, sys +root = sys.argv[1] +names = [name for name in os.listdir(root) if os.path.isdir(os.path.join(root, name))] + +def key(name): + match = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)(?:-(.+))?", name) + if not match: + return (0, 0, 0, 0, name) + pre = match.group(4) + return (int(match.group(1)), int(match.group(2)), int(match.group(3)), 1 if pre is None else 0, pre or "") + +for name in sorted(names, key=key): + print(os.path.join(root, name)) +PY +) total=${#dirs[@]} if [ "$total" -le "$KEEP_VERSIONS" ]; then return 0