fix: remove non-portable askpass bridge; rely on native git credentials
All checks were successful
CI / test (push) Successful in 17s
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:
72
src/main.ts
72
src/main.ts
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user