commit a134e2de963e5ddc5731df181d58ed09802509d0
parent 97f8fcd6fa58e150df0351eb09f28e7986195d76
Author: finwo <finwo@pm.me>
Date: Mon, 21 Sep 2026 15:00:56 +0200
Copy the smoke repo into the conductor instead of bind mounting it
Diffstat:
| M | deploy/smoke.sh | | | 78 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ |
1 file changed, 48 insertions(+), 30 deletions(-)
diff --git a/deploy/smoke.sh b/deploy/smoke.sh
@@ -23,18 +23,12 @@ PORT="${SMOKE_PORT:-18500}"
PROJECT=conductor-smoke
SECRET=smoke-trigger-secret
-# When running inside a container (e.g. CI task), 127.0.0.1 refers to the
-# container itself, not the Docker host where published ports live. Detect
-# the gateway IP so curl can reach back out.
-if [ -n "${SMOKE_HOST:-}" ]; then
- : # explicit override
-elif [ -f /proc/1/root/.dockerenv ]; then
- SMOKE_HOST=$(ip route | awk '/default/{print $3; exit}') || \
- SMOKE_HOST=$(route -n | awk '$2 ~ /^172\./{print $2; exit}') || \
- SMOKE_HOST=127.0.0.1
-else
- SMOKE_HOST=127.0.0.1
+# Published ports live on the docker host, which is the gateway when this
+# runs in a container, as it does in CI.
+if [ -z "${SMOKE_HOST:-}" ] && [ -f /.dockerenv ]; then
+ SMOKE_HOST=$(ip route 2>/dev/null | awk '/^default/{print $3; exit}')
fi
+SMOKE_HOST="${SMOKE_HOST:-127.0.0.1}"
@@ -102,7 +96,6 @@ services:
CONDUCTOR_ADMIN_PASSWORD: smoke-admin-password
volumes:
- smoke-data:/data
- - ${WORK}/repo:/repo:ro
worker:
image: conductor-worker:smoke
depends_on: [conductor]
@@ -122,8 +115,6 @@ compose() { SMOKE_TOKEN="${SMOKE_TOKEN:-unused}" docker compose -p "${PROJECT}"
log "starting the conductor"
compose up -d conductor
-# All curl requests go through port publishing. The container IP is not
-# reachable because the smoke test runs in its own network namespace.
BASE_URL="http://${SMOKE_HOST}:${PORT}"
printf 'using %s\n' "${BASE_URL}"
@@ -140,6 +131,17 @@ fi
curl -fsS "${BASE_URL}/health"
printf '\n'
+# Copied rather than bind mounted: the daemon resolves a mount source on the
+# host, where a path made in this container does not exist.
+log "copying the repository into the conductor"
+CONDUCTOR_CID=$(compose ps -q conductor)
+[ -n "${CONDUCTOR_CID}" ] || fail "the conductor container is not running"
+docker cp "${WORK}/repo" "${CONDUCTOR_CID}:/repo"
+# docker cp writes as root; git refuses a repository owned by somebody else.
+docker exec -u 0 "${CONDUCTOR_CID}" chown -R conductor:conductor /repo
+compose exec -T conductor git -C /repo rev-parse HEAD >/dev/null \
+ || fail "the copied repository is not readable inside the conductor"
+
log "registering the project and a worker"
compose exec -T conductor node src/admin-cli.js project:add demo /repo --secret "${SECRET}" >/dev/null
SMOKE_TOKEN=$(compose exec -T conductor node src/admin-cli.js token:add smoke-worker | awk '/token:/{print $2}')
@@ -161,21 +163,23 @@ printf '%s\n' "${RESPONSE}"
JOB=$(printf '%s' "${RESPONSE}" | sed 's/.*"job_id":"\([^"]*\)".*/\1/')
[ -n "${JOB}" ] || fail "no job was created"
-# The job state is the first badge on the job page.
+# The job state is the first badge on the job page. A job still being
+# compiled is not readable yet, so a refusal is just another turn.
job_state() {
- curl -fsS "${BASE_URL}/jobs/$1" \
+ curl -fsS "${BASE_URL}/jobs/$1" 2>/dev/null \
| sed -n 's/.*class="badge \([a-z]*\)".*/\1/p' | head -1
}
-# Verify /jobs is reachable before the main loop; if it's not, dump
-# diagnostic info rather than spinning silently for 90 seconds.
-sleep 1
-HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' "${BASE_URL}/jobs/${JOB}")
-if [ "${HTTP_CODE}" != "200" ]; then
- printf '/jobs/%s returned %s (expected 200)\n' "${JOB}" "${HTTP_CODE}" >&2
- curl -s "${BASE_URL}/jobs/" | head -20 >&2
- compose logs conductor | tail -20 >&2
-fi
+# A pipeline that fails to compile is recorded on the job row and nowhere
+# else, and leaves the job private, which reads here as a 404.
+job_row() {
+ compose exec -T conductor node --input-type=module -e "
+ import { DatabaseSync } from 'node:sqlite';
+ const db = new DatabaseSync(process.env.CONDUCTOR_DATABASE_PATH);
+ const row = db.prepare('SELECT state, visibility, error FROM jobs WHERE id = ?').get('$1');
+ console.log(JSON.stringify(row));
+ " 2>/dev/null || true
+}
log "waiting for the job to finish"
STATE_NOW=
@@ -185,12 +189,19 @@ while [ "${i}" -lt 90 ]; do
case "${STATE_NOW}" in
success|failed|cancelled) break ;;
esac
+ # An unreadable job page does not become readable by waiting it out.
+ if [ -z "${STATE_NOW}" ] && [ "${i}" -ge 10 ]; then
+ printf 'the job page stayed unreadable\njob row: %s\n' "$(job_row "${JOB}")" >&2
+ compose logs conductor | tail -20 >&2
+ fail "the job never became readable"
+ fi
i=$((i + 1))
sleep 1
done
printf 'job %s finished as %s after %ss\n' "${JOB}" "${STATE_NOW}" "${i}"
[ "${STATE_NOW}" = success ] || {
+ printf 'job row: %s\n' "$(job_row "${JOB}")"
compose logs worker | tail -30
fail "the job ended as ${STATE_NOW}"
}
@@ -252,10 +263,9 @@ BRANCH=$(curl -fsS -X POST "${BASE_URL}/api/v1/projects/demo/trigger" \
-d "${BRANCH_BODY}")
printf '%s\n' "${BRANCH}"
-printf '%s' "${BRANCH}" | grep -q '"tasks":2' \
- || fail "expected 2 tasks on a branch, got: ${BRANCH}"
-
BRANCH_JOB=$(printf '%s' "${BRANCH}" | sed 's/.*"job_id":"\([^"]*\)".*/\1/')
+[ -n "${BRANCH_JOB}" ] || fail "no branch job was created"
+
i=0
while [ "${i}" -lt 90 ]; do
BRANCH_STATE=$(job_state "${BRANCH_JOB}")
@@ -266,10 +276,18 @@ while [ "${i}" -lt 90 ]; do
sleep 1
done
-curl -fsS "${BASE_URL}/jobs/${BRANCH_JOB}" | grep -q '>release<' \
+# The trigger only records the job, so the tasks are counted off the
+# finished job rather than the response.
+BRANCH_PAGE=$(curl -fsS "${BASE_URL}/jobs/${BRANCH_JOB}")
+BRANCH_TASKS=$(printf '%s' "${BRANCH_PAGE}" | grep -c 'href="/tasks/')
+[ "${BRANCH_TASKS}" -eq 2 ] \
+ || fail "expected 2 tasks on a branch, got ${BRANCH_TASKS}"
+
+printf '%s' "${BRANCH_PAGE}" | grep -q '>release<' \
&& fail "the release task should not exist on a branch job"
-printf 'branch job finished as %s without the release task\n' "${BRANCH_STATE}"
+printf 'branch job finished as %s with %s tasks and no release task\n' \
+ "${BRANCH_STATE}" "${BRANCH_TASKS}"
[ "${BRANCH_STATE}" = success ] || fail "the branch job ended as ${BRANCH_STATE}"
printf '\nPASSED\n'