Expand VS Code generated SSH config paths
ci / test (push) Canceled after 0s
ci / fuzz-smoke (push) Canceled after 0s
ci / macos-client (macos-aarch64, macos-14) (push) Canceled after 0s
ci / macos-client (macos-x86_64, macos-13) (push) Canceled after 0s
ci / windows-client (push) Canceled after 0s
ci / package-release (linux-x86_64, ubuntu-latest, , , ) (push) Canceled after 0s
ci / package-release (macos-aarch64, macos-14, , , ) (push) Canceled after 0s
ci / package-release (macos-x86_64, macos-13, , , ) (push) Canceled after 0s
ci / package-release (windows-aarch64, windows-latest, aarch64, windows, aarch64-pc-windows-msvc) (push) Canceled after 0s
ci / package-release (windows-x86_64, windows-latest, , , ) (push) Canceled after 0s
ci / remote-bench (push) Canceled after 0s
ci / publish-gitea-release (push) Canceled after 0s

This commit is contained in:
DuProcess
2026-07-16 22:38:52 -04:00
parent abd2317c8b
commit d471d9576a
2 changed files with 70 additions and 2 deletions
+32 -2
View File
@@ -84,7 +84,11 @@ async function configureHost() {
}); });
const sshDir = path.join(os.homedir(), '.ssh'); const sshDir = path.join(os.homedir(), '.ssh');
fs.mkdirSync(sshDir, { recursive: true }); fs.mkdirSync(sshDir, { recursive: true });
const generatedPath = config.get('generatedSshConfig') || path.join(sshDir, 'config.dosh'); const generatedPath = expandHomePath(
config.get('generatedSshConfig') || path.join(sshDir, 'config.dosh'),
os.homedir(),
process.platform
);
const mainConfig = path.join(sshDir, 'config'); const mainConfig = path.join(sshDir, 'config');
ensureInclude(mainConfig, sshIncludeTarget(mainConfig, generatedPath, process.platform)); ensureInclude(mainConfig, sshIncludeTarget(mainConfig, generatedPath, process.platform));
upsertBlock(generatedPath, alias, block); upsertBlock(generatedPath, alias, block);
@@ -116,6 +120,10 @@ function sshBlock(options) {
} }
function ensureInclude(configPath, includeFile) { function ensureInclude(configPath, includeFile) {
const dir = path.dirname(configPath);
if (dir && dir !== '.') {
fs.mkdirSync(dir, { recursive: true });
}
const raw = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : ''; const raw = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : '';
if (raw.split(/\r?\n/).some((line) => line.trim().toLowerCase() === `include ${includeFile}`.toLowerCase())) { if (raw.split(/\r?\n/).some((line) => line.trim().toLowerCase() === `include ${includeFile}`.toLowerCase())) {
return; return;
@@ -136,6 +144,10 @@ function sshIncludeTarget(mainConfigPath, generatedPath, platform = process.plat
} }
function upsertBlock(configPath, alias, block) { function upsertBlock(configPath, alias, block) {
const dir = path.dirname(configPath);
if (dir && dir !== '.') {
fs.mkdirSync(dir, { recursive: true });
}
const raw = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : ''; const raw = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : '';
const begin = `# BEGIN DOSH ${alias}`; const begin = `# BEGIN DOSH ${alias}`;
const end = `# END DOSH ${alias}`; const end = `# END DOSH ${alias}`;
@@ -173,7 +185,11 @@ async function showSshConfig() {
const api = requireVscode(); const api = requireVscode();
const config = api.workspace.getConfiguration('dosh'); const config = api.workspace.getConfiguration('dosh');
const sshDir = path.join(os.homedir(), '.ssh'); const sshDir = path.join(os.homedir(), '.ssh');
const generatedPath = config.get('generatedSshConfig') || path.join(sshDir, 'config.dosh'); const generatedPath = expandHomePath(
config.get('generatedSshConfig') || path.join(sshDir, 'config.dosh'),
os.homedir(),
process.platform
);
if (!fs.existsSync(generatedPath)) { if (!fs.existsSync(generatedPath)) {
api.window.showWarningMessage('No generated Dosh SSH config yet.'); api.window.showWarningMessage('No generated Dosh SSH config yet.');
return; return;
@@ -213,6 +229,17 @@ function normalizeCase(value, platform) {
return platform === 'win32' ? value.toLowerCase() : value; return platform === 'win32' ? value.toLowerCase() : value;
} }
function expandHomePath(value, home = os.homedir(), platform = process.platform) {
if (value === '~') {
return home;
}
if (value.startsWith('~/') || value.startsWith('~\\')) {
const pathApi = platform === 'win32' ? path.win32 : path;
return pathApi.join(home, value.slice(2));
}
return value;
}
function windowsCommandWord(value) { function windowsCommandWord(value) {
let out = '"'; let out = '"';
let backslashes = 0; let backslashes = 0;
@@ -241,10 +268,13 @@ function deactivate() {}
module.exports = { module.exports = {
activate, activate,
deactivate, deactivate,
expandHomePath,
ensureInclude,
sshBlock, sshBlock,
sshConfigPathWord, sshConfigPathWord,
shellQuote, shellQuote,
sshIncludeTarget, sshIncludeTarget,
sshConfigWord, sshConfigWord,
upsertBlock,
windowsCommandWord windowsCommandWord
}; };
+38
View File
@@ -1,12 +1,18 @@
const test = require('node:test'); const test = require('node:test');
const assert = require('node:assert/strict'); const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { const {
expandHomePath,
ensureInclude,
shellQuote, shellQuote,
sshBlock, sshBlock,
sshConfigPathWord, sshConfigPathWord,
sshIncludeTarget, sshIncludeTarget,
sshConfigWord, sshConfigWord,
upsertBlock,
windowsCommandWord windowsCommandWord
} = require('./extension'); } = require('./extension');
@@ -83,3 +89,35 @@ test('ssh include target handles Windows custom generated config paths', () => {
'C:/Users/palav/.ssh/config.dosh' 'C:/Users/palav/.ssh/config.dosh'
); );
}); });
test('home-relative generated config paths expand for POSIX and Windows', () => {
assert.equal(
expandHomePath('~/Library/Application Support/Dosh/config.dosh', '/Users/palav', 'darwin'),
'/Users/palav/Library/Application Support/Dosh/config.dosh'
);
assert.equal(
expandHomePath('~\\.ssh\\config.dosh', 'C:\\Users\\palav', 'win32'),
'C:\\Users\\palav\\.ssh\\config.dosh'
);
});
test('config writers create missing parent directories', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'dosh-vscode-'));
const mainConfig = path.join(root, 'ssh', 'config');
const generatedConfig = path.join(root, 'generated', 'config.dosh');
ensureInclude(mainConfig, '../generated/config.dosh');
upsertBlock(
generatedConfig,
'dosh-test',
[
'# BEGIN DOSH dosh-test',
'Host dosh-test',
' ProxyCommand dosh proxy-stdio test %h %p',
'# END DOSH dosh-test'
].join('\n')
);
assert.equal(fs.readFileSync(mainConfig, 'utf8'), 'Include ../generated/config.dosh\n');
assert.match(fs.readFileSync(generatedConfig, 'utf8'), /Host dosh-test/);
});