114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import * as fs from 'node:fs/promises';
|
|
import * as os from 'node:os';
|
|
import * as path from 'node:path';
|
|
import {
|
|
buildSshArgs,
|
|
listConnectableHosts,
|
|
parseSshConfig,
|
|
readSshConfig,
|
|
saveSshHostConfig,
|
|
} from '../packages/ssh/ts/index.js';
|
|
|
|
tap.test('should parse ssh config hosts', async () => {
|
|
const hosts = parseSshConfig(`
|
|
Host *
|
|
ServerAliveInterval 30
|
|
|
|
Host dev-box staging-box
|
|
HostName dev.example.com
|
|
User deploy
|
|
Port 2222
|
|
IdentityFile ~/.ssh/id_ed25519
|
|
ProxyJump bastion
|
|
|
|
Host *.internal
|
|
User ignored
|
|
`);
|
|
|
|
const connectableHosts = listConnectableHosts(hosts);
|
|
expect(connectableHosts).toHaveLength(2);
|
|
expect(connectableHosts[0]!.alias).toEqual('dev-box');
|
|
expect(connectableHosts[0]!.hostName).toEqual('dev.example.com');
|
|
expect(connectableHosts[0]!.user).toEqual('deploy');
|
|
expect(connectableHosts[0]!.port).toEqual(2222);
|
|
expect(connectableHosts[0]!.identityFiles[0]!).toEndWith('/.ssh/id_ed25519');
|
|
expect(connectableHosts[0]!.proxyJump).toEqual('bastion');
|
|
});
|
|
|
|
tap.test('should build ssh args with destination and command', async () => {
|
|
const args = buildSshArgs(
|
|
{
|
|
id: 'dev-box',
|
|
hostAlias: 'dev-box',
|
|
user: 'deploy',
|
|
port: 2222,
|
|
},
|
|
'uname -a',
|
|
);
|
|
|
|
expect(args).toContain('-p');
|
|
expect(args).toContain('2222');
|
|
expect(args).toContain('deploy@dev-box');
|
|
expect(args[args.length - 1]).toEqual('uname -a');
|
|
});
|
|
|
|
tap.test('should build ssh args for one-time hostname overrides', async () => {
|
|
const args = buildSshArgs(
|
|
{
|
|
id: 'manual-box',
|
|
hostAlias: 'manual-box',
|
|
hostName: '192.168.1.20',
|
|
user: 'root',
|
|
},
|
|
'uname -a',
|
|
);
|
|
|
|
expect(args).toContain('-o');
|
|
expect(args).toContain('HostName=192.168.1.20');
|
|
expect(args).toContain('root@manual-box');
|
|
});
|
|
|
|
tap.test('should save and update ssh host config', async () => {
|
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitzone-ssh-'));
|
|
const configPath = path.join(tempDir, '.ssh', 'config');
|
|
|
|
await saveSshHostConfig({
|
|
alias: 'dev-box',
|
|
hostName: 'dev.example.com',
|
|
user: 'deploy',
|
|
port: 22,
|
|
identityFile: '~/.ssh/id_ed25519',
|
|
}, configPath);
|
|
await saveSshHostConfig({
|
|
alias: 'dev-box',
|
|
hostName: 'dev2.example.com',
|
|
user: 'deploy',
|
|
port: 2200,
|
|
}, configPath);
|
|
|
|
const configText = await fs.readFile(configPath, 'utf8');
|
|
const hosts = parseSshConfig(configText);
|
|
expect(hosts).toHaveLength(1);
|
|
expect(hosts[0]!.hostName).toEqual('dev2.example.com');
|
|
expect(hosts[0]!.port).toEqual(2200);
|
|
expect(configText.includes('dev.example.com')).toEqual(false);
|
|
});
|
|
|
|
tap.test('should read included ssh config files', async () => {
|
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitzone-ssh-'));
|
|
const sshDir = path.join(tempDir, '.ssh');
|
|
const includeDir = path.join(sshDir, 'config.d');
|
|
await fs.mkdir(includeDir, { recursive: true });
|
|
await fs.writeFile(path.join(sshDir, 'config'), 'Include config.d/*\n');
|
|
await fs.writeFile(path.join(includeDir, 'dev.conf'), 'Host included-box\n HostName included.example.com\n');
|
|
|
|
const hosts = await readSshConfig(path.join(sshDir, 'config'));
|
|
const connectableHosts = listConnectableHosts(hosts);
|
|
expect(connectableHosts).toHaveLength(1);
|
|
expect(connectableHosts[0]!.alias).toEqual('included-box');
|
|
expect(connectableHosts[0]!.hostName).toEqual('included.example.com');
|
|
});
|
|
|
|
export default tap.start();
|