#!/usr/bin/env pwsh <# .SYNOPSIS Installs all prerequisites for Git Workshop using winget and clones the repository. .DESCRIPTION This script automates the installation of required tools for the Git Workshop: - PowerShell 7 (cross-platform PowerShell) - Git 2.23+ (version control system) - Visual Studio Code (code editor with Git integration) Optional tools (with user prompts): - Windows Terminal (modern terminal experience) The script checks for existing installations, shows clear progress, and verifies each installation succeeded. At the end, it clones the repository and can open it in VSCode for immediate workshop access. One-shot installation: Invoke-RestMethod -Uri https://git.frod.dk/floppydiscen/git-workshop/raw/branch/main/install.ps1 | Invoke-Expression .EXAMPLE PS> Invoke-RestMethod -Uri https://git.frod.dk/floppydiscen/git-workshop/raw/branch/main/install.ps1 | Invoke-Expression Runs the complete installation and setup in one command. .EXAMPLE PS> .\install.ps1 Runs the installation script with interactive prompts. .NOTES Requires Windows 11 with winget (App Installer) available. Some installations may require administrator privileges. #> [CmdletBinding()] param() Set-StrictMode -Version Latest $ErrorActionPreference = 'Continue' # Continue on errors to show all results #region Helper Functions function Write-ColorMessage { param( [string]$Message, [string]$Color = 'White' ) Write-Host $Message -ForegroundColor $Color } function Write-Step { param([string]$Message) Write-ColorMessage "`n=== $Message ===" -Color Cyan } function Write-Success { param([string]$Message) Write-ColorMessage " ✓ $Message" -Color Green } function Write-Warning { param([string]$Message) Write-ColorMessage " ⚠ $Message" -Color Yellow } function Write-Error { param([string]$Message) Write-ColorMessage " ✗ $Message" -Color Red } function Test-CommandExists { param([string]$Command) $oldPreference = $ErrorActionPreference $ErrorActionPreference = 'SilentlyContinue' try { if (Get-Command $Command -ErrorAction SilentlyContinue) { return $true } return $false } finally { $ErrorActionPreference = $oldPreference } } function Get-InstalledVersion { param( [string]$Command, [string]$VersionArg = '--version' ) try { $output = & $Command $VersionArg 2>&1 | Select-Object -First 1 return $output.ToString().Trim() } catch { return $null } } function Test-WingetAvailable { if (-not (Test-CommandExists 'winget')) { Write-Error "winget is not available on this system." Write-Host "`nTo fix this:" -ForegroundColor Yellow Write-Host " 1. Update Windows 11 to the latest version (Settings → Windows Update)" -ForegroundColor White Write-Host " 2. Install 'App Installer' from the Microsoft Store" -ForegroundColor White Write-Host " 3. Restart your computer and run this script again" -ForegroundColor White return $false } return $true } function Install-Package { param( [string]$Name, [string]$WingetId, [string]$CheckCommand, [string]$MinVersion = $null, [string]$AdditionalArgs = '' ) Write-Step "Installing $Name" # Check if already installed if (Test-CommandExists $CheckCommand) { $version = Get-InstalledVersion $CheckCommand Write-Success "$Name is already installed: $version" if ($MinVersion -and $version) { # Extract semantic version numbers only - stop before any non-digit/non-dot characters # This extracts "2.52.0" from "2.52.0.windows.1" if ($version -match '(\d+)(?:\.(\d+))?(?:\.(\d+))?') { $installedVersion = $matches[1] + "." +$matches[2] try { if ([version]$installedVersion -lt [version]$MinVersion) { Write-Warning "Version $installedVersion is below minimum required version $MinVersion" Write-Host " Attempting to upgrade..." -ForegroundColor Cyan } else { return $true } } catch { Write-Warning "Version comparison failed - assuming sufficient version" return $true } } else { Write-Warning "Could not parse version from: $version" Write-Host " Assuming installed version is sufficient..." -ForegroundColor Cyan return $true } } else { return $true } } # Install using winget Write-Host " Installing via winget: $WingetId" -ForegroundColor Cyan $installCmd = "winget install --id $WingetId --source winget --silent $AdditionalArgs".Trim() Write-Host " Running: $installCmd" -ForegroundColor Gray try { # Show progress during installation Write-Progress -Activity "Installing $Name" -Status "Downloading and installing..." -PercentComplete 25 $result = Invoke-Expression $installCmd 2>&1 Write-Progress -Activity "Installing $Name" -Status "Verifying installation..." -PercentComplete 75 # Check if installation succeeded Start-Sleep -Seconds 2 # Give the system time to register the new command # Refresh environment variables in current session $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") if (Test-CommandExists $CheckCommand) { $version = Get-InstalledVersion $CheckCommand Write-Success "$Name installed successfully: $version" Write-Progress -Activity "Installing $Name" -Completed return $true } else { Write-Warning "$Name installation completed, but command '$CheckCommand' not found." Write-Host " You may need to restart your terminal or computer." -ForegroundColor Yellow Write-Progress -Activity "Installing $Name" -Completed return $false } } catch { Write-Error "Failed to install $Name`: $_" Write-Progress -Activity "Installing $Name" -Completed return $false } } function Test-GitVersion { if (-not (Test-CommandExists 'git')) { return $false } $version = Get-InstalledVersion 'git' # Parse Git version from various formats: # "git version 2.52.0", "git version 2.52.0.windows.1", etc. if ($version -match 'git version (\d+)(?:\.(\d+))?(?:\.(\d+))?') { $majorVersion = [int]$matches[1] $minorVersion = [int]$matches[2] # Check if version is 2.23 or higher if ($majorVersion -gt 2 -or ($majorVersion -eq 2 -and $minorVersion -ge 23)) { return $true } else { Write-Warning "Git version $majorVersion.$minorVersion is below required version 2.23" return $false } } Write-Warning "Could not parse Git version from: $version" return $false } function Get-UserConfirmation { param([string]$Prompt) while ($true) { $response = Read-Host "$Prompt (y/n)" $response = $response.Trim().ToLower() if ($response -eq 'y' -or $response -eq 'yes') { return $true } elseif ($response -eq 'n' -or $response -eq 'no') { return $false } else { Write-Host "Please enter 'y' or 'n'" -ForegroundColor Yellow } } } #endregion #region Main Script Write-Host @" ╔═══════════════════════════════════════════════════════════╗ ║ ║ ║ Git Workshop - Prerequisites Installation Script ║ ║ ║ ╚═══════════════════════════════════════════════════════════╝ "@ -ForegroundColor Cyan Write-Host "This script will install the required tools for the Git Workshop:" -ForegroundColor White Write-Host " • PowerShell 7 (cross-platform PowerShell)" -ForegroundColor White Write-Host " • Git 2.23+ (version control system)" -ForegroundColor White Write-Host " • Visual Studio Code (code editor)" -ForegroundColor White Write-Host "" Write-Host "You will be prompted for optional tools:" -ForegroundColor White Write-Host " • Windows Terminal (modern terminal experience)" -ForegroundColor White Write-Host "" # Check for winget Write-Step "Checking Prerequisites" if (-not (Test-WingetAvailable)) { Write-Host "`nInstallation cannot continue without winget." -ForegroundColor Red return } Write-Success "winget is available" # Track installation results $results = @{ PowerShell = $false Git = $false VSCode = $false VSCodeExtensions = $false VSCodePowerShellIntegration = $null # null = not asked, true = configured, false = skipped/failed WindowsTerminal = $null } # Progress tracking $totalSteps = 4 # Required installations + extensions $currentStep = 0 Write-Host "`nStarting installation..." -ForegroundColor Cyan Write-Host "Note: Some installations may take a few minutes." -ForegroundColor Gray Write-Host "" # Progress bar helper function Write-ProgressIndicator { param( [string]$Activity, [string]$Status, [int]$PercentComplete ) Write-Progress -Activity $Activity -Status $Status -PercentComplete $PercentComplete } function Install-VSCodeExtension { param( [string]$ExtensionId, [string]$ExtensionName ) Write-Host " Installing VSCode extension: $ExtensionName" -ForegroundColor Cyan try { # Refresh environment to ensure code command is available $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") $result = & code --install-extension $ExtensionId 2>&1 if ($LASTEXITCODE -eq 0) { Write-Success "VSCode extension '$ExtensionName' installed successfully" return $true } else { Write-Warning "Failed to install VSCode extension '$ExtensionName'" Write-Host " You can install it manually later: Ctrl+Shift+X → Search '$ExtensionName'" -ForegroundColor Gray return $false } } catch { Write-Warning "Could not install VSCode extension '$ExtensionName`: $_" Write-Host " You can install it manually later: Ctrl+Shift+X → Search '$ExtensionName'" -ForegroundColor Gray return $false } } function Set-VSCodePowerShellIntegration { # Ask user if they want to set PowerShell 7 as default terminal $setAsDefault = Get-UserConfirmation "Set PowerShell 7 as the default terminal in VSCode? (Recommended for this workshop)" if (-not $setAsDefault) { Write-Host " Skipping PowerShell 7 terminal configuration." -ForegroundColor Gray return $false } Write-Host " Configuring PowerShell 7 integration with VSCode..." -ForegroundColor Cyan try { # Set PowerShell 7 as the default terminal in VSCode $vscodeSettingsPath = Join-Path $env:APPDATA "Code\User\settings.json" $vscodeSettingsDir = Split-Path $vscodeSettingsPath -Parent # Create directory if it doesn't exist if (-not (Test-Path $vscodeSettingsDir)) { New-Item -Path $vscodeSettingsDir -ItemType Directory -Force | Out-Null } # Read existing settings or create new if (Test-Path $vscodeSettingsPath) { $settings = Get-Content $vscodeSettingsPath -Raw | ConvertFrom-Json } else { $settings = @{} } # Set PowerShell 7 as default terminal if (-not $settings.PSObject.Properties.Name -contains "terminal.integrated.defaultProfile.windows") { $settings | Add-Member -NotePropertyName "terminal.integrated.defaultProfile.windows" -NotePropertyValue "PowerShell" } else { $settings."terminal.integrated.defaultProfile.windows" = "PowerShell" } # Add terminal profiles if not present if (-not $settings.PSObject.Properties.Name -contains "terminal.integrated.profiles.windows") { $profiles = @{ "PowerShell" = @{ "source" = "PowerShell" "icon" = "terminal-powershell" "path" = "pwsh.exe" } } $settings | Add-Member -NotePropertyName "terminal.integrated.profiles.windows" -NotePropertyValue $profiles } # Save settings $settings | ConvertTo-Json -Depth 10 | Set-Content $vscodeSettingsPath Write-Success "VSCode configured to use PowerShell 7 as default terminal" return $true } catch { Write-Warning "Could not configure VSCode PowerShell integration automatically" Write-Host " You can configure it manually in VSCode: Ctrl+Shift+P → Terminal: Select Default Profile → PowerShell" -ForegroundColor Gray return $false } } #region Required Installations # Install PowerShell 7 $currentStep++ Write-ProgressIndicator -Activity "Installing Required Tools" -Status "Installing PowerShell 7 (1/3)" -PercentComplete (($currentStep / $totalSteps) * 100) $results.PowerShell = Install-Package ` -Name "PowerShell 7" ` -WingetId "Microsoft.PowerShell" ` -CheckCommand "pwsh" # Install Git $currentStep++ Write-ProgressIndicator -Activity "Installing Required Tools" -Status "Installing Git (2/3)" -PercentComplete (($currentStep / $totalSteps) * 100) $results.Git = Install-Package ` -Name "Git" ` -WingetId "Git.Git" ` -CheckCommand "git" ` -MinVersion "2.23" ` -AdditionalArgs "-e" # Verify Git version specifically if ($results.Git) { if (-not (Test-GitVersion)) { Write-Warning "Git is installed but version may be below 2.23" $results.Git = $false } } # Install Visual Studio Code $currentStep++ Write-ProgressIndicator -Activity "Installing Required Tools" -Status "Installing Visual Studio Code (3/4)" -PercentComplete (($currentStep / $totalSteps) * 100) $results.VSCode = Install-Package ` -Name "Visual Studio Code" ` -WingetId "Microsoft.VisualStudioCode" ` -CheckCommand "code" # Install VSCode Extensions and configure PowerShell integration if ($results.VSCode) { $currentStep++ Write-ProgressIndicator -Activity "Installing Required Tools" -Status "Installing VSCode Extensions (4/4)" -PercentComplete (($currentStep / $totalSteps) * 100) Write-Host "" Write-Step "Configuring VSCode" # Install PowerShell extension $powershellExtensionResult = Install-VSCodeExtension -ExtensionId "ms-vscode.PowerShell" -ExtensionName "PowerShell" # Configure PowerShell 7 integration (optional but recommended) $powershellIntegrationResult = Set-VSCodePowerShellIntegration $results.VSCodePowerShellIntegration = $powershellIntegrationResult $results.VSCodeExtensions = $powershellExtensionResult } else { $results.VSCodeExtensions = $false } # Clear progress bar Write-Progress -Activity "Installing Required Tools" -Completed #endregion #region Optional Installations # Windows Terminal (optional) Write-Host "" if (Get-UserConfirmation "Do you want to install Windows Terminal? (Highly recommended for better terminal experience)") { Write-ProgressIndicator -Activity "Installing Optional Tools" -Status "Installing Windows Terminal" -PercentComplete 50 $results.WindowsTerminal = Install-Package ` -Name "Windows Terminal" ` -WingetId "Microsoft.WindowsTerminal" ` -CheckCommand "wt" Write-Progress -Activity "Installing Optional Tools" -Completed } else { Write-Host " Skipping Windows Terminal installation." -ForegroundColor Gray $results.WindowsTerminal = $null } #endregion #region Installation Summary Write-Step "Installation Summary" $allRequired = $results.PowerShell -and $results.Git -and $results.VSCode Write-Host "" Write-Host "Required Tools:" -ForegroundColor White if ($results.PowerShell) { Write-Success "PowerShell 7" } else { Write-Error "PowerShell 7 - Installation failed or needs restart" } if ($results.Git) { Write-Success "Git 2.23+" } else { Write-Error "Git 2.23+ - Installation failed or needs restart" } if ($results.VSCode) { Write-Success "Visual Studio Code" if ($results.VSCodeExtensions) { Write-Success " • PowerShell extension" } else { Write-Warning " • VSCode PowerShell extension may need manual installation" } if ($results.VSCodePowerShellIntegration -eq $true) { Write-Success " • PowerShell 7 terminal integration" } elseif ($results.VSCodePowerShellIntegration -eq $false) { Write-Host " • PowerShell 7 terminal integration: Skipped" -ForegroundColor Gray } } else { Write-Error "Visual Studio Code - Installation failed or needs restart" } if ($results.WindowsTerminal -ne $null) { Write-Host "" Write-Host "Optional Tools:" -ForegroundColor White if ($results.WindowsTerminal) { Write-Success "Windows Terminal" } else { Write-Error "Windows Terminal - Installation failed or needs restart" } } #endregion #region Next Steps Write-Step "Next Steps" if ($allRequired) { Write-Host "" Write-Success "All required tools installed successfully!" Write-Host "" Write-Host "IMPORTANT: Configure Git before your first commit:" -ForegroundColor Yellow Write-Host "" Write-Host " git config --global user.name `"Your Name`"" -ForegroundColor White Write-Host " git config --global user.email `"your.email@example.com`"" -ForegroundColor White Write-Host "" Write-Host "Optional: Set VS Code as Git's default editor:" -ForegroundColor Cyan Write-Host " git config --global core.editor `"code --wait`"" -ForegroundColor White Write-Host "" Write-Host "Verify your installation:" -ForegroundColor Cyan Write-Host " pwsh --version" -ForegroundColor White Write-Host " git --version" -ForegroundColor White Write-Host " code --version" -ForegroundColor White Write-Host "" Write-Host "Set PowerShell execution policy (if needed):" -ForegroundColor Cyan Write-Host " Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser" -ForegroundColor White Write-Host "" Write-Host "Recommended VS Code Extensions:" -ForegroundColor Cyan Write-Host " • PowerShell - Better PowerShell support (from Microsoft)" -ForegroundColor White Write-Host "" Write-Host " Install via: Ctrl+Shift+X in VS Code" -ForegroundColor Gray Write-Host "" Write-Host "You're ready to start the workshop!" -ForegroundColor Green Write-Host "" # Ask user if they want to clone the workshop repository Write-Step "Workshop Setup" if (Get-UserConfirmation "Clone the Git Workshop repository to Documents\git-workshop and open in VSCode?") { try { $documentsPath = [System.Environment]::GetFolderPath("MyDocuments") $workshopPath = Join-Path $documentsPath "git-workshop" Write-Host " Cloning to: $workshopPath" -ForegroundColor Gray # Create directory if it doesn't exist $documentsDir = Split-Path $workshopPath -Parent if (-not (Test-Path $documentsDir)) { New-Item -Path $documentsDir -ItemType Directory -Force | Out-Null } # Clone or update the repository if (Test-Path $workshopPath) { Write-Host " Directory already exists. Checking if it's a git repository..." -ForegroundColor Yellow Push-Location $workshopPath if (Get-Command git -ErrorAction SilentlyContinue) { $remoteResult = git remote get-url origin 2>$null if ($LASTEXITCODE -eq 0 -and $remoteResult -like "*git-workshop*") { Write-Host " Repository already exists. Updating..." -ForegroundColor Cyan git pull origin main } else { Write-Warning " Directory exists but is not the git-workshop repository" Write-Host " Please remove the directory manually and run again" -ForegroundColor Yellow Pop-Location return } } Pop-Location } else { git clone "https://git.frod.dk/floppydiscen/git-workshop.git" $workshopPath Write-Success "Repository cloned successfully!" } Write-Host " Opening in VSCode..." -ForegroundColor Cyan if (Get-Command code -ErrorAction SilentlyContinue) { & code $workshopPath Write-Success "VSCode opened with the workshop repository" Write-Host "" Write-Host "=== IMPORTANT: Terminal Instructions ===" -ForegroundColor Yellow Write-Host "Once VSCode opens, you MUST open the integrated terminal:" -ForegroundColor White Write-Host "" Write-Host "Option 1 (Recommended): Press Ctrl+` (backtick key)" -ForegroundColor Cyan Write-Host "Option 2: Use menu: View → Terminal" -ForegroundColor Cyan Write-Host "Option 3: Use Command Palette: Ctrl+Shift+P → 'Terminal: Create New Terminal'" -ForegroundColor Cyan Write-Host "" Write-Host "=== Quick Start (run in VSCode terminal) ===" -ForegroundColor Yellow Write-Host " cd 01-essentials\01-basics" -ForegroundColor White Write-Host " .\setup.ps1" -ForegroundColor White Write-Host "" Write-Host "The terminal should show 'PowerShell' and open to the correct directory." -ForegroundColor Green Write-Host "" } else { Write-Warning "VSCode command not found. Please open manually:" Write-Host " code '$workshopPath'" -ForegroundColor White Write-Host "" Write-Host "=== Manual Instructions ===" -ForegroundColor Yellow Write-Host "1. Open VSCode manually" -ForegroundColor White Write-Host "2. Open integrated terminal (Ctrl+` or View → Terminal)" -ForegroundColor White Write-Host "3. Navigate: cd 01-essentials\01-basics" -ForegroundColor White Write-Host "4. Run: .\setup.ps1" -ForegroundColor White Write-Host "" } Write-Host "" Write-Host "Quick start commands (run in VSCode terminal):" -ForegroundColor Cyan Write-Host " cd 01-essentials\01-basics" -ForegroundColor White Write-Host " .\setup.ps1" -ForegroundColor White Write-Host "" } catch { Write-Error "Failed to clone repository: $_" Write-Host "" Write-Host "You can clone manually:" -ForegroundColor Yellow Write-Host " git clone https://git.frod.dk/floppydiscen/git-workshop.git ~/Documents/git-workshop" -ForegroundColor White Write-Host " code ~/Documents/git-workshop" -ForegroundColor White } } else { Write-Host " Skipping repository clone." -ForegroundColor Gray Write-Host "" Write-Host "Manual setup:" -ForegroundColor Cyan Write-Host " cd path\to\git-workshop" -ForegroundColor White Write-Host " cd 01-essentials\01-basics" -ForegroundColor White Write-Host " .\setup.ps1" -ForegroundColor White Write-Host "" } } else { Write-Host "" Write-Warning "Some required installations failed or need verification." Write-Host "" Write-Host "Troubleshooting steps:" -ForegroundColor Yellow Write-Host " 1. Close and reopen your terminal (or restart your computer)" -ForegroundColor White Write-Host " 2. Run this script again: .\install.ps1" -ForegroundColor White Write-Host " 3. If issues persist, try manual installation:" -ForegroundColor White Write-Host " See INSTALLATION.md for detailed instructions" -ForegroundColor White Write-Host "" if (-not $results.Git) { Write-Host "For Git issues:" -ForegroundColor Yellow Write-Host " • Restart terminal after installation (PATH needs to refresh)" -ForegroundColor White Write-Host " • Manual download: https://git-scm.com/downloads" -ForegroundColor White Write-Host "" } if (-not $results.VSCode) { Write-Host "For VS Code issues:" -ForegroundColor Yellow Write-Host " • Ensure 'Add to PATH' option is enabled during installation" -ForegroundColor White Write-Host " • Manual download: https://code.visualstudio.com/" -ForegroundColor White Write-Host "" } if (-not $results.PowerShell) { Write-Host "For PowerShell 7 issues:" -ForegroundColor Yellow Write-Host " • Manual download: https://github.com/PowerShell/PowerShell/releases/latest" -ForegroundColor White Write-Host " • Download the file ending in '-win-x64.msi'" -ForegroundColor White Write-Host "" } }