(options: DeployOptions)
| 53 | } |
| 54 | |
| 55 | export async function deployBinary(options: DeployOptions): Promise<string> { |
| 56 | const { host, remotePlatform, remoteArch, localVersion, onProgress } = options |
| 57 | |
| 58 | if (remotePlatform !== 'linux' && remotePlatform !== 'darwin') { |
| 59 | throw new Error( |
| 60 | `Remote platform "${remotePlatform}" is not supported. Only linux and darwin are supported.`, |
| 61 | ) |
| 62 | } |
| 63 | |
| 64 | logForDebugging( |
| 65 | `[SSHDeploy] deploying to ${host} (${remotePlatform}/${remoteArch}, v${localVersion})`, |
| 66 | ) |
| 67 | |
| 68 | const localBinary = findLocalBinary() |
| 69 | logForDebugging(`[SSHDeploy] local binary: ${localBinary}`) |
| 70 | |
| 71 | onProgress?.('Creating remote directory...') |
| 72 | const mkdirResult = await runSshCommand(host, `mkdir -p ${REMOTE_BIN_DIR}`) |
| 73 | if (mkdirResult.exitCode !== 0) { |
| 74 | throw new Error(`Failed to create remote directory: ${mkdirResult.stderr}`) |
| 75 | } |
| 76 | |
| 77 | onProgress?.('Uploading binary...') |
| 78 | const remotePath = `${REMOTE_BIN_DIR}/${REMOTE_CLI_FILE}` |
| 79 | const scpProc = Bun.spawn( |
| 80 | ['scp', '-o', 'ConnectTimeout=10', localBinary, `${host}:${remotePath}`], |
| 81 | { stdout: 'pipe', stderr: 'pipe' }, |
| 82 | ) |
| 83 | const scpTimer = setTimeout(() => scpProc.kill(), SSH_TIMEOUT_MS) |
| 84 | const scpStderr = await new Response(scpProc.stderr).text() |
| 85 | const scpExit = await scpProc.exited |
| 86 | clearTimeout(scpTimer) |
| 87 | |
| 88 | if (scpExit !== 0) { |
| 89 | throw new Error(`SCP upload failed (exit ${scpExit}): ${scpStderr.trim()}`) |
| 90 | } |
| 91 | |
| 92 | onProgress?.('Installing wrapper script...') |
| 93 | const wrapperScript = [ |
| 94 | `cat > ${REMOTE_BIN_DIR}/${REMOTE_WRAPPER} << 'WRAPPER'`, |
| 95 | '#!/bin/sh', |
| 96 | `exec bun ${REMOTE_BIN_DIR}/${REMOTE_CLI_FILE} "$@"`, |
| 97 | 'WRAPPER', |
| 98 | `chmod +x ${REMOTE_BIN_DIR}/${REMOTE_WRAPPER}`, |
| 99 | ].join('\n') |
| 100 | |
| 101 | const wrapperResult = await runSshCommand(host, wrapperScript) |
| 102 | if (wrapperResult.exitCode !== 0) { |
| 103 | throw new Error(`Failed to install wrapper script: ${wrapperResult.stderr}`) |
| 104 | } |
| 105 | |
| 106 | onProgress?.('Verifying installation...') |
| 107 | const verifyResult = await runSshCommand( |
| 108 | host, |
| 109 | `${REMOTE_BIN_DIR}/${REMOTE_WRAPPER} --version`, |
| 110 | ) |
| 111 | if (verifyResult.exitCode !== 0) { |
| 112 | throw new Error( |
no test coverage detected