fix(ci): give the smoke-test container a per-commit name
CI / lint (pull_request) Successful in 55s
docker / lint (pull_request) Successful in 56s
CI / test (pull_request) Successful in 1m6s
docker / test (pull_request) Successful in 1m4s
CI / publish (pull_request) Has been skipped
docker / docker (pull_request) Successful in 41s

The Docker smoke-test ran the server container under the fixed name
familiar-smoke. On the self-hosted runner the job shares the host
Docker daemon, so a container left behind by a killed or re-run job
(or a concurrent dev push + PR) collides on that name and 'docker run'
fails with exit 125.

Derive the name from the commit SHA, remove any stale container of that
name before starting, and thread the name through the smoke-test and
cleanup steps.
This commit is contained in:
Latte committed 2026-07-05 13:55:29 +02:00
1 parent 6c4cbd13ad
commit 288c696f28
1 file changed
+14 -6
+14 -6
View File
@@ -52,6 +52,9 @@ jobs:
IMAGE="git.hiddenden.cafe/${GITHUB_REPOSITORY,,}" IMAGE="git.hiddenden.cafe/${GITHUB_REPOSITORY,,}"
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
echo "sha_tag=${IMAGE}:sha-${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT" echo "sha_tag=${IMAGE}:sha-${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT"
# Per-commit container name: the self-hosted runner shares the host
# Docker daemon, so a fixed name collides across concurrent/re-run jobs.
echo "container=familiar-smoke-${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT"
if [ "${GITHUB_REF_NAME}" = "main" ]; then if [ "${GITHUB_REF_NAME}" = "main" ]; then
echo "branch_tags=${IMAGE}:latest ${IMAGE}:main" >> "$GITHUB_OUTPUT" echo "branch_tags=${IMAGE}:latest ${IMAGE}:main" >> "$GITHUB_OUTPUT"
else else
@@ -67,8 +70,13 @@ jobs:
run: | run: |
set -euo pipefail set -euo pipefail
IMG="${{ steps.meta.outputs.sha_tag }}" IMG="${{ steps.meta.outputs.sha_tag }}"
CONTAINER="${{ steps.meta.outputs.container }}"
TOKEN="smoke-$(date +%s)" TOKEN="smoke-$(date +%s)"
# Remove any container left behind by a killed/re-run job before we
# reuse the name (shared host daemon → stale containers can survive).
docker rm -f "$CONTAINER" >/dev/null 2>&1 || true
# 1. Runs as a non-root user (uid 1000). # 1. Runs as a non-root user (uid 1000).
uid=$(docker run --rm --entrypoint id "$IMG" -u) uid=$(docker run --rm --entrypoint id "$IMG" -u)
echo "container uid: $uid" echo "container uid: $uid"
@@ -83,13 +91,13 @@ jobs:
# socket and runs the probe in the server's own netns, where # socket and runs the probe in the server's own netns, where
# 127.0.0.1:8080 always works regardless of runner topology. The image # 127.0.0.1:8080 always works regardless of runner topology. The image
# ships python + httpx, so no curl or extra pull is needed. # ships python + httpx, so no curl or extra pull is needed.
docker run -d --name familiar-smoke -e FAMILIAR_HTTP_TOKEN="$TOKEN" "$IMG" docker run -d --name "$CONTAINER" -e FAMILIAR_HTTP_TOKEN="$TOKEN" "$IMG"
# probe "<httpx call returning a Response>" -> prints the status code # probe "<httpx call returning a Response>" -> prints the status code
probe() { docker exec familiar-smoke python -c "import httpx; print(($1).status_code)"; } probe() { docker exec "$CONTAINER" python -c "import httpx; print(($1).status_code)"; }
for i in $(seq 1 30); do for i in $(seq 1 30); do
if docker exec familiar-smoke python -c \ if docker exec "$CONTAINER" python -c \
"import httpx; httpx.get('http://127.0.0.1:8080/healthz', timeout=5)" >/dev/null 2>&1; then "import httpx; httpx.get('http://127.0.0.1:8080/healthz', timeout=5)" >/dev/null 2>&1; then
break break
fi fi
@@ -98,7 +106,7 @@ jobs:
echo "== /healthz (expect 200) ==" echo "== /healthz (expect 200) =="
code=$(probe "httpx.get('http://127.0.0.1:8080/healthz', timeout=10)") code=$(probe "httpx.get('http://127.0.0.1:8080/healthz', timeout=10)")
[ "$code" = "200" ] || { echo "::error::/healthz returned $code"; docker logs familiar-smoke; exit 1; } [ "$code" = "200" ] || { echo "::error::/healthz returned $code"; docker logs "$CONTAINER"; exit 1; }
echo "== MCP without token (expect 401) ==" echo "== MCP without token (expect 401) =="
code=$(probe "httpx.post('http://127.0.0.1:8080/mcp', timeout=10, headers={'Accept':'application/json, text/event-stream','Content-Type':'application/json'}, json={'jsonrpc':'2.0','id':1,'method':'initialize','params':{}})") code=$(probe "httpx.post('http://127.0.0.1:8080/mcp', timeout=10, headers={'Accept':'application/json, text/event-stream','Content-Type':'application/json'}, json={'jsonrpc':'2.0','id':1,'method':'initialize','params':{}})")
@@ -106,7 +114,7 @@ jobs:
echo "== MCP with valid token (expect 200) ==" echo "== MCP with valid token (expect 200) =="
code=$(probe "httpx.post('http://127.0.0.1:8080/mcp', timeout=10, headers={'Authorization':'Bearer $TOKEN','Accept':'application/json, text/event-stream','Content-Type':'application/json'}, json={'jsonrpc':'2.0','id':1,'method':'initialize','params':{'protocolVersion':'2025-06-18','capabilities':{},'clientInfo':{'name':'ci','version':'1'}}})") code=$(probe "httpx.post('http://127.0.0.1:8080/mcp', timeout=10, headers={'Authorization':'Bearer $TOKEN','Accept':'application/json, text/event-stream','Content-Type':'application/json'}, json={'jsonrpc':'2.0','id':1,'method':'initialize','params':{'protocolVersion':'2025-06-18','capabilities':{},'clientInfo':{'name':'ci','version':'1'}}})")
[ "$code" = "200" ] || { echo "::error::authenticated MCP returned $code"; docker logs familiar-smoke; exit 1; } [ "$code" = "200" ] || { echo "::error::authenticated MCP returned $code"; docker logs "$CONTAINER"; exit 1; }
echo "Image smoke-test passed." echo "Image smoke-test passed."
@@ -135,7 +143,7 @@ jobs:
if: always() if: always()
shell: bash shell: bash
run: | run: |
docker rm -f familiar-smoke || true docker rm -f ${{ steps.meta.outputs.container }} || true
docker rmi -f ${{ steps.meta.outputs.sha_tag }} ${{ steps.meta.outputs.branch_tags }} || true docker rmi -f ${{ steps.meta.outputs.sha_tag }} ${{ steps.meta.outputs.branch_tags }} || true
docker image prune -f || true docker image prune -f || true
docker builder prune -f || true docker builder prune -f || true