upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/docs/archive/2026-01-relay-ngit-dev-migration/scripts/run-migration-analysis.sh
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-02-03 14:41:46 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-02-03 14:46:09 +0000
commit92a9a3bfe0bc522e8ae411991a366a3a6310d525 (patch)
tree9fc5045a9df0ef56cc8ad37afaef09fad37d95ed /docs/archive/2026-01-relay-ngit-dev-migration/scripts/run-migration-analysis.sh
parentf148b3a0e4b032c0acf835cda6d2935e19b9f67e (diff)
docs: archive relay.ngit.dev migration materials for reference
Move migration guide and scripts to docs/archive/2026-01-relay-ngit-dev-migration/ with clear warnings that these are reference-only materials from a specific migration context, not general-purpose tools. These materials document the relay.ngit.dev migration from ngit-relay to ngit-grasp in January 2026. The scripts were developed iteratively during the migration and are specific to that context. They are preserved for: - Historical reference - Context for production fixes in this branch - Inspiration for future migrations (not direct reuse) The migration uncovered critical bugs now fixed in this branch: - Git protocol error handling - Naughty list false positives - Purgatory event tracking - Sync startup issues - Configuration management
Diffstat (limited to 'docs/archive/2026-01-relay-ngit-dev-migration/scripts/run-migration-analysis.sh')
-rwxr-xr-xdocs/archive/2026-01-relay-ngit-dev-migration/scripts/run-migration-analysis.sh779
1 files changed, 779 insertions, 0 deletions
diff --git a/docs/archive/2026-01-relay-ngit-dev-migration/scripts/run-migration-analysis.sh b/docs/archive/2026-01-relay-ngit-dev-migration/scripts/run-migration-analysis.sh
new file mode 100755
index 0000000..acc5e44
--- /dev/null
+++ b/docs/archive/2026-01-relay-ngit-dev-migration/scripts/run-migration-analysis.sh
@@ -0,0 +1,779 @@
1#!/usr/bin/env bash
2#
3# run-migration-analysis.sh - Orchestrate the complete GRASP relay to ngit-grasp migration analysis
4#
5# This script runs all 5 phases of the migration analysis pipeline in sequence,
6# with proper error handling, progress reporting, and timing information.
7#
8# QUICK START:
9# # Basic usage (local analysis only - Phases 1, 3, 5)
10# ./run-migration-analysis.sh --prod-relay wss://relay.ngit.dev --archive-relay wss://archive.relay.ngit.dev
11#
12# # Full analysis including git sync check (requires VPS access)
13# ./run-migration-analysis.sh \
14# --prod-relay wss://relay.ngit.dev \
15# --archive-relay wss://archive.relay.ngit.dev \
16# --prod-git /var/lib/grasp-relay/git \
17# --archive-git /var/lib/ngit-grasp/git
18#
19# USAGE:
20# ./run-migration-analysis.sh [options]
21#
22# REQUIRED OPTIONS:
23# --prod-relay <url> Production relay WebSocket URL (e.g., wss://relay.ngit.dev)
24# --archive-relay <url> Archive relay WebSocket URL (e.g., wss://archive.relay.ngit.dev)
25#
26# OPTIONAL OPTIONS:
27# --prod-git <path> Git base directory for prod (enables Phase 2)
28# --archive-git <path> Git base directory for archive (enables Phase 2)
29# --service <name> Systemd service name for log extraction (enables Phase 4)
30# --output <dir> Output directory (default: work/migration-analysis-YYYYMMDD-HHMM)
31# --since <date> Start date for log extraction (default: 30 days ago)
32# --until <date> End date for log extraction (default: now)
33#
34# PHASE CONTROL:
35# --skip-phase-1 Skip event fetching (use existing data)
36# --skip-phase-2 Skip git sync check (use existing data)
37# --skip-phase-3 Skip categorization (use existing data)
38# --skip-phase-4 Skip log extraction (use existing data)
39# --skip-phase-5 Skip final classification
40# --only-phase-N Run only phase N (1-5)
41# --from-phase-N Start from phase N (skip earlier phases)
42#
43# OTHER OPTIONS:
44# --dry-run Show what would be executed without running
45# --continue-on-error Continue to next phase even if current phase fails
46# --help Show this help message
47#
48# PHASES:
49# Phase 1: Fetch events from both relays (~30s each, local)
50# Phase 2: Check git sync status (~20 min each, requires VPS)
51# Phase 3: Categorize and compare results (fast, local)
52# Phase 4: Extract logs from systemd (requires VPS)
53# Phase 5: Final classification (fast, local)
54#
55# EXAMPLES:
56# # Dry run to see what would happen
57# ./run-migration-analysis.sh --prod-relay wss://relay.ngit.dev --archive-relay wss://archive.relay.ngit.dev --dry-run
58#
59# # Run only Phase 1 (fetch events)
60# ./run-migration-analysis.sh --prod-relay wss://relay.ngit.dev --archive-relay wss://archive.relay.ngit.dev --only-phase-1
61#
62# # Resume from Phase 3 using existing Phase 1-2 data
63# ./run-migration-analysis.sh --prod-relay wss://relay.ngit.dev --archive-relay wss://archive.relay.ngit.dev --from-phase-3 --output work/migration-analysis-20260122-1430
64#
65# # Full analysis on VPS with all features
66# ./run-migration-analysis.sh \
67# --prod-relay wss://relay.ngit.dev \
68# --archive-relay wss://archive.relay.ngit.dev \
69# --prod-git /var/lib/grasp-relay/git \
70# --archive-git /var/lib/ngit-grasp/git \
71# --service ngit-grasp.service
72#
73# SEE ALSO:
74# docs/how-to/migrate-to-ngit-grasp.md - Full migration guide
75#
76
77set -euo pipefail
78
79# Get script directory for finding other scripts
80SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
81
82# Colors for output (disabled if not a terminal)
83if [[ -t 1 ]]; then
84 RED='\033[0;31m'
85 GREEN='\033[0;32m'
86 YELLOW='\033[0;33m'
87 BLUE='\033[0;34m'
88 CYAN='\033[0;36m'
89 BOLD='\033[1m'
90 NC='\033[0m'
91else
92 RED=''
93 GREEN=''
94 YELLOW=''
95 BLUE=''
96 CYAN=''
97 BOLD=''
98 NC=''
99fi
100
101# Logging functions
102log_header() {
103 echo ""
104 echo -e "${BOLD}${CYAN}════════════════════════════════════════════════════════════════${NC}"
105 echo -e "${BOLD}${CYAN} $*${NC}"
106 echo -e "${BOLD}${CYAN}════════════════════════════════════════════════════════════════${NC}"
107 echo ""
108}
109
110log_phase() {
111 echo ""
112 echo -e "${BOLD}${BLUE}┌──────────────────────────────────────────────────────────────┐${NC}"
113 echo -e "${BOLD}${BLUE}│ $*${NC}"
114 echo -e "${BOLD}${BLUE}└──────────────────────────────────────────────────────────────┘${NC}"
115}
116
117log_info() {
118 echo -e "${BLUE}[INFO]${NC} $*" >&2
119}
120
121log_success() {
122 echo -e "${GREEN}[OK]${NC} $*" >&2
123}
124
125log_warn() {
126 echo -e "${YELLOW}[WARN]${NC} $*" >&2
127}
128
129log_error() {
130 echo -e "${RED}[ERROR]${NC} $*" >&2
131}
132
133log_step() {
134 echo -e "${CYAN} →${NC} $*" >&2
135}
136
137# Default values
138PROD_RELAY=""
139ARCHIVE_RELAY=""
140PROD_GIT=""
141ARCHIVE_GIT=""
142SERVICE_NAME=""
143OUTPUT_DIR=""
144DRY_RUN=false
145CONTINUE_ON_ERROR=false
146LOG_SINCE=""
147LOG_UNTIL=""
148
149# Phase control
150SKIP_PHASE_1=false
151SKIP_PHASE_2=false
152SKIP_PHASE_3=false
153SKIP_PHASE_4=false
154SKIP_PHASE_5=false
155ONLY_PHASE=""
156FROM_PHASE=""
157
158# Timing
159declare -A PHASE_TIMES
160
161usage() {
162 head -73 "$0" | tail -n +3 | sed 's/^# //' | sed 's/^#//'
163 exit 0
164}
165
166# Parse command line arguments
167parse_args() {
168 while [[ $# -gt 0 ]]; do
169 case "$1" in
170 --prod-relay)
171 PROD_RELAY="$2"
172 shift 2
173 ;;
174 --archive-relay)
175 ARCHIVE_RELAY="$2"
176 shift 2
177 ;;
178 --prod-git)
179 PROD_GIT="$2"
180 shift 2
181 ;;
182 --archive-git)
183 ARCHIVE_GIT="$2"
184 shift 2
185 ;;
186 --service)
187 SERVICE_NAME="$2"
188 shift 2
189 ;;
190 --output)
191 OUTPUT_DIR="$2"
192 shift 2
193 ;;
194 --skip-phase-1)
195 SKIP_PHASE_1=true
196 shift
197 ;;
198 --skip-phase-2)
199 SKIP_PHASE_2=true
200 shift
201 ;;
202 --skip-phase-3)
203 SKIP_PHASE_3=true
204 shift
205 ;;
206 --skip-phase-4)
207 SKIP_PHASE_4=true
208 shift
209 ;;
210 --skip-phase-5)
211 SKIP_PHASE_5=true
212 shift
213 ;;
214 --only-phase-1|--only-phase-2|--only-phase-3|--only-phase-4|--only-phase-5)
215 ONLY_PHASE="${1#--only-phase-}"
216 shift
217 ;;
218 --from-phase-1|--from-phase-2|--from-phase-3|--from-phase-4|--from-phase-5)
219 FROM_PHASE="${1#--from-phase-}"
220 shift
221 ;;
222 --dry-run)
223 DRY_RUN=true
224 shift
225 ;;
226 --continue-on-error)
227 CONTINUE_ON_ERROR=true
228 shift
229 ;;
230 --since)
231 LOG_SINCE="$2"
232 shift 2
233 ;;
234 --until)
235 LOG_UNTIL="$2"
236 shift 2
237 ;;
238 --help|-h)
239 usage
240 ;;
241 *)
242 log_error "Unknown option: $1"
243 echo "Use --help for usage information."
244 exit 1
245 ;;
246 esac
247 done
248}
249
250# Validate required arguments
251validate_args() {
252 local errors=0
253
254 if [[ -z "$PROD_RELAY" ]]; then
255 log_error "Missing required option: --prod-relay"
256 errors=1
257 fi
258
259 if [[ -z "$ARCHIVE_RELAY" ]]; then
260 log_error "Missing required option: --archive-relay"
261 errors=1
262 fi
263
264 # Validate relay URLs
265 if [[ -n "$PROD_RELAY" && ! "$PROD_RELAY" =~ ^wss?:// ]]; then
266 log_error "Invalid prod relay URL: $PROD_RELAY (must start with ws:// or wss://)"
267 errors=1
268 fi
269
270 if [[ -n "$ARCHIVE_RELAY" && ! "$ARCHIVE_RELAY" =~ ^wss?:// ]]; then
271 log_error "Invalid archive relay URL: $ARCHIVE_RELAY (must start with ws:// or wss://)"
272 errors=1
273 fi
274
275 # Validate git paths if provided
276 if [[ -n "$PROD_GIT" && ! -d "$PROD_GIT" ]]; then
277 log_warn "Prod git directory not found: $PROD_GIT"
278 log_warn "Phase 2 will fail unless running on VPS with access to this path."
279 fi
280
281 if [[ -n "$ARCHIVE_GIT" && ! -d "$ARCHIVE_GIT" ]]; then
282 log_warn "Archive git directory not found: $ARCHIVE_GIT"
283 log_warn "Phase 2 will fail unless running on VPS with access to this path."
284 fi
285
286 if [[ $errors -eq 1 ]]; then
287 echo ""
288 echo "Use --help for usage information."
289 exit 1
290 fi
291}
292
293# Check prerequisites
294check_prerequisites() {
295 local missing=0
296
297 log_info "Checking prerequisites..."
298
299 # Required tools
300 for tool in git nak jq awk sort; do
301 if command -v "$tool" &> /dev/null; then
302 log_step "$tool: found"
303 else
304 log_error "$tool: NOT FOUND"
305 missing=1
306 fi
307 done
308
309 # Optional tools
310 if command -v journalctl &> /dev/null; then
311 log_step "journalctl: found (Phase 4 available)"
312 else
313 log_step "journalctl: not found (Phase 4 will be skipped)"
314 SKIP_PHASE_4=true
315 fi
316
317 if [[ $missing -eq 1 ]]; then
318 log_error "Missing required tools. Install them and try again."
319 exit 1
320 fi
321
322 # Check scripts exist
323 for script in 01-fetch-events.sh 10-check-git-sync.sh 20-categorize.sh 21-compare-relays.sh 22-compare-git-data.sh 30-extract-parse-failures.sh 31-extract-purgatory-expiry.sh 40-classify-actions.sh; do
324 if [[ ! -x "$SCRIPT_DIR/$script" ]]; then
325 log_error "Script not found or not executable: $SCRIPT_DIR/$script"
326 missing=1
327 fi
328 done
329
330 if [[ $missing -eq 1 ]]; then
331 exit 1
332 fi
333
334 log_success "All prerequisites satisfied"
335}
336
337# Determine which phases to run
338determine_phases() {
339 # Handle --only-phase-N
340 if [[ -n "$ONLY_PHASE" ]]; then
341 for i in 1 2 3 4 5; do
342 if [[ "$i" != "$ONLY_PHASE" ]]; then
343 eval "SKIP_PHASE_$i=true"
344 fi
345 done
346 fi
347
348 # Handle --from-phase-N
349 if [[ -n "$FROM_PHASE" ]]; then
350 for i in 1 2 3 4 5; do
351 if [[ "$i" -lt "$FROM_PHASE" ]]; then
352 eval "SKIP_PHASE_$i=true"
353 fi
354 done
355 fi
356
357 # Auto-skip Phase 2 if git paths not provided
358 if [[ -z "$PROD_GIT" && -z "$ARCHIVE_GIT" ]]; then
359 if [[ "$SKIP_PHASE_2" != "true" ]]; then
360 log_warn "No git paths provided. Phase 2 (git sync check) will be skipped."
361 log_warn "Use --prod-git and --archive-git to enable Phase 2."
362 SKIP_PHASE_2=true
363 fi
364 fi
365
366 # Auto-skip Phase 4 if service not provided
367 if [[ -z "$SERVICE_NAME" ]]; then
368 if [[ "$SKIP_PHASE_4" != "true" ]]; then
369 log_warn "No service name provided. Phase 4 (log extraction) will be skipped."
370 log_warn "Use --service to enable Phase 4."
371 SKIP_PHASE_4=true
372 fi
373 fi
374}
375
376# Setup output directory
377setup_output_dir() {
378 if [[ -z "$OUTPUT_DIR" ]]; then
379 OUTPUT_DIR="work/migration-analysis-$(date +%Y%m%d-%H%M)"
380 fi
381
382 log_info "Output directory: $OUTPUT_DIR"
383
384 if [[ "$DRY_RUN" == "true" ]]; then
385 log_info "[DRY RUN] Would create directory structure"
386 return
387 fi
388
389 mkdir -p "$OUTPUT_DIR"/{prod/raw,archive/raw,comparison,logs,results}
390
391 # Save configuration
392 cat > "$OUTPUT_DIR/config.txt" << EOF
393# Migration Analysis Configuration
394# Generated: $(date -Iseconds)
395
396PROD_RELAY=$PROD_RELAY
397ARCHIVE_RELAY=$ARCHIVE_RELAY
398PROD_GIT=$PROD_GIT
399ARCHIVE_GIT=$ARCHIVE_GIT
400SERVICE_NAME=$SERVICE_NAME
401OUTPUT_DIR=$OUTPUT_DIR
402EOF
403
404 log_success "Created output directory structure"
405}
406
407# Run a phase with timing and error handling
408run_phase() {
409 local phase_num="$1"
410 local phase_name="$2"
411 shift 2
412 local cmd=("$@")
413
414 local skip_var="SKIP_PHASE_$phase_num"
415 if [[ "${!skip_var}" == "true" ]]; then
416 log_phase "Phase $phase_num: $phase_name [SKIPPED]"
417 return 0
418 fi
419
420 log_phase "Phase $phase_num: $phase_name"
421
422 if [[ "$DRY_RUN" == "true" ]]; then
423 log_info "[DRY RUN] Would execute:"
424 for c in "${cmd[@]}"; do
425 echo " $c"
426 done
427 return 0
428 fi
429
430 local start_time
431 start_time=$(date +%s)
432
433 local exit_code=0
434
435 # Execute the command(s)
436 for c in "${cmd[@]}"; do
437 log_step "Running: $c"
438 if ! eval "$c"; then
439 exit_code=1
440 if [[ "$CONTINUE_ON_ERROR" == "true" ]]; then
441 log_warn "Command failed, continuing due to --continue-on-error"
442 else
443 log_error "Command failed"
444 break
445 fi
446 fi
447 done
448
449 local end_time
450 end_time=$(date +%s)
451 local duration=$((end_time - start_time))
452 PHASE_TIMES[$phase_num]=$duration
453
454 if [[ $exit_code -eq 0 ]]; then
455 log_success "Phase $phase_num completed in ${duration}s"
456 else
457 log_error "Phase $phase_num failed after ${duration}s"
458 if [[ "$CONTINUE_ON_ERROR" != "true" ]]; then
459 return 1
460 fi
461 fi
462
463 return $exit_code
464}
465
466# Phase 1: Fetch events
467run_phase_1() {
468 local cmds=()
469
470 # Fetch from prod relay
471 cmds+=("'$SCRIPT_DIR/01-fetch-events.sh' '$PROD_RELAY' '$OUTPUT_DIR/prod'")
472
473 # Fetch from archive relay
474 cmds+=("'$SCRIPT_DIR/01-fetch-events.sh' '$ARCHIVE_RELAY' '$OUTPUT_DIR/archive'")
475
476 run_phase 1 "Fetch Events (~30s each)" "${cmds[@]}"
477}
478
479# Phase 2: Git sync check
480run_phase_2() {
481 local cmds=()
482
483 if [[ -n "$PROD_GIT" ]]; then
484 cmds+=("'$SCRIPT_DIR/10-check-git-sync.sh' '$OUTPUT_DIR/prod/raw/state-events.json' '$PROD_GIT' '$OUTPUT_DIR/prod' --categorize")
485 else
486 log_warn "Skipping prod git sync check (no --prod-git provided)"
487 fi
488
489 if [[ -n "$ARCHIVE_GIT" ]]; then
490 cmds+=("'$SCRIPT_DIR/10-check-git-sync.sh' '$OUTPUT_DIR/archive/raw/state-events.json' '$ARCHIVE_GIT' '$OUTPUT_DIR/archive' --categorize")
491 else
492 log_warn "Skipping archive git sync check (no --archive-git provided)"
493 fi
494
495 if [[ ${#cmds[@]} -eq 0 ]]; then
496 log_warn "No git paths provided, skipping Phase 2"
497 return 0
498 fi
499
500 run_phase 2 "Git Sync Check (~20 min each)" "${cmds[@]}"
501}
502
503# Phase 3: Categorize and compare
504run_phase_3() {
505 local cmds=()
506
507 # Check if we have git-sync-status.tsv files (from Phase 2)
508 # If not, we can't run categorization
509 local has_prod_sync=false
510 local has_archive_sync=false
511
512 if [[ -f "$OUTPUT_DIR/prod/git-sync-status.tsv" ]]; then
513 has_prod_sync=true
514 fi
515
516 if [[ -f "$OUTPUT_DIR/archive/git-sync-status.tsv" ]]; then
517 has_archive_sync=true
518 fi
519
520 # Run categorization if we have sync data but no category files
521 if [[ "$has_prod_sync" == "true" && ! -f "$OUTPUT_DIR/prod/category1-complete-match.txt" ]]; then
522 cmds+=("'$SCRIPT_DIR/20-categorize.sh' '$OUTPUT_DIR/prod/git-sync-status.tsv' '$OUTPUT_DIR/prod'")
523 fi
524
525 if [[ "$has_archive_sync" == "true" && ! -f "$OUTPUT_DIR/archive/category1-complete-match.txt" ]]; then
526 cmds+=("'$SCRIPT_DIR/20-categorize.sh' '$OUTPUT_DIR/archive/git-sync-status.tsv' '$OUTPUT_DIR/archive'")
527 fi
528
529 # Run comparison if we have category files
530 if [[ -f "$OUTPUT_DIR/prod/category1-complete-match.txt" && -f "$OUTPUT_DIR/archive/category1-complete-match.txt" ]]; then
531 cmds+=("'$SCRIPT_DIR/21-compare-relays.sh' '$OUTPUT_DIR/prod' '$OUTPUT_DIR/archive' '$OUTPUT_DIR/comparison'")
532 else
533 log_warn "Missing category files for comparison."
534 log_warn "Phase 2 must complete successfully before Phase 3 can compare relays."
535
536 # Create placeholder comparison files if they don't exist
537 if [[ "$DRY_RUN" != "true" ]]; then
538 mkdir -p "$OUTPUT_DIR/comparison"
539 for f in complete-in-both.txt complete-prod-missing-archive.txt complete-prod-incomplete-archive.txt incomplete-in-both.txt in-archive-not-prod.txt; do
540 if [[ ! -f "$OUTPUT_DIR/comparison/$f" ]]; then
541 echo "# Placeholder - Phase 2 data not available" > "$OUTPUT_DIR/comparison/$f"
542 fi
543 done
544 echo "# Comparison not available - Phase 2 data missing" > "$OUTPUT_DIR/comparison/summary.txt"
545 fi
546 fi
547
548 if [[ ${#cmds[@]} -eq 0 ]]; then
549 log_warn "No categorization or comparison needed (already done or missing input)"
550 return 0
551 fi
552
553 run_phase 3 "Categorize & Compare (fast)" "${cmds[@]}"
554
555 # Phase 3c: Compare git data between relays (requires git paths)
556 # This determines if archive is ahead of prod for repos with mismatched state
557 if [[ -n "$PROD_GIT" && -n "$ARCHIVE_GIT" ]]; then
558 # Build list of repos to compare: those where prod=complete but archive is not
559 local repos_to_compare="$OUTPUT_DIR/comparison/complete-prod-incomplete-archive.txt"
560 if [[ -f "$repos_to_compare" ]] && [[ ! -f "$OUTPUT_DIR/comparison/git-ancestry.tsv" ]]; then
561 log_info "Running git ancestry comparison (Phase 3c)..."
562 run_phase 3 "Git Ancestry Comparison" "'$SCRIPT_DIR/22-compare-git-data.sh' '$PROD_GIT' '$ARCHIVE_GIT' '$repos_to_compare' '$OUTPUT_DIR/comparison'"
563 fi
564 else
565 log_warn "Git paths not provided - skipping git ancestry comparison"
566 log_warn "Without git comparison, repos where archive is ahead will be incorrectly flagged as needing re-sync"
567 fi
568}
569
570# Phase 4: Extract logs
571run_phase_4() {
572 if [[ -z "$SERVICE_NAME" ]]; then
573 log_warn "No service name provided, skipping Phase 4"
574 return 0
575 fi
576
577 # Validate service name before running Phase 4
578 # Structured logging only exists in ngit-grasp, not ngit-relay
579 if [[ "$SERVICE_NAME" == *"ngit-relay"* ]]; then
580 log_error "SERVICE_NAME appears to be ngit-relay: $SERVICE_NAME"
581 log_error ""
582 log_error "Phase 4 requires an ngit-grasp service with structured logging."
583 log_error "Structured logging ([PARSE_FAIL], [PURGATORY_EXPIRED]) only exists"
584 log_error "in ngit-grasp services, NOT in ngit-relay services."
585 log_error ""
586 log_error "Please update --service to use the ngit-grasp archive service."
587 log_error ""
588 log_error "To find the correct service name:"
589 log_error " systemctl list-units 'ngit-grasp*' --all"
590 log_error ""
591 log_error "Common ngit-grasp service names:"
592 log_error " - ngit-grasp.service"
593 log_error " - ngit-grasp-relay-ngit-dev.service (NixOS multi-instance)"
594 log_error " - ngit-grasp-archive.service"
595 return 1
596 fi
597
598 # Warn if service name doesn't look like ngit-grasp
599 if [[ "$SERVICE_NAME" != *"ngit-grasp"* && "$SERVICE_NAME" != *"grasp"* ]]; then
600 log_warn "SERVICE_NAME doesn't contain 'ngit-grasp': $SERVICE_NAME"
601 log_warn "Structured logging only exists in ngit-grasp services."
602 log_warn "If this is not an ngit-grasp service, Phase 4 will find no logs."
603 fi
604
605 local cmds=()
606
607 # Build log extraction options
608 local log_opts=""
609 if [[ -n "$LOG_SINCE" ]]; then
610 log_opts="$log_opts --since '$LOG_SINCE'"
611 fi
612 if [[ -n "$LOG_UNTIL" ]]; then
613 log_opts="$log_opts --until '$LOG_UNTIL'"
614 fi
615
616 cmds+=("'$SCRIPT_DIR/30-extract-parse-failures.sh' '$SERVICE_NAME' '$OUTPUT_DIR/logs' $log_opts")
617 cmds+=("'$SCRIPT_DIR/31-extract-purgatory-expiry.sh' '$SERVICE_NAME' '$OUTPUT_DIR/logs' $log_opts")
618
619 run_phase 4 "Extract Logs (VPS required)" "${cmds[@]}"
620}
621
622# Phase 5: Final classification
623run_phase_5() {
624 # Check if we have the minimum required files
625 local can_run=true
626
627 if [[ ! -d "$OUTPUT_DIR/prod" ]]; then
628 log_warn "Missing prod directory"
629 can_run=false
630 fi
631
632 if [[ ! -d "$OUTPUT_DIR/archive" ]]; then
633 log_warn "Missing archive directory"
634 can_run=false
635 fi
636
637 if [[ ! -d "$OUTPUT_DIR/comparison" ]]; then
638 log_warn "Missing comparison directory"
639 can_run=false
640 fi
641
642 # Create logs directory with empty files if missing
643 if [[ "$DRY_RUN" != "true" ]]; then
644 mkdir -p "$OUTPUT_DIR/logs"
645 for f in parse-failures.txt purgatory-expired.txt; do
646 if [[ ! -f "$OUTPUT_DIR/logs/$f" ]]; then
647 echo "# No data - Phase 4 not run" > "$OUTPUT_DIR/logs/$f"
648 fi
649 done
650 fi
651
652 if [[ "$can_run" == "false" ]]; then
653 log_error "Cannot run Phase 5 - missing required input directories"
654 return 1
655 fi
656
657 run_phase 5 "Final Classification (fast)" "'$SCRIPT_DIR/40-classify-actions.sh' '$OUTPUT_DIR'"
658}
659
660# Display summary
661display_summary() {
662 log_header "Migration Analysis Complete"
663
664 echo "Output Directory: $OUTPUT_DIR"
665 echo ""
666
667 # Phase timing summary
668 echo "Phase Timing:"
669 local total_time=0
670 for phase in 1 2 3 4 5; do
671 local skip_var="SKIP_PHASE_$phase"
672 if [[ "${!skip_var}" == "true" ]]; then
673 echo " Phase $phase: SKIPPED"
674 elif [[ -n "${PHASE_TIMES[$phase]:-}" ]]; then
675 local t="${PHASE_TIMES[$phase]}"
676 echo " Phase $phase: ${t}s"
677 total_time=$((total_time + t))
678 else
679 echo " Phase $phase: N/A"
680 fi
681 done
682 echo " ─────────────"
683 echo " Total: ${total_time}s"
684 echo ""
685
686 # Results summary
687 if [[ -f "$OUTPUT_DIR/results/summary.txt" ]]; then
688 echo "Results Summary:"
689 echo ""
690 # Extract key metrics from summary
691 if grep -q "No Action Required" "$OUTPUT_DIR/results/summary.txt"; then
692 grep -A1 "No Action Required" "$OUTPUT_DIR/results/summary.txt" | head -2
693 fi
694 if grep -q "Action Required" "$OUTPUT_DIR/results/summary.txt"; then
695 grep -A1 "Action Required" "$OUTPUT_DIR/results/summary.txt" | head -2
696 fi
697 if grep -q "Manual Investigation" "$OUTPUT_DIR/results/summary.txt"; then
698 grep -A1 "Manual Investigation" "$OUTPUT_DIR/results/summary.txt" | head -2
699 fi
700 echo ""
701 fi
702
703 # Output files
704 echo "Output Files:"
705 echo " $OUTPUT_DIR/results/no-action-required.txt"
706 echo " $OUTPUT_DIR/results/action-required.txt"
707 echo " $OUTPUT_DIR/results/manual-investigation.txt"
708 echo " $OUTPUT_DIR/results/summary.txt"
709 echo ""
710
711 # Next steps
712 echo "Next Steps:"
713 echo " 1. Review results/summary.txt for overview"
714 echo " 2. Address items in results/action-required.txt"
715 echo " 3. Investigate items in results/manual-investigation.txt"
716 echo " 4. Plan migration window when action items are resolved"
717 echo ""
718}
719
720# Main
721main() {
722 parse_args "$@"
723
724 log_header "GRASP Relay to ngit-grasp Migration Analysis"
725
726 validate_args
727 check_prerequisites
728 determine_phases
729 setup_output_dir
730
731 # Show configuration
732 log_info "Configuration:"
733 log_step "Prod relay: $PROD_RELAY"
734 log_step "Archive relay: $ARCHIVE_RELAY"
735 [[ -n "$PROD_GIT" ]] && log_step "Prod git: $PROD_GIT"
736 [[ -n "$ARCHIVE_GIT" ]] && log_step "Archive git: $ARCHIVE_GIT"
737 [[ -n "$SERVICE_NAME" ]] && log_step "Service: $SERVICE_NAME"
738 log_step "Output: $OUTPUT_DIR"
739 echo ""
740
741 # Show phase plan
742 log_info "Phase Plan:"
743 for phase in 1 2 3 4 5; do
744 local skip_var="SKIP_PHASE_$phase"
745 if [[ "${!skip_var}" == "true" ]]; then
746 log_step "Phase $phase: SKIP"
747 else
748 log_step "Phase $phase: RUN"
749 fi
750 done
751 echo ""
752
753 if [[ "$DRY_RUN" == "true" ]]; then
754 log_warn "DRY RUN MODE - No changes will be made"
755 echo ""
756 fi
757
758 # Run phases
759 local overall_exit=0
760
761 run_phase_1 || overall_exit=1
762 run_phase_2 || overall_exit=1
763 run_phase_3 || overall_exit=1
764 run_phase_4 || overall_exit=1
765 run_phase_5 || overall_exit=1
766
767 # Display summary
768 if [[ "$DRY_RUN" != "true" ]]; then
769 display_summary
770 fi
771
772 if [[ $overall_exit -ne 0 ]]; then
773 log_warn "Some phases failed. Review output for details."
774 fi
775
776 exit $overall_exit
777}
778
779main "$@"