Files
claudetools/projects/dataforth-dos/sync-fixes/deploy-sync-fixes.ps1
Mike Swanson 470638ff86 sync: Dataforth sync fixes, TestDataDB stability, and client scripts
Dataforth DOS:
- TestDataDB: singleton DB connection fix (crash prevention), WAL mode,
  WinSW service config, backup script, uncaught exception handlers
- Sync-FromNAS.ps1: Get-NASFileList temp file approach to avoid SSH
  stdout deadlock, *> $null output suppression, 8.3 filename filter
  for PUSH phase, backslash-escaped SCP paths, rename-to-.synced
- import.js: INSERT OR REPLACE for re-tested devices
- Full import run: 1,028,275 -> 1,632,793 records, indexes added
- Deploy script for sync fixes to AD2

Client scripts (temp/):
- BG Builders: Lesley account check, MFA phone update
- Lonestar Electrical: Kyla/Russ Google Workspace setup, 2FA bypass
- AD2 diagnostics and NAS connectivity tests

PENDING: Investigate why newest test_date is Jan 19 despite daily tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 06:08:31 -07:00

303 lines
12 KiB
PowerShell

# deploy-sync-fixes.ps1
# Deploys patched Sync-FromNAS.ps1 and import.js to AD2 (192.168.0.6)
#
# Fixes deployed:
# 1. Sync-FromNAS.ps1 - PULL hang fix (temp file approach for large find output)
# 2. Sync-FromNAS.ps1 - SCP quoting fix (handles spaces, parens, special chars)
# 3. Sync-FromNAS.ps1 - Rename-OnNAS replaces Remove-FromNAS (.synced instead of delete)
# 4. import.js - INSERT OR REPLACE instead of INSERT OR IGNORE (re-tests keep latest)
#
# Run from: Mike's workstation (where the patched files live)
# Target: AD2 (192.168.0.6) as INTRANET\sysadmin
param(
[switch]$DryRun # Show what would be done without doing it
)
# ============================================================================
# Configuration
# ============================================================================
$AD2_IP = "192.168.0.6"
$AD2_USER = "INTRANET\sysadmin"
$AD2_PASSWORD = "Paper123!@#"
$SSH = "C:\Windows\System32\OpenSSH\ssh.exe"
$SCP = "C:\Windows\System32\OpenSSH\scp.exe"
# Source files (local to this machine)
$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
$LOCAL_SYNC_SCRIPT = Join-Path $SCRIPT_DIR "Sync-FromNAS.ps1"
$LOCAL_IMPORT_SCRIPT = Join-Path $SCRIPT_DIR "import.js"
# Destination paths on AD2
$REMOTE_SYNC_PATH = "C:\Shares\test\scripts\Sync-FromNAS.ps1"
$REMOTE_IMPORT_PATH = "C:\Shares\testdatadb\database\import.js"
$REMOTE_SSH_DIR = "C:\Shares\test\scripts\.ssh"
$DATE_SUFFIX = Get-Date -Format "yyyyMMdd"
# ============================================================================
# Functions
# ============================================================================
function Write-Status {
param(
[string]$Marker,
[string]$Message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Host "$timestamp [$Marker] $Message"
}
function Invoke-AD2Command {
param([string]$Command)
$result = & $SSH -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 "${AD2_USER}@${AD2_IP}" $Command 2>&1
return $result
}
function Copy-ToAD2 {
param(
[string]$LocalPath,
[string]$RemotePath
)
# SCP to AD2 - quote remote path for Windows paths with backslashes
$result = & $SCP -O -o StrictHostKeyChecking=accept-new "$LocalPath" "${AD2_USER}@${AD2_IP}:`"${RemotePath}`"" 2>&1
if ($LASTEXITCODE -ne 0) {
$errorMsg = $result | Out-String
Write-Status "ERROR" "SCP failed (exit $LASTEXITCODE): $errorMsg"
return $false
}
return $true
}
# ============================================================================
# Pre-flight Checks
# ============================================================================
Write-Host ""
Write-Host "============================================"
Write-Host " Dataforth Sync Fixes - Deployment Script"
Write-Host "============================================"
Write-Host ""
if ($DryRun) {
Write-Status "INFO" "DRY RUN - no changes will be made"
Write-Host ""
}
# Verify source files exist
Write-Status "INFO" "Checking source files..."
if (-not (Test-Path $LOCAL_SYNC_SCRIPT)) {
Write-Status "ERROR" "Source file not found: $LOCAL_SYNC_SCRIPT"
exit 1
}
Write-Status "OK" "Found: $LOCAL_SYNC_SCRIPT"
if (-not (Test-Path $LOCAL_IMPORT_SCRIPT)) {
Write-Status "ERROR" "Source file not found: $LOCAL_IMPORT_SCRIPT"
exit 1
}
Write-Status "OK" "Found: $LOCAL_IMPORT_SCRIPT"
# Verify AD2 is reachable
Write-Status "INFO" "Testing connectivity to AD2 ($AD2_IP)..."
$pingResult = Test-Connection -ComputerName $AD2_IP -Count 1 -Quiet
if (-not $pingResult) {
Write-Status "ERROR" "Cannot reach AD2 at $AD2_IP"
exit 1
}
Write-Status "OK" "AD2 is reachable"
# Test SSH connection
Write-Status "INFO" "Testing SSH connection..."
$sshTest = Invoke-AD2Command "echo CONNECTION_OK"
$sshTestStr = $sshTest | Out-String
if ($sshTestStr -notmatch "CONNECTION_OK") {
Write-Status "ERROR" "SSH connection failed: $sshTestStr"
exit 1
}
Write-Status "OK" "SSH connection established"
# ============================================================================
# Step 1: Create backups on AD2
# ============================================================================
Write-Host ""
Write-Status "INFO" "--- Step 1: Creating backups on AD2 ---"
if ($DryRun) {
Write-Status "INFO" "[DRY RUN] Would backup $REMOTE_SYNC_PATH -> ${REMOTE_SYNC_PATH}.bak-${DATE_SUFFIX}"
Write-Status "INFO" "[DRY RUN] Would backup $REMOTE_IMPORT_PATH -> ${REMOTE_IMPORT_PATH}.bak-${DATE_SUFFIX}"
} else {
# Backup Sync-FromNAS.ps1
$backupCmd1 = "copy `"$REMOTE_SYNC_PATH`" `"${REMOTE_SYNC_PATH}.bak-${DATE_SUFFIX}`""
$backupResult1 = Invoke-AD2Command "cmd /c $backupCmd1"
if ($LASTEXITCODE -eq 0) {
Write-Status "OK" "Backed up Sync-FromNAS.ps1 -> .bak-${DATE_SUFFIX}"
} else {
Write-Status "WARNING" "Backup of Sync-FromNAS.ps1 may have failed (file might not exist yet): $($backupResult1 | Out-String)"
}
# Backup import.js
$backupCmd2 = "copy `"$REMOTE_IMPORT_PATH`" `"${REMOTE_IMPORT_PATH}.bak-${DATE_SUFFIX}`""
$backupResult2 = Invoke-AD2Command "cmd /c $backupCmd2"
if ($LASTEXITCODE -eq 0) {
Write-Status "OK" "Backed up import.js -> .bak-${DATE_SUFFIX}"
} else {
Write-Status "WARNING" "Backup of import.js may have failed (file might not exist yet): $($backupResult2 | Out-String)"
}
}
# ============================================================================
# Step 2: Create .ssh directory on AD2 if missing
# ============================================================================
Write-Host ""
Write-Status "INFO" "--- Step 2: Ensuring .ssh directory exists ---"
if ($DryRun) {
Write-Status "INFO" "[DRY RUN] Would create $REMOTE_SSH_DIR if missing"
} else {
$mkdirCmd = "cmd /c if not exist `"$REMOTE_SSH_DIR`" mkdir `"$REMOTE_SSH_DIR`""
Invoke-AD2Command $mkdirCmd | Out-Null
Write-Status "OK" "Ensured .ssh directory exists: $REMOTE_SSH_DIR"
# Create empty known_hosts if it does not exist
$knownHostsPath = "$REMOTE_SSH_DIR\known_hosts"
$touchCmd = "cmd /c if not exist `"$knownHostsPath`" type nul > `"$knownHostsPath`""
Invoke-AD2Command $touchCmd | Out-Null
Write-Status "OK" "Ensured known_hosts file exists: $knownHostsPath"
}
# ============================================================================
# Step 3: Deploy patched files
# ============================================================================
Write-Host ""
Write-Status "INFO" "--- Step 3: Deploying patched files ---"
if ($DryRun) {
Write-Status "INFO" "[DRY RUN] Would SCP $LOCAL_SYNC_SCRIPT -> ${AD2_IP}:${REMOTE_SYNC_PATH}"
Write-Status "INFO" "[DRY RUN] Would SCP $LOCAL_IMPORT_SCRIPT -> ${AD2_IP}:${REMOTE_IMPORT_PATH}"
} else {
# Deploy Sync-FromNAS.ps1
Write-Status "INFO" "Deploying Sync-FromNAS.ps1..."
$success1 = Copy-ToAD2 -LocalPath $LOCAL_SYNC_SCRIPT -RemotePath $REMOTE_SYNC_PATH
if ($success1) {
Write-Status "OK" "Deployed Sync-FromNAS.ps1"
} else {
Write-Status "ERROR" "Failed to deploy Sync-FromNAS.ps1"
exit 1
}
# Deploy import.js
Write-Status "INFO" "Deploying import.js..."
$success2 = Copy-ToAD2 -LocalPath $LOCAL_IMPORT_SCRIPT -RemotePath $REMOTE_IMPORT_PATH
if ($success2) {
Write-Status "OK" "Deployed import.js"
} else {
Write-Status "ERROR" "Failed to deploy import.js"
exit 1
}
}
# ============================================================================
# Step 4: Verify deployment
# ============================================================================
Write-Host ""
Write-Status "INFO" "--- Step 4: Verifying deployment ---"
if ($DryRun) {
Write-Status "INFO" "[DRY RUN] Would verify files exist on AD2"
} else {
# Check Sync-FromNAS.ps1 exists and has content
$verifyCmd1 = "cmd /c if exist `"$REMOTE_SYNC_PATH`" (echo FILE_EXISTS) else (echo FILE_MISSING)"
$verify1 = Invoke-AD2Command $verifyCmd1 | Out-String
if ($verify1 -match "FILE_EXISTS") {
Write-Status "OK" "Verified: Sync-FromNAS.ps1 exists on AD2"
} else {
Write-Status "ERROR" "Verification failed: Sync-FromNAS.ps1 not found on AD2"
exit 1
}
# Check import.js exists
$verifyCmd2 = "cmd /c if exist `"$REMOTE_IMPORT_PATH`" (echo FILE_EXISTS) else (echo FILE_MISSING)"
$verify2 = Invoke-AD2Command $verifyCmd2 | Out-String
if ($verify2 -match "FILE_EXISTS") {
Write-Status "OK" "Verified: import.js exists on AD2"
} else {
Write-Status "ERROR" "Verification failed: import.js not found on AD2"
exit 1
}
}
# ============================================================================
# Step 5: Quick dry-run test of the sync script
# ============================================================================
Write-Host ""
Write-Status "INFO" "--- Step 5: Running dry-run test of sync script ---"
if ($DryRun) {
Write-Status "INFO" "[DRY RUN] Would run: powershell -ExecutionPolicy Bypass -File $REMOTE_SYNC_PATH -DryRun"
} else {
Write-Status "INFO" "Executing sync script in dry-run mode on AD2..."
$testCmd = "powershell -ExecutionPolicy Bypass -File `"$REMOTE_SYNC_PATH`" -DryRun -Verbose"
$testResult = Invoke-AD2Command $testCmd
$testOutput = $testResult | Out-String
# Check if the script ran without critical errors
if ($LASTEXITCODE -eq 0) {
Write-Status "OK" "Sync script dry-run completed successfully"
if ($testOutput.Trim().Length -gt 0) {
Write-Host ""
Write-Host " --- Dry-run output ---"
foreach ($line in ($testResult | Select-Object -First 20)) {
Write-Host " $line"
}
$totalLines = ($testResult | Measure-Object).Count
if ($totalLines -gt 20) {
Write-Host " ... ($($totalLines - 20) more lines)"
}
Write-Host " --- End dry-run output ---"
}
} else {
Write-Status "WARNING" "Sync script dry-run exited with code $LASTEXITCODE"
Write-Status "INFO" "This may be expected if NAS is unreachable from AD2 during test"
if ($testOutput.Trim().Length -gt 0) {
Write-Host ""
Write-Host " --- Dry-run output ---"
foreach ($line in ($testResult | Select-Object -First 10)) {
Write-Host " $line"
}
Write-Host " --- End dry-run output ---"
}
}
}
# ============================================================================
# Summary
# ============================================================================
Write-Host ""
Write-Host "============================================"
Write-Host " Deployment Summary"
Write-Host "============================================"
Write-Host ""
if ($DryRun) {
Write-Status "INFO" "DRY RUN complete - no changes were made"
} else {
Write-Status "OK" "Sync-FromNAS.ps1 deployed to AD2 (backup: .bak-${DATE_SUFFIX})"
Write-Status "OK" "import.js deployed to AD2 (backup: .bak-${DATE_SUFFIX})"
Write-Status "OK" ".ssh directory and known_hosts verified"
Write-Status "OK" "Dry-run test executed"
Write-Host ""
Write-Status "INFO" "Fixes applied:"
Write-Host " 1. PULL hang fix: find output written to temp file, pulled via SCP"
Write-Host " 2. SCP quoting fix: remote paths quoted to handle special characters"
Write-Host " 3. Rename-OnNAS: files renamed to .synced instead of deleted"
Write-Host " 4. INSERT OR REPLACE: re-tested devices keep latest result"
Write-Host ""
Write-Status "INFO" "Next sync cycle will use the patched scripts automatically"
}
Write-Host ""
exit 0