<# .SYNOPSIS Save-and-run launcher for a ShopDB dev workstation (stable or edge). .DESCRIPTION Download this one file and run it. It clones (or updates) the repo at the channel branch into a local path, then hands off to scripts/dev-bootstrap.ps1 for the real dependency-check / build / up work. ge-aero/shopdb-flask is a PRIVATE repo, but this needs no token pasting: the clone uses a plain https URL and Git Credential Manager supplies your stored GitHub credential automatically - the same way `git clone` works in the wiki DEVELOPMENT-SETUP procedure. If you have ever cloned this repo or signed in to GitHub in Visual Studio / VS Code on this box, GCM already has what it needs. This is a downloaded FILE, not a web `iex` one-liner, precisely because a private repo cannot be fetched anonymously from raw.githubusercontent.com. A saved script sidesteps that entirely - only the git operations need auth, and GCM handles those. .PARAMETER Channel 'stable' (main) or 'edge' (dev-be). Defaults to 'stable'. .PARAMETER TargetDir Where to clone. Defaults to %USERPROFILE%\shopdb-dev, a separate working copy from any repo you already have open. .EXAMPLE # Bleeding edge: powershell -ExecutionPolicy Bypass -File .\Start-ShopDBDev.ps1 -Channel edge .EXAMPLE # Stable: powershell -ExecutionPolicy Bypass -File .\Start-ShopDBDev.ps1 -Channel stable .NOTES ASCII only, no emoji or smart punctuation, per repo style. PowerShell 5.1+. Needs Git (with Credential Manager, the Git-for-Windows default); dev-bootstrap.ps1 offers to winget-install anything else that is missing. #> [CmdletBinding()] param( [ValidateSet('stable', 'edge')] [string]$Channel = 'stable', [string]$TargetDir ) $ErrorActionPreference = 'Stop' # Reset leftovers from an interrupted or timed-out prior run so the retry starts # clean. Proxy bottlenecks can kill a build mid-flight, leaving orphaned # containers holding the channel ports, dangling build cache from the partial # build, and sometimes a half-written clone. This is scoped to the channel and # is non-destructive to data: the compose down keeps named volumes (the DB), and # only dangling build cache is pruned - never tagged images or volumes. function Clear-InterruptedRun { param( [Parameter(Mandatory)][string]$Project, [Parameter(Mandatory)][string]$Target ) if (Get-Command docker -ErrorAction SilentlyContinue) { # docker/compose write warnings to stderr (e.g. "No resource found to # remove"), which would trip $ErrorActionPreference='Stop' and abort the # whole run. These cleanup steps are best-effort, so soften the preference # and swallow all streams for their duration. $previous = $ErrorActionPreference $ErrorActionPreference = 'Continue' try { # Only try compose down when the engine is actually responding, otherwise # skip quietly - a fresh box may not have Docker started yet. docker info *> $null if ($LASTEXITCODE -eq 0) { Write-Host "==> Cleaning up any interrupted '$Project' stack" -ForegroundColor Cyan # --remove-orphans clears containers left by a killed build; volumes are # intentionally kept so the DB survives a retry. docker compose -p $Project down --remove-orphans *> $null # Reclaim only dangling build cache from failed/partial builds. docker builder prune -f *> $null } } finally { $ErrorActionPreference = $previous } } # A directory that exists but has no valid .git is a broken/partial clone; # remove it so the clone below can start fresh instead of failing. if ((Test-Path $Target) -and -not (Test-Path (Join-Path $Target '.git'))) { Write-Host "==> Removing incomplete clone at $Target" -ForegroundColor Cyan Remove-Item -Path $Target -Recurse -Force } # A clone created by an elevated (Administrator) run is owned by # BUILTIN\Administrators; a later non-elevated run then trips git's # "detected dubious ownership" guard and every git op fails with exit 128. # That stale clone is effectively unusable for this user, so detect the # condition and remove it here for a clean re-clone rather than surfacing a # misleading auth error later. if (Test-Path (Join-Path $Target '.git')) { $previous = $ErrorActionPreference $ErrorActionPreference = 'Continue' try { $probe = git -C $Target rev-parse --is-inside-work-tree 2>&1 } catch { $probe = "$_" } finally { $ErrorActionPreference = $previous } if ($probe -match 'dubious ownership') { Write-Host "==> Removing clone with mismatched ownership at $Target (was it created from an elevated prompt?)" -ForegroundColor Cyan Remove-Item -Path $Target -Recurse -Force } } } # Plain https URL. No token in the URL: Git Credential Manager injects the # stored GitHub credential for github.com, so clone/fetch authenticate against # the private repo without anything sensitive touching this script or disk. $repoUrl = 'https://github.com/ge-aero/shopdb-flask.git' $branch = if ($Channel -eq 'edge') { 'dev-be' } else { 'main' } $target = if ($TargetDir) { $TargetDir } else { Join-Path $env:USERPROFILE 'shopdb-dev' } Write-Host "ShopDB dev launcher" -ForegroundColor White Write-Host " Channel : $Channel ($branch)" -ForegroundColor White Write-Host " Target : $target" -ForegroundColor White if (-not (Get-Command git -ErrorAction SilentlyContinue)) { throw 'git is required. Install Git for Windows (winget install Git.Git) and re-run.' } $project = if ($Channel -eq 'edge') { 'shopdb-edge' } else { 'shopdb-stable' } Clear-InterruptedRun -Project $project -Target $target if (Test-Path (Join-Path $target '.git')) { Write-Host "==> Updating existing clone" -ForegroundColor Cyan git -C $target fetch origin $branch if ($LASTEXITCODE -ne 0) { throw "git fetch failed ($LASTEXITCODE). If this is an auth error, clone the repo once in Visual Studio / VS Code so Credential Manager stores your GitHub login." } git -C $target checkout $branch if ($LASTEXITCODE -ne 0) { throw "git checkout failed ($LASTEXITCODE)." } git -C $target pull --ff-only origin $branch if ($LASTEXITCODE -ne 0) { throw "git pull failed ($LASTEXITCODE)." } } else { Write-Host "==> Cloning $branch" -ForegroundColor Cyan git clone --branch $branch $repoUrl $target if ($LASTEXITCODE -ne 0) { throw "git clone failed ($LASTEXITCODE). If this is an auth error, sign in to GitHub once in Visual Studio / VS Code (or run 'git clone $repoUrl' manually) so Credential Manager stores your login, then re-run." } } $bootstrap = Join-Path $target 'scripts\dev-bootstrap.ps1' if (-not (Test-Path $bootstrap)) { throw "dev-bootstrap.ps1 not found at $bootstrap. Is the '$branch' branch correct?" } Write-Host "==> Handing off to dev-bootstrap.ps1" -ForegroundColor Cyan & $bootstrap -Channel $Channel