From d7aec36cf2d83587e83a48b370e93571887700ef Mon Sep 17 00:00:00 2001 From: ryanyao Date: Tue, 15 Jul 2025 00:59:23 +0000 Subject: [PATCH] Update devbox/cli/devbox --- devbox/cli/devbox | 147 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 143 insertions(+), 4 deletions(-) diff --git a/devbox/cli/devbox b/devbox/cli/devbox index fe77670..cba5866 100755 --- a/devbox/cli/devbox +++ b/devbox/cli/devbox @@ -948,6 +948,145 @@ compile_backend_service() { echo "[BACKEND] $(date '+%Y-%m-%d %H:%M:%S') Start backend service from $DEVBOX_NAME." docker exec -i "$DEVBOX_NAME" bash <&2 + return 1 + fi + + if [[ ! -f "\$requirements_file" ]]; then + echo "[ERROR] Requirements file '\$requirements_file' does not exist" >&2 + return 1 + fi + + if [[ ! -r "\$requirements_file" ]]; then + echo "[ERROR] Requirements file '\$requirements_file' is not readable" >&2 + return 1 + fi + + echo "[INFO] Starting package installation and verification for: \$requirements_file" + + # Create temporary directory for hash verification + local temp_dir=\$(mktemp -d) + local hash_file="\$temp_dir/package_hashes.txt" + + # Function to get package hash from PyPI + get_package_hash() { + local package_name="\$1" + local version="\$2" + + local pypi_url="https://pypi.org/pypi/\${package_name}/\${version}/json" + local hash_value="" + + if command -v curl &>/dev/null; then + hash_value=\$(curl -s "\$pypi_url" | grep -o '"sha256":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "") + elif command -v wget &>/dev/null; then + hash_value=\$(wget -qO- "\$pypi_url" | grep -o '"sha256":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "") + fi + + echo "\$hash_value" + } + + # Function to verify installed package + verify_package() { + local package_name="\$1" + local expected_hash="\$2" + + if [[ -z "\$expected_hash" ]]; then + echo "[INFO] No hash available for \$package_name, skipping verification" + return 0 + fi + + local package_location="" + if command -v python3.11 &>/dev/null; then + package_location=\$(python3.11 -c "import \$package_name; print(\$package_name.__file__)" 2>/dev/null || echo "") + fi + + if [[ -z "\$package_location" ]]; then + echo "[ERROR] Package \$package_name not found, verification failed" >&2 + return 1 + fi + + local actual_hash=\$(find "\$(dirname "\$package_location")" -name "*\$package_name*" -type f -exec sha256sum {} \; 2>/dev/null | head -1 | cut -d' ' -f1) + + if [[ "\$actual_hash" == "\$expected_hash" ]]; then + echo "[INFO] Package \$package_name verification successful" + return 0 + else + echo "[ERROR] Package \$package_name verification failed - Expected: \$expected_hash, Got: \$actual_hash" >&2 + return 1 + fi + } + # Step 1: Install all packages + echo "[INFO] Installing all packages from \$requirements_file" + if ! pip install --no-cache-dir -r "\$requirements_file"; then + echo "[ERROR] Failed to install packages" >&2 + rm -rf "\$temp_dir" + return 1 + fi + + # Step 2: Collect hashes for verification + echo "[INFO] Collecting package hashes for verification" + while IFS= read -r line; do + [[ "\$line" =~ ^[[:space:]]*# ]] && continue + [[ -z "\$line" ]] && continue + + local package_name=\$(echo "\$line" | cut -d'=' -f1 | cut -d'[' -f1 | tr -d ' ') + local version=\$(echo "\$line" | grep -o '[=<>!][=<>!]*[^;]*' | head -1 | sed 's/[=<>!]*//' || echo "") + + if [[ -n "\$package_name" ]]; then + echo "[INFO] Getting hash for \$package_name\${version:+:\$version}" + local hash_value=\$(get_package_hash "\$package_name" "\$version") + if [[ -n "\$hash_value" ]]; then + echo "\$package_name:\$hash_value" >> "\$hash_file" + fi + fi + done < "\$requirements_file" + + # Step 3: Verify each package and reinstall if failed + echo "[INFO] Verifying installed packages" + local failed_packages=() + + while IFS=: read -r package_name hash_value; do + if [[ -n "\$package_name" && -n "\$hash_value" ]]; then + if ! verify_package "\$package_name" "\$hash_value"; then + failed_packages+=("\$package_name") + echo "[WARN] Package \$package_name failed verification, will reinstall" + fi + fi + done < "\$hash_file" + + # Reinstall failed packages + if [[ \${#failed_packages[@]} -gt 0 ]]; then + echo "[INFO] Reinstalling \${#failed_packages[@]} failed packages" + + for package in "\${failed_packages[@]}"; do + + echo "[INFO] Reinstalling \$package" + if ! pip install --no-cache-dir --force-reinstall "\$package"; then + echo "[ERROR] Failed to reinstall \$package" >&2 + rm -rf "\$temp_dir" + return 1 + fi + + # Verify again after reinstall + local package_hash=\$(grep "^\$package:" "\$hash_file" | cut -d: -f2) + if ! verify_package "\$package" "\$package_hash"; then + echo "[ERROR] Package \$package still failed verification after reinstall" >&2 + rm -rf "\$temp_dir" + return 1 + fi + done + fi + + echo "[INFO] All packages installed and verified successfully" + rm -rf "\$temp_dir" + return 0 + } + # Check if /home/devbox/.backend.pid exits if [ -f /home/devbox/.backend.pid ]; then backend_pid=\$(cat /home/devbox/.backend.pid) @@ -1020,7 +1159,7 @@ compile_backend_service() { # Check if it's the first time by verifying if the backend dependencies have been installed if [ ! -f "/home/devbox/.backend_deps_installed" ]; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Install backend dependencies..." - pip install --no-cache-dir -r /home/devbox/freeleaps/apps/freeleaps/requirements.txt + install_and_verify_packages /home/devbox/freeleaps/apps/freeleaps/requirements.txt if ! pip show async_timeout; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') async_timeout is missing. Installing..." pip install async_timeout @@ -1066,7 +1205,7 @@ compile_backend_service() { if [ \$IS_NEW_REQ_ADDED -eq 1 ]; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Reinstalling dependencies..." - pip install --no-cache-dir -r /home/devbox/freeleaps/apps/freeleaps/requirements.txt + install_and_verify_packages /home/devbox/freeleaps/apps/freeleaps/requirements.txt fi # Undo update for /home/devbox/freeleaps/apps/requirements.txt rm /home/devbox/freeleaps/apps/freeleaps/requirements.txt @@ -1080,7 +1219,7 @@ compile_backend_service() { # Check if all dependencies are installed, if not, install them if ! pip check; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Some dependencies are missing. Reinstalling..." - pip install --no-cache-dir -r /home/devbox/freeleaps/apps/freeleaps/requirements.txt + install_and_verify_packages /home/devbox/freeleaps/apps/freeleaps/requirements.txt fi # pip install async_timeout if not installed @@ -1126,7 +1265,7 @@ compile_backend_service() { if [ \$IS_NEW_REQ_ADDED -eq 1 ]; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Reinstalling dependencies..." - pip install --no-cache-dir -r /home/devbox/freeleaps/apps/requirements.txt + install_and_verify_packages /home/devbox/freeleaps/apps/requirements.txt fi # Undo update for /home/devbox/freeleaps/apps/requirements.txt