#Requires -Version 5.1 <# .SYNOPSIS Настройка git на машине: user, SSH, Gitea keys, clone/update Rules. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$Root, [string]$RulesDest = '', [string]$CloneUrl = 'ssh://git@git.papatramp.ru:2222/PapaTramp/Rules.git', [string]$HttpsCloneUrl = 'https://git.papatramp.ru/PapaTramp/Rules.git', [switch]$SkipClone, [switch]$SkipProfile, [switch]$SkipGitSetup, [switch]$WhatIf ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' function Write-Step([string]$Message) { Write-Host "[*] $Message" -ForegroundColor Cyan } function Write-Ok([string]$Message) { Write-Host "[+] $Message" -ForegroundColor Green } function Write-Warn([string]$Message) { Write-Host "[!] $Message" -ForegroundColor Yellow } function Read-KeyValueFile { param([string]$Path) $data = @{} if (-not (Test-Path -LiteralPath $Path)) { return $data } Get-Content -LiteralPath $Path | ForEach-Object { if ($_ -match '^([^#=]+)=(.*)$') { $data[$Matches[1].Trim()] = $Matches[2].Trim() } } return $data } function Test-MachineGitReady { param([string]$ConfigDir) $hostsConfig = Join-Path $ConfigDir 'git-ssh-hosts.conf' if (-not (Test-Path -LiteralPath $hostsConfig)) { return $false } $sshConfig = Join-Path $HOME '.ssh\config' if (-not (Test-Path -LiteralPath $sshConfig)) { return $false } $profiles = @{} $currentProfile = $null Get-Content -LiteralPath $hostsConfig | ForEach-Object { $line = $_.Trim() if ($line -match '^\[profile:(.+)\]$') { $currentProfile = $Matches[1] $profiles[$currentProfile] = @{} return } if ($line -match '^\[host:') { $currentProfile = $null; return } if ($line -match '^([^=]+)=(.*)$' -and $currentProfile) { $profiles[$currentProfile][$Matches[1].Trim()] = $Matches[2].Trim() } } $sshText = Get-Content -LiteralPath $sshConfig -Raw foreach ($profile in $profiles.Values) { $keyName = $profile.KeyName if (-not $keyName) { continue } $keyPath = Join-Path $HOME ".ssh\$keyName" if (-not (Test-Path -LiteralPath $keyPath)) { return $false } } if ($sshText -notmatch '(?m)^Host\s+git\.kalinamall\.ru\s*$') { return $false } return $true } function Ensure-RulesRepository { param( [string]$Destination, [string]$SshUrl, [string]$HttpsUrl ) if (Test-Path -LiteralPath $Destination) { if (Test-Path (Join-Path $Destination '.git')) { if ($WhatIf) { Write-Step "WhatIf: git pull $Destination" return } Write-Step "Updating $Destination" git -C $Destination pull --ff-only Write-Ok 'CursorRules updated' return } $backup = "${Destination}.bak-$(Get-Date -Format 'yyyyMMdd-HHmmss')" Write-Warn "Replacing non-git folder: $Destination -> $backup" if (-not $WhatIf) { Move-Item -LiteralPath $Destination -Destination $backup } } if ($WhatIf) { Write-Step "WhatIf: clone -> $Destination" return } Write-Step "Cloning Rules via SSH: $SshUrl" git clone --depth 1 $SshUrl $Destination 2>&1 | Out-Null if ($LASTEXITCODE -eq 0) { Write-Ok "Cloned to $Destination" return } Write-Warn 'SSH clone failed - trying HTTPS (enter Gitea login once)' git clone --depth 1 $HttpsUrl $Destination if ($LASTEXITCODE -ne 0) { throw "Failed to clone Rules repository" } Write-Ok "Cloned via HTTPS to $Destination" } function Ensure-InitCursorProfile { param([string]$RulesDest) $profileHook = ". `"$RulesDest\init-cursor.ps1`"" $profilePath = $PROFILE.CurrentUserAllHosts if (-not $profilePath) { $profilePath = $PROFILE.CurrentUserCurrentHost } if ($WhatIf) { Write-Step "WhatIf: profile hook -> $profilePath" return } $profileDir = Split-Path -Parent $profilePath if (-not (Test-Path -LiteralPath $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force | Out-Null } $profileText = if (Test-Path -LiteralPath $profilePath) { Get-Content -LiteralPath $profilePath -Raw } else { '' } if ($profileText -notmatch [regex]::Escape($profileHook)) { if ($profileText -and -not $profileText.EndsWith("`n")) { $profileText += "`n" } $profileText += $profileHook + "`n" Set-Content -LiteralPath $profilePath -Value $profileText.TrimEnd() -Encoding UTF8 Write-Ok "Profile updated: $profilePath" } } if (-not $RulesDest) { $RulesDest = Join-Path $HOME 'CursorRules' } $configDir = Join-Path $Root 'local' if (-not (Test-Path -LiteralPath $configDir)) { throw "Bootstrap config not found: $configDir" } $gitReady = Test-MachineGitReady -ConfigDir $configDir $repoReady = Test-Path (Join-Path $RulesDest '.git') if (-not $gitReady -and -not $SkipGitSetup) { Write-Step 'Machine bootstrap: git + SSH' $userCfgPath = Join-Path $configDir 'git-user.conf' if (Test-Path -LiteralPath $userCfgPath) { $userCfg = Read-KeyValueFile -Path $userCfgPath if ($userCfg.UserName -and -not $WhatIf) { git config --global user.name $userCfg.UserName | Out-Null Write-Ok "git user.name = $($userCfg.UserName)" } if ($userCfg.UserEmail -and -not $WhatIf) { git config --global user.email $userCfg.UserEmail | Out-Null Write-Ok "git user.email = $($userCfg.UserEmail)" } } $setupArgs = @{ ConfigDir = $configDir; WhatIf = $WhatIf.IsPresent } & (Join-Path $Root 'scripts\Setup-GitSsh.ps1') @setupArgs $registerArgs = @{ ConfigDir = $configDir; WhatIf = $WhatIf.IsPresent } & (Join-Path $Root 'scripts\Register-GiteaSshKeys.ps1') @registerArgs if (-not $WhatIf) { Write-Step 'Testing git@git.kalinamall.ru' ssh -T -o BatchMode=yes -o ConnectTimeout=10 git@git.kalinamall.ru 2>&1 | ForEach-Object { Write-Host $_ } } } elseif ($gitReady) { Write-Ok 'Git SSH already configured' } if (-not $SkipClone -and -not $repoReady) { Ensure-RulesRepository -Destination $RulesDest -SshUrl $CloneUrl -HttpsUrl $HttpsCloneUrl } elseif ($repoReady -and -not $SkipClone -and -not $WhatIf) { Write-Ok "CursorRules repo OK: $RulesDest" } if (-not $SkipProfile) { Ensure-InitCursorProfile -RulesDest $RulesDest } Write-Ok 'Machine bootstrap complete' # Export test function for init-cursor dot-source function Test-CursorMachineGitReady { param([string]$ConfigDir = (Join-Path $HOME 'CursorRules\local')) return Test-MachineGitReady -ConfigDir $ConfigDir }