Skip to content

Commit 724d3dc

Browse files
committed
Implement CLI download logic in node bootstrap
Add download functionality using npm pack and tar extraction for the Node.js internal bootstrap. Downloads latest CLI version and installs to DLX directory on first use.
1 parent 7558568 commit 724d3dc

File tree

1 file changed

+89
-7
lines changed

1 file changed

+89
-7
lines changed

src/bootstrap/node.mts

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@
1515
*/
1616

1717
import { spawn } from 'node:child_process'
18-
import { existsSync } from 'node:fs'
18+
import { existsSync, promises as fs } from 'node:fs'
19+
import path from 'node:path'
1920

20-
import { getCliEntryPoint, getCliPackageDir } from './shared/paths.mjs'
21+
import {
22+
getCliEntryPoint,
23+
getCliPackageDir,
24+
getCliPackageName,
25+
getDlxDir,
26+
} from './shared/paths.mjs'
2127

2228
/**
2329
* Check if CLI is installed.
@@ -28,18 +34,94 @@ function isCliInstalled(): boolean {
2834
return existsSync(entryPoint) && existsSync(packageJson)
2935
}
3036

37+
/**
38+
* Download CLI using npm pack command.
39+
* This delegates to npm which handles downloading and extracting the latest version.
40+
*/
41+
async function downloadCli(): Promise<void> {
42+
const packageName = getCliPackageName()
43+
const dlxDir = getDlxDir()
44+
const cliDir = getCliPackageDir()
45+
46+
await fs.mkdir(dlxDir, { recursive: true })
47+
48+
console.error(`Downloading ${packageName}...`)
49+
50+
return new Promise((resolve, reject) => {
51+
const npmPackProcess = spawn(
52+
'npm',
53+
['pack', packageName, '--pack-destination', dlxDir],
54+
{
55+
stdio: ['ignore', 'pipe', 'inherit'],
56+
},
57+
)
58+
59+
let tarballName = ''
60+
npmPackProcess.stdout?.on('data', data => {
61+
tarballName += data.toString()
62+
})
63+
64+
npmPackProcess.on('error', e => {
65+
reject(new Error(`Failed to run npm pack: ${e}`))
66+
})
67+
68+
npmPackProcess.on('exit', async code => {
69+
if (code !== 0) {
70+
reject(new Error(`npm pack exited with code ${code}`))
71+
return
72+
}
73+
74+
try {
75+
const tarballPath = path.join(dlxDir, tarballName.trim())
76+
77+
await fs.mkdir(cliDir, { recursive: true })
78+
79+
const tarExtractProcess = spawn(
80+
'tar',
81+
['-xzf', tarballPath, '-C', cliDir, '--strip-components=1'],
82+
{
83+
stdio: 'inherit',
84+
},
85+
)
86+
87+
tarExtractProcess.on('error', e => {
88+
reject(new Error(`Failed to extract tarball: ${e}`))
89+
})
90+
91+
tarExtractProcess.on('exit', async extractCode => {
92+
if (extractCode !== 0) {
93+
reject(new Error(`tar extraction exited with code ${extractCode}`))
94+
return
95+
}
96+
97+
await fs.unlink(tarballPath).catch(() => {
98+
// Ignore cleanup errors.
99+
})
100+
101+
console.error('Socket CLI installed successfully')
102+
resolve()
103+
})
104+
} catch (e) {
105+
reject(e)
106+
}
107+
})
108+
})
109+
}
110+
31111
/**
32112
* Main entry point.
33113
*/
34114
async function main(): Promise<void> {
35115
// Check if CLI is already installed.
36116
if (!isCliInstalled()) {
37-
// TODO: Implement download logic using shared utilities.
38117
console.error('Socket CLI not installed yet.')
39-
console.error('Installing from npm...')
40-
console.error('TODO: Implement download from npm registry')
41-
// eslint-disable-next-line n/no-process-exit
42-
process.exit(1)
118+
try {
119+
await downloadCli()
120+
} catch (error) {
121+
console.error('Failed to download Socket CLI:', error)
122+
// eslint-disable-next-line n/no-process-exit
123+
process.exit(1)
124+
}
43125
}
44126

45127
// CLI is installed, delegate to it.

0 commit comments

Comments
 (0)