mirror of https://github.com/buster-so/buster.git
remove all the random .js stuff, apply fixes based on greptile comments.
This commit is contained in:
parent
af9014a30a
commit
11978e99a4
|
@ -7,7 +7,7 @@ import {
|
|||
deleteCredentials,
|
||||
loadCredentials,
|
||||
saveCredentials,
|
||||
} from '../utils/credentials.js';
|
||||
} from '../utils/credentials';
|
||||
|
||||
interface AuthProps {
|
||||
apiKey?: string;
|
||||
|
|
|
@ -5,8 +5,8 @@ import { Box, Text, useApp, useInput } from 'ink';
|
|||
import Spinner from 'ink-spinner';
|
||||
import TextInput from 'ink-text-input';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { BusterBanner } from '../components/banner.js';
|
||||
import { type Credentials, getCredentials, saveCredentials } from '../utils/credentials.js';
|
||||
import { BusterBanner } from '../components/banner';
|
||||
import { type Credentials, getCredentials, saveCredentials } from '../utils/credentials';
|
||||
|
||||
interface InitProps {
|
||||
apiKey?: string;
|
||||
|
|
|
@ -3,11 +3,11 @@ import { Box, render, Text, useApp, useInput } from 'ink';
|
|||
import Spinner from 'ink-spinner';
|
||||
import TextInput from 'ink-text-input';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { BusterBanner } from '../components/banner.js';
|
||||
import { type Credentials, getCredentials, saveCredentials } from '../utils/credentials.js';
|
||||
import { DeployCommand } from './deploy/deploy.js';
|
||||
import { DeployOptionsSchema } from './deploy/schemas.js';
|
||||
import { InitCommand } from './init.js';
|
||||
import { BusterBanner } from '../components/banner';
|
||||
import { type Credentials, getCredentials, saveCredentials } from '../utils/credentials';
|
||||
import { DeployCommand } from './deploy/deploy';
|
||||
import { DeployOptionsSchema } from './deploy/schemas';
|
||||
import { InitCommand } from './init';
|
||||
|
||||
const DEFAULT_HOST = 'https://api2.buster.so';
|
||||
const _LOCAL_HOST = 'http://localhost:3001';
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
getDirectUpdateInstructions,
|
||||
getHomebrewUpdateInstructions,
|
||||
isInstalledViaHomebrew,
|
||||
} from './homebrew-detection.js';
|
||||
} from './homebrew-detection';
|
||||
|
||||
// Mock modules
|
||||
vi.mock('node:child_process', () => ({
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { execSync } from 'node:child_process';
|
||||
import { existsSync, readlinkSync } from 'node:fs';
|
||||
import { platform } from 'node:os';
|
||||
import chalk from 'chalk';
|
||||
|
||||
/**
|
||||
* Check if the CLI was installed via Homebrew
|
||||
|
@ -84,6 +85,3 @@ export function getDirectUpdateInstructions(): string {
|
|||
|
||||
This will download and install the latest version.`;
|
||||
}
|
||||
|
||||
// Import chalk for colored output
|
||||
import chalk from 'chalk';
|
||||
|
|
|
@ -4,12 +4,12 @@ export {
|
|||
getDirectUpdateInstructions,
|
||||
getHomebrewUpdateInstructions,
|
||||
isInstalledViaHomebrew,
|
||||
} from './homebrew-detection.js';
|
||||
export { UpdateCommand } from './update.js';
|
||||
} from './homebrew-detection';
|
||||
export { UpdateCommand } from './update';
|
||||
export {
|
||||
getBinaryFileName,
|
||||
getBinaryInfo,
|
||||
getCurrentVersion,
|
||||
updateHandler,
|
||||
} from './update-handler.js';
|
||||
export { type UpdateOptions, UpdateOptionsSchema, type UpdateResult } from './update-schemas.js';
|
||||
} from './update-handler';
|
||||
export { type UpdateOptions, UpdateOptionsSchema, type UpdateResult } from './update-schemas';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { arch, platform } from 'node:os';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { getBinaryFileName, getBinaryInfo, getCurrentVersion } from './update-handler.js';
|
||||
import { getBinaryFileName, getBinaryInfo, getCurrentVersion } from './update-handler';
|
||||
|
||||
// Mock os module
|
||||
vi.mock('node:os', () => ({
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import { execSync } from 'node:child_process';
|
||||
import { execSync, spawn } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { createWriteStream, existsSync } from 'node:fs';
|
||||
import { chmod, mkdir, rename, unlink } from 'node:fs/promises';
|
||||
import { arch, platform, tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { pipeline } from 'node:stream/promises';
|
||||
import { promisify } from 'node:util';
|
||||
import chalk from 'chalk';
|
||||
import { checkForUpdate, formatVersion } from '../../utils/version/index.js';
|
||||
import { isInstalledViaHomebrew } from './homebrew-detection.js';
|
||||
import { checkForUpdate, formatVersion } from '../../utils/version/index';
|
||||
import { isInstalledViaHomebrew } from './homebrew-detection';
|
||||
import {
|
||||
type BinaryInfo,
|
||||
type UpdateOptions,
|
||||
UpdateOptionsSchema,
|
||||
type UpdateResult,
|
||||
} from './update-schemas.js';
|
||||
} from './update-schemas';
|
||||
|
||||
const GITHUB_RELEASES_URL = 'https://github.com/buster-so/buster/releases/download';
|
||||
|
||||
|
@ -119,23 +120,74 @@ async function extractArchive(archivePath: string, destination: string): Promise
|
|||
const os = platform();
|
||||
|
||||
if (os === 'win32') {
|
||||
// Use PowerShell for Windows
|
||||
execSync(
|
||||
`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${destination}' -Force"`,
|
||||
{
|
||||
stdio: 'ignore',
|
||||
}
|
||||
);
|
||||
// Use PowerShell for Windows with spawn to avoid shell injection
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const child = spawn(
|
||||
'powershell',
|
||||
[
|
||||
'-Command',
|
||||
'Expand-Archive',
|
||||
'-Path',
|
||||
archivePath,
|
||||
'-DestinationPath',
|
||||
destination,
|
||||
'-Force',
|
||||
],
|
||||
{ stdio: 'ignore' }
|
||||
);
|
||||
|
||||
child.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`PowerShell exited with code ${code}`));
|
||||
}
|
||||
});
|
||||
|
||||
child.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
return join(destination, 'buster.exe');
|
||||
} else {
|
||||
// Use tar for Unix-like systems
|
||||
execSync(`tar -xzf "${archivePath}" -C "${destination}"`, {
|
||||
stdio: 'ignore',
|
||||
// Use tar for Unix-like systems with spawn to avoid shell injection
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const child = spawn('tar', ['-xzf', archivePath, '-C', destination], {
|
||||
stdio: 'ignore',
|
||||
});
|
||||
|
||||
child.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`tar exited with code ${code}`));
|
||||
}
|
||||
});
|
||||
|
||||
child.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
return join(destination, 'buster');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the CLI is running as a standalone binary
|
||||
*/
|
||||
function isBinaryExecution(): boolean {
|
||||
// Check if running via node/npm/pnpm/yarn/bun
|
||||
const execPath = process.execPath.toLowerCase();
|
||||
const argv0 = process.argv[0]?.toLowerCase() || '';
|
||||
|
||||
// Common indicators of non-binary execution
|
||||
const nonBinaryIndicators = ['node', 'npm', 'pnpm', 'yarn', 'bun', 'tsx', 'ts-node'];
|
||||
|
||||
return !nonBinaryIndicators.some(
|
||||
(indicator) => execPath.includes(indicator) || argv0.includes(indicator)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the current binary with the new one
|
||||
*/
|
||||
|
@ -189,6 +241,16 @@ export async function updateHandler(options: UpdateOptions): Promise<UpdateResul
|
|||
};
|
||||
}
|
||||
|
||||
// Check if running as a binary (not via node/npm/etc)
|
||||
if (!isBinaryExecution() && !validated.check && !validated.force) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Auto-update only works with standalone binary installations.\n\nYou appear to be running Buster via ${chalk.yellow(process.execPath.includes('node') ? 'Node.js' : 'a package manager')}.\n\nTo update, please reinstall Buster or use your package manager's update command.`,
|
||||
currentVersion,
|
||||
isHomebrew,
|
||||
};
|
||||
}
|
||||
|
||||
// Check for updates
|
||||
const updateCheck = await checkForUpdate(currentVersion);
|
||||
|
||||
|
@ -252,11 +314,21 @@ export async function updateHandler(options: UpdateOptions): Promise<UpdateResul
|
|||
downloadFile(binaryInfo.checksumUrl, checksumPath),
|
||||
]);
|
||||
|
||||
// Read checksum
|
||||
// Read and validate checksum
|
||||
const { readFile } = await import('node:fs/promises');
|
||||
const checksumContent = await readFile(checksumPath, 'utf-8');
|
||||
const checksumParts = checksumContent.split(' ');
|
||||
const expectedChecksum = checksumParts[0]?.trim() || '';
|
||||
|
||||
// SHA256 checksums are 64 hex characters
|
||||
// Format can be either "checksum" or "checksum filename"
|
||||
const checksumMatch = checksumContent.match(/^([a-f0-9]{64})/i);
|
||||
|
||||
if (!checksumMatch) {
|
||||
throw new Error(
|
||||
`Invalid checksum format in file. Expected SHA256 hash, got: ${checksumContent.substring(0, 100)}`
|
||||
);
|
||||
}
|
||||
|
||||
const expectedChecksum = checksumMatch[1]!.toLowerCase();
|
||||
|
||||
// Verify checksum
|
||||
console.info(chalk.blue('Verifying download...'));
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Box, Text } from 'ink';
|
||||
import Spinner from 'ink-spinner';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getCurrentVersion, updateHandler } from './update-handler.js';
|
||||
import type { UpdateOptions } from './update-schemas.js';
|
||||
import { getCurrentVersion, updateHandler } from './update-handler';
|
||||
import type { UpdateOptions } from './update-schemas';
|
||||
|
||||
interface UpdateCommandProps extends UpdateOptions {}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Box, Text, useApp } from 'ink';
|
||||
import { useEffect } from 'react';
|
||||
import { AnimatedLogo } from '../components/animated-logo.js';
|
||||
import { AnimatedLogo } from '../components/animated-logo';
|
||||
|
||||
export function Welcome() {
|
||||
const { exit } = useApp();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Box, Text } from 'ink';
|
||||
import { SimpleBigText } from './simple-big-text.js';
|
||||
import { SimpleBigText } from './simple-big-text';
|
||||
|
||||
interface BannerProps {
|
||||
showSubtitle?: boolean;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export { AnimatedLogo } from './animated-logo.js';
|
||||
export { BusterBanner } from './banner.js';
|
||||
export { Spinner } from './spinner.js';
|
||||
export { AnimatedLogo } from './animated-logo';
|
||||
export { BusterBanner } from './banner';
|
||||
export { Spinner } from './spinner';
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
import chalk from 'chalk';
|
||||
import { program } from 'commander';
|
||||
import { render } from 'ink';
|
||||
import { Auth } from './commands/auth.js';
|
||||
import { DeployCommand } from './commands/deploy/deploy.js';
|
||||
import { DeployOptionsSchema } from './commands/deploy/schemas.js';
|
||||
import { InitCommand } from './commands/init.js';
|
||||
import { UpdateCommand } from './commands/update/index.js';
|
||||
import { getCurrentVersion } from './commands/update/update-handler.js';
|
||||
import { checkForUpdate, formatVersion } from './utils/version/index.js';
|
||||
import { Auth } from './commands/auth';
|
||||
import { DeployCommand } from './commands/deploy/deploy';
|
||||
import { DeployOptionsSchema } from './commands/deploy/schemas';
|
||||
import { InitCommand } from './commands/init';
|
||||
import { UpdateCommand } from './commands/update/index';
|
||||
import { getCurrentVersion } from './commands/update/update-handler';
|
||||
import { checkForUpdate, formatVersion } from './utils/version/index';
|
||||
|
||||
// Get current version
|
||||
const currentVersion = getCurrentVersion();
|
||||
|
|
|
@ -7,19 +7,19 @@ export {
|
|||
isUpdateCheckDisabled,
|
||||
loadVersionCache,
|
||||
saveVersionCache,
|
||||
} from './version-cache.js';
|
||||
} from './version-cache';
|
||||
export {
|
||||
checkForUpdate,
|
||||
checkForUpdateInBackground,
|
||||
fetchLatestRelease,
|
||||
getLatestVersion,
|
||||
} from './version-check.js';
|
||||
} from './version-check';
|
||||
export {
|
||||
createUpdateCheckResult,
|
||||
formatVersion,
|
||||
isUpdateAvailable,
|
||||
parseVersion,
|
||||
} from './version-compare.js';
|
||||
} from './version-compare';
|
||||
|
||||
// Export types
|
||||
export type {
|
||||
|
@ -28,4 +28,4 @@ export type {
|
|||
PlatformInfo,
|
||||
UpdateCheckResult,
|
||||
VersionCache,
|
||||
} from './version-schemas.js';
|
||||
} from './version-schemas';
|
||||
|
|
|
@ -9,8 +9,8 @@ import {
|
|||
isUpdateCheckDisabled,
|
||||
loadVersionCache,
|
||||
saveVersionCache,
|
||||
} from './version-cache.js';
|
||||
import type { VersionCache } from './version-schemas.js';
|
||||
} from './version-cache';
|
||||
import type { VersionCache } from './version-schemas';
|
||||
|
||||
// Mock fs module
|
||||
vi.mock('node:fs/promises');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { homedir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { type VersionCache, VersionCacheSchema } from './version-schemas.js';
|
||||
import { type VersionCache, VersionCacheSchema } from './version-schemas';
|
||||
|
||||
const CACHE_DIR = join(homedir(), '.buster');
|
||||
const CACHE_FILE = join(CACHE_DIR, 'update-check.json');
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import { getCachedVersion, isUpdateCheckDisabled, saveVersionCache } from './version-cache.js';
|
||||
import { createUpdateCheckResult } from './version-compare.js';
|
||||
import {
|
||||
type GitHubRelease,
|
||||
GitHubReleaseSchema,
|
||||
type UpdateCheckResult,
|
||||
} from './version-schemas.js';
|
||||
import { getCachedVersion, isUpdateCheckDisabled, saveVersionCache } from './version-cache';
|
||||
import { createUpdateCheckResult } from './version-compare';
|
||||
import { type GitHubRelease, GitHubReleaseSchema, type UpdateCheckResult } from './version-schemas';
|
||||
|
||||
const GITHUB_API_URL = 'https://api.github.com/repos/buster-so/buster/releases/latest';
|
||||
const USER_AGENT = 'buster-cli';
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
formatVersion,
|
||||
isUpdateAvailable,
|
||||
parseVersion,
|
||||
} from './version-compare.js';
|
||||
} from './version-compare';
|
||||
|
||||
describe('version-compare', () => {
|
||||
describe('isUpdateAvailable', () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import semver from 'semver';
|
||||
import type { UpdateCheckResult } from './version-schemas.js';
|
||||
import type { UpdateCheckResult } from './version-schemas';
|
||||
|
||||
/**
|
||||
* Compare two semantic versions and determine if an update is available
|
||||
|
|
Loading…
Reference in New Issue