From ff75d620d2874e30cfff338dbec0729b09deddfb Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 27 Aug 2026 22:47:31 +0200 Subject: [PATCH 1/4] Migrate Process-PSModule caller workflow to v8 Move from the SHA pin on v6.1.19 to the mutable @v8 major tag and adopt the canonical v8 caller contract: - Add the push trigger on main, which v8 uses to publish stable releases. - Add the unlabeled pull_request type. - Key concurrency on the PR number or ref with cancel-in-progress: false. - Narrow permissions to contents: read, pages: write, id-token: write. - Replace the removed APIKey secret with PSGALLERY_API_KEY and the GitHubAppClientId/GitHubAppPrivateKey pair. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/Process-PSModule.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/Process-PSModule.yml b/.github/workflows/Process-PSModule.yml index 08fc9a3..1181c77 100644 --- a/.github/workflows/Process-PSModule.yml +++ b/.github/workflows/Process-PSModule.yml @@ -4,6 +4,9 @@ on: workflow_dispatch: schedule: - cron: '0 0 * * *' + push: + branches: + - main pull_request: branches: - main @@ -13,20 +16,21 @@ on: - reopened - synchronize - labeled + - unlabeled concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: false permissions: - contents: write - pull-requests: write - statuses: write + contents: read pages: write id-token: write jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@bf67cd90269ca5ce25cd76b203678907dc2984b4 # v6.1.19 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: - APIKey: ${{ secrets.APIKey }} + PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} + GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} + GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} From 759bba78df8af29f3a5372f307127ff0c11efc97 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 27 Aug 2026 23:03:45 +0200 Subject: [PATCH 2/4] Migrate tests to Pester 6 assertion syntax Rewrite tests/NerdFonts.Tests.ps1 in the Pester 6 Should-* syntax and align the requirement with the version Process-PSModule v8 installs. - Require Pester 6.1.0 instead of 6.0.0. The v8 Test-ModuleLocal workflow pins Invoke-Pester to '[6.1.0,7.0.0)', so 6.0.0 understated the floor. - Replace classic assertions with the v6 family: Should-NotBeNull, Should-Be, Should-All, Should-BeCollection, Should-Throw, Should-Invoke and Should-NotInvoke. - Drop 'Should -Not -Throw'. Pester 6 has no Should-NotThrow; the documented idiom is to call the code directly and let an unexpected exception fail the test with a real stack trace. - Use Should-NotInvoke instead of 'Should -Invoke -Times 0 -Exactly'. - Collapse the three duplicated -Variant tests into one -ForEach case. - Extract Use-TestFontData, Get-TestFont and Get-TestCacheRoot helpers so the module catalog is always restored via a single try/finally. Verified against Pester 6.1.0: 12/12 pass, PSScriptAnalyzer clean, and mutation checks confirm the suite still fails when the install-skip and variant-filter logic are broken. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tests/NerdFonts.Tests.ps1 | 366 +++++++++++++++++--------------------- 1 file changed, 163 insertions(+), 203 deletions(-) diff --git a/tests/NerdFonts.Tests.ps1 b/tests/NerdFonts.Tests.ps1 index 515e3c7..9550db6 100644 --- a/tests/NerdFonts.Tests.ps1 +++ b/tests/NerdFonts.Tests.ps1 @@ -1,4 +1,4 @@ -#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '6.0.0'; MaximumVersion = '6.*' } +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '6.1.0'; MaximumVersion = '6.*' } [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseDeclaredVarsMoreThanAssignments', '', @@ -19,129 +19,158 @@ [CmdletBinding()] param() +BeforeAll { + function Use-TestFontData { + <# + .SYNOPSIS + Runs a test body against a replaced module-internal font catalog. + + .DESCRIPTION + Swaps $script:NerdFonts inside the NerdFonts module for the supplied font objects, runs the + body, and restores the original catalog afterwards even when the body fails. + + .EXAMPLE + Use-TestFontData -Fonts $testFonts -Body { Install-NerdFont -Name 'Test' } + + Installs against the test catalog and restores the real catalog when finished. + #> + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]] $Fonts, + + [Parameter(Mandatory)] + [scriptblock] $Body + ) + + $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } + InModuleScope NerdFonts -Parameters @{ fonts = $Fonts } { + param($fonts) + $script:NerdFonts = $fonts + } + try { + & $Body + } finally { + InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { + param($fonts) + $script:NerdFonts = $fonts + } + } + } + + function Get-TestFont { + <# + .SYNOPSIS + Gets a single font entry from the repository's FontsData.json. + + .DESCRIPTION + Reads the source FontsData.json and returns the first entry whose name matches exactly, so + tests can build catalogs from real font metadata instead of hard-coded URLs. + + .EXAMPLE + Get-TestFont -Name 'Tinos' + + Returns the Tinos font entry. + #> + param( + [Parameter(Mandatory)] + [string] $Name + ) + + $fontsDataPath = Join-Path -Path $PSScriptRoot -ChildPath '../src/FontsData.json' + Get-Content -Path $fontsDataPath | ConvertFrom-Json | Where-Object Name -EQ $Name | Select-Object -First 1 + } + + function Get-TestCacheRoot { + <# + .SYNOPSIS + Gets the platform-specific NerdFonts download cache root. + + .DESCRIPTION + Mirrors the cache root that Install-NerdFont computes, so tests can seed and clean up cache + entries on the same path the function uses. + + .EXAMPLE + Get-TestCacheRoot + + Returns the cache root path for the current platform. + #> + if ($IsWindows) { + Join-Path -Path ([Environment]::GetFolderPath('LocalApplicationData')) -ChildPath 'PSModule/NerdFonts/cache' + } else { + Join-Path -Path $HOME -ChildPath '.cache/PSModule/NerdFonts' + } + } +} + Describe 'Module' { Context 'Function: Get-NerdFont' { It 'Returns all fonts' { $fonts = Get-NerdFont Write-Verbose ($fonts | Out-String) -Verbose - $fonts | Should -Not -BeNullOrEmpty + $fonts | Should-NotBeNull } It 'Returns a specific font' { $font = Get-NerdFont -Name 'Tinos' Write-Verbose ($font | Out-String) -Verbose - $font | Should -Not -BeNullOrEmpty + $font | Should-NotBeNull + $font.Name | Should-Be 'Tinos' } } Context 'Function: Install-NerdFont' { It 'Install-NerdFont - Installs a font' { - { Install-NerdFont -Name 'Tinos' } | Should -Not -Throw - Get-Font -Name 'Tinos*' | Should -Not -BeNullOrEmpty + Install-NerdFont -Name 'Tinos' + Get-Font -Name 'Tinos*' | Should-NotBeNull } It 'Install-NerdFont - Continues when one queued download fails' { - $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } - $loadedFonts = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath '../src/FontsData.json') | ConvertFrom-Json - $goodFont = $loadedFonts | Where-Object Name -EQ 'Tinos' | Select-Object -First 1 - $testFonts = @( [pscustomobject]@{ Name = 'BrokenDownloadTest' URL = 'https://github.com/ryanoasis/nerd-fonts/releases/download/v3.4.0/does-not-exist.zip' - }, - $goodFont + } + Get-TestFont -Name 'Tinos' ) - InModuleScope NerdFonts -Parameters @{ fonts = $testFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - try { + Use-TestFontData -Fonts $testFonts -Body { Mock -ModuleName NerdFonts Install-Font {} - { Install-NerdFont -Name @('BrokenDownloadTest', 'Tinos') -Force -ErrorAction SilentlyContinue } | Should -Not -Throw - Should -Invoke -ModuleName NerdFonts Install-Font -Times 1 -Exactly - } finally { - InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { - param($fonts) - $script:NerdFonts = $fonts - } + + Install-NerdFont -Name @('BrokenDownloadTest', 'Tinos') -Force -ErrorAction SilentlyContinue + + Should-Invoke -CommandName Install-Font -ModuleName NerdFonts -Times 1 -Exactly } } It 'Install-NerdFont - Skips already installed fonts without downloading' { - $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } $testFonts = @( [pscustomobject]@{ Name = 'AlreadyInstalledTest' URL = 'https://example.invalid/already-installed.zip' } ) - InModuleScope NerdFonts -Parameters @{ fonts = $testFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - try { + Use-TestFontData -Fonts $testFonts -Body { Mock -ModuleName NerdFonts Get-Font { [pscustomobject]@{ Name = 'AlreadyInstalledTest Nerd Font' } } Mock -ModuleName NerdFonts Install-Font {} - { Install-NerdFont -Name 'AlreadyInstalledTest' -ErrorAction Stop } | Should -Not -Throw - Should -Invoke -ModuleName NerdFonts Install-Font -Times 0 -Exactly - } finally { - InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - } - } - - It 'Install-NerdFont - Installs a font with -Variant Mono' { - $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } - $loadedFonts = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath '../src/FontsData.json') | ConvertFrom-Json - $goodFont = $loadedFonts | Where-Object Name -EQ 'Hack' | Select-Object -First 1 - $testFonts = @($goodFont) - InModuleScope NerdFonts -Parameters @{ fonts = $testFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - - try { - Mock -ModuleName NerdFonts Get-Font { @() } - $script:TestCapturedFiles = $null - Mock -ModuleName NerdFonts Install-Font {} -ParameterFilter { - $script:TestCapturedFiles = @( - Get-ChildItem -Path $Path -Recurse -File -Include '*.ttf', '*.otf' | - Select-Object -ExpandProperty Name - ) - $true - } + Install-NerdFont -Name 'AlreadyInstalledTest' -ErrorAction Stop - { Install-NerdFont -Name 'Hack' -Variant Mono -ErrorAction Stop } | Should -Not -Throw - Should -Invoke -ModuleName NerdFonts Install-Font -Times 1 -Exactly - $script:TestCapturedFiles | Should -Not -BeNullOrEmpty - $script:TestCapturedFiles | ForEach-Object { $_ | Should -BeLike '*NerdFontMono*' } - } finally { - InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { - param($fonts) - $script:NerdFonts = $fonts - } + Should-NotInvoke -CommandName Install-Font -ModuleName NerdFonts } } - It 'Install-NerdFont - Installs a font with -Variant Standard' { - $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } - $loadedFonts = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath '../src/FontsData.json') | ConvertFrom-Json - $goodFont = $loadedFonts | Where-Object Name -EQ 'Hack' | Select-Object -First 1 - $testFonts = @($goodFont) - InModuleScope NerdFonts -Parameters @{ fonts = $testFonts } { - param($fonts) - $script:NerdFonts = $fonts - } + It 'Install-NerdFont - Installs a font with -Variant ' -ForEach @( + @{ Variant = 'Mono'; Expected = '*NerdFontMono*'; NotExpected = @() } + @{ Variant = 'Propo'; Expected = '*NerdFontPropo*'; NotExpected = @() } + @{ Variant = 'Standard'; Expected = '*NerdFont*'; NotExpected = @('*NerdFontMono*', '*NerdFontPropo*') } + ) { + $testFonts = @(Get-TestFont -Name 'Hack') - try { + Use-TestFontData -Fonts $testFonts -Body { Mock -ModuleName NerdFonts Get-Font { @() } $script:TestCapturedFiles = $null Mock -ModuleName NerdFonts Install-Font {} -ParameterFilter { @@ -152,103 +181,51 @@ Describe 'Module' { $true } - { Install-NerdFont -Name 'Hack' -Variant Standard -ErrorAction Stop } | Should -Not -Throw - Should -Invoke -ModuleName NerdFonts Install-Font -Times 1 -Exactly - $script:TestCapturedFiles | Should -Not -BeNullOrEmpty - $script:TestCapturedFiles | ForEach-Object { - $_ | Should -BeLike '*NerdFont*' - $_ | Should -Not -BeLike '*NerdFontMono*' - $_ | Should -Not -BeLike '*NerdFontPropo*' - } - } finally { - InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - } - } - - It 'Install-NerdFont - Installs a font with -Variant Propo' { - $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } - $loadedFonts = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath '../src/FontsData.json') | ConvertFrom-Json - $goodFont = $loadedFonts | Where-Object Name -EQ 'Hack' | Select-Object -First 1 - $testFonts = @($goodFont) - InModuleScope NerdFonts -Parameters @{ fonts = $testFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - - try { - Mock -ModuleName NerdFonts Get-Font { @() } - $script:TestCapturedFiles = $null - Mock -ModuleName NerdFonts Install-Font {} -ParameterFilter { - $script:TestCapturedFiles = @( - Get-ChildItem -Path $Path -Recurse -File -Include '*.ttf', '*.otf' | - Select-Object -ExpandProperty Name - ) - $true - } + Install-NerdFont -Name 'Hack' -Variant $Variant -ErrorAction Stop - { Install-NerdFont -Name 'Hack' -Variant Propo -ErrorAction Stop } | Should -Not -Throw - Should -Invoke -ModuleName NerdFonts Install-Font -Times 1 -Exactly - $script:TestCapturedFiles | Should -Not -BeNullOrEmpty - $script:TestCapturedFiles | ForEach-Object { $_ | Should -BeLike '*NerdFontPropo*' } - } finally { - InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { - param($fonts) - $script:NerdFonts = $fonts + Should-Invoke -CommandName Install-Font -ModuleName NerdFonts -Times 1 -Exactly + $script:TestCapturedFiles | Should-NotBeNull + $script:TestCapturedFiles | Should-All { $_ -like $Expected } + foreach ($pattern in $NotExpected) { + $script:TestCapturedFiles | Should-All { $_ -notlike $pattern } } } } It 'Install-NerdFont - Handles -All without downloading already installed fonts' { - $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } $testFonts = @( [pscustomobject]@{ Name = 'AllPathSmokeTest' URL = 'https://example.invalid/all-path-smoke.zip' } ) - InModuleScope NerdFonts -Parameters @{ fonts = $testFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - try { + Use-TestFontData -Fonts $testFonts -Body { Mock -ModuleName NerdFonts Get-Font { [pscustomobject]@{ Name = 'AllPathSmokeTest Nerd Font' } } Mock -ModuleName NerdFonts Install-Font {} - { Install-NerdFont -All -Verbose -ErrorAction Stop } | Should -Not -Throw - Should -Invoke -ModuleName NerdFonts Install-Font -Times 0 -Exactly - } finally { - InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { - param($fonts) - $script:NerdFonts = $fonts - } + Install-NerdFont -All -Verbose -ErrorAction Stop + + Should-NotInvoke -CommandName Install-Font -ModuleName NerdFonts } } It 'Install-NerdFont - Throws when -Scope AllUsers without admin rights' { Mock -ModuleName NerdFonts IsAdmin { $false } - { Install-NerdFont -Name 'Tinos' -Scope AllUsers -ErrorAction Stop } | Should -Throw '*Administrator*' + { Install-NerdFont -Name 'Tinos' -Scope AllUsers -ErrorAction Stop } | + Should-Throw -ExceptionMessage '*Administrator*' } It 'Install-NerdFont - Falls back to download when cache read fails' { - $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } - $loadedFonts = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath '../src/FontsData.json') | ConvertFrom-Json - $goodFont = $loadedFonts | Where-Object Name -EQ 'Tinos' | Select-Object -First 1 + $goodFont = Get-TestFont -Name 'Tinos' $fontName = $goodFont.Name - $cacheRoot = if ($IsWindows) { - Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'PSModule/NerdFonts/cache' - } else { - Join-Path $HOME '.cache/PSModule/NerdFonts' - } + $cacheRoot = Get-TestCacheRoot $cacheTag = if ($goodFont.URL -match '/releases/download/([^/]+)/') { $Matches[1] } else { 'unknown' } - $cacheTagDir = Join-Path $cacheRoot $cacheTag + $cacheTagDir = Join-Path -Path $cacheRoot -ChildPath $cacheTag $downloadFileName = Split-Path -Path $goodFont.URL -Leaf - $cachedFile = Join-Path $cacheTagDir $downloadFileName + $cachedFile = Join-Path -Path $cacheTagDir -ChildPath $downloadFileName # Backup any existing real cache entry to restore after the test $backupPath = "$cachedFile.test-bak" @@ -259,36 +236,33 @@ Describe 'Module' { Copy-Item -LiteralPath $cachedFile -Destination $backupPath -Force } - $testFonts = @($goodFont) - InModuleScope NerdFonts -Parameters @{ fonts = $testFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - $fileLock = $null try { - # Lock the cached file with an exclusive share so Copy-Item fails, forcing the - # function to fall back to a real download using live test data. - if (-not (Test-Path -LiteralPath $cacheTagDir)) { - $null = New-Item -ItemType Directory -Path $cacheTagDir -Force - } - if (Test-Path -LiteralPath $cachedFile) { - Remove-Item -LiteralPath $cachedFile -Recurse -Force -ErrorAction SilentlyContinue - } - Set-Content -LiteralPath $cachedFile -Value 'locked-cache-entry' -Force - $fileLock = [System.IO.File]::Open( - $cachedFile, - [System.IO.FileMode]::Open, - [System.IO.FileAccess]::Read, - [System.IO.FileShare]::None - ) + Use-TestFontData -Fonts @($goodFont) -Body { + # Lock the cached file with an exclusive share so Copy-Item fails, forcing the + # function to fall back to a real download using live test data. + if (-not (Test-Path -LiteralPath $cacheTagDir)) { + $null = New-Item -ItemType Directory -Path $cacheTagDir -Force + } + if (Test-Path -LiteralPath $cachedFile) { + Remove-Item -LiteralPath $cachedFile -Recurse -Force -ErrorAction SilentlyContinue + } + Set-Content -LiteralPath $cachedFile -Value 'locked-cache-entry' -Force + $fileLock = [System.IO.File]::Open( + $cachedFile, + [System.IO.FileMode]::Open, + [System.IO.FileAccess]::Read, + [System.IO.FileShare]::None + ) - Mock -ModuleName NerdFonts Get-Font { @() } - Mock -ModuleName NerdFonts Install-Font {} + Mock -ModuleName NerdFonts Get-Font { @() } + Mock -ModuleName NerdFonts Install-Font {} - # Should not throw — falls back to download - { Install-NerdFont -Name $fontName -Force:$false -ErrorAction Stop } | Should -Not -Throw - Should -Invoke -ModuleName NerdFonts Install-Font -Times 1 -Exactly + # Falls back to a real download instead of failing on the unreadable cache entry. + Install-NerdFont -Name $fontName -Force:$false -ErrorAction Stop + + Should-Invoke -CommandName Install-Font -ModuleName NerdFonts -Times 1 -Exactly + } } finally { # Release the lock before restoring cache state. if ($fileLock) { @@ -308,21 +282,12 @@ Describe 'Module' { if (-not $hadExistingCacheRoot -and (Test-Path -LiteralPath $cacheRoot)) { Remove-Item -LiteralPath $cacheRoot -Recurse -Force -ErrorAction SilentlyContinue } - InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { - param($fonts) - $script:NerdFonts = $fonts - } } } It 'Install-NerdFont - Deduplicates variant files from cached archives' { - $originalFonts = InModuleScope NerdFonts { $script:NerdFonts } $fontName = 'DuplicateMonoTest' - $cacheRoot = if ($IsWindows) { - Join-Path -Path ([Environment]::GetFolderPath('LocalApplicationData')) -ChildPath 'PSModule/NerdFonts/cache' - } else { - Join-Path -Path $HOME -ChildPath '.cache/PSModule/NerdFonts' - } + $cacheRoot = Get-TestCacheRoot $cacheTagDir = Join-Path -Path $cacheRoot -ChildPath 'test-dedup-v0' $zipPath = Join-Path -Path $cacheTagDir -ChildPath 'DuplicateMonoTest.zip' $hadExistingCacheRoot = Test-Path -LiteralPath $cacheRoot @@ -354,25 +319,24 @@ Describe 'Module' { URL = 'https://github.com/ryanoasis/nerd-fonts/releases/download/test-dedup-v0/DuplicateMonoTest.zip' } ) - InModuleScope NerdFonts -Parameters @{ fonts = $testFonts } { - param($fonts) - $script:NerdFonts = $fonts - } - Mock -ModuleName NerdFonts Get-Font { @() } - $script:TestCapturedFiles = $null - Mock -ModuleName NerdFonts Install-Font {} -ParameterFilter { - $script:TestCapturedFiles = @( - Get-ChildItem -Path $Path -Recurse -File -Include '*.ttf', '*.otf' | - Select-Object -ExpandProperty Name - ) - $true - } + Use-TestFontData -Fonts $testFonts -Body { + Mock -ModuleName NerdFonts Get-Font { @() } + $script:TestCapturedFiles = $null + Mock -ModuleName NerdFonts Install-Font {} -ParameterFilter { + $script:TestCapturedFiles = @( + Get-ChildItem -Path $Path -Recurse -File -Include '*.ttf', '*.otf' | + Select-Object -ExpandProperty Name + ) + $true + } - { Install-NerdFont -Name $fontName -Variant Mono -ErrorAction Stop } | Should -Not -Throw - Should -Invoke -ModuleName NerdFonts Install-Font -Times 1 -Exactly - $script:TestCapturedFiles.Count | Should -Be 1 - ($script:TestCapturedFiles | Select-Object -Unique).Count | Should -Be 1 + Install-NerdFont -Name $fontName -Variant Mono -ErrorAction Stop + + Should-Invoke -CommandName Install-Font -ModuleName NerdFonts -Times 1 -Exactly + $script:TestCapturedFiles | Should-BeCollection -Count 1 + ($script:TestCapturedFiles | Select-Object -Unique).Count | Should-Be 1 + } } finally { if (Test-Path -LiteralPath $cacheTagDir) { Remove-Item -LiteralPath $cacheTagDir -Recurse -Force -ErrorAction SilentlyContinue @@ -380,10 +344,6 @@ Describe 'Module' { if (-not $hadExistingCacheRoot -and (Test-Path -LiteralPath $cacheRoot)) { Remove-Item -LiteralPath $cacheRoot -Recurse -Force -ErrorAction SilentlyContinue } - InModuleScope NerdFonts -Parameters @{ fonts = $originalFonts } { - param($fonts) - $script:NerdFonts = $fonts - } } } } From 1f7d9adaafe397e55f385c51b60f32338e9aa789 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Fri, 28 Aug 2026 07:05:06 +0200 Subject: [PATCH 3/4] Allow ref-pinned PSModule workflow references in zizmor The Process-PSModule caller tracks the mutable @v8 major tag, which zizmor's default blanket hash-pin policy reports as a high-severity unpinned-uses finding. Add .github/linters/zizmor.yaml with a 'PSModule/*: ref-pin' policy so first-party PSModule references may use a symbolic ref. Any other owner still falls through to the implicit '*': hash-pin rule, so third-party actions must be hash-pinned. Super-linter invokes zizmor with '--config' resolved from LINTER_RULES_PATH, so the file must be named zizmor.yaml under .github/linters. Enable the validator by dropping the VALIDATE_GITHUB_ACTIONS_ZIZMOR override, now that the workflows pass. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/PSModule.yml | 1 - .github/linters/zizmor.yaml | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .github/linters/zizmor.yaml diff --git a/.github/PSModule.yml b/.github/PSModule.yml index 60d3f27..d640592 100644 --- a/.github/PSModule.yml +++ b/.github/PSModule.yml @@ -24,7 +24,6 @@ Linter: env: VALIDATE_BIOME_FORMAT: false VALIDATE_BIOME_LINT: false - VALIDATE_GITHUB_ACTIONS_ZIZMOR: false VALIDATE_JSCPD: false VALIDATE_JSON_PRETTIER: false VALIDATE_MARKDOWN_PRETTIER: false diff --git a/.github/linters/zizmor.yaml b/.github/linters/zizmor.yaml new file mode 100644 index 0000000..af0b88f --- /dev/null +++ b/.github/linters/zizmor.yaml @@ -0,0 +1,15 @@ +# Configuration for zizmor, the GitHub Actions static analysis tool. +# Reference: https://docs.zizmor.sh/configuration/ +# +# Super-linter passes this file with '--config', resolved from LINTER_RULES_PATH +# (default '.github/linters'), so the filename must stay 'zizmor.yaml'. + +rules: + unpinned-uses: + config: + # Process-PSModule is a first-party PSModule workflow that this repository + # tracks by major tag on purpose, so the framework can ship fixes without a + # Dependabot bump per consumer. Allow a symbolic ref for the PSModule org + # only; everything else falls through to the implicit '*': hash-pin rule. + policies: + PSModule/*: ref-pin From 8c31087006da2a963ae21ffaacfcf5a766c438ff Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Fri, 28 Aug 2026 19:46:41 +0200 Subject: [PATCH 4/4] Add Dependabot cooldown and fix zizmor config lint warnings Enabling the zizmor validator surfaced a pre-existing dependabot-cooldown finding: the github-actions updater had no cooldown, so Dependabot used its implicit three-day default while zizmor requires seven. Set 'cooldown.default-days: 7' on the updater, which reduces the risk of pulling a compromised release before the ecosystem takes it down. Also add the document start marker and wrap the comment lines in zizmor.yaml so it passes the repository's yamllint rules. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/dependabot.yml | 2 ++ .github/linters/zizmor.yaml | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 53188fe..f25754b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,3 +12,5 @@ updates: - github-actions schedule: interval: weekly + cooldown: + default-days: 7 diff --git a/.github/linters/zizmor.yaml b/.github/linters/zizmor.yaml index af0b88f..9174feb 100644 --- a/.github/linters/zizmor.yaml +++ b/.github/linters/zizmor.yaml @@ -1,15 +1,18 @@ +--- # Configuration for zizmor, the GitHub Actions static analysis tool. # Reference: https://docs.zizmor.sh/configuration/ # -# Super-linter passes this file with '--config', resolved from LINTER_RULES_PATH -# (default '.github/linters'), so the filename must stay 'zizmor.yaml'. +# Super-linter passes this file with '--config', resolved from +# LINTER_RULES_PATH (default '.github/linters'), so the filename must stay +# 'zizmor.yaml'. rules: unpinned-uses: config: - # Process-PSModule is a first-party PSModule workflow that this repository - # tracks by major tag on purpose, so the framework can ship fixes without a - # Dependabot bump per consumer. Allow a symbolic ref for the PSModule org - # only; everything else falls through to the implicit '*': hash-pin rule. + # Process-PSModule is a first-party PSModule workflow that this + # repository tracks by major tag on purpose, so the framework can ship + # fixes without a Dependabot bump per consumer. Allow a symbolic ref for + # the PSModule org only; everything else falls through to the implicit + # '*': hash-pin rule. policies: PSModule/*: ref-pin