diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 00a643b5..ea10d23d 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -117,6 +117,6 @@ jobs: fi 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 done diff --git a/src/utils/update-checker.ts b/src/utils/update-checker.ts index d1f78934..c4a0e41d 100644 --- a/src/utils/update-checker.ts +++ b/src/utils/update-checker.ts @@ -80,6 +80,10 @@ export function compareVersions(v1: string, v2: string): number { /** * 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 */ 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; // Same base version - check prerelease - // Release > prerelease (5.0.2 > 5.0.2-dev.1) - if (p1.prerelease === null && p2.prerelease !== null) return 1; - if (p1.prerelease !== null && p2.prerelease === null) return -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; // Both are prereleases - compare prerelease numbers if (p1.prereleaseNum !== null && p2.prereleaseNum !== null) { diff --git a/tests/unit/utils/version-comparison.test.js b/tests/unit/utils/version-comparison.test.js index d58d0e4f..0c6e38ac 100644 --- a/tests/unit/utils/version-comparison.test.js +++ b/tests/unit/utils/version-comparison.test.js @@ -7,11 +7,14 @@ * - Downgrade detection in update-command.ts * - 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: * - `5.0.2` < `5.1.0-dev.3` (upgrade 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.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) * - Downgrade warning shown for stable → older dev * - Invalid version string handling @@ -135,14 +138,14 @@ describe('Version Comparison Implementation (Phase 4)', () => { }); describe('Prerelease vs Release comparison', function () { - it('should treat release versions as newer than prerelease', function () { - // 5.0.2 > 5.0.2-dev.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); + it('should treat prerelease versions as newer than release (dev is work AFTER release)', function () { + // 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-dev.1', '5.0.2'), 1); - // 5.1.0 > 5.1.0-dev.3 - 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); + // 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-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'); }); - it('should handle `5.0.2-dev.1` < `5.0.2` (prerelease < release)', function () { - 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'); + 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, + '5.0.2-dev.1 should be greater than 5.0.2'); }); 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', () => { it('should identify downgrade scenarios correctly', () => { // 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 = [ - ['5.0.2', '4.9.0-dev.1', true], // stable to older dev - ['5.1.0', '5.0.0-dev.5', true], // newer 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 (base version older) ['5.0.2', '5.0.1', true], // simple downgrade ['5.0.2', '5.0.2', false], // same version ['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', '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]) => { @@ -221,11 +227,13 @@ describe('Version Comparison Implementation (Phase 4)', () => { }); it('should handle beta update warnings scenarios', () => { + // With new semantic: X.Y.Z-dev.N > X.Y.Z const betaScenarios = [ ['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-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-dev.1', false], // upgrade to dev ]; betaScenarios.forEach(([current, latest, shouldWarn]) => { @@ -334,12 +342,13 @@ describe('Version Comparison Implementation (Phase 4)', () => { describe('Integration tests with version comparison', 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 = [ // [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-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.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'],