fix: simplify updater to @mosaic/mosaic only, add explicit tea repo/login flags (#388)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline was successful

This commit was merged in pull request #388.
This commit is contained in:
2026-04-05 02:09:23 +00:00
parent 255ba46a4d
commit 2e31626f87
5 changed files with 106 additions and 101 deletions

View File

@@ -1,6 +1,6 @@
/**
* Mosaic update checker — compares the installed Mosaic package against the
* Gitea npm registry and reports when an upgrade is available.
* Mosaic update checker — compares the installed @mosaic/mosaic package
* against the Gitea npm registry and reports when an upgrade is available.
*
* Used by:
* - CLI startup (non-blocking background check)
@@ -40,9 +40,7 @@ export interface UpdateCheckResult {
// ─── Constants ──────────────────────────────────────────────────────────────
const REGISTRY = 'https://git.mosaicstack.dev/api/packages/mosaic/npm/';
const MODERN_PKG = '@mosaic/mosaic';
const LEGACY_PKG = '@mosaic/cli';
const INSTALLED_PACKAGE_ORDER = [MODERN_PKG, LEGACY_PKG] as const;
const PKG = '@mosaic/mosaic';
const CACHE_DIR = join(homedir(), '.cache', 'mosaic');
const CACHE_FILE = join(CACHE_DIR, 'update-check.json');
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
@@ -131,20 +129,17 @@ export function semverLt(a: string, b: string): boolean {
/** Cache stores only the latest registry version (the expensive network call).
* The installed version is always checked fresh — it's a local `npm ls`. */
interface RegistryCache {
currentPackage?: string;
latest: string;
targetPackage?: string;
checkedAt: string;
registry: string;
}
function readCache(currentPackage: string): RegistryCache | null {
function readCache(): RegistryCache | null {
try {
if (!existsSync(CACHE_FILE)) return null;
const raw = JSON.parse(readFileSync(CACHE_FILE, 'utf-8')) as RegistryCache;
const age = Date.now() - new Date(raw.checkedAt).getTime();
if (age > CACHE_TTL_MS) return null;
if ((raw.currentPackage || '') !== currentPackage) return null;
return raw;
} catch {
return null;
@@ -163,22 +158,18 @@ function writeCache(entry: RegistryCache): void {
// ─── Public API ─────────────────────────────────────────────────────────────
/**
* Get the currently installed Mosaic package version.
* Prefers the consolidated @mosaic/mosaic package over legacy @mosaic/cli.
* Get the currently installed @mosaic/mosaic version.
*/
export function getInstalledVersion(): { name: string; version: string } {
// Fast path: check via package.json require chain
try {
const raw = npmExec(`ls -g --depth=0 --json 2>/dev/null`, 3000);
if (raw) {
const data = JSON.parse(raw) as {
dependencies?: Record<string, { version?: string }>;
};
for (const pkg of INSTALLED_PACKAGE_ORDER) {
const version = data?.dependencies?.[pkg]?.version;
if (version) {
return { name: pkg, version };
}
const version = data?.dependencies?.[PKG]?.version;
if (version) {
return { name: PKG, version };
}
}
} catch {
@@ -188,27 +179,19 @@ export function getInstalledVersion(): { name: string; version: string } {
}
/**
* Fetch the latest published version from the Gitea npm registry.
* For legacy @mosaic/cli installs, try the legacy package first and fall back
* to @mosaic/mosaic to support the CLI -> mosaic package consolidation.
* Fetch the latest published @mosaic/mosaic version from the Gitea npm registry.
* Returns empty string on failure.
*/
export function getLatestVersion(installedPackage = ''): { name: string; version: string } {
const candidates =
installedPackage === LEGACY_PKG ? [LEGACY_PKG, MODERN_PKG] : [MODERN_PKG, LEGACY_PKG];
for (const pkg of candidates) {
const version = npmExec(`view ${pkg} version --registry=${REGISTRY}`);
if (version) {
return { name: pkg, version };
}
export function getLatestVersion(): { name: string; version: string } {
const version = npmExec(`view ${PKG} version --registry=${REGISTRY}`);
if (version) {
return { name: PKG, version };
}
return { name: '', version: '' };
}
export function getInstallCommand(result: Pick<UpdateCheckResult, 'targetPackage'>): string {
return `npm i -g ${result.targetPackage || MODERN_PKG}@latest`;
return `npm i -g ${result.targetPackage || PKG}@latest`;
}
/**
@@ -225,31 +208,27 @@ export function checkForUpdate(options?: { skipCache?: boolean }): UpdateCheckRe
let checkedAt: string;
if (!options?.skipCache) {
const cached = readCache(currentInfo.name);
const cached = readCache();
if (cached) {
latestInfo = {
name: cached.targetPackage || MODERN_PKG,
name: PKG,
version: cached.latest,
};
checkedAt = cached.checkedAt;
} else {
latestInfo = getLatestVersion(currentInfo.name);
latestInfo = getLatestVersion();
checkedAt = new Date().toISOString();
writeCache({
currentPackage: currentInfo.name,
latest: latestInfo.version,
targetPackage: latestInfo.name,
checkedAt,
registry: REGISTRY,
});
}
} else {
latestInfo = getLatestVersion(currentInfo.name);
latestInfo = getLatestVersion();
checkedAt = new Date().toISOString();
writeCache({
currentPackage: currentInfo.name,
latest: latestInfo.version,
targetPackage: latestInfo.name,
checkedAt,
registry: REGISTRY,
});
@@ -257,9 +236,9 @@ export function checkForUpdate(options?: { skipCache?: boolean }): UpdateCheckRe
return {
current,
currentPackage: currentInfo.name,
currentPackage: currentInfo.name || PKG,
latest: latestInfo.version,
targetPackage: latestInfo.name || MODERN_PKG,
targetPackage: latestInfo.name || PKG,
updateAvailable: !!(current && latestInfo.version && semverLt(current, latestInfo.version)),
checkedAt,
registry: REGISTRY,
@@ -273,17 +252,12 @@ export function formatUpdateNotice(result: UpdateCheckResult): string {
if (!result.updateAvailable) return '';
const installCommand = getInstallCommand(result);
const targetChanged =
result.currentPackage && result.targetPackage && result.currentPackage !== result.targetPackage;
const lines = [
'',
'╭─────────────────────────────────────────────────╮',
'│ │',
`│ Update available: ${result.current}${result.latest}`.padEnd(50) + '│',
...(targetChanged
? [`│ Package target: ${result.currentPackage}${result.targetPackage}`.padEnd(50) + '│']
: []),
'│ │',
'│ Run: bash tools/install.sh │',
`│ Or: ${installCommand}`.padEnd(50) + '│',