#!/bin/sh # Write a JSON handoff manifest for a completed Clawdie ISO image artifact. # Intended for the FreeBSD publish/deploy path; safe to run from repo root. set -eu PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" export PATH usage() { cat <<'EOF' Usage: scripts/write-artifact-manifest.sh [--base-url URL] Writes .manifest.json next to the image artifacts. The manifest is the handoff contract for Hermes USB/IMG Deployer and other non-git artifact users. Environment: BUILD_COMMAND Optional build command string to record in the manifest. BUILT_BY Optional builder identity; defaults to "Codex ISO Builder". EOF } if [ "$#" -lt 1 ]; then usage >&2 exit 2 fi _artifact="$1" shift _base_url="" while [ "$#" -gt 0 ]; do case "$1" in --base-url) shift if [ "$#" -eq 0 ]; then echo "ERROR: --base-url requires a value" >&2 exit 2 fi _base_url="${1%/}" ;; -h|--help) usage exit 0 ;; *) echo "ERROR: unknown argument: $1" >&2 usage >&2 exit 2 ;; esac shift done case "${_artifact}" in *.img.gz) _gz="${_artifact}" _raw="${_artifact%.gz}" _stem="${_artifact%.img.gz}" ;; *.img) _raw="${_artifact}" _gz="${_artifact}.gz" _stem="${_artifact%.img}" ;; *) echo "ERROR: artifact must end in .img or .img.gz: ${_artifact}" >&2 exit 2 ;; esac _sha="${_gz}.sha256" _manifest="${_stem}.manifest.json" if [ ! -f "${_gz}" ]; then echo "ERROR: compressed image missing: ${_gz}" >&2 exit 1 fi if [ ! -f "${_sha}" ]; then echo "ERROR: checksum file missing: ${_sha}" >&2 exit 1 fi json_escape() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g' } json_string_or_null() { if [ -n "$1" ]; then printf '"%s"' "$(json_escape "$1")" else printf 'null' fi } file_size() { if stat -f '%z' "$1" >/dev/null 2>&1; then stat -f '%z' "$1" else stat -c '%s' "$1" fi } file_sha256() { if command -v sha256 >/dev/null 2>&1; then sha256 -q "$1" else sha256sum "$1" | awk '{print $1}' fi } sha_from_file() { if grep -q '=' "$1" 2>/dev/null; then awk -F'= ' 'NF > 1 {print $2; exit}' "$1" else awk '{print $1; exit}' "$1" fi } _gz_hash="$(file_sha256 "${_gz}")" _sha_hash="$(sha_from_file "${_sha}")" if [ "${_gz_hash}" != "${_sha_hash}" ]; then echo "ERROR: checksum file does not match compressed image" >&2 echo " computed: ${_gz_hash}" >&2 echo " file: ${_sha_hash}" >&2 exit 1 fi _raw_base="$(basename "${_raw}")" _gz_base="$(basename "${_gz}")" _sha_base="$(basename "${_sha}")" _manifest_base="$(basename "${_manifest}")" if [ -f "${_raw}" ]; then _raw_size="$(file_size "${_raw}")" else _raw_size="null" fi _gz_size="$(file_size "${_gz}")" _branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)" _commit="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)" _repo_dirty="null" if git diff --quiet 2>/dev/null && git diff --cached --quiet 2>/dev/null; then _repo_dirty="false" elif git rev-parse --git-dir >/dev/null 2>&1; then _repo_dirty="true" fi _host="$(hostname 2>/dev/null || echo unknown)" _written_at="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" _run_stamp="$(date -u '+%Y%m%dT%H%M%SZ')" _run_id="${_run_stamp}-${_stem##*/}" _freebsd="$(freebsd-version -kru 2>/dev/null | tr '\n' ' ' | sed 's/[[:space:]]*$//')" _builder="${BUILT_BY:-Codex ISO Builder}" _build_command="${BUILD_COMMAND:-unknown}" if [ -n "${_base_url}" ]; then _image_url="${_base_url}/${_gz_base}" _sha_url="${_base_url}/${_sha_base}" _manifest_url="${_base_url}/${_manifest_base}" else _image_url="" _sha_url="" _manifest_url="" fi _tmp="${_manifest}.tmp.$$" cat > "${_tmp}" <