Добавить bootstrap для новых ПК и Linux-скрипты.
SSH-ключи и Gitea API до clone Rules; init-cursor копирует из локального $HOME/CursorRules.
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Генерирует SSH-ключи и ~/.ssh/config для Git-хостов из local/git-ssh-hosts.conf.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ConfigDir = "$HOME\CursorRules\local",
|
||||
[string]$HostsConfig = '',
|
||||
[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 Read-GitSshHostsConfig {
|
||||
param([string]$Path)
|
||||
|
||||
$profiles = @{}
|
||||
$hosts = @{}
|
||||
$currentProfile = $null
|
||||
$currentHost = $null
|
||||
|
||||
Get-Content -LiteralPath $Path | ForEach-Object {
|
||||
$line = $_.Trim()
|
||||
if (-not $line -or $line.StartsWith('#')) { return }
|
||||
|
||||
if ($line -match '^\[profile:(.+)\]$') {
|
||||
$currentProfile = $Matches[1]
|
||||
$currentHost = $null
|
||||
$profiles[$currentProfile] = @{}
|
||||
return
|
||||
}
|
||||
if ($line -match '^\[host:(.+)\]$') {
|
||||
$currentHost = $Matches[1]
|
||||
$currentProfile = $null
|
||||
$hosts[$currentHost] = @{ Name = $currentHost }
|
||||
return
|
||||
}
|
||||
if ($line -match '^([^=]+)=(.*)$') {
|
||||
$key = $Matches[1].Trim()
|
||||
$value = $Matches[2].Trim()
|
||||
if ($currentHost) { $hosts[$currentHost][$key] = $value }
|
||||
elseif ($currentProfile) { $profiles[$currentProfile][$key] = $value }
|
||||
}
|
||||
}
|
||||
|
||||
return @{ Profiles = $profiles; Hosts = $hosts }
|
||||
}
|
||||
|
||||
function Get-SshConfigBlock {
|
||||
param(
|
||||
[string]$HostAlias,
|
||||
[hashtable]$HostEntry,
|
||||
[string]$IdentityFile
|
||||
)
|
||||
|
||||
@(
|
||||
"# BEGIN Setup-SshAccess:$HostAlias"
|
||||
"Host $HostAlias"
|
||||
" HostName $($HostEntry.HostName)"
|
||||
" User $($HostEntry.User)"
|
||||
" Port $($HostEntry.Port)"
|
||||
" IdentityFile $IdentityFile"
|
||||
" IdentitiesOnly yes"
|
||||
" AddKeysToAgent yes"
|
||||
" StrictHostKeyChecking accept-new"
|
||||
"# END Setup-SshAccess:$HostAlias"
|
||||
) -join "`n"
|
||||
}
|
||||
|
||||
function Update-SshConfig {
|
||||
param(
|
||||
[string]$ConfigPath,
|
||||
[string]$HostAlias,
|
||||
[string]$Block
|
||||
)
|
||||
|
||||
$begin = "# BEGIN Setup-SshAccess:$HostAlias"
|
||||
$end = "# END Setup-SshAccess:$HostAlias"
|
||||
$existing = if (Test-Path -LiteralPath $ConfigPath) {
|
||||
Get-Content -LiteralPath $ConfigPath -Raw
|
||||
} else {
|
||||
''
|
||||
}
|
||||
|
||||
$pattern = "(?ms)^$([regex]::Escape($begin)).*?$([regex]::Escape($end))\r?\n?"
|
||||
if ($existing -match $pattern) {
|
||||
return ($existing -replace $pattern, ($Block + "`n"))
|
||||
}
|
||||
|
||||
if ($existing -and -not $existing.EndsWith("`n")) {
|
||||
$existing += "`n"
|
||||
}
|
||||
return $existing + $Block + "`n"
|
||||
}
|
||||
|
||||
if (-not $HostsConfig) {
|
||||
$HostsConfig = Join-Path $ConfigDir 'git-ssh-hosts.conf'
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $HostsConfig)) {
|
||||
throw "Config not found: $HostsConfig"
|
||||
}
|
||||
|
||||
$parsed = Read-GitSshHostsConfig -Path $HostsConfig
|
||||
$sshDir = Join-Path $HOME '.ssh'
|
||||
$sshConfigPath = Join-Path $sshDir 'config'
|
||||
|
||||
if (-not $WhatIf) {
|
||||
New-Item -ItemType Directory -Path $sshDir -Force | Out-Null
|
||||
}
|
||||
|
||||
$configText = if (Test-Path -LiteralPath $sshConfigPath) {
|
||||
Get-Content -LiteralPath $sshConfigPath -Raw
|
||||
} else {
|
||||
''
|
||||
}
|
||||
|
||||
$profileKeys = @{}
|
||||
foreach ($profileName in $parsed.Profiles.Keys) {
|
||||
$profile = $parsed.Profiles[$profileName]
|
||||
$keyName = $profile.KeyName
|
||||
if (-not $keyName -or $profileKeys.ContainsKey($keyName)) { continue }
|
||||
|
||||
$keyPath = Join-Path $sshDir $keyName
|
||||
$comment = if ($profile.KeyComment) { $profile.KeyComment } else { $keyName }
|
||||
|
||||
if (-not (Test-Path -LiteralPath $keyPath)) {
|
||||
Write-Step "Generating $keyName"
|
||||
if ($WhatIf) {
|
||||
Write-Warn "WhatIf: ssh-keygen -t ed25519 -f $keyPath"
|
||||
}
|
||||
else {
|
||||
& ssh-keygen -t ed25519 -f $keyPath -N '""' -C $comment -q
|
||||
Write-Ok "key $keyName created"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Ok "key $keyName exists"
|
||||
}
|
||||
|
||||
$profileKeys[$keyName] = $profile
|
||||
}
|
||||
|
||||
foreach ($hostAlias in ($parsed.Hosts.Keys | Sort-Object)) {
|
||||
$hostEntry = $parsed.Hosts[$hostAlias]
|
||||
$profileName = $hostEntry.Profile
|
||||
if (-not $profileName -or -not $parsed.Profiles.ContainsKey($profileName)) {
|
||||
throw "Host ${hostAlias}: unknown profile $profileName"
|
||||
}
|
||||
|
||||
$profile = $parsed.Profiles[$profileName]
|
||||
$identity = "~/.ssh/$($profile.KeyName)"
|
||||
$block = Get-SshConfigBlock -HostAlias $hostAlias -HostEntry $hostEntry -IdentityFile $identity
|
||||
|
||||
if ($WhatIf) {
|
||||
Write-Step "WhatIf: ssh config block for $hostAlias"
|
||||
continue
|
||||
}
|
||||
|
||||
$configText = Update-SshConfig -ConfigPath $sshConfigPath -HostAlias $hostAlias -Block $block
|
||||
Write-Ok "ssh config: $hostAlias"
|
||||
}
|
||||
|
||||
if (-not $WhatIf) {
|
||||
Set-Content -LiteralPath $sshConfigPath -Value $configText.TrimEnd() -Encoding UTF8 -NoNewline
|
||||
Add-Content -LiteralPath $sshConfigPath -Value '' -Encoding UTF8
|
||||
|
||||
Get-Service ssh-agent -ErrorAction SilentlyContinue | Where-Object { $_.Status -ne 'Running' } |
|
||||
ForEach-Object { Start-Service ssh-agent | Out-Null }
|
||||
|
||||
foreach ($keyName in $profileKeys.Keys) {
|
||||
$keyPath = Join-Path $sshDir $keyName
|
||||
if (Test-Path -LiteralPath $keyPath) {
|
||||
ssh-add $keyPath 2>$null | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Ok 'SSH setup complete'
|
||||
Reference in New Issue
Block a user