레포트 작성 기능 보완

This commit is contained in:
Lectom C Han
2025-12-19 17:06:19 +09:00
parent e04467f962
commit 4de3e04b99
5 changed files with 186 additions and 32 deletions

View File

@@ -31,9 +31,10 @@ jobs:
run: |
set -euo pipefail
echo "Refreshing mirror repos on 172.16.10.191 ..."
ssh engdev@172.16.10.191 'set -euo pipefail; shopt -s nullglob; for repo in */.git; do dir="${repo%/.git}"; echo "Updating ${dir}"; (cd "${dir}" && git fetch --mirror --prune); done'
ssh engdev@172.16.10.191 'set -euo pipefail; export GIT_PROGRESS_DELAY=0 GIT_FLUSH=1; shopt -s nullglob; for repo in *.git; do [ -d "${repo}" ] || continue; echo "Updating ${repo}"; (cd "${repo}" && git fetch --progress --mirror --prune); done'
- name: Backup Branches (pre-scan → decision → execution)
continue-on-error: true
env:
BASE_GITEA_TOKEN: ${{ secrets.BASE_GITEA_TOKEN }}
BASE_GITEA_URL: ${{ vars.BASE_GITEA_URL }} # e.g., https://gitea.example.com
@@ -52,6 +53,8 @@ jobs:
NOTIFY_WEBHOOK="${NOTIFY_WEBHOOK:-}"
SYNC_TAGS="${SYNC_TAGS:-true}"
TARGET_SEED_DEPTH="${TARGET_SEED_DEPTH:-50}"
export GIT_PROGRESS_DELAY=0
export GIT_FLUSH=1
if ! [[ "${TARGET_SEED_DEPTH}" =~ ^[0-9]+$ ]] || (( TARGET_SEED_DEPTH <= 0 )); then
echo "::warning::TARGET_SEED_DEPTH(${TARGET_SEED_DEPTH}) is invalid; resetting to 50"
TARGET_SEED_DEPTH=50
@@ -64,13 +67,14 @@ jobs:
TS_KST=$(TZ=Asia/Seoul date '+%Y%m%d_%H%M%S')
REPORT_DIR="${ROOT_DIR}/backup_reports"
mkdir -p "${REPORT_DIR}"
SOURCE_HEADS_FILE="${REPORT_DIR}/source_heads_${TS_KST}.tsv"
TARGET_HEADS_FILE="${REPORT_DIR}/target_heads_${TS_KST}.tsv"
DECISIONS_FILE="${REPORT_DIR}/decisions_${TS_KST}.tsv"
echo -e "source_repo\tbranch\tcommit" > "${SOURCE_HEADS_FILE}"
echo -e "target_repo\tbranch\tcommit\texists" > "${TARGET_HEADS_FILE}"
echo -e "source_repo\tbranch\talias\tresolved_repo\tsource_commit\ttarget_commit\tdecision\tnote" > "${DECISIONS_FILE}"
DECISIONS_LOG="${REPORT_DIR}/decisions_${TS_KST}.log"
TIMINGS_LOG="${REPORT_DIR}/timings_${TS_KST}.log"
REPORT_MD="${REPORT_DIR}/report_${TS_KST}.md"
echo "REPORT_TS=${TS_KST}" >> "${GITHUB_ENV}"
echo "REPORT_DIR=${REPORT_DIR}" >> "${GITHUB_ENV}"
echo "SOURCE_SSH_HOST=${SOURCE_SSH_HOST}" >> "${GITHUB_ENV}"
: > "${DECISIONS_LOG}"
: > "${TIMINGS_LOG}"
notify_status() {
local status="$1" repo="$2" branch="$3" mode="$4" start_epoch="$5" reason="${6:-}" details="${7:-}"
@@ -143,6 +147,28 @@ jobs:
fi
}
append_decision_row() {
local note="$1"
printf "%s|%s|%s|%s|%s|%s|%s|%s\n" "${source_repo}" "${branch_name}" "${alias_name}" "${repo_name}" "${source_commit}" "${target_commit}" "${decision}" "${note}" >> "${DECISIONS_LOG}"
}
append_timing_row() {
local status="$1" note="$2"
local end_ts="${exec_end_epoch:-$(date +%s)}"
local dec_ts="${decision_epoch:-$start_epoch}"
local exec_start_ts="${exec_start_epoch:-}"
local total_dur=$(( end_ts - start_epoch ))
local exec_dur=0
if [[ -n "${exec_start_ts}" ]]; then
exec_dur=$(( end_ts - exec_start_ts ))
fi
printf "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s\n" \
"${source_repo}" "${branch_name}" "${alias_name}" "${repo_name}" \
"${source_commit}" "${target_commit}" "${decision}" "${status}" \
"${start_epoch}" "${dec_ts}" "${exec_start_ts}" "${end_ts}" \
"${total_dur}" "${exec_dur}" "${note}" >> "${TIMINGS_LOG}"
}
map_repo_name() {
local branch_name="$1" alias_name="$2" resolved
resolved="${branch_name}"
@@ -227,14 +253,14 @@ jobs:
local source_repo="$1" cache_dir="${CACHE_BASE}/${source_repo//\//_}.git"
if [[ ! -d "${cache_dir}" ]]; then
echo "Initializing local cache for ${source_repo} at ${cache_dir}"
if ! git clone --mirror "${SOURCE_SSH_HOST}:${source_repo}" "${cache_dir}"; then
echo "::warning::Failed to clone cache for ${source_repo}"
if ! git clone --progress --mirror "${SOURCE_SSH_HOST}:${source_repo}" "${cache_dir}"; then
echo "::warning::Failed to clone cache for ${source_repo} (${SOURCE_SSH_HOST}:${source_repo})"
return 1
fi
else
echo "Refreshing cache for ${source_repo}"
if ! git -C "${cache_dir}" fetch --mirror --prune; then
echo "::warning::Failed to refresh cache for ${source_repo}"
if ! git -C "${cache_dir}" fetch --progress --mirror --prune; then
echo "::warning::Failed to refresh cache for ${source_repo} (${SOURCE_SSH_HOST}:${source_repo})"
return 1
fi
fi
@@ -252,13 +278,13 @@ jobs:
source_repo_url="${SOURCE_SSH_HOST}:${source_repo}"
if [[ -n "${cache_dir}" && -d "${cache_dir}" ]]; then
echo " - using cache: ${cache_dir}"
remote_output=$(git -C "${cache_dir}" for-each-ref --format='%(objectname)\t%(refname)' 'refs/heads/*') || {
echo "::warning::Failed to read heads from cache ${cache_dir}; falling back to remote."
if ! remote_output=$(git -C "${cache_dir}" for-each-ref --format='%(objectname)\t%(refname)' 'refs/heads/*'); then
echo "::warning::Failed to read heads from cache ${cache_dir}; falling back to remote ${source_repo_url}."
remote_output=$(git ls-remote --heads "${source_repo_url}") || {
echo "::warning::Failed to ls-remote ${source_repo_url}. Entries for this repo may fail."
continue
}
}
fi
else
echo " - ${source_repo_url}"
if ! remote_output=$(git ls-remote --heads "${source_repo_url}"); then
@@ -266,6 +292,19 @@ jobs:
continue
fi
fi
if [[ -z "${remote_output}" ]]; then
echo "::warning::No branches found for ${source_repo_url}"
continue
fi
while IFS=$'\t' read -r commit ref || [[ -n "${commit}" ]]; do
[[ -z "${commit}" || -z "${ref}" ]] && continue
branch="${ref#refs/heads/}"
SOURCE_HEADS["${source_repo}|${branch}"]="${commit}"
done <<< "${remote_output}"
done
continue
fi
fi
while IFS=$'\t' read -r commit ref || [[ -n "${commit}" ]]; do
[[ -z "${commit}" || -z "${ref}" ]] && continue
branch="${ref#refs/heads/}"
@@ -287,13 +326,19 @@ jobs:
note=""
target_commit=""
heads_detail="src=unknown tgt=unknown"
decision_epoch=""
exec_start_epoch=""
exec_end_epoch=""
source_commit="${SOURCE_HEADS[${source_repo}|${branch_name}]:-}"
if [[ -z "${source_commit}" ]]; then
note="source branch 없음/조회 실패"
echo -e "${source_repo}\t${branch_name}\t${alias_name}\t${repo_name}\t\t\t건너뜀\t${note}" >> "${DECISIONS_FILE}"
decision="사전 스캔 실패"
decision_epoch=$(date +%s)
note="source branch 없음/조회 실패 (repo=${source_repo}, branch=${branch_name})"
append_decision_row "${note}"
heads_detail="src=none tgt=unknown"
notify_status "error" "${repo_name}" "${branch_name}" "사전 스캔 실패" "${start_epoch}" "${note}" "${heads_detail}"
append_timing_row "error" "${note}"
echo "Skipping ${branch_name} (${repo_name}) - ${note}"
continue
fi
@@ -319,11 +364,14 @@ jobs:
elif [[ "${http_status}" == "404" ]]; then
repo_exists=false
else
decision="사전 조회 실패"
decision_epoch=$(date +%s)
note="repo 조회 실패 (HTTP ${http_status})"
echo "::warning::${note}"
echo -e "${source_repo}\t${branch_name}\t${alias_name}\t${repo_name}\t${source_commit}\t\t건너뜀\t${note}" >> "${DECISIONS_FILE}"
heads_detail="src=${source_commit} tgt=unknown"
notify_status "error" "${repo_name}" "${branch_name}" "사전 조회 실패" "${start_epoch}" "${note}" "${heads_detail}"
append_decision_row "${note}"
append_timing_row "error" "${note}"
continue
fi
@@ -351,11 +399,13 @@ jobs:
note="커밋 상이"
fi
echo -e "${source_repo}\t${branch_name}\t${alias_name}\t${repo_name}\t${source_commit}\t${target_commit}\t${decision}\t${note}" >> "${DECISIONS_FILE}"
decision_epoch=$(date +%s)
append_decision_row "${note}"
if [[ "${decision}" == "동일 커밋 - 건너뜀" ]]; then
notify_status "skip" "${repo_name}" "${branch_name}" "${decision}" "${start_epoch}" "" "${heads_detail}"
echo "Pre-scan marked ${branch_name} (${repo_name}) as skip (same commit)."
append_timing_row "skip" "${note}"
continue
fi
@@ -378,6 +428,8 @@ jobs:
if [[ "${create_status}" != "201" ]]; then
echo "::error::Failed to create repository. HTTP ${create_status}"
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "repo 생성 실패 (HTTP ${create_status})" "${heads_detail}"
exec_end_epoch=$(date +%s)
append_timing_row "error" "repo 생성 실패 (HTTP ${create_status})"
continue
fi
echo "Repository created successfully."
@@ -391,6 +443,7 @@ jobs:
notify_status "start" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "" "${heads_detail}"
exec_start_epoch=$(date +%s)
shallow_exclude_args=()
if ${repo_exists} && [[ -n "${target_commit:-}" ]]; then
shallow_exclude_args=(--shallow-exclude="${target_commit}")
@@ -404,6 +457,8 @@ jobs:
if ! git clone --bare --no-tags --single-branch --branch "${branch_name}" "${SOURCE_FETCH_REMOTE}" "${CLONE_DIR}"; then
echo "::error::Failed to clone source repository ${SOURCE_FETCH_REMOTE}"
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "source clone 실패" "${heads_detail}"
exec_end_epoch=$(date +%s)
append_timing_row "error" "source clone 실패"
rm -rf "${CLONE_DIR}"
continue
fi
@@ -415,6 +470,8 @@ jobs:
if ! git init --bare; then
echo "::error::Failed to init bare repository"
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "bare init 실패" "${heads_detail}"
exec_end_epoch=$(date +%s)
append_timing_row "error" "bare init 실패"
cd "${ROOT_DIR}"
rm -rf "${CLONE_DIR}"
continue
@@ -451,6 +508,8 @@ jobs:
fi
echo "::error::Failed to fetch branch '${branch_name}' from source repo (fallback without shallow-exclude)"
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "source fetch 오류(폴백)" "${heads_detail}"
exec_end_epoch=$(date +%s)
append_timing_row "error" "source fetch 오류(폴백)"
cd "${ROOT_DIR}"
rm -rf "${CLONE_DIR}"
continue
@@ -458,6 +517,8 @@ jobs:
else
echo "::error::Failed to fetch branch '${branch_name}' from source repo"
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "source fetch 오류" "${heads_detail}"
exec_end_epoch=$(date +%s)
append_timing_row "error" "source fetch 오류"
cd "${ROOT_DIR}"
rm -rf "${CLONE_DIR}"
continue
@@ -469,6 +530,8 @@ jobs:
if ! git fetch --progress --prune --prune-tags --no-tags source "refs/tags/*:refs/tags/*"; then
echo "::error::Failed to fetch tags from source repo"
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "tag fetch 오류" "${heads_detail}"
exec_end_epoch=$(date +%s)
append_timing_row "error" "tag fetch 오류"
cd "${ROOT_DIR}"
rm -rf "${CLONE_DIR}"
continue
@@ -479,6 +542,8 @@ jobs:
if ! git push --progress --force origin "refs/heads/${branch_name}:refs/heads/main"; then
echo "::error::Failed to push branch '${branch_name}' to target repository"
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "push 오류 (-> main)" "${heads_detail}"
exec_end_epoch=$(date +%s)
append_timing_row "error" "push 오류 (-> main)"
cd "${ROOT_DIR}"
rm -rf "${CLONE_DIR}"
continue
@@ -488,6 +553,8 @@ jobs:
if ! git push --force --prune origin "refs/tags/*:refs/tags/*"; then
echo "::warning::Failed to push tags to target repository"
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "tag push 오류" "${heads_detail}"
exec_end_epoch=$(date +%s)
append_timing_row "error" "tag push 오류"
fi
fi
@@ -497,6 +564,8 @@ jobs:
echo "Successfully mirrored ${branch_name} to center_dev/${repo_name}"
set_default_branch_main "${repo_name}"
final_heads_detail="src=${source_commit} tgt=${source_commit}"
exec_end_epoch=$(date +%s)
append_timing_row "success" "${backup_mode}"
notify_status "success" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "" "${final_heads_detail}"
echo "================================================="
echo ""
@@ -506,9 +575,8 @@ jobs:
SUMMARY_TS=$(TZ=Asia/Seoul date '+%Y-%m-%d %H:%M:%S %Z')
SUMMARY_TEXT="브랜치 동기화 완료: 총 ${TOTAL_PROCESSED}개 (성공 ${TOTAL_SUCCESS}, 동일로 건너뜀 ${TOTAL_SKIP}, 오류 ${TOTAL_ERROR}) - ${SUMMARY_TS}"
echo "${SUMMARY_TEXT}"
echo "Source head 기록: ${SOURCE_HEADS_FILE}"
echo "Target head 기록: ${TARGET_HEADS_FILE}"
echo "판정 테이블: ${DECISIONS_FILE}"
echo "보고서(마크다운)는 이후 스텝에서 생성됩니다."
echo "${TOTAL_ERROR}" > "${REPORT_DIR}/exit_code"
if [[ -n "${NOTIFY_WEBHOOK}" ]]; then
SUMMARY_PAYLOAD=${SUMMARY_TEXT//\"/\\\"}
curl -sS -i -X POST \
@@ -521,3 +589,86 @@ jobs:
echo "::warning::One or more branches failed (${TOTAL_ERROR})."
exit 1
fi
- name: Build markdown report
if: always()
run: |
set -euo pipefail
REPORT_DIR="${REPORT_DIR:-${{ github.workspace }}/backup_reports}"
TS="${REPORT_TS:-}"
if [[ -z "${TS}" ]]; then
TS=$(ls -t ${REPORT_DIR}/decisions_*.log 2>/dev/null | head -1 | sed -E 's/.*decisions_([0-9_]+)\\.log/\\1/')
fi
if [[ -z "${TS}" ]]; then
echo "::error::No report timestamp found; cannot build report."
exit 1
fi
DECISIONS_LOG="${REPORT_DIR}/decisions_${TS}.log"
TIMINGS_LOG="${REPORT_DIR}/timings_${TS}.log"
REPORT_MD="${REPORT_DIR}/report_${TS}.md"
SOURCE_SSH_HOST="${SOURCE_SSH_HOST:-${{ env.SOURCE_SSH_HOST }}}"
if [[ ! -f "${DECISIONS_LOG}" || ! -f "${TIMINGS_LOG}" ]]; then
echo "::error::Missing decisions or timings log."
exit 1
fi
total=0; success=0; skip=0; error=0
while IFS='|' read -r sr br al rr sc tc dc st se de ee xe dt dexe nt; do
[[ -z "${sr}" ]] && continue
((total++))
case "${st}" in
success) ((success++)) ;;
skip) ((skip++)) ;;
error) ((error++)) ;;
esac
done < "${TIMINGS_LOG}"
SUMMARY_TS=$(TZ=Asia/Seoul date '+%Y-%m-%d %H:%M:%S %Z')
{
echo "# Git Backup Report"
echo ""
echo "- Generated: ${SUMMARY_TS}"
echo "- Run ID: ${GITHUB_RUN_ID:-local}"
echo "- Source host: ${SOURCE_SSH_HOST}"
echo "- Total: ${total} (success ${success}, skip ${skip}, error ${error})"
echo ""
echo "## Decisions (all)"
echo "|source_repo|branch|alias|resolved_repo|source_commit|target_commit|decision|note|"
echo "|---|---|---|---|---|---|---|---|"
while IFS='|' read -r sr br al rr sc tc dc nt; do
[[ -z "${sr}" ]] && continue
printf "|%s|%s|%s|%s|%s|%s|%s|%s|\n" "$sr" "$br" "$al" "$rr" "$sc" "$tc" "$dc" "$nt"
done < "${DECISIONS_LOG}"
echo ""
echo "## Timings (all)"
echo "|source_repo|branch|alias|resolved_repo|source_commit|target_commit|decision|status|start_epoch|decision_epoch|exec_start_epoch|exec_end_epoch|duration_total(s)|duration_exec(s)|note|"
echo "|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|"
while IFS='|' read -r sr br al rr sc tc dc st se de ee xe dt dexe nt; do
[[ -z "${sr}" ]] && continue
printf "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|\n" "$sr" "$br" "$al" "$rr" "$sc" "$tc" "$dc" "$st" "$se" "$de" "$ee" "$xe" "$dt" "$dexe" "$nt"
done < "${TIMINGS_LOG}"
echo ""
} > "${REPORT_MD}"
echo "Report generated at ${REPORT_MD}"
- name: Upload backup reports
if: always()
uses: actions/upload-artifact@v4
with:
name: backup_reports_${{ github.run_id }}
path: backup_reports
- name: Fail if backup had errors
if: always()
run: |
EXIT_CODE=0
if [[ -f backup_reports/exit_code ]]; then
EXIT_CODE=$(cat backup_reports/exit_code)
fi
if [[ "${EXIT_CODE}" != "0" ]]; then
echo "::error::Backup reported ${EXIT_CODE} errors."
exit 1
else
echo "Backup completed without errors."
fi

View File

@@ -29,7 +29,7 @@ jobs:
run: |
set -euo pipefail
echo "Refreshing mirror repos on 172.16.10.191 ..."
ssh engdev@172.16.10.191 'set -euo pipefail; shopt -s nullglob; for repo in */.git; do dir="${repo%/.git}"; echo "Updating ${dir}"; (cd "${dir}" && git fetch --mirror --prune); done'
ssh engdev@172.16.10.191 'set -euo pipefail; export GIT_PROGRESS_DELAY=0 GIT_FLUSH=1; shopt -s nullglob; for repo in *.git; do [ -d "${repo}" ] || continue; echo "Updating ${repo}"; (cd "${repo}" && git fetch --progress --mirror --prune); done'
- name: Mirror Branches
env:
@@ -41,10 +41,12 @@ jobs:
SYNC_TAGS: ${{ vars.SYNC_TAGS }} # Optional, "false" to skip tag sync
run: |
set -euo pipefail
export GIT_PROGRESS_DELAY=0
export GIT_FLUSH=1
CENTER_ORG="center_dev"
AUTH_HEADER="Authorization: token ${BASE_GITEA_TOKEN}"
SOURCE_SSH_HOST="engdev@172.16.42.118"
SOURCE_SSH_HOST="engdev@172.16.10.191"
ROOT_DIR="$(pwd)"
NOTIFY_WEBHOOK="${NOTIFY_WEBHOOK:-}"
SYNC_TAGS="${SYNC_TAGS:-true}"

View File

@@ -8,7 +8,7 @@
- 트리거: cron `0 2 * * *``workflow_dispatch`.
- 기본 데이터 소스: `branch_list` 파일을 한 줄씩 읽어 순차 처리.
- 브랜치→저장소 매핑 규칙: `Develop_Net8``base`, `Develop_Net8_*``*` 부분을 저장소 이름으로 사용하며, 그 외 브랜치는 브랜치명을 그대로 저장소 이름으로 사용.
- 실행 단계: 워크플로우 리포지토리 체크아웃 → SSH 키 저장/권한 설정 후 `ssh-keyscan 172.16.10.191``engdev@172.16.10.191` 홈 내 `.git` 디렉터리를 가진 하위 디렉터리마다 `git fetch --mirror --prune`로 사전 동기화 → 워크플로우 내부에서 소스별 로컬 캐시를 생성/갱신해 동일 커밋의 중복 fetch를 최소화 → 각 브랜치마다 Gitea API로 `center_dev` 조직에 저장소 존재 여부 확인(있으면 대상 저장소를 mirror clone 후 source 브랜치를 fetch/pull해 업데이트만, 없으면 저장소 생성 시 기본 브랜치를 `main`으로 설정) → 소스 브랜치를 대상 저장소 `main`으로 강제 푸시 → 푸시 후에도 기본 브랜치를 `main`으로 패치 → 임시 폴더 정리. 오류가 나도 다음 브랜치로 계속 진행하도록 처리.
- 실행 단계: 워크플로우 리포지토리 체크아웃 → SSH 키 저장/권한 설정 후 `ssh-keyscan 172.16.10.191``engdev@172.16.10.191` 홈 내 `.git` 디렉터리를 가진 하위 디렉터리마다 `git fetch --mirror --prune`로 사전 동기화 → 워크플로우 내부에서 소스별 로컬 캐시를 생성/갱신해 동일 커밋의 중복 fetch를 최소화 → 각 브랜치마다 Gitea API로 `center_dev` 조직에 저장소 존재 여부 확인(있으면 대상 저장소를 mirror clone 후 source 브랜치를 fetch/pull해 업데이트만, 없으면 저장소 생성 시 기본 브랜치를 `main`으로 설정) → 소스 브랜치를 대상 저장소 `main`으로 강제 푸시 → 푸시 후에도 기본 브랜치를 `main`으로 패치 → 임시 폴더 정리. 오류가 나도 다음 브랜치로 계속 진행하도록 처리. 실행 후 `backup_reports/`(report_*.md에 Decisions/Timings 전체 테이블 포함)를 artifact로 업로드해 사후 확인 가능.
## 시크릿/변수
- 현재 요구: `SSH_PRIVATE_KEY`(소스 접근), `GITEA_TOKEN`(Gitea API/푸시), `GITEA_URL`(Gitea 인스턴스 URL).

View File

@@ -41,12 +41,13 @@
## backup.yml (프리스캔 백업) 동작 개요
1) 입력 또는 `branch_list`를 파싱해 유효 항목을 확정하고, 사전 단계에서 미러 서버(`engdev@172.16.10.191`) 홈 내 `.git` 디렉터리마다 `git fetch --mirror --prune`로 최신 상태로 맞춥니다.
2) 소스별 로컬 캐시(워크플로우 내 생성/갱신)를 이용해 브랜치 해시를 수집하고 필요한 브랜치만 fetch하므로 동일 커밋 다중 브랜치에서도 중복 전송을 줄입니다. 캐시가 없으면 원본을 직접 조회합니다.
2) Gitea에 해당 저장소가 있는지 조회 후 `main` 해시를 확인합니다.
3) 사전 판정: 타겟이 없거나 `main`이 없으면 “신규 백업”, 해시가 같으면 “건너뜀”, 다르면 “증분 백업”으로 결정합니다.
4) 해시 스냅샷과 판정 테이블을 `backup_reports/` 폴더에 `source_heads_*.tsv`, `target_heads_*.tsv`, `decisions_*.tsv`로 기록합니다.
5) “건너뜀”은 바로 알림 후 종료, “신규/증분”만 fetch→push 실행합니다.
6) 실행 단계: 저장소 존재 확인/생성(`default_branch=main`) → shallow-exclude 기반 fetch(미지원 시 타겟 main 얕은 시드 후 일반 fetch) → `main`으로 강제 푸시 → 태그 동기화(옵션) → 기본 브랜치 `main` 패치 → 임시 폴더 정리.
7) 알림은 시작/성공/실패/건너뜀에 대해 KST 타임스탬프, 모드, 소요시간을 포함해 전송합니다.
3) Gitea에 해당 저장소가 있는지 조회 후 `main` 해시를 확인합니다.
4) 사전 판정: 타겟이 없거나 `main`이 없으면 “신규 백업”, 해시가 같으면 “건너뜀”, 다르면 “증분 백업”으로 결정합니다.
5) 판정/타이밍 정보를 로그 파일(decisions/timings)로 남기고, 이후 스텝에서 `report_<ts>.md`로 전체 테이블을 생성합니다.
6) “건너뜀”은 바로 알림 후 종료, “신규/증분”만 fetch→push 실행합니다.
7) 실행 단계: 저장소 존재 확인/생성(`default_branch=main`) → shallow-exclude 기반 fetch(미지원 시 타겟 main 얕은 시드 후 일반 fetch) → `main`으로 강제 푸시 → 태그 동기화(옵션) → 기본 브랜치 `main` 패치 → 임시 폴더 정리.
8) 알림은 시작/성공/실패/건너뜀에 대해 KST 타임스탬프, 모드, 소요시간을 포함해 전송합니다.
9) 실행 후 `backup_reports/` 전체를 `backup_reports_<run_id>` 이름으로 artifact 업로드합니다. `report_*.md` 하나에 Decisions/Timings 전체 테이블이 포함됩니다.
## mirror.yml (즉시 복제) 동작 개요
1) 브랜치 존재 여부 확인 (`git ls-remote`)

View File

@@ -1,4 +1,4 @@
dev_Net8.git/Develop_Net8
dev_Net8.git/Develop_Net8, base
dev_Net8.git/Develop_Net8_bridge
dev_Net8.git/Develop_Net8_Graphics
dev_Net8.git/Develop_Net8_Graphics_B