Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions home-manager/services/obsidian/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ let
}
)
);

# Trigger obsidian-git's auto-backup via CDP. The Electron renderer's
# setTimeout doesn't fire under headless xvfb (futex blocks the event
# loop), but CDP uses IPC and bypasses it. All git operations still
# run through the obsidian-git plugin.
obsidianGitTrigger = pkgs.writeShellScriptBin "obsidian-git-trigger" (
builtins.readFile (
pkgs.replaceVars ./obsidian-git-trigger.sh {
inherit (pkgs) curl jq websocat;
}
)
);
in
# Only enable on kyber (gateway host) - desktops already get pkgs.obsidian
# directly via home-manager/packages/default.nix.
Expand All @@ -49,4 +61,29 @@ lib.mkIf host.isKyber {
WantedBy = [ "default.target" ];
};
};

systemd.user.services.obsidian-git-trigger = {
Unit = {
Description = "Trigger obsidian-git auto-backup via CDP";
After = [ "obsidian.service" ];
};
Service = {
Type = "oneshot";
ExecStart = "${obsidianGitTrigger}/bin/obsidian-git-trigger";
};
};

systemd.user.timers.obsidian-git-trigger = {
Unit = {
Description = "Trigger obsidian-git auto-backup every 3 minutes";
};
Timer = {
OnBootSec = "2min";
OnUnitActiveSec = "3min";
Unit = "obsidian-git-trigger.service";
};
Install = {
WantedBy = [ "timers.target" ];
};
};
}
21 changes: 21 additions & 0 deletions home-manager/services/obsidian/obsidian-git-trigger.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Trigger obsidian-git's commitAndSync via CDP.
#
# The Electron renderer's setTimeout doesn't fire under headless xvfb
# (futex_wait_queue blocks the event loop pump), but CDP messages use
# IPC and bypass the stuck loop. We trigger the backup and keep the
# websocket open with ping-interval for 15s to pump the event loop
# while git operations complete through the obsidian-git plugin.

CDP="http://localhost:9222"

WS_URL=$(@curl@/bin/curl -sf "$CDP/json" | @jq@/bin/jq -r '.[0].webSocketDebuggerUrl // empty')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current jq filter will produce a parse error on stderr if curl returns an empty array [] or if the input is empty (e.g., when the debugging port is not yet reachable). Using the optional chaining operator ? and suppressing stderr for these discovery commands will make the script more robust and keep the systemd logs cleaner during service startup or downtime.

Suggested change
WS_URL=$(@curl@/bin/curl -sf "$CDP/json" | @jq@/bin/jq -r '.[0].webSocketDebuggerUrl // empty')
WS_URL=$(@curl@/bin/curl -sf "$CDP/json" 2>/dev/null | @jq@/bin/jq -r '.[0].webSocketDebuggerUrl? // empty' 2>/dev/null)

[ -z "$WS_URL" ] && exit 0

# Send trigger, then keep stdin open for 15s so websocat stays alive.
# The --ping-interval sends websocket pings that pump the Electron
# event loop, allowing the obsidian-git promise queue to execute.
{
echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
sleep 15
} | @websocat@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true
Comment on lines +18 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not mask CDP/websocket failures.

|| true hides trigger failures and reports success to systemd even when the backup call was never delivered.

Suggested patch
 {
   echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
   sleep 15
-} | `@websocat`@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true
+} | `@websocat`@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
sleep 15
} | @websocat@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true
{
echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
sleep 15
} | `@websocat`@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@home-manager/services/obsidian/obsidian-git-trigger.sh` around lines 18 - 21,
The script masks WebSocket/CDP failures by appending "|| true" to the websocat
pipeline; remove the "|| true" so failures propagate to systemd (or replace it
with explicit error handling/logging), i.e. ensure the websocat invocation line
that pipes the JSON payload to `@websocat`@/bin/websocat --ping-interval 1
"$WS_URL" returns its exit code instead of being ignored so systemd sees and
records delivery failures of the CDP trigger.

@cubic-dev-ai cubic-dev-ai Bot Apr 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: || true masks websocat/CDP failures, so systemd always reports success even when the backup trigger wasn't delivered. Since this is a Type=oneshot service for backups, failures should propagate so they're visible in systemctl status and journal logs. The timer will retry on the next tick regardless.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/services/obsidian/obsidian-git-trigger.sh, line 21:

<comment>`|| true` masks websocat/CDP failures, so systemd always reports success even when the backup trigger wasn't delivered. Since this is a `Type=oneshot` service for backups, failures should propagate so they're visible in `systemctl status` and journal logs. The timer will retry on the next tick regardless.</comment>

<file context>
@@ -0,0 +1,21 @@
+{
+  echo '{"id":1,"method":"Runtime.evaluate","params":{"expression":"app.plugins.plugins['"'"'obsidian-git'"'"']?.automaticsManager?.doAutoCommitAndSync()"}}'
+  sleep 15
+} | @websocat@/bin/websocat --ping-interval 1 "$WS_URL" >/dev/null 2>&1 || true
</file context>
Fix with Cubic

2 changes: 1 addition & 1 deletion home-manager/services/obsidian/obsidian-headless.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env bash
exec @xvfbRun@/bin/xvfb-run -a -s "-screen 0 1280x1024x24" @obsidian@/bin/obsidian --no-sandbox --disable-gpu --disable-features=FontationsFontIndexer --vault @homeDir@/ghq/github.com/shunkakinoki/wiki "$@"
exec @xvfbRun@/bin/xvfb-run -a -s "-screen 0 1280x1024x24" @obsidian@/bin/obsidian --no-sandbox --disable-gpu --disable-features=FontationsFontIndexer --remote-debugging-port=9222 --vault @homeDir@/ghq/github.com/shunkakinoki/wiki "$@"
1 change: 1 addition & 0 deletions spec/coverage_spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ home-manager/modules/local-scripts/notify-local.sh
home-manager/modules/local-scripts/pushover-notify.sh
home-manager/modules/local-scripts/tmux-bridge.sh
home-manager/modules/npm-globals/install-npm-globals.sh
home-manager/services/obsidian/obsidian-git-trigger.sh
home-manager/services/obsidian/obsidian-headless.sh
home-manager/services/openclaw/activate.sh
home-manager/services/paperclip/activate.sh
Expand Down
48 changes: 48 additions & 0 deletions spec/obsidian_git_trigger_spec.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# shellcheck disable=SC2329

Describe 'home-manager/services/obsidian/obsidian-git-trigger.sh'
SCRIPT="$PWD/home-manager/services/obsidian/obsidian-git-trigger.sh"

Describe 'script properties'
It 'uses bash shebang'
When run bash -c "head -1 '$SCRIPT'"
The output should include '#!/usr/bin/env bash'
End

It 'passes bash syntax check after replacing placeholders'
When run bash -c "sed -e 's|@curl@|/usr|g' -e 's|@jq@|/usr|g' -e 's|@websocat@|/usr|g' '$SCRIPT' | bash -n"
The status should be success
End
End

Describe 'placeholder references'
It 'references curl via placeholder'
When run bash -c "grep '@curl@' '$SCRIPT'"
The output should include '@curl@'
End

It 'references jq via placeholder'
When run bash -c "grep '@jq@' '$SCRIPT'"
The output should include '@jq@'
End

It 'references websocat via placeholder'
When run bash -c "grep '@websocat@' '$SCRIPT'"
The output should include '@websocat@'
End
End

Describe 'CDP integration'
It 'connects to CDP on port 9222'
When run bash -c "grep '9222' '$SCRIPT'"
The output should include '9222'
End

It 'triggers doAutoCommitAndSync'
When run bash -c "grep 'doAutoCommitAndSync' '$SCRIPT'"
The output should include 'doAutoCommitAndSync'
End
End

End
Loading