All checks were successful
Test and deploy / Verify production build (push) Successful in 10m25s
Build and test the Next.js application once with persistent BuildKit caches, smoke-test the immutable runtime image, and activate that same image through the shared production Docker daemon with serialized rollback-safe deployment.
66 lines
1.9 KiB
Bash
66 lines
1.9 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
image="${1:?runtime image is required}"
|
|
container_name="portfolio-smoke-${GITEA_RUN_ID:-$$}"
|
|
|
|
cleanup() {
|
|
docker rm --force "$container_name" >/dev/null 2>&1 || true
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
cleanup
|
|
docker run --detach --name "$container_name" "$image" >/dev/null
|
|
|
|
for _ in {1..30}; do
|
|
status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_name")"
|
|
case "$status" in
|
|
healthy)
|
|
break
|
|
;;
|
|
unhealthy|exited|dead)
|
|
docker logs "$container_name" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
sleep 2
|
|
done
|
|
|
|
if [[ "$(docker inspect --format '{{.State.Health.Status}}' "$container_name")" != "healthy" ]]; then
|
|
docker logs "$container_name" >&2
|
|
echo "Runtime image did not become healthy within 60 seconds." >&2
|
|
exit 1
|
|
fi
|
|
|
|
docker exec -i "$container_name" node <<'NODE'
|
|
const origin = 'http://127.0.0.1:3000';
|
|
|
|
async function expectOk(path, options) {
|
|
const response = await fetch(`${origin}${path}`, options);
|
|
if (!response.ok) throw new Error(`${path} returned ${response.status}`);
|
|
return response;
|
|
}
|
|
|
|
const english = await expectOk('/');
|
|
await expectOk('/fa');
|
|
await expectOk('/projects/interanet-solutions');
|
|
await expectOk('/fa/projects/interanet-solutions');
|
|
|
|
const html = await english.text();
|
|
const staticAsset = html.match(/(?:src|href)="(\/_next\/static\/[^"?#]+)/)?.[1];
|
|
if (!staticAsset) throw new Error('No built static asset was found in the home page.');
|
|
await expectOk(staticAsset);
|
|
|
|
const optimized = await expectOk(
|
|
'/_next/image?url=%2Fimages%2Favatar.png&w=256&q=75',
|
|
{ headers: { Accept: 'image/webp' } },
|
|
);
|
|
const contentType = optimized.headers.get('content-type') ?? '';
|
|
if (!/^image\/(?:webp|png)$/.test(contentType)) {
|
|
throw new Error(`Unexpected optimized image type: ${contentType}`);
|
|
}
|
|
if ((await optimized.arrayBuffer()).byteLength === 0) {
|
|
throw new Error('Optimized image response was empty.');
|
|
}
|
|
NODE
|