mirror of https://github.com/raandree/NTFSSecurity
Browse Source
.github/workflows/ci.yml replaces appveyor.yml and runs on pull requests and pushes to master: - build (windows-2025): the same restore, Release build, and documentation checks as before, then the Pester tests in Windows PowerShell 5.1 and in PowerShell 7. .github/scripts/Invoke-Tests.ps1 writes the counts and the failed tests to the job summary and the NUnit file to the test-results artifact, and also fails on test files that fail. - wiki (ubuntu-latest): generates the wiki from Docs; on pull requests it lists the pages that would change, from master it publishes them with the built-in token. Only this job has contents: write. Actions are pinned by commit SHA. Every native command checks its exit code, because GitHub checks only the last one. The contributor guide describes the workflow and the wiki. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant <ai@example.com>pull/96/head
4 changed files with 296 additions and 98 deletions
@ -0,0 +1,79 @@ |
|||
<# |
|||
.SYNOPSIS |
|||
Runs the Pester tests in the Tests folder and reports the result to GitHub Actions. |
|||
|
|||
.DESCRIPTION |
|||
Imports Pester 5.7.1, runs the tests against the module build in NTFSSecurity\bin\Release, writes the result |
|||
file in the NUnit format, and adds the counts and the failed tests to the job summary of GitHub Actions. Fails if |
|||
a test or a test file fails. |
|||
|
|||
.PARAMETER ResultPath |
|||
Specifies the path of the result file. |
|||
|
|||
.PARAMETER Title |
|||
Specifies the heading of the test results in the job summary, such as the PowerShell edition. |
|||
|
|||
.EXAMPLE |
|||
.\.github\scripts\Invoke-Tests.ps1 -ResultPath TestResults\WindowsPowerShell.xml -Title 'Windows PowerShell 5.1' |
|||
|
|||
Runs the tests and writes the result file. Outside GitHub Actions, the script writes no job summary. |
|||
#> |
|||
[CmdletBinding()] |
|||
param ( |
|||
[Parameter(Mandatory)] |
|||
[ValidateNotNullOrEmpty()] |
|||
[string] |
|||
$ResultPath, |
|||
|
|||
[Parameter(Mandatory)] |
|||
[ValidateNotNullOrEmpty()] |
|||
[string] |
|||
$Title |
|||
) |
|||
|
|||
$ErrorActionPreference = 'Stop' |
|||
Import-Module -Name Pester -RequiredVersion 5.7.1 |
|||
|
|||
$resultFolder = Split-Path -Path $ResultPath -Parent |
|||
if ($resultFolder -and -not (Test-Path -LiteralPath $resultFolder)) { |
|||
New-Item -ItemType Directory -Path $resultFolder | Out-Null |
|||
} |
|||
|
|||
$configuration = New-PesterConfiguration |
|||
$configuration.Run.Path = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Tests' |
|||
$configuration.Run.PassThru = $true |
|||
$configuration.Output.Verbosity = 'Detailed' |
|||
$configuration.TestResult.Enabled = $true |
|||
$configuration.TestResult.OutputFormat = 'NUnitXml' |
|||
$configuration.TestResult.OutputPath = $ResultPath |
|||
$result = Invoke-Pester -Configuration $configuration |
|||
|
|||
if ($env:GITHUB_STEP_SUMMARY) { |
|||
$summary = New-Object -TypeName 'System.Collections.Generic.List[string]' |
|||
$summary.Add("### Tests in $Title") |
|||
$summary.Add('') |
|||
$summary.Add('| Result | Passed | Failed | Skipped | Total |') |
|||
$summary.Add('| --- | ---: | ---: | ---: | ---: |') |
|||
$summary.Add(('| {0} | {1} | {2} | {3} | {4} |' -f $result.Result, $result.PassedCount, $result.FailedCount, |
|||
$result.SkippedCount, $result.TotalCount)) |
|||
if ($result.Failed.Count -gt 0 -or $result.FailedContainersCount -gt 0) { |
|||
$summary.Add('') |
|||
$summary.Add('Failed:') |
|||
$summary.Add('') |
|||
foreach ($test in $result.Failed) { |
|||
$message = "$(@($test.ErrorRecord)[0])" -replace '\s+', ' ' |
|||
$summary.Add(('- {0}: {1}' -f $test.ExpandedPath, $message)) |
|||
} |
|||
foreach ($container in $result.Containers | Where-Object -Property Result -EQ -Value 'Failed') { |
|||
$summary.Add(('- {0}: {1}' -f $container.Item, ("$(@($container.ErrorRecord)[0])" -replace '\s+', ' '))) |
|||
} |
|||
} |
|||
$summary.Add('') |
|||
|
|||
$encoding = New-Object -TypeName 'System.Text.UTF8Encoding' -ArgumentList $false |
|||
[IO.File]::AppendAllText($env:GITHUB_STEP_SUMMARY, ($summary -join "`n") + "`n", $encoding) |
|||
} |
|||
|
|||
if ($result.Result -ne 'Passed') { |
|||
throw "The tests in $Title failed: $($result.FailedCount) failed tests, $($result.FailedContainersCount) failed test files." |
|||
} |
|||
@ -0,0 +1,181 @@ |
|||
# Builds the NTFSSecurity module from source, checks that the cmdlet |
|||
# documentation in Docs/Cmdlets and the help file generated from it match the |
|||
# cmdlets of that build, runs the Pester tests in Windows PowerShell 5.1 and |
|||
# PowerShell 7, and publishes Docs to the GitHub wiki from master. |
|||
name: CI |
|||
|
|||
on: |
|||
pull_request: |
|||
push: |
|||
branches: |
|||
- master |
|||
workflow_dispatch: |
|||
|
|||
permissions: |
|||
contents: read |
|||
|
|||
concurrency: |
|||
group: ci-${{ github.ref }} |
|||
cancel-in-progress: ${{ github.event_name == 'pull_request' }} |
|||
|
|||
jobs: |
|||
build: |
|||
name: Build and test |
|||
runs-on: windows-2025 |
|||
timeout-minutes: 30 |
|||
steps: |
|||
- name: Keep Windows line endings in the working tree |
|||
run: git config --global core.autocrlf true |
|||
|
|||
- name: Check out the repository |
|||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
|||
with: |
|||
persist-credentials: false |
|||
|
|||
- name: Install platyPS, MarkdownLinkCheck, and Pester |
|||
shell: powershell |
|||
run: | |
|||
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 |
|||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null |
|||
# Installed for all users, so that PowerShell 7 finds the modules, too |
|||
Install-Module -Name platyPS -RequiredVersion 0.14.2 -Scope AllUsers -Force |
|||
Install-Module -Name MarkdownLinkCheck -RequiredVersion 0.2.0 -Scope AllUsers -Force |
|||
# The image includes Pester 3.4.0, which is signed by a different publisher. |
|||
Install-Module -Name Pester -RequiredVersion 5.7.1 -Scope AllUsers -Force -SkipPublisherCheck |
|||
|
|||
- name: Restore the NuGet packages |
|||
shell: powershell |
|||
run: | |
|||
nuget restore NTFSSecurity\packages.config -PackagesDirectory packages -NonInteractive |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "NuGet failed with exit code $LASTEXITCODE." |
|||
} |
|||
nuget restore Security2\packages.config -PackagesDirectory packages -NonInteractive |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "NuGet failed with exit code $LASTEXITCODE." |
|||
} |
|||
# Provides the .NET Framework 4.5.2 reference assemblies, so the build does |
|||
# not depend on a targeting pack installed on the runner. |
|||
nuget install Microsoft.NETFramework.ReferenceAssemblies.net452 -Version 1.0.3 -OutputDirectory packages -NonInteractive |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "NuGet failed with exit code $LASTEXITCODE." |
|||
} |
|||
|
|||
- name: Build the module |
|||
id: build |
|||
shell: powershell |
|||
run: | |
|||
# MSBuild isn't on the path of the runner; vswhere finds the one of Visual Studio. |
|||
$vswhere = Join-Path -Path ${env:ProgramFiles(x86)} -ChildPath 'Microsoft Visual Studio\Installer\vswhere.exe' |
|||
$msbuild = & $vswhere -latest -requires Microsoft.Component.MSBuild -find 'MSBuild\**\Bin\MSBuild.exe' | Select-Object -First 1 |
|||
if (-not $msbuild) { |
|||
throw 'MSBuild was not found.' |
|||
} |
|||
$referenceAssemblies = "$env:GITHUB_WORKSPACE\packages\Microsoft.NETFramework.ReferenceAssemblies.net452.1.0.3\build" |
|||
& $msbuild NTFSSecurity\NTFSSecurity.csproj /nologo /verbosity:minimal /p:Configuration=Release "/p:TargetFrameworkRootPath=$referenceAssemblies" "/p:FrameworkPathOverride=$referenceAssemblies\.NETFramework\v4.5.2" |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "MSBuild failed with exit code $LASTEXITCODE." |
|||
} |
|||
|
|||
- name: Check the documentation against the build |
|||
shell: powershell |
|||
run: | |
|||
Import-Module -Name platyPS -RequiredVersion 0.14.2 |
|||
Import-Module -Name MarkdownLinkCheck -RequiredVersion 0.2.0 |
|||
Import-Module -Name .\NTFSSecurity\bin\Release\NTFSSecurity.psd1 -Force |
|||
|
|||
# 01. Test that the documentation matches the cmdlets built from source |
|||
Update-MarkdownHelp -Path ./Docs/Cmdlets | Out-Null |
|||
$diff = git diff -- Docs/Cmdlets |
|||
if ($diff) { |
|||
throw "Help is not up-to-date, run Update-MarkdownHelp: $diff" |
|||
} |
|||
|
|||
# 02. Verify hyperlinks |
|||
$brokenLinks = Get-MarkdownLink -Path .\Docs\ -BrokenOnly |
|||
if ($brokenLinks) { |
|||
throw "Found broken hyperlinks $brokenLinks" |
|||
} |
|||
|
|||
# 03. Test that the help file of the module matches the documentation |
|||
New-ExternalHelp -Path ./Docs/Cmdlets -OutputPath ./NTFSSecurity/en-US -Force | Out-Null |
|||
$helpChanges = git status --porcelain -- NTFSSecurity/en-US |
|||
if ($helpChanges) { |
|||
throw "The help file is not up-to-date, run New-ExternalHelp -Path .\Docs\Cmdlets -OutputPath .\NTFSSecurity\en-US -Force: $helpChanges" |
|||
} |
|||
|
|||
# 04. Run the Pester tests against the build, also when the documentation check failed |
|||
- name: Run the tests in Windows PowerShell 5.1 |
|||
if: ${{ !cancelled() && steps.build.outcome == 'success' }} |
|||
shell: powershell |
|||
run: .\.github\scripts\Invoke-Tests.ps1 -ResultPath TestResults\WindowsPowerShell.xml -Title 'Windows PowerShell 5.1' |
|||
|
|||
- name: Run the tests in PowerShell 7 |
|||
if: ${{ !cancelled() && steps.build.outcome == 'success' }} |
|||
shell: pwsh |
|||
run: ./.github/scripts/Invoke-Tests.ps1 -ResultPath TestResults/PowerShell7.xml -Title 'PowerShell 7' |
|||
|
|||
- name: Upload the test results |
|||
if: ${{ !cancelled() }} |
|||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
|||
with: |
|||
name: test-results |
|||
path: TestResults/ |
|||
if-no-files-found: ignore |
|||
|
|||
wiki: |
|||
name: Wiki |
|||
needs: build |
|||
runs-on: ubuntu-latest |
|||
timeout-minutes: 10 |
|||
# Only this job can write: it publishes the wiki from master. On pull |
|||
# requests, it shows the pages that would change. |
|||
permissions: |
|||
contents: write |
|||
steps: |
|||
- name: Check out the repository |
|||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 |
|||
with: |
|||
persist-credentials: false |
|||
|
|||
- name: Generate the wiki pages |
|||
shell: pwsh |
|||
env: |
|||
GH_TOKEN: ${{ github.token }} |
|||
run: | |
|||
gh auth setup-git |
|||
$wiki = Join-Path -Path $env:RUNNER_TEMP -ChildPath 'wiki' |
|||
git clone --quiet --depth 1 "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY.wiki.git" $wiki |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "Cloning the wiki failed with exit code $LASTEXITCODE." |
|||
} |
|||
./.github/scripts/Export-WikiContent.ps1 -Path ./Docs -DestinationPath $wiki -RepositoryUrl "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY" |
|||
git -C $wiki add --all |
|||
$changes = @(git -C $wiki diff --cached --name-status) |
|||
$summary = if ($changes) { |
|||
@('### Wiki', '', 'Changed pages (A added, M modified, D deleted):', '', '```text') + $changes + @('```') |
|||
} else { |
|||
@('### Wiki', '', 'The wiki is up to date.') |
|||
} |
|||
Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY -Value $summary |
|||
Add-Content -LiteralPath $env:GITHUB_ENV -Value "WIKI_PATH=$wiki" |
|||
|
|||
- name: Publish the wiki |
|||
if: ${{ github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }} |
|||
shell: pwsh |
|||
env: |
|||
GH_TOKEN: ${{ github.token }} |
|||
run: | |
|||
git -C $env:WIKI_PATH diff --cached --quiet |
|||
if ($LASTEXITCODE -eq 0) { |
|||
'The wiki is up to date.' |
|||
exit 0 |
|||
} |
|||
git -C $env:WIKI_PATH -c user.name='github-actions[bot]' -c user.email='41898282+github-actions[bot]@users.noreply.github.com' commit --quiet --message "Update from $env:GITHUB_SHA" |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "Committing the wiki failed with exit code $LASTEXITCODE." |
|||
} |
|||
git -C $env:WIKI_PATH push --quiet |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "Publishing the wiki failed with exit code $LASTEXITCODE." |
|||
} |
|||
@ -1,86 +0,0 @@ |
|||
# Builds the NTFSSecurity module from source, checks that the cmdlet |
|||
# documentation in Docs/Cmdlets and the help file generated from it match the |
|||
# cmdlets of that build, and runs the Pester tests against the build. |
|||
image: Visual Studio 2022 |
|||
|
|||
init: |
|||
- ps: git config --global core.autocrlf true |
|||
|
|||
install: |
|||
- ps: | |
|||
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 |
|||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null |
|||
Install-Module -Name platyPS -RequiredVersion 0.14.2 -Force |
|||
Install-Module -Name MarkdownLinkCheck -RequiredVersion 0.2.0 -Force |
|||
# The image includes Pester 3.4.0, which is signed by a different publisher. |
|||
Install-Module -Name Pester -RequiredVersion 5.7.1 -Force -SkipPublisherCheck |
|||
|
|||
before_build: |
|||
- nuget restore NTFSSecurity\packages.config -PackagesDirectory packages -NonInteractive |
|||
- nuget restore Security2\packages.config -PackagesDirectory packages -NonInteractive |
|||
# Provides the .NET Framework 4.5.2 reference assemblies, so the build does |
|||
# not depend on a targeting pack installed on the build image. |
|||
- nuget install Microsoft.NETFramework.ReferenceAssemblies.net452 -Version 1.0.3 -OutputDirectory packages -NonInteractive |
|||
|
|||
build_script: |
|||
- ps: | |
|||
$referenceAssemblies = "$env:APPVEYOR_BUILD_FOLDER\packages\Microsoft.NETFramework.ReferenceAssemblies.net452.1.0.3\build" |
|||
msbuild NTFSSecurity\NTFSSecurity.csproj /nologo /verbosity:minimal /p:Configuration=Release "/p:TargetFrameworkRootPath=$referenceAssemblies" "/p:FrameworkPathOverride=$referenceAssemblies\.NETFramework\v4.5.2" |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "MSBuild failed with exit code $LASTEXITCODE." |
|||
} |
|||
|
|||
test_script: |
|||
- ps: | |
|||
$ErrorActionPreference = 'Stop' |
|||
Import-Module -Name platyPS |
|||
Import-Module -Name MarkdownLinkCheck |
|||
Import-Module -Name .\NTFSSecurity\bin\Release\NTFSSecurity.psd1 -Force |
|||
|
|||
# 01. Test that the documentation matches the cmdlets built from source |
|||
Update-MarkdownHelp -Path ./Docs/Cmdlets | Out-Null |
|||
$diff = git diff -- Docs/Cmdlets |
|||
if ($diff) { |
|||
throw "Help is not up-to-date, run Update-MarkdownHelp: $diff" |
|||
} |
|||
|
|||
# 02. Verify hyperlinks |
|||
$brokenLinks = Get-MarkdownLink -Path .\Docs\ -BrokenOnly |
|||
if ($brokenLinks) { |
|||
throw "Found broken hyperlinks $brokenLinks" |
|||
} |
|||
|
|||
# 03. Test that the help file of the module matches the documentation |
|||
New-ExternalHelp -Path ./Docs/Cmdlets -OutputPath ./NTFSSecurity/en-US -Force | Out-Null |
|||
$helpChanges = git status --porcelain -- NTFSSecurity/en-US |
|||
if ($helpChanges) { |
|||
throw "The help file is not up-to-date, run New-ExternalHelp -Path .\Docs\Cmdlets -OutputPath .\NTFSSecurity\en-US -Force: $helpChanges" |
|||
} |
|||
|
|||
# 04. Run the Pester tests against the module build |
|||
Import-Module -Name Pester -RequiredVersion 5.7.1 |
|||
$configuration = New-PesterConfiguration |
|||
$configuration.Run.Path = '.\Tests' |
|||
$configuration.Run.PassThru = $true |
|||
$configuration.Output.Verbosity = 'Detailed' |
|||
$result = Invoke-Pester -Configuration $configuration |
|||
|
|||
# Report every test once on the Tests tab. An uploaded NUnit file lists a |
|||
# Pester 5 test once for each block that contains it. |
|||
if ($env:APPVEYOR_API_URL -and $result.Tests) { |
|||
$tests = @(foreach ($test in $result.Tests) { |
|||
@{ |
|||
testName = $test.ExpandedPath |
|||
testFramework = 'Pester' |
|||
fileName = Split-Path -Path $test.ScriptBlock.File -Leaf |
|||
outcome = if ($test.Result -eq 'NotRun') { 'NotRunnable' } else { "$($test.Result)" } |
|||
durationMilliseconds = [long] $test.Duration.TotalMilliseconds |
|||
ErrorMessage = @($test.ErrorRecord | ForEach-Object -Process { "$_" }) -join [Environment]::NewLine |
|||
} |
|||
}) |
|||
$body = [Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject $tests -Compress)) |
|||
Invoke-RestMethod -Method Post -Uri ($env:APPVEYOR_API_URL.TrimEnd('/') + '/api/tests/batch') -Body $body -ContentType 'application/json; charset=utf-8' | Out-Null |
|||
} |
|||
if ($result.FailedCount -gt 0) { |
|||
throw "$($result.FailedCount) Pester tests failed." |
|||
} |
|||
Loading…
Reference in new issue