Skip to content

fix(ci): quote pip ':all:' flag that invalidated the workflow YAML (red badge) - #13

Merged
soroshsabz merged 11 commits into
mainfrom
fix/ci-workflow-yaml
Nov 6, 2026
Merged

soroshsabz merged 11 commits into
mainfrom
fix/ci-workflow-yaml

Conversation

@soroshsabz

@soroshsabz soroshsabz commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

ITNOA

Why the CI badge is red

Since commit 9b44e76 ("Fix SonarCloud security findings on pip install"), the
workflow file has been invalid YAML, so the CI workflow has not actually run:

run: pip install --only-binary :all: asciinema==2.4.0

The colon+space inside :all: asciinema makes YAML parse that scalar as a
nested mapping instead of a string. Reproduced with PyYAML:

yaml.scanner.ScannerError: mapping values are not allowed here
  in ".github/workflows/CI.yml", line 25, column 44

Symptoms this explains

Symptom Cause
Push runs on main fail in 0s with no jobs and no check runs GitHub creates the run record, then rejects the unparseable workflow
PRs show green checks but no "Analyze and test (...)" jobs Invalid workflows create no pull_request runs; the green checks were CodeQL/SonarCloud/SonarGate
workflow_dispatch returns HTTP 422 The degraded workflow registration lost its trigger metadata
gh workflow list shows the workflow as its file path, not CI Same degraded registration

Last real CI run: PR run 42290459537 (head d2e097d, the commit before 9b44e76).
PR #12's CI never ran - its green checks were the other workflows.

The fix

run: pip install --only-binary ':all:' asciinema==2.4.0

Quoting ':all:' removes the colon+space ambiguity. The security intent of
9b44e76 is fully preserved:

  • S8541: pip still refuses sdists (no setup.py execution)
  • S8544: asciinema stays pinned to 2.4.0

oh-my-posh install is guaranteed, not best-effort

Review feedback: if the stress-test tool fails to install, the job must fail,
not silently skip the test - the same false-green trap as the broken workflow.
The step uses winget with an exact version pin (review decision; winget
supports install --version and pin):

winget install --id JanDeDobbeleer.OhMyPosh --version 31.9.1 --exact --silent ...
  • winget verifies the downloaded installer against the winget-pkgs manifest
    InstallerSha256 during install (visible as "Successfully verified installer
    hash" in the logs) - no manual hash step needed
    (winget hash docs)

Three windows-latest iterations were needed (each failed loudly, by design):

  1. The winget package is not a classic installer: it is an MSIX
    (ohmyposh.cli), so the binary is not under Programs\oh-my-posh\bin.
  2. It is also not a winget portable package - WinGet\Links stays empty. The
    entry point is the app execution alias in Microsoft\WindowsApps.
  3. Executing that alias from pwsh fails on hosted runners under redirected
    stdio (StandardOutputEncoding is only supported when standard output is redirected) - the alias is a 0-byte reparse point.

Final approach: resolve the real package via Get-AppxPackage, verify the
package version is exactly 31.9.1.0, execute <InstallLocation>\oh-my-posh.exe
(a real PE) for the version check, prepend InstallLocation to GITHUB_PATH
(so the recorded scenario never resolves through the alias), and set
POSH_THEMES_PATH to the package's themes directory (atomic.omp.json).

Verified on the runner: install + hash verification + [+] records an oh-my-posh prompt burst with zero U+FFFD in the cast (~65s of real recording).

Review fixes landed in this PR

  • Platform checks use the BsnOperatingSystem enum, not strings, and not
    duplicated $PSVersionTable/$env:OS logic
    (review feedback). Gotcha
    discovered en route: bare enum literals do not resolve from Pester test
    scope (Unable to find type [BsnOperatingSystem]), and a using module
    header in a test file executes before BeforeAll can make Bsn.Common
    resolvable - so the tests pull the enum value from the defining module's
    scope: & (Get-Module Bsn.Common) { [BsnOperatingSystem]::Windows }.
    Get-BsnDefaultShell.ps1 now uses the enum literal via a proper
    using module header (same shape as Test-BsnRecordingEnvironment.ps1).
  • IsWindowsPlatform is now pure platform semantics (review feedback):
    Get-BsnOperatingSystem alone decides it. The old PSVersion.Major -ge 6
    term made it mean "Windows and PS6+", misleading for engine dispatch; every
    recording test already asserts the PS 7 requirement with its own explicit skip.
  • Issue Recording from a host with redirected stdio (opencode TUI shell, VS Code, CI) produces an empty cast but reports success #8 unit tests were platform-dependent: the first real Linux CI run
    in days exposed that -Platform Windows checks the OS version before the
    redirected-stdio check - on Linux that is the kernel version (major < 10),
    so the version error fired and the test failed (macOS passed only by luck:
    Darwin 23+ parses as >= 10). Both tests now skip unless on a Windows host.
  • Test-BsnRecordingEnvironment is unchanged - the check order is correct for
    real usage; only the tests were platform-dependent.

Verification

  • yaml.safe_load parses the file; name: CI, all three triggers, and the
    build job resolve correctly
  • Local: Bsn.Host analyze clean, 62/62 tests pass
  • The appearance of the "Analyze and test (...)" checks on this PR is itself
    the end-to-end proof the workflow runs again

Commit 9b44e76 (SonarCloud S8541/S8544 fix) changed the asciinema
install line to:

    run: pip install --only-binary :all: asciinema==2.4.0

Unquoted, the colon+space inside ':all: asciinema' makes YAML parse
the scalar as a nested mapping (reproduced with PyYAML:

  yaml.scanner.ScannerError: mapping values are not allowed here
    line 25, column 44)

which invalidated the entire workflow file. Since that commit:

- push runs are created but die in 0s with no jobs and no check runs
- pull_request runs are not created at all, so PRs since then showed
  only CodeQL/SonarCloud checks while CI silently never ran
- workflow_dispatch returns 422 and the workflow is registered under
  its file path instead of its name (symptoms of the parse failure)

Fix: quote the flag - the security intent (no setup.py execution from
sdists, pinned version) is preserved:

    pip install --only-binary ':all:' asciinema==2.4.0

Also mark the oh-my-posh winget install step continue-on-error (the
stress test skips gracefully without the tool; this is the first CI
run that will execute that step).

Verified locally: yaml.safe_load now parses the file; name, all three
triggers, and the build job resolve correctly.
@coderabbitai

coderabbitai Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d48cc9d1-34ba-494e-b0b1-5bf75bba3dcd
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@codecov

codecov Bot commented Nov 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Review feedback: the stress test must never silently skip on CI - a
failed install has to fail the job, exactly like the silent-CI-gap
this PR fixes. Replace continue-on-error winget with a deterministic,
pinned direct download:

- version pinned to 31.9.1 (manual bumps; no Dependabot ecosystem)
- SHA256 verified against the pinned hash (supply-chain posture
  matches the SHA-pinned actions and pinned asciinema)
- hard verification: the binary must execute and report its version
- POSH_THEMES_PATH exported with atomic.omp.json from the same
  release so the test uses the glyph-rich theme
First real Linux CI run (the workflow had been invalid YAML since
9b44e76) exposed a platform dependency: Test-BsnRecordingEnvironment
-Platform Windows checks [Environment]::OSVersion.Version before the
redirected-stdio check, and on Linux that is the kernel version
(major below 10), so the version error fired instead of the stdio
error and the test failed. macOS passed only by luck (Darwin 23+).

Skip both issue #8 tests unless running on a Windows host (5.1-safe
detection: PSVersionTable.Platform guard with \Windows_NT fallback).
…atform detection

Review feedback: the Windows-host guards inlined \System.Collections.Hashtable/
\Windows_NT detection, duplicating exactly the logic Bsn.Common owns
(AGENTS.md: do not duplicate it in consumer modules). Both places now
call the shared helper:

- Start-InteractiveTranscript.Tests.ps1 (issue #8 tests): guard via
  (Get-BsnOperatingSystem) -ne 'Windows'
- InteractiveTranscript.System.Tests.ps1: IsWindowsPlatform keeps its
  PS7+ requirement but delegates platform detection to the helper

The string -eq comparison pattern already exists in production code
(Get-BsnDefaultShell.ps1) and avoids enum type-literal resolution
concerns in test scope.
@soroshsabz

Copy link
Copy Markdown
Member Author

@soroshsabz

Copy link
Copy Markdown
Member Author

@soroshsabz soroshsabz added the bug Something isn't working label Nov 6, 2026
@soroshsabz soroshsabz self-assigned this Nov 6, 2026
…ings

Review feedback. Two lessons learned empirically:

1. Get-BsnDefaultShell.ps1: -eq 'Windows' relied on string-to-enum
   coercion. Now uses a bare [BsnOperatingSystem]::Windows literal,
   bound at compile time by a 'using module Bsn.Common' header (same
   shape as Test-BsnRecordingEnvironment.ps1; the psm1 loader
   self-heals PSModulePath before dot-sourcing).

2. Test files CANNOT use bare enum literals: 'Unable to find type
   [BsnOperatingSystem]' at It-body runtime (verified by a failing
   local run). The AGENTS.md hazards are not limited to attributes
   and Mock proxies - test scope never binds the type. A 'using
   module' header in a test file would execute before BeforeAll can
   make Bsn.Common resolvable, so it is fragile too. Instead,
   BeforeAll pulls the enum value from the defining module's own
   scope (where the literal always resolves):

   \ = & (Get-Module -Name 'Bsn.Common') { [BsnOperatingSystem]::Windows }

   and the guards compare enum to enum.
The previous commit message claimed winget is 'not version-pinnable'
- that is wrong: winget install supports --version for exact
   versions, and winget pin locks future upgrades
   (winget.tech/blog/winget-pin-lock-versions,
   microsoft/winget-cli#4002). The actual reasons for the direct
   download are: the repo can pin the artifact's SHA256 (same
   supply-chain posture as the SHA-pinned actions), and it avoids
   winget/msstore source flakiness on hosted runners.
Review decision: winget is the cleaner, idiomatic approach, and it
supports exact version pinning:

  winget install --id JanDeDobbeleer.OhMyPosh --version 31.9.1 --exact

winget verifies the downloaded installer against the winget-pkgs
manifest InstallerSha256 during install, so no manual hash step is
needed (winget hash exists for manual verification when required:
https://learn.microsoft.com/en-us/windows/package-manager/winget/hash).

The step still fails loudly (no continue-on-error): after install it
verifies the binary exists, executes, and reports exactly 31.9.1, and
that the themes directory (POSH_THEMES_PATH for the stress test)
contains atomic.omp.json.
Review feedback: Get-BsnOperatingSystem alone is enough - the PSVersion
Major -ge 6 term made a variable named IsWindowsPlatform mean 'Windows
AND PowerShell 6+', which is misleading:

- every recording test already asserts the PS 7 requirement explicitly
  with its own 'system tests require PowerShell 7 console
  infrastructure' skip (9 occurrences)
- Invoke-BsnSystemRecording uses the flag for engine dispatch (Windows
  hidden-console runner vs Unix script(1)), where platform - not
  PowerShell edition - is the correct discriminator
First real run on windows-latest: winget install --version 31.9.1
succeeded, but the exe was not at the classic installer layout
(Programs\oh-my-posh\bin) - the winget community package is a PORTABLE
install: the exe lands under WinGet\Packages with a symlink in
WinGet\Links (added to the user PATH registry, not to the step's
process). The step now resolves the binary from known candidates
(Links first, classic layouts as fallback) before verifying it, adds
its directory to GITHUB_PATH, and treats themes opportunistically
because portable packages do not ship them (the stress test falls
back to the default theme, which still renders nerd-font glyphs).
Second windows-latest failure with empty WinGet\Links: the winget
manifest for JanDeDobbeleer.OhMyPosh is InstallerType: appx - an MSIX
(ohmyposh.cli), not a portable package. The executable is exposed via
the app execution alias:

  %LOCALAPPDATA%\Microsoft\WindowsApps\oh-my-posh.exe

(verified locally by installing the same pinned version). Candidate
order now: MSIX alias, WinGet Links (if the manifest ever moves to
portable), classic installer layout.

Themes: the MSIX ships them inside the package directory, resolved
via Get-AppxPackage (InstallLocation\\themes\\atomic.omp.json);
classic Programs\\oh-my-posh\\themes remains a fallback and the test
ultimately falls back to the default theme.
Third windows-latest failure: the alias WAS found, but executing the
WindowsApps app-execution alias (a reparse point) from pwsh fails on
hosted runners under redirected stdio:

  Program 'oh-my-posh.exe' failed to run: StandardOutputEncoding is
  only supported when standard output is redirected.

Resolve everything from Get-AppxPackage instead:
- verify the installed package version equals 31.9.1.0
- execute <InstallLocation>\oh-my-posh.exe (the real PE - verified
  working locally) for the version check
- GITHUB_PATH gets InstallLocation (prepended), so the recorded
  scenario resolves 'oh-my-posh' to the real binary, never the alias
- POSH_THEMES_PATH points at the package themes directory
@sonarqubecloud

sonarqubecloud Bot commented Nov 6, 2026

Copy link
Copy Markdown

@soroshsabz
soroshsabz merged commit 04ac08a into main Nov 6, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant