Inspect deployment: show recent logs and how the state-volume profile is composed
Read-only probe for boot failures: collapsed log tail, the profile package.json and patch layers (key-like values redacted, credentials never read), profile-local bundle packages and the rows they insert. Tooling probe falls back to the image when the container is not running.
This commit is contained in:
1 parent
a30d19c143
commit
dfaa1a82fc
1 file changed
+58
-4
@@ -2,9 +2,11 @@ name: Inspect deployment
|
||||
|
||||
# Manual-only: report what the DeepSeek Harness container on the runner host
|
||||
# (the Unraid box) is actually running — image reference and digest, whether
|
||||
# that digest is one this repo published, uptime, and whether dsh/pnpm are on
|
||||
# PATH inside it. Read-only: nothing is changed, and nothing is run inside the
|
||||
# container beyond `--version` probes.
|
||||
# that digest is one this repo published, recent logs, how the dsh profile in
|
||||
# the state volume is composed (bundle layers, user patch layers, profile-local
|
||||
# plugins), and whether dsh/pnpm are on PATH inside it. Read-only: nothing is
|
||||
# changed, the state volume is mounted read-only, credential files are never
|
||||
# read, and any key/token/secret-looking value in a patch file is redacted.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -30,19 +32,71 @@ jobs:
|
||||
echo "== $C =="
|
||||
docker inspect --format 'image ref: {{.Config.Image}}' "$C"
|
||||
docker inspect --format 'image id: {{.Image}}' "$C"
|
||||
docker inspect --format 'state: {{.State.Status}} (started {{.State.StartedAt}})' "$C"
|
||||
docker inspect --format 'state: {{.State.Status}} (started {{.State.StartedAt}}, restarts {{.RestartCount}}, exit {{.State.ExitCode}})' "$C"
|
||||
docker inspect --format 'entrypoint: {{json .Config.Entrypoint}}' "$C"
|
||||
docker inspect --format 'cmd: {{json .Config.Cmd}}' "$C"
|
||||
docker inspect --format 'user: {{json .Config.User}}' "$C"
|
||||
docker inspect --format 'mounts: {{range .Mounts}}{{.Source}} -> {{.Destination}}; {{end}}' "$C"
|
||||
docker inspect --format 'env: {{range .Config.Env}}{{.}} {{end}}' "$C" | sed -E 's/(KEY|TOKEN|SECRET|PASSWORD)=[^ ]*/\1=<redacted>/g'
|
||||
IMG=$(docker inspect --format '{{.Image}}' "$C")
|
||||
echo "image digests: $(docker image inspect --format '{{json .RepoDigests}}' "$IMG")"
|
||||
echo "image labels: $(docker image inspect --format '{{json .Config.Labels}}' "$IMG" | grep -o '"dev.milesward[^,}]*' | tr '\n' ' ' || true)"
|
||||
echo "image created: $(docker image inspect --format '{{.Created}}' "$IMG")"
|
||||
|
||||
- name: Recent container logs
|
||||
run: |
|
||||
set -eu
|
||||
C="${{ inputs.container || 'Deepseek' }}"
|
||||
echo "== last 120 log lines of $C (stack frames collapsed) =="
|
||||
docker logs --tail 120 "$C" 2>&1 | grep -v -E '^[[:space:]]+at ' || true
|
||||
|
||||
- name: Profile composition in the state volume
|
||||
run: |
|
||||
set -eu
|
||||
C="${{ inputs.container || 'Deepseek' }}"
|
||||
IMG=$(docker inspect --format '{{.Image}}' "$C")
|
||||
STATE=$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/home/node/.dsh"}}{{.Source}}{{end}}{{end}}' "$C")
|
||||
if [ -z "$STATE" ]; then
|
||||
echo "nothing mounted at /home/node/.dsh — state lives inside the container"
|
||||
exit 0
|
||||
fi
|
||||
echo "state volume: $STATE (mounted read-only below)"
|
||||
docker run --rm --entrypoint sh -v "$STATE:/state:ro" "$IMG" -c '
|
||||
cd /state || exit 1
|
||||
echo "== layout (depth 3; sessions, caches, stores, node_modules elided) =="
|
||||
find . -maxdepth 3 -not -path "*/node_modules*" -not -path "./sessions*" -not -path "./.pnpm*" -not -path "./.cache*" -not -path "./.state*" -not -path "./.config*" -not -path "./attachments*" -not -path "./storages/*" | sort
|
||||
for f in profiles/web/package.json profiles/web/cordis.patch.yml cordis.patch.yml profiles/web/cordis.yml profiles/web/pnpm-workspace.yaml; do
|
||||
if [ ! -f "$f" ]; then echo "== $f: absent =="; continue; fi
|
||||
echo "== $f ($(wc -c < "$f") bytes) =="
|
||||
sed -E "s/^([[:space:]]*[^:#]*(key|token|secret|password|authorization)[^:]*:).*/\1 <redacted>/I" "$f"
|
||||
echo
|
||||
done
|
||||
echo "== profiles/web/node_modules =="
|
||||
if [ -d profiles/web/node_modules ]; then
|
||||
ls -la profiles/web/node_modules
|
||||
ls -d profiles/web/node_modules/@*/* 2>/dev/null || true
|
||||
else
|
||||
echo "(none)"
|
||||
fi
|
||||
echo "== profile-local packages declaring a dsh bundle layer, and the rows their patch inserts =="
|
||||
for p in profiles/web/node_modules/*/package.json profiles/web/node_modules/@*/*/package.json; do
|
||||
[ -f "$p" ] || continue
|
||||
grep -q "\"bundle\"" "$p" || continue
|
||||
echo "-- $p: $(grep -o "\"version\": *\"[^\"]*\"" "$p" | head -1)"
|
||||
grep -n -E "^[[:space:]]*-[[:space:]]*(id|insert):" "$(dirname "$p")"/*.yml 2>/dev/null | head -30 || true
|
||||
done
|
||||
echo "== end =="
|
||||
'
|
||||
|
||||
- name: Probe tooling inside the running container
|
||||
run: |
|
||||
set -eu
|
||||
C="${{ inputs.container || 'Deepseek' }}"
|
||||
if [ "$(docker inspect --format '{{.State.Status}}' "$C")" != running ]; then
|
||||
echo "$C is not running ($(docker inspect --format '{{.State.Status}}' "$C")) — probing the image instead of the container"
|
||||
IMG=$(docker inspect --format '{{.Image}}' "$C")
|
||||
docker run --rm --entrypoint sh "$IMG" -c 'echo "dsh: $(command -v dsh || echo MISSING) $(dsh --version 2>/dev/null)"; echo "pnpm: $(command -v pnpm || echo MISSING) $(pnpm --version 2>/dev/null)"; echo "node: $(node --version)"'
|
||||
exit 0
|
||||
fi
|
||||
echo "== inside $C =="
|
||||
docker exec "$C" sh -c 'echo "uid=$(id -u) HOME=$HOME PATH=$PATH"; echo "dsh: $(command -v dsh || echo MISSING) $(dsh --version 2>/dev/null)"; echo "pnpm: $(command -v pnpm || echo MISSING) $(pnpm --version 2>/dev/null)"; echo "node: $(node --version)"; ls -la /usr/local/bin | grep -E "dsh|pnpm" || true'
|
||||
Reference in new issue
Block a user