runner fix
CI / verify (push) Successful in 24s
CI / infrastructure-smoke (push) Successful in 19s

This commit is contained in:
ekklesia
2026-07-13 20:19:30 -05:00
parent 7b448d8706
commit 2465d9c1f7
3 changed files with 94 additions and 6 deletions
+3
View File
@@ -62,6 +62,9 @@ pnpm infrastructure:smoke
The smoke test verifies PostgreSQL password authentication and authenticated S3 list, put, get, The smoke test verifies PostgreSQL password authentication and authenticated S3 list, put, get,
head, and delete operations. It also proves invalid database passwords, anonymous object access, head, and delete operations. It also proves invalid database passwords, anonymous object access,
and invalid S3 signatures are rejected. Its containers, network, and volumes are removed on exit. and invalid S3 signatures are rejected. Its containers, network, and volumes are removed on exit.
When the smoke script itself runs in a Docker-based CI job, it temporarily joins that job container
to the isolated Compose network and reaches object storage by its service name. Direct host runs
continue to use Docker-assigned random ports published only on loopback.
PostgreSQL and the S3 endpoint bind to `127.0.0.1` only. Compose uses persistent named volumes; PostgreSQL and the S3 endpoint bind to `127.0.0.1` only. Compose uses persistent named volumes;
`infrastructure:down` preserves data. The S3 service creates the configured development bucket `infrastructure:down` preserves data. The S3 service creates the configured development bucket
+70 -4
View File
@@ -50,6 +50,18 @@ compose=(
--env-file .env.example --env-file .env.example
--file infrastructure/compose.yaml --file infrastructure/compose.yaml
) )
running_in_container=false
current_container=""
compose_network=""
connected_to_compose_network=false
if [[ -f /.dockerenv ]]; then
running_in_container=true
if [[ -z "${HOSTNAME:-}" ]]; then
echo "HOSTNAME is required when the smoke test runs inside a Docker container." >&2
exit 1
fi
current_container="$HOSTNAME"
fi
cleanup() { cleanup() {
local exit_status=$? local exit_status=$?
@@ -58,6 +70,17 @@ cleanup() {
if ((exit_status != 0)); then if ((exit_status != 0)); then
"${compose[@]}" logs --no-color >&2 || true "${compose[@]}" logs --no-color >&2 || true
fi fi
if [[ "$connected_to_compose_network" == "true" ]]; then
if ! cleanup_output="$(
docker network disconnect "$compose_network" "$current_container" 2>&1
)"; then
echo "Infrastructure smoke network cleanup failed:" >&2
echo "$cleanup_output" >&2
if ((exit_status == 0)); then
exit_status=1
fi
fi
fi
if ! cleanup_output="$( if ! cleanup_output="$(
"${compose[@]}" down --volumes --remove-orphans --timeout 10 2>&1 "${compose[@]}" down --volumes --remove-orphans --timeout 10 2>&1
)"; then )"; then
@@ -110,13 +133,56 @@ if "${compose[@]}" exec -T \
exit 1 exit 1
fi fi
s3_binding="$("${compose[@]}" port object-storage 8333)" if [[ "$running_in_container" == "true" ]]; then
s3_port="${s3_binding##*:}" object_storage_container="$("${compose[@]}" ps -q object-storage)"
if [[ ! "$s3_port" =~ ^[0-9]+$ ]]; then if [[ -z "$object_storage_container" ]]; then
echo "Compose did not return the object-storage container ID." >&2
exit 1
fi
network_output="$(
docker inspect \
--format '{{range $networkName, $_ := .NetworkSettings.Networks}}{{println $networkName}}{{end}}' \
"$object_storage_container"
)"
object_storage_networks=()
while IFS= read -r network_name; do
if [[ -n "$network_name" ]]; then
object_storage_networks+=("$network_name")
fi
done <<<"$network_output"
if ((${#object_storage_networks[@]} != 1)); then
echo "Expected object-storage to use exactly one Compose network." >&2
exit 1
fi
compose_network="${object_storage_networks[0]}"
current_network_output="$(
docker inspect \
--format '{{range $networkName, $_ := .NetworkSettings.Networks}}{{println $networkName}}{{end}}' \
"$current_container"
)"
already_connected=false
while IFS= read -r network_name; do
if [[ "$network_name" == "$compose_network" ]]; then
already_connected=true
break
fi
done <<<"$current_network_output"
if [[ "$already_connected" != "true" ]]; then
docker network connect "$compose_network" "$current_container"
connected_to_compose_network=true
fi
s3_endpoint="http://object-storage:8333"
else
s3_binding="$("${compose[@]}" port object-storage 8333)"
s3_port="${s3_binding##*:}"
if [[ ! "$s3_port" =~ ^[0-9]+$ ]]; then
echo "Compose returned an invalid S3 port binding: $s3_binding" >&2 echo "Compose returned an invalid S3 port binding: $s3_binding" >&2
exit 1 exit 1
fi
s3_endpoint="http://127.0.0.1:$s3_port"
fi fi
s3_endpoint="http://127.0.0.1:$s3_port"
signed_curl=( signed_curl=(
curl curl
--silent --silent
+19
View File
@@ -79,6 +79,25 @@ describe("development infrastructure", () => {
expect(infrastructureCommands).toContain("bash scripts/smoke-infrastructure.sh"); expect(infrastructureCommands).toContain("bash scripts/smoke-infrastructure.sh");
}); });
it("routes containerized S3 smoke checks through the isolated Compose network", async () => {
const source = await readFile(
path.join(repositoryRoot, "scripts", "smoke-infrastructure.sh"),
"utf8",
);
expect(source).toContain("[[ -f /.dockerenv ]]");
expect(source).toContain(`object_storage_container="$("\${compose[@]}" ps -q object-storage)"`);
expect(source).toContain("docker inspect");
expect(source).toContain('docker network connect "$compose_network" "$current_container"');
expect(source).toContain('docker network disconnect "$compose_network" "$current_container"');
expect(source).toContain('s3_endpoint="http://object-storage:8333"');
expect(source).toContain('s3_endpoint="http://127.0.0.1:$s3_port"');
expect(source).not.toContain("0.0.0.0:${S3_PORT");
expect(source.indexOf("docker network disconnect")).toBeLessThan(
source.indexOf(`"\${compose[@]}" down`),
);
});
it("includes every official platform layer in the workspace", async () => { it("includes every official platform layer in the workspace", async () => {
const source = await readFile(path.join(repositoryRoot, "pnpm-workspace.yaml"), "utf8"); const source = await readFile(path.join(repositoryRoot, "pnpm-workspace.yaml"), "utf8");
const workspace = parse(source) as WorkspaceDocument; const workspace = parse(source) as WorkspaceDocument;