fix: v0.9.1 robustness cleanups
- run_resume falls back to a new conversation only when the transcript file is gone; other failures end at the menu with the recorded id intact. - The SessionStart hook resolves the session name under the registry lock; rename holds it across tmux rename-session and the registry move (dd_registry_locked, nestable). - dd_load_env parses KEY=value like systemd instead of sourcing the files. - devdesk-deploy: DPkg::Lock::Timeout, explicit FAILED lines on stdout and in the log; the hook answers synchronously and CI checks the HTTP status. logrotate for the deploy log. - postinst drops duplicate terminal-features entries; hooks sweep stale temp files. Closes #12 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+84
-3
@@ -15,12 +15,14 @@ setup() {
|
||||
export DEVDESK_MATRIX_WEBHOOK_FILE="$TMP/no-webhook" # never the real room
|
||||
export FAKE_LOG="$TMP/claude-args"
|
||||
export CLAUDE_BIN="$TMP/fake-claude"
|
||||
export CLAUDE_CONFIG_DIR="$TMP/claude" # never the real conversation history
|
||||
export PATH="$REPO/bin:$PATH"
|
||||
mkdir -p "$DEVDESK_PROJECTS_ROOT/alpha" "$DEVDESK_PROJECTS_ROOT/test code" "$TMP/outside" "$FAKE_LOG"
|
||||
ln -s "$TMP/outside" "$DEVDESK_PROJECTS_ROOT/escape"
|
||||
cat >"$CLAUDE_BIN" <<'EOF'
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >>"$FAKE_LOG/$DEVDESK_SESSION"
|
||||
[ -z "${FAKE_EXIT:-}" ] || exit "$FAKE_EXIT"
|
||||
exec sleep 300
|
||||
EOF
|
||||
chmod +x "$CLAUDE_BIN"
|
||||
@@ -440,12 +442,91 @@ posts() { [ -f "$TMP/posts" ] && wc -l <"$TMP/posts" || echo 0; }
|
||||
|
||||
@test "session hook records the Claude session and the next start resumes it" {
|
||||
start_server
|
||||
echo '{"session_id":"abc-123","source":"startup"}' | DEVDESK_SESSION=alpha "$REPO/libexec/devdesk-session-hook"
|
||||
id=abcabcab-1234-1234-1234-123412341234
|
||||
mkdir -p "$CLAUDE_CONFIG_DIR/projects/-p" && touch "$CLAUDE_CONFIG_DIR/projects/-p/$id.jsonl"
|
||||
echo "{\"session_id\":\"$id\",\"source\":\"startup\"}" | DEVDESK_SESSION=alpha "$REPO/libexec/devdesk-session-hook"
|
||||
run jq -r '.sessions.alpha.claude_session_id' "$DEVDESK_STATE_DIR/registry.json"
|
||||
[ "$output" = "abc-123" ]
|
||||
[ "$output" = "$id" ]
|
||||
devdesk new alpha
|
||||
run claude_args alpha
|
||||
[ "$output" = "--resume abc-123 --settings $REPO/share/claude-settings.json" ]
|
||||
[ "$output" = "--resume $id --settings $REPO/share/claude-settings.json" ]
|
||||
}
|
||||
|
||||
@test "a conversation whose file is gone starts fresh; a failed resume keeps the id (#12)" {
|
||||
start_server
|
||||
id=abcabcab-1234-1234-1234-123412341234
|
||||
echo "{\"session_id\":\"$id\"}" | DEVDESK_SESSION=alpha "$REPO/libexec/devdesk-session-hook"
|
||||
devdesk new alpha
|
||||
run claude_args alpha
|
||||
[ "$output" = "-n alpha --settings $REPO/share/claude-settings.json" ]
|
||||
tmux -S "$DEVDESK_SOCKET" kill-server
|
||||
# The file exists, but Claude exits at once (Ctrl+C, network): the menu, not a new conversation.
|
||||
mkdir -p "$CLAUDE_CONFIG_DIR/projects/-p" && touch "$CLAUDE_CONFIG_DIR/projects/-p/$id.jsonl"
|
||||
rm -f "$FAKE_LOG/alpha"
|
||||
FAKE_EXIT=1 start_server
|
||||
devdesk new alpha
|
||||
run claude_args alpha
|
||||
[ "$output" = "--resume $id --settings $REPO/share/claude-settings.json" ]
|
||||
for _ in $(seq 1 30); do
|
||||
tmux -S "$DEVDESK_SOCKET" capture-pane -p -t =alpha:claude | grep -q "Claude exited" && break
|
||||
sleep 0.1
|
||||
done
|
||||
tmux -S "$DEVDESK_SOCKET" capture-pane -p -t =alpha:claude | grep -q "Claude exited"
|
||||
[ "$(wc -l <"$FAKE_LOG/alpha")" = 1 ]
|
||||
[ "$(jq -r '.sessions.alpha.claude_session_id' "$DEVDESK_STATE_DIR/registry.json")" = "$id" ]
|
||||
}
|
||||
|
||||
@test "env files are parsed like systemd does, never run (#12)" {
|
||||
cat >"$TMP/env" <<'ENV'
|
||||
# comment
|
||||
; also a comment
|
||||
DEVDESK_DEFAULT_DIR=/srv/ops
|
||||
CLAUDE_ARGS=--model opus --verbose
|
||||
DEVDESK_PUBLIC_URL="https://dev.example.org"
|
||||
DEVDESK_GRAFANA_URL='http://g:3000'
|
||||
DEVDESK_MATRIX_DELAY=$(touch /tmp/devdesk-env-pwned)
|
||||
lowercase=x
|
||||
not a line
|
||||
DEVDESK_BIND="unterminated
|
||||
ENV
|
||||
run env DEVDESK_ENV="$TMP/env" DEVDESK_ENV_LOCAL=/dev/null bash -c '. "$1/libexec/devdesk-lib.sh"; printf "%s|" "$DEVDESK_DEFAULT_DIR" "$CLAUDE_ARGS" "$DEVDESK_PUBLIC_URL" "$DEVDESK_GRAFANA_URL" "$DEVDESK_MATRIX_DELAY" "${lowercase:-unset}" "${DEVDESK_BIND:-unset}"' _ "$REPO"
|
||||
[ ! -e /tmp/devdesk-env-pwned ]
|
||||
[[ $output == *'/srv/ops|--model opus --verbose|https://dev.example.org|http://g:3000|$(touch /tmp/devdesk-env-pwned)|unset|unset|'* ]]
|
||||
[[ $output == *"env:8: not a KEY=value line"* ]]
|
||||
[[ $output == *"env:10: unmatched quote"* ]]
|
||||
}
|
||||
|
||||
@test "the SessionStart hook reads the session name under the rename lock (#12)" {
|
||||
start_server
|
||||
devdesk new alpha
|
||||
claude_args alpha >/dev/null
|
||||
pane=$(tmux -S "$DEVDESK_SOCKET" display -p -t =alpha:claude '#{pane_id}')
|
||||
# A rename holds the lock; the hook fires meanwhile, while tmux still says "alpha".
|
||||
(
|
||||
flock 9
|
||||
sleep 0.5
|
||||
tmux -S "$DEVDESK_SOCKET" rename-session -t =alpha beta
|
||||
DEVDESK_ENV=/dev/null bash -c '. "$1/libexec/devdesk-lib.sh"; DD_REGISTRY_LOCKED=1 dd_registry_rename alpha beta' _ "$REPO"
|
||||
) 9>"$DEVDESK_STATE_DIR/registry.json.lock" 3>&- &
|
||||
renamer=$!
|
||||
sleep 0.1
|
||||
echo '{"session_id":"ffffffff-0000-0000-0000-000000000000","source":"clear"}' |
|
||||
TMUX="$DEVDESK_SOCKET,1,0" TMUX_PANE=$pane "$REPO/libexec/devdesk-session-hook"
|
||||
wait "$renamer"
|
||||
[ "$(jq -r '.sessions | keys | join(",")' "$DEVDESK_STATE_DIR/registry.json")" = beta ]
|
||||
[ "$(jq -r '.sessions.beta.claude_session_id' "$DEVDESK_STATE_DIR/registry.json")" = ffffffff-0000-0000-0000-000000000000 ]
|
||||
}
|
||||
|
||||
@test "leftover temp files of killed writers are swept (#12)" {
|
||||
mkdir -p "$DEVDESK_STATE_DIR/state" "$DEVDESK_STATE_DIR/context"
|
||||
touch -d '20 minutes ago' "$DEVDESK_STATE_DIR/state/.alpha.Ab12Cd" "$DEVDESK_STATE_DIR/context/.alpha.Xy34Zw" "$DEVDESK_STATE_DIR/registry.json.Qq11Rr"
|
||||
touch "$DEVDESK_STATE_DIR/state/.beta.Ab12Cd" "$DEVDESK_STATE_DIR/state/alpha.json"
|
||||
echo '{"session_id":"x"}' | DEVDESK_SESSION=alpha "$REPO/libexec/devdesk-session-hook"
|
||||
[ ! -e "$DEVDESK_STATE_DIR/state/.alpha.Ab12Cd" ]
|
||||
[ ! -e "$DEVDESK_STATE_DIR/context/.alpha.Xy34Zw" ]
|
||||
[ ! -e "$DEVDESK_STATE_DIR/registry.json.Qq11Rr" ]
|
||||
[ -e "$DEVDESK_STATE_DIR/state/.beta.Ab12Cd" ]
|
||||
[ -e "$DEVDESK_STATE_DIR/state/alpha.json" ]
|
||||
}
|
||||
|
||||
@test "session hook ignores sessions it does not own" {
|
||||
|
||||
Reference in New Issue
Block a user