Skip to content

Instantly share code, notes, and snippets.

@Kenya-West
Last active March 15, 2024 07:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Kenya-West/2997492a6f23c82c47263a493c808d47 to your computer and use it in GitHub Desktop.
Save Kenya-West/2997492a6f23c82c47263a493c808d47 to your computer and use it in GitHub Desktop.
This is a guide that helps you install oh-my-posh and its necessary boilerplate without deep dive to their documentation

(Almost) automatically install oh-my-posh

This is a guide that helps you install oh-my-posh and its necessary boilerplate without deep dive to their documentation

0. Pre-requisites

In this guide, it is assumed that you have installed:

  1. VS Code (User setup, not system one);
  2. PowerShell 6+, whatever way (Microsoft Store, WinGet, Portable);
  3. Windows Terminal from Microsoft Store.

If not, you need to modify all following scripts below to make it work with different environment.

1. Install it!

Install oh-my-posh, posh-git and then close tab.

winget install XP8K0HKJFRXGCK --source msstore --accept-source-agreements --accept-package-agreements
Install-Module posh-git -Scope CurrentUser
exit

Then close Windows Terminal window or reopen a tab, because we need environment variables to refresh.

2. Add exclusions to make oh-my-posh faster

Open Windows Terminal/pswh from admin:

Win+R, type wt, Ctrl + Shift + Enter, click "Yes"

then execute these commands:

Add-MpPreference -ExclusionProcess "oh-my-posh.exe"

You now can close tab from admin, it will not be needed again.

3. Add profile

PowerShell can do some things before launch. We need posh-git and oh-my-posh to launch on every PowerShell instance.

Open wt/pwsh and then execute this code:

if (Test-Path -Path $Profile -PathType Leaf) {
    Write-Host "Profile already exists"
} else {
    New-Item -Path $PROFILE -Type File
    Write-Host "Profile Created"
}

function AddLineToFile {
    param (
        [Parameter(Mandatory=$True)]
        [string]$PathToFile,
        [Parameter(Mandatory=$True)]
        [string]$Pattern,
        [Parameter(Mandatory=$True)]
        [string]$LineToAdd
    )
    
    # get the file contents
    $fileContents = Get-Content -Path $PathToFile
    
    $found = $False
    Foreach ($item in $fileContents)
    {
        If ($item -like $Pattern) 
        {
            Write-Host "`"$Pattern`" string already exists"
            Write-Host "Nothing to do"
            $found = $True
            break
        }
    }

    if ($found -ne $True) {
        Add-Content -Path $PROFILE -Value $LineToAdd
        Write-Host "`"$LineToAdd`" line was added"
    }
}

AddLineToFile $PROFILE "Import-Module posh-git*" "Import-Module posh-git";
AddLineToFile $PROFILE "oh-my-posh init pwsh*" "oh-my-posh init pwsh | Invoke-Expression";

You can add some tweaks in your $PROFILE later, above is just necessary stuff to get oh-my-posh work.

4. Add fonts and configure Windows Terminal and VS Code settings

This code does what the paragraph says:

  1. It downloads the latest CaskaydiaCove font zip-file;
  2. Unzips it;
  3. Installs all the kinds of the font;
  4. Removes downloaded font files;
  5. Patches Windows Terminal settings with backup;
  6. Patches VS Code settings with backup.

You can execute this code with the same pwsh instance from previous paragraph:

# Initial setup
$developerName = "ryanoasis";
$repoName = "nerd-fonts";
$wtFileToDownload = "CascadiaCode.zip";
$fontNameToSet = "CaskaydiaCove NFM";

# Get latest NerdFont release version
$url = "https://github.com/$developerName/$repoName/releases/latest";
$request = [System.Net.WebRequest]::Create($url);
$response = $request.GetResponse();
$realTagUrl = $response.ResponseUri.OriginalString;
$version = $realTagUrl.split("/")[-1].Trim("v");
$wtFileName = $wtFileToDownload; # $wtFileName should be because maybe there is $version in its name
$realDownloadUrl = $realTagUrl.Replace("tag", "download") + "/" + $wtFileName;
Write-Host $realDownloadUrl;

# Download Nerdfont file
$fontsFileLocation = "$env:TEMP/$wtFileName";
$fontsDirectoryLocation = "$env:TEMP/"+(Split-Path $fontsFileLocation -LeafBase);
Invoke-WebRequest -Uri $realDownloadUrl -OutFile $fontsFileLocation;

# Extract Nerdfont file
Write-Host "Expanding $fontsFileLocation";
Expand-Archive $fontsFileLocation -DestinationPath $fontsDirectoryLocation;

Get-ChildItem -Path $fontsDirectoryLocation -Include "*.ttf","*.ttc","*.otf" -Recurse | ForEach-Object {(New-Object -ComObject Shell.Application).Namespace(0x14).CopyHere($_.FullName, 0x10)};

Write-Host "Removing file at path $fontsFileLocation";
Remove-Item $fontsFileLocation -ErrorAction Continue;
Write-Host "Succesfully removed file at path $fontsFileLocation";
Remove-Item $fontsDirectoryLocation -ErrorAction Continue -Recurse;
Write-Host "Succesfully removed directory at path $fontsDirectoryLocation";
Write-Host "";

# Set font in Windows Terminal
$wtSettingsFileLocation = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json";
if (Test-Path -Path $wtSettingsFileLocation) {
    Write-Host "File at path $wtSettingsFileLocation exists. Performing changes";
    # get backup copy file with .bak extension
    $configPath = $wtSettingsFileLocation;
    $configPathBak = $configPath + ".bak";
    Copy-Item $configPath $configPathBak -ErrorAction Continue;

    # change font-face
    $wtFile = Get-Content $wtSettingsFileLocation -raw | ConvertFrom-Json -Depth 100;
    $success = $False;
    try {
        if (-not $wtFile.profiles.defaults.font) {
            Write-Host "No font property found in $wtSettingsFileLocation. Creating it";
            $jsonContent = [PSCustomObject]@{}
            $wtFile.profiles.defaults | Add-Member -NotePropertyName "font" -NotePropertyValue ([PSCustomObject]@{"face" = $fontNameToSet})
        } else {
            if (-not $wtFile.profiles.defaults.font.face) {
                Write-Host "No font.face property found in $wtSettingsFileLocation. Creating it";
                Add-Member -InputObject $wtFile.profiles.defaults.font -MemberType NoteProperty -Name "face" -Value $fontNameToSet
            } else {
                $wtFile.profiles.defaults.font.face = $fontNameToSet;
            }
        }
        $success = $True;
        Write-Host "File $wtSettingsFileLocation successfully modified";
    }
    catch {
        $wtFileName = Split-Path $wtSettingsFileLocation -Leaf;
        Write-Host "Error modifying $wtFileName file contents: $_";
    }
    # save successfully modified file
    if ($success -eq $True) {
        Set-Content -Value ($wtFile | ConvertTo-Json -Depth 100) -Path $wtSettingsFileLocation;
    }
} else {
    Write-Host "File at path $wtSettingsFileLocation does not exist. No changes will be made";
}

# Set font in VS Code
$vscodeSettingsPath = "$env:APPDATA\Code\User\settings.json";

# get backup copy file with .bak extension
if (Test-Path -Path $vscodeSettingsPath) {
    Write-Host "File at path $vscodeSettingsPath exists. Performing changes";
    $configPath = $vscodeSettingsPath;
    $configPathBak = $configPath + ".bak";
    Copy-Item $configPath $configPathBak -ErrorAction Continue;
    
    $fontSetting = "terminal.integrated.fontFamily";
    $vscodeFile = Get-Content -Raw -Path $vscodeSettingsPath -ErrorAction silentlycontinue | ConvertFrom-Json -Depth 100

    $success = $False;
    try {
        if (-not $vscodeFile.$fontSetting) {
            Add-Member -InputObject $vscodeFile -MemberType NoteProperty -Name $fontSetting -Value $fontNameToSet
        } else {
            $vscodeFile.$fontSetting = $fontNameToSet;
        }        
        $success = $True;
        Write-Host "File $vscodeSettingsPath successfully modified";
    }
    catch {
        $vscodeFileName = Split-Path $vscodeSettingsPath -Leaf;
        Write-Host "Error modifying $vscodeFileName file contents: $_";
    }
    # save successfully modified file
    if ($success -eq $True) {
        Set-Content -Value ($vscodeFile | ConvertTo-Json -Depth 100) -Path $vscodeSettingsPath;
    }
} 

We are using Caskaydia Cove font, but you can change $wtFileToDownload and $fontNameToSet to set a different one. List is here.

Script also creates backup files of configs for a case if somethings is getting corrupted. Follow these paths to restore or modify configs:

  • Windows Terminal config path: %LOCALAPPDATA%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState
  • VS Code config path: %APPDATA%\Code\User

5. Use theme

You can use either this command, getting the latest of theme, not tied up with oh-my-posh setup:

oh-my-posh init pwsh --config "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/M365Princess.omp.json" | Invoke-Expression

Or use the same theme but out-of-the-box:

oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH/M365Princess.omp.json" | Invoke-Expression

I prefer the second one, because who knows what breaking changes would happen by theme?

You can set a different theme from here.

That's all, folks! You just need 3 wt instances and 5 scripts to install oh-my-posh. Hope this is the simplest guide you ever met!

@bimber-ua
Copy link

Thanks for your job! Can you please integrate the auto-skipping for the fonts installation? I have a Win11, and for the command:

Get-ChildItem -Path $fontsDirectoryLocation -Include "*.ttf","*.ttc","*.otf" -Recurse | ForEach-Object {(New-Object -ComObject Shell.Application).Namespace(0x14).CopyHere($_.FullName, 0x10)};
I should manually skip or confirm the installation for all font they already installed in my system.
Best regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment