aboutsummaryrefslogtreecommitdiff
path: root/windows/Documents/PowerShell/bootstrap.ps1
diff options
context:
space:
mode:
authorsrdusr <trevorgray@srdusr.com>2025-10-01 17:04:38 +0200
committersrdusr <trevorgray@srdusr.com>2025-10-01 17:04:38 +0200
commit94cab560d29444159e0648bbc0f421bffe28b2e5 (patch)
treefd5ab26a9fc35a64ae8b6222b08d354198a52622 /windows/Documents/PowerShell/bootstrap.ps1
parent3bb4dbc5e0b9b9a003b3d47b7f48a98319ad9108 (diff)
downloaddotfiles-94cab560d29444159e0648bbc0f421bffe28b2e5.tar.gz
dotfiles-94cab560d29444159e0648bbc0f421bffe28b2e5.zip
Testing updated dir structures
Diffstat (limited to 'windows/Documents/PowerShell/bootstrap.ps1')
-rw-r--r--windows/Documents/PowerShell/bootstrap.ps1669
1 files changed, 0 insertions, 669 deletions
diff --git a/windows/Documents/PowerShell/bootstrap.ps1 b/windows/Documents/PowerShell/bootstrap.ps1
deleted file mode 100644
index d2f4369..0000000
--- a/windows/Documents/PowerShell/bootstrap.ps1
+++ /dev/null
@@ -1,669 +0,0 @@
-#!/usr/bin/env pwsh
-
-# Created By: srdusr
-# Created On: Windows PowerShell Bootstrap Script
-# Project: Dotfiles installation script for Windows
-
-# Dependencies: git, powershell
-
-param(
- [string]$Profile = "essentials",
- [switch]$Force = $false,
- [switch]$Ask = $false,
- [switch]$Help = $false
-)
-
-# Color definitions for pretty UI
-$Script:Colors = @{
- Reset = "`e[0m"
- Red = "`e[0;31m"
- Green = "`e[0;32m"
- Yellow = "`e[0;33m"
- Blue = "`e[0;34m"
- Cyan = "`e[0;36m"
- White = "`e[0;37m"
- Bold = "`e[1m"
-}
-
-# Prompt helper: Yes/No with default; in non-Ask mode, returns default immediately
-function Prompt-YesNo {
- param(
- [Parameter(Mandatory=$true)][string]$Question,
- [ValidateSet('Y','N')][string]$Default = 'Y'
- )
- if (-not $Script:AskPreference) {
- return $Default -eq 'Y'
- }
- $suffix = if ($Default -eq 'Y') { '[Y/n]' } else { '[y/N]' }
- while ($true) {
- Write-Host -NoNewline "$Question $suffix: " -ForegroundColor Yellow
- $resp = Read-Host
- if ([string]::IsNullOrWhiteSpace($resp)) { $resp = $Default }
- switch ($resp.ToUpper()) {
- 'Y' { return $true }
- 'YES' { return $true }
- 'N' { return $false }
- 'NO' { return $false }
- default { Write-Warning "Please answer Y/yes or N/no" }
- }
- }
-}
-
-# Configuration
-$Script:Config = @{
- DotfilesUrl = 'https://github.com/srdusr/dotfiles.git'
- DotfilesDir = "$HOME\.cfg"
- LogFile = "$HOME\AppData\Local\dotfiles_install.log"
- StateFile = "$HOME\AppData\Local\dotfiles_install_state"
- BackupDir = "$HOME\.dotfiles-backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
- PackagesFile = "packages.yml"
- OS = "windows"
-}
-
-# Logging functions
-function Write-Log {
- param([string]$Message, [string]$Level = "INFO")
- $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
- $logEntry = "[$timestamp] [$Level] $Message"
- Add-Content -Path $Script:Config.LogFile -Value $logEntry -Force
-}
-
-function Write-ColorOutput {
- param([string]$Message, [string]$Color = "White")
- Write-Host $Message -ForegroundColor $Color
- Write-Log $Message
-}
-
-function Write-Success { param([string]$Message) Write-ColorOutput "✓ $Message" "Green" }
-function Write-Info { param([string]$Message) Write-ColorOutput "ℹ $Message" "Cyan" }
-function Write-Warning { param([string]$Message) Write-ColorOutput "⚠ $Message" "Yellow" }
-function Write-Error { param([string]$Message) Write-ColorOutput "✗ $Message" "Red" }
-
-function Write-Header {
- param([string]$Title)
- Write-Host ""
- Write-Host "=" * 60 -ForegroundColor Blue
- Write-Host " $Title" -ForegroundColor Bold
- Write-Host "=" * 60 -ForegroundColor Blue
- Write-Host ""
-}
-
-# Utility functions
-function Test-CommandExists {
- param([string]$Command)
- return [bool](Get-Command $Command -ErrorAction SilentlyContinue)
-}
-
-function Test-IsAdmin {
- $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
- $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
- return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
-}
-
-function Invoke-AdminCommand {
- param([string]$Command)
- if (-not (Test-IsAdmin)) {
- Write-Warning "Elevating privileges for: $Command"
- Start-Process powershell.exe -ArgumentList "-NoProfile", "-Command", $Command -Verb RunAs -Wait
- } else {
- Invoke-Expression $Command
- }
-}
-
-# Package management functions
-function Get-PackageManager {
- if (Test-CommandExists "choco") { return "chocolatey" }
- if (Test-CommandExists "winget") { return "winget" }
- if (Test-CommandExists "scoop") { return "scoop" }
- return $null
-}
-
-# Return $true if a package appears to be installed for the given manager
-function Test-PackageInstalled {
- param(
- [Parameter(Mandatory=$true)][string]$Manager,
- [Parameter(Mandatory=$true)][string]$Name
- )
- switch ($Manager) {
- "chocolatey" {
- $out = choco list --local-only --exact $Name 2>$null
- return ($out | Select-String -Pattern "^\s*$([regex]::Escape($Name))\s").Length -gt 0
- }
- "winget" {
- # Winget list may return multiple rows; use --exact name match when possible
- $out = winget list --name $Name 2>$null
- return ($out | Select-String -SimpleMatch $Name).Length -gt 0
- }
- "scoop" {
- # scoop list <name> returns 0 when installed
- scoop list $Name *> $null
- return $LASTEXITCODE -eq 0
- }
- default { return $false }
- }
-}
-
-function Install-PackageManager {
- Write-Header "Installing Package Manager"
-
- if (-not (Test-CommandExists "choco")) {
- Write-Info "Installing Chocolatey..."
- Set-ExecutionPolicy Bypass -Scope Process -Force
- [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
- Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
-
- if (Test-CommandExists "choco") {
- Write-Success "Chocolatey installed successfully"
- } else {
- Write-Error "Failed to install Chocolatey"
- return $false
- }
- } else {
- Write-Info "Chocolatey already installed"
- }
- return $true
-}
-
-function Install-Packages {
- param([string]$PackagesFile, [string]$Profile)
-
- if (-not (Test-Path $PackagesFile)) {
- Write-Warning "Packages file not found: $PackagesFile"
- return
- }
-
- Write-Header "Installing Packages"
-
- # Install powershell-yaml if not available
- if (-not (Get-Module powershell-yaml -ListAvailable)) {
- Write-Info "Installing powershell-yaml module..."
- $policy = Get-PSRepository -Name 'PSGallery' | Select-Object -ExpandProperty InstallationPolicy
- Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
- Install-Module powershell-yaml -Force
- Set-PSRepository -Name 'PSGallery' -InstallationPolicy $policy
- }
-
- Import-Module powershell-yaml
-
- # Helper: run custom_installs.<name>.windows if condition passes
- function Invoke-CustomInstallsWindows {
- param([Parameter(Mandatory=$true)]$Yaml)
- if (-not $Yaml.custom_installs) { return }
- foreach ($name in $Yaml.custom_installs.PSObject.Properties.Name) {
- $entry = $Yaml.custom_installs.$name
- if (-not $entry) { continue }
- $winCmd = $entry.windows
- if (-not $winCmd) { continue }
- $shouldRun = $true
- if ($entry.condition) {
- $cond = [string]$entry.condition
- if ($cond -match "!\s*command\s+-v\s+([A-Za-z0-9._-]+)") {
- $shouldRun = -not (Test-CommandExists $Matches[1])
- } elseif ($cond -match "command\s+-v\s+([A-Za-z0-9._-]+)") {
- $shouldRun = (Test-CommandExists $Matches[1])
- }
- }
- if (-not $shouldRun) { Write-Info "Skipping custom install: $name"; continue }
- Write-Info "Running custom install: $name"
- try { Invoke-Expression $winCmd; Write-Success "Custom install completed: $name" }
- catch { Write-Warning "Custom install failed for '$name': $_" }
- }
- }
-
- try {
- $packages = Get-Content $PackagesFile | ConvertFrom-Yaml
- $packageManager = Get-PackageManager
-
- if (-not $packageManager) {
- Write-Error "No package manager available"
- return
- }
-
- # Get packages for current profile and OS
- $profilePackages = @()
- if ($packages.profiles.$Profile.windows) {
- $profilePackages += $packages.profiles.$Profile.windows
- }
- if ($packages.profiles.$Profile.common) {
- $profilePackages += $packages.profiles.$Profile.common
- }
-
- foreach ($package in $profilePackages) {
- $packageName = if ($packages.packages.$package.windows) {
- $packages.packages.$package.windows
- } else {
- $package
- }
-
- if (Test-PackageInstalled -Manager $packageManager -Name $packageName) {
- Write-Info "Already installed: $packageName"
- continue
- }
-
- Write-Info "Installing package: $packageName"
-
- switch ($packageManager) {
- "chocolatey" {
- if (-not (choco list --local-only | Select-String -Pattern "^$packageName\s")) {
- choco install $packageName -y
- if ($LASTEXITCODE -eq 0) {
- Write-Success "Installed: $packageName"
- } else {
- Write-Error "Failed to install: $packageName"
- }
- } else {
- Write-Info "Already installed: $packageName"
- }
- }
- "winget" {
- winget install $packageName --accept-package-agreements --accept-source-agreements
- }
- "scoop" {
- scoop install $packageName
- }
- }
- }
-
- # Also install top-level Windows packages list if present
- if ($packages.windows) {
- foreach ($pkg in $packages.windows) {
- if ([string]::IsNullOrWhiteSpace($pkg)) { continue }
- if (Test-PackageInstalled -Manager $packageManager -Name $pkg) { Write-Info "Already installed: $pkg"; continue }
- Write-Info "Installing package: $pkg"
- switch ($packageManager) {
- "chocolatey" {
- if (-not (choco list --local-only | Select-String -Pattern "^$([regex]::Escape($pkg))\s")) { choco install $pkg -y }
- }
- "winget" { winget install --id $pkg --silent --accept-package-agreements --accept-source-agreements }
- "scoop" { scoop install $pkg }
- }
- }
- }
-
- # Execute Windows custom installs from packages.yml
- Invoke-CustomInstallsWindows -Yaml $packages
- } catch {
- Write-Error "Error processing packages: $_"
- }
-}
-
-# Dotfiles management functions
-function Install-Dotfiles {
- Write-Header "Installing Dotfiles"
-
- if (Test-Path $Script:Config.DotfilesDir) {
- Write-Info "Updating existing dotfiles repository..."
- & git --git-dir="$($Script:Config.DotfilesDir)" --work-tree="$($Script:Config.DotfilesDir)" pull origin main
- } else {
- Write-Info "Cloning dotfiles repository..."
- git clone --bare $Script:Config.DotfilesUrl $Script:Config.DotfilesDir
-
- if (-not (Test-Path $Script:Config.DotfilesDir)) {
- Write-Error "Failed to clone dotfiles repository"
- return $false
- }
- }
-
- # Set up config alias for this session
- function script:config {
- git --git-dir="$($Script:Config.DotfilesDir)" --work-tree="$($Script:Config.DotfilesDir)" @args
- }
-
- # Configure repository
- config config --local status.showUntrackedFiles no
-
- # Checkout files to restore directory structure
- Write-Info "Checking out dotfiles..."
- try {
- config checkout 2>$null
- if ($LASTEXITCODE -ne 0) {
- Write-Info "Forcing checkout to overwrite existing files..."
- config checkout -f
- }
- Write-Success "Dotfiles checked out successfully"
- } catch {
- Write-Error "Failed to checkout dotfiles: $_"
- return $false
- }
-
- return $true
-}
-
-function Deploy-Dotfiles {
- Write-Header "Deploying Dotfiles"
-
- if (-not (Test-Path $Script:Config.DotfilesDir)) {
- Write-Error "Dotfiles directory not found. Run Install-Dotfiles first."
- return $false
- }
-
- # Source the config command from profile if available
- $profilePath = "$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
- if (Test-Path $profilePath) {
- Write-Info "Loading config command from profile..."
- . $profilePath
- }
-
- # Deploy using config command if available, otherwise manual deployment
- if (Get-Command config -ErrorAction SilentlyContinue) {
- Write-Info "Deploying dotfiles using config command..."
- config deploy
- } else {
- Write-Warning "Config command not available, using manual deployment..."
- # Manual deployment fallback would go here
- }
-
- Write-Success "Dotfiles deployment completed"
- return $true
-}
-
-# Locate profile-specific packages.yml similar to Linux installer
-function Get-ProfilePackagesFile {
- param([string]$Profile)
- $candidates = @(
- Join-Path $HOME ".cfg/profile/$Profile/packages.yml",
- Join-Path $HOME "profile/$Profile/packages.yml",
- Join-Path $HOME "dot_setup/profile/$Profile/packages.yml",
- Join-Path $Script:Config.DotfilesDir "common/$($Script:Config.PackagesFile)"
- )
- foreach ($pf in $candidates) {
- if (Test-Path $pf) { return $pf }
- }
- return $null
-}
-
-# System configuration functions
-function Set-WindowsConfiguration {
- param(
- [string]$PackagesFile
- )
-
- Write-Header "Configuring Windows Settings"
-
- if (-not $PackagesFile -or -not (Test-Path $PackagesFile)) {
- Write-Warning "Packages file not found, skipping Windows configuration"
- return
- }
-
- try {
- # Load YAML content
- $yamlContent = Get-Content $PackagesFile -Raw | ConvertFrom-Yaml
- $registrySettings = $yamlContent.system_tweaks.windows.registry
-
- if (-not $registrySettings) {
- Write-Warning "No Windows registry settings found in packages.yml"
- return
- }
-
- Write-Info "Applying registry settings from packages.yml..."
-
- foreach ($setting in $registrySettings) {
- try {
- $path = $setting.path
- $name = $setting.name
- $value = $setting.value
- $type = $setting.type
- $description = $setting.description
-
- Write-Info "Setting: $description"
-
- # Ensure the registry path exists
- $pathParts = $path -split '\\'
- $currentPath = $pathParts[0]
- for ($i = 1; $i -lt $pathParts.Length; $i++) {
- $currentPath = "$currentPath\$($pathParts[$i])"
- if (-not (Test-Path $currentPath)) {
- New-Item -Path $currentPath -Force | Out-Null
- }
- }
-
- # Set the registry value
- Set-ItemProperty -Path $path -Name $name -Value $value -Type $type -Force
- Write-Success "Applied: $description"
-
- } catch {
- Write-Warning "Failed to apply setting '$($setting.description)': $_"
- }
- }
-
- Write-Success "Windows configuration applied"
-
- # Restart explorer to apply changes
- Write-Info "Restarting Windows Explorer..."
- Stop-Process -Name explorer -Force
- Start-Process explorer.exe
-
- } catch {
- Write-Warning "Failed to process Windows configuration: $_"
- }
-}
-
-function Enable-WindowsFeatures {
- param(
- [string]$PackagesFile
- )
-
- Write-Header "Enabling Windows Features"
-
- if (-not $PackagesFile -or -not (Test-Path $PackagesFile)) {
- Write-Warning "Packages file not found, skipping Windows features"
- return
- }
-
- try {
- # Load YAML content
- $yamlContent = Get-Content $PackagesFile -Raw | ConvertFrom-Yaml
- $features = $yamlContent.system_tweaks.windows.features
-
- if (-not $features) {
- Write-Warning "No Windows features found in packages.yml"
- return
- }
-
- foreach ($feature in $features) {
- $featureName = $feature.name
- $description = $feature.description
- $requiresAdmin = $feature.requires_admin
-
- if ($requiresAdmin -and -not (Test-IsAdmin)) {
- Write-Warning "Skipping '$description' - requires administrator privileges"
- continue
- }
-
- try {
- Write-Info "Enabling: $description"
- dism.exe /online /enable-feature /featurename:$featureName /all /norestart
- Write-Success "Enabled: $description"
- } catch {
- Write-Warning "Failed to enable '$description': $_"
- }
- }
-
- Write-Success "Windows features processing complete (restart may be required)"
-
- } catch {
- Write-Warning "Failed to process Windows features: $_"
- }
-}
-
-function Install-PowerShellProfile {
- Write-Header "Setting up PowerShell Profile"
-
- $documentsPath = [System.Environment]::GetFolderPath('MyDocuments')
- $powerShellProfileDir = "$documentsPath\PowerShell"
- $profilePath = "$powerShellProfileDir\Microsoft.PowerShell_profile.ps1"
-
- Write-Info "PowerShell profile directory: $powerShellProfileDir"
-
- if (-not (Test-Path $powerShellProfileDir)) {
- New-Item -ItemType Directory -Path $powerShellProfileDir -Force | Out-Null
- Write-Success "Created PowerShell profile directory"
- }
-
- # Copy profile from dotfiles if it exists
- $dotfilesProfile = "$($Script:Config.DotfilesDir)\windows\Documents\PowerShell\Microsoft.PowerShell_profile.ps1"
- if (Test-Path $dotfilesProfile) {
- Copy-Item $dotfilesProfile $profilePath -Force
- Write-Success "PowerShell profile installed from dotfiles"
- } else {
- Write-Warning "PowerShell profile not found in dotfiles"
- }
-}
-
-# Main execution function
-function Start-Bootstrap {
- param([string]$Profile, [switch]$Force, [switch]$Ask)
-
- Write-Header "Windows Dotfiles Bootstrap"
- Write-Info "Profile: $Profile"
- Write-Info "Force mode: $Force"
- Write-Info "Interactive mode: $Ask"
-
- # Initialize logging
- $logDir = Split-Path $Script:Config.LogFile
- if (-not (Test-Path $logDir)) {
- New-Item -ItemType Directory -Path $logDir -Force | Out-Null
- }
-
- Write-Log "Bootstrap started with profile: $Profile"
-
- # Set Ask preference for all prompts
- $Script:AskPreference = [bool]$Ask
-
- # Check dependencies
- Write-Header "Checking Dependencies"
- $requiredCommands = @("git", "powershell")
- $missingCommands = @()
-
- foreach ($cmd in $requiredCommands) {
- if (-not (Test-CommandExists $cmd)) {
- $missingCommands += $cmd
- Write-Error "Required command not found: $cmd"
- } else {
- Write-Success "Found: $cmd"
- }
- }
-
- if ($missingCommands.Count -gt 0) {
- Write-Error "Missing required dependencies. Please install: $($missingCommands -join ', ')"
- return $false
- }
-
- # Install package manager (skippable)
- if (Prompt-YesNo -Question "Install/check package manager?" -Default 'Y') {
- if (-not (Install-PackageManager)) {
- Write-Error "Failed to install package manager"
- return $false
- }
- } else {
- Write-Warning "Skipped package manager step by user choice"
- }
-
- # Install dotfiles
- if (Prompt-YesNo -Question "Install or update dotfiles?" -Default 'Y') {
- if (-not (Install-Dotfiles)) {
- Write-Error "Failed to install dotfiles"
- return $false
- }
- } else {
- Write-Warning "Skipped dotfiles installation by user choice"
- }
-
- # Get packages file (profile-aware)
- $packagesFile = Get-ProfilePackagesFile -Profile $Profile
- if (-not $packagesFile) {
- Write-Error "Failed to get packages file for profile '$Profile'"
- return $false
- }
-
- # Install packages
- if (Prompt-YesNo -Question "Install profile packages?" -Default 'Y') {
- Install-Packages -PackagesFile $packagesFile -Profile $Profile
- } else {
- Write-Warning "Skipped package installation by user choice"
- }
-
- # Set up PowerShell profile
- if (Prompt-YesNo -Question "Install PowerShell profile?" -Default 'Y') {
- Install-PowerShellProfile
- } else {
- Write-Warning "Skipped PowerShell profile setup by user choice"
- }
-
- # Deploy dotfiles
- if (Prompt-YesNo -Question "Deploy dotfiles to system locations?" -Default 'Y') {
- if (-not (Deploy-Dotfiles)) {
- Write-Error "Failed to deploy dotfiles"
- return $false
- }
- } else {
- Write-Warning "Skipped dotfiles deployment by user choice"
- }
-
- # Configure Windows
- if (Prompt-YesNo -Question "Apply Windows configuration from packages.yml?" -Default 'N') {
- Set-WindowsConfiguration -PackagesFile $packagesPath
- } else {
- Write-Warning "Skipped Windows configuration by user choice"
- }
-
- # Enable Windows features (if admin)
- if (Prompt-YesNo -Question "Enable Windows optional features?" -Default 'N') {
- Enable-WindowsFeatures -PackagesFile $packagesPath
- } else {
- Write-Warning "Skipped enabling Windows features by user choice"
- }
-
- Write-Header "Bootstrap Complete"
- Write-Success "Windows dotfiles bootstrap completed successfully!"
- Write-Info "Please restart your computer to apply all changes."
- Write-Log "Bootstrap completed successfully"
-
- return $true
-}
-
-# Help function
-function Show-Help {
- Write-Host @"
-Windows Dotfiles Bootstrap Script
-
-USAGE:
- .\bootstrap.ps1 [-Profile <profile>] [-Force] [-Ask] [-Help]
-
-PARAMETERS:
- -Profile <string> Installation profile (default: essentials)
- Available: essentials, minimal, dev, server, full, or a custom profile.
- Custom profile files are resolved from:
- - %USERPROFILE%\.cfg\profile\<name>\packages.yml
- - %USERPROFILE%\profile\<name>\packages.yml
- - %USERPROFILE%\dot_setup\profile\<name>\packages.yml
- -Force Force installation without prompts
- -Ask Interactive mode with step-by-step prompts
- -Help Show this help message
-
-EXAMPLES:
- .\bootstrap.ps1 # Install with essentials profile
- .\bootstrap.ps1 -Profile dev # Install development profile
- .\bootstrap.ps1 -Profile full -Force # Force install full profile
- .\bootstrap.ps1 -Ask # Interactive installation
-
-"@ -ForegroundColor Cyan
-}
-
-# Script entry point
-if ($Help) {
- Show-Help
- exit 0
-}
-
-# Run bootstrap
-try {
- $result = Start-Bootstrap -Profile $Profile -Force:$Force -Ask:$Ask
- if (-not $result) {
- exit 1
- }
-} catch {
- Write-Error "Bootstrap failed: $_"
- Write-Log "Bootstrap failed: $_" "ERROR"
- exit 1
-}