Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/agent-memory/archgate-developer/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Skipping steps 2 or 3 is a workflow violation. The user should NEVER have to inv
- **GCM prompt suppression requires 5 env vars** — `GIT_TERMINAL_PROMPT=0` alone does NOT prevent Git Credential Manager (GCM) from showing GUI prompts on Windows or askpass prompts on Linux. The full set for `gitCredentialEnv()` in `src/helpers/credential-store.ts` is: `GIT_TERMINAL_PROMPT=0`, `GCM_INTERACTIVE=never`, `GCM_GUI_PROMPT=false`, `GIT_ASKPASS=""`, `SSH_ASKPASS=""`. Omitting any one can trigger unexpected prompts in editor contexts where the CLI runs as a subprocess.
- **Module-level `{ ...Bun.env }` captures env at import time** — Spreading `Bun.env` into a module-level constant freezes the env snapshot. Tests that override `Bun.env.HOME` after import won't affect the constant. Fix: use a function that returns `{ ...Bun.env, ... }` on each call so it picks up test-time overrides. Applied in `src/helpers/credential-store.ts`.
- **`Bun.Glob.scan({ dot: false })` silently drops dot-prefixed segments — even on explicit paths** — `dot: false` (the default) skips matches whose path contains a `.`-prefixed segment, including patterns that explicitly name the dir (e.g. `.github/workflows/release.yml`). Behavior also varies across platforms — Windows reliably drops the match while Linux can match the same pattern, so a rule appears to "work in CI" but no-ops locally. For code repos where `.github/`, `.husky/`, `.vscode/` are first-class source dirs, ALWAYS pass `dot: true` to `Bun.Glob.scan()`. Applied in `src/engine/runner.ts` (`ctx.glob`, `ctx.grepFiles`) and `src/engine/git-files.ts` (`resolveScopedFiles`). See archgate/cli#222.
- **PowerShell 5.1 reads BOM-less `.ps1` files as ANSI — never use non-ASCII chars in `install.ps1`** — `install.ps1` has no UTF-8 BOM (first bytes are `# A...`). On Windows PowerShell 5.1, this means the file is decoded as the system codepage (Windows-1252), so multi-byte UTF-8 characters like em-dash (`—`, `\xE2\x80\x94`) get split into garbage bytes that break later string parsing — the parser reports cryptic errors like "string is missing the terminator" on lines that look fine. ALWAYS stick to ASCII (`-`, `--`, straight quotes) in comments and string literals in `install.ps1`. To verify after editing: `[System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path .\install.ps1).Path, [ref]$null, [ref]$errs)`. Same caveat applies to any unsigned `.ps1` file the project distributes.

## Validation Pipeline

Expand Down
95 changes: 75 additions & 20 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -109,50 +109,105 @@ if ($CurrentPath -notlike "*$InstallDir*") {
Write-Host "Restart your terminal for the change to take effect in new sessions."
}

# --- Update Git Bash / MSYS2 shell profiles ---
# --- Update shell profiles (Git Bash + PowerShell) ---
#
# These shells share the Windows ecosystem but each have their own profile
# system. We detect existing profiles only (never create new ones) and
# prompt once for the whole batch.

$GitBashProfiles = @()
$ProfileUpdates = @()

# Git Bash / MSYS2 - first matching rc file wins
$InstallDirPosix = $InstallDir -replace '\\', '/'
if ($InstallDirPosix -match '^([A-Za-z]):') {
$InstallDirPosix = '/' + $Matches[1].ToLower() + $InstallDirPosix.Substring(2)
}
$BashLine = "export PATH=`"${InstallDirPosix}:`$PATH`""
foreach ($f in @("$HOME\.bashrc", "$HOME\.bash_profile", "$HOME\.profile")) {
if (Test-Path $f) {
$GitBashProfiles += $f
$ProfileUpdates += [pscustomobject]@{
Path = $f
Line = $BashLine
Label = "Git Bash"
Match = $InstallDirPosix
}
break
}
}

$InstallDirPosix = $InstallDir -replace '\\', '/'
if ($InstallDirPosix -match '^([A-Za-z]):') {
$InstallDirPosix = '/' + $Matches[1].ToLower() + $InstallDirPosix.Substring(2)
# PowerShell 5.1 (Windows PowerShell) and PowerShell 7+ have separate profile
# paths and can coexist on the same machine - detect both. Use
# GetFolderPath('MyDocuments') so OneDrive-redirected Documents folders work.
try {
$DocsDir = [Environment]::GetFolderPath('MyDocuments')
} catch {
$DocsDir = "$HOME\Documents"
}
$PSLine = "`$env:PATH = `"$InstallDir;`$env:PATH`""
$PSProfileCandidates = @(
@{ Path = Join-Path $DocsDir "WindowsPowerShell\Microsoft.PowerShell_profile.ps1"; Label = "PowerShell 5.1" },
@{ Path = Join-Path $DocsDir "PowerShell\Microsoft.PowerShell_profile.ps1"; Label = "PowerShell 7+" }
)
foreach ($entry in $PSProfileCandidates) {
if (Test-Path $entry.Path) {
$ProfileUpdates += [pscustomobject]@{
Path = $entry.Path
Line = $PSLine
Label = $entry.Label
Match = $InstallDir
}
}
}
$PathLine = "export PATH=`"${InstallDirPosix}:`$PATH`""

# Filter to profiles that don't already reference the install dir.
# Read failures are warnings, not fatal - skip the file and continue.
$NeedsUpdate = @()
foreach ($f in $GitBashProfiles) {
if (-not (Select-String -Path $f -SimpleMatch $InstallDirPosix -Quiet)) {
$NeedsUpdate += $f
foreach ($entry in $ProfileUpdates) {
try {
$alreadyHas = Select-String -Path $entry.Path -SimpleMatch $entry.Match -Quiet -ErrorAction Stop
if (-not $alreadyHas) {
$NeedsUpdate += $entry
}
} catch {
Write-Warning "Could not read $($entry.Path): $($_.Exception.Message)"
}
}

if ($NeedsUpdate.Count -gt 0) {
Write-Host ""
Write-Host "Detected Git Bash shell profiles to update:"
foreach ($f in $NeedsUpdate) {
Write-Host " $f -> $PathLine"
Write-Host "Detected shell profiles to update:"
foreach ($entry in $NeedsUpdate) {
Write-Host " [$($entry.Label)] $($entry.Path)"
Write-Host " $($entry.Line)"
}
Write-Host ""

$answer = Read-Host "Update these files now? [Y/n]"
if ($answer -match '^[nN]') {
Write-Host ""
Write-Host "Skipped. To add manually, append this line to your shell profile:"
Write-Host ""
Write-Host " $PathLine"
Write-Host "Skipped. To add manually, append the corresponding line above to each profile."
} else {
foreach ($f in $NeedsUpdate) {
Add-Content -Path $f -Value "`n# archgate`n$PathLine"
Write-Host " Updated: $f"
$updated = 0
$failed = 0
foreach ($entry in $NeedsUpdate) {
try {
Add-Content -Path $entry.Path -Value "`n# archgate`n$($entry.Line)" -ErrorAction Stop
Write-Host " Updated: $($entry.Path)"
$updated++
} catch {
Write-Warning "Failed to update $($entry.Path): $($_.Exception.Message)"
Write-Host " Manually append to that file:"
Write-Host " $($entry.Line)"
$failed++
}
}
Write-Host ""
Write-Host "Restart Git Bash for the change to take effect."
if ($updated -gt 0) {
Write-Host "Restart the relevant shell(s) for the change to take effect."
}
if ($failed -gt 0) {
Write-Host "$failed profile(s) could not be updated automatically - see warnings above."
}
}
}

Expand Down
Loading