refactor(windows): причёсан стиль chezmoi-скриптов и pwsh-профиля
- Зачем: - код Codex работал, но был тяжёл для чтения и сопровождения: 4 naming-конвенции, скрытая мутация $script:changed, 298 строк WT-скрипта почти без комментариев. - Что: - profile.ps1: комментарии на русском перед каждой функцией, секционные разделители, wt-here → wt_here (единый snake_case), Get-PublicIp упрощён до Invoke-RestMethod, Chocolatey boilerplate обрезан. - WT-скрипт: Set/Remove-Prop возвращают bool вместо мутации $script:changed, naming выровнен в PascalCase, хелперы переименованы (Get-Prop, Set-Prop, Test-IsPS7, Test-IsStockPS7), комментарии к каждому блоку логики, вывод на русском. - Git-миграция: блок комментариев с описанием 4 сценариев, пояснение 3 вариантов ManagedStubs, naming выровнен, вывод на русском. - Проверка: - pwsh -NoProfile -Command "[System.Management.Automation.Language.Parser]::ParseFile(...)" — синтаксис всех трёх файлов OK. - chezmoi -S "$HOME\dotfiles\chezmoi" apply --dry-run --verbose. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c06ad1ecbb
commit
1963b6a117
3 files changed
+273
-379
No files matched your search
@@ -1,13 +1,17 @@
|
||||
# Keep PowerShell 7 console text I/O on UTF-8 for predictable native tool behavior.
|
||||
# ── UTF-8 ────────────────────────────────────────────────────────────
|
||||
# Принудительный UTF-8 для pwsh 7 в интерактивной консоли,
|
||||
# независимо от унаследованной code page.
|
||||
# НЕ трогает PS 5.1, VS Code terminal, remoting и т.д.
|
||||
if ($IsWindows -and $PSVersionTable.PSEdition -eq 'Core' -and $Host.Name -eq 'ConsoleHost') {
|
||||
chcp 65001 > $null
|
||||
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
[Console]::InputEncoding = $Utf8NoBom
|
||||
[Console]::OutputEncoding = $Utf8NoBom
|
||||
$OutputEncoding = $Utf8NoBom
|
||||
$Utf8 = New-Object System.Text.UTF8Encoding($false)
|
||||
[Console]::InputEncoding = $Utf8
|
||||
[Console]::OutputEncoding = $Utf8
|
||||
$OutputEncoding = $Utf8
|
||||
}
|
||||
|
||||
# Keep interactive editing comfortable without depending on extra prompt frameworks.
|
||||
# ── PSReadLine ───────────────────────────────────────────────────────
|
||||
# Предиктивный ввод по истории, только для интерактивной консоли.
|
||||
if (
|
||||
$Host.Name -eq 'ConsoleHost' -and
|
||||
-not [Console]::IsOutputRedirected -and
|
||||
@@ -15,170 +19,112 @@ if (
|
||||
(Get-Module -ListAvailable -Name PSReadLine)
|
||||
) {
|
||||
Import-Module PSReadLine -ErrorAction SilentlyContinue
|
||||
Set-PSReadLineOption -PredictionSource History -ErrorAction SilentlyContinue
|
||||
Set-PSReadLineOption -PredictionViewStyle ListView -ErrorAction SilentlyContinue
|
||||
Set-PSReadLineOption -HistoryNoDuplicates:$true -ErrorAction SilentlyContinue
|
||||
Set-PSReadLineOption -PredictionSource History -ErrorAction SilentlyContinue
|
||||
Set-PSReadLineOption -PredictionViewStyle ListView -ErrorAction SilentlyContinue
|
||||
Set-PSReadLineOption -HistoryNoDuplicates:$true -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
function Reload-Profile {
|
||||
. $PROFILE
|
||||
}
|
||||
# ── Unix-хелперы (lowercase — привычка из bash) ─────────────────────
|
||||
|
||||
function which {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name
|
||||
)
|
||||
# Перечитать профиль без перезапуска сессии.
|
||||
function Reload-Profile { . $PROFILE }
|
||||
|
||||
# Показать все команды с данным именем (аналог which в bash).
|
||||
function which ($Name) {
|
||||
Get-Command -Name $Name -All | Select-Object CommandType, Name, Source
|
||||
}
|
||||
|
||||
# Создать файл или обновить mtime (аналог touch в bash).
|
||||
function touch {
|
||||
param(
|
||||
[Parameter(Mandatory = $true, ValueFromRemainingArguments = $true)]
|
||||
[string[]]$Path
|
||||
)
|
||||
|
||||
param([Parameter(Mandatory, ValueFromRemainingArguments)][string[]]$Path)
|
||||
foreach ($Item in $Path) {
|
||||
if (Test-Path -LiteralPath $Item) {
|
||||
(Get-Item -LiteralPath $Item).LastWriteTime = Get-Date
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
New-Item -ItemType File -Path $Item -Force | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mkcd {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
# mkdir + cd в одно действие.
|
||||
function mkcd ($Path) {
|
||||
New-Item -ItemType Directory -Path $Path -Force | Out-Null
|
||||
Set-Location -Path $Path
|
||||
}
|
||||
|
||||
function open {
|
||||
param(
|
||||
[string]$Path = '.'
|
||||
)
|
||||
|
||||
Invoke-Item -Path $Path
|
||||
}
|
||||
# Открыть путь в Explorer (аналог xdg-open / macOS open).
|
||||
function open ($Path = '.') { Invoke-Item -Path $Path }
|
||||
|
||||
# Перейти в корень текущего git-репозитория.
|
||||
function croot {
|
||||
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
||||
Write-Error 'git is not available in PATH.'
|
||||
return
|
||||
Write-Error 'git is not available in PATH.'; return
|
||||
}
|
||||
|
||||
$Root = git rev-parse --show-toplevel 2>$null
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($Root)) {
|
||||
Write-Error 'Not inside a Git repository.'
|
||||
return
|
||||
Write-Error 'Not inside a Git repository.'; return
|
||||
}
|
||||
|
||||
Set-Location -Path $Root.Trim()
|
||||
}
|
||||
|
||||
# ── Прокси (аналог bash proxy_on/proxy_off/myip) ────────────────────
|
||||
|
||||
# Получить текущий внешний IP (или $null при ошибке/таймауте).
|
||||
function Get-PublicIp {
|
||||
$CurlExe = Get-Command curl.exe -ErrorAction SilentlyContinue
|
||||
if ($null -ne $CurlExe) {
|
||||
try {
|
||||
$CurrentIp = & $CurlExe.Source --silent --show-error --max-time 3 https://ifconfig.me/ip
|
||||
return ([string]$CurrentIp).Trim()
|
||||
}
|
||||
catch {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return ([string](Invoke-RestMethod -Uri 'https://ifconfig.me/ip' -TimeoutSec 3)).Trim()
|
||||
}
|
||||
catch {
|
||||
return $null
|
||||
}
|
||||
try { return ([string](Invoke-RestMethod -Uri 'https://ifconfig.me/ip' -TimeoutSec 3)).Trim() }
|
||||
catch { return $null }
|
||||
}
|
||||
|
||||
# Включить прокси для текущей сессии (hproxy.local).
|
||||
function proxy_on {
|
||||
$ProxyHost = 'hproxy.local'
|
||||
$ProxyPort = 12334
|
||||
$Host_ = 'hproxy.local'; $Port = 12334
|
||||
|
||||
$env:HTTP_PROXY = 'http://{0}:{1}' -f $ProxyHost, $ProxyPort
|
||||
$env:HTTPS_PROXY = 'http://{0}:{1}' -f $ProxyHost, $ProxyPort
|
||||
$env:ALL_PROXY = 'socks5://{0}:{1}' -f $ProxyHost, $ProxyPort
|
||||
$env:NO_PROXY = '{0},localhost,127.0.0.1,.concept-software.ru' -f $ProxyHost
|
||||
$env:HTTP_PROXY = "http://${Host_}:${Port}"
|
||||
$env:HTTPS_PROXY = "http://${Host_}:${Port}"
|
||||
$env:ALL_PROXY = "socks5://${Host_}:${Port}"
|
||||
$env:NO_PROXY = "${Host_},localhost,127.0.0.1,.concept-software.ru"
|
||||
|
||||
'Proxy enabled. Checking route...'
|
||||
$CurrentIp = Get-PublicIp
|
||||
if ([string]::IsNullOrWhiteSpace($CurrentIp)) {
|
||||
'Current IP: unavailable'
|
||||
}
|
||||
else {
|
||||
'Current IP: {0}' -f $CurrentIp
|
||||
}
|
||||
$Ip = Get-PublicIp
|
||||
"Proxy ON. IP: $(if ($Ip) { $Ip } else { 'unavailable' })"
|
||||
}
|
||||
|
||||
# Выключить прокси для текущей сессии.
|
||||
function proxy_off {
|
||||
foreach ($VariableName in 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY') {
|
||||
Remove-Item -Path ('Env:{0}' -f $VariableName) -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
'Proxy disabled. Checking route...'
|
||||
$CurrentIp = Get-PublicIp
|
||||
if ([string]::IsNullOrWhiteSpace($CurrentIp)) {
|
||||
'Current IP: unavailable'
|
||||
}
|
||||
else {
|
||||
'Current IP: {0}' -f $CurrentIp
|
||||
foreach ($V in 'HTTP_PROXY','HTTPS_PROXY','ALL_PROXY','NO_PROXY') {
|
||||
Remove-Item -Path "Env:$V" -ErrorAction SilentlyContinue
|
||||
}
|
||||
$Ip = Get-PublicIp
|
||||
"Proxy OFF. IP: $(if ($Ip) { $Ip } else { 'unavailable' })"
|
||||
}
|
||||
|
||||
# Показать текущий внешний IP.
|
||||
function myip {
|
||||
$CurrentIp = Get-PublicIp
|
||||
if ([string]::IsNullOrWhiteSpace($CurrentIp)) {
|
||||
'IP: unavailable'
|
||||
}
|
||||
else {
|
||||
'IP: {0}' -f $CurrentIp
|
||||
}
|
||||
$Ip = Get-PublicIp
|
||||
"IP: $(if ($Ip) { $Ip } else { 'unavailable' })"
|
||||
}
|
||||
|
||||
# ── Шорткаты Windows Terminal ────────────────────────────────────────
|
||||
|
||||
if (Get-Command wt -ErrorAction SilentlyContinue) {
|
||||
function wt-here {
|
||||
wt -d (Get-Location).ProviderPath
|
||||
}
|
||||
|
||||
function wt-tab-here {
|
||||
wt -w 0 new-tab -d (Get-Location).ProviderPath
|
||||
}
|
||||
|
||||
function wt-split-here {
|
||||
wt -w 0 split-pane -d (Get-Location).ProviderPath
|
||||
}
|
||||
# Новое окно / вкладка / split-pane Windows Terminal в текущей директории.
|
||||
function wt_here { wt -d (Get-Location).ProviderPath }
|
||||
function wt_tab_here { wt -w 0 new-tab -d (Get-Location).ProviderPath }
|
||||
function wt_split_here { wt -w 0 split-pane -d (Get-Location).ProviderPath }
|
||||
}
|
||||
|
||||
# Keep stable hooks for machine-local overrides and future managed fragments.
|
||||
# ── Локальные дополнения ─────────────────────────────────────────────
|
||||
# Машинно-специфичные добавки: profile.extra.ps1 или profile.d/*.ps1
|
||||
|
||||
$ExtraProfile = Join-Path $HOME '.config\powershell\profile.extra.ps1'
|
||||
if (Test-Path -LiteralPath $ExtraProfile) {
|
||||
. $ExtraProfile
|
||||
}
|
||||
if (Test-Path -LiteralPath $ExtraProfile) { . $ExtraProfile }
|
||||
|
||||
$ExtraProfileDir = Join-Path $HOME '.config\powershell\profile.d'
|
||||
if (Test-Path -LiteralPath $ExtraProfileDir) {
|
||||
foreach ($ProfileFragment in (Get-ChildItem -Path $ExtraProfileDir -Filter '*.ps1' -File | Sort-Object Name)) {
|
||||
. $ProfileFragment.FullName
|
||||
}
|
||||
Get-ChildItem -Path $ExtraProfileDir -Filter '*.ps1' -File |
|
||||
Sort-Object Name |
|
||||
ForEach-Object { . $_.FullName }
|
||||
}
|
||||
|
||||
# Import the Chocolatey Profile that contains the necessary code to enable
|
||||
# tab-completions to function for `choco`.
|
||||
# Be aware that if you are missing these lines from your profile, tab completion
|
||||
# for `choco` will not function.
|
||||
# See https://ch0.co/tab-completion for details.
|
||||
# ── Chocolatey tab-completion ────────────────────────────────────────
|
||||
$ChocolateyProfile = "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||
if (Test-Path -LiteralPath $ChocolateyProfile) {
|
||||
Import-Module $ChocolateyProfile
|
||||
}
|
||||
if (Test-Path -LiteralPath $ChocolateyProfile) { Import-Module $ChocolateyProfile }
|
||||
Reference in new issue
Block a user