@lvnt/release-radar-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/release-radar-cli.js +2 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +23 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +38 -0
- package/dist/downloader.d.ts +7 -0
- package/dist/downloader.js +25 -0
- package/dist/downloader.test.d.ts +1 -0
- package/dist/downloader.test.js +17 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +89 -0
- package/dist/tracker.d.ts +15 -0
- package/dist/tracker.js +32 -0
- package/dist/tracker.test.d.ts +1 -0
- package/dist/tracker.test.js +32 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.js +1 -0
- package/dist/ui.d.ts +15 -0
- package/dist/ui.js +86 -0
- package/dist/updater.d.ts +1 -0
- package/dist/updater.js +51 -0
- package/dist/versions.d.ts +2 -0
- package/dist/versions.js +10 -0
- package/package.json +40 -0
- package/versions.json +21 -0
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
export class ConfigManager {
|
|
5
|
+
configPath;
|
|
6
|
+
constructor(baseDir) {
|
|
7
|
+
const dir = baseDir ?? join(homedir(), '.release-radar-cli');
|
|
8
|
+
if (!existsSync(dir)) {
|
|
9
|
+
mkdirSync(dir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
this.configPath = join(dir, 'config.json');
|
|
12
|
+
}
|
|
13
|
+
isConfigured() {
|
|
14
|
+
return existsSync(this.configPath);
|
|
15
|
+
}
|
|
16
|
+
load() {
|
|
17
|
+
const content = readFileSync(this.configPath, 'utf-8');
|
|
18
|
+
return JSON.parse(content);
|
|
19
|
+
}
|
|
20
|
+
save(config) {
|
|
21
|
+
writeFileSync(this.configPath, JSON.stringify(config, null, 2));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { ConfigManager } from './config.js';
|
|
3
|
+
import { mkdtempSync, rmSync, existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
describe('ConfigManager', () => {
|
|
7
|
+
let tempDir;
|
|
8
|
+
let configManager;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
tempDir = mkdtempSync(join(tmpdir(), 'cli-test-'));
|
|
11
|
+
configManager = new ConfigManager(tempDir);
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
rmSync(tempDir, { recursive: true });
|
|
15
|
+
});
|
|
16
|
+
it('returns false for isConfigured when no config exists', () => {
|
|
17
|
+
expect(configManager.isConfigured()).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
it('saves and loads config correctly', () => {
|
|
20
|
+
configManager.save({
|
|
21
|
+
nexusUrl: 'http://nexus.local',
|
|
22
|
+
downloadDir: '/downloads',
|
|
23
|
+
});
|
|
24
|
+
expect(configManager.isConfigured()).toBe(true);
|
|
25
|
+
const loaded = configManager.load();
|
|
26
|
+
expect(loaded.nexusUrl).toBe('http://nexus.local');
|
|
27
|
+
expect(loaded.downloadDir).toBe('/downloads');
|
|
28
|
+
});
|
|
29
|
+
it('creates config directory if it does not exist', () => {
|
|
30
|
+
const configPath = join(tempDir, 'config.json');
|
|
31
|
+
expect(existsSync(configPath)).toBe(false);
|
|
32
|
+
configManager.save({
|
|
33
|
+
nexusUrl: 'http://nexus.local',
|
|
34
|
+
downloadDir: '/downloads',
|
|
35
|
+
});
|
|
36
|
+
expect(existsSync(configPath)).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function replaceNexusUrl(url: string, nexusUrl: string): string;
|
|
2
|
+
export declare function buildWgetCommand(url: string, outputPath: string): string;
|
|
3
|
+
export interface DownloadResult {
|
|
4
|
+
success: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function downloadFile(url: string, downloadDir: string, filename: string, nexusUrl: string): DownloadResult;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
4
|
+
export function replaceNexusUrl(url, nexusUrl) {
|
|
5
|
+
return url.replace('{{NEXUS_URL}}', nexusUrl);
|
|
6
|
+
}
|
|
7
|
+
export function buildWgetCommand(url, outputPath) {
|
|
8
|
+
return `wget -O "${outputPath}" "${url}"`;
|
|
9
|
+
}
|
|
10
|
+
export function downloadFile(url, downloadDir, filename, nexusUrl) {
|
|
11
|
+
const resolvedUrl = replaceNexusUrl(url, nexusUrl);
|
|
12
|
+
if (!existsSync(downloadDir)) {
|
|
13
|
+
mkdirSync(downloadDir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
const outputPath = join(downloadDir, filename);
|
|
16
|
+
const command = buildWgetCommand(resolvedUrl, outputPath);
|
|
17
|
+
try {
|
|
18
|
+
execSync(command, { stdio: 'inherit' });
|
|
19
|
+
return { success: true };
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
23
|
+
return { success: false, error: message };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { buildWgetCommand, replaceNexusUrl } from './downloader.js';
|
|
3
|
+
describe('downloader', () => {
|
|
4
|
+
describe('replaceNexusUrl', () => {
|
|
5
|
+
it('replaces {{NEXUS_URL}} placeholder', () => {
|
|
6
|
+
const url = '{{NEXUS_URL}}/github.com/ninja/v1.0/ninja.zip';
|
|
7
|
+
const result = replaceNexusUrl(url, 'http://nexus.local');
|
|
8
|
+
expect(result).toBe('http://nexus.local/github.com/ninja/v1.0/ninja.zip');
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
describe('buildWgetCommand', () => {
|
|
12
|
+
it('builds correct wget command', () => {
|
|
13
|
+
const cmd = buildWgetCommand('http://nexus.local/file.zip', '/downloads/file.zip');
|
|
14
|
+
expect(cmd).toBe('wget -O "/downloads/file.zip" "http://nexus.local/file.zip"');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { ConfigManager } from './config.js';
|
|
3
|
+
import { DownloadTracker } from './tracker.js';
|
|
4
|
+
import { loadVersions } from './versions.js';
|
|
5
|
+
import { checkAndUpdate } from './updater.js';
|
|
6
|
+
import { downloadFile } from './downloader.js';
|
|
7
|
+
import { promptSetup, promptToolSelection } from './ui.js';
|
|
8
|
+
async function showStatus() {
|
|
9
|
+
const tracker = new DownloadTracker();
|
|
10
|
+
const versions = loadVersions();
|
|
11
|
+
const downloaded = tracker.getAll();
|
|
12
|
+
console.log(chalk.bold('\nTool Status:\n'));
|
|
13
|
+
for (const tool of versions.tools) {
|
|
14
|
+
const record = downloaded[tool.name];
|
|
15
|
+
const downloadedVersion = record?.version ?? '-';
|
|
16
|
+
let status;
|
|
17
|
+
if (!record) {
|
|
18
|
+
status = chalk.blue('NEW');
|
|
19
|
+
}
|
|
20
|
+
else if (record.version !== tool.version) {
|
|
21
|
+
status = chalk.yellow('UPDATE');
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
status = chalk.green('✓');
|
|
25
|
+
}
|
|
26
|
+
console.log(` ${tool.displayName.padEnd(20)} ${tool.version.padEnd(12)} ${downloadedVersion.padEnd(12)} ${status}`);
|
|
27
|
+
}
|
|
28
|
+
console.log('');
|
|
29
|
+
}
|
|
30
|
+
async function runConfig() {
|
|
31
|
+
const configManager = new ConfigManager();
|
|
32
|
+
const config = await promptSetup();
|
|
33
|
+
configManager.save(config);
|
|
34
|
+
console.log(chalk.green('\nConfiguration saved!'));
|
|
35
|
+
}
|
|
36
|
+
async function runInteractive() {
|
|
37
|
+
const configManager = new ConfigManager();
|
|
38
|
+
const tracker = new DownloadTracker();
|
|
39
|
+
// First run setup
|
|
40
|
+
if (!configManager.isConfigured()) {
|
|
41
|
+
const config = await promptSetup();
|
|
42
|
+
configManager.save(config);
|
|
43
|
+
console.log(chalk.green('\nConfiguration saved!\n'));
|
|
44
|
+
}
|
|
45
|
+
// Check for updates and restart if needed
|
|
46
|
+
await checkAndUpdate();
|
|
47
|
+
// Load data
|
|
48
|
+
const config = configManager.load();
|
|
49
|
+
const versions = loadVersions();
|
|
50
|
+
const downloaded = tracker.getAll();
|
|
51
|
+
// Show interactive menu
|
|
52
|
+
const selected = await promptToolSelection(versions.tools, downloaded, versions.generatedAt);
|
|
53
|
+
if (selected.length === 0) {
|
|
54
|
+
console.log(chalk.gray('\nNo tools selected. Exiting.'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Download selected tools
|
|
58
|
+
console.log('');
|
|
59
|
+
for (const tool of selected) {
|
|
60
|
+
console.log(chalk.bold(`Downloading ${tool.displayName} ${tool.version}...`));
|
|
61
|
+
const result = downloadFile(tool.downloadUrl, config.downloadDir, tool.filename, config.nexusUrl);
|
|
62
|
+
if (result.success) {
|
|
63
|
+
tracker.recordDownload(tool.name, tool.version, tool.filename);
|
|
64
|
+
console.log(chalk.green(` Saved to ${config.downloadDir}/${tool.filename} ✓\n`));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log(chalk.red(` Failed: ${result.error}\n`));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
console.log(chalk.green('Done!'));
|
|
71
|
+
}
|
|
72
|
+
async function main() {
|
|
73
|
+
const command = process.argv[2];
|
|
74
|
+
switch (command) {
|
|
75
|
+
case 'status':
|
|
76
|
+
await showStatus();
|
|
77
|
+
break;
|
|
78
|
+
case 'config':
|
|
79
|
+
await runConfig();
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
await runInteractive();
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
main().catch((error) => {
|
|
87
|
+
console.error(chalk.red('Error:'), error.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface DownloadRecord {
|
|
2
|
+
version: string;
|
|
3
|
+
downloadedAt: string;
|
|
4
|
+
filename: string;
|
|
5
|
+
}
|
|
6
|
+
export interface DownloadedState {
|
|
7
|
+
[toolName: string]: DownloadRecord;
|
|
8
|
+
}
|
|
9
|
+
export declare class DownloadTracker {
|
|
10
|
+
private filePath;
|
|
11
|
+
constructor(baseDir?: string);
|
|
12
|
+
getAll(): DownloadedState;
|
|
13
|
+
getDownloadedVersion(toolName: string): string | null;
|
|
14
|
+
recordDownload(toolName: string, version: string, filename: string): void;
|
|
15
|
+
}
|
package/dist/tracker.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
export class DownloadTracker {
|
|
5
|
+
filePath;
|
|
6
|
+
constructor(baseDir) {
|
|
7
|
+
const dir = baseDir ?? join(homedir(), '.release-radar-cli');
|
|
8
|
+
if (!existsSync(dir)) {
|
|
9
|
+
mkdirSync(dir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
this.filePath = join(dir, 'downloaded.json');
|
|
12
|
+
}
|
|
13
|
+
getAll() {
|
|
14
|
+
if (!existsSync(this.filePath)) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
return JSON.parse(readFileSync(this.filePath, 'utf-8'));
|
|
18
|
+
}
|
|
19
|
+
getDownloadedVersion(toolName) {
|
|
20
|
+
const all = this.getAll();
|
|
21
|
+
return all[toolName]?.version ?? null;
|
|
22
|
+
}
|
|
23
|
+
recordDownload(toolName, version, filename) {
|
|
24
|
+
const all = this.getAll();
|
|
25
|
+
all[toolName] = {
|
|
26
|
+
version,
|
|
27
|
+
downloadedAt: new Date().toISOString(),
|
|
28
|
+
filename,
|
|
29
|
+
};
|
|
30
|
+
writeFileSync(this.filePath, JSON.stringify(all, null, 2));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { DownloadTracker } from './tracker.js';
|
|
3
|
+
import { mkdtempSync, rmSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
describe('DownloadTracker', () => {
|
|
7
|
+
let tempDir;
|
|
8
|
+
let tracker;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tracker-test-'));
|
|
11
|
+
tracker = new DownloadTracker(tempDir);
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
rmSync(tempDir, { recursive: true });
|
|
15
|
+
});
|
|
16
|
+
it('returns null for untracked tool', () => {
|
|
17
|
+
expect(tracker.getDownloadedVersion('unknown')).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
it('tracks downloaded version', () => {
|
|
20
|
+
tracker.recordDownload('Ninja', '1.12.0', 'ninja-1.12.0.zip');
|
|
21
|
+
const version = tracker.getDownloadedVersion('Ninja');
|
|
22
|
+
expect(version).toBe('1.12.0');
|
|
23
|
+
});
|
|
24
|
+
it('returns all downloaded tools', () => {
|
|
25
|
+
tracker.recordDownload('Ninja', '1.12.0', 'ninja.zip');
|
|
26
|
+
tracker.recordDownload('CMake', '3.28.0', 'cmake.tar.gz');
|
|
27
|
+
const all = tracker.getAll();
|
|
28
|
+
expect(Object.keys(all)).toHaveLength(2);
|
|
29
|
+
expect(all['Ninja'].version).toBe('1.12.0');
|
|
30
|
+
expect(all['CMake'].version).toBe('3.28.0');
|
|
31
|
+
});
|
|
32
|
+
});
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { VersionsJsonTool } from './types.js';
|
|
2
|
+
import type { DownloadedState } from './tracker.js';
|
|
3
|
+
import type { CliConfig } from './config.js';
|
|
4
|
+
export declare function promptSetup(): Promise<CliConfig>;
|
|
5
|
+
interface ToolChoice {
|
|
6
|
+
name: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
version: string;
|
|
9
|
+
downloadUrl: string;
|
|
10
|
+
filename: string;
|
|
11
|
+
status: 'new' | 'update' | 'current';
|
|
12
|
+
downloadedVersion: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function promptToolSelection(tools: VersionsJsonTool[], downloaded: DownloadedState, generatedAt: string): Promise<ToolChoice[]>;
|
|
15
|
+
export {};
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
export async function promptSetup() {
|
|
4
|
+
console.log(chalk.bold('\nWelcome to release-radar-cli!\n'));
|
|
5
|
+
console.log("Let's configure your settings.\n");
|
|
6
|
+
const answers = await inquirer.prompt([
|
|
7
|
+
{
|
|
8
|
+
type: 'input',
|
|
9
|
+
name: 'nexusUrl',
|
|
10
|
+
message: 'Enter your Nexus proxy base URL:',
|
|
11
|
+
validate: (input) => {
|
|
12
|
+
if (!input.trim())
|
|
13
|
+
return 'URL is required';
|
|
14
|
+
if (!input.startsWith('http'))
|
|
15
|
+
return 'URL must start with http:// or https://';
|
|
16
|
+
return true;
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'input',
|
|
21
|
+
name: 'downloadDir',
|
|
22
|
+
message: 'Enter download directory:',
|
|
23
|
+
default: '~/downloads/tools',
|
|
24
|
+
validate: (input) => input.trim() ? true : 'Directory is required',
|
|
25
|
+
},
|
|
26
|
+
]);
|
|
27
|
+
return {
|
|
28
|
+
nexusUrl: answers.nexusUrl.replace(/\/$/, ''), // Remove trailing slash
|
|
29
|
+
downloadDir: answers.downloadDir.replace('~', process.env.HOME || ''),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function getStatus(tool, downloaded) {
|
|
33
|
+
const record = downloaded[tool.name];
|
|
34
|
+
if (!record) {
|
|
35
|
+
return { status: 'new', downloadedVersion: null };
|
|
36
|
+
}
|
|
37
|
+
if (record.version !== tool.version) {
|
|
38
|
+
return { status: 'update', downloadedVersion: record.version };
|
|
39
|
+
}
|
|
40
|
+
return { status: 'current', downloadedVersion: record.version };
|
|
41
|
+
}
|
|
42
|
+
export async function promptToolSelection(tools, downloaded, generatedAt) {
|
|
43
|
+
const choices = tools.map((tool) => {
|
|
44
|
+
const { status, downloadedVersion } = getStatus(tool, downloaded);
|
|
45
|
+
return {
|
|
46
|
+
...tool,
|
|
47
|
+
status,
|
|
48
|
+
downloadedVersion,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
console.log(chalk.bold(`\nrelease-radar-cli`));
|
|
52
|
+
console.log(chalk.gray(`Last updated: ${new Date(generatedAt).toLocaleString()}\n`));
|
|
53
|
+
// Display table
|
|
54
|
+
console.log(chalk.bold(' Tool Latest Downloaded Status'));
|
|
55
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
56
|
+
choices.forEach((choice) => {
|
|
57
|
+
const downloadedStr = choice.downloadedVersion ?? '-';
|
|
58
|
+
let statusStr;
|
|
59
|
+
switch (choice.status) {
|
|
60
|
+
case 'new':
|
|
61
|
+
statusStr = chalk.blue('NEW');
|
|
62
|
+
break;
|
|
63
|
+
case 'update':
|
|
64
|
+
statusStr = chalk.yellow('UPDATE');
|
|
65
|
+
break;
|
|
66
|
+
case 'current':
|
|
67
|
+
statusStr = chalk.green('✓');
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
console.log(` ${choice.displayName.padEnd(18)} ${choice.version.padEnd(12)} ${downloadedStr.padEnd(12)} ${statusStr}`);
|
|
71
|
+
});
|
|
72
|
+
console.log('');
|
|
73
|
+
const { selected } = await inquirer.prompt([
|
|
74
|
+
{
|
|
75
|
+
type: 'checkbox',
|
|
76
|
+
name: 'selected',
|
|
77
|
+
message: 'Select tools to download:',
|
|
78
|
+
choices: choices.map((choice) => ({
|
|
79
|
+
name: `${choice.displayName} ${choice.version}`,
|
|
80
|
+
value: choice,
|
|
81
|
+
checked: choice.status !== 'current',
|
|
82
|
+
})),
|
|
83
|
+
},
|
|
84
|
+
]);
|
|
85
|
+
return selected;
|
|
86
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkAndUpdate(): Promise<boolean>;
|
package/dist/updater.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { execSync, spawn } from 'child_process';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
function getCurrentVersion() {
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const pkgPath = join(__dirname, '..', 'package.json');
|
|
8
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
9
|
+
return pkg.version;
|
|
10
|
+
}
|
|
11
|
+
function getLatestVersion() {
|
|
12
|
+
try {
|
|
13
|
+
const result = execSync('npm view @lvnt/release-radar-cli version', {
|
|
14
|
+
encoding: 'utf-8',
|
|
15
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
16
|
+
});
|
|
17
|
+
return result.trim();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function checkAndUpdate() {
|
|
24
|
+
const current = getCurrentVersion();
|
|
25
|
+
const latest = getLatestVersion();
|
|
26
|
+
if (!latest) {
|
|
27
|
+
// Can't reach npm registry, continue with current
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (current === latest) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
console.log(`Updating from ${current} to ${latest}...`);
|
|
34
|
+
try {
|
|
35
|
+
execSync('npm update -g @lvnt/release-radar-cli', {
|
|
36
|
+
stdio: 'inherit',
|
|
37
|
+
});
|
|
38
|
+
console.log('Update complete. Restarting...\n');
|
|
39
|
+
// Restart self
|
|
40
|
+
const child = spawn(process.argv[0], process.argv.slice(1), {
|
|
41
|
+
detached: true,
|
|
42
|
+
stdio: 'inherit',
|
|
43
|
+
});
|
|
44
|
+
child.unref();
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error('Update failed, continuing with current version');
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
package/dist/versions.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
export function loadVersions() {
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
// In dist/, versions.json is at package root
|
|
7
|
+
const versionsPath = join(__dirname, '..', 'versions.json');
|
|
8
|
+
const content = readFileSync(versionsPath, 'utf-8');
|
|
9
|
+
return JSON.parse(content);
|
|
10
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lvnt/release-radar-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Interactive CLI for downloading tools through Nexus proxy",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"release-radar-cli": "bin/release-radar-cli.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"bin",
|
|
19
|
+
"versions.json"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"release",
|
|
23
|
+
"download",
|
|
24
|
+
"nexus",
|
|
25
|
+
"cli"
|
|
26
|
+
],
|
|
27
|
+
"author": "lvnt",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"chalk": "^5.3.0",
|
|
31
|
+
"inquirer": "^9.2.12"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/inquirer": "^9.0.7",
|
|
35
|
+
"@types/node": "^20.10.0",
|
|
36
|
+
"tsx": "^4.7.0",
|
|
37
|
+
"typescript": "^5.3.0",
|
|
38
|
+
"vitest": "^1.1.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/versions.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generatedAt": "2026-01-24T10:00:00Z",
|
|
3
|
+
"tools": [
|
|
4
|
+
{
|
|
5
|
+
"name": "Ninja",
|
|
6
|
+
"displayName": "Ninja",
|
|
7
|
+
"version": "1.12.0",
|
|
8
|
+
"publishedAt": "2026-01-20T00:00:00Z",
|
|
9
|
+
"downloadUrl": "{{NEXUS_URL}}/github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux.zip",
|
|
10
|
+
"filename": "ninja-1.12.0-linux.zip"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "CMake",
|
|
14
|
+
"displayName": "CMake",
|
|
15
|
+
"version": "3.28.1",
|
|
16
|
+
"publishedAt": "2026-01-18T00:00:00Z",
|
|
17
|
+
"downloadUrl": "{{NEXUS_URL}}/github.com/Kitware/CMake/releases/download/v3.28.1/cmake-3.28.1-linux-x86_64.tar.gz",
|
|
18
|
+
"filename": "cmake-3.28.1-linux-x86_64.tar.gz"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|