diff --git a/.artifactignore b/.artifactignore deleted file mode 100644 index 1c7b2496e..000000000 --- a/.artifactignore +++ /dev/null @@ -1,15 +0,0 @@ -src/csharp/bin/ -src/csharp/obj/ -src/csharp/.vs/ -publish/ -.git -.gitignore -.gitattributes -.artifactignore -azure-pipelines.yml -.github -.vscode -LICENSE -README.MD -VERIFICATION.txt - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..c9d371327 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,152 @@ +# GitHub Actions CI — the CI for the Pester 5 maintenance branch. +# +# Same shape as the workflow on main: build once on Windows, publish the built +# module as an artifact, then fan out the test matrix, then a single "Done" +# gate. That gate is the check to require in branch protection, it is named the +# same on every branch so one branch policy covers all of them. +# +# This replaces azure-pipelines.yml, which is removed in the same change. +# Releasing still runs on Azure DevOps (azure-pipelines-publish.yml). +# +# No code coverage here. v5 turns coverage off in CI ("still very slow"), so +# unlike main there is nothing to summarize or upload. +# +# Test results are uploaded as artifacts and rendered by test-report.yml, which +# lives on main. workflow_run workflows always run from the default branch, so +# runs of this workflow get reports without a copy of that file here. +name: CI + +on: + push: + branches: ['rel/*'] + paths-ignore: + - '.devcontainer/**' + - '.vscode/**' + - 'docs/**' + - 'images/**' + - '**/*.md' + pull_request: + branches: ['rel/*'] + paths-ignore: + - '.devcontainer/**' + - '.vscode/**' + - 'docs/**' + - 'images/**' + - '**/*.md' + workflow_dispatch: + +permissions: + contents: read + +env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: '1' + DOTNET_CLI_TELEMETRY_OPTOUT: '1' + DOTNET_GENERATE_ASPNET_CERTIFICATE: '0' + DOTNET_NOLOGO: '1' + CI: '1' + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build + runs-on: windows-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Setup .NET (global.json) + uses: actions/setup-dotnet@v6 + with: + global-json-file: global.json + cache: true + cache-dependency-path: '**/packages.lock.json' + + - name: Build module (inline, locked restore) + shell: pwsh + run: | + "Running .NET SDK v$(dotnet --version)" + ./build.ps1 -LockedRestore -Clean -Inline + + - name: Upload build output + uses: actions/upload-artifact@v7 + with: + name: pester-build + retention-days: 1 + include-hidden-files: true + path: | + bin/ + src/ + tst/ + build.ps1 + test.ps1 + global.json + !src/csharp/**/bin/** + !src/csharp/**/obj/** + !src/csharp/.vs/** + + test: + name: ${{ matrix.name }} + needs: build + runs-on: ${{ matrix.os }} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + # PowerShell 7 (psexe: pwsh) — latest + one previous GA per OS. + - { name: 'PS7 - Ubuntu latest', id: ps7-ubuntu-latest, os: ubuntu-latest, psexe: pwsh } + - { name: 'PS7 - Ubuntu 22.04', id: ps7-ubuntu-2204, os: ubuntu-22.04, psexe: pwsh } + - { name: 'PS7 - macOS latest', id: ps7-macos-latest, os: macos-latest, psexe: pwsh } + - { name: 'PS7 - macOS 14', id: ps7-macos-14, os: macos-14, psexe: pwsh } + - { name: 'PS7 - Windows latest', id: ps7-windows-latest, os: windows-latest, psexe: pwsh } + - { name: 'PS7 - Windows 2022', id: ps7-windows-2022, os: windows-2022, psexe: pwsh } + # Windows PowerShell 5.1 (psexe: powershell) — Windows only. + - { name: 'PS5.1 - Windows latest', id: ps51-windows-latest, os: windows-latest, psexe: powershell } + - { name: 'PS5.1 - Windows 2022', id: ps51-windows-2022, os: windows-2022, psexe: powershell } + steps: + # No checkout: the legs run against the artifact from Build, so they test + # the exact same build. + - name: Download build output + uses: actions/download-artifact@v8 + with: + name: pester-build + path: . + + # `shell:` cannot take a matrix expression, so run from a fixed pwsh (present + # on every runner) and launch the leg's interpreter: pwsh for PS7, powershell + # (Windows PowerShell 5.1) for the 5.1 legs. exit $LASTEXITCODE so a failure + # in the launched process still fails the job. + - name: Test Pester + shell: pwsh + run: | + & ${{ matrix.psexe }} -NoProfile -Command "& ./test.ps1 -CI -NoBuild" + exit $LASTEXITCODE + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v7 + with: + name: results-${{ matrix.id }} + retention-days: 7 + if-no-files-found: ignore + path: testResults.xml + + # Single gate after the matrix so branch protection and auto-merge only need to + # require "Done" instead of listing all 8 legs. This is the check to require on + # this branch. + done: + name: Done + needs: test + if: always() + runs-on: ubuntu-latest + steps: + - name: All test legs passed + run: | + echo "Test matrix result: ${{ needs.test.result }}" + [ "${{ needs.test.result }}" = "success" ] || exit 1 + echo "done" diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml index f07dd302e..cf4260a25 100644 --- a/.github/workflows/code-analysis.yml +++ b/.github/workflows/code-analysis.yml @@ -2,9 +2,9 @@ name: Code analysis on: push: - branches: [ main ] + branches: [ 'rel/*' ] pull_request: - branches: [ main ] + branches: [ 'rel/*' ] workflow_dispatch: jobs: @@ -18,10 +18,10 @@ jobs: steps: # Checkout the repository to the GitHub Actions runner - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: PowerShell Module Cache - uses: potatoqualitee/psmodulecache@v6.2 + uses: potatoqualitee/psmodulecache@v6.2.1 with: modules-to-cache: PSScriptAnalyzer, ConvertToSARIF:1.0.0 @@ -37,7 +37,7 @@ jobs: # Upload the SARIF file generated in the previous step - name: Upload SARIF results file - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: results.sarif # codeql: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4cb628c99..21eef289e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -92,9 +92,9 @@ Get-Help ./test.ps1 -Detailed ## Continuous Integration -The Azure Devops Pipeline azure-pipelines.yml file contains the code definition used for builds, unit and integration tests in the CI pipeline. +CI runs on GitHub Actions. `.github/workflows/ci.yml` builds the module once and then runs the test matrix: PS7 and PS5.1, on Windows, Linux (Ubuntu) and MacOS. -Within the pipeline, tests are executed against PS7 Core on a strategy matrix of machines, including Ubuntu 16.04, 18.04, macOS Mojave 10.14, Catalina 10.15, Windows Server 2016, 2019. Tests are also executed against PS6.2, PS4, PS3. +Releasing still runs on Azure DevOps (`azure-pipelines-publish.yml`), because it needs the code signing and publishing credentials. ## Documentation diff --git a/README.md b/README.md index 4b6d1e8c9..a9eed8921 100644 --- a/README.md +++ b/README.md @@ -154,9 +154,9 @@ test_script: See it [in action here!](https://ci.appveyor.com/project/nohwnd/planets) If you do not need to test your scripts against PowerShell Core, just simply remove the entire line mentioning Ubuntu. -Pester itself is built on AzureDevOps, and distributed mainly via PowerShell gallery. +Pester itself is built on GitHub Actions, and distributed mainly via PowerShell gallery. -[![Build Status](https://nohwnd.visualstudio.com/Pester/_apis/build/status/Pester%20PR?branchName=main)](https://nohwnd.visualstudio.com/Pester/_build/latest?definitionId=6&branchName=main) [![latest version](https://img.shields.io/powershellgallery/v/Pester.svg?label=latest+version)](https://www.powershellgallery.com/packages/Pester) [![downloads](https://img.shields.io/powershellgallery/dt/Pester.svg?label=downloads)](https://www.powershellgallery.com/packages/Pester) +[![Build Status](https://github.com/pester/Pester/actions/workflows/ci.yml/badge.svg?branch=rel%2F5.x.x)](https://github.com/pester/Pester/actions/workflows/ci.yml?query=branch%3Arel%2F5.x.x) [![latest version](https://img.shields.io/powershellgallery/v/Pester.svg?label=latest+version)](https://www.powershellgallery.com/packages/Pester) [![downloads](https://img.shields.io/powershellgallery/dt/Pester.svg?label=downloads)](https://www.powershellgallery.com/packages/Pester) ## Further reading diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 9e736cf1d..000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,132 +0,0 @@ -trigger: - branches: - include: - - main - - rel/* - paths: - exclude: - - .devcontainer - - .github - - .vscode - - docs - - images - - BACKERS.md - - CODE_OF_CONDUCT.md - - CONTRIBUTING.md - - README.md - - SECURITY.md - - SUPPORT.md -pr: - branches: - include: - - main - - rel/* - - dev/* - paths: - exclude: - - .devcontainer - - .github - - .vscode - - docs - - images - - BACKERS.md - - CODE_OF_CONDUCT.md - - CONTRIBUTING.md - - README.md - - SECURITY.md - - SUPPORT.md - -variables: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - DOTNET_GENERATE_ASPNET_CERTIFICATE: 0 - DOTNET_NOLOGO: 1 - CI: 1 - NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages - -stages: - - stage: Build - jobs: - - job: build - workspace: - clean: all - pool: - vmImage: windows-2022 - timeoutInMinutes: 5 - steps: - - checkout: self - fetchDepth: 1 - - task: Cache@2 - displayName: Cache - inputs: - # https://docs.microsoft.com/en-us/azure/devops/pipelines/artifacts/caching-nuget?view=azure-devops - key: 'nuget | "$(Agent.OS)" | **/packages.lock.json,!**/bin/**,!**/obj/**' - path: '$(NUGET_PACKAGES)' - cacheHitVar: 'CACHE_RESTORED' - - powershell: | - "Running .NET SDK v$(dotnet --version)" - ./build.ps1 -LockedRestore -Clean -Inline - - publish: $(Build.SourcesDirectory) - artifact: all - - - stage: Test - jobs: - - job: - workspace: - clean: all - strategy: - matrix: - PS7_Ubuntu_22_04: - vmImage: ubuntu-22.04 - pwsh: true - PS7_Ubuntu_24_04: - vmImage: ubuntu-24.04 - pwsh: true - PS7_macOS_14: - vmImage: macOS-14 - pwsh: true - PS7_macOS_15: - vmImage: macOS-15 - pwsh: true - PS7_Windows_Server2022: - vmImage: windows-2022 - pwsh: true - PS_5_1_Windows_Server2022: - vmImage: windows-2022 - pwsh: false - PS7_Windows_Server2025: - vmImage: windows-2025 - pwsh: true - PS_5_1_Windows_Server2025: - vmImage: windows-2025 - pwsh: false - pool: - vmImage: $[ variables['vmImage'] ] - steps: - - checkout: none - - task: DownloadPipelineArtifact@2 - inputs: - buildType: 'current' - artifactName: 'all' - targetPath: '$(Build.SourcesDirectory)' - - task: PowerShell@2 - displayName: 'Test Pester' - inputs: - targetType: inline - pwsh: $(pwsh) - script: | - & ./test.ps1 -CI -PassThru -NoBuild - workingDirectory: '$(Build.SourcesDirectory)' - - task: PublishCodeCoverageResults@2 - inputs: - summaryFileLocation: 'coverage.xml' - pathToSources: 'src/' - failIfCoverageEmpty: false - condition: succeededOrFailed() - - task: PublishTestResults@2 - inputs: - testResultsFormat: 'NUnit' - testResultsFiles: 'testResults.xml' - failTaskOnFailedTests: true - condition: succeededOrFailed() - timeoutInMinutes: 20 diff --git a/test.ps1 b/test.ps1 index 6a905b6ab..fe39facb1 100644 --- a/test.ps1 +++ b/test.ps1 @@ -150,6 +150,10 @@ if ($CI) { $configuration.CodeCoverage.UseBreakpoints = $false $configuration.TestResult.Enabled = $true + + # Modern NUnit3 schema, which is what dorny/test-reporter reads in + # .github/workflows/test-report.yml on main. + $configuration.TestResult.OutputFormat = 'NUnit3' } $r = Invoke-Pester -Configuration $configuration