mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-30 07:21:32 -07:00
fix(launcher): resolve remote Bun path for sync
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { assertSafeSshHost, runScp, shellQuote } from './ssh.js';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { assertSafeSshHost, resolveRemoteSubminerCommand, runScp, shellQuote } from './ssh.js';
|
||||
|
||||
test('assertSafeSshHost rejects option-like hosts', () => {
|
||||
assert.throws(() => assertSafeSshHost('-oProxyCommand=touch pwned'), /looks like an option/);
|
||||
@@ -29,3 +32,30 @@ test('runScp rejects option-like remote host components', () => {
|
||||
/SSH host that looks like an option/,
|
||||
);
|
||||
});
|
||||
|
||||
test('resolveRemoteSubminerCommand verifies the launcher under the remote runtime PATH', () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-ssh-test-'));
|
||||
const sshPath = path.join(tempDir, 'ssh');
|
||||
const originalPath = process.env.PATH;
|
||||
fs.writeFileSync(
|
||||
sshPath,
|
||||
`#!/bin/sh
|
||||
case "$2" in
|
||||
*'$HOME/.local/bin'*'$HOME/.bun/bin'*'/opt/homebrew/bin'*'/usr/local/bin'*'/usr/bin'*'/bin'*'subminer --help'*) exit 0 ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
`,
|
||||
{ mode: 0o755 },
|
||||
);
|
||||
|
||||
try {
|
||||
process.env.PATH = `${tempDir}:${originalPath ?? ''}`;
|
||||
assert.equal(
|
||||
resolveRemoteSubminerCommand('macbook', null),
|
||||
'PATH="$HOME/.local/bin:$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:$PATH" subminer',
|
||||
);
|
||||
} finally {
|
||||
process.env.PATH = originalPath;
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
+13
-9
@@ -71,23 +71,27 @@ export function shellQuote(value: string): string {
|
||||
return `'${value.replaceAll("'", `'\\''`)}'`;
|
||||
}
|
||||
|
||||
const REMOTE_RUNTIME_PATH =
|
||||
'PATH="$HOME/.local/bin:$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:$PATH"';
|
||||
|
||||
/**
|
||||
* Non-interactive SSH shells often miss ~/.local/bin in PATH, so probe the
|
||||
* configured command first and fall back to the default install location.
|
||||
* Non-interactive SSH shells often miss user-installed launchers and Bun.
|
||||
* Probe the launcher under the same deterministic PATH used by sync itself.
|
||||
*/
|
||||
export function resolveRemoteSubminerCommand(host: string, preferred: string | null): string {
|
||||
// Trusted defaults stay unquoted so the remote shell expands `~`; the
|
||||
// Trusted defaults stay unquoted so the remote shell expands `~`; a
|
||||
// user-supplied override is shell-quoted to prevent command injection.
|
||||
const candidates: Array<{ value: string; probe: string }> = preferred
|
||||
? [{ value: preferred, probe: shellQuote(preferred) }]
|
||||
const candidates: Array<{ value: string; invocation: string }> = preferred
|
||||
? [{ value: preferred, invocation: shellQuote(preferred) }]
|
||||
: [
|
||||
{ value: 'subminer', probe: 'subminer' },
|
||||
{ value: '~/.local/bin/subminer', probe: '~/.local/bin/subminer' },
|
||||
{ value: 'subminer', invocation: 'subminer' },
|
||||
{ value: '~/.local/bin/subminer', invocation: '~/.local/bin/subminer' },
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
const probe = runSsh(host, `command -v ${candidate.probe} >/dev/null 2>&1`);
|
||||
const command = `${REMOTE_RUNTIME_PATH} ${candidate.invocation}`;
|
||||
const probe = runSsh(host, `${command} --help >/dev/null 2>&1`);
|
||||
if (probe.status === 0) {
|
||||
return candidate.value;
|
||||
return command;
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
|
||||
Reference in New Issue
Block a user