mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-14 14:23:14 -06:00
A plain string compare on the prerelease tag put "alpha11" below "alpha8", because
'1' < '8'. The OTA check therefore answered client-newer and refused to offer the
update — while reporting the newer build as `latest` in the same response:
latest_version: 1.9.34-alpha11 current_version: 1.9.34-alpha8
update_available: false reason: "client-newer"
So a fleet on alpha8 or alpha9 could not be moved forward at all, silently, and
nothing about the symptom pointed at version ordering. alpha10 was never really on
offer either; the last update that genuinely worked was alpha6 -> alpha8, where the
lexical order happens to be right by luck.
This is what semver specifies for a single alphanumeric identifier, and it is
simply not what the naming means. lib/version-precedence.js compares digit runs
NUMERICALLY, so alpha8 < alpha9 < alpha10 < alpha11, while leaving everything else
alphabetical — beta still outranks alpha, rc still outranks beta, and a release
still outranks any prerelease of the same core.
Dot-separated identifiers are compared per semver and a shorter run loses, so
moving the naming to the semver-correct `-alpha.11` form later needs no further
change here.
TWO comparators carried the assumption, each with a comment asserting lexical was
fine "for our naming" — true only while the counter stayed below 10. Both now use
the shared helper rather than a third copy drifting into the same trap:
- lib/ota-breaker.js the Android OTA path
- lib/brightsign-update.js the BrightSign host package, where a wrong-way
comparison replaces the script that boots the player
lib/ghcr-check.js was checked and is unaffected: it rejects prerelease strings
outright rather than ordering them.
Tests pin the exact stranding case end to end — decide('1.9.34-alpha8',
'1.9.34-alpha11') must be an offer, not client-newer — plus the reverse direction,
so a future change cannot merely invert it.
1668/1668 pass.
69 lines
3.3 KiB
JavaScript
69 lines
3.3 KiB
JavaScript
'use strict';
|
|
|
|
/*
|
|
* Prerelease ordering.
|
|
*
|
|
* The bug this pins: a plain string compare put every build from alpha10 onward BELOW alpha8,
|
|
* because '1' < '8'. The OTA check then answered `client-newer` and refused to offer the update,
|
|
* so a fleet on alpha8 could not be moved forward — silently, while the server reported the newer
|
|
* build as `latest` in the same response. Two comparators carried the assumption, each with a
|
|
* comment saying lexical was fine "for our naming". It was, until the counter passed 9.
|
|
*/
|
|
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { preCmp } = require('../lib/version-precedence');
|
|
const { cmp } = require('../lib/ota-breaker');
|
|
const bsUpdate = require('../lib/brightsign-update');
|
|
|
|
const sign = (n) => (n === 0 ? 0 : n < 0 ? -1 : 1);
|
|
|
|
test('double-digit prereleases outrank single-digit ones', () => {
|
|
// The exact case that stranded the fleet.
|
|
assert.equal(sign(preCmp('alpha11', 'alpha8')), 1, 'alpha11 must be newer than alpha8');
|
|
assert.equal(sign(preCmp('alpha10', 'alpha9')), 1);
|
|
assert.equal(sign(preCmp('beta12', 'beta2')), 1);
|
|
assert.equal(sign(preCmp('rc10', 'rc9')), 1);
|
|
// And the reverse still holds, so nothing was merely inverted.
|
|
assert.equal(sign(preCmp('alpha2', 'alpha10')), -1);
|
|
});
|
|
|
|
test('ordinary alphabetical precedence is unchanged', () => {
|
|
assert.equal(sign(preCmp('beta1', 'alpha11')), 1, 'beta outranks alpha regardless of number');
|
|
assert.equal(sign(preCmp('rc1', 'beta9')), 1, 'rc outranks beta');
|
|
assert.equal(sign(preCmp('alpha8', 'alpha8')), 0);
|
|
});
|
|
|
|
test('semver dot form works too, so the naming can move without another fix', () => {
|
|
assert.equal(sign(preCmp('alpha.11', 'alpha.8')), 1);
|
|
assert.equal(sign(preCmp('alpha', 'alpha.1')), -1, 'fewer identifiers = lower precedence');
|
|
assert.equal(sign(preCmp('alpha.1', 'beta.1')), -1);
|
|
});
|
|
|
|
test('OTA: a device on alpha8 is offered alpha11', () => {
|
|
// Through the real comparator the update check uses, not just the helper.
|
|
assert.equal(cmp('1.9.34-alpha11', '1.9.34-alpha8'), 1);
|
|
assert.equal(cmp('1.9.34-alpha10', '1.9.34-alpha6'), 1);
|
|
// A release still outranks any prerelease of the same core.
|
|
assert.equal(cmp('1.9.34', '1.9.34-alpha11'), 1);
|
|
// And a newer core still wins outright, whatever the prerelease says.
|
|
assert.equal(cmp('1.9.35-alpha1', '1.9.34-alpha11'), 1);
|
|
});
|
|
|
|
test('OTA decide(): alpha8 -> alpha11 is an offer, not client-newer', () => {
|
|
// The end-to-end symptom: the endpoint reported the newer build as `latest` and refused it
|
|
// in the same breath.
|
|
const { decide } = require('../lib/ota-breaker');
|
|
const d = decide('1.9.34-alpha8', '1.9.34-alpha11', 'test-device-precedence');
|
|
assert.equal(d.update_available, true, `expected an offer, got ${d.reason}`);
|
|
assert.notEqual(d.reason, 'client-newer');
|
|
});
|
|
|
|
test('BrightSign host packages order the same way', () => {
|
|
// Same assumption lived here, with the same comment. A BrightSign package update that goes
|
|
// wrong replaces the script that boots the player, so wrong-way ordering matters more here.
|
|
assert.equal(sign(bsUpdate.compareVersions('1.9.34-rc10', '1.9.34-rc9')), 1);
|
|
assert.equal(sign(bsUpdate.compareVersions('1.9.34', '1.9.34-rc10')), 1);
|
|
assert.equal(sign(bsUpdate.compareVersions('1.9.34-alpha2', '1.9.34-alpha10')), -1);
|
|
});
|