feat: add phases 10-11, enhance phase 8 direct-check mode, and update Caddy migration
- Phase 10: local repo cutover (rename origin→github, add Gitea remote, push branches/tags) - Phase 11: custom runner infrastructure with toolchain-based naming (go-node-runner, jvm-android-runner) and repo variables via Gitea API - Add container_options support to manage_runner.sh for KVM passthrough - Phase 8: add --allow-direct-checks flag for LAN/split-DNS staging - Phase 7.5: add Cloudflare TLS block, retry logic for probes, multi-upstream support - Add toggle_dns.sh helper and update orchestration scripts for phases 10-11 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
223
lib/phase10_common.sh
Normal file
223
lib/phase10_common.sh
Normal file
@@ -0,0 +1,223 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# lib/phase10_common.sh — Shared helpers for phase 10 local repo cutover
|
||||
# =============================================================================
|
||||
|
||||
# Shared discovery results (parallel arrays; bash 3.2 compatible).
|
||||
PHASE10_REPO_NAMES=()
|
||||
PHASE10_REPO_PATHS=()
|
||||
PHASE10_GITHUB_URLS=()
|
||||
PHASE10_DUPLICATES=()
|
||||
|
||||
# Parse common git remote URL formats into: host|owner|repo
|
||||
# Supports:
|
||||
# - https://host/owner/repo(.git)
|
||||
# - ssh://git@host/owner/repo(.git)
|
||||
# - git@host:owner/repo(.git)
|
||||
phase10_parse_git_url() {
|
||||
local url="$1"
|
||||
local rest host path owner repo
|
||||
|
||||
if [[ "$url" =~ ^[a-zA-Z][a-zA-Z0-9+.-]*:// ]]; then
|
||||
rest="${url#*://}"
|
||||
# Drop optional userinfo component.
|
||||
rest="${rest#*@}"
|
||||
host="${rest%%/*}"
|
||||
path="${rest#*/}"
|
||||
elif [[ "$url" == *@*:* ]]; then
|
||||
rest="${url#*@}"
|
||||
host="${rest%%:*}"
|
||||
path="${rest#*:}"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
|
||||
path="${path#/}"
|
||||
path="${path%.git}"
|
||||
owner="${path%%/*}"
|
||||
repo="${path#*/}"
|
||||
repo="${repo%%/*}"
|
||||
|
||||
if [[ -z "$host" ]] || [[ -z "$owner" ]] || [[ -z "$repo" ]] || [[ "$owner" == "$path" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s|%s|%s\n' "$host" "$owner" "$repo"
|
||||
}
|
||||
|
||||
phase10_host_matches() {
|
||||
local host="$1" expected="$2"
|
||||
[[ "$host" == "$expected" ]] || [[ "$host" == "${expected}:"* ]]
|
||||
}
|
||||
|
||||
# Return 0 when URL matches github.com/<owner>/<repo>.
|
||||
# If <repo> is omitted, only owner is checked.
|
||||
phase10_url_is_github_repo() {
|
||||
local url="$1" owner_expected="$2" repo_expected="${3:-}"
|
||||
local parsed host owner repo
|
||||
|
||||
parsed=$(phase10_parse_git_url "$url" 2>/dev/null) || return 1
|
||||
IFS='|' read -r host owner repo <<< "$parsed"
|
||||
|
||||
phase10_host_matches "$host" "github.com" || return 1
|
||||
[[ "$owner" == "$owner_expected" ]] || return 1
|
||||
if [[ -n "$repo_expected" ]] && [[ "$repo" != "$repo_expected" ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
phase10_url_is_gitea_repo() {
|
||||
local url="$1" domain="$2" org="$3" repo_expected="$4"
|
||||
local parsed host owner repo
|
||||
|
||||
parsed=$(phase10_parse_git_url "$url" 2>/dev/null) || return 1
|
||||
IFS='|' read -r host owner repo <<< "$parsed"
|
||||
|
||||
phase10_host_matches "$host" "$domain" || return 1
|
||||
[[ "$owner" == "$org" ]] || return 1
|
||||
[[ "$repo" == "$repo_expected" ]] || return 1
|
||||
}
|
||||
|
||||
phase10_canonical_github_url() {
|
||||
local owner="$1" repo="$2"
|
||||
printf 'https://github.com/%s/%s.git' "$owner" "$repo"
|
||||
}
|
||||
|
||||
phase10_canonical_gitea_url() {
|
||||
local domain="$1" org="$2" repo="$3"
|
||||
printf 'https://%s/%s/%s.git' "$domain" "$org" "$repo"
|
||||
}
|
||||
|
||||
# Stable in-place sort by repo name (keeps arrays aligned).
|
||||
phase10_sort_repo_arrays() {
|
||||
local i j tmp
|
||||
for ((i = 0; i < ${#PHASE10_REPO_NAMES[@]}; i++)); do
|
||||
for ((j = i + 1; j < ${#PHASE10_REPO_NAMES[@]}; j++)); do
|
||||
if [[ "${PHASE10_REPO_NAMES[$i]}" > "${PHASE10_REPO_NAMES[$j]}" ]]; then
|
||||
tmp="${PHASE10_REPO_NAMES[$i]}"
|
||||
PHASE10_REPO_NAMES[i]="${PHASE10_REPO_NAMES[j]}"
|
||||
PHASE10_REPO_NAMES[j]="$tmp"
|
||||
|
||||
tmp="${PHASE10_REPO_PATHS[i]}"
|
||||
PHASE10_REPO_PATHS[i]="${PHASE10_REPO_PATHS[j]}"
|
||||
PHASE10_REPO_PATHS[j]="$tmp"
|
||||
|
||||
tmp="${PHASE10_GITHUB_URLS[i]}"
|
||||
PHASE10_GITHUB_URLS[i]="${PHASE10_GITHUB_URLS[j]}"
|
||||
PHASE10_GITHUB_URLS[j]="$tmp"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
# Discover local repos under root that map to github.com/<github_owner>.
|
||||
# Discovery rules:
|
||||
# - Only direct children of root are considered.
|
||||
# - Excludes exclude_path (typically this toolkit repo).
|
||||
# - Accepts a repo if either "github" or "origin" points at GitHub owner.
|
||||
# - Deduplicates by repo slug, preferring directory basename == slug.
|
||||
#
|
||||
# Args:
|
||||
# $1 root dir (e.g., /Users/s/development)
|
||||
# $2 github owner (from GITHUB_USERNAME)
|
||||
# $3 exclude absolute path (optional; pass "" for none)
|
||||
# $4 expected count (0 = don't enforce)
|
||||
phase10_discover_local_repos() {
|
||||
local root="$1"
|
||||
local github_owner="$2"
|
||||
local exclude_path="${3:-}"
|
||||
local expected_count="${4:-0}"
|
||||
|
||||
PHASE10_REPO_NAMES=()
|
||||
PHASE10_REPO_PATHS=()
|
||||
PHASE10_GITHUB_URLS=()
|
||||
PHASE10_DUPLICATES=()
|
||||
|
||||
if [[ ! -d "$root" ]]; then
|
||||
log_error "Local repo root not found: ${root}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local dir top github_url parsed host owner repo canonical
|
||||
local i idx existing existing_base new_base duplicate
|
||||
for dir in "$root"/*; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
if [[ -n "$exclude_path" ]] && [[ "$dir" == "$exclude_path" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! git -C "$dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
continue
|
||||
fi
|
||||
|
||||
top=$(git -C "$dir" rev-parse --show-toplevel 2>/dev/null || true)
|
||||
[[ "$top" == "$dir" ]] || continue
|
||||
|
||||
github_url=""
|
||||
if github_url=$(git -C "$dir" remote get-url github 2>/dev/null); then
|
||||
if ! phase10_url_is_github_repo "$github_url" "$github_owner"; then
|
||||
github_url=""
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$github_url" ]] && github_url=$(git -C "$dir" remote get-url origin 2>/dev/null); then
|
||||
if ! phase10_url_is_github_repo "$github_url" "$github_owner"; then
|
||||
github_url=""
|
||||
fi
|
||||
fi
|
||||
|
||||
[[ -n "$github_url" ]] || continue
|
||||
|
||||
parsed=$(phase10_parse_git_url "$github_url" 2>/dev/null) || continue
|
||||
IFS='|' read -r host owner repo <<< "$parsed"
|
||||
canonical=$(phase10_canonical_github_url "$owner" "$repo")
|
||||
|
||||
idx=-1
|
||||
for i in "${!PHASE10_REPO_NAMES[@]}"; do
|
||||
if [[ "${PHASE10_REPO_NAMES[$i]}" == "$repo" ]]; then
|
||||
idx="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$idx" -ge 0 ]]; then
|
||||
existing="${PHASE10_REPO_PATHS[$idx]}"
|
||||
existing_base="$(basename "$existing")"
|
||||
new_base="$(basename "$dir")"
|
||||
if [[ "$new_base" == "$repo" ]] && [[ "$existing_base" != "$repo" ]]; then
|
||||
PHASE10_REPO_PATHS[idx]="$dir"
|
||||
PHASE10_GITHUB_URLS[idx]="$canonical"
|
||||
PHASE10_DUPLICATES+=("${repo}: preferred ${dir} over ${existing}")
|
||||
else
|
||||
PHASE10_DUPLICATES+=("${repo}: ignored duplicate ${dir} (using ${existing})")
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
PHASE10_REPO_NAMES+=("$repo")
|
||||
PHASE10_REPO_PATHS+=("$dir")
|
||||
PHASE10_GITHUB_URLS+=("$canonical")
|
||||
done
|
||||
|
||||
phase10_sort_repo_arrays
|
||||
|
||||
for duplicate in "${PHASE10_DUPLICATES[@]}"; do
|
||||
log_info "$duplicate"
|
||||
done
|
||||
|
||||
if [[ "${#PHASE10_REPO_NAMES[@]}" -eq 0 ]]; then
|
||||
log_error "No local GitHub repos found under ${root} for owner '${github_owner}'"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$expected_count" -gt 0 ]] && [[ "${#PHASE10_REPO_NAMES[@]}" -ne "$expected_count" ]]; then
|
||||
log_error "Expected ${expected_count} local repos under ${root}; found ${#PHASE10_REPO_NAMES[@]}"
|
||||
for i in "${!PHASE10_REPO_NAMES[@]}"; do
|
||||
log_error " - ${PHASE10_REPO_NAMES[$i]} -> ${PHASE10_REPO_PATHS[$i]}"
|
||||
done
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user