Files
ngit-grasp/scripts/test-container-release.sh
DanConwayDev 5fed50e5e3 ci(release): publish OCI container images
Motivation: Release images should be built, checked, published, and consumed through the same Nostr-native OCI path operators will use, without relying on an unreviewed local release procedure.

Approach: Add a checked-in container manifest, a Docker-to-OCI layout helper, automatic tag publication and pull verification, a safe exact-tag backfill workflow, and deployment CI that imports and runs the exact generated layout.

Correctness: Release tags come only from reviewed OCI index annotations; ordinary publication preserves prior tags; historical backfills cannot move latest or prerelease channels; generated images and temporary resources use bounded, validated names and cleanup.

Excluded scope: This change does not alter ngit-grasp runtime behavior, change package versions, create v3.0.2, publish a container, move a release tag, or run the heavyweight container build in the coding VM.

Validation: git diff --check; shellcheck on all container scripts; actionlint on all affected workflows; ngit parsing of .ngit/containers.yaml; canonical source and v3.0.1 tag resolution. The PR pipeline performs the full OCI build, import, and deployment test.

Assisted-by: Codex (GPT-5)
2026-09-10 15:30:13 +00:00

82 lines
2.1 KiB
Bash
Executable File

#!/bin/sh
set -eu
if [ "$#" -ne 2 ]; then
echo "usage: $0 <version> <40-character-git-revision>" >&2
exit 2
fi
version=$1
revision=$2
script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
case "${revision}" in
*[!0-9a-f]*)
echo "revision is not a lowercase hexadecimal Git object ID: ${revision}" >&2
exit 2
;;
esac
if [ "${#revision}" -ne 40 ]; then
echo "revision is not a 40-character Git object ID: ${revision}" >&2
exit 2
fi
for required_command in docker jq skopeo; do
if ! command -v "${required_command}" >/dev/null 2>&1; then
echo "missing required command: ${required_command}" >&2
exit 2
fi
done
temporary_parent=${RUNNER_TEMP:-${TMPDIR:-/tmp}}
temporary_parent=${temporary_parent%/}
case "${temporary_parent}" in
/*) ;;
*)
echo "temporary directory parent must be absolute: ${temporary_parent}" >&2
exit 2
;;
esac
if [ ! -d "${temporary_parent}" ]; then
echo "temporary directory parent does not exist: ${temporary_parent}" >&2
exit 2
fi
test_directory=$(mktemp -d "${temporary_parent}/ngit-grasp-container-release-test.XXXXXX")
case "${test_directory}" in
"${temporary_parent}"/ngit-grasp-container-release-test.??????) ;;
*)
echo "refusing to use unexpected test directory: ${test_directory}" >&2
exit 2
;;
esac
layout=${test_directory}/layout
image_name=ngit-grasp:release-test-${revision}-$$
case "${image_name}" in
ngit-grasp:release-test-[0-9a-f]*-[0-9]*) ;;
*)
echo "refusing to use unexpected test image name" >&2
exit 2
;;
esac
cleanup() {
docker image rm --force "${image_name}" >/dev/null 2>&1 || true
rm -rf -- "${test_directory}"
}
trap cleanup EXIT HUP INT TERM
"${script_dir}/build-container-layout.sh" \
"${version}" \
"${revision}" \
"${layout}"
skopeo --insecure-policy copy --format v2s2 \
"oci:${layout}:${version}" \
"docker-daemon:${image_name}"
NGIT_TEST_IMAGE=${image_name} \
"${script_dir}/test-container-deployment.sh"
printf 'OCI release layout and imported image verified for %s\n' "${version}"