fix: remove non-portable askpass bridge; rely on native git credentials
All checks were successful
CI / test (push) Successful in 17s

The askpass bridge bound a Unix-domain socket to a vault filesystem path
(net.createServer().listen(...sock)). That is invalid on Windows, where
Node's listen() expects a named pipe, so publish aborted with
'listen EACCES: permission denied ...askpass.sock' before git ever ran —
even when the user's git could authenticate. The bridge was non-portable in
general (Windows named pipes; the helper script needs a node binary on PATH).

Remove the bridge and let the user's system git handle credentials via its
native helpers (Git Credential Manager / osxkeychain / libsecret) for https
and ssh-agent for ssh — the 'store nothing' model we already chose.
GIT_TERMINAL_PROMPT=0 stays so git fails fast instead of hanging, and
publish errors now include a credential/identity hint. Deletes askpass.ts
and its test (3 tests); suite 32/32, tsc clean, build OK.

Co-Authored-By: Claude
This commit is contained in:
2026-06-19 03:02:28 +00:00
parent f7c2b863f3
commit 61910acac3
4 changed files with 36 additions and 208 deletions

View File

@@ -1,9 +1,8 @@
import { Modal, Notice, Plugin, Setting, TFile, normalizePath } from "obsidian";
import { Notice, Plugin, TFile } from "obsidian";
import { DEFAULT_SETTINGS, JekyllPublishSettings } from "./settings";
import { JekyllPublishSettingTab } from "./SettingsTab";
import { ModalResult, PublishModal } from "./PublishModal";
import { ChildProcessGitClient } from "./git";
import { AskpassBridge } from "./askpass";
import { publish } from "./publish";
export default class JekyllPublishPlugin extends Plugin {
@@ -43,31 +42,26 @@ export default class JekyllPublishPlugin extends Plugin {
new Notice("Jekyll Publish: set a remote URL in settings first.");
return;
}
const dir = normalizePath(this.app.vault.configDir + "/plugins/jekyll-publish");
const adapter = this.app.vault.adapter as any;
if (typeof adapter.getFullPath !== "function") {
new Notice("Jekyll Publish requires desktop Obsidian (filesystem access unavailable).");
return;
}
const bridge = new AskpassBridge({
dir: adapter.getFullPath(dir),
onPrompt: (prompt) => this.promptCredential(prompt),
});
try {
const env = await bridge.start();
const git = new ChildProcessGitClient({ env });
// Credentials are handled entirely by the user's system git: its
// configured credential helper (Git Credential Manager on Windows,
// osxkeychain on macOS, libsecret on Linux) for https:// URLs, or the
// ssh-agent / key for ssh:// URLs. The plugin stores nothing and sets
// GIT_TERMINAL_PROMPT=0 (in ChildProcessGitClient) so git fails fast
// with a clear message instead of hanging when no helper is available.
const git = new ChildProcessGitClient();
const result = await publish(
{ noteText, ...r },
this.settings,
git,
(linktext) => this.resolveImage(linktext)
);
const warn = result.unresolved.length ? ` (${result.unresolved.length} image(s) unresolved)` : "";
const warn = result.unresolved.length
? ` (${result.unresolved.length} image(s) unresolved)`
: "";
new Notice(`Published ${result.postPath} with ${result.imageCount} image(s)${warn}`);
} catch (e) {
new Notice(`Publish failed: ${(e as Error).message}`);
} finally {
bridge.stop();
new Notice(`Publish failed: ${withCredentialHint((e as Error).message)}`, 12000);
}
}
@@ -77,26 +71,24 @@ export default class JekyllPublishPlugin extends Plugin {
const ab = await this.app.vault.readBinary(dest);
return Buffer.from(ab);
}
private promptCredential(prompt: string): Promise<string> {
return new Promise((resolve) => {
const modal = new Modal(this.app);
modal.titleEl.setText("Git credentials");
modal.contentEl.createEl("p", { text: prompt });
let value = "";
const masked = /pass|secret|token/i.test(prompt);
new Setting(modal.contentEl).addText((t) => {
if (masked) t.inputEl.type = "password";
t.onChange((v) => (value = v));
t.inputEl.addEventListener("keydown", (e) => {
if (e.key === "Enter") { modal.close(); resolve(value); }
});
});
new Setting(modal.contentEl).addButton((b) =>
b.setButtonText("OK").setCta().onClick(() => { modal.close(); resolve(value); })
);
modal.onClose = () => resolve(value);
modal.open();
});
}
}
/**
* When git fails for an authentication reason, append guidance pointing the
* user at their system credential helper / ssh setup, since the plugin
* deliberately does not store or prompt for credentials itself.
*/
function withCredentialHint(message: string): string {
const authPattern =
/could not read (Username|Password)|terminal prompts disabled|Authentication failed|Permission denied|access denied|fatal: Authentication|no email was given|Author identity unknown/i;
if (authPattern.test(message)) {
return (
`${message}\n\n` +
"Git could not authenticate or identify you. For an https:// URL, set up a " +
"git credential helper (e.g. Git Credential Manager). For an ssh:// URL, make " +
"sure your SSH key/agent is configured. You can also set an author name/email " +
"in the plugin settings."
);
}
return message;
}