Last active
May 3, 2025 05:55
-
-
Save m-fire/380efe79a1e9dbb637abd15e81d1fa0f to your computer and use it in GitHub Desktop.
특정 깃허브 사용자의 저장소에 릴리즈 된 Assets 다운로드 스크립트(종류별 확장자지정 가능)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# ============================================================================== | |
# GitHub 최신 릴리즈 에셋 병렬 다운로드 스크립트 | |
# ============================================================================== | |
# | |
# 사용법: ./download-github-assets.sh <소유자> <저장소> <다운로드_경로> [옵션] | |
# 예시: ./download-github-assets.sh octocat Spoon-Knife ./downloads -e zip,tar.gz | |
# | |
# ============================================================================== | |
# --- 필수 명령어 확인 --- | |
command -v curl >/dev/null 2>&1 || { echo >&2 "오류: 'curl' 명령어를 찾을 수 없습니다. 설치 후 다시 시도해주세요."; exit 1; } | |
command -v jq >/dev/null 2>&1 || { echo >&2 "오류: 'jq' 명령어를 찾을 수 없습니다. 설치 후 다시 시도해주세요."; exit 1; } | |
# --- 도움말 함수 --- | |
usage() { | |
echo -e "" | |
echo -e "--------------------------------------------------" | |
echo -e "🌟 GitHub Asset 다운로드 🌟" | |
echo -e "--------------------------------------------------" | |
echo -e "사용법: $(basename "$0") <repo-owner> <repo-name> <download-path> [options]" | |
echo -e "예시) $ $(basename "$0") my-name repo-name ./download -e zip,tar.gz" | |
echo -e "" | |
echo -e "필수 인자:" | |
echo -e " <repo-owner> : GitHub 저장소 소유자 이름" | |
echo -e " <repo-name> : GitHub 저장소 이름" | |
echo -e " <download-path> : 파일들이 저장될 로컬 디렉토리" | |
echo -e "" | |
echo -e "옵션:" | |
echo -e " -e, --ext : 다운로드 할 확장자 목록 (쉼표로 구분, 기본값: \"zip\") 예: zip,txt,md" | |
echo -e " -h, --help : 도움말 출력" | |
exit 1 | |
} | |
# --- 인자 파싱 --- | |
# 도움말 옵션 확인 (개별 인자 확인 방식) | |
for arg in "$@"; do | |
if [[ "$arg" == "-h" ]] || [[ "$arg" == "--help" ]]; then | |
usage | |
fi | |
done | |
# 필수 인자 개수 확인 (최소 3개) | |
if [ "$#" -lt 3 ]; then | |
echo "❌ 오류: 필수 인자가 부족합니다. (현재 $# 개)" | |
usage | |
fi | |
# 필수 인자 할당 | |
REPO_OWNER="$1" | |
REPO_NAME="$2" | |
DOWNLOAD_TO="$3" | |
shift 3 # 처리된 필수 인자 제거 | |
# 옵션 기본값 설정 | |
EXTENSIONS_STRING="zip" # 다운로드할 확장자 문자열 (쉼표 구분) | |
# 옵션 파싱 | |
while [ "$#" -gt 0 ]; do | |
case "$1" in | |
-e|--ext) | |
if [ -z "$2" ]; then | |
echo "❌ 오류: -e 또는 --ext 옵션에는 확장자 목록이 필요합니다." | |
usage | |
fi | |
EXTENSIONS_STRING="$2" | |
shift 2 # 처리된 옵션과 값 (2개)을 인자 목록에서 제거 | |
;; | |
*) # 그 외의 알 수 없는 옵션 처리 | |
shift # 알 수 없는 인자는 건너뛰기 | |
;; | |
esac | |
done | |
# 확장자 문자열을 배열로 변환 (쉼표 기준) | |
IFS=',' read -ra DOWNLOAD_EXTS <<< "$EXTENSIONS_STRING" | |
# --- 설정 정보 출력 --- | |
echo -e "" | |
echo -e "🚀 다운로드 설정:" | |
echo -e " From\t: ${REPO_OWNER}/${REPO_NAME}" | |
echo -e " To\t: ${DOWNLOAD_TO}" | |
echo -e " 확장자\t: [${EXTENSIONS_STRING}]" | |
echo -e "" | |
# --- GitHub API 요청 --- | |
API_URL="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" | |
echo -e "🔍 GitHub API 요청: ${API_URL}" | |
# curl을 사용하여 API 요청 및 응답 저장 | |
response=$(curl -fsSL "$API_URL") | |
curl_exit_code=$? | |
# curl 실행 오류 확인 | |
if [ $curl_exit_code -ne 0 ]; then | |
echo "❌ 오류: GitHub API 요청 실패 (curl 종료 코드: $curl_exit_code)." | |
echo " URL: $API_URL" | |
echo " 저장소 이름과 소유자가 올바른지, 네트워크 연결 상태를 확인하세요." | |
exit 1 | |
fi | |
# 응답이 비어있는 경우 | |
if [ -z "$response" ]; then | |
echo "❌ 오류: GitHub API로부터 비어있는 응답을 받았습니다." | |
exit 1 | |
fi | |
# jq를 사용하여 API 응답에서 오류 메시지 확인 | |
message=$(echo "$response" | jq -r '.message // "null"') | |
jq_exit_code=$? | |
if [ $jq_exit_code -ne 0 ]; then | |
echo "❌ 오류: jq 실행 실패 (종료 코드: $jq_exit_code). API 응답 처리 중 문제 발생." | |
echo " API 응답 내용:" | |
echo "$response" | |
exit 1 | |
fi | |
# API 자체에서 오류 메시지를 반환했는지 확인 | |
if [[ "$message" != "null" ]]; then | |
echo "❌ GitHub API 오류: $message" | |
echo " (저장소가 존재하지 않거나, 릴리즈가 없을 수 있습니다.)" | |
exit 1 | |
fi | |
# --- 릴리즈 정보 및 다운로드 준비 --- | |
echo "--- 릴리즈 정보 및 다운로드 준비 ---" | |
# 최신 릴리즈 태그 추출 및 출력 | |
latest_tag=$(echo "$response" | jq -r '.tag_name // ""') | |
if [[ -z "$latest_tag" ]]; then | |
echo "⚠️ 경고: 최신 릴리즈 태그 정보를 찾을 수 없습니다." | |
else | |
echo -e "🌈 최신 릴리즈 태그: $latest_tag" | |
fi | |
# 다운로드 대상 디렉토리 준비 | |
echo -e "\n🧹 다운로드 디렉토리 준비: $DOWNLOAD_TO" | |
if [ -d "$DOWNLOAD_TO" ]; then | |
echo " 기존 디렉토리를 삭제합니다." | |
rm -rf "$DOWNLOAD_TO" | |
if [ $? -ne 0 ]; then | |
echo "❌ 오류: 기존 디렉토리 삭제 실패: $DOWNLOAD_TO" | |
exit 1 | |
fi | |
fi | |
mkdir -p "$DOWNLOAD_TO" | |
if [ $? -ne 0 ]; then | |
echo "❌ 오류: 다운로드 디렉토리 생성 실패: $DOWNLOAD_TO" | |
exit 1 | |
fi | |
echo "다운로드 디렉토리 준비 완료." | |
# --- 에셋 병렬 다운로드 --- | |
echo -e "\n⬇️ 에셋 파일 병렬 다운로드 시작:" | |
asset_count=0 | |
download_pids=() # 백그라운드 다운로드 프로세스 ID(PID)를 저장할 배열 | |
# jq를 사용하여 assets 배열 처리 | |
while IFS= read -r asset_json; do | |
if [ -z "$asset_json" ]; then | |
continue | |
fi | |
asset_count=$((asset_count + 1)) | |
asset_name=$(echo "$asset_json" | jq -r '.name // ""') | |
download_url=$(echo "$asset_json" | jq -r '.browser_download_url // ""') | |
if [[ -z "$asset_name" ]] || [[ -z "$download_url" ]]; then | |
echo "⏩ 건너뜀: 에셋 정보 파싱 오류 (JSON: $asset_json)" | |
continue | |
fi | |
should_download=false | |
for ext in "${DOWNLOAD_EXTS[@]}"; do | |
trimmed_ext=$(echo "$ext" | xargs) | |
if [[ -n "$trimmed_ext" && "$asset_name" == *."$trimmed_ext" ]]; then | |
should_download=true | |
break | |
fi | |
done | |
if [ "$should_download" = true ]; then | |
echo -e "📥 백그라운드 다운로드 시작: $asset_name" | |
# curl 명령어를 백그라운드에서 실행하고 '&' PID를 배열에 저장 | |
( # 서브쉘에서 실행하여 개별 오류 처리 용이 | |
curl -sSL -o "$DOWNLOAD_TO/$asset_name" "$download_url" | |
# 개별 다운로드 성공/실패 메시지 (선택 사항) | |
# if [ $? -eq 0 ]; then | |
# echo " ✅ 완료: $asset_name" | |
# else | |
# echo " ❌ 실패: $asset_name (오류 코드: $?)" | |
# # 실패 시 임시 파일 삭제 등 추가 처리 가능 | |
# # rm -f "$DOWNLOAD_TO/$asset_name" | |
# fi | |
) & # '&'를 붙여 백그라운드 실행 | |
download_pids+=($!) # 마지막 백그라운드 프로세스의 PID 저장 | |
else | |
echo -e "⏩ 건너뜀 (확장자 불일치): $asset_name" | |
fi | |
done < <(echo "$response" | jq -c '.assets[]?') | |
# --- 모든 백그라운드 다운로드 완료 대기 --- | |
echo -e "\n⏳ 모든 다운로드가 완료될 때까지 기다립니다..." | |
wait_count=0 | |
total_pids=${#download_pids[@]} | |
failed_pids=() | |
# 저장된 모든 PID에 대해 wait 명령어 실행 | |
for pid in "${download_pids[@]}"; do | |
wait "$pid" # 해당 PID의 프로세스가 종료될 때까지 기다림 | |
exit_code=$? # 프로세스의 종료 코드 확인 | |
if [ $exit_code -ne 0 ]; then | |
echo "⚠️ 경고: PID $pid (다운로드 프로세스)가 오류 코드 $exit_code 로 종료되었습니다." | |
failed_pids+=($pid) | |
fi | |
wait_count=$((wait_count + 1)) | |
# 진행률 표시 (선택 사항) | |
# echo -ne " 진행률: $wait_count / $total_pids \r" | |
done | |
echo -e "\n모든 백그라운드 작업 확인 완료." | |
# --- 완료 메시지 --- | |
echo -e "\n--------------------------------------------------" | |
download_count=$((total_pids - ${#failed_pids[@]})) # 성공한 다운로드 수 계산 | |
if [ $asset_count -eq 0 ]; then | |
echo "ℹ️ 정보: 최신 릴리즈에 다운로드할 에셋이 없습니다." | |
elif [ $total_pids -eq 0 ]; then # 다운로드를 시도한 파일이 없는 경우 | |
echo "⚠️ 경고: 지정된 확장자와 일치하는 에셋이 없어 다운로드된 파일이 없습니다." | |
elif [ ${#failed_pids[@]} -gt 0 ]; then # 실패한 다운로드가 있는 경우 | |
echo "⚠️ 경고: 총 ${total_pids}개 중 ${download_count}개의 파일 다운로드를 완료했습니다. (${#failed_pids[@]}개 실패)" | |
echo " 실패한 프로세스 PID: ${failed_pids[*]}" | |
else # 모든 다운로드가 성공한 경우 | |
echo "✅ 다운로드 완료! 총 ${download_count}개의 파일을 받았습니다." | |
fi | |
# tree 명령어가 존재하고, 다운로드된 파일이 있을 경우 tree 구조 출력 | |
if command -v tree >/dev/null 2>&1 && [ $download_count -gt 0 ]; then | |
tree "$DOWNLOAD_TO" | |
elif [ $download_count -gt 0 ]; then | |
echo -e "\nℹ️ 'tree' 명령어를 찾을 수 없어 파일 구조를 표시할 수 없습니다." | |
fi | |
echo -e "--------------------------------------------------" | |
# 실패한 다운로드가 있으면 0이 아닌 종료 코드 반환 (선택 사항) | |
if [ ${#failed_pids[@]} -gt 0 ]; then | |
exit 1 | |
else | |
exit 0 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment