Compare commits
1 Commits
mosaic-v0.
...
fix/update
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c39fa065fc |
54
docs/scratchpads/387-updater-wrapper-gitea-20260404.md
Normal file
54
docs/scratchpads/387-updater-wrapper-gitea-20260404.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Scratchpad — issue #387 updater simplification + Gitea wrapper repo context
|
||||||
|
|
||||||
|
- Objective: simplify updater behavior to `@mosaic/mosaic` only, and fix Gitea wrapper scripts so merge/CI/issue operations work reliably when tea needs explicit repo/login context.
|
||||||
|
- Scope:
|
||||||
|
- `packages/mosaic/src/runtime/update-checker.ts`
|
||||||
|
- `packages/mosaic/__tests__/update-checker.test.ts`
|
||||||
|
- any package metadata/version bumps needed
|
||||||
|
- repo-source git wrappers under `packages/mosaic/framework/tools/git/`
|
||||||
|
- Constraints:
|
||||||
|
- Jason approved breaking changes; legacy `@mosaic/cli` support is out of scope.
|
||||||
|
- Keep changes focused and mergeable.
|
||||||
|
- Acceptance:
|
||||||
|
- updater only targets `@mosaic/mosaic`
|
||||||
|
- wrapper path works on Gitea in this environment without manual repo guessing
|
||||||
|
- PR merges squash-only after green CI
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
|
||||||
|
### 2026-04-04
|
||||||
|
|
||||||
|
#### Update Checker Simplification (DONE)
|
||||||
|
|
||||||
|
- Removed `LEGACY_PKG`, `INSTALLED_PACKAGE_ORDER` constants — only `PKG = '@mosaic/mosaic'` remains
|
||||||
|
- `getInstalledVersion()` — removed loop over multiple packages, only checks `@mosaic/mosaic`
|
||||||
|
- `getLatestVersion()` — removed `installedPackage` parameter and candidate iteration; single query for `@mosaic/mosaic`
|
||||||
|
- `checkForUpdate()` — removed `currentPackage`-based cache key comparison; cache is now package-agnostic
|
||||||
|
- `RegistryCache` — removed `currentPackage` and `targetPackage` fields
|
||||||
|
- `formatUpdateNotice()` — removed `targetChanged` branch (package migration notice no longer relevant)
|
||||||
|
- All legacy fallback/compatibility logic removed
|
||||||
|
|
||||||
|
#### Test Updates (DONE)
|
||||||
|
|
||||||
|
- Removed legacy `@mosaic/cli` fallback test
|
||||||
|
- Removed cache cross-contamination test (was testing legacy→modern package transition)
|
||||||
|
- Added `does not query legacy @mosaic/cli package` test — asserts no `@mosaic/cli` npm commands are issued
|
||||||
|
- Added `returns empty result when package is not installed` test
|
||||||
|
- All 8 tests pass ✅
|
||||||
|
|
||||||
|
#### Gitea Wrapper Fixes (DONE)
|
||||||
|
|
||||||
|
- `pr-merge.sh`:
|
||||||
|
- Added `OWNER=$(get_repo_owner)` / `REPO=$(get_repo_name)` before case block
|
||||||
|
- tea merge command now includes `--repo $OWNER/$REPO --login ${GITEA_LOGIN:-mosaicstack}`
|
||||||
|
- `issue-close.sh`:
|
||||||
|
- Added `OWNER=$(get_repo_owner)` / `REPO=$(get_repo_name)` after detect_platform
|
||||||
|
- Both `tea issue comment` and `tea issue close` now include `--repo "$OWNER/$REPO" --login "${GITEA_LOGIN:-mosaicstack}"`
|
||||||
|
- `pr-ci-wait.sh`: No changes needed — uses curl API calls (not tea), already passes owner/repo correctly
|
||||||
|
- `detect-platform.sh`: No changes needed — provides the `get_repo_owner`/`get_repo_name` functions used by wrappers
|
||||||
|
|
||||||
|
#### Verification
|
||||||
|
|
||||||
|
- `vitest run` — 8/8 tests pass
|
||||||
|
- `tsc --noEmit` — no errors in update-checker.ts (pre-existing workspace dep errors unrelated)
|
||||||
|
- `eslint` — clean, no warnings
|
||||||
@@ -88,19 +88,18 @@ describe('formatUpdateNotice', () => {
|
|||||||
expect(notice).toContain('Update available');
|
expect(notice).toContain('Update available');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses @mosaic/mosaic hints for modern installs', async () => {
|
it('uses @mosaic/mosaic for installs', async () => {
|
||||||
execSyncMock.mockImplementation((command: string) => {
|
execSyncMock.mockImplementation((command: string) => {
|
||||||
if (command.includes('ls -g --depth=0 --json')) {
|
if (command.includes('ls -g --depth=0 --json')) {
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
dependencies: {
|
dependencies: {
|
||||||
'@mosaic/mosaic': { version: '0.0.17' },
|
'@mosaic/mosaic': { version: '0.0.19' },
|
||||||
'@mosaic/cli': { version: '0.0.16' },
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.includes('view @mosaic/mosaic version')) {
|
if (command.includes('view @mosaic/mosaic version')) {
|
||||||
return '0.0.18';
|
return '0.0.20';
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Unexpected command: ${command}`);
|
throw new Error(`Unexpected command: ${command}`);
|
||||||
@@ -110,28 +109,29 @@ describe('formatUpdateNotice', () => {
|
|||||||
const result = checkForUpdate({ skipCache: true });
|
const result = checkForUpdate({ skipCache: true });
|
||||||
const notice = formatUpdateNotice(result);
|
const notice = formatUpdateNotice(result);
|
||||||
|
|
||||||
expect(result.current).toBe('0.0.17');
|
expect(result.current).toBe('0.0.19');
|
||||||
expect(result.latest).toBe('0.0.18');
|
expect(result.latest).toBe('0.0.20');
|
||||||
|
expect(result.currentPackage).toBe('@mosaic/mosaic');
|
||||||
|
expect(result.targetPackage).toBe('@mosaic/mosaic');
|
||||||
expect(notice).toContain('@mosaic/mosaic@latest');
|
expect(notice).toContain('@mosaic/mosaic@latest');
|
||||||
expect(notice).not.toContain('@mosaic/cli@latest');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to @mosaic/mosaic for legacy @mosaic/cli installs when cli is unavailable', async () => {
|
it('does not query legacy @mosaic/cli package', async () => {
|
||||||
execSyncMock.mockImplementation((command: string) => {
|
execSyncMock.mockImplementation((command: string) => {
|
||||||
|
if (command.includes('view @mosaic/cli')) {
|
||||||
|
throw new Error('Should not query @mosaic/cli');
|
||||||
|
}
|
||||||
|
|
||||||
if (command.includes('ls -g --depth=0 --json')) {
|
if (command.includes('ls -g --depth=0 --json')) {
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
dependencies: {
|
dependencies: {
|
||||||
'@mosaic/cli': { version: '0.0.16' },
|
'@mosaic/mosaic': { version: '0.0.19' },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.includes('view @mosaic/cli version')) {
|
|
||||||
throw new Error('not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.includes('view @mosaic/mosaic version')) {
|
if (command.includes('view @mosaic/mosaic version')) {
|
||||||
return '0.0.17';
|
return '0.0.20';
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Unexpected command: ${command}`);
|
throw new Error(`Unexpected command: ${command}`);
|
||||||
@@ -139,57 +139,31 @@ describe('formatUpdateNotice', () => {
|
|||||||
|
|
||||||
const { checkForUpdate } = await importUpdateChecker();
|
const { checkForUpdate } = await importUpdateChecker();
|
||||||
const result = checkForUpdate({ skipCache: true });
|
const result = checkForUpdate({ skipCache: true });
|
||||||
const notice = formatUpdateNotice(result);
|
|
||||||
|
|
||||||
expect(result.current).toBe('0.0.16');
|
expect(result.targetPackage).toBe('@mosaic/mosaic');
|
||||||
expect(result.latest).toBe('0.0.17');
|
expect(result.latest).toBe('0.0.20');
|
||||||
expect(notice).toContain('@mosaic/mosaic@latest');
|
// Verify no @mosaic/cli queries were made
|
||||||
|
const calls = execSyncMock.mock.calls.map((c: any[]) => c[0] as string);
|
||||||
|
expect(calls.some((c) => c.includes('@mosaic/cli'))).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not reuse a cached modern-package result for a legacy install', async () => {
|
it('returns empty result when package is not installed', async () => {
|
||||||
let installedPackage = '@mosaic/mosaic';
|
|
||||||
|
|
||||||
execSyncMock.mockImplementation((command: string) => {
|
execSyncMock.mockImplementation((command: string) => {
|
||||||
if (command.includes('ls -g --depth=0 --json')) {
|
if (command.includes('ls -g --depth=0 --json')) {
|
||||||
return JSON.stringify({
|
return JSON.stringify({ dependencies: {} });
|
||||||
dependencies:
|
|
||||||
installedPackage === '@mosaic/mosaic'
|
|
||||||
? { '@mosaic/mosaic': { version: '0.0.17' } }
|
|
||||||
: { '@mosaic/cli': { version: '0.0.16' } },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.includes('view @mosaic/mosaic version')) {
|
if (command.includes('view @mosaic/mosaic version')) {
|
||||||
return installedPackage === '@mosaic/mosaic' ? '0.0.18' : '0.0.17';
|
return '';
|
||||||
}
|
|
||||||
|
|
||||||
if (command.includes('view @mosaic/cli version')) {
|
|
||||||
throw new Error('not found');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Unexpected command: ${command}`);
|
throw new Error(`Unexpected command: ${command}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
const { checkForUpdate } = await importUpdateChecker();
|
const { checkForUpdate } = await importUpdateChecker();
|
||||||
|
const result = checkForUpdate({ skipCache: true });
|
||||||
|
|
||||||
const modernResult = checkForUpdate();
|
expect(result.current).toBe('');
|
||||||
installedPackage = '@mosaic/cli';
|
expect(result.updateAvailable).toBe(false);
|
||||||
const legacyResult = checkForUpdate();
|
|
||||||
|
|
||||||
expect(modernResult.currentPackage).toBe('@mosaic/mosaic');
|
|
||||||
expect(modernResult.targetPackage).toBe('@mosaic/mosaic');
|
|
||||||
expect(modernResult.latest).toBe('0.0.18');
|
|
||||||
|
|
||||||
expect(legacyResult.currentPackage).toBe('@mosaic/cli');
|
|
||||||
expect(legacyResult.targetPackage).toBe('@mosaic/mosaic');
|
|
||||||
expect(legacyResult.latest).toBe('0.0.17');
|
|
||||||
expect(execSyncMock).toHaveBeenCalledWith(
|
|
||||||
expect.stringContaining('view @mosaic/cli version'),
|
|
||||||
expect.any(Object),
|
|
||||||
);
|
|
||||||
expect(execSyncMock).toHaveBeenCalledWith(
|
|
||||||
expect.stringContaining('view @mosaic/mosaic version'),
|
|
||||||
expect.any(Object),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ fi
|
|||||||
|
|
||||||
# Detect platform and close issue
|
# Detect platform and close issue
|
||||||
detect_platform
|
detect_platform
|
||||||
|
OWNER=$(get_repo_owner)
|
||||||
|
REPO=$(get_repo_name)
|
||||||
|
|
||||||
if [[ "$PLATFORM" == "github" ]]; then
|
if [[ "$PLATFORM" == "github" ]]; then
|
||||||
if [[ -n "$COMMENT" ]]; then
|
if [[ -n "$COMMENT" ]]; then
|
||||||
@@ -54,9 +56,9 @@ if [[ "$PLATFORM" == "github" ]]; then
|
|||||||
echo "Closed GitHub issue #$ISSUE_NUMBER"
|
echo "Closed GitHub issue #$ISSUE_NUMBER"
|
||||||
elif [[ "$PLATFORM" == "gitea" ]]; then
|
elif [[ "$PLATFORM" == "gitea" ]]; then
|
||||||
if [[ -n "$COMMENT" ]]; then
|
if [[ -n "$COMMENT" ]]; then
|
||||||
tea issue comment "$ISSUE_NUMBER" "$COMMENT"
|
tea issue comment "$ISSUE_NUMBER" "$COMMENT" --repo "$OWNER/$REPO" --login "${GITEA_LOGIN:-mosaicstack}"
|
||||||
fi
|
fi
|
||||||
tea issue close "$ISSUE_NUMBER"
|
tea issue close "$ISSUE_NUMBER" --repo "$OWNER/$REPO" --login "${GITEA_LOGIN:-mosaicstack}"
|
||||||
echo "Closed Gitea issue #$ISSUE_NUMBER"
|
echo "Closed Gitea issue #$ISSUE_NUMBER"
|
||||||
else
|
else
|
||||||
echo "Error: Unknown platform"
|
echo "Error: Unknown platform"
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ if [[ "$SKIP_QUEUE_GUARD" != true ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
PLATFORM=$(detect_platform)
|
PLATFORM=$(detect_platform)
|
||||||
|
OWNER=$(get_repo_owner)
|
||||||
|
REPO=$(get_repo_name)
|
||||||
|
|
||||||
case "$PLATFORM" in
|
case "$PLATFORM" in
|
||||||
github)
|
github)
|
||||||
@@ -97,8 +99,7 @@ case "$PLATFORM" in
|
|||||||
eval "$CMD"
|
eval "$CMD"
|
||||||
;;
|
;;
|
||||||
gitea)
|
gitea)
|
||||||
# tea pr merge syntax
|
CMD="tea pr merge $PR_NUMBER --style squash --repo $OWNER/$REPO --login ${GITEA_LOGIN:-mosaicstack}"
|
||||||
CMD="tea pr merge $PR_NUMBER --style squash"
|
|
||||||
|
|
||||||
# Delete branch after merge if requested
|
# Delete branch after merge if requested
|
||||||
if [[ "$DELETE_BRANCH" == true ]]; then
|
if [[ "$DELETE_BRANCH" == true ]]; then
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Mosaic update checker — compares the installed Mosaic package against the
|
* Mosaic update checker — compares the installed @mosaic/mosaic package
|
||||||
* Gitea npm registry and reports when an upgrade is available.
|
* against the Gitea npm registry and reports when an upgrade is available.
|
||||||
*
|
*
|
||||||
* Used by:
|
* Used by:
|
||||||
* - CLI startup (non-blocking background check)
|
* - CLI startup (non-blocking background check)
|
||||||
@@ -40,9 +40,7 @@ export interface UpdateCheckResult {
|
|||||||
// ─── Constants ──────────────────────────────────────────────────────────────
|
// ─── Constants ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const REGISTRY = 'https://git.mosaicstack.dev/api/packages/mosaic/npm/';
|
const REGISTRY = 'https://git.mosaicstack.dev/api/packages/mosaic/npm/';
|
||||||
const MODERN_PKG = '@mosaic/mosaic';
|
const PKG = '@mosaic/mosaic';
|
||||||
const LEGACY_PKG = '@mosaic/cli';
|
|
||||||
const INSTALLED_PACKAGE_ORDER = [MODERN_PKG, LEGACY_PKG] as const;
|
|
||||||
const CACHE_DIR = join(homedir(), '.cache', 'mosaic');
|
const CACHE_DIR = join(homedir(), '.cache', 'mosaic');
|
||||||
const CACHE_FILE = join(CACHE_DIR, 'update-check.json');
|
const CACHE_FILE = join(CACHE_DIR, 'update-check.json');
|
||||||
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
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).
|
/** Cache stores only the latest registry version (the expensive network call).
|
||||||
* The installed version is always checked fresh — it's a local `npm ls`. */
|
* The installed version is always checked fresh — it's a local `npm ls`. */
|
||||||
interface RegistryCache {
|
interface RegistryCache {
|
||||||
currentPackage?: string;
|
|
||||||
latest: string;
|
latest: string;
|
||||||
targetPackage?: string;
|
|
||||||
checkedAt: string;
|
checkedAt: string;
|
||||||
registry: string;
|
registry: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readCache(currentPackage: string): RegistryCache | null {
|
function readCache(): RegistryCache | null {
|
||||||
try {
|
try {
|
||||||
if (!existsSync(CACHE_FILE)) return null;
|
if (!existsSync(CACHE_FILE)) return null;
|
||||||
const raw = JSON.parse(readFileSync(CACHE_FILE, 'utf-8')) as RegistryCache;
|
const raw = JSON.parse(readFileSync(CACHE_FILE, 'utf-8')) as RegistryCache;
|
||||||
const age = Date.now() - new Date(raw.checkedAt).getTime();
|
const age = Date.now() - new Date(raw.checkedAt).getTime();
|
||||||
if (age > CACHE_TTL_MS) return null;
|
if (age > CACHE_TTL_MS) return null;
|
||||||
if ((raw.currentPackage || '') !== currentPackage) return null;
|
|
||||||
return raw;
|
return raw;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
@@ -163,22 +158,18 @@ function writeCache(entry: RegistryCache): void {
|
|||||||
// ─── Public API ─────────────────────────────────────────────────────────────
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the currently installed Mosaic package version.
|
* Get the currently installed @mosaic/mosaic version.
|
||||||
* Prefers the consolidated @mosaic/mosaic package over legacy @mosaic/cli.
|
|
||||||
*/
|
*/
|
||||||
export function getInstalledVersion(): { name: string; version: string } {
|
export function getInstalledVersion(): { name: string; version: string } {
|
||||||
// Fast path: check via package.json require chain
|
|
||||||
try {
|
try {
|
||||||
const raw = npmExec(`ls -g --depth=0 --json 2>/dev/null`, 3000);
|
const raw = npmExec(`ls -g --depth=0 --json 2>/dev/null`, 3000);
|
||||||
if (raw) {
|
if (raw) {
|
||||||
const data = JSON.parse(raw) as {
|
const data = JSON.parse(raw) as {
|
||||||
dependencies?: Record<string, { version?: string }>;
|
dependencies?: Record<string, { version?: string }>;
|
||||||
};
|
};
|
||||||
for (const pkg of INSTALLED_PACKAGE_ORDER) {
|
const version = data?.dependencies?.[PKG]?.version;
|
||||||
const version = data?.dependencies?.[pkg]?.version;
|
if (version) {
|
||||||
if (version) {
|
return { name: PKG, version };
|
||||||
return { name: pkg, version };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -188,27 +179,19 @@ export function getInstalledVersion(): { name: string; version: string } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the latest published version from the Gitea npm registry.
|
* Fetch the latest published @mosaic/mosaic 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.
|
|
||||||
* Returns empty string on failure.
|
* Returns empty string on failure.
|
||||||
*/
|
*/
|
||||||
export function getLatestVersion(installedPackage = ''): { name: string; version: string } {
|
export function getLatestVersion(): { name: string; version: string } {
|
||||||
const candidates =
|
const version = npmExec(`view ${PKG} version --registry=${REGISTRY}`);
|
||||||
installedPackage === LEGACY_PKG ? [LEGACY_PKG, MODERN_PKG] : [MODERN_PKG, LEGACY_PKG];
|
if (version) {
|
||||||
|
return { name: PKG, version };
|
||||||
for (const pkg of candidates) {
|
|
||||||
const version = npmExec(`view ${pkg} version --registry=${REGISTRY}`);
|
|
||||||
if (version) {
|
|
||||||
return { name: pkg, version };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { name: '', version: '' };
|
return { name: '', version: '' };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInstallCommand(result: Pick<UpdateCheckResult, 'targetPackage'>): string {
|
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;
|
let checkedAt: string;
|
||||||
|
|
||||||
if (!options?.skipCache) {
|
if (!options?.skipCache) {
|
||||||
const cached = readCache(currentInfo.name);
|
const cached = readCache();
|
||||||
if (cached) {
|
if (cached) {
|
||||||
latestInfo = {
|
latestInfo = {
|
||||||
name: cached.targetPackage || MODERN_PKG,
|
name: PKG,
|
||||||
version: cached.latest,
|
version: cached.latest,
|
||||||
};
|
};
|
||||||
checkedAt = cached.checkedAt;
|
checkedAt = cached.checkedAt;
|
||||||
} else {
|
} else {
|
||||||
latestInfo = getLatestVersion(currentInfo.name);
|
latestInfo = getLatestVersion();
|
||||||
checkedAt = new Date().toISOString();
|
checkedAt = new Date().toISOString();
|
||||||
writeCache({
|
writeCache({
|
||||||
currentPackage: currentInfo.name,
|
|
||||||
latest: latestInfo.version,
|
latest: latestInfo.version,
|
||||||
targetPackage: latestInfo.name,
|
|
||||||
checkedAt,
|
checkedAt,
|
||||||
registry: REGISTRY,
|
registry: REGISTRY,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
latestInfo = getLatestVersion(currentInfo.name);
|
latestInfo = getLatestVersion();
|
||||||
checkedAt = new Date().toISOString();
|
checkedAt = new Date().toISOString();
|
||||||
writeCache({
|
writeCache({
|
||||||
currentPackage: currentInfo.name,
|
|
||||||
latest: latestInfo.version,
|
latest: latestInfo.version,
|
||||||
targetPackage: latestInfo.name,
|
|
||||||
checkedAt,
|
checkedAt,
|
||||||
registry: REGISTRY,
|
registry: REGISTRY,
|
||||||
});
|
});
|
||||||
@@ -257,9 +236,9 @@ export function checkForUpdate(options?: { skipCache?: boolean }): UpdateCheckRe
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
current,
|
current,
|
||||||
currentPackage: currentInfo.name,
|
currentPackage: currentInfo.name || PKG,
|
||||||
latest: latestInfo.version,
|
latest: latestInfo.version,
|
||||||
targetPackage: latestInfo.name || MODERN_PKG,
|
targetPackage: latestInfo.name || PKG,
|
||||||
updateAvailable: !!(current && latestInfo.version && semverLt(current, latestInfo.version)),
|
updateAvailable: !!(current && latestInfo.version && semverLt(current, latestInfo.version)),
|
||||||
checkedAt,
|
checkedAt,
|
||||||
registry: REGISTRY,
|
registry: REGISTRY,
|
||||||
@@ -273,17 +252,12 @@ export function formatUpdateNotice(result: UpdateCheckResult): string {
|
|||||||
if (!result.updateAvailable) return '';
|
if (!result.updateAvailable) return '';
|
||||||
|
|
||||||
const installCommand = getInstallCommand(result);
|
const installCommand = getInstallCommand(result);
|
||||||
const targetChanged =
|
|
||||||
result.currentPackage && result.targetPackage && result.currentPackage !== result.targetPackage;
|
|
||||||
|
|
||||||
const lines = [
|
const lines = [
|
||||||
'',
|
'',
|
||||||
'╭─────────────────────────────────────────────────╮',
|
'╭─────────────────────────────────────────────────╮',
|
||||||
'│ │',
|
'│ │',
|
||||||
`│ Update available: ${result.current} → ${result.latest}`.padEnd(50) + '│',
|
`│ Update available: ${result.current} → ${result.latest}`.padEnd(50) + '│',
|
||||||
...(targetChanged
|
|
||||||
? [`│ Package target: ${result.currentPackage} → ${result.targetPackage}`.padEnd(50) + '│']
|
|
||||||
: []),
|
|
||||||
'│ │',
|
'│ │',
|
||||||
'│ Run: bash tools/install.sh │',
|
'│ Run: bash tools/install.sh │',
|
||||||
`│ Or: ${installCommand}`.padEnd(50) + '│',
|
`│ Or: ${installCommand}`.padEnd(50) + '│',
|
||||||
|
|||||||
Reference in New Issue
Block a user