chore(home): mirror from kalinamall (c53704a) with papatramp URLs
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Создать репозиторий на home Gitea через API, если его ещё нет.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$RepoName,
|
||||
[string]$Owner = 'PapaTramp',
|
||||
[string]$ConfigPath = "$HOME\CursorRules\local\papatramp-gitea.conf",
|
||||
[string]$Description = ''
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ConfigPath)) {
|
||||
throw "Config not found: $ConfigPath"
|
||||
}
|
||||
|
||||
$cfg = @{}
|
||||
Get-Content -LiteralPath $ConfigPath | ForEach-Object {
|
||||
if ($_ -match '^([^#=]+)=(.*)$') {
|
||||
$cfg[$Matches[1].Trim()] = $Matches[2].Trim()
|
||||
}
|
||||
}
|
||||
|
||||
$base = $cfg.GiteaApiUrl.TrimEnd('/')
|
||||
$pair = '{0}:{1}' -f $cfg.GiteaApiUser, $cfg.GiteaApiPassword
|
||||
$auth = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($pair))
|
||||
$headers = @{ Authorization = $auth; 'Content-Type' = 'application/json' }
|
||||
|
||||
$checkUrl = "$base/api/v1/repos/$Owner/$RepoName"
|
||||
try {
|
||||
$existing = Invoke-RestMethod -Method Get -Uri $checkUrl -Headers $headers -TimeoutSec 15
|
||||
Write-Host "[+] EXISTS $($existing.full_name)" -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
catch {
|
||||
if ($_.Exception.Response.StatusCode.value__ -ne 404) {
|
||||
throw "check failed: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
$body = (@{
|
||||
name = $RepoName
|
||||
private = $true
|
||||
auto_init = $false
|
||||
description = $Description
|
||||
} | ConvertTo-Json)
|
||||
|
||||
foreach ($url in @("$base/api/v1/orgs/$Owner/repos", "$base/api/v1/user/repos")) {
|
||||
try {
|
||||
$created = Invoke-RestMethod -Method Post -Uri $url -Headers $headers -Body $body -TimeoutSec 30
|
||||
Write-Host "[+] CREATED $($created.full_name)" -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
catch {
|
||||
Write-Warning "create via $url : $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
throw 'failed to create repository on home Gitea'
|
||||
@@ -0,0 +1,210 @@
|
||||
#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
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#Requires -Version 5.1
|
||||
# Publish kalinamall/main tree to github.com/PTah as a single orphan commit (no history).
|
||||
# Hard sanitize: internal URLs/hosts, site names, Cursor trailers/mentions.
|
||||
param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$SourceRepo = (Get-Location).Path,
|
||||
[string]$RemoteName = 'github',
|
||||
[string]$Version = '',
|
||||
[string[]]$ExcludePaths = @(),
|
||||
[hashtable]$OverlayFromGithub = @{}
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$SourceRepo = (Resolve-Path $SourceRepo).Path
|
||||
Set-Location $SourceRepo
|
||||
|
||||
$remoteUrl = (git remote get-url $RemoteName).Trim()
|
||||
if (-not $remoteUrl) { throw "remote not found: $RemoteName" }
|
||||
|
||||
$work = Join-Path $env:TEMP "gh-orphan-$([IO.Path]::GetFileName($SourceRepo))-$PID"
|
||||
if (Test-Path $work) { Remove-Item $work -Recurse -Force }
|
||||
New-Item -ItemType Directory -Path $work | Out-Null
|
||||
|
||||
$zip = Join-Path $env:TEMP "gh-archive-$PID.zip"
|
||||
if (Test-Path $zip) { Remove-Item $zip -Force }
|
||||
git archive --format=zip -o $zip main
|
||||
Expand-Archive -Path $zip -DestinationPath $work -Force
|
||||
Remove-Item $zip -Force
|
||||
|
||||
foreach ($drop in @('.cursor', '.cursorignore', 'AGENTS.md', 'CURSOR.md')) {
|
||||
$t = Join-Path $work $drop
|
||||
if (Test-Path $t) {
|
||||
Remove-Item $t -Recurse -Force
|
||||
Write-Host "exclude: $drop"
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rel in $ExcludePaths) {
|
||||
$target = Join-Path $work $rel
|
||||
if (Test-Path $target) {
|
||||
Remove-Item $target -Recurse -Force
|
||||
Write-Host "exclude: $rel"
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($entry in $OverlayFromGithub.GetEnumerator()) {
|
||||
$rel = $entry.Key
|
||||
$content = git show "${RemoteName}/main:$rel" 2>$null
|
||||
if ($LASTEXITCODE -ne 0) { throw "overlay missing on ${RemoteName}/main: $rel" }
|
||||
$out = Join-Path $work $rel
|
||||
$dir = Split-Path $out -Parent
|
||||
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
|
||||
[IO.File]::WriteAllText($out, ($content -join "`n") + "`n", [Text.UTF8Encoding]::new($false))
|
||||
Write-Host "overlay: $rel"
|
||||
}
|
||||
|
||||
$textExt = @('.md', '.py', '.ts', '.vue', '.json', '.sh', '.ps1', '.yml', '.yaml', '.example', '.service', '.txt', '.html', '.css', '.kt', '.kts', '.xml', '.properties', '.gradle', '.pro')
|
||||
$replacements = @(
|
||||
@{ From = 'https://git\.kalinamall\.ru/PapaTramp/([^)/\s"''`]+)/src/branch/main/'; To = 'https://git.papatramp.ru/PapaTramp/$1/src/branch/main/' }
|
||||
@{ From = 'https://git\.kalinamall\.ru/PapaTramp/([^)/\s"''`]+)/blob/main/'; To = 'https://git.papatramp.ru/PapaTramp/$1/src/branch/main/' }
|
||||
@{ From = 'https://git\.kalinamall\.ru/PapaTramp/'; To = 'https://git.papatramp.ru/PapaTramp/' }
|
||||
@{ From = 'git\.kalinamall\.ru/PapaTramp/'; To = 'git.papatramp.ru/PapaTramp/' }
|
||||
@{ From = 'https://git\.papatramp\.ru/(PapaTramp|PTah)/'; To = 'https://git.papatramp.ru/PapaTramp/' }
|
||||
@{ From = 'git\.papatramp\.ru/(PapaTramp|PTah)/'; To = 'git.papatramp.ru/PapaTramp/' }
|
||||
@{ From = 'ssh://git@git\.kalinamall\.ru:2222/PapaTramp/'; To = 'https://git.papatramp.ru/PapaTramp/' }
|
||||
@{ From = 'ssh://git@git\.papatramp\.ru:2222/PapaTramp/'; To = 'https://git.papatramp.ru/PapaTramp/' }
|
||||
@{ From = 'sac-api\.kalinamall\.ru'; To = 'sac-api.example.com' }
|
||||
@{ From = 'sac\.kalinamall\.ru'; To = 'sac.example.com' }
|
||||
@{ From = 'ext\.kalinamall\.ru'; To = 'ext.example.com' }
|
||||
@{ From = '\*\.kalinamall\.ru'; To = '*.example.com' }
|
||||
@{ From = 'kalinamall\.ru'; To = 'example.com' }
|
||||
@{ From = 'papatramp\.lan'; To = 'example.lan' }
|
||||
@{ From = '192\.168\.\d+\.\d+'; To = '10.0.0.1' }
|
||||
@{ From = 'Co-authored-by:\s*Cursor\s*<cursoragent@cursor\.com>\s*'; To = '' }
|
||||
@{ From = 'Made-with:\s*Cursor\s*'; To = '' }
|
||||
@{ From = 'Cursor Agent'; To = '' }
|
||||
@{ From = 'Cursor IDE'; To = '' }
|
||||
@{ From = 'cursoragent@cursor\.com'; To = '' }
|
||||
@{ From = '(?i)\bpowered by cursor\b'; To = '' }
|
||||
@{ From = 'B26\\'; To = 'CONTOSO\' }
|
||||
@{ From = 'B26/'; To = 'CONTOSO/' }
|
||||
@{ From = 'K6A-DC\d'; To = 'WIN-DC01' }
|
||||
@{ From = 'UNMS Kalina'; To = 'Example Site' }
|
||||
@{ From = 'k\.khodasevich'; To = 'user1' }
|
||||
@{ From = '\bkalinamall\b'; To = 'example' }
|
||||
@{ From = '\bpapatramp\b'; To = 'jdoe' }
|
||||
)
|
||||
|
||||
Get-ChildItem -Path $work -Recurse -File | ForEach-Object {
|
||||
$ext = $_.Extension.ToLowerInvariant()
|
||||
if ($textExt -notcontains $ext -and $_.Name -notmatch '\.(example|service)$') { return }
|
||||
$content = [IO.File]::ReadAllText($_.FullName)
|
||||
$updated = $content
|
||||
foreach ($r in $replacements) {
|
||||
$updated = [regex]::Replace($updated, $r.From, $r.To)
|
||||
}
|
||||
if ($updated -ne $content) {
|
||||
[IO.File]::WriteAllText($_.FullName, $updated, [Text.UTF8Encoding]::new($false))
|
||||
}
|
||||
}
|
||||
|
||||
$forbidden = @(
|
||||
'git\.kalinamall\.ru',
|
||||
'git\.papatramp\.ru',
|
||||
'papatramp\.lan',
|
||||
'192\.168\.\d+\.\d+',
|
||||
'cursoragent@cursor\.com',
|
||||
'Co-authored-by:\s*Cursor',
|
||||
'Made-with:\s*Cursor',
|
||||
'(?i)Cursor Agent',
|
||||
'(?i)Cursor IDE',
|
||||
'(?i)powered by cursor',
|
||||
'\bkalinamall\b',
|
||||
'\bpapatramp\b',
|
||||
'K6A-DC',
|
||||
'B26\\'
|
||||
)
|
||||
$hits = 0
|
||||
Get-ChildItem -Path $work -Recurse -File | ForEach-Object {
|
||||
$ext = $_.Extension.ToLowerInvariant()
|
||||
if ($textExt -notcontains $ext -and $_.Name -notmatch '\.(example|service)$') { return }
|
||||
$content = [IO.File]::ReadAllText($_.FullName)
|
||||
foreach ($pat in $forbidden) {
|
||||
if ([regex]::IsMatch($content, $pat)) {
|
||||
$rel = $_.FullName.Substring($work.Length + 1)
|
||||
Write-Warning "FORBIDDEN '$pat' in $rel"
|
||||
$script:hits++
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($hits -gt 0) { throw "sanitization failed: $hits forbidden pattern(s)" }
|
||||
|
||||
Set-Location $work
|
||||
if (Test-Path .git) { Remove-Item .git -Recurse -Force }
|
||||
git init -b main | Out-Null
|
||||
git add -A
|
||||
$msg = if ($Version) { "chore(github): sync public mirror ($Version)" } else { 'chore(github): sync public mirror' }
|
||||
git -c user.name='PTah' -c user.email='papatramp@gmail.com' commit -m $msg
|
||||
git remote add target $remoteUrl
|
||||
git push target main --force
|
||||
Write-Host "OK -> $remoteUrl (orphan, force)"
|
||||
|
||||
Set-Location $SourceRepo
|
||||
Remove-Item $work -Recurse -Force
|
||||
@@ -0,0 +1,60 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Mirror current kalinamall/main tree to remote "home" with papatramp URLs (force).
|
||||
.DESCRIPTION
|
||||
Canonical working tree stays on kalinamall. This builds a temp orphan commit
|
||||
with git host URLs rewritten to git.papatramp.ru and force-pushes to home.
|
||||
Private content (LAN, names, secrets) is kept — unlike Publish-GithubOrphan.
|
||||
#>
|
||||
param(
|
||||
[string]$SourceRepo = (Get-Location).Path,
|
||||
[string]$Branch = 'main',
|
||||
[string]$RemoteName = 'home'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$SourceRepo = (Resolve-Path $SourceRepo).Path
|
||||
Set-Location $SourceRepo
|
||||
|
||||
$rewrite = Join-Path $PSScriptRoot 'Rewrite-GitHostUrls.ps1'
|
||||
if (-not (Test-Path $rewrite)) { throw "missing: $rewrite" }
|
||||
|
||||
$ensure = Join-Path $PSScriptRoot 'Ensure-GiteaHomeRepo.ps1'
|
||||
$repoName = Split-Path -Leaf (git rev-parse --show-toplevel)
|
||||
if (Test-Path $ensure) {
|
||||
try {
|
||||
& powershell -NoProfile -ExecutionPolicy Bypass -File $ensure -RepoName $repoName
|
||||
} catch {
|
||||
Write-Warning "Ensure-GiteaHomeRepo: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
$remoteUrl = (git remote get-url $RemoteName).Trim()
|
||||
if (-not $remoteUrl) { throw "remote not found: $RemoteName" }
|
||||
|
||||
$work = Join-Path $env:TEMP "home-mirror-$repoName-$PID"
|
||||
if (Test-Path $work) { Remove-Item $work -Recurse -Force }
|
||||
New-Item -ItemType Directory -Path $work | Out-Null
|
||||
|
||||
$zip = Join-Path $env:TEMP "home-archive-$PID.zip"
|
||||
if (Test-Path $zip) { Remove-Item $zip -Force }
|
||||
git archive --format=zip -o $zip $Branch
|
||||
Expand-Archive -Path $zip -DestinationPath $work -Force
|
||||
Remove-Item $zip -Force
|
||||
|
||||
& powershell -NoProfile -ExecutionPolicy Bypass -File $rewrite -Target home -Root $work
|
||||
|
||||
Set-Location $work
|
||||
if (Test-Path .git) { Remove-Item .git -Recurse -Force }
|
||||
git init -b main | Out-Null
|
||||
git add -A
|
||||
$sha = (git -C $SourceRepo rev-parse --short HEAD).Trim()
|
||||
$msg = "chore(home): mirror from kalinamall ($sha) with papatramp URLs"
|
||||
git -c user.name='PTah' -c user.email='papatramp@gmail.com' commit -m $msg | Out-Null
|
||||
git remote add target $remoteUrl
|
||||
git push target main --force
|
||||
Write-Host "OK -> $remoteUrl (home mirror, force)"
|
||||
|
||||
Set-Location $SourceRepo
|
||||
Remove-Item $work -Recurse -Force
|
||||
@@ -0,0 +1,136 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Регистрирует публичные SSH-ключи в Gitea через API (kalinamall, papatramp).
|
||||
#>
|
||||
[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 = @{}
|
||||
Get-Content -LiteralPath $Path | ForEach-Object {
|
||||
if ($_ -match '^([^#=]+)=(.*)$') {
|
||||
$data[$Matches[1].Trim()] = $Matches[2].Trim()
|
||||
}
|
||||
}
|
||||
return $data
|
||||
}
|
||||
|
||||
function Read-GitSshHostsConfig {
|
||||
param([string]$Path)
|
||||
|
||||
$profiles = @{}
|
||||
$currentProfile = $null
|
||||
|
||||
Get-Content -LiteralPath $Path | ForEach-Object {
|
||||
$line = $_.Trim()
|
||||
if (-not $line -or $line.StartsWith('#')) { return }
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
return $profiles
|
||||
}
|
||||
|
||||
function Get-GiteaAuthHeader {
|
||||
param([hashtable]$Cfg)
|
||||
$pair = '{0}:{1}' -f $Cfg.GiteaApiUser, $Cfg.GiteaApiPassword
|
||||
$auth = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($pair))
|
||||
return @{ Authorization = $auth; 'Content-Type' = 'application/json' }
|
||||
}
|
||||
|
||||
function Register-GiteaKey {
|
||||
param(
|
||||
[hashtable]$Cfg,
|
||||
[string]$PublicKeyPath,
|
||||
[string]$Title
|
||||
)
|
||||
|
||||
if (-not $Cfg.GiteaApiPassword) {
|
||||
Write-Warn "$Title : GiteaApiPassword empty - add key manually in Gitea UI"
|
||||
Write-Host (Get-Content -LiteralPath $PublicKeyPath -Raw).Trim()
|
||||
return
|
||||
}
|
||||
|
||||
$base = $Cfg.GiteaApiUrl.TrimEnd('/')
|
||||
$headers = Get-GiteaAuthHeader -Cfg $Cfg
|
||||
$pub = (Get-Content -LiteralPath $PublicKeyPath -Raw).Trim()
|
||||
|
||||
$existing = Invoke-RestMethod -Method Get -Uri "$base/api/v1/user/keys" -Headers $headers -TimeoutSec 15
|
||||
foreach ($item in @($existing)) {
|
||||
if ($item.key.Trim() -eq $pub) {
|
||||
Write-Ok "$Title : key already registered ($($item.title))"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$body = (@{ title = $Title; key = $pub } | ConvertTo-Json)
|
||||
if ($WhatIf) {
|
||||
Write-Step "WhatIf: POST $base/api/v1/user/keys ($Title)"
|
||||
return
|
||||
}
|
||||
|
||||
$created = Invoke-RestMethod -Method Post -Uri "$base/api/v1/user/keys" -Headers $headers -Body $body -TimeoutSec 30
|
||||
Write-Ok "$Title : registered ($($created.title))"
|
||||
}
|
||||
|
||||
if (-not $HostsConfig) {
|
||||
$HostsConfig = Join-Path $ConfigDir 'git-ssh-hosts.conf'
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $HostsConfig)) {
|
||||
throw "Config not found: $HostsConfig"
|
||||
}
|
||||
|
||||
$profiles = Read-GitSshHostsConfig -Path $HostsConfig
|
||||
$sshDir = Join-Path $HOME '.ssh'
|
||||
$machine = $env:COMPUTERNAME
|
||||
if (-not $machine) { $machine = [Environment]::MachineName }
|
||||
$registered = @{}
|
||||
|
||||
foreach ($profileName in $profiles.Keys) {
|
||||
$profile = $profiles[$profileName]
|
||||
$giteaConfigName = $profile.GiteaConfig
|
||||
if (-not $giteaConfigName) { continue }
|
||||
|
||||
$keyName = $profile.KeyName
|
||||
if ($registered.ContainsKey($keyName)) { continue }
|
||||
$registered[$keyName] = $true
|
||||
|
||||
$giteaPath = Join-Path $ConfigDir $giteaConfigName
|
||||
if (-not (Test-Path -LiteralPath $giteaPath)) {
|
||||
Write-Warn "Gitea config not found: $giteaPath"
|
||||
continue
|
||||
}
|
||||
|
||||
$cfg = Read-KeyValueFile -Path $giteaPath
|
||||
$pubPath = Join-Path $sshDir "$keyName.pub"
|
||||
if (-not (Test-Path -LiteralPath $pubPath)) {
|
||||
Write-Warn "Public key not found: $pubPath (run Setup-GitSsh first)"
|
||||
continue
|
||||
}
|
||||
|
||||
$title = "$machine-$keyName"
|
||||
Write-Step "Registering $keyName on $($cfg.GiteaHost)"
|
||||
Register-GiteaKey -Cfg $cfg -PublicKeyPath $pubPath -Title $title
|
||||
}
|
||||
|
||||
Write-Ok 'Gitea SSH key registration done'
|
||||
@@ -0,0 +1,91 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Rewrite cross-repo git host URLs in tracked text files for the target remote.
|
||||
.PARAMETER Target
|
||||
kalinamall | home | papatramp | github
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('github', 'kalinamall', 'home', 'papatramp')]
|
||||
[string]$Target,
|
||||
|
||||
[string]$Root = (Get-Location).Path
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Root = (Resolve-Path $Root).Path
|
||||
Set-Location $Root
|
||||
|
||||
if ($Target -eq 'home') { $Target = 'papatramp' }
|
||||
|
||||
switch ($Target) {
|
||||
'github' {
|
||||
$Base = 'https://github.com/PTah'
|
||||
$BlobSuffix = '/blob/main'
|
||||
}
|
||||
'kalinamall' {
|
||||
$Base = 'https://git.kalinamall.ru/PapaTramp'
|
||||
$BlobSuffix = '/src/branch/main'
|
||||
}
|
||||
'papatramp' {
|
||||
$Base = 'https://git.papatramp.ru/PapaTramp'
|
||||
$BlobSuffix = '/src/branch/main'
|
||||
}
|
||||
}
|
||||
|
||||
$BaseHost = $Base -replace '^https://', ''
|
||||
$sshTo = switch ($Target) {
|
||||
'github' { 'https://git.papatramp.ru/PapaTramp/' }
|
||||
'kalinamall' { 'ssh://git@git.papatramp.ru:2222/PapaTramp/' }
|
||||
default { 'ssh://git@git.papatramp.ru:2222/PapaTramp/' }
|
||||
}
|
||||
|
||||
$patterns = @(
|
||||
@{ From = 'https://github\.com/PTah/([^)/''"\s]+)/blob/main/'; To = "$Base/`${1}$BlobSuffix/" }
|
||||
@{ From = 'https://github\.com/PTah/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`${1}$BlobSuffix/" }
|
||||
@{ From = 'https://git\.kalinamall\.ru/PapaTramp/([^)/''"\s]+)/blob/main/'; To = "$Base/`${1}$BlobSuffix/" }
|
||||
@{ From = 'https://git\.kalinamall\.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`${1}$BlobSuffix/" }
|
||||
@{ From = 'https://git\.papatramp\.ru/PapaTramp/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`${1}$BlobSuffix/" }
|
||||
@{ From = 'https://git\.papatramp\.ru/PTah/([^)/''"\s]+)/src/branch/main/'; To = "$Base/`${1}$BlobSuffix/" }
|
||||
@{ From = 'https://github\.com/PTah/'; To = "$Base/" }
|
||||
@{ From = 'https://git\.kalinamall\.ru/PapaTramp/'; To = "$Base/" }
|
||||
@{ From = 'https://git\.papatramp\.ru/PapaTramp/'; To = "$Base/" }
|
||||
@{ From = 'https://git\.papatramp\.ru/PTah/'; To = "$Base/" }
|
||||
@{ From = 'ssh://git@git\.kalinamall\.ru:2222/PapaTramp/'; To = $sshTo }
|
||||
@{ From = 'ssh://git@git\.papatramp\.ru:2222/PapaTramp/'; To = $sshTo }
|
||||
@{ From = 'github\.com/PTah/'; To = "$BaseHost/" }
|
||||
@{ From = 'git\.kalinamall\.ru/PapaTramp/'; To = "$BaseHost/" }
|
||||
@{ From = 'git\.papatramp\.ru/PapaTramp/'; To = "$BaseHost/" }
|
||||
@{ From = 'git\.papatramp\.ru/PTah/'; To = "$BaseHost/" }
|
||||
)
|
||||
|
||||
$extensions = @('*.md', '*.json', '*.service', '*.example', '*.sh', '*.ps1', '*.yml', '*.yaml', '*.txt', '*.kt', '*.kts', '*.xml', '*.html', '*.css', '*.ts', '*.vue', '*.py')
|
||||
$files = @()
|
||||
if (Test-Path (Join-Path $Root '.git')) {
|
||||
$files = @(git -C $Root ls-files $extensions 2>$null | Where-Object { $_ -and (Test-Path (Join-Path $Root $_)) })
|
||||
}
|
||||
if ($files.Count -eq 0) {
|
||||
$files = @(Get-ChildItem -Path $Root -Recurse -File -Include $extensions |
|
||||
Where-Object { $_.FullName -notmatch '\\(\.git|node_modules|\.venv|venv|dist|build)\\' } |
|
||||
ForEach-Object { $_.FullName.Substring($Root.Length).TrimStart('\', '/') })
|
||||
}
|
||||
|
||||
$n = 0
|
||||
foreach ($file in $files) {
|
||||
$path = Join-Path $Root $file
|
||||
$content = [System.IO.File]::ReadAllText($path)
|
||||
$updated = $content
|
||||
foreach ($p in $patterns) {
|
||||
$updated = [regex]::Replace($updated, $p.From, $p.To)
|
||||
}
|
||||
if ($updated -ne $content) {
|
||||
# UTF-8 BOM for .ps1 (Windows PS 5.1); otherwise UTF-8 no BOM
|
||||
$bom = ($path -match '\.(ps1|psm1)$')
|
||||
[System.IO.File]::WriteAllText($path, $updated, [System.Text.UTF8Encoding]::new($bom))
|
||||
Write-Output "updated: $file"
|
||||
$n++
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Rewrite-GitHostUrls: target=$Target base=$Base files_changed=$n"
|
||||
@@ -0,0 +1,132 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Стандартные git remote: home (papatramp), kalinamall, github (PTah).
|
||||
|
||||
.EXAMPLE
|
||||
cd C:\Users\papat\Projects\MyRepo
|
||||
& "$HOME\CursorRules\scripts\Set-GitRemotes.ps1"
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Owner = 'PapaTramp',
|
||||
[string]$GithubOrg = 'PTah',
|
||||
[string]$Repo = '',
|
||||
[switch]$SkipKalinamall,
|
||||
[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 Test-GitRepo {
|
||||
git rev-parse --is-inside-work-tree 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'Run inside a git repository or pass -Repo.'
|
||||
}
|
||||
}
|
||||
|
||||
function Get-RepoNameFromRemoteUrl {
|
||||
param([string]$Url)
|
||||
if ($Url -match '/([^/]+?)(?:\.git)?/?$') {
|
||||
return $Matches[1]
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-RepoName {
|
||||
param([string]$Name)
|
||||
if ($Name) { return $Name }
|
||||
|
||||
foreach ($remote in @('kalinamall', 'home', 'github', 'origin')) {
|
||||
$url = git remote get-url $remote 2>$null
|
||||
if ($url) {
|
||||
$fromUrl = Get-RepoNameFromRemoteUrl -Url $url.Trim()
|
||||
if ($fromUrl) { return $fromUrl }
|
||||
}
|
||||
}
|
||||
|
||||
return (Split-Path -Leaf (git rev-parse --show-toplevel))
|
||||
}
|
||||
|
||||
function Get-OriginRemoteTarget {
|
||||
$url = (git remote get-url origin 2>$null)
|
||||
if (-not $url) { return $null }
|
||||
$lower = $url.ToLower()
|
||||
if ($lower -match 'kalinamall') { return 'kalinamall' }
|
||||
if ($lower -match 'papatramp') { return 'home' }
|
||||
if ($lower -match 'github\.com') { return 'github' }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Set-Remote {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$Url
|
||||
)
|
||||
|
||||
$existing = @(git remote 2>$null)
|
||||
$has = $existing -contains $Name
|
||||
|
||||
if ($WhatIf) {
|
||||
Write-Step "WhatIf: remote $Name -> $Url"
|
||||
return
|
||||
}
|
||||
|
||||
if ($has) {
|
||||
$current = (git remote get-url $Name 2>$null).Trim()
|
||||
if ($current -eq $Url) {
|
||||
Write-Ok "remote $Name OK"
|
||||
return
|
||||
}
|
||||
git remote set-url $Name $Url
|
||||
Write-Ok "remote $Name updated -> $Url"
|
||||
return
|
||||
}
|
||||
|
||||
if ($existing -contains 'origin') {
|
||||
$originTarget = Get-OriginRemoteTarget
|
||||
if ($originTarget -eq $Name) {
|
||||
git remote rename origin $Name
|
||||
git remote set-url $Name $Url
|
||||
Write-Ok "remote origin renamed to $Name -> $Url"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$legacy = @{
|
||||
home = @('gitea', 'papatramp', 'origin-home')
|
||||
github = @()
|
||||
kalinamall = @('gitea-kalinamall', 'kalina')
|
||||
}
|
||||
foreach ($old in $legacy[$Name]) {
|
||||
if ($existing -contains $old) {
|
||||
git remote rename $old $Name
|
||||
git remote set-url $Name $Url
|
||||
Write-Ok "remote $old renamed to $Name -> $Url"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
git remote add $Name $Url
|
||||
Write-Ok "remote $Name added -> $Url"
|
||||
}
|
||||
|
||||
Test-GitRepo
|
||||
$repoName = Get-RepoName -Name $Repo
|
||||
|
||||
$homeUrl = "ssh://git@git.papatramp.ru:2222/${Owner}/${repoName}.git"
|
||||
$kalinamallUrl = "ssh://git@git.kalinamall.ru:2222/${Owner}/${repoName}.git"
|
||||
$githubUrl = "git@github.com:${GithubOrg}/${repoName}.git"
|
||||
|
||||
Write-Step "Repo: $repoName"
|
||||
Set-Remote -Name 'home' -Url $homeUrl
|
||||
Set-Remote -Name 'kalinamall' -Url $kalinamallUrl
|
||||
Set-Remote -Name 'github' -Url $githubUrl
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Remotes:' -ForegroundColor Magenta
|
||||
git remote -v
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
# Стандартные git remote: home (papatramp), kalinamall, github (PTah).
|
||||
set -euo pipefail
|
||||
|
||||
OWNER="${OWNER:-PapaTramp}"
|
||||
GITHUB_ORG="${GITHUB_ORG:-PTah}"
|
||||
REPO="${REPO:-}"
|
||||
WHAT_IF="${WHAT_IF:-0}"
|
||||
|
||||
log_step() { printf '[*] %s\n' "$1"; }
|
||||
log_ok() { printf '[+] %s\n' "$1"; }
|
||||
|
||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
echo 'Run inside a git repository or set REPO.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
repo_name_from_url() {
|
||||
local url="$1" name=""
|
||||
if [[ "$url" =~ /([^/]+)/?$ ]]; then
|
||||
name="${BASH_REMATCH[1]}"
|
||||
name="${name%.git}"
|
||||
printf '%s\n' "$name"
|
||||
fi
|
||||
}
|
||||
|
||||
repo_name="${REPO:-}"
|
||||
if [[ -z "$repo_name" ]]; then
|
||||
for remote in kalinamall home github origin; do
|
||||
if url="$(git remote get-url "$remote" 2>/dev/null)"; then
|
||||
repo_name="$(repo_name_from_url "$url")"
|
||||
[[ -n "$repo_name" ]] && break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
repo_name="${repo_name:-$(basename "$(git rev-parse --show-toplevel)")}"
|
||||
repo_name="${repo_name%.git}"
|
||||
home_url="ssh://git@git.papatramp.ru:2222/${OWNER}/${repo_name}.git"
|
||||
kalinamall_url="ssh://git@git.kalinamall.ru:2222/${OWNER}/${repo_name}.git"
|
||||
github_url="git@github.com:${GITHUB_ORG}/${repo_name}.git"
|
||||
|
||||
origin_remote_target() {
|
||||
local url
|
||||
url="$(git remote get-url origin 2>/dev/null | tr '[:upper:]' '[:lower:]' || true)"
|
||||
[[ -z "$url" ]] && return 0
|
||||
if [[ "$url" == *kalinamall* ]]; then echo kalinamall; return 0; fi
|
||||
if [[ "$url" == *papatramp* ]]; then echo home; return 0; fi
|
||||
if [[ "$url" == *github.com* ]]; then echo github; return 0; fi
|
||||
}
|
||||
|
||||
set_remote() {
|
||||
local name="$1"
|
||||
local url="$2"
|
||||
local existing current old origin_target
|
||||
|
||||
if [[ "$WHAT_IF" == '1' ]]; then
|
||||
log_step "WhatIf: remote $name -> $url"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if git remote | grep -Fxq "$name"; then
|
||||
current="$(git remote get-url "$name" | tr -d '\r\n')"
|
||||
if [[ "$current" == "$url" ]]; then
|
||||
log_ok "remote $name OK"
|
||||
return 0
|
||||
fi
|
||||
git remote set-url "$name" "$url"
|
||||
log_ok "remote $name updated -> $url"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if git remote | grep -Fxq origin; then
|
||||
origin_target="$(origin_remote_target || true)"
|
||||
if [[ "$origin_target" == "$name" ]]; then
|
||||
git remote rename origin "$name"
|
||||
git remote set-url "$name" "$url"
|
||||
log_ok "remote origin renamed to $name -> $url"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$name" in
|
||||
home) legacy=(gitea papatramp origin-home) ;;
|
||||
github) legacy=() ;;
|
||||
kalinamall) legacy=(gitea-kalinamall kalina) ;;
|
||||
*) legacy=() ;;
|
||||
esac
|
||||
|
||||
if ((${#legacy[@]} > 0)); then
|
||||
for old in "${legacy[@]}"; do
|
||||
if git remote | grep -Fxq "$old"; then
|
||||
git remote rename "$old" "$name"
|
||||
git remote set-url "$name" "$url"
|
||||
log_ok "remote $old renamed to $name -> $url"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
git remote add "$name" "$url"
|
||||
log_ok "remote $name added -> $url"
|
||||
}
|
||||
|
||||
log_step "Repo: $repo_name"
|
||||
set_remote home "$home_url"
|
||||
set_remote kalinamall "$kalinamall_url"
|
||||
set_remote github "$github_url"
|
||||
|
||||
printf '\n'
|
||||
git remote -v
|
||||
@@ -0,0 +1,192 @@
|
||||
#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-SshConfigText {
|
||||
param(
|
||||
[string]$ExistingText,
|
||||
[string]$HostAlias,
|
||||
[string]$Block
|
||||
)
|
||||
|
||||
$begin = "# BEGIN Setup-SshAccess:$HostAlias"
|
||||
$end = "# END Setup-SshAccess:$HostAlias"
|
||||
$existing = $ExistingText
|
||||
|
||||
$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-SshConfigText -ExistingText $configText -HostAlias $hostAlias -Block $block
|
||||
Write-Ok "ssh config: $hostAlias"
|
||||
}
|
||||
|
||||
if (-not $WhatIf) {
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
|
||||
[System.IO.File]::WriteAllText($sshConfigPath, $configText.TrimEnd() + "`n", $utf8NoBom)
|
||||
|
||||
Get-Service ssh-agent -ErrorAction SilentlyContinue | Where-Object { $_.Status -ne 'Running' } |
|
||||
ForEach-Object { Start-Service ssh-agent -ErrorAction SilentlyContinue | Out-Null }
|
||||
|
||||
foreach ($keyName in $profileKeys.Keys) {
|
||||
$keyPath = Join-Path $sshDir $keyName
|
||||
if (Test-Path -LiteralPath $keyPath) {
|
||||
try { ssh-add $keyPath 2>$null | Out-Null } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Ok 'SSH setup complete'
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
# Настройка git на машине: user, SSH, Gitea keys, clone/update Rules.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="${ROOT:?ROOT is required}"
|
||||
RULES_DEST="${RULES_DEST:-$HOME/CursorRules}"
|
||||
CLONE_URL="${CLONE_URL:-ssh://git@git.papatramp.ru:2222/PapaTramp/Rules.git}"
|
||||
HTTPS_CLONE_URL="${HTTPS_CLONE_URL:-https://git.papatramp.ru/PapaTramp/Rules.git}"
|
||||
SKIP_CLONE="${SKIP_CLONE:-0}"
|
||||
SKIP_PROFILE="${SKIP_PROFILE:-0}"
|
||||
SKIP_GIT_SETUP="${SKIP_GIT_SETUP:-0}"
|
||||
WHAT_IF="${WHAT_IF:-0}"
|
||||
|
||||
log_step() { printf '[*] %s\n' "$1"; }
|
||||
log_ok() { printf '[+] %s\n' "$1"; }
|
||||
log_warn() { printf '[!] %s\n' "$1"; }
|
||||
|
||||
CONFIG_DIR="$ROOT/local"
|
||||
[[ -d "$CONFIG_DIR" ]] || { echo "Bootstrap config not found: $CONFIG_DIR" >&2; exit 1; }
|
||||
|
||||
test_machine_git_ready() {
|
||||
local hosts_config="$CONFIG_DIR/git-ssh-hosts.conf"
|
||||
local ssh_config="$HOME/.ssh/config"
|
||||
[[ -f "$hosts_config" && -f "$ssh_config" ]] || return 1
|
||||
grep -q '^Host git.kalinamall.ru$' "$ssh_config" || return 1
|
||||
|
||||
local line current_profile='' key_name
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line%%#*}"
|
||||
line="${line%"${line##*[![:space:]]}"}"
|
||||
[[ -z "$line" ]] && continue
|
||||
if [[ "$line" =~ ^\[profile:(.+)\]$ ]]; then
|
||||
current_profile="${BASH_REMATCH[1]}"
|
||||
continue
|
||||
fi
|
||||
if [[ "$line" =~ ^\[host: ]]; then
|
||||
current_profile=''
|
||||
continue
|
||||
fi
|
||||
if [[ "$line" == KeyName=* && -n "$current_profile" ]]; then
|
||||
key_name="${line#KeyName=}"
|
||||
[[ -f "$HOME/.ssh/$key_name" ]] || return 1
|
||||
fi
|
||||
done < "$hosts_config"
|
||||
return 0
|
||||
}
|
||||
|
||||
ensure_rules_repository() {
|
||||
if [[ -d "$RULES_DEST/.git" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -e "$RULES_DEST" ]]; then
|
||||
local backup="${RULES_DEST}.bak-$(date +%Y%m%d-%H%M%S)"
|
||||
log_warn "Replacing non-git folder: $RULES_DEST -> $backup"
|
||||
if [[ "$WHAT_IF" != '1' ]]; then
|
||||
mv "$RULES_DEST" "$backup"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_step "Cloning Rules via SSH: $CLONE_URL"
|
||||
if [[ "$WHAT_IF" == '1' ]]; then
|
||||
log_step "WhatIf: clone -> $RULES_DEST"
|
||||
return 0
|
||||
fi
|
||||
if git clone --depth 1 "$CLONE_URL" "$RULES_DEST" 2>/dev/null; then
|
||||
log_ok "Cloned to $RULES_DEST"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warn 'SSH clone failed - trying HTTPS (enter Gitea login once)'
|
||||
git clone --depth 1 "$HTTPS_CLONE_URL" "$RULES_DEST"
|
||||
log_ok "Cloned via HTTPS to $RULES_DEST"
|
||||
}
|
||||
|
||||
ensure_init_cursor_profile() {
|
||||
local hook="source \"$RULES_DEST/init-cursor.sh\""
|
||||
local profile
|
||||
for profile in "$HOME/.bashrc" "$HOME/.zshrc"; do
|
||||
[[ -f "$profile" ]] || continue
|
||||
if [[ "$WHAT_IF" == '1' ]]; then
|
||||
log_step "WhatIf: profile hook -> $profile"
|
||||
continue
|
||||
fi
|
||||
if ! grep -Fq "$hook" "$profile" 2>/dev/null; then
|
||||
printf '\n%s\n' "$hook" >> "$profile"
|
||||
log_ok "Profile updated: $profile"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
git_ready=0
|
||||
repo_ready=0
|
||||
test_machine_git_ready && git_ready=1
|
||||
[[ -d "$RULES_DEST/.git" ]] && repo_ready=1
|
||||
|
||||
if [[ "$git_ready" -eq 0 && "$SKIP_GIT_SETUP" != '1' ]]; then
|
||||
log_step 'Machine bootstrap: git + SSH'
|
||||
if [[ -f "$CONFIG_DIR/git-user.conf" ]]; then
|
||||
while IFS= read -r kv; do
|
||||
key="${kv%%=*}"
|
||||
value="${kv#*=}"
|
||||
case "$key" in
|
||||
UserName) git config --global user.name "$value"; log_ok "git user.name = $value" ;;
|
||||
UserEmail) git config --global user.email "$value"; log_ok "git user.email = $value" ;;
|
||||
esac
|
||||
done < <(grep -v '^#' "$CONFIG_DIR/git-user.conf" | grep '=' || true)
|
||||
fi
|
||||
export CONFIG_DIR WHAT_IF
|
||||
bash "$ROOT/scripts/setup-git-ssh.sh"
|
||||
bash "$ROOT/scripts/register-gitea-ssh-keys.sh"
|
||||
log_step 'Testing git@git.kalinamall.ru'
|
||||
ssh -T -o BatchMode=yes -o ConnectTimeout=10 git@git.kalinamall.ru || true
|
||||
elif [[ "$git_ready" -eq 1 ]]; then
|
||||
log_ok 'Git SSH already configured'
|
||||
fi
|
||||
|
||||
if [[ "$SKIP_CLONE" != '1' && "$repo_ready" -eq 0 ]]; then
|
||||
ensure_rules_repository
|
||||
elif [[ "$repo_ready" -eq 1 ]]; then
|
||||
log_ok "CursorRules repo OK: $RULES_DEST"
|
||||
fi
|
||||
|
||||
if [[ "$SKIP_PROFILE" != '1' ]]; then
|
||||
ensure_init_cursor_profile
|
||||
fi
|
||||
|
||||
log_ok 'Machine bootstrap complete'
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env bash
|
||||
# Publish current main tree to github as orphan + hard sanitize (force).
|
||||
# Usage: publish-to-github.sh [source_repo] [version]
|
||||
set -euo pipefail
|
||||
|
||||
SOURCE_REPO="${1:-$(pwd)}"
|
||||
VERSION="${2:-}"
|
||||
REMOTE_NAME="${REMOTE_NAME:-github}"
|
||||
SOURCE_REPO="$(cd "$SOURCE_REPO" && pwd)"
|
||||
cd "$SOURCE_REPO"
|
||||
|
||||
REMOTE_URL="$(git remote get-url "$REMOTE_NAME")"
|
||||
REPO_NAME="$(basename "$(git rev-parse --show-toplevel)")"
|
||||
WORK="${TMPDIR:-/tmp}/gh-orphan-${REPO_NAME}-$$"
|
||||
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$WORK"
|
||||
git archive --format=tar main | tar -x -C "$WORK"
|
||||
rm -rf "$WORK/.cursor" "$WORK/.cursorignore" 2>/dev/null || true
|
||||
|
||||
# URL + PII + Cursor sanitize
|
||||
export LC_ALL=C
|
||||
while IFS= read -r -d '' f; do
|
||||
case "$f" in
|
||||
*.md|*.py|*.ts|*.vue|*.json|*.sh|*.ps1|*.yml|*.yaml|*.example|*.service|*.txt|*.html|*.css|*.kt|*.kts|*.xml|*.properties|*.gradle|*.pro) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
perl -i -pe '
|
||||
s#https://git\.kalinamall\.ru/PapaTramp/([^)/\s\"\x27`]+)/src/branch/main/#https://git.papatramp.ru/PapaTramp/$1/src/branch/main/#g;
|
||||
s#https://git\.kalinamall\.ru/PapaTramp/([^)/\s\"\x27`]+)/blob/main/#https://git.papatramp.ru/PapaTramp/$1/src/branch/main/#g;
|
||||
s#https://git\.kalinamall\.ru/PapaTramp/#https://git.papatramp.ru/PapaTramp/#g;
|
||||
s#git\.kalinamall\.ru/PapaTramp/#git.papatramp.ru/PapaTramp/#g;
|
||||
s#https://git\.papatramp\.ru/(PapaTramp|PTah)/#https://git.papatramp.ru/PapaTramp/#g;
|
||||
s#git\.papatramp\.ru/(PapaTramp|PTah)/#git.papatramp.ru/PapaTramp/#g;
|
||||
s#ssh://git\@git\.kalinamall\.ru:2222/PapaTramp/#https://git.papatramp.ru/PapaTramp/#g;
|
||||
s#ssh://git\@git\.papatramp\.ru:2222/PapaTramp/#https://git.papatramp.ru/PapaTramp/#g;
|
||||
s#sac-api\.kalinamall\.ru#sac-api.example.com#g;
|
||||
s#sac\.kalinamall\.ru#sac.example.com#g;
|
||||
s#ext\.kalinamall\.ru#ext.example.com#g;
|
||||
s#\*\.kalinamall\.ru#*.example.com#g;
|
||||
s#kalinamall\.ru#example.com#g;
|
||||
s#papatramp\.lan#example.lan#g;
|
||||
s#192\.168\.\d+\.\d+#10.0.0.1#g;
|
||||
s#Co-authored-by:\s*Cursor\s*<cursoragent\@cursor\.com>\s*##g;
|
||||
s#Made-with:\s*Cursor\s*##g;
|
||||
s#Cursor Agent##g;
|
||||
s#Cursor IDE##g;
|
||||
s#cursoragent\@cursor\.com##g;
|
||||
s#B26\\#CONTOSO\\#g;
|
||||
s#B26/#CONTOSO/#g;
|
||||
s#K6A-DC\d#WIN-DC01#g;
|
||||
s#UNMS Kalina#Example Site#g;
|
||||
s#k\.khodasevich#user1#g;
|
||||
s#\bkalinamall\b#example#g;
|
||||
s#\bpapatramp\b#jdoe#g;
|
||||
' "$f"
|
||||
done < <(find "$WORK" -type f -print0)
|
||||
|
||||
HITS=0
|
||||
while IFS= read -r -d '' f; do
|
||||
case "$f" in
|
||||
*.md|*.py|*.ts|*.vue|*.json|*.sh|*.ps1|*.yml|*.yaml|*.example|*.service|*.txt|*.html|*.css|*.kt|*.kts|*.xml|*.properties|*.gradle|*.pro) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
if grep -nE 'git\.kalinamall\.ru|git\.papatramp\.ru|papatramp\.lan|192\.168\.[0-9]+\.[0-9]+|cursoragent@cursor\.com|Co-authored-by:[[:space:]]*Cursor|Made-with:[[:space:]]*Cursor|\bkalinamall\b|\bpapatramp\b|K6A-DC|B26\\\\' "$f" >/dev/null 2>&1; then
|
||||
echo "FORBIDDEN in ${f#$WORK/}" >&2
|
||||
grep -nE 'git\.kalinamall\.ru|git\.papatramp\.ru|papatramp\.lan|192\.168\.[0-9]+\.[0-9]+|cursoragent@cursor\.com|Co-authored-by:[[:space:]]*Cursor|Made-with:[[:space:]]*Cursor|\bkalinamall\b|\bpapatramp\b|K6A-DC|B26\\\\' "$f" >&2 || true
|
||||
HITS=$((HITS + 1))
|
||||
fi
|
||||
done < <(find "$WORK" -type f -print0)
|
||||
|
||||
if [[ "$HITS" -gt 0 ]]; then
|
||||
echo "sanitization failed: $HITS file(s)" >&2
|
||||
rm -rf "$WORK"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$WORK"
|
||||
git init -b main >/dev/null
|
||||
git add -A
|
||||
if [[ -n "$VERSION" ]]; then
|
||||
MSG="chore(github): sync public mirror ($VERSION)"
|
||||
else
|
||||
MSG='chore(github): sync public mirror'
|
||||
fi
|
||||
git -c user.name='PTah' -c user.email='papatramp@gmail.com' commit -m "$MSG" >/dev/null
|
||||
git remote add target "$REMOTE_URL"
|
||||
git push target main --force
|
||||
echo "OK -> $REMOTE_URL (orphan, force)"
|
||||
|
||||
cd "$SOURCE_REPO"
|
||||
rm -rf "$WORK"
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# Mirror kalinamall/main to remote "home" with papatramp URLs (force).
|
||||
set -euo pipefail
|
||||
|
||||
SOURCE_REPO="${1:-$(pwd)}"
|
||||
BRANCH="${2:-main}"
|
||||
REMOTE_NAME="${3:-home}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REWRITE="$SCRIPT_DIR/rewrite-git-host-urls.sh"
|
||||
SOURCE_REPO="$(cd "$SOURCE_REPO" && pwd)"
|
||||
cd "$SOURCE_REPO"
|
||||
|
||||
REPO_NAME="$(basename "$(git rev-parse --show-toplevel)")"
|
||||
REMOTE_URL="$(git remote get-url "$REMOTE_NAME")"
|
||||
WORK="${TMPDIR:-/tmp}/home-mirror-${REPO_NAME}-$$"
|
||||
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$WORK"
|
||||
git archive --format=tar "$BRANCH" | tar -x -C "$WORK"
|
||||
bash "$REWRITE" home "$WORK"
|
||||
|
||||
cd "$WORK"
|
||||
git init -b main >/dev/null
|
||||
git add -A
|
||||
SHA="$(git -C "$SOURCE_REPO" rev-parse --short HEAD)"
|
||||
git -c user.name='PTah' -c user.email='papatramp@gmail.com' \
|
||||
commit -m "chore(home): mirror from kalinamall ($SHA) with papatramp URLs" >/dev/null
|
||||
git remote add target "$REMOTE_URL"
|
||||
git push target main --force
|
||||
echo "OK -> $REMOTE_URL (home mirror, force)"
|
||||
|
||||
cd "$SOURCE_REPO"
|
||||
rm -rf "$WORK"
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
# Регистрирует публичные SSH-ключи в Gitea через API.
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_DIR="${CONFIG_DIR:-$HOME/CursorRules/local}"
|
||||
HOSTS_CONFIG="${HOSTS_CONFIG:-$CONFIG_DIR/git-ssh-hosts.conf}"
|
||||
WHAT_IF="${WHAT_IF:-0}"
|
||||
|
||||
log_step() { printf '[*] %s\n' "$1"; }
|
||||
log_ok() { printf '[+] %s\n' "$1"; }
|
||||
log_warn() { printf '[!] %s\n' "$1"; }
|
||||
|
||||
if [[ ! -f "$HOSTS_CONFIG" ]]; then
|
||||
echo "Config not found: $HOSTS_CONFIG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read_kv_file() {
|
||||
local file="$1"
|
||||
local line key value
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ "$line" != *=* ]] && continue
|
||||
key="${line%%=*}"
|
||||
value="${line#*=}"
|
||||
key="${key%"${key##*[![:space:]]}"}"
|
||||
value="${value#"${value%%[![:space:]]*}"}"
|
||||
printf '%s=%s\n' "$key" "$value"
|
||||
done < "$file"
|
||||
}
|
||||
|
||||
declare -A PROFILE_KEYNAME PROFILE_GITEA
|
||||
current_section=''
|
||||
current_name=''
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line%%#*}"
|
||||
line="${line%"${line##*[![:space:]]}"}"
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
if [[ "$line" =~ ^\[profile:(.+)\]$ ]]; then
|
||||
current_section='profile'
|
||||
current_name="${BASH_REMATCH[1]}"
|
||||
continue
|
||||
fi
|
||||
if [[ "$line" =~ ^\[host: ]]; then
|
||||
current_section=''
|
||||
continue
|
||||
fi
|
||||
[[ "$line" != *=* || "$current_section" != 'profile' ]] && continue
|
||||
|
||||
key="${line%%=*}"
|
||||
value="${line#*=}"
|
||||
key="${key%"${key##*[![:space:]]}"}"
|
||||
value="${value#"${value%%[![:space:]]*}"}"
|
||||
|
||||
case "$key" in
|
||||
KeyName) PROFILE_KEYNAME["$current_name"]="$value" ;;
|
||||
GiteaConfig) PROFILE_GITEA["$current_name"]="$value" ;;
|
||||
esac
|
||||
done < "$HOSTS_CONFIG"
|
||||
|
||||
SSH_DIR="$HOME/.ssh"
|
||||
MACHINE="$(hostname -s 2>/dev/null || hostname)"
|
||||
declare -A REGISTERED
|
||||
|
||||
register_gitea_key() {
|
||||
local cfg_file="$1"
|
||||
local pub_path="$2"
|
||||
local title="$3"
|
||||
|
||||
declare -A CFG=()
|
||||
local kv key value
|
||||
while IFS= read -r kv; do
|
||||
key="${kv%%=*}"
|
||||
value="${kv#*=}"
|
||||
CFG["$key"]="$value"
|
||||
done < <(read_kv_file "$cfg_file")
|
||||
|
||||
if [[ -z "${CFG[GiteaApiPassword]:-}" ]]; then
|
||||
log_warn "$title : GiteaApiPassword empty — add key manually in Gitea UI"
|
||||
cat "$pub_path"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local base="${CFG[GiteaApiUrl]%/}"
|
||||
local auth user pass pub body response
|
||||
user="${CFG[GiteaApiUser]}"
|
||||
pass="${CFG[GiteaApiPassword]}"
|
||||
auth="$(printf '%s:%s' "$user" "$pass" | base64 -w0 2>/dev/null || printf '%s:%s' "$user" "$pass" | base64)"
|
||||
pub="$(tr -d '\r\n' < "$pub_path")"
|
||||
|
||||
response="$(curl -fsS -H "Authorization: Basic $auth" "$base/api/v1/user/keys" 2>/dev/null || true)"
|
||||
if [[ -n "$response" ]] && printf '%s' "$response" | grep -Fq "$pub"; then
|
||||
log_ok "$title : key already registered"
|
||||
return 0
|
||||
fi
|
||||
|
||||
body="$(printf '{"title":"%s","key":"%s"}' "$title" "$pub")"
|
||||
|
||||
if [[ "$WHAT_IF" == '1' ]]; then
|
||||
log_step "WhatIf: POST $base/api/v1/user/keys ($title)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
curl -fsS -X POST -H "Authorization: Basic $auth" -H 'Content-Type: application/json' \
|
||||
-d "$body" "$base/api/v1/user/keys" >/dev/null
|
||||
log_ok "$title : registered"
|
||||
}
|
||||
|
||||
for profile in "${!PROFILE_GITEA[@]}"; do
|
||||
gitea_config="${PROFILE_GITEA[$profile]}"
|
||||
[[ -z "$gitea_config" ]] && continue
|
||||
|
||||
key_name="${PROFILE_KEYNAME[$profile]}"
|
||||
[[ -z "$key_name" || -n "${REGISTERED[$key_name]:-}" ]] && continue
|
||||
REGISTERED["$key_name"]=1
|
||||
|
||||
gitea_path="$CONFIG_DIR/$gitea_config"
|
||||
pub_path="$SSH_DIR/$key_name.pub"
|
||||
|
||||
if [[ ! -f "$gitea_path" ]]; then
|
||||
log_warn "Gitea config not found: $gitea_path"
|
||||
continue
|
||||
fi
|
||||
if [[ ! -f "$pub_path" ]]; then
|
||||
log_warn "Public key not found: $pub_path (run setup-git-ssh.sh first)"
|
||||
continue
|
||||
fi
|
||||
|
||||
title="$MACHINE-$key_name"
|
||||
log_step "Registering $key_name"
|
||||
register_gitea_key "$gitea_path" "$pub_path" "$title"
|
||||
done
|
||||
|
||||
log_ok 'Gitea SSH key registration done'
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
# Rewrite cross-repo git host URLs in tracked text files.
|
||||
# Usage: rewrite-git-host-urls.sh github|kalinamall|home|papatramp [root]
|
||||
set -euo pipefail
|
||||
|
||||
TARGET="${1:-}"
|
||||
ROOT="${2:-$(pwd)}"
|
||||
if [[ -z "$TARGET" ]]; then
|
||||
echo "Usage: $0 github|kalinamall|home|papatramp [root]" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ "$TARGET" == "home" ]]; then TARGET=papatramp; fi
|
||||
ROOT="$(cd "$ROOT" && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
case "$TARGET" in
|
||||
github)
|
||||
BASE='https://github.com/PTah'
|
||||
BLOB='/blob/main'
|
||||
SSHTO='https://git.papatramp.ru/PapaTramp/'
|
||||
;;
|
||||
kalinamall)
|
||||
BASE='https://git.kalinamall.ru/PapaTramp'
|
||||
BLOB='/src/branch/main'
|
||||
SSHTO='ssh://git@git.papatramp.ru:2222/PapaTramp/'
|
||||
;;
|
||||
papatramp)
|
||||
BASE='https://git.papatramp.ru/PapaTramp'
|
||||
BLOB='/src/branch/main'
|
||||
SSHTO='ssh://git@git.papatramp.ru:2222/PapaTramp/'
|
||||
;;
|
||||
*)
|
||||
echo "Unknown target: $TARGET" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
BASE_HOST="${BASE#https://}"
|
||||
N=0
|
||||
|
||||
export BASE BLOB SSHTO BASE_HOST
|
||||
|
||||
rewrite_file() {
|
||||
local f="$1"
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
perl -pe '
|
||||
s#https://github\.com/PTah/([^)/\x27\"\s]+)/blob/main/#$ENV{BASE}/$1$ENV{BLOB}/#g;
|
||||
s#https://github\.com/PTah/([^)/\x27\"\s]+)/src/branch/main/#$ENV{BASE}/$1$ENV{BLOB}/#g;
|
||||
s#https://git\.kalinamall\.ru/PapaTramp/([^)/\x27\"\s]+)/blob/main/#$ENV{BASE}/$1$ENV{BLOB}/#g;
|
||||
s#https://git\.kalinamall\.ru/PapaTramp/([^)/\x27\"\s]+)/src/branch/main/#$ENV{BASE}/$1$ENV{BLOB}/#g;
|
||||
s#https://git\.papatramp\.ru/PapaTramp/([^)/\x27\"\s]+)/src/branch/main/#$ENV{BASE}/$1$ENV{BLOB}/#g;
|
||||
s#https://git\.papatramp\.ru/PTah/([^)/\x27\"\s]+)/src/branch/main/#$ENV{BASE}/$1$ENV{BLOB}/#g;
|
||||
s#https://github\.com/PTah/#$ENV{BASE}/#g;
|
||||
s#https://git\.kalinamall\.ru/PapaTramp/#$ENV{BASE}/#g;
|
||||
s#https://git\.papatramp\.ru/PapaTramp/#$ENV{BASE}/#g;
|
||||
s#https://git\.papatramp\.ru/PTah/#$ENV{BASE}/#g;
|
||||
s#ssh://git\@git\.kalinamall\.ru:2222/PapaTramp/#$ENV{SSHTO}#g;
|
||||
s#ssh://git\@git\.papatramp\.ru:2222/PapaTramp/#$ENV{SSHTO}#g;
|
||||
s#github\.com/PTah/#$ENV{BASE_HOST}/#g;
|
||||
s#git\.kalinamall\.ru/PapaTramp/#$ENV{BASE_HOST}/#g;
|
||||
s#git\.papatramp\.ru/PapaTramp/#$ENV{BASE_HOST}/#g;
|
||||
s#git\.papatramp\.ru/PTah/#$ENV{BASE_HOST}/#g;
|
||||
' "$f" > "$tmp"
|
||||
if ! cmp -s "$f" "$tmp"; then
|
||||
mv "$tmp" "$f"
|
||||
echo "updated: $f"
|
||||
N=$((N + 1))
|
||||
else
|
||||
rm -f "$tmp"
|
||||
fi
|
||||
}
|
||||
|
||||
mapfile -t FILES < <(git -C "$ROOT" ls-files '*.md' '*.json' '*.service' '*.example' '*.sh' '*.ps1' '*.yml' '*.yaml' '*.txt' '*.kt' '*.kts' '*.xml' '*.html' '*.css' '*.ts' '*.vue' '*.py' 2>/dev/null || true)
|
||||
if [[ ${#FILES[@]} -eq 0 ]]; then
|
||||
mapfile -t FILES < <(find "$ROOT" -type f \( -name '*.md' -o -name '*.ps1' -o -name '*.sh' -o -name '*.yml' -o -name '*.yaml' -o -name '*.json' -o -name '*.txt' \) ! -path '*/.git/*' ! -path '*/node_modules/*' || true)
|
||||
fi
|
||||
|
||||
for f in "${FILES[@]}"; do
|
||||
[[ -f "$f" ]] || f="$ROOT/$f"
|
||||
[[ -f "$f" ]] || continue
|
||||
rewrite_file "$f"
|
||||
done
|
||||
|
||||
echo "Rewrite-GitHostUrls: target=$TARGET base=$BASE files_changed=$N"
|
||||
@@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env bash
|
||||
# Генерирует SSH-ключи и ~/.ssh/config для Git-хостов из local/git-ssh-hosts.conf.
|
||||
# Совместим с macOS bash 3.2 (без declare -A).
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_DIR="${CONFIG_DIR:-$HOME/CursorRules/local}"
|
||||
HOSTS_CONFIG="${HOSTS_CONFIG:-$CONFIG_DIR/git-ssh-hosts.conf}"
|
||||
WHAT_IF="${WHAT_IF:-0}"
|
||||
|
||||
log_step() { printf '[*] %s\n' "$1"; }
|
||||
log_ok() { printf '[+] %s\n' "$1"; }
|
||||
log_warn() { printf '[!] %s\n' "$1"; }
|
||||
|
||||
cfg_key() { printf '%s' "$1" | tr '.-' '__'; }
|
||||
|
||||
set_profile_field() {
|
||||
local profile="$1" field="$2" value="$3"
|
||||
eval "__pf_$(cfg_key "$profile")_${field}=\"\$value\""
|
||||
}
|
||||
|
||||
get_profile_field() {
|
||||
local profile="$1" field="$2"
|
||||
eval "printf '%s' \"\${__pf_$(cfg_key "$profile")_${field}:-}\""
|
||||
}
|
||||
|
||||
set_host_field() {
|
||||
local host="$1" field="$2" value="$3"
|
||||
eval "__hf_$(cfg_key "$host")_${field}=\"\$value\""
|
||||
}
|
||||
|
||||
get_host_field() {
|
||||
local host="$1" field="$2"
|
||||
eval "printf '%s' \"\${__hf_$(cfg_key "$host")_${field}:-}\""
|
||||
}
|
||||
|
||||
if [[ ! -f "$HOSTS_CONFIG" ]]; then
|
||||
echo "Config not found: $HOSTS_CONFIG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SSH_DIR="$HOME/.ssh"
|
||||
SSH_CONFIG="$SSH_DIR/config"
|
||||
mkdir -p "$SSH_DIR"
|
||||
chmod 700 "$SSH_DIR"
|
||||
|
||||
PROFILE_LIST=()
|
||||
HOST_LIST=()
|
||||
current_section=''
|
||||
current_name=''
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line%%#*}"
|
||||
line="${line%"${line##*[![:space:]]}"}"
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
if [[ "$line" =~ ^\[profile:(.+)\]$ ]]; then
|
||||
current_section='profile'
|
||||
current_name="${BASH_REMATCH[1]}"
|
||||
PROFILE_LIST+=("$current_name")
|
||||
continue
|
||||
fi
|
||||
if [[ "$line" =~ ^\[host:(.+)\]$ ]]; then
|
||||
current_section='host'
|
||||
current_name="${BASH_REMATCH[1]}"
|
||||
HOST_LIST+=("$current_name")
|
||||
set_host_field "$current_name" HostName ''
|
||||
continue
|
||||
fi
|
||||
if [[ "$line" != *=* ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
key="${line%%=*}"
|
||||
value="${line#*=}"
|
||||
key="${key%"${key##*[![:space:]]}"}"
|
||||
value="${value#"${value%%[![:space:]]*}"}"
|
||||
|
||||
if [[ "$current_section" == 'profile' ]]; then
|
||||
case "$key" in
|
||||
KeyName|KeyComment|GiteaConfig) set_profile_field "$current_name" "$key" "$value" ;;
|
||||
esac
|
||||
elif [[ "$current_section" == 'host' ]]; then
|
||||
case "$key" in
|
||||
HostName|Port|User|Profile) set_host_field "$current_name" "$key" "$value" ;;
|
||||
esac
|
||||
fi
|
||||
done < "$HOSTS_CONFIG"
|
||||
|
||||
KEYS_SEEN=()
|
||||
|
||||
for profile in "${PROFILE_LIST[@]}"; do
|
||||
key_name="$(get_profile_field "$profile" KeyName)"
|
||||
[[ -z "$key_name" ]] && continue
|
||||
|
||||
seen=0
|
||||
for existing in "${KEYS_SEEN[@]:-}"; do
|
||||
[[ "$existing" == "$key_name" ]] && seen=1 && break
|
||||
done
|
||||
[[ "$seen" -eq 1 ]] && continue
|
||||
KEYS_SEEN+=("$key_name")
|
||||
|
||||
key_path="$SSH_DIR/$key_name"
|
||||
comment="$(get_profile_field "$profile" KeyComment)"
|
||||
comment="${comment:-$key_name}"
|
||||
|
||||
if [[ ! -f "$key_path" ]]; then
|
||||
log_step "Generating $key_name"
|
||||
if [[ "$WHAT_IF" == '1' ]]; then
|
||||
log_warn "WhatIf: ssh-keygen -t ed25519 -f $key_path"
|
||||
else
|
||||
ssh-keygen -t ed25519 -f "$key_path" -N '' -C "$comment" -q
|
||||
chmod 600 "$key_path"
|
||||
log_ok "key $key_name created"
|
||||
fi
|
||||
else
|
||||
log_ok "key $key_name exists"
|
||||
fi
|
||||
done
|
||||
|
||||
update_ssh_block() {
|
||||
local host_alias="$1"
|
||||
local block="$2"
|
||||
local begin="# BEGIN Setup-SshAccess:$host_alias"
|
||||
local end="# END Setup-SshAccess:$host_alias"
|
||||
local tmp block_file
|
||||
tmp="$(mktemp)"
|
||||
block_file="$(mktemp)"
|
||||
|
||||
printf '%s\n' "$block" > "$block_file"
|
||||
|
||||
if [[ -f "$SSH_CONFIG" ]]; then
|
||||
awk -v begin="$begin" -v end="$end" -v blockfile="$block_file" '
|
||||
$0 == begin { skip=1; next }
|
||||
$0 == end { skip=0; next }
|
||||
skip { next }
|
||||
{ print }
|
||||
END {
|
||||
if (blockfile != "") {
|
||||
while ((getline line < blockfile) > 0) {
|
||||
print line
|
||||
}
|
||||
close(blockfile)
|
||||
}
|
||||
}
|
||||
' "$SSH_CONFIG" > "$tmp"
|
||||
else
|
||||
cp "$block_file" "$tmp"
|
||||
fi
|
||||
|
||||
rm -f "$block_file"
|
||||
|
||||
if [[ "$WHAT_IF" != '1' ]]; then
|
||||
mv "$tmp" "$SSH_CONFIG"
|
||||
chmod 600 "$SSH_CONFIG"
|
||||
else
|
||||
rm -f "$tmp"
|
||||
fi
|
||||
}
|
||||
|
||||
IFS=$'\n'
|
||||
sorted_hosts=($(printf '%s\n' "${HOST_LIST[@]}" | sort))
|
||||
unset IFS
|
||||
|
||||
for host_alias in "${sorted_hosts[@]}"; do
|
||||
profile="$(get_host_field "$host_alias" Profile)"
|
||||
key_name="$(get_profile_field "$profile" KeyName)"
|
||||
if [[ -z "$profile" || -z "$key_name" ]]; then
|
||||
echo "Host $host_alias: unknown profile" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
block="# BEGIN Setup-SshAccess:$host_alias
|
||||
Host $host_alias
|
||||
HostName $(get_host_field "$host_alias" HostName)
|
||||
User $(get_host_field "$host_alias" User)
|
||||
Port $(get_host_field "$host_alias" Port)
|
||||
IdentityFile ~/.ssh/$key_name
|
||||
IdentitiesOnly yes
|
||||
AddKeysToAgent yes
|
||||
StrictHostKeyChecking accept-new
|
||||
# END Setup-SshAccess:$host_alias"
|
||||
|
||||
if [[ "$WHAT_IF" == '1' ]]; then
|
||||
log_step "WhatIf: ssh config block for $host_alias"
|
||||
else
|
||||
update_ssh_block "$host_alias" "$block"
|
||||
log_ok "ssh config: $host_alias"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$WHAT_IF" != '1' ]]; then
|
||||
eval "$(ssh-agent -s)" >/dev/null
|
||||
for key_name in "${KEYS_SEEN[@]:-}"; do
|
||||
[[ -f "$SSH_DIR/$key_name" ]] && ssh-add "$SSH_DIR/$key_name" >/dev/null 2>&1 || true
|
||||
done
|
||||
fi
|
||||
|
||||
log_ok 'SSH setup complete'
|
||||
Reference in New Issue
Block a user