#!/usr/bin/env bash set -euo pipefail usage() { cat <<'USAGE' Usage: scripts/verify-deployable-image-purity.sh --image --profile USAGE } image_ref="" profile="" while [[ $# -gt 0 ]]; do case "$1" in --image) image_ref="${2:-}" shift 2 ;; --profile) profile="${2:-}" shift 2 ;; -h|--help) usage exit 0 ;; *) echo "Unknown argument: $1" >&2 usage exit 2 ;; esac done if [[ -z "${image_ref}" || -z "${profile}" ]]; then usage exit 2 fi case "${profile}" in backend) checks=( ruff pyright pytest pydoclint xdoctest pre-commit yamllint toml-sort eslint prettier tsc vitest playwright yarn npm node pnpm ) ;; frontend) checks=( ruff pyright pytest pydoclint xdoctest pre-commit yamllint toml-sort eslint prettier tsc vitest playwright uv python python3 pip pip3 node npm yarn pnpm ) ;; *) echo "Unsupported profile: ${profile}" >&2 usage exit 2 ;; esac tmp_file="$(mktemp)" trap 'rm -f "${tmp_file}"' EXIT printf '%s\n' "${checks[@]}" > "${tmp_file}" binary_violations="$(docker run --rm --entrypoint /bin/sh "${image_ref}" -c ' set -e while IFS= read -r cmd; do if command -v "$cmd" >/dev/null 2>&1; then printf "%s\n" "$cmd" fi done ' < "${tmp_file}")" if [[ -n "${binary_violations}" ]]; then echo "❌ Found CI/development tooling binaries in ${profile} image: ${image_ref}" >&2 echo "${binary_violations}" >&2 exit 1 fi case "${profile}" in backend) backend_python_modules=( pytest ruff pyright pydoclint xdoctest pre_commit yamllint toml_sort playwright ) backend_python_packages=( pytest ruff pyright pydoclint xdoctest pre-commit yamllint toml-sort playwright ) module_violations="$(docker run --rm --entrypoint /bin/sh \ -e MODULES="${backend_python_modules[*]}" \ "${image_ref}" -c ' set -e PYTHON_BIN="" if command -v python3 >/dev/null 2>&1; then PYTHON_BIN="python3" elif command -v python >/dev/null 2>&1; then PYTHON_BIN="python" fi if [ -z "$PYTHON_BIN" ]; then echo "__missing_python_runtime__" exit 0 fi for module in ${MODULES}; do if "$PYTHON_BIN" -c "import importlib.util,sys; sys.exit(0 if importlib.util.find_spec(\"$module\") else 1)" >/dev/null 2>&1; then printf "%s\n" "$module" fi done ')" if echo "${module_violations}" | grep -q '__missing_python_runtime__'; then echo "❌ Backend deployable image is missing python runtime: ${image_ref}" >&2 exit 1 fi if [[ -n "${module_violations}" ]]; then echo "❌ Found CI/development Python modules importable in backend image: ${image_ref}" >&2 echo "${module_violations}" >&2 exit 1 fi pip_violations="$(docker run --rm --entrypoint /bin/sh \ -e PACKAGES="${backend_python_packages[*]}" \ "${image_ref}" -c ' set -e PIP_BIN="" if command -v pip3 >/dev/null 2>&1; then PIP_BIN="pip3" elif command -v pip >/dev/null 2>&1; then PIP_BIN="pip" fi if [ -z "$PIP_BIN" ]; then exit 0 fi for package in ${PACKAGES}; do if "$PIP_BIN" show "$package" >/dev/null 2>&1; then printf "%s\n" "$package" fi done ')" if [[ -n "${pip_violations}" ]]; then echo "❌ Found CI/development Python packages in backend image metadata: ${image_ref}" >&2 echo "${pip_violations}" >&2 exit 1 fi ;; frontend) frontend_os_packages=( nodejs npm yarn python3 py3-pip py3-setuptools ) os_pkg_violations="$(docker run --rm --entrypoint /bin/sh \ -e PACKAGES="${frontend_os_packages[*]}" \ "${image_ref}" -c ' set -e if command -v apk >/dev/null 2>&1; then for package in ${PACKAGES}; do if apk info -e "$package" >/dev/null 2>&1; then printf "apk:%s\n" "$package" fi done elif command -v dpkg-query >/dev/null 2>&1; then for package in ${PACKAGES}; do if dpkg-query -W -f='"'"'${db:Status-Status}'"'"' "$package" 2>/dev/null | grep -q '^installed$'; then printf "dpkg:%s\n" "$package" fi done fi ')" if [[ -n "${os_pkg_violations}" ]]; then echo "❌ Found CI/development OS packages in frontend runtime image: ${image_ref}" >&2 echo "${os_pkg_violations}" >&2 exit 1 fi dir_violations="$(docker run --rm --entrypoint /bin/sh "${image_ref}" -c ' set -e check_dir_tree() { local base_dir="$1" [ -d "$base_dir" ] || return 0 find "$base_dir" -maxdepth 5 -type d \ \( -name node_modules -o -name .venv -o -name site-packages -o -name dist-packages \) \ 2>/dev/null || true } check_dir_tree /app check_dir_tree /usr/local/lib check_dir_tree /usr/lib check_dir_tree /opt ')" if [[ -n "${dir_violations}" ]]; then echo "❌ Found development package directories in frontend runtime image: ${image_ref}" >&2 echo "${dir_violations}" >&2 exit 1 fi ;; esac echo "✅ ${profile} image is clean of disallowed CI/development tooling binaries and metadata"