fix(10): centralize semver comparison policy across hooks and changeset
This commit is contained in:
@@ -12,23 +12,12 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
const { isSemverNewer } = require('../get-shit-done/bin/lib/semver-compare.cjs');
|
||||
|
||||
const cacheFile = process.env.GSD_CACHE_FILE;
|
||||
const projectVersionFile = process.env.GSD_PROJECT_VERSION_FILE;
|
||||
const globalVersionFile = process.env.GSD_GLOBAL_VERSION_FILE;
|
||||
|
||||
// Compare semver: true if a > b (a is strictly newer than b)
|
||||
// Strips pre-release suffixes (e.g. '3-beta.1' → '3') to avoid NaN from Number()
|
||||
function isNewer(a, b) {
|
||||
const pa = (a || '').split('.').map(s => Number(s.replace(/-.*/, '')) || 0);
|
||||
const pb = (b || '').split('.').map(s => Number(s.replace(/-.*/, '')) || 0);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (pa[i] > pb[i]) return true;
|
||||
if (pa[i] < pb[i]) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check project directory first (local install), then global
|
||||
let installed = '0.0.0';
|
||||
let configDir = '';
|
||||
@@ -75,7 +64,7 @@ if (configDir) {
|
||||
const versionMatch = content.match(/(?:\/\/|#) gsd-hook-version:\s*(.+)/);
|
||||
if (versionMatch) {
|
||||
const hookVersion = versionMatch[1].trim();
|
||||
if (isNewer(installed, hookVersion) && !hookVersion.includes('{{')) {
|
||||
if (isSemverNewer(installed, hookVersion) && !hookVersion.includes('{{')) {
|
||||
staleHooks.push({ file: hookFile, hookVersion, installedVersion: installed });
|
||||
}
|
||||
} else {
|
||||
@@ -105,7 +94,7 @@ try {
|
||||
} catch (e) {}
|
||||
|
||||
const result = {
|
||||
update_available: latest && isNewer(latest, installed),
|
||||
update_available: latest && isSemverNewer(latest, installed),
|
||||
installed,
|
||||
latest: latest || 'unknown',
|
||||
checked: Math.floor(Date.now() / 1000),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const { isSemverNewer } = require('../get-shit-done/bin/lib/semver-compare.cjs');
|
||||
|
||||
// --- Config + last-command readers ------------------------------------------
|
||||
|
||||
@@ -403,13 +404,12 @@ function runStatusline() {
|
||||
if (cache.stale_hooks && cache.stale_hooks.length > 0) {
|
||||
// If installed version is ahead of npm latest, this is a dev install.
|
||||
// Running /gsd:update would downgrade — show a contextual warning instead.
|
||||
const isDevInstall = (() => {
|
||||
if (!cache.installed || !cache.latest || cache.latest === 'unknown') return false;
|
||||
const parseV = v => v.replace(/^v/, '').split('.').map(Number);
|
||||
const [ai, bi, ci] = parseV(cache.installed);
|
||||
const [an, bn, cn] = parseV(cache.latest);
|
||||
return ai > an || (ai === an && bi > bn) || (ai === an && bi === bn && ci > cn);
|
||||
})();
|
||||
const isDevInstall = (
|
||||
cache.installed &&
|
||||
cache.latest &&
|
||||
cache.latest !== 'unknown' &&
|
||||
isInstalledAheadOfLatest(cache.installed, cache.latest)
|
||||
);
|
||||
if (isDevInstall) {
|
||||
gsdUpdate += '\x1b[33m⚠ dev install — re-run installer to sync hooks\x1b[0m │ ';
|
||||
} else {
|
||||
@@ -497,11 +497,16 @@ function composeStatusline({
|
||||
return `${gsdUpdate}${modelSeg} │ ${dirSeg}${ctx}${lastCmdSuffix}`;
|
||||
}
|
||||
|
||||
function isInstalledAheadOfLatest(installed, latest) {
|
||||
return isSemverNewer(installed, latest);
|
||||
}
|
||||
|
||||
// Export helpers for unit tests. Harmless when run as a script.
|
||||
module.exports = {
|
||||
readGsdState, parseStateMd, formatGsdState,
|
||||
readGsdConfig, getConfigValue, readLastSlashCommand,
|
||||
composeStatusline,
|
||||
isInstalledAheadOfLatest,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user