Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 53 additions & 8 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ jobs:

- name: Install asciinema (Linux cross-check tool)
if: runner.os == 'Linux'
run: pip install --only-binary :all: asciinema==2.4.0
# ':all:' must stay quoted: unquoted, the colon+space after :all: turns
# this scalar into a nested YAML mapping and invalidates the whole
# workflow file (runs then fail in 0s with no jobs - see PR history).
run: pip install --only-binary ':all:' asciinema==2.4.0

- name: Build console-logger (Windows cross-check tool)
if: runner.os == 'Windows'
Expand Down Expand Up @@ -50,13 +53,55 @@ jobs:
if: runner.os == 'Windows'
shell: pwsh
run: |
winget install JanDeDobbeleer.OhMyPosh --accept-source-agreements --accept-package-agreements --silent
$poshPath = "$env:LOCALAPPDATA\Programs\oh-my-posh\bin"
if (Test-Path $poshPath) {
Add-Content -LiteralPath $env:GITHUB_PATH -Value $poshPath
Write-Host "oh-my-posh installed"
} else {
Write-Warning "oh-my-posh installation failed; stress test will skip"
# winget is the idiomatic installer (review decision). The version is
# pinned with --version/--exact, and winget verifies the downloaded
# installer against the winget-pkgs manifest InstallerSha256 during
# install - no manual hash step needed
# (https://learn.microsoft.com/en-us/windows/package-manager/winget/hash).
# The issue #5 stress test depends on this binary: if the install
# fails the job fails - never a silent skip.
winget install --id JanDeDobbeleer.OhMyPosh --version 31.9.1 --exact --silent --disable-interactivity --accept-source-agreements --accept-package-agreements

# The winget package is an MSIX (ohmyposh.cli). Its public entry
# point is the app execution alias in WindowsApps - a reparse point
# that fails to execute from pwsh under redirected stdio on hosted
# runners ("StandardOutputEncoding is only supported when standard
# output is redirected"). Resolve the REAL package binary via
# Get-AppxPackage and use it for verification and PATH.
$appx = Get-AppxPackage -Name 'ohmyposh.cli' -ErrorAction Stop
if (-not $appx) {
throw 'ohmyposh.cli package not found after winget install.'
}
if ($appx.Version -ne '31.9.1.0') {
throw "oh-my-posh package version $($appx.Version) installed; expected 31.9.1.0."
}

$packageExe = Join-Path $appx.InstallLocation 'oh-my-posh.exe'
if (-not (Test-Path -LiteralPath $packageExe)) {
throw "oh-my-posh.exe not found inside the package at $packageExe."
}

# Hard verification: the binary must execute and report the pinned version.
$reported = ("{0}" -f (& $packageExe version)).Trim()
if ($LASTEXITCODE -ne 0 -or $reported -ne '31.9.1') {
throw "oh-my-posh reported '$reported' (exit $LASTEXITCODE); expected 31.9.1."
}
Write-Host "oh-my-posh $reported installed via winget ($packageExe)"

# GITHUB_PATH is prepended, so 'oh-my-posh' resolves to the package
# binary, not the WindowsApps alias reparse point.
Add-Content -LiteralPath $env:GITHUB_PATH -Value $appx.InstallLocation

# The MSIX ships the themes inside the package directory; point the
# stress test at the glyph-rich atomic theme when present (it falls
# back to the default theme, which still renders nerd-font glyphs).
$themesDir = Join-Path $appx.InstallLocation 'themes'
if (Test-Path -LiteralPath (Join-Path $themesDir 'atomic.omp.json')) {
Add-Content -LiteralPath $env:GITHUB_ENV -Value "POSH_THEMES_PATH=$themesDir"
Write-Host 'POSH_THEMES_PATH set; the stress test will use atomic.omp.json'
}
else {
Write-Host 'No themes directory found; the stress test will render the default theme.'
}

- name: Bootstrap development dependencies
Expand Down
4 changes: 3 additions & 1 deletion src/Bsn.Host/Private/Get-BsnDefaultShell.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using module Bsn.Common

<#
.SYNOPSIS
Resolves the default interactive shell for recording.
Expand All @@ -12,7 +14,7 @@ function Get-BsnDefaultShell {
[OutputType([string])]
param()

if ((Get-BsnOperatingSystem) -eq 'Windows') {
if ((Get-BsnOperatingSystem) -eq [BsnOperatingSystem]::Windows) {
foreach ($shellName in @('pwsh', 'powershell')) {
$shellCommand = Get-Command -Name $shellName -ErrorAction 'SilentlyContinue'
if ($shellCommand) {
Expand Down
11 changes: 10 additions & 1 deletion src/Bsn.Host/tests/system/InteractiveTranscript.System.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,16 @@ BeforeAll {

$script:RepoRoot = Split-Path -Parent -Path (Split-Path -Parent -Path $moduleRoot)
$script:HostModule = Get-Module -Name 'Bsn.Host'
$script:IsWindowsPlatform = ($PSVersionTable.PSVersion.Major -ge 6) -and ($PSVersionTable.ContainsKey('Platform') -and ($PSVersionTable['Platform'] -eq 'Win32NT') -or $env:OS -eq 'Windows_NT')
# Bsn.Common (loaded above via Bsn.Host's RequiredModules) owns platform
# detection - reuse it instead of duplicating $PSVersionTable/$env:OS logic.
# The enum value comes from the defining module's scope: a bare type
# literal does not resolve from test scope, and a 'using module' header
# here would execute before the PSModulePath fix above (see AGENTS.md).
# IsWindowsPlatform is pure platform semantics (engine dispatch); the PS 7
# console requirement is asserted per recording test with an explicit
# "system tests require PowerShell 7 console infrastructure" skip.
$script:BsnWindows = & (Get-Module -Name 'Bsn.Common') { [BsnOperatingSystem]::Windows }
$script:IsWindowsPlatform = (Get-BsnOperatingSystem) -eq $script:BsnWindows
$script:CanRunSystemTests = [bool](Get-Command -Name 'pwsh' -ErrorAction 'SilentlyContinue')
$script:HasAsciinema = [bool](Get-Command -Name 'asciinema' -ErrorAction 'SilentlyContinue')
$script:HasTmux = [bool](Get-Command -Name 'tmux' -ErrorAction 'SilentlyContinue')
Expand Down
19 changes: 19 additions & 0 deletions src/Bsn.Host/tests/unit/Start-InteractiveTranscript.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ BeforeAll {
catch [System.IO.FileLoadException] {
Import-Module -Name (Join-Path $moduleRoot 'Bsn.Host.psm1') -Force -ErrorAction 'Stop'
}

# Native keyword enums cannot be referenced by a bare type literal from
# test scope: the literal resolves only in files whose 'using module'
# header bound it at compile time (module files; see AGENTS.md), and a
# using header in this test file would execute before BeforeAll can make
# Bsn.Common resolvable. Pull the enum value from the defining module's
# own scope instead, where the literal always resolves.
$script:BsnWindows = & (Get-Module -Name 'Bsn.Common') { [BsnOperatingSystem]::Windows }
}

Describe 'Start-InteractiveTranscript' {
Expand Down Expand Up @@ -219,6 +227,14 @@ Describe 'Start-InteractiveTranscript' {

It 'throws when both stdin and stdout are redirected on Windows (issue #8)' {
# https://github.com/BSVN/Lmt.Commons/issues/8
# Requires a real Windows host: with -Platform Windows the version
# guard runs before the redirected-stdio check, and on Unix hosts
# [Environment]::OSVersion.Version is the kernel version (major
# below 10), so the version error fires instead of the stdio one.
# Compare against the enum value pulled from Bsn.Common (BeforeAll).
if ((Get-BsnOperatingSystem) -ne $script:BsnWindows) {
Set-ItResult -Skipped -Because 'requires a Windows host; on Unix the Windows-version guard throws first.'
}
# In CI/test context, both are typically redirected
if ([Console]::IsInputRedirected -and [Console]::IsOutputRedirected) {
{ & (Get-Module -Name 'Bsn.Host') { Test-BsnRecordingEnvironment -Platform Windows } } | Should -Throw '*cannot capture output*'
Expand All @@ -230,6 +246,9 @@ Describe 'Start-InteractiveTranscript' {

It 'warns (not throws) when only one handle is redirected (issue #8)' {
# https://github.com/BSVN/Lmt.Commons/issues/8
if ((Get-BsnOperatingSystem) -ne $script:BsnWindows) {
Set-ItResult -Skipped -Because 'requires a Windows host; on Unix the Windows-version guard throws first.'
}
if ([Console]::IsInputRedirected -xor [Console]::IsOutputRedirected) {
{ & (Get-Module -Name 'Bsn.Host') { Test-BsnRecordingEnvironment -Platform Windows } } | Should -Not -Throw
}
Expand Down
Loading