diff --git a/.gitea/workflows/inspect-deployment.yml b/.gitea/workflows/inspect-deployment.yml index bad90e17a7..4525142b99 100644 --- a/.gitea/workflows/inspect-deployment.yml +++ b/.gitea/workflows/inspect-deployment.yml @@ -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=/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 /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'