#!/bin/sh set -eu PROJECT_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) BASE_VERSION=$(sed -n 's/^version[[:space:]]*=[[:space:]]*//p' "$PROJECT_ROOT/fnos/manifest" | head -n 1 | tr -d '\r') PACKAGE_VERSION="${PACKAGE_VERSION:-$BASE_VERSION}" case "$PACKAGE_VERSION" in ''|*[!0-9.]*|.*|*..*|*.) printf 'invalid fnOS package version: %s\n' "$PACKAGE_VERSION" >&2; exit 1 ;; esac [ "$(printf '%s' "$PACKAGE_VERSION" | awk -F. '{ print NF }')" -eq 3 ] || { printf 'fnOS package version must contain exactly three numeric components: %s\n' "$PACKAGE_VERSION" >&2 exit 1 } BUILD_ROOT="$PROJECT_ROOT/.build-fnos" STAGE="$BUILD_ROOT/imagefind" PACKED_ROOT="$BUILD_ROOT/packed" FNPACK_TMP_ROOT="$BUILD_ROOT/fnpack-tmp" WHEEL_BUILD_ROOT="$BUILD_ROOT/application-wheel" OUTPUT="${1:-$PROJECT_ROOT/dist/imagefind-${PACKAGE_VERSION}-x86_64.fpk}" WHEEL_CACHE="${IMAGEFIND_WHEEL_CACHE:-$PROJECT_ROOT/.fnos-wheel-cache/python312}" if [ -n "${FNPACK:-}" ]; then FNPACK_BIN="$FNPACK" elif [ -x "$PROJECT_ROOT/.tools/fnpack" ]; then FNPACK_BIN="$PROJECT_ROOT/.tools/fnpack" else FNPACK_BIN=fnpack fi if [ -n "${PYTHON:-}" ]; then PYTHON_BIN="$PYTHON" elif [ -x "$PROJECT_ROOT/.release-venv/bin/python" ]; then PYTHON_BIN="$PROJECT_ROOT/.release-venv/bin/python" elif [ -x "$PROJECT_ROOT/.venv/bin/python" ]; then PYTHON_BIN="$PROJECT_ROOT/.venv/bin/python" else PYTHON_BIN=python3 fi require_file() { if [ ! -e "$1" ]; then printf 'missing required release artifact: %s\n' "$1" >&2 exit 1 fi } FNPACK_BIN=$(command -v "$FNPACK_BIN") || { printf 'fnpack is required; install it from the fnOS developer portal.\n' >&2 exit 1 } PYTHON_BIN=$(command -v "$PYTHON_BIN") || { printf 'Python with hatchling is required to assemble the release.\n' >&2 exit 1 } APP_VERSION=$("$PYTHON_BIN" -c 'import pathlib,sys,tomllib; print(tomllib.loads(pathlib.Path(sys.argv[1]).read_text())["project"]["version"])' "$PROJECT_ROOT/pyproject.toml") command -v npm >/dev/null 2>&1 || { printf 'npm is required.\n' >&2; exit 1; } "$PYTHON_BIN" -c 'import hatchling' >/dev/null 2>&1 || { printf 'hatchling is required in the release Python environment.\n' >&2 exit 1 } for artifact in libOpenCL.so.1; do require_file "$PROJECT_ROOT/vendor/$artifact" done require_file "$PROJECT_ROOT/requirements/runtime-core.txt" require_file "$PROJECT_ROOT/requirements/runtime-ai/constraints-cp312.txt" [ -d "$WHEEL_CACHE" ] || { printf 'missing Python 3.12 core wheelhouse: %s\n' "$WHEEL_CACHE" >&2; exit 1; } rm -rf "$BUILD_ROOT" mkdir -p "$STAGE/app/bin" "$STAGE/app/frontend" "$STAGE/app/runtime/ai" \ "$STAGE/app/runtime/wheels" "$PACKED_ROOT" "$FNPACK_TMP_ROOT" \ "$WHEEL_BUILD_ROOT" "$(dirname "$OUTPUT")" npm run build --prefix "$PROJECT_ROOT/frontend" "$PYTHON_BIN" -m hatchling build -t wheel -d "$WHEEL_BUILD_ROOT" cp -R "$PROJECT_ROOT/fnos/." "$STAGE/" sed -i -E "s/^version[[:space:]]*=.*/version=${PACKAGE_VERSION}/" "$STAGE/manifest" cp -R "$PROJECT_ROOT/frontend/dist/." "$STAGE/app/frontend/" cp -p "$PROJECT_ROOT/vendor/libOpenCL.so.1" "$STAGE/app/bin/libOpenCL.so.1" cp -p "$PROJECT_ROOT/requirements/runtime-core.txt" "$STAGE/app/runtime/runtime-core.txt" cp -p "$PROJECT_ROOT/requirements/runtime-ai/"*.txt "$STAGE/app/runtime/ai/" cp -p "$WHEEL_CACHE/"*.whl "$STAGE/app/runtime/wheels/" cp -p "$WHEEL_BUILD_ROOT/"imagefind-*.whl "$STAGE/app/runtime/" STAGED_APP_WHEEL="" for candidate in "$STAGE/app/runtime/"imagefind-*.whl; do if [ -f "$candidate" ]; then STAGED_APP_WHEEL="$candidate" break fi done [ -n "$STAGED_APP_WHEEL" ] || { printf 'built application wheel is missing\n' >&2; exit 1; } PACKAGE_ID=$("$PYTHON_BIN" -c \ 'import hashlib,sys,zipfile; h=hashlib.sha256(); z=zipfile.ZipFile(sys.argv[1]); [(h.update(n.encode()),h.update(z.read(n))) for n in sorted(z.namelist())]; h.update(open(sys.argv[2],"rb").read()); print(h.hexdigest())' \ "$STAGED_APP_WHEEL" "$STAGE/app/runtime/runtime-core.txt") printf '%s\n%s\n%s\n' "$PACKAGE_VERSION" "$APP_VERSION" "$PACKAGE_ID" >"$STAGE/app/runtime/VERSION" "$PYTHON_BIN" "$PROJECT_ROOT/scripts/make_icons.py" "$STAGE" chmod 0755 "$STAGE/cmd/"* ( cd "$PACKED_ROOT" TMPDIR="$FNPACK_TMP_ROOT" "$FNPACK_BIN" build --directory "$STAGE" ) mv "$PACKED_ROOT/imagefind.fpk" "$OUTPUT" ( cd "$(dirname "$OUTPUT")" sha256sum "$(basename "$OUTPUT")" >"$(basename "$OUTPUT").sha256" ) printf 'created %s\n' "$OUTPUT"