chore: 저장소 관리형 pre-commit 훅 추가
- 백업 환경에서 사용하던 Unity 자산 네이밍 컨벤션 검사 훅을 .githooks/pre-commit으로 저장소에 편입 - Anim 클립과 Data_Skill 자산의 키 매칭 검사를 동일하게 유지해 로컬 커밋 전에 누락을 차단 - 현재 clone은 core.hooksPath=.githooks 로 연결해 즉시 동일한 pre-commit 검사를 사용하도록 설정
This commit is contained in:
212
.githooks/pre-commit
Executable file
212
.githooks/pre-commit
Executable file
@@ -0,0 +1,212 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Pre-commit hook: Unity 에셋 네이밍 컨벤션 검사
|
||||
# 검사 대상: Assets/_Game/ 하위에 새로 추가(A)되거나 이름 변경(R)된 파일
|
||||
# 제외: .meta 파일, Scripts/
|
||||
|
||||
ERRORS=()
|
||||
|
||||
STAGED_FILES=$(git diff --cached --name-only --diff-filter=AR \
|
||||
| grep "^Assets/_Game/" \
|
||||
| grep -v "\.meta$" \
|
||||
| grep -v "^Assets/_Game/Scripts/" \
|
||||
| grep -v "Human-Custom" \
|
||||
| grep -E "\.(asset|prefab|controller|fbx)$")
|
||||
|
||||
for FILE in $STAGED_FILES; do
|
||||
FILENAME=$(basename "$FILE")
|
||||
NAME="${FILENAME%.*}" # 확장자 제거
|
||||
DIR=$(dirname "$FILE")
|
||||
|
||||
VALID=true
|
||||
EXPECTED=""
|
||||
|
||||
case "$DIR" in
|
||||
Assets/_Game/Data/Passives/Nodes*)
|
||||
EXPECTED="Data_PassiveNode_{대상}_{이름} 예) Data_PassiveNode_Player_Tank_Core"
|
||||
echo "$NAME" | grep -qE "^Data_PassiveNode_(Player|Boss|Enemy|Common|[A-Za-z]+)_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Data/Passives/Presets*)
|
||||
EXPECTED="Data_PassivePreset_{대상}_{이름} 예) Data_PassivePreset_Player_Tank"
|
||||
echo "$NAME" | grep -qE "^Data_PassivePreset_(Player|Boss|Enemy|Common|[A-Za-z]+)_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Data/Passives*)
|
||||
EXPECTED="Data_PassiveTree_{대상}_{이름} / Data_PassiveCatalog_{대상}_{이름} / Data_PassivePrototypeCatalog 예) Data_PassiveTree_Player_Prototype"
|
||||
echo "$NAME" | grep -qE "^Data_Passive(Tree|Catalog)_(Player|Boss|Enemy|Common|[A-Za-z]+)_.+$|^Data_PassivePrototypeCatalog$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Data/Skills/Effects*)
|
||||
EXPECTED="Data_SkillEffect_{대상}_{스킬명}_{순서}_{효과} 예) Data_SkillEffect_Player_베기_0_데미지"
|
||||
echo "$NAME" | grep -qE "^Data_SkillEffect_(Player|Boss|Enemy|Common|[A-Za-z]+)_.+_[0-9]+_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Data/Skills*)
|
||||
EXPECTED="Data_Skill_{대상}_{이름} 예) Data_Skill_Player_근접베기"
|
||||
echo "$NAME" | grep -qE "^Data_Skill_(Player|Boss|Enemy|Common|[A-Za-z]+)_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Data/Abnormalities*)
|
||||
EXPECTED="Data_Abnormality_{대상}_{이름} 예) Data_Abnormality_Test_버프"
|
||||
echo "$NAME" | grep -qE "^Data_Abnormality_.+_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Data/Weapons*)
|
||||
EXPECTED="Data_Weapon_{이름} 예) Data_Weapon_검"
|
||||
echo "$NAME" | grep -qE "^Data_Weapon_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Data/Enemies*)
|
||||
EXPECTED="Data_Enemy_{이름} 예) Data_Enemy_TestBoss"
|
||||
echo "$NAME" | grep -qE "^Data_Enemy_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Data/Patterns*)
|
||||
EXPECTED="Data_Pattern_{대상}_{이름} 예) Data_Pattern_TestBoss_우수2연타"
|
||||
echo "$NAME" | grep -qE "^Data_Pattern_.+_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Prefabs/UI*)
|
||||
EXPECTED="UI_{이름} 예) UI_BossHealthBar"
|
||||
echo "$NAME" | grep -qE "^UI_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Prefabs*)
|
||||
EXPECTED="Prefab_{대상}_{이름} 예) Prefab_Player_Default"
|
||||
echo "$NAME" | grep -qE "^Prefab_(Player|Boss|Enemy|Weapon|Common|[A-Za-z]+)_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Animations/Controllers*)
|
||||
EXPECTED="AC_{대상}_{이름} 예) AC_Player_Default"
|
||||
echo "$NAME" | grep -qE "^AC_(Player|Boss|Enemy|Common|[A-Za-z]+)_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/Animations*)
|
||||
if echo "$NAME" | grep -qE "^Anim_Player_"; then
|
||||
# 플레이어: Anim_Player_{무기타입}_{스킬이름}_{순서}
|
||||
# 무기타입: 한손, 양손, 활, 방패, 마법, 공용
|
||||
# 순서: _{숫자} (선택)
|
||||
EXPECTED="Anim_Player_{무기타입}_{스킬이름}_{순서} 예) Anim_Player_한손_베기_0, Anim_Player_공용_구르기_0"
|
||||
echo "$NAME" | grep -qE "^Anim_Player_(한손|양손|활|방패|마법|공용)_.+_[0-9]+$" || VALID=false
|
||||
else
|
||||
# 기타: Anim_{소유자}_{이름}_{순서}
|
||||
# 순서: _{숫자} (선택)
|
||||
EXPECTED="Anim_{소유자}_{이름}_{순서} 예) Anim_Drog_평타1R_0, Anim_Common_Idle"
|
||||
echo "$NAME" | grep -qE "^Anim_(Player|Boss|Enemy|Common|[A-Za-z]+)_.+(_[0-9]+)?$" || VALID=false
|
||||
fi
|
||||
;;
|
||||
Assets/_Game/Models*)
|
||||
EXPECTED="Model_{대상}_{이름} 예) Model_Player_Base"
|
||||
echo "$NAME" | grep -qE "^Model_(Player|Boss|Enemy|Weapon|Common|[A-Za-z]+)_.+$" || VALID=false
|
||||
;;
|
||||
Assets/_Game/AI*)
|
||||
EXPECTED="BT_{이름} 예) BT_TestBoss"
|
||||
echo "$NAME" | grep -qE "^BT_.+$" || VALID=false
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$VALID" = false ]; then
|
||||
ERRORS+=("$FILE\n 형식: $EXPECTED")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#ERRORS[@]} -gt 0 ]; then
|
||||
echo ""
|
||||
echo "❌ 네이밍 컨벤션 위반:"
|
||||
for ERR in "${ERRORS[@]}"; do
|
||||
echo -e " ✗ $ERR"
|
||||
done
|
||||
echo ""
|
||||
echo "파일 이름을 수정하거나, 의도적으로 건너뛰려면 git commit --no-verify 를 사용하세요."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Anim_ ↔ Data_Skill_ 매칭 검증 ──
|
||||
# Anim_{key}_{순서} 클립과 Data_Skill_{key} 애셋의 key가 일치하는지 확인합니다.
|
||||
|
||||
MISMATCH_ERRORS=()
|
||||
|
||||
# 스테이징된 Anim_ 클립에서 key 추출 (Anim_ 접두사 제거 후 마지막 _숫자 제거)
|
||||
ANIM_KEYS=()
|
||||
STAGED_ANIMS=$(git diff --cached --name-only --diff-filter=ACDMR \
|
||||
| grep "^Assets/_Game/Animations/" \
|
||||
| grep "\.anim$" \
|
||||
| grep -v "\.meta$")
|
||||
|
||||
for FILE in $STAGED_ANIMS; do
|
||||
FILENAME=$(basename "$FILE")
|
||||
NAME="${FILENAME%.*}"
|
||||
if echo "$NAME" | grep -qE "^Anim_"; then
|
||||
# Anim_ 제거 → 마지막 _숫자 제거 → key
|
||||
KEY=$(echo "$NAME" | sed 's/^Anim_//' | sed 's/_[0-9]\+$//')
|
||||
if [ -n "$KEY" ]; then
|
||||
ANIM_KEYS+=("$KEY")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 스테이징된 Data_Skill_ 애셋에서 key 추출
|
||||
SKILL_KEYS=()
|
||||
STAGED_SKILLS=$(git diff --cached --name-only --diff-filter=ACDMR \
|
||||
| grep "^Assets/_Game/Data/Skills/Data_Skill_" \
|
||||
| grep "\.asset$" \
|
||||
| grep -v "\.meta$")
|
||||
|
||||
for FILE in $STAGED_SKILLS; do
|
||||
FILENAME=$(basename "$FILE")
|
||||
NAME="${FILENAME%.*}"
|
||||
KEY=$(echo "$NAME" | sed 's/^Data_Skill_//')
|
||||
if [ -n "$KEY" ]; then
|
||||
SKILL_KEYS+=("$KEY")
|
||||
fi
|
||||
done
|
||||
|
||||
# 매칭 검증: 스테이징된 Data_Skill_에 대응하는 Anim_ 클립이 있는지
|
||||
for SKILL_KEY in "${SKILL_KEYS[@]}"; do
|
||||
FOUND=false
|
||||
# 워킹 트리(스테이징 포함)에서 매칭 클립 검색
|
||||
MATCHED=$(git diff --cached --name-only --diff-filter=ACDMR \
|
||||
| grep "^Assets/_Game/Animations/Anim_${SKILL_KEY}_" \
|
||||
| grep "\.anim$" \
|
||||
| head -1)
|
||||
if [ -n "$MATCHED" ]; then
|
||||
FOUND=true
|
||||
fi
|
||||
# 워킹 트리에서 기존 클립도 확인
|
||||
if [ "$FOUND" = false ]; then
|
||||
MATCHED=$(find Assets/_Game/Animations -name "Anim_${SKILL_KEY}_[0-9]*.anim" 2>/dev/null | head -1)
|
||||
if [ -n "$MATCHED" ]; then
|
||||
FOUND=true
|
||||
fi
|
||||
fi
|
||||
if [ "$FOUND" = false ]; then
|
||||
MISMATCH_ERRORS+=("Data_Skill_${SKILL_KEY} 에 대응하는 Anim_${SKILL_KEY}_{순서} 클립이 없습니다")
|
||||
fi
|
||||
done
|
||||
|
||||
# 매칭 검증: 스테이징된 Anim_ 클립에 대응하는 Data_Skill_이 있는지
|
||||
for ANIM_KEY in "${ANIM_KEYS[@]}"; do
|
||||
FOUND=false
|
||||
# 워킹 트리에서 매칭 스킬 검색
|
||||
MATCHED=$(find Assets/_Game/Data/Skills -name "Data_Skill_${ANIM_KEY}.asset" 2>/dev/null | head -1)
|
||||
if [ -n "$MATCHED" ]; then
|
||||
FOUND=true
|
||||
fi
|
||||
# 스테이징된 파일에서도 확인
|
||||
if [ "$FOUND" = false ]; then
|
||||
for FILE in $STAGED_SKILLS; do
|
||||
FILENAME=$(basename "$FILE")
|
||||
NAME="${FILENAME%.*}"
|
||||
CHECK_KEY=$(echo "$NAME" | sed 's/^Data_Skill_//')
|
||||
if [ "$CHECK_KEY" = "$ANIM_KEY" ]; then
|
||||
FOUND=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [ "$FOUND" = false ]; then
|
||||
MISMATCH_ERRORS+=("Anim_${ANIM_KEY} 클립에 대응하는 Data_Skill_${ANIM_KEY} 애셋이 없습니다")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#MISMATCH_ERRORS[@]} -gt 0 ]; then
|
||||
echo ""
|
||||
echo "❌ Anim_ ↔ Data_Skill_ 매칭 누락:"
|
||||
for ERR in "${MISMATCH_ERRORS[@]}"; do
|
||||
echo -e " ✗ $ERR"
|
||||
done
|
||||
echo ""
|
||||
echo "대응하는 애셋을 생성하거나, 의도적으로 건너뛰려면 git commit --no-verify 를 사용하세요."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user