First commit
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
JingTian LLM - File Sync Script
|
||||
.DESCRIPTION
|
||||
Syncs files between Windows (BenjaminTeam/) and the Ubuntu server.
|
||||
- PULL first: Ubuntu -> Windows for bidirectional files (pipeline outputs)
|
||||
- PUSH second: Windows -> Ubuntu (one-way, Windows is source of truth)
|
||||
|
||||
Order matters: pull gets pipeline updates, then push sends everything
|
||||
(including those updates + Mr. Choi's edits) back as source of truth.
|
||||
|
||||
Called automatically by Windows Task Scheduler every N minutes.
|
||||
#>
|
||||
|
||||
param(
|
||||
[string]$SyncRoot = $PSScriptRoot
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
|
||||
# ============================================================================
|
||||
# Helpers
|
||||
# ============================================================================
|
||||
|
||||
function Read-Config {
|
||||
param([string]$Path)
|
||||
$config = @{}
|
||||
$section = ""
|
||||
$listItems = @()
|
||||
$listSection = ""
|
||||
|
||||
foreach ($line in Get-Content $Path) {
|
||||
$line = $line.Trim()
|
||||
if ($line -eq "" -or $line.StartsWith("#")) { continue }
|
||||
|
||||
if ($line -match '^\[(.+)\]$') {
|
||||
if ($listSection -and $listItems.Count -gt 0) {
|
||||
$config[$listSection] = $listItems
|
||||
}
|
||||
$section = $Matches[1]
|
||||
if ($section -eq "bidirectional") {
|
||||
$listSection = $section
|
||||
$listItems = @()
|
||||
} else {
|
||||
$listSection = ""
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if ($listSection) {
|
||||
$listItems += $line
|
||||
} elseif ($line -match '^(.+?)=(.+)$') {
|
||||
$key = "$section.$($Matches[1].Trim())"
|
||||
$config[$key] = $Matches[2].Trim()
|
||||
}
|
||||
}
|
||||
|
||||
if ($listSection -and $listItems.Count -gt 0) {
|
||||
$config[$listSection] = $listItems
|
||||
}
|
||||
|
||||
return $config
|
||||
}
|
||||
|
||||
function Write-Log {
|
||||
param([string]$Message, [string]$Level = "INFO")
|
||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$entry = "[$timestamp] [$Level] $Message"
|
||||
Add-Content -Path $logFile -Value $entry
|
||||
if ($Level -eq "ERROR") {
|
||||
Write-Host $entry -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Setup
|
||||
# ============================================================================
|
||||
|
||||
$configPath = Join-Path $SyncRoot "config.ini"
|
||||
$rcloneExe = Join-Path $SyncRoot "rclone\rclone.exe"
|
||||
$rcloneConf = Join-Path $SyncRoot "rclone\rclone.conf"
|
||||
$logsPath = Join-Path $SyncRoot "logs"
|
||||
$ignoreFile = Join-Path $SyncRoot ".sync_ignore"
|
||||
|
||||
# BenjaminTeam is three levels up from Code/Sync/ (_LLM -> BenjaminTeam)
|
||||
$benjaminTeam = (Resolve-Path (Join-Path $SyncRoot "..\..\.." )).Path
|
||||
|
||||
# Log file: one per day
|
||||
$logDate = Get-Date -Format "yyyy-MM-dd"
|
||||
$logFile = Join-Path $logsPath "sync-$logDate.log"
|
||||
|
||||
# Ensure logs directory
|
||||
if (-not (Test-Path $logsPath)) {
|
||||
New-Item -ItemType Directory -Path $logsPath -Force | Out-Null
|
||||
}
|
||||
|
||||
# Preflight checks
|
||||
if (-not (Test-Path $configPath)) {
|
||||
Write-Log "config.ini not found at $configPath" "ERROR"
|
||||
exit 1
|
||||
}
|
||||
if (-not (Test-Path $rcloneExe)) {
|
||||
Write-Log "rclone.exe not found at $rcloneExe. Run Setup.bat first." "ERROR"
|
||||
exit 1
|
||||
}
|
||||
if (-not (Test-Path $rcloneConf)) {
|
||||
Write-Log "rclone.conf not found at $rcloneConf. Run Setup.bat first." "ERROR"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Read config
|
||||
$config = Read-Config $configPath
|
||||
$remoteName = $config["sync.remote_name"]
|
||||
$remotePath = $config["server.remote_path"]
|
||||
$bidirectionalFiles = @()
|
||||
if ($config.ContainsKey("bidirectional")) {
|
||||
$bidirectionalFiles = $config["bidirectional"]
|
||||
}
|
||||
|
||||
$remoteBase = "${remoteName}:${remotePath}"
|
||||
|
||||
Write-Log "=== Sync started ==="
|
||||
Write-Log "BenjaminTeam: $benjaminTeam"
|
||||
Write-Log "Remote: $remoteBase"
|
||||
|
||||
# ============================================================================
|
||||
# Phase 1: PULL bidirectional files (Ubuntu -> Windows)
|
||||
# Runs FIRST so pipeline updates reach Windows before push sends everything back.
|
||||
# Uses --update: only copies if remote file is newer than local.
|
||||
# This means Mr. Choi's recent edits are preserved (his file is newer).
|
||||
# ============================================================================
|
||||
|
||||
if ($bidirectionalFiles.Count -gt 0) {
|
||||
Write-Log "--- Phase 1: PULL bidirectional files (Ubuntu -> Windows) ---"
|
||||
|
||||
foreach ($relPath in $bidirectionalFiles) {
|
||||
# Normalize path separators
|
||||
$relPath = $relPath.Replace("/", "\")
|
||||
$remoteSrc = "${remoteBase}/$($relPath.Replace('\', '/'))"
|
||||
$localDest = Join-Path $benjaminTeam $relPath
|
||||
$localDir = Split-Path $localDest -Parent
|
||||
|
||||
# Ensure local directory exists
|
||||
if (-not (Test-Path $localDir)) {
|
||||
New-Item -ItemType Directory -Path $localDir -Force | Out-Null
|
||||
}
|
||||
|
||||
Write-Log "Pulling: $relPath"
|
||||
|
||||
try {
|
||||
$pullOutput = & $rcloneExe copyto $remoteSrc $localDest `
|
||||
--config $rcloneConf `
|
||||
--update `
|
||||
--contimeout 30s `
|
||||
--timeout 60s `
|
||||
--retries 3 `
|
||||
--log-level NOTICE 2>&1
|
||||
$pullExitCode = $LASTEXITCODE
|
||||
|
||||
if ($pullExitCode -eq 0) {
|
||||
Write-Log " Pulled OK: $relPath"
|
||||
} else {
|
||||
# Exit code 3 = directory not found / file doesn't exist yet (OK)
|
||||
if ($pullExitCode -eq 3) {
|
||||
Write-Log " Not found on server (yet): $relPath"
|
||||
} else {
|
||||
Write-Log " Pull failed (exit code: $pullExitCode): $relPath" "ERROR"
|
||||
foreach ($line in $pullOutput) {
|
||||
Write-Log " $line" "ERROR"
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-Log " Pull exception for ${relPath}: $_" "ERROR"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Log "No bidirectional files configured, skipping pull phase"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Phase 2: PUSH (Windows -> Ubuntu)
|
||||
# Windows is source of truth. rclone sync mirrors local to remote.
|
||||
# Bidirectional files are EXCLUDED from push — they are managed by the
|
||||
# pipeline on Ubuntu and pulled down in Phase 1.
|
||||
# ============================================================================
|
||||
|
||||
Write-Log "--- Phase 2: PUSH (Windows -> Ubuntu) ---"
|
||||
|
||||
$pushArgs = @(
|
||||
"sync"
|
||||
$benjaminTeam
|
||||
$remoteBase
|
||||
"--config", $rcloneConf
|
||||
"--transfers", "4"
|
||||
"--checkers", "8"
|
||||
"--contimeout", "30s"
|
||||
"--timeout", "120s"
|
||||
"--retries", "3"
|
||||
"--log-level", "NOTICE"
|
||||
)
|
||||
|
||||
# Add filter file if it exists
|
||||
if (Test-Path $ignoreFile) {
|
||||
$pushArgs += "--filter-from"
|
||||
$pushArgs += $ignoreFile
|
||||
}
|
||||
|
||||
# Exclude bidirectional files from push (pipeline manages these on Ubuntu)
|
||||
foreach ($relPath in $bidirectionalFiles) {
|
||||
$excludePath = $relPath.Replace("\", "/")
|
||||
$pushArgs += "--exclude"
|
||||
$pushArgs += $excludePath
|
||||
}
|
||||
|
||||
$pushStart = Get-Date
|
||||
Write-Log "Running: rclone sync (push)"
|
||||
|
||||
try {
|
||||
$pushOutput = & $rcloneExe @pushArgs 2>&1
|
||||
$pushExitCode = $LASTEXITCODE
|
||||
$pushDuration = ((Get-Date) - $pushStart).TotalSeconds
|
||||
|
||||
if ($pushExitCode -eq 0) {
|
||||
Write-Log "Push completed in ${pushDuration}s"
|
||||
} else {
|
||||
Write-Log "Push failed (exit code: $pushExitCode) in ${pushDuration}s" "ERROR"
|
||||
foreach ($line in $pushOutput) {
|
||||
Write-Log " $line" "ERROR"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-Log "Push exception: $_" "ERROR"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Done
|
||||
# ============================================================================
|
||||
|
||||
Write-Log "=== Sync finished ==="
|
||||
Reference in New Issue
Block a user