init-cursor: авто-bootstrap git на новом ПК, install.ps1 с Gitea.
Один вызов init-cursor настраивает SSH при необходимости и копирует rules в проект.
This commit is contained in:
@@ -2,58 +2,55 @@
|
|||||||
|
|
||||||
Правила и скрипты для настройки нового компьютера: SSH-ключи, Cursor rules, git remotes, home Gitea.
|
Правила и скрипты для настройки нового компьютера: SSH-ключи, Cursor rules, git remotes, home Gitea.
|
||||||
|
|
||||||
## Как это устроено
|
## Один вызов: `init-cursor`
|
||||||
|
|
||||||
| Этап | Скрипт | Что делает |
|
| Состояние машины | Что делает `init-cursor` |
|
||||||
|------|--------|------------|
|
|------------------|--------------------------|
|
||||||
| Новый ПК | `bootstrap.ps1` / `bootstrap.sh` | SSH-ключи, регистрация в Gitea, `git clone` Rules → `$HOME/CursorRules` |
|
| Ничего не настроено | SSH-ключи, Gitea API, clone Rules в `$HOME/CursorRules`, профиль shell |
|
||||||
| Новый проект | `init-cursor` | Копирует rules **из локального** `$HOME/CursorRules` в текущий каталог |
|
| Git уже настроен | Только копирует rules в текущий проект + remotes |
|
||||||
| Обновить rules | `init-cursor -Update` | `git pull` в `$HOME/CursorRules`, затем копирование |
|
|
||||||
|
|
||||||
`init-cursor` **не** тянет правила с remote напрямую — только из локального клона. Сначала нужен bootstrap (или ручной clone после настройки SSH).
|
Источник правил — локальный клон `$HOME/CursorRules` (после bootstrap). Обновить: `init-cursor -Update`.
|
||||||
|
|
||||||
## Новый Windows-ПК
|
## Новый Windows-ПК
|
||||||
|
|
||||||
На голом ПК нет доступа к приватному git — сначала скопируйте каталог Rules с рабочей машины:
|
В каталоге **вашего проекта** (один раз, Gitea спросит логин/пароль по HTTPS):
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
scp -r user@working-pc:~/CursorRules $env:TEMP\rules-bootstrap
|
irm https://git.kalinamall.ru/PapaTramp/Rules/raw/branch/main/install.ps1 | iex
|
||||||
cd $env:TEMP\rules-bootstrap
|
|
||||||
.\bootstrap.ps1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Bootstrap:
|
Дальше в любом проекте:
|
||||||
|
|
||||||
1. `git config` user.name / user.email (`local/git-user.conf`)
|
```powershell
|
||||||
2. Генерирует SSH-ключи, пишет `~/.ssh/config` (`scripts/Setup-GitSsh.ps1`)
|
init-cursor
|
||||||
3. Регистрирует pubkey в Gitea API (`scripts/Register-GiteaSshKeys.ps1`)
|
```
|
||||||
4. `git clone ssh://git@git.kalinamall.lan:2222/PapaTramp/Rules.git` → `$HOME\CursorRules`
|
|
||||||
5. Добавляет `init-cursor` в PowerShell profile
|
|
||||||
|
|
||||||
## Новый Linux-хост
|
## Новый Linux-хост
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scp -r user@working-pc:~/CursorRules /tmp/rules-bootstrap
|
cd ~/projects/your-repo
|
||||||
cd /tmp/rules-bootstrap
|
curl -fsSL https://git.kalinamall.ru/PapaTramp/Rules/raw/branch/main/install.sh | bash
|
||||||
chmod +x bootstrap.sh scripts/*.sh
|
|
||||||
./bootstrap.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## В каждом проекте
|
Затем:
|
||||||
|
|
||||||
```powershell
|
|
||||||
cd C:\path\to\repo
|
|
||||||
init-cursor
|
|
||||||
init-cursor -Update # подтянуть свежие rules из remote
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/projects/myrepo
|
|
||||||
init-cursor
|
init-cursor
|
||||||
init-cursor --update
|
init-cursor --update
|
||||||
```
|
```
|
||||||
|
|
||||||
Скрипт копирует `.cursorignore` и `.cursor/rules/*.mdc`, дополняет `.gitignore`, настраивает remotes (`home`, `github`, `kalinamall`) и создаёт репозиторий на home Gitea через API.
|
## Что происходит внутри
|
||||||
|
|
||||||
|
1. **install.ps1 / install.sh** — shallow clone Rules по HTTPS (первый раз)
|
||||||
|
2. **init-cursor** вызывает `Invoke-MachineBootstrap` / `invoke-machine-bootstrap.sh`:
|
||||||
|
- `local/git-user.conf` — git user.name / email
|
||||||
|
- `scripts/Setup-GitSsh*` — ключи и `~/.ssh/config`
|
||||||
|
- `scripts/Register-GiteaSshKeys*` — pubkey в Gitea API
|
||||||
|
- clone/update `$HOME/CursorRules` (SSH, fallback HTTPS)
|
||||||
|
3. Копирует `.cursorignore`, `.cursor/rules/*.mdc`, дополняет `.gitignore`
|
||||||
|
4. `Set-GitRemotes*` + `Ensure-GiteaHomeRepo.ps1` для текущего репозитория
|
||||||
|
|
||||||
|
`bootstrap.ps1` / `bootstrap.sh` оставлены для ручного запуска без проекта.
|
||||||
|
|
||||||
## Конфиги (local/)
|
## Конфиги (local/)
|
||||||
|
|
||||||
@@ -61,7 +58,5 @@ init-cursor --update
|
|||||||
|------|------------|
|
|------|------------|
|
||||||
| `git-ssh-hosts.conf` | Хосты и профили SSH-ключей |
|
| `git-ssh-hosts.conf` | Хосты и профили SSH-ключей |
|
||||||
| `git-user.conf` | `user.name` / `user.email` |
|
| `git-user.conf` | `user.name` / `user.email` |
|
||||||
| `kalinamall-gitea.conf` | API kalinamall (для регистрации SSH-ключа) |
|
| `kalinamall-gitea.conf` | API kalinamall |
|
||||||
| `papatramp-gitea.conf` | API home Gitea + Ensure-GiteaHomeRepo |
|
| `papatramp-gitea.conf` | API home Gitea |
|
||||||
|
|
||||||
Заполните `GiteaApiPassword` в `kalinamall-gitea.conf` (приватный репозиторий).
|
|
||||||
|
|||||||
+8
-120
@@ -1,140 +1,28 @@
|
|||||||
#Requires -Version 5.1
|
#Requires -Version 5.1
|
||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Первичная настройка нового компьютера: SSH, Gitea keys, clone Rules, init-cursor в профиль.
|
Обёртка над Invoke-MachineBootstrap.ps1 (совместимость).
|
||||||
|
|
||||||
.DESCRIPTION
|
|
||||||
1. git user.name / user.email
|
|
||||||
2. SSH-ключи и ~/.ssh/config
|
|
||||||
3. Регистрация pubkey в Gitea (kalinamall, papatramp)
|
|
||||||
4. git clone Rules -> $HOME\CursorRules
|
|
||||||
5. Подключение init-cursor в PowerShell profile
|
|
||||||
|
|
||||||
На голом ПК скопируйте каталог Rules с рабочей машины (scp/USB), затем:
|
|
||||||
.\bootstrap.ps1
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
.\bootstrap.ps1
|
|
||||||
#>
|
#>
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[string]$RulesDest = '',
|
[string]$RulesDest = '',
|
||||||
[string]$CloneUrl = 'ssh://git@git.kalinamall.lan:2222/PapaTramp/Rules.git',
|
|
||||||
[switch]$SkipClone,
|
[switch]$SkipClone,
|
||||||
[switch]$SkipProfile,
|
[switch]$SkipProfile,
|
||||||
[switch]$WhatIf
|
[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 }
|
|
||||||
|
|
||||||
$root = $PSScriptRoot
|
$root = $PSScriptRoot
|
||||||
$configDir = Join-Path $root 'local'
|
|
||||||
if (-not $RulesDest) {
|
if (-not $RulesDest) {
|
||||||
$RulesDest = Join-Path $HOME 'CursorRules'
|
$RulesDest = Join-Path $HOME 'CursorRules'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Read-KeyValueFile {
|
& (Join-Path $root 'scripts\Invoke-MachineBootstrap.ps1') @{
|
||||||
param([string]$Path)
|
Root = $root
|
||||||
$data = @{}
|
RulesDest = $RulesDest
|
||||||
if (-not (Test-Path -LiteralPath $Path)) { return $data }
|
SkipClone = $SkipClone.IsPresent
|
||||||
Get-Content -LiteralPath $Path | ForEach-Object {
|
SkipProfile = $SkipProfile.IsPresent
|
||||||
if ($_ -match '^([^#=]+)=(.*)$') {
|
WhatIf = $WhatIf.IsPresent
|
||||||
$data[$Matches[1].Trim()] = $Matches[2].Trim()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $data
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Step 'Bootstrap: new machine setup'
|
|
||||||
|
|
||||||
$userCfgPath = Join-Path $configDir 'git-user.conf'
|
|
||||||
if (Test-Path -LiteralPath $userCfgPath) {
|
|
||||||
$userCfg = Read-KeyValueFile -Path $userCfgPath
|
|
||||||
if ($userCfg.UserName) {
|
|
||||||
git config --global user.name $userCfg.UserName | Out-Null
|
|
||||||
Write-Ok "git user.name = $($userCfg.UserName)"
|
|
||||||
}
|
|
||||||
if ($userCfg.UserEmail) {
|
|
||||||
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.lan'
|
|
||||||
ssh -T -o BatchMode=yes -o ConnectTimeout=10 git@git.kalinamall.lan 2>&1 | ForEach-Object { Write-Host $_ }
|
|
||||||
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 1) {
|
|
||||||
Write-Warn 'SSH test failed — check VPN/LAN and Gitea SSH keys'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $SkipClone) {
|
|
||||||
if ($WhatIf) {
|
|
||||||
Write-Step "WhatIf: clone $CloneUrl -> $RulesDest"
|
|
||||||
}
|
|
||||||
elseif (Test-Path (Join-Path $RulesDest '.git')) {
|
|
||||||
Write-Step "Updating $RulesDest"
|
|
||||||
git -C $RulesDest pull --ff-only
|
|
||||||
Write-Ok 'CursorRules updated'
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (Test-Path -LiteralPath $RulesDest) {
|
|
||||||
throw "Path exists and is not a git repo: $RulesDest"
|
|
||||||
}
|
|
||||||
Write-Step "Cloning $CloneUrl"
|
|
||||||
git clone $CloneUrl $RulesDest
|
|
||||||
Write-Ok "Cloned to $RulesDest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $SkipProfile) {
|
|
||||||
$profileHook = ". `"$RulesDest\init-cursor.ps1`""
|
|
||||||
$profilePath = $PROFILE.CurrentUserAllHosts
|
|
||||||
if (-not $profilePath) { $profilePath = $PROFILE.CurrentUserCurrentHost }
|
|
||||||
|
|
||||||
if ($WhatIf) {
|
|
||||||
Write-Step "WhatIf: add to profile $profilePath"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$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"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Ok 'init-cursor already in profile'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Ok 'Bootstrap complete. Open a new shell, then in a project: init-cursor'
|
Write-Host '[+] Bootstrap complete. In a project run: init-cursor' -ForegroundColor Green
|
||||||
|
|||||||
+4
-78
@@ -1,87 +1,13 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Первичная настройка нового Linux-хоста: SSH, Gitea keys, clone Rules, init-cursor в shell profile.
|
# Обёртка над invoke-machine-bootstrap.sh (совместимость).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
CONFIG_DIR="$ROOT/local"
|
|
||||||
RULES_DEST="${RULES_DEST:-$HOME/CursorRules}"
|
RULES_DEST="${RULES_DEST:-$HOME/CursorRules}"
|
||||||
CLONE_URL="${CLONE_URL:-ssh://git@git.kalinamall.lan:2222/PapaTramp/Rules.git}"
|
|
||||||
SKIP_CLONE="${SKIP_CLONE:-0}"
|
SKIP_CLONE="${SKIP_CLONE:-0}"
|
||||||
SKIP_PROFILE="${SKIP_PROFILE:-0}"
|
SKIP_PROFILE="${SKIP_PROFILE:-0}"
|
||||||
WHAT_IF="${WHAT_IF:-0}"
|
|
||||||
|
|
||||||
log_step() { printf '[*] %s\n' "$1"; }
|
ROOT="$ROOT" RULES_DEST="$RULES_DEST" SKIP_CLONE="$SKIP_CLONE" SKIP_PROFILE="$SKIP_PROFILE" \
|
||||||
log_ok() { printf '[+] %s\n' "$1"; }
|
bash "$ROOT/scripts/invoke-machine-bootstrap.sh"
|
||||||
log_warn() { printf '[!] %s\n' "$1"; }
|
|
||||||
|
|
||||||
read_kv_file() {
|
printf '\n[+] Bootstrap complete. In a project run: init-cursor\n'
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
log_step 'Bootstrap: new machine setup'
|
|
||||||
|
|
||||||
user_cfg="$CONFIG_DIR/git-user.conf"
|
|
||||||
if [[ -f "$user_cfg" ]]; 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 < <(read_kv_file "$user_cfg")
|
|
||||||
fi
|
|
||||||
|
|
||||||
export CONFIG_DIR WHAT_IF
|
|
||||||
bash "$ROOT/scripts/setup-git-ssh.sh"
|
|
||||||
bash "$ROOT/scripts/register-gitea-ssh-keys.sh"
|
|
||||||
|
|
||||||
if [[ "$WHAT_IF" != '1' ]]; then
|
|
||||||
log_step 'Testing git@git.kalinamall.lan'
|
|
||||||
ssh -T -o BatchMode=yes -o ConnectTimeout=10 git@git.kalinamall.lan || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$SKIP_CLONE" != '1' ]]; then
|
|
||||||
if [[ "$WHAT_IF" == '1' ]]; then
|
|
||||||
log_step "WhatIf: clone $CLONE_URL -> $RULES_DEST"
|
|
||||||
elif [[ -d "$RULES_DEST/.git" ]]; then
|
|
||||||
log_step "Updating $RULES_DEST"
|
|
||||||
git -C "$RULES_DEST" pull --ff-only
|
|
||||||
log_ok 'CursorRules updated'
|
|
||||||
else
|
|
||||||
[[ -e "$RULES_DEST" ]] && { echo "Path exists and is not a git repo: $RULES_DEST" >&2; exit 1; }
|
|
||||||
log_step "Cloning $CLONE_URL"
|
|
||||||
git clone "$CLONE_URL" "$RULES_DEST"
|
|
||||||
log_ok "Cloned to $RULES_DEST"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$SKIP_PROFILE" != '1' ]]; then
|
|
||||||
profile_hook="source \"$RULES_DEST/init-cursor.sh\""
|
|
||||||
for profile in "$HOME/.bashrc" "$HOME/.zshrc"; do
|
|
||||||
[[ -f "$profile" ]] || continue
|
|
||||||
if ! grep -Fq "$profile_hook" "$profile" 2>/dev/null; then
|
|
||||||
if [[ "$WHAT_IF" == '1' ]]; then
|
|
||||||
log_step "WhatIf: add to $profile"
|
|
||||||
else
|
|
||||||
printf '\n%s\n' "$profile_hook" >> "$profile"
|
|
||||||
log_ok "Profile updated: $profile"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log_ok "init-cursor already in $profile"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf '\n'
|
|
||||||
log_ok 'Bootstrap complete. Open a new shell, then in a project: init-cursor'
|
|
||||||
|
|||||||
+82
-23
@@ -1,47 +1,91 @@
|
|||||||
#Requires -Version 5.1
|
#Requires -Version 5.1
|
||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Копирует Cursor rules из локального $HOME\CursorRules в текущий проект.
|
Инициализация Cursor в текущем проекте; на новом ПК сначала настраивает git/SSH.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Источник — локальный клон репозитория Rules ($HOME\CursorRules), не remote напрямую.
|
Один вызов init-cursor:
|
||||||
На новом ПК сначала: bootstrap.ps1 / bootstrap.sh (SSH + clone Rules).
|
- если машина не настроена: SSH, Gitea keys, clone Rules в $HOME\CursorRules
|
||||||
Обновить правила из remote: init-cursor -Update
|
- затем копирует rules в текущий репозиторий и настраивает remotes
|
||||||
|
|
||||||
|
Первый запуск на голом ПК (из каталога проекта):
|
||||||
|
irm https://git.kalinamall.ru/PapaTramp/Rules/raw/branch/main/install.ps1 | iex
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
cd C:\Users\papat\Projects\MyRepo
|
cd C:\Users\papat\Projects\MyRepo
|
||||||
init-cursor
|
init-cursor
|
||||||
init-cursor -Update
|
init-cursor -Update
|
||||||
#>
|
#>
|
||||||
function init-cursor {
|
function Get-CursorRulesDest {
|
||||||
|
return (Join-Path $HOME 'CursorRules')
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-CursorBootstrapRoot {
|
||||||
|
param([string]$RulesDest)
|
||||||
|
|
||||||
|
$scriptRoot = $PSScriptRoot
|
||||||
|
if (Test-Path (Join-Path $scriptRoot 'scripts\Invoke-MachineBootstrap.ps1')) {
|
||||||
|
return $scriptRoot
|
||||||
|
}
|
||||||
|
if (Test-Path (Join-Path $RulesDest 'local\git-ssh-hosts.conf')) {
|
||||||
|
return $RulesDest
|
||||||
|
}
|
||||||
|
if (Test-Path (Join-Path $scriptRoot 'local\git-ssh-hosts.conf')) {
|
||||||
|
return $scriptRoot
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Initialize-CursorRulesEnvironment {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[switch]$SkipGitRemotes,
|
|
||||||
[switch]$Update
|
[switch]$Update
|
||||||
)
|
)
|
||||||
|
|
||||||
$source = Join-Path $HOME 'CursorRules'
|
$rulesDest = Get-CursorRulesDest
|
||||||
$rulesSrc = Join-Path $source '.cursor\rules'
|
$bootstrapRoot = Get-CursorBootstrapRoot -RulesDest $rulesDest
|
||||||
$scripts = Join-Path $source 'scripts'
|
|
||||||
|
|
||||||
if (-not (Test-Path $source)) {
|
if (-not $bootstrapRoot) {
|
||||||
Write-Host "Error: $source not found — run bootstrap.ps1 on this machine first" -ForegroundColor Red
|
Write-Host 'Error: CursorRules not found. Run install.ps1 from Gitea first:' -ForegroundColor Red
|
||||||
return
|
Write-Host ' irm https://git.kalinamall.ru/PapaTramp/Rules/raw/branch/main/install.ps1 | iex' -ForegroundColor Yellow
|
||||||
|
return $false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Update) {
|
$bootstrapScript = Join-Path $bootstrapRoot 'scripts\Invoke-MachineBootstrap.ps1'
|
||||||
if (Test-Path (Join-Path $source '.git')) {
|
if (-not (Test-Path -LiteralPath $bootstrapScript)) {
|
||||||
Write-Host '[*] Updating CursorRules from remote...' -ForegroundColor Cyan
|
Write-Host "Error: bootstrap script not found: $bootstrapScript" -ForegroundColor Red
|
||||||
git -C $source pull --ff-only
|
return $false
|
||||||
if ($LASTEXITCODE -ne 0) {
|
}
|
||||||
Write-Host '[!] git pull failed in CursorRules' -ForegroundColor Yellow
|
|
||||||
}
|
$repoReady = Test-Path (Join-Path $rulesDest '.git')
|
||||||
else {
|
$skipClone = $repoReady
|
||||||
Write-Host '[+] CursorRules updated' -ForegroundColor Green
|
|
||||||
}
|
& $bootstrapScript -Root $bootstrapRoot -RulesDest $rulesDest -SkipClone:$skipClone
|
||||||
|
|
||||||
|
if ($Update -and $repoReady) {
|
||||||
|
Write-Host '[*] Updating CursorRules from remote...' -ForegroundColor Cyan
|
||||||
|
git -C $rulesDest pull --ff-only
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host '[!] git pull failed in CursorRules' -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Host '[+] CursorRules updated' -ForegroundColor Green
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-CursorInCurrentProject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[switch]$SkipGitRemotes
|
||||||
|
)
|
||||||
|
|
||||||
|
$source = Get-CursorRulesDest
|
||||||
|
$rulesSrc = Join-Path $source '.cursor\rules'
|
||||||
|
$scripts = Join-Path $source 'scripts'
|
||||||
|
|
||||||
$cursorIgnore = Join-Path $source '.cursorignore'
|
$cursorIgnore = Join-Path $source '.cursorignore'
|
||||||
if (Test-Path $cursorIgnore) {
|
if (Test-Path $cursorIgnore) {
|
||||||
Copy-Item $cursorIgnore . -Force
|
Copy-Item $cursorIgnore . -Force
|
||||||
@@ -83,7 +127,7 @@ function init-cursor {
|
|||||||
|
|
||||||
git rev-parse --is-inside-work-tree 2>$null | Out-Null
|
git rev-parse --is-inside-work-tree 2>$null | Out-Null
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host '[!] Not a git repo — remotes skipped' -ForegroundColor Yellow
|
Write-Host '[!] Not a git repo - remotes skipped' -ForegroundColor Yellow
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,3 +149,18 @@ function init-cursor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function init-cursor {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[switch]$SkipGitRemotes,
|
||||||
|
[switch]$Update
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not (Initialize-CursorRulesEnvironment -Update:$Update)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Install-CursorInCurrentProject -SkipGitRemotes:$SkipGitRemotes
|
||||||
|
Write-Host '[+] init-cursor complete' -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|||||||
+85
-28
@@ -1,33 +1,71 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Копирует Cursor rules из локального $HOME/CursorRules в текущий проект.
|
# Инициализация Cursor в текущем проекте; на новом хосте сначала настраивает git/SSH.
|
||||||
init-cursor() {
|
|
||||||
local source="$HOME/CursorRules"
|
|
||||||
local rules_src="$source/.cursor/rules"
|
|
||||||
local scripts="$source/scripts"
|
|
||||||
local update=0
|
|
||||||
local skip_git_remotes=0
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
get_cursor_rules_dest() {
|
||||||
case "$1" in
|
printf '%s' "$HOME/CursorRules"
|
||||||
-Update|--update) update=1; shift ;;
|
}
|
||||||
-SkipGitRemotes|--skip-git-remotes) skip_git_remotes=1; shift ;;
|
|
||||||
*) shift ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ ! -d "$source" ]]; then
|
get_cursor_bootstrap_root() {
|
||||||
echo "Error: $source not found — run bootstrap.sh on this machine first" >&2
|
local rules_dest script_dir
|
||||||
return 1
|
rules_dest="$(get_cursor_rules_dest)"
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
if [[ -f "$script_dir/scripts/invoke-machine-bootstrap.sh" ]]; then
|
||||||
|
printf '%s' "$script_dir"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
if [[ -f "$rules_dest/local/git-ssh-hosts.conf" ]]; then
|
||||||
|
printf '%s' "$rules_dest"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -f "$script_dir/local/git-ssh-hosts.conf" ]]; then
|
||||||
|
printf '%s' "$script_dir"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
if [[ "$update" == '1' && -d "$source/.git" ]]; then
|
initialize_cursor_rules_environment() {
|
||||||
|
local update=0
|
||||||
|
[[ "${1:-}" == '--update' ]] && update=1
|
||||||
|
|
||||||
|
local rules_dest bootstrap_root
|
||||||
|
rules_dest="$(get_cursor_rules_dest)"
|
||||||
|
bootstrap_root="$(get_cursor_bootstrap_root)" || {
|
||||||
|
echo 'Error: CursorRules not found. Run install.sh from Gitea first:' >&2
|
||||||
|
echo ' curl -fsSL https://git.kalinamall.ru/PapaTramp/Rules/raw/branch/main/install.sh | bash' >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local skip_clone=0
|
||||||
|
[[ -d "$rules_dest/.git" ]] && skip_clone=1
|
||||||
|
|
||||||
|
ROOT="$bootstrap_root" RULES_DEST="$rules_dest" SKIP_CLONE="$skip_clone" \
|
||||||
|
bash "$bootstrap_root/scripts/invoke-machine-bootstrap.sh"
|
||||||
|
|
||||||
|
if [[ "$update" -eq 1 && -d "$rules_dest/.git" ]]; then
|
||||||
echo '[*] Updating CursorRules from remote...'
|
echo '[*] Updating CursorRules from remote...'
|
||||||
if git -C "$source" pull --ff-only; then
|
if git -C "$rules_dest" pull --ff-only; then
|
||||||
echo '[+] CursorRules updated'
|
echo '[+] CursorRules updated'
|
||||||
else
|
else
|
||||||
echo '[!] git pull failed in CursorRules' >&2
|
echo '[!] git pull failed in CursorRules' >&2
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_cursor_in_current_project() {
|
||||||
|
local skip_git_remotes=0
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--skip-git-remotes) skip_git_remotes=1; shift ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local source rules_src scripts
|
||||||
|
source="$(get_cursor_rules_dest)"
|
||||||
|
rules_src="$source/.cursor/rules"
|
||||||
|
scripts="$source/scripts"
|
||||||
|
|
||||||
if [[ -f "$source/.cursorignore" ]]; then
|
if [[ -f "$source/.cursorignore" ]]; then
|
||||||
cp -f "$source/.cursorignore" .
|
cp -f "$source/.cursorignore" .
|
||||||
@@ -41,7 +79,7 @@ init-cursor() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -d .git ]]; then
|
if [[ -d .git ]]; then
|
||||||
local gitignore='.gitignore'
|
local gitignore='.gitignore' line
|
||||||
for line in '.cursor/' '.cursorignore'; do
|
for line in '.cursor/' '.cursorignore'; do
|
||||||
if [[ ! -f "$gitignore" ]]; then
|
if [[ ! -f "$gitignore" ]]; then
|
||||||
printf '%s\n' "$line" > "$gitignore"
|
printf '%s\n' "$line" > "$gitignore"
|
||||||
@@ -53,21 +91,19 @@ init-cursor() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$skip_git_remotes" == '1' ]]; then
|
if [[ "$skip_git_remotes" -eq 1 ]]; then
|
||||||
echo '[!] Git remotes skipped'
|
echo '[!] Git remotes skipped'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
echo '[!] Not a git repo — remotes skipped'
|
echo '[!] Not a git repo - remotes skipped'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "$scripts/Set-GitRemotes.sh" ]]; then
|
if [[ -f "$scripts/Set-GitRemotes.sh" ]]; then
|
||||||
echo '[*] Configuring git remotes...'
|
echo '[*] Configuring git remotes...'
|
||||||
bash "$scripts/Set-GitRemotes.sh" || true
|
bash "$scripts/Set-GitRemotes.sh" || true
|
||||||
elif [[ -f "$scripts/Set-GitRemotes.ps1" ]]; then
|
|
||||||
echo '[!] Set-GitRemotes.ps1 only; install bash script or pwsh' >&2
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "$scripts/Ensure-GiteaHomeRepo.ps1" ]]; then
|
if [[ -f "$scripts/Ensure-GiteaHomeRepo.ps1" ]]; then
|
||||||
@@ -76,10 +112,31 @@ init-cursor() {
|
|||||||
echo "[*] Ensuring home Gitea repo: $repo_name"
|
echo "[*] Ensuring home Gitea repo: $repo_name"
|
||||||
if command -v pwsh >/dev/null 2>&1; then
|
if command -v pwsh >/dev/null 2>&1; then
|
||||||
pwsh -NoProfile -File "$scripts/Ensure-GiteaHomeRepo.ps1" -RepoName "$repo_name" || true
|
pwsh -NoProfile -File "$scripts/Ensure-GiteaHomeRepo.ps1" -RepoName "$repo_name" || true
|
||||||
elif command -v powershell >/dev/null 2>&1; then
|
|
||||||
powershell -NoProfile -ExecutionPolicy Bypass -File "$scripts/Ensure-GiteaHomeRepo.ps1" -RepoName "$repo_name" || true
|
|
||||||
else
|
|
||||||
echo '[!] PowerShell not found — skip Ensure-GiteaHomeRepo.ps1' >&2
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init-cursor() {
|
||||||
|
local update=0
|
||||||
|
local skip_git_remotes=0
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-Update|--update) update=1; shift ;;
|
||||||
|
-SkipGitRemotes|--skip-git-remotes) skip_git_remotes=1; shift ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$update" -eq 1 ]]; then
|
||||||
|
initialize_cursor_rules_environment --update || return 1
|
||||||
|
else
|
||||||
|
initialize_cursor_rules_environment || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$skip_git_remotes" -eq 1 ]]; then
|
||||||
|
install_cursor_in_current_project --skip-git-remotes
|
||||||
|
else
|
||||||
|
install_cursor_in_current_project
|
||||||
|
fi
|
||||||
|
echo '[+] init-cursor complete'
|
||||||
|
}
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#Requires -Version 5.1
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Первая загрузка с Gitea: clone Rules и запуск init-cursor в текущем проекте.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
cd C:\path\to\your-repo
|
||||||
|
irm https://git.kalinamall.ru/PapaTramp/Rules/raw/branch/main/install.ps1 | iex
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(ValueFromRemainingArguments = $true)]
|
||||||
|
[object[]]$InitCursorArgs
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$rulesDest = Join-Path $HOME 'CursorRules'
|
||||||
|
$httpsUrl = 'https://git.kalinamall.ru/PapaTramp/Rules.git'
|
||||||
|
$sshUrl = 'ssh://git@git.kalinamall.lan:2222/PapaTramp/Rules.git'
|
||||||
|
|
||||||
|
if (-not (Test-Path (Join-Path $rulesDest '.git'))) {
|
||||||
|
Write-Host '[*] Cloning Rules from Gitea (HTTPS, login once if asked)...' -ForegroundColor Cyan
|
||||||
|
if (Test-Path -LiteralPath $rulesDest) {
|
||||||
|
throw "Path exists and is not a git repo: $rulesDest"
|
||||||
|
}
|
||||||
|
git clone --depth 1 $httpsUrl $rulesDest
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw 'git clone failed'
|
||||||
|
}
|
||||||
|
Write-Host "[+] Cloned to $rulesDest" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
. (Join-Path $rulesDest 'init-cursor.ps1')
|
||||||
|
init-cursor @InitCursorArgs
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Первая загрузка с Gitea: clone Rules и запуск init-cursor в текущем проекте.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RULES_DEST="${RULES_DEST:-$HOME/CursorRules}"
|
||||||
|
HTTPS_URL="${HTTPS_URL:-https://git.kalinamall.ru/PapaTramp/Rules.git}"
|
||||||
|
|
||||||
|
if [[ ! -d "$RULES_DEST/.git" ]]; then
|
||||||
|
echo '[*] Cloning Rules from Gitea (HTTPS, login once if asked)...'
|
||||||
|
[[ -e "$RULES_DEST" ]] && { echo "Path exists and is not a git repo: $RULES_DEST" >&2; exit 1; }
|
||||||
|
git clone --depth 1 "$HTTPS_URL" "$RULES_DEST"
|
||||||
|
echo "[+] Cloned to $RULES_DEST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$RULES_DEST/init-cursor.sh"
|
||||||
|
init-cursor "$@"
|
||||||
@@ -4,6 +4,6 @@ GiteaPort=2222
|
|||||||
GiteaWebUrl=https://git.kalinamall.ru
|
GiteaWebUrl=https://git.kalinamall.ru
|
||||||
GiteaApiUrl=http://192.168.160.129:3000
|
GiteaApiUrl=http://192.168.160.129:3000
|
||||||
GiteaApiUser=papatramp
|
GiteaApiUser=papatramp
|
||||||
GiteaApiPassword=
|
GiteaApiPassword=R3tt3tnatur
|
||||||
KeyName=id_ed25519_git_kalinamall
|
KeyName=id_ed25519_git_kalinamall
|
||||||
KeyComment=papatramp@kalinamall
|
KeyComment=papatramp@kalinamall
|
||||||
|
|||||||
@@ -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.kalinamall.lan:2222/PapaTramp/Rules.git',
|
||||||
|
[string]$HttpsCloneUrl = 'https://git.kalinamall.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\.lan\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.lan'
|
||||||
|
ssh -T -o BatchMode=yes -o ConnectTimeout=10 git@git.kalinamall.lan 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,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.kalinamall.lan:2222/PapaTramp/Rules.git}"
|
||||||
|
HTTPS_CLONE_URL="${HTTPS_CLONE_URL:-https://git.kalinamall.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.lan$' "$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.lan'
|
||||||
|
ssh -T -o BatchMode=yes -o ConnectTimeout=10 git@git.kalinamall.lan || 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'
|
||||||
Reference in New Issue
Block a user