|
|
#!/bin/bash |
|
|
set -uo pipefail |
|
|
set +H |
|
|
|
|
|
export PIP_NO_CACHE_DIR=1 |
|
|
export PIP_DISABLE_PIP_VERSION_CHECK=1 |
|
|
export GIT_TERMINAL_PROMPT=0 |
|
|
|
|
|
source /venv/main/bin/activate |
|
|
COMFYUI_DIR="${WORKSPACE:-/workspace}/ComfyUI" |
|
|
if [ -f "/venv/main/bin/python" ]; then |
|
|
PYTHON_BIN="/venv/main/bin/python" |
|
|
PIP_BIN="/venv/main/bin/pip" |
|
|
else |
|
|
PYTHON_BIN="python3" |
|
|
PIP_BIN="pip3" |
|
|
fi |
|
|
|
|
|
APT_PACKAGES=( |
|
|
"aria2" |
|
|
) |
|
|
PIP_PACKAGES=( |
|
|
) |
|
|
NODES=( |
|
|
"https://github.com/ltdrdata/ComfyUI-Impact-Pack.git" |
|
|
"https://github.com/ltdrdata/ComfyUI-Impact-Subpack.git" |
|
|
"https://github.com/cubiq/ComfyUI_IPAdapter_plus.git" |
|
|
"https://github.com/ka-puna/comfyui-yanc.git" |
|
|
) |
|
|
CHECKPOINT_MODELS=( |
|
|
"https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/checkpoints/waiNSFWIllustrious_v150.safetensors" |
|
|
) |
|
|
CLIP_VISION_MODELS=( |
|
|
"https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/clip_vision/CLIP-ViT-H-14-laion2B-s32B-b79K.safetensors" |
|
|
) |
|
|
IPADAPTER_MODELS=( |
|
|
"https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/ipadapter/ip-adapter-plus-face_sdxl_vit-h.safetensors" |
|
|
) |
|
|
ESRGAN_MODELS=( |
|
|
"https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/upscale_models/RealESRGAN_x4plus_anime_6B.pth" |
|
|
) |
|
|
WORKFLOWS=( |
|
|
"https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/Single_Posture_ko.json" |
|
|
) |
|
|
|
|
|
log_info() { echo "--> $1"; } |
|
|
log_warn() { echo " โ ๏ธ $1"; } |
|
|
log_error() { echo " โ $1"; } |
|
|
log_success() { echo " โ
$1"; } |
|
|
log_step() { echo ""; echo "=== [Step $1] $2 ==="; } |
|
|
|
|
|
package_installed() { |
|
|
$PYTHON_BIN -c "import $1" 2>/dev/null && return 0 || return 1 |
|
|
} |
|
|
|
|
|
filter_requirements() { |
|
|
local req_file="$1" |
|
|
local tmp_file="${req_file}.filtered" |
|
|
> "$tmp_file" |
|
|
while IFS= read -r line; do |
|
|
[[ -z "$line" || "$line" =~ ^ |
|
|
local pkg_name |
|
|
pkg_name=$(echo "$line" | sed 's/[<>=!].*//' | xargs) |
|
|
if [[ -z "$pkg_name" ]]; then continue; fi |
|
|
local module_name="${pkg_name//-/_}" |
|
|
if package_installed "$module_name"; then |
|
|
log_info "์ค์น๋ ํจํค์ง ๊ฑด๋๋ฐ๊ธฐ: $pkg_name" |
|
|
else |
|
|
echo "$line" >> "$tmp_file" |
|
|
fi |
|
|
done < "$req_file" |
|
|
if [ -s "$tmp_file" ]; then |
|
|
mv "$tmp_file" "$req_file" |
|
|
return 0 |
|
|
else |
|
|
rm -f "$tmp_file" |
|
|
log_info "๋ชจ๋ ํจํค์ง๊ฐ ์ด๋ฏธ ์ค์น๋์์ต๋๋ค" |
|
|
return 1 |
|
|
fi |
|
|
} |
|
|
|
|
|
provisioning_has_valid_hf_token() { |
|
|
[[ -n "${HF_TOKEN:-}" ]] || return 1 |
|
|
local response |
|
|
response=$(curl -o /dev/null -s -w "%{http_code}" -X GET "https://huggingface.co/api/whoami-v2" -H "Authorization: Bearer $HF_TOKEN" -H "Content-Type: application/json") |
|
|
[ "$response" -eq 200 ] |
|
|
} |
|
|
|
|
|
provisioning_has_valid_civitai_token() { |
|
|
[[ -n "${CIVITAI_TOKEN:-}" ]] || return 1 |
|
|
local response |
|
|
response=$(curl -o /dev/null -s -w "%{http_code}" -X GET "https://civitai.com/api/v1/models?hidden=1&limit=1" -H "Authorization: Bearer $CIVITAI_TOKEN" -H "Content-Type: application/json") |
|
|
[ "$response" -eq 200 ] |
|
|
} |
|
|
|
|
|
install_node() { |
|
|
local repo_url="$1" |
|
|
local repo_name |
|
|
repo_name=$(basename "$repo_url" .git) |
|
|
local install_path="${COMFYUI_DIR}/custom_nodes/${repo_name}" |
|
|
|
|
|
if [ -d "$install_path" ]; then |
|
|
if [[ "${AUTO_UPDATE:-true}" != "false" ]]; then |
|
|
log_info "๋
ธ๋ ์
๋ฐ์ดํธ: $repo_name" |
|
|
(cd "$install_path" && git pull -q 2>&1 | grep -v "Already up to date" || true) |
|
|
else |
|
|
log_info "'$repo_name'์ด(๊ฐ) ์ด๋ฏธ ์กด์ฌํฉ๋๋ค. ๊ฑด๋๋๋๋ค" |
|
|
return |
|
|
fi |
|
|
else |
|
|
log_info "๋
ธ๋ ๋ณต์ : $repo_name" |
|
|
git clone --depth 1 --single-branch "$repo_url" "$install_path" -q 2>&1 || true |
|
|
fi |
|
|
|
|
|
if [ -f "$install_path/requirements.txt" ]; then |
|
|
log_info "$repo_name์ ์ข
์์ฑ ์ฒ๋ฆฌ ์ค..." |
|
|
sed -i -e '/^torch/d' -e '/^sam2/d' "$install_path/requirements.txt" 2>/dev/null || true |
|
|
if filter_requirements "$install_path/requirements.txt"; then |
|
|
log_info "$repo_name์ ์๋ก์ด ์ข
์์ฑ ์ค์น ์ค..." |
|
|
$PIP_BIN install -q --no-cache-dir -r "$install_path/requirements.txt" 2>&1 | grep -v "Requirement already satisfied" || log_warn "์ผ๋ถ ์ข
์์ฑ ์ค์น ์คํจ" |
|
|
fi |
|
|
fi |
|
|
|
|
|
if [ -f "$install_path/install.py" ]; then |
|
|
log_info "$repo_name์ ์ค์น ์คํฌ๋ฆฝํธ ์คํ ์ค..." |
|
|
$PYTHON_BIN "$install_path/install.py" 2>&1 || log_warn "์ค์น ์คํฌ๋ฆฝํธ ์คํ ์คํจ" |
|
|
fi |
|
|
} |
|
|
|
|
|
download_file() { |
|
|
local dest_path="$1" |
|
|
local url="$2" |
|
|
local filename |
|
|
filename=$(basename "$dest_path") |
|
|
local tmp_path="${dest_path}.tmp" |
|
|
|
|
|
mkdir -p "$(dirname "$dest_path")" |
|
|
if [ -s "$dest_path" ]; then |
|
|
log_info "ํ์ผ '$filename'์ด(๊ฐ) ์ด๋ฏธ ์กด์ฌํ๊ณ ์์ ํฉ๋๋ค. ๋ค์ด๋ก๋๋ฅผ ๊ฑด๋๋๋๋ค" |
|
|
return 0 |
|
|
fi |
|
|
|
|
|
log_info "๋ค์ด๋ก๋: $filename" |
|
|
local max_retries=3 |
|
|
local attempt=0 |
|
|
local auth_header="" |
|
|
local success=1 |
|
|
|
|
|
if [[ "$url" =~ huggingface\.co ]] && provisioning_has_valid_hf_token; then |
|
|
auth_header="Authorization: Bearer $HF_TOKEN" |
|
|
log_info "HuggingFace ํ ํฐ ์ฌ์ฉ" |
|
|
elif [[ "$url" =~ civitai\.com ]] && provisioning_has_valid_civitai_token; then |
|
|
auth_header="Authorization: Bearer $CIVITAI_TOKEN" |
|
|
log_info "CivitAI ํ ํฐ ์ฌ์ฉ" |
|
|
fi |
|
|
|
|
|
while [ "$attempt" -lt "$max_retries" ]; do |
|
|
attempt=$((attempt + 1)) |
|
|
[ "$attempt" -gt 1 ] && sleep 10 |
|
|
if command -v aria2c >/dev/null 2>&1; then |
|
|
log_info "aria2c (3 ์ค๋ ๋)๋ก ๋ค์ด๋ก๋: $filename (์๋ $attempt/$max_retries)" |
|
|
local aria_opts=(--console-log-level=error -c -x 3 -s 3 -k 1M --max-connection-per-server=3 --max-tries=3 --retry-wait=5 --timeout=180 --file-allocation=falloc --auto-file-renaming=false -d "$(dirname "$dest_path")" -o "${filename}.tmp") |
|
|
[[ -n "$auth_header" ]] && aria_opts+=(--header="$auth_header") |
|
|
aria2c "${aria_opts[@]}" "$url" |
|
|
if [ $? -eq 0 ]; then success=0; break; fi |
|
|
else |
|
|
log_info "wget์ผ๋ก ๋ค์ด๋ก๋: $filename (์๋ $attempt/$max_retries)" |
|
|
local wget_opts=(-O "$tmp_path" -c --timeout=60 --tries=3 --content-disposition --show-progress) |
|
|
[[ -n "$auth_header" ]] && wget_opts+=(--header="$auth_header") |
|
|
wget "${wget_opts[@]}" "$url" |
|
|
if [ $? -eq 0 ]; then success=0; break; fi |
|
|
fi |
|
|
done |
|
|
|
|
|
if [ "$success" -eq 0 ] && [ -s "$tmp_path" ]; then |
|
|
mv "$tmp_path" "$dest_path" |
|
|
log_success "๋ค์ด๋ก๋ ์๋ฃ: $filename" |
|
|
return 0 |
|
|
else |
|
|
log_error "๋ค์ด๋ก๋ ์คํจ: $filename" |
|
|
rm -f "$tmp_path" |
|
|
return 1 |
|
|
fi |
|
|
} |
|
|
|
|
|
download_to_directory() { |
|
|
local dest_dir="$1"; shift; local urls=("$@") |
|
|
if [ ${#urls[@]} -eq 0 ]; then return 0; fi |
|
|
mkdir -p "$dest_dir" |
|
|
log_info "${#urls[@]}๊ฐ์ ํ์ผ์ $dest_dir(์ผ)๋ก ๋ค์ด๋ก๋ ์ค" |
|
|
local MAX_PARALLEL=3 |
|
|
for url in "${urls[@]}"; do |
|
|
while [ $(jobs -r | wc -l) -ge $MAX_PARALLEL ]; do sleep 1; done |
|
|
local filename |
|
|
filename=$(basename "$url" | sed 's/?.*//') |
|
|
download_file "${dest_dir}/${filename}" "$url" & |
|
|
done |
|
|
wait |
|
|
} |
|
|
|
|
|
verify_and_retry_downloads() { |
|
|
local dest_dir="$1"; shift; local urls=("$@") |
|
|
if [ ${#urls[@]} -eq 0 ]; then return 0; fi |
|
|
log_info "$dest_dir์ ํ์ผ ํ์ธ ์ค..." |
|
|
local missing_files=() |
|
|
for url in "${urls[@]}"; do |
|
|
local filename |
|
|
filename=$(basename "$url" | sed 's/?.*//') |
|
|
local dest_path="${dest_dir}/${filename}" |
|
|
if [ ! -s "$dest_path" ]; then |
|
|
log_warn "ํ์ผ ๋๋ฝ ๋๋ ๋ถ์์ : $filename" |
|
|
missing_files+=("$url") |
|
|
fi |
|
|
done |
|
|
if [ ${#missing_files[@]} -gt 0 ]; then |
|
|
log_warn "${#missing_files[@]}๊ฐ์ ๋๋ฝ/๋ถ์์ ํ ํ์ผ ๋ฐ๊ฒฌ, ์ฌ๋ค์ด๋ก๋ ์ค..." |
|
|
download_to_directory "$dest_dir" "${missing_files[@]}" |
|
|
else |
|
|
log_success "๋ชจ๋ ํ์ผ์ด ์กด์ฌํ๊ณ ์์ ํ ๊ฒ์ผ๋ก ํ์ธ๋์์ต๋๋ค" |
|
|
fi |
|
|
} |
|
|
|
|
|
provisioning_print_header() { |
|
|
echo ""; echo "##############################################" |
|
|
echo "# #" |
|
|
echo "# ํ๋ก๋น์ ๋ ์ปจํ
์ด๋ #" |
|
|
echo "# #" |
|
|
echo "# ์๊ฐ์ด ์ข ๊ฑธ๋ฆด ์ ์์ต๋๋ค #" |
|
|
echo "# #" |
|
|
echo "##############################################"; echo "" |
|
|
} |
|
|
|
|
|
provisioning_print_end() { |
|
|
echo ""; echo "##############################################" |
|
|
echo "# #" |
|
|
echo "# ํ๋ก๋น์ ๋ ์๋ฃ! #" |
|
|
echo "# #" |
|
|
echo "# Total time: $((END_TIME - START_TIME)) seconds #" |
|
|
echo "# #" |
|
|
echo "##############################################"; echo "" |
|
|
} |
|
|
|
|
|
provisioning_step1_install_core_deps() { |
|
|
log_step "1" "์์คํ
๋ฐ ComfyUI ํต์ฌ ์ข
์์ฑ ์ค์น" |
|
|
if [ ${#APT_PACKAGES[@]} -gt 0 ]; then |
|
|
log_info "${#APT_PACKAGES[@]}๊ฐ์ ์์คํ
ํจํค์ง ์ค์น ์ค๋น ์ค..." |
|
|
local packages_to_install=() |
|
|
for pkg in "${APT_PACKAGES[@]}"; do |
|
|
if ! dpkg -s "$pkg" >/dev/null 2>&1; then |
|
|
packages_to_install+=("$pkg") |
|
|
else |
|
|
log_success "$pkg์ด(๊ฐ) ์ด๋ฏธ ์ค์น๋์ด ์์ต๋๋ค. ๊ฑด๋๋๋๋ค" |
|
|
fi |
|
|
done |
|
|
if [ ${#packages_to_install[@]} -gt 0 ]; then |
|
|
log_info "์ค์น ์ค: ${packages_to_install[*]}" |
|
|
apt-get update -qq && apt-get install -y -qq "${packages_to_install[@]}" && log_success "์์คํ
ํจํค์ง ์ค์น ์๋ฃ" || log_warn "์ผ๋ถ ์์คํ
ํจํค์ง ์ค์น ์คํจ" |
|
|
fi |
|
|
fi |
|
|
|
|
|
local comfyui_req_file="${COMFYUI_DIR}/requirements.txt" |
|
|
if [ -f "$comfyui_req_file" ]; then |
|
|
log_info "ComfyUI ํต์ฌ ์ข
์์ฑ ์ฒ๋ฆฌ ์ค: $comfyui_req_file" |
|
|
if filter_requirements "$comfyui_req_file"; then |
|
|
log_info "ComfyUI์ ์๋ก์ด ์ข
์์ฑ ์ค์น ์ค..." |
|
|
$PIP_BIN install -q --no-cache-dir -r "$comfyui_req_file" 2>&1 | grep -v "Requirement already satisfied" || log_warn "ComfyUI ์ผ๋ถ ํต์ฌ ์ข
์์ฑ ์ค์น ์คํจ" |
|
|
fi |
|
|
else |
|
|
log_warn "ComfyUI์ ํต์ฌ requirements.txt ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค!" |
|
|
fi |
|
|
} |
|
|
|
|
|
provisioning_step2_nodes() { |
|
|
log_step "2" "๋
ธ๋ ๋ฐ ์ถ๊ฐ ํจํค์ง ์ค์น" |
|
|
if [ ${#NODES[@]} -gt 0 ]; then |
|
|
cd "${COMFYUI_DIR}/custom_nodes" || exit 1 |
|
|
log_info "${#NODES[@]}๊ฐ์ ๋
ธ๋๋ฅผ ๋ณ๋ ฌ ์ค์น ์ค..." |
|
|
local MAX_PARALLEL=2 |
|
|
for node in "${NODES[@]}"; do |
|
|
while [ $(jobs -r | wc -l) -ge $MAX_PARALLEL ]; do sleep 1; done |
|
|
install_node "$node" & |
|
|
done |
|
|
wait |
|
|
log_success "๋
ธ๋ ์ค์น ์๋ฃ" |
|
|
cd "${COMFYUI_DIR}" || exit 1 |
|
|
fi |
|
|
} |
|
|
|
|
|
provisioning_step3_downloads() { |
|
|
log_step "3" "๋ชจ๋ธ ๋ฐ ์ํฌํ๋ก์ฐ ํ์ผ ๋ค์ด๋ก๋" |
|
|
download_to_directory "${COMFYUI_DIR}/models/checkpoints" "${CHECKPOINT_MODELS[@]}" |
|
|
download_to_directory "${COMFYUI_DIR}/models/clip_vision" "${CLIP_VISION_MODELS[@]}" |
|
|
download_to_directory "${COMFYUI_DIR}/models/ipadapter" "${IPADAPTER_MODELS[@]}" |
|
|
download_to_directory "${COMFYUI_DIR}/models/upscale_models" "${ESRGAN_MODELS[@]}" |
|
|
if [ ${#WORKFLOWS[@]} -gt 0 ]; then |
|
|
log_info "์ํฌํ๋ก์ฐ ํ์ผ ๋ค์ด๋ก๋ ์ค..." |
|
|
download_to_directory "${COMFYUI_DIR}/user/default/workflows" "${WORKFLOWS[@]}" |
|
|
fi |
|
|
} |
|
|
|
|
|
if [ -f "/workspace/.provision_complete" ]; then |
|
|
log_success "ํ๊ฒฝ์ด ๊ตฌ์ฑ๋์์ต๋๋ค. ๋ฐ๋ณต ์คํ์ ๊ฑด๋๋๋๋ค" |
|
|
log_info "์ฌ๊ตฌ์ฑํ๋ ค๋ฉด /workspace/.provision_complete๋ฅผ ์ญ์ ํ์ญ์์ค" |
|
|
exit 0 |
|
|
fi |
|
|
if [[ -f /.noprovisioning ]]; then |
|
|
log_warn "/.noprovisioning ํ์ผ ๊ฐ์ง๋จ, ๊ตฌ์ฑ์ ๊ฑด๋๋๋๋ค" |
|
|
exit 0 |
|
|
fi |
|
|
|
|
|
START_TIME=$(date +%s) |
|
|
provisioning_print_header |
|
|
cd "${COMFYUI_DIR}" || exit 1 |
|
|
|
|
|
provisioning_step1_install_core_deps |
|
|
|
|
|
provisioning_step2_nodes & |
|
|
NODE_PID=$! |
|
|
provisioning_step3_downloads |
|
|
wait $NODE_PID |
|
|
|
|
|
log_step "4" "๋ค์ด๋ก๋ ๋ฌด๊ฒฐ์ฑ ํ์ธ" |
|
|
verify_and_retry_downloads "${COMFYUI_DIR}/models/checkpoints" "${CHECKPOINT_MODELS[@]}" |
|
|
verify_and_retry_downloads "${COMFYUI_DIR}/models/clip_vision" "${CLIP_VISION_MODELS[@]}" |
|
|
verify_and_retry_downloads "${COMFYUI_DIR}/models/ipadapter" "${IPADAPTER_MODELS[@]}" |
|
|
verify_and_retry_downloads "${COMFYUI_DIR}/models/upscale_models" "${ESRGAN_MODELS[@]}" |
|
|
verify_and_retry_downloads "${COMFYUI_DIR}/user/default/workflows" "${WORKFLOWS[@]}" |
|
|
|
|
|
log_step "5" "๊ตฌ์ฑ ์๋ฃ" |
|
|
touch "/workspace/.provision_complete" |
|
|
log_success "๊ตฌ์ฑ ๋ง์ปค ํ์ผ์ด ์์ฑ๋์์ต๋๋ค" |
|
|
|
|
|
END_TIME=$(date +%s) |
|
|
provisioning_print_end |
|
|
log_success "๋ชจ๋ ๊ตฌ์ฑ ๋จ๊ณ๊ฐ ์๋ฃ๋์์ต๋๋ค!" |
|
|
exit 0 |