Files
git-workshop/install.ps1

708 lines
26 KiB
PowerShell

#!/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]
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 ""
}
}