|
|
|
@@ -31,9 +31,10 @@ jobs:
|
|
|
|
run: |
|
|
|
|
run: |
|
|
|
|
set -euo pipefail
|
|
|
|
set -euo pipefail
|
|
|
|
echo "Refreshing mirror repos on 172.16.10.191 ..."
|
|
|
|
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)
|
|
|
|
- name: Backup Branches (pre-scan → decision → execution)
|
|
|
|
|
|
|
|
continue-on-error: true
|
|
|
|
env:
|
|
|
|
env:
|
|
|
|
BASE_GITEA_TOKEN: ${{ secrets.BASE_GITEA_TOKEN }}
|
|
|
|
BASE_GITEA_TOKEN: ${{ secrets.BASE_GITEA_TOKEN }}
|
|
|
|
BASE_GITEA_URL: ${{ vars.BASE_GITEA_URL }} # e.g., https://gitea.example.com
|
|
|
|
BASE_GITEA_URL: ${{ vars.BASE_GITEA_URL }} # e.g., https://gitea.example.com
|
|
|
|
@@ -52,6 +53,8 @@ jobs:
|
|
|
|
NOTIFY_WEBHOOK="${NOTIFY_WEBHOOK:-}"
|
|
|
|
NOTIFY_WEBHOOK="${NOTIFY_WEBHOOK:-}"
|
|
|
|
SYNC_TAGS="${SYNC_TAGS:-true}"
|
|
|
|
SYNC_TAGS="${SYNC_TAGS:-true}"
|
|
|
|
TARGET_SEED_DEPTH="${TARGET_SEED_DEPTH:-50}"
|
|
|
|
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
|
|
|
|
if ! [[ "${TARGET_SEED_DEPTH}" =~ ^[0-9]+$ ]] || (( TARGET_SEED_DEPTH <= 0 )); then
|
|
|
|
echo "::warning::TARGET_SEED_DEPTH(${TARGET_SEED_DEPTH}) is invalid; resetting to 50"
|
|
|
|
echo "::warning::TARGET_SEED_DEPTH(${TARGET_SEED_DEPTH}) is invalid; resetting to 50"
|
|
|
|
TARGET_SEED_DEPTH=50
|
|
|
|
TARGET_SEED_DEPTH=50
|
|
|
|
@@ -64,13 +67,14 @@ jobs:
|
|
|
|
TS_KST=$(TZ=Asia/Seoul date '+%Y%m%d_%H%M%S')
|
|
|
|
TS_KST=$(TZ=Asia/Seoul date '+%Y%m%d_%H%M%S')
|
|
|
|
REPORT_DIR="${ROOT_DIR}/backup_reports"
|
|
|
|
REPORT_DIR="${ROOT_DIR}/backup_reports"
|
|
|
|
mkdir -p "${REPORT_DIR}"
|
|
|
|
mkdir -p "${REPORT_DIR}"
|
|
|
|
SOURCE_HEADS_FILE="${REPORT_DIR}/source_heads_${TS_KST}.tsv"
|
|
|
|
DECISIONS_LOG="${REPORT_DIR}/decisions_${TS_KST}.log"
|
|
|
|
TARGET_HEADS_FILE="${REPORT_DIR}/target_heads_${TS_KST}.tsv"
|
|
|
|
TIMINGS_LOG="${REPORT_DIR}/timings_${TS_KST}.log"
|
|
|
|
DECISIONS_FILE="${REPORT_DIR}/decisions_${TS_KST}.tsv"
|
|
|
|
REPORT_MD="${REPORT_DIR}/report_${TS_KST}.md"
|
|
|
|
|
|
|
|
echo "REPORT_TS=${TS_KST}" >> "${GITHUB_ENV}"
|
|
|
|
echo -e "source_repo\tbranch\tcommit" > "${SOURCE_HEADS_FILE}"
|
|
|
|
echo "REPORT_DIR=${REPORT_DIR}" >> "${GITHUB_ENV}"
|
|
|
|
echo -e "target_repo\tbranch\tcommit\texists" > "${TARGET_HEADS_FILE}"
|
|
|
|
echo "SOURCE_SSH_HOST=${SOURCE_SSH_HOST}" >> "${GITHUB_ENV}"
|
|
|
|
echo -e "source_repo\tbranch\talias\tresolved_repo\tsource_commit\ttarget_commit\tdecision\tnote" > "${DECISIONS_FILE}"
|
|
|
|
: > "${DECISIONS_LOG}"
|
|
|
|
|
|
|
|
: > "${TIMINGS_LOG}"
|
|
|
|
|
|
|
|
|
|
|
|
notify_status() {
|
|
|
|
notify_status() {
|
|
|
|
local status="$1" repo="$2" branch="$3" mode="$4" start_epoch="$5" reason="${6:-}" details="${7:-}"
|
|
|
|
local status="$1" repo="$2" branch="$3" mode="$4" start_epoch="$5" reason="${6:-}" details="${7:-}"
|
|
|
|
@@ -143,6 +147,28 @@ jobs:
|
|
|
|
fi
|
|
|
|
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() {
|
|
|
|
map_repo_name() {
|
|
|
|
local branch_name="$1" alias_name="$2" resolved
|
|
|
|
local branch_name="$1" alias_name="$2" resolved
|
|
|
|
resolved="${branch_name}"
|
|
|
|
resolved="${branch_name}"
|
|
|
|
@@ -227,14 +253,14 @@ jobs:
|
|
|
|
local source_repo="$1" cache_dir="${CACHE_BASE}/${source_repo//\//_}.git"
|
|
|
|
local source_repo="$1" cache_dir="${CACHE_BASE}/${source_repo//\//_}.git"
|
|
|
|
if [[ ! -d "${cache_dir}" ]]; then
|
|
|
|
if [[ ! -d "${cache_dir}" ]]; then
|
|
|
|
echo "Initializing local cache for ${source_repo} at ${cache_dir}"
|
|
|
|
echo "Initializing local cache for ${source_repo} at ${cache_dir}"
|
|
|
|
if ! git clone --mirror "${SOURCE_SSH_HOST}:${source_repo}" "${cache_dir}"; then
|
|
|
|
if ! git clone --progress --mirror "${SOURCE_SSH_HOST}:${source_repo}" "${cache_dir}"; then
|
|
|
|
echo "::warning::Failed to clone cache for ${source_repo}"
|
|
|
|
echo "::warning::Failed to clone cache for ${source_repo} (${SOURCE_SSH_HOST}:${source_repo})"
|
|
|
|
return 1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
else
|
|
|
|
else
|
|
|
|
echo "Refreshing cache for ${source_repo}"
|
|
|
|
echo "Refreshing cache for ${source_repo}"
|
|
|
|
if ! git -C "${cache_dir}" fetch --mirror --prune; then
|
|
|
|
if ! git -C "${cache_dir}" fetch --progress --mirror --prune; then
|
|
|
|
echo "::warning::Failed to refresh cache for ${source_repo}"
|
|
|
|
echo "::warning::Failed to refresh cache for ${source_repo} (${SOURCE_SSH_HOST}:${source_repo})"
|
|
|
|
return 1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
@@ -252,13 +278,13 @@ jobs:
|
|
|
|
source_repo_url="${SOURCE_SSH_HOST}:${source_repo}"
|
|
|
|
source_repo_url="${SOURCE_SSH_HOST}:${source_repo}"
|
|
|
|
if [[ -n "${cache_dir}" && -d "${cache_dir}" ]]; then
|
|
|
|
if [[ -n "${cache_dir}" && -d "${cache_dir}" ]]; then
|
|
|
|
echo " - using cache: ${cache_dir}"
|
|
|
|
echo " - using cache: ${cache_dir}"
|
|
|
|
remote_output=$(git -C "${cache_dir}" for-each-ref --format='%(objectname)\t%(refname)' 'refs/heads/*') || {
|
|
|
|
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."
|
|
|
|
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}") || {
|
|
|
|
remote_output=$(git ls-remote --heads "${source_repo_url}") || {
|
|
|
|
echo "::warning::Failed to ls-remote ${source_repo_url}. Entries for this repo may fail."
|
|
|
|
echo "::warning::Failed to ls-remote ${source_repo_url}. Entries for this repo may fail."
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fi
|
|
|
|
else
|
|
|
|
else
|
|
|
|
echo " - ${source_repo_url}"
|
|
|
|
echo " - ${source_repo_url}"
|
|
|
|
if ! remote_output=$(git ls-remote --heads "${source_repo_url}"); then
|
|
|
|
if ! remote_output=$(git ls-remote --heads "${source_repo_url}"); then
|
|
|
|
@@ -266,6 +292,19 @@ jobs:
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
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
|
|
|
|
while IFS=$'\t' read -r commit ref || [[ -n "${commit}" ]]; do
|
|
|
|
[[ -z "${commit}" || -z "${ref}" ]] && continue
|
|
|
|
[[ -z "${commit}" || -z "${ref}" ]] && continue
|
|
|
|
branch="${ref#refs/heads/}"
|
|
|
|
branch="${ref#refs/heads/}"
|
|
|
|
@@ -287,13 +326,19 @@ jobs:
|
|
|
|
note=""
|
|
|
|
note=""
|
|
|
|
target_commit=""
|
|
|
|
target_commit=""
|
|
|
|
heads_detail="src=unknown tgt=unknown"
|
|
|
|
heads_detail="src=unknown tgt=unknown"
|
|
|
|
|
|
|
|
decision_epoch=""
|
|
|
|
|
|
|
|
exec_start_epoch=""
|
|
|
|
|
|
|
|
exec_end_epoch=""
|
|
|
|
|
|
|
|
|
|
|
|
source_commit="${SOURCE_HEADS[${source_repo}|${branch_name}]:-}"
|
|
|
|
source_commit="${SOURCE_HEADS[${source_repo}|${branch_name}]:-}"
|
|
|
|
if [[ -z "${source_commit}" ]]; then
|
|
|
|
if [[ -z "${source_commit}" ]]; then
|
|
|
|
note="source branch 없음/조회 실패"
|
|
|
|
decision="사전 스캔 실패"
|
|
|
|
echo -e "${source_repo}\t${branch_name}\t${alias_name}\t${repo_name}\t\t\t건너뜀\t${note}" >> "${DECISIONS_FILE}"
|
|
|
|
decision_epoch=$(date +%s)
|
|
|
|
|
|
|
|
note="source branch 없음/조회 실패 (repo=${source_repo}, branch=${branch_name})"
|
|
|
|
|
|
|
|
append_decision_row "${note}"
|
|
|
|
heads_detail="src=none tgt=unknown"
|
|
|
|
heads_detail="src=none tgt=unknown"
|
|
|
|
notify_status "error" "${repo_name}" "${branch_name}" "사전 스캔 실패" "${start_epoch}" "${note}" "${heads_detail}"
|
|
|
|
notify_status "error" "${repo_name}" "${branch_name}" "사전 스캔 실패" "${start_epoch}" "${note}" "${heads_detail}"
|
|
|
|
|
|
|
|
append_timing_row "error" "${note}"
|
|
|
|
echo "Skipping ${branch_name} (${repo_name}) - ${note}"
|
|
|
|
echo "Skipping ${branch_name} (${repo_name}) - ${note}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
@@ -319,11 +364,14 @@ jobs:
|
|
|
|
elif [[ "${http_status}" == "404" ]]; then
|
|
|
|
elif [[ "${http_status}" == "404" ]]; then
|
|
|
|
repo_exists=false
|
|
|
|
repo_exists=false
|
|
|
|
else
|
|
|
|
else
|
|
|
|
|
|
|
|
decision="사전 조회 실패"
|
|
|
|
|
|
|
|
decision_epoch=$(date +%s)
|
|
|
|
note="repo 조회 실패 (HTTP ${http_status})"
|
|
|
|
note="repo 조회 실패 (HTTP ${http_status})"
|
|
|
|
echo "::warning::${note}"
|
|
|
|
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"
|
|
|
|
heads_detail="src=${source_commit} tgt=unknown"
|
|
|
|
notify_status "error" "${repo_name}" "${branch_name}" "사전 조회 실패" "${start_epoch}" "${note}" "${heads_detail}"
|
|
|
|
notify_status "error" "${repo_name}" "${branch_name}" "사전 조회 실패" "${start_epoch}" "${note}" "${heads_detail}"
|
|
|
|
|
|
|
|
append_decision_row "${note}"
|
|
|
|
|
|
|
|
append_timing_row "error" "${note}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
@@ -351,11 +399,13 @@ jobs:
|
|
|
|
note="커밋 상이"
|
|
|
|
note="커밋 상이"
|
|
|
|
fi
|
|
|
|
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
|
|
|
|
if [[ "${decision}" == "동일 커밋 - 건너뜀" ]]; then
|
|
|
|
notify_status "skip" "${repo_name}" "${branch_name}" "${decision}" "${start_epoch}" "" "${heads_detail}"
|
|
|
|
notify_status "skip" "${repo_name}" "${branch_name}" "${decision}" "${start_epoch}" "" "${heads_detail}"
|
|
|
|
echo "Pre-scan marked ${branch_name} (${repo_name}) as skip (same commit)."
|
|
|
|
echo "Pre-scan marked ${branch_name} (${repo_name}) as skip (same commit)."
|
|
|
|
|
|
|
|
append_timing_row "skip" "${note}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
@@ -378,6 +428,8 @@ jobs:
|
|
|
|
if [[ "${create_status}" != "201" ]]; then
|
|
|
|
if [[ "${create_status}" != "201" ]]; then
|
|
|
|
echo "::error::Failed to create repository. HTTP ${create_status}"
|
|
|
|
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}"
|
|
|
|
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
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
echo "Repository created successfully."
|
|
|
|
echo "Repository created successfully."
|
|
|
|
@@ -391,6 +443,7 @@ jobs:
|
|
|
|
|
|
|
|
|
|
|
|
notify_status "start" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "" "${heads_detail}"
|
|
|
|
notify_status "start" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "" "${heads_detail}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exec_start_epoch=$(date +%s)
|
|
|
|
shallow_exclude_args=()
|
|
|
|
shallow_exclude_args=()
|
|
|
|
if ${repo_exists} && [[ -n "${target_commit:-}" ]]; then
|
|
|
|
if ${repo_exists} && [[ -n "${target_commit:-}" ]]; then
|
|
|
|
shallow_exclude_args=(--shallow-exclude="${target_commit}")
|
|
|
|
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
|
|
|
|
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}"
|
|
|
|
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}"
|
|
|
|
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}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
@@ -415,6 +470,8 @@ jobs:
|
|
|
|
if ! git init --bare; then
|
|
|
|
if ! git init --bare; then
|
|
|
|
echo "::error::Failed to init bare repository"
|
|
|
|
echo "::error::Failed to init bare repository"
|
|
|
|
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "bare init 실패" "${heads_detail}"
|
|
|
|
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}"
|
|
|
|
cd "${ROOT_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
@@ -451,6 +508,8 @@ jobs:
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
echo "::error::Failed to fetch branch '${branch_name}' from source repo (fallback without shallow-exclude)"
|
|
|
|
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}"
|
|
|
|
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}"
|
|
|
|
cd "${ROOT_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
@@ -458,6 +517,8 @@ jobs:
|
|
|
|
else
|
|
|
|
else
|
|
|
|
echo "::error::Failed to fetch branch '${branch_name}' from source repo"
|
|
|
|
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}"
|
|
|
|
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}"
|
|
|
|
cd "${ROOT_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
@@ -469,6 +530,8 @@ jobs:
|
|
|
|
if ! git fetch --progress --prune --prune-tags --no-tags source "refs/tags/*:refs/tags/*"; then
|
|
|
|
if ! git fetch --progress --prune --prune-tags --no-tags source "refs/tags/*:refs/tags/*"; then
|
|
|
|
echo "::error::Failed to fetch tags from source repo"
|
|
|
|
echo "::error::Failed to fetch tags from source repo"
|
|
|
|
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "tag fetch 오류" "${heads_detail}"
|
|
|
|
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}"
|
|
|
|
cd "${ROOT_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
@@ -479,6 +542,8 @@ jobs:
|
|
|
|
if ! git push --progress --force origin "refs/heads/${branch_name}:refs/heads/main"; then
|
|
|
|
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"
|
|
|
|
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}"
|
|
|
|
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}"
|
|
|
|
cd "${ROOT_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
rm -rf "${CLONE_DIR}"
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
@@ -488,6 +553,8 @@ jobs:
|
|
|
|
if ! git push --force --prune origin "refs/tags/*:refs/tags/*"; then
|
|
|
|
if ! git push --force --prune origin "refs/tags/*:refs/tags/*"; then
|
|
|
|
echo "::warning::Failed to push tags to target repository"
|
|
|
|
echo "::warning::Failed to push tags to target repository"
|
|
|
|
notify_status "error" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "tag push 오류" "${heads_detail}"
|
|
|
|
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
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
@@ -497,6 +564,8 @@ jobs:
|
|
|
|
echo "Successfully mirrored ${branch_name} to center_dev/${repo_name}"
|
|
|
|
echo "Successfully mirrored ${branch_name} to center_dev/${repo_name}"
|
|
|
|
set_default_branch_main "${repo_name}"
|
|
|
|
set_default_branch_main "${repo_name}"
|
|
|
|
final_heads_detail="src=${source_commit} tgt=${source_commit}"
|
|
|
|
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}"
|
|
|
|
notify_status "success" "${repo_name}" "${branch_name}" "${backup_mode}" "${start_epoch}" "" "${final_heads_detail}"
|
|
|
|
echo "================================================="
|
|
|
|
echo "================================================="
|
|
|
|
echo ""
|
|
|
|
echo ""
|
|
|
|
@@ -506,9 +575,8 @@ jobs:
|
|
|
|
SUMMARY_TS=$(TZ=Asia/Seoul date '+%Y-%m-%d %H:%M:%S %Z')
|
|
|
|
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}"
|
|
|
|
SUMMARY_TEXT="브랜치 동기화 완료: 총 ${TOTAL_PROCESSED}개 (성공 ${TOTAL_SUCCESS}, 동일로 건너뜀 ${TOTAL_SKIP}, 오류 ${TOTAL_ERROR}) - ${SUMMARY_TS}"
|
|
|
|
echo "${SUMMARY_TEXT}"
|
|
|
|
echo "${SUMMARY_TEXT}"
|
|
|
|
echo "Source head 기록: ${SOURCE_HEADS_FILE}"
|
|
|
|
echo "보고서(마크다운)는 이후 스텝에서 생성됩니다."
|
|
|
|
echo "Target head 기록: ${TARGET_HEADS_FILE}"
|
|
|
|
echo "${TOTAL_ERROR}" > "${REPORT_DIR}/exit_code"
|
|
|
|
echo "판정 테이블: ${DECISIONS_FILE}"
|
|
|
|
|
|
|
|
if [[ -n "${NOTIFY_WEBHOOK}" ]]; then
|
|
|
|
if [[ -n "${NOTIFY_WEBHOOK}" ]]; then
|
|
|
|
SUMMARY_PAYLOAD=${SUMMARY_TEXT//\"/\\\"}
|
|
|
|
SUMMARY_PAYLOAD=${SUMMARY_TEXT//\"/\\\"}
|
|
|
|
curl -sS -i -X POST \
|
|
|
|
curl -sS -i -X POST \
|
|
|
|
@@ -521,3 +589,86 @@ jobs:
|
|
|
|
echo "::warning::One or more branches failed (${TOTAL_ERROR})."
|
|
|
|
echo "::warning::One or more branches failed (${TOTAL_ERROR})."
|
|
|
|
exit 1
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
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
|
|
|
|
|