fix(update): correct dev version comparison semantic

- dev versions (X.Y.Z-dev.N) now treated as newer than base release (X.Y.Z)
- reflects convention that dev work is AFTER the release, not before
- update GH Actions message to use 'ccs update --dev' instead of npm install
- update tests to match new semantic
This commit is contained in:
kaitranntt committed 2025-12-20 19:48:05 -05:00
1 parent 322e9c9f5a
commit df77745eca
3 files changed
+33 -20

No files matched your search

+1 -1
View File
@@ -117,6 +117,6 @@ jobs:
fi fi
echo "Tagging issue #$NUM" echo "Tagging issue #$NUM"
gh issue comment "$NUM" --repo "${{ github.repository }}" --body "[i] Available in \`$VERSION\`. Install: \`npm i @kaitranntt/ccs@dev\`" || true gh issue comment "$NUM" --repo "${{ github.repository }}" --body "[i] Available in \`$VERSION\`. Update: \`ccs update --dev\`" || true
gh issue edit "$NUM" --add-label "released-dev" --repo "${{ github.repository }}" || true gh issue edit "$NUM" --add-label "released-dev" --repo "${{ github.repository }}" || true
done done
+7 -3
View File
@@ -80,6 +80,10 @@ export function compareVersions(v1: string, v2: string): number {
/** /**
* Compare versions with prerelease support * Compare versions with prerelease support
*
* Prerelease convention: X.Y.Z-dev.N is a development version AFTER X.Y.Z
* For example: 6.7.1-dev.3 > 6.7.1 (dev version is work toward next release)
*
* @returns 1 if v1 > v2 (upgrade), -1 if v1 < v2 (downgrade), 0 if equal * @returns 1 if v1 > v2 (upgrade), -1 if v1 < v2 (downgrade), 0 if equal
*/ */
export function compareVersionsWithPrerelease(v1: string, v2: string): number { export function compareVersionsWithPrerelease(v1: string, v2: string): number {
@@ -92,9 +96,9 @@ export function compareVersionsWithPrerelease(v1: string, v2: string): number {
if (p1.patch !== p2.patch) return p1.patch > p2.patch ? 1 : -1; if (p1.patch !== p2.patch) return p1.patch > p2.patch ? 1 : -1;
// Same base version - check prerelease // Same base version - check prerelease
// Release > prerelease (5.0.2 > 5.0.2-dev.1) // Prerelease (dev) > Release: 6.7.1-dev.3 > 6.7.1 (dev is work AFTER release)
if (p1.prerelease === null && p2.prerelease !== null) return 1; if (p1.prerelease !== null && p2.prerelease === null) return 1;
if (p1.prerelease !== null && p2.prerelease === null) return -1; if (p1.prerelease === null && p2.prerelease !== null) return -1;
// Both are prereleases - compare prerelease numbers // Both are prereleases - compare prerelease numbers
if (p1.prereleaseNum !== null && p2.prereleaseNum !== null) { if (p1.prereleaseNum !== null && p2.prereleaseNum !== null) {
+25 -16
View File
@@ -7,11 +7,14 @@
* - Downgrade detection in update-command.ts * - Downgrade detection in update-command.ts
* - Edge cases with version parsing * - Edge cases with version parsing
* *
* Prerelease convention: X.Y.Z-dev.N is a development version AFTER X.Y.Z
* For example: 6.7.1-dev.3 > 6.7.1 (dev version is work toward next release)
*
* Test scenarios to cover: * Test scenarios to cover:
* - `5.0.2` < `5.1.0-dev.3` (upgrade to dev) * - `5.0.2` < `5.1.0-dev.3` (upgrade to dev)
* - `5.0.2` > `4.9.0-dev.1` (downgrade to dev) * - `5.0.2` > `4.9.0-dev.1` (downgrade to dev)
* - `5.1.0-dev.1` < `5.1.0-dev.3` (dev-to-dev upgrade) * - `5.1.0-dev.1` < `5.1.0-dev.3` (dev-to-dev upgrade)
* - `5.0.2-dev.1` < `5.0.2` (prerelease < release) * - `5.0.2-dev.1` > `5.0.2` (prerelease > release - dev is work AFTER release)
* - `5.0.2` = `5.0.2` (same version) * - `5.0.2` = `5.0.2` (same version)
* - Downgrade warning shown for stable → older dev * - Downgrade warning shown for stable → older dev
* - Invalid version string handling * - Invalid version string handling
@@ -135,14 +138,14 @@ describe('Version Comparison Implementation (Phase 4)', () => {
}); });
describe('Prerelease vs Release comparison', function () { describe('Prerelease vs Release comparison', function () {
it('should treat release versions as newer than prerelease', function () { it('should treat prerelease versions as newer than release (dev is work AFTER release)', function () {
// 5.0.2 > 5.0.2-dev.1 // 5.0.2-dev.1 > 5.0.2 (dev version is development AFTER 5.0.2)
assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.0.2', '5.0.2-dev.1'), 1); assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.0.2', '5.0.2-dev.1'), -1);
assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.0.2-dev.1', '5.0.2'), -1); assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.0.2-dev.1', '5.0.2'), 1);
// 5.1.0 > 5.1.0-dev.3 // 5.1.0-dev.3 > 5.1.0 (dev version is development AFTER 5.1.0)
assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.1.0', '5.1.0-dev.3'), 1); assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.1.0', '5.1.0-dev.3'), -1);
assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.1.0-dev.3', '5.1.0'), -1); assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.1.0-dev.3', '5.1.0'), 1);
}); });
}); });
@@ -187,9 +190,9 @@ describe('Version Comparison Implementation (Phase 4)', () => {
'5.1.0-dev.1 should be less than 5.1.0-dev.3'); '5.1.0-dev.1 should be less than 5.1.0-dev.3');
}); });
it('should handle `5.0.2-dev.1` < `5.0.2` (prerelease < release)', function () { it('should handle `5.0.2-dev.1` > `5.0.2` (prerelease > release - dev is work AFTER release)', function () {
assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.0.2-dev.1', '5.0.2'), -1, assert.strictEqual(updateCheckerModule.compareVersionsWithPrerelease('5.0.2-dev.1', '5.0.2'), 1,
'5.0.2-dev.1 should be less than 5.0.2'); '5.0.2-dev.1 should be greater than 5.0.2');
}); });
it('should handle `5.0.2` = `5.0.2` (same version)', function () { it('should handle `5.0.2` = `5.0.2` (same version)', function () {
@@ -202,13 +205,16 @@ describe('Version Comparison Implementation (Phase 4)', () => {
describe('Downgrade detection logic tests', () => { describe('Downgrade detection logic tests', () => {
it('should identify downgrade scenarios correctly', () => { it('should identify downgrade scenarios correctly', () => {
// Test the comparison logic that would trigger downgrade warnings // Test the comparison logic that would trigger downgrade warnings
// With new semantic: X.Y.Z-dev.N > X.Y.Z (dev is work AFTER release)
const downgradeScenarios = [ const downgradeScenarios = [
['5.0.2', '4.9.0-dev.1', true], // stable to older dev ['5.0.2', '4.9.0-dev.1', true], // stable to older dev (base version older)
['5.1.0', '5.0.0-dev.5', true], // newer stable to older dev ['5.1.0', '5.0.0-dev.5', true], // newer stable to older dev (base version older)
['5.0.2', '5.0.1', true], // simple downgrade ['5.0.2', '5.0.1', true], // simple downgrade
['5.0.2', '5.0.2', false], // same version ['5.0.2', '5.0.2', false], // same version
['5.0.2', '5.0.3', false], // upgrade ['5.0.2', '5.0.3', false], // upgrade
['5.0.2-dev.1', '5.0.1-dev.2', true], // dev downgrade (base version older) ['5.0.2-dev.1', '5.0.1-dev.2', true], // dev downgrade (base version older)
['5.0.2', '5.0.2-dev.1', false], // upgrade to dev (dev is newer)
['5.0.2-dev.1', '5.0.2', true], // downgrade from dev to stable
]; ];
downgradeScenarios.forEach(([current, latest, isDowngrade]) => { downgradeScenarios.forEach(([current, latest, isDowngrade]) => {
@@ -221,11 +227,13 @@ describe('Version Comparison Implementation (Phase 4)', () => {
}); });
it('should handle beta update warnings scenarios', () => { it('should handle beta update warnings scenarios', () => {
// With new semantic: X.Y.Z-dev.N > X.Y.Z
const betaScenarios = [ const betaScenarios = [
['5.0.2', '5.1.0-dev.3', false], // newer dev version (not downgrade) ['5.0.2', '5.1.0-dev.3', false], // newer dev version (not downgrade)
['5.0.2', '4.9.0-dev.1', true], // downgrade to older dev ['5.0.2', '4.9.0-dev.1', true], // downgrade to older dev
['5.0.2-dev.1', '5.0.2', false], // upgrade to release ['5.0.2-dev.1', '5.0.2', true], // downgrade from dev to release
['5.0.2', '5.0.2', false], // same version ['5.0.2', '5.0.2', false], // same version
['5.0.2', '5.0.2-dev.1', false], // upgrade to dev
]; ];
betaScenarios.forEach(([current, latest, shouldWarn]) => { betaScenarios.forEach(([current, latest, shouldWarn]) => {
@@ -334,12 +342,13 @@ describe('Version Comparison Implementation (Phase 4)', () => {
describe('Integration tests with version comparison', function () { describe('Integration tests with version comparison', function () {
it('should handle complex version comparison scenarios', function () { it('should handle complex version comparison scenarios', function () {
// With new semantic: X.Y.Z-dev.N > X.Y.Z (dev is work AFTER release)
const scenarios = [ const scenarios = [
// [version1, version2, expected_result, description] // [version1, version2, expected_result, description]
['1.0.0', '1.0.0-alpha.1', 1, 'release > alpha'], ['1.0.0', '1.0.0-alpha.1', -1, 'release < alpha (alpha is work AFTER release)'],
['1.0.0-alpha.1', '1.0.0-beta.1', 0, 'alpha = beta (same number, different identifier)'], ['1.0.0-alpha.1', '1.0.0-beta.1', 0, 'alpha = beta (same number, different identifier)'],
['1.0.0-beta.1', '1.0.0-rc.1', 0, 'beta = rc (same number, different identifier)'], ['1.0.0-beta.1', '1.0.0-rc.1', 0, 'beta = rc (same number, different identifier)'],
['1.0.0-rc.1', '1.0.0', -1, 'rc < release'], ['1.0.0-rc.1', '1.0.0', 1, 'rc > release (rc is work AFTER release)'],
['1.0.0-alpha.1', '1.0.0-alpha.2', -1, 'alpha.1 < alpha.2'], ['1.0.0-alpha.1', '1.0.0-alpha.2', -1, 'alpha.1 < alpha.2'],
['1.0.0-alpha.2', '1.0.0-alpha.1', 1, 'alpha.2 > alpha.1'], ['1.0.0-alpha.2', '1.0.0-alpha.1', 1, 'alpha.2 > alpha.1'],
['2.0.0-alpha.1', '1.9.9', 1, 'new major prerelease > old release'], ['2.0.0-alpha.1', '1.9.9', 1, 'new major prerelease > old release'],