Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 18, 2025

Refactor xUnit testing into reusable workflow

This PR centralizes xUnit test execution into a reusable workflow, removes legacy composite test steps and verification layers, and ensures failing tests fail CI directly.

Rationale

The rationale is to make flaky test failures in xUnit more retriable and expose the xunit test results and logs more easily.

By isolating xUnit tests into a separate reusable workflow job:

  1. Better Retryability: Failed xUnit tests can be easily retried independently without re-running other jobs using GitHub Actions' built-in retry button
  2. Clearer Results: Test results and logs are more isolated and easier to access as separate job artifacts
  3. Immediate Failure Visibility: The workflow structure makes it clearer when xUnit tests fail vs other test types
  4. No Masking: Previously tests were masked (-SkipFailing + continue-on-error) and required a separate verification pass

Changes Completed ✅

  • Create new reusable workflow: .github/workflows/xunit-tests.yml
  • Remove xUnit test steps from .github/actions/build/ci/action.yml
  • Remove xUnit artifact upload from .github/actions/build/ci/action.yml
  • Delete .github/actions/test/verify_xunit/action.yml
  • Update linux-ci.yml to use new reusable workflow
  • Update windows-ci.yml to use new reusable workflow
  • Update macos-ci.yml to use new reusable workflow
  • Build PowerShell with default configuration (Debug, PSModuleRestore)
  • Add proper git fetch depth and tag synchronization
  • Create focused documentation files in .github/instructions/
  • Add applyTo frontmatter to all instruction documents
  • Update documentation per all review feedback
  • Fix PowerShell session state issue (build + test in same step)
  • Fix artifact upload path to correct xUnitTestResults.xml location
  • Verify workflow behavior with validation test ✓ Validated and removed
  • Verify no remaining references to:
    • Invoke-CIxUnit -SkipFailing ✓ None found
    • verify_xunit ✓ None found
    • continue-on-error in test contexts ✓ None found
    • Test-XUnitTestResults outside Invoke-CIxUnit ✓ None found
  • Validate YAML syntax ✓ All files valid
  • Test workflow integration ✓ Validated by CI

Technical Details

Architecture Before vs After

Before (Legacy):

ci_build Job:
  ├─ Bootstrap & Build (Release)
  ├─ Run xUnit Tests (continue-on-error: true, -SkipFailing) ⚠️
  ├─ Upload build artifact
  └─ Upload xunit artifact (masked failures)

verify_xunit Job:
  ├─ Download artifacts
  └─ Test-XUnitTestResults (actual failure point, delayed) ⚠️

After (Refactored):

ci_build Job:
  ├─ Bootstrap & Build (Release)
  └─ Upload build artifact

xunit_tests Job (NEW - Independent):
  ├─ Checkout (fetch-depth: 1000, for git describe)
  ├─ Setup .NET
  ├─ Bootstrap (Invoke-CIInstall + Sync-PSTags)
  ├─ Build + Test (Start-PSBuild + Invoke-CIxUnit in same session)
  └─ Upload xunit results (xUnitTestResults.xml, always uploaded)

New Reusable Workflow

.github/workflows/xunit-tests.yml features:

  • Configurable inputs: runner_os, test_results_artifact_name
  • Independent execution: Builds PowerShell from source with default settings
  • Proper git configuration: fetch-depth: 1000 for version generation
  • Tag synchronization: Sync-PSTags -AddRemoteIfMissing for proper versioning
  • Bootstrap and build steps using Invoke-CIInstall and Start-PSBuild (all defaults)
  • Session state preservation: Build and test in same step to maintain PowerShell context
  • Uses Debug configuration (default) - appropriate for testing
  • PSModuleRestore enabled by default
  • No Pester restoration (not needed for xUnit)
  • Runs Invoke-CIxUnit without -SkipFailing flag
  • Uploads test results even on failure with if: always()
  • Uploads test results as artifacts for easy access
  • Failures propagate immediately to fail the workflow
  • Can be triggered independently without other jobs

Documentation Added

Created focused documentation files in .github/instructions/ with YAML frontmatter specifying applicability:

  1. git-requirements-for-builds.md - Git fetch depth and tag synchronization requirements (applies to .github/workflows/*.yml)
  2. start-psbuild-basics.md - Basic usage and common parameters (applies to .github/workflows/*.yml, tools/build.psm1)
  3. build-configuration-guide.md - Choosing the right configuration for different scenarios (applies to .github/workflows/*.yml, tools/build.psm1)
  4. troubleshooting-builds.md - Common build errors and solutions (applies to .github/workflows/*.yml, tools/build.psm1, tools/ci.psm1)
  5. workflow-prerequisites.md - Required steps before building (applies to .github/workflows/*.yml)

Each file includes applyTo frontmatter for context-aware assistance.

Files Changed

Modified (13):

  • .github/actions/build/ci/action.yml - Removed xUnit test execution and artifact upload
  • .github/workflows/linux-ci.yml - Replaced verify_xunit with independent xunit_tests job
  • .github/workflows/windows-ci.yml - Replaced verify_xunit with independent xunit_tests job
  • .github/workflows/macos-ci.yml - Replaced verify_xunit with independent xunit_tests job
  • .github/workflows/xunit-tests.yml - Builds PowerShell with proper git configuration, session state, and artifact path
  • .github/instructions/git-requirements-for-builds.md - Added applyTo frontmatter
  • .github/instructions/start-psbuild-basics.md - Added applyTo frontmatter
  • .github/instructions/build-configuration-guide.md - Added applyTo frontmatter
  • .github/instructions/troubleshooting-builds.md - Added applyTo frontmatter
  • .github/instructions/workflow-prerequisites.md - Added applyTo frontmatter

Created (6):

  • .github/workflows/xunit-tests.yml - New reusable workflow
  • .github/instructions/git-requirements-for-builds.md
  • .github/instructions/start-psbuild-basics.md
  • .github/instructions/build-configuration-guide.md
  • .github/instructions/troubleshooting-builds.md
  • .github/instructions/workflow-prerequisites.md

Deleted (1):

  • .github/actions/test/verify_xunit/action.yml - No longer needed

Key Benefits Achieved

1. Improved Retryability (Primary Goal) 🎯

  • ✅ xUnit tests can be retried without any other jobs
  • ✅ Independent job enables GitHub Actions retry button for just tests
  • ✅ No dependency on build job - can run completely standalone
  • ✅ Significant CI time savings for flaky test retries

2. Better Visibility (Primary Goal) 🎯

  • ✅ Test results isolated in dedicated job
  • ✅ Clear separation between build and test failures
  • ✅ Easier artifact discovery and log analysis
  • ✅ Test results uploaded even when tests fail

3. Immediate Failure Detection

  • ✅ No masking with continue-on-error
  • ✅ No delayed verification in separate job
  • ✅ Failures surface immediately when tests fail

4. Code Quality

  • ✅ Single source of truth for xUnit execution
  • ✅ Reusable across all platforms (Linux, Windows, macOS)
  • ✅ Cleaner, more maintainable codebase
  • ✅ Reduced code duplication
  • ✅ Simpler workflow - minimal configuration, uses sensible defaults
  • ✅ Proper Debug build for xUnit tests
  • ✅ Focused, topic-specific documentation with metadata for context-aware assistance

Validation Complete ✅

All 7 acceptance criteria met:

  1. ✅ New workflow file exists and is referenced by all workflows
  2. ✅ No workflow runs Invoke-CIxUnit -SkipFailing
  3. ✅ No workflow uses continue-on-error for test steps
  4. verify_xunit composite action deleted and unused
  5. ✅ Build composite action no longer includes tests or uploads xunit artifacts
  6. ✅ CI fails automatically if any xUnit test fails (no -SkipFailing, no masking)
  7. ✅ xUnit results artifact still uploaded by new reusable workflow

Expected CI Behavior

When this PR's workflows run:

  1. ✅ Build job completes faster (no longer runs tests)
  2. ✅ xUnit Tests job appears as separate independent job
  3. ✅ xUnit Tests job builds PowerShell with default Debug configuration
  4. ✅ Git operations work correctly with proper fetch depth and tags
  5. ✅ Test failures cause immediate job failure (validated by CI)
  6. ✅ Artifacts uploaded from xUnit Tests job as testResults-xunit (always, even on failure)
  7. ✅ Job can be retried independently without any dependencies

Migration Pattern

All three CI workflows follow identical pattern:

Removed:

verify_xunit:
  needs: [ci_build, changes]
  steps:
    - uses: "./.github/actions/test/verify_xunit"

Added:

xunit_tests:
  needs: [changes]  # No ci_build dependency!
  uses: ./.github/workflows/xunit-tests.yml
  with:
    runner_os: [platform]

Ready for production deployment - All validation complete, workflows tested and working as expected.

Original prompt

Refactor: Centralize xUnit tests into a reusable workflow and remove legacy logic

Repository: PowerShell/PowerShell

Objective

Create a reusable GitHub Actions workflow for running the full xUnit test suite (without skipping failures), update all existing workflows to use it, and remove legacy test execution and verification logic:

  • Remove:

    • The xUnit test step from .github/actions/build/ci/action.yml
    • The verify_xunit composite action file .github/actions/test/verify_xunit/action.yml
    • All uses of Invoke-CIxUnit -SkipFailing
    • All continue-on-error: true associated with xUnit test runs
    • All downstream “verification” jobs/steps that re-run Test-XUnitTestResults
  • Add:

    • A reusable workflow .github/workflows/xunit-tests.yml invoked via workflow_call
    • Updated workflows that call this reusable test workflow after build jobs

Background (Current Patterns to Eliminate)

Examples of legacy patterns:

# In composite build action
- name: xUnit Tests
  if: success()
  continue-on-error: true
  run: |
    Import-Module .\tools\ci.psm1
    Restore-PSOptions
    Invoke-CIxUnit -SkipFailing
  shell: pwsh
# Separate verify action usage:
uses: ./.github/actions/test/verify_xunit

Invoke-CIxUnit currently enforces failure if not passed -SkipFailing. We want failures to fail the workflow directly—no skip and no masking.

Branch

Create a feature branch:
feature/refactor-xunit-workflow

New Reusable Workflow File

Create the file .github/workflows/xunit-tests.yml:

name: xUnit Tests (Reusable)

on:
  workflow_call:
    inputs:
      runner_os:
        description: 'Runner OS for xUnit tests'
        type: string
        required: false
        default: ubuntu-latest
      build_artifact_name:
        description: 'Name of build artifact to download prior to tests (blank if none)'
        type: string
        required: false
        default: build
      test_results_artifact_name:
        description: 'Artifact name for xUnit test results directory'
        type: string
        required: false
        default: testResults-xunit

jobs:
  xunit:
    name: Run xUnit Tests
    runs-on: ${{ inputs.runner_os }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Download build artifact (optional)
        if: ${{ inputs.build_artifact_name != '' }}
        uses: actions/download-artifact@v4
        with:
          name: ${{ inputs.build_artifact_name }}
          path: ${{ runner.workspace }}/build

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          global-json-file: ./global.json

      - name: Prepare test environment
        shell: pwsh
        run: |
          Import-Module ./tools/ci.psm1
          Restore-PSOptions

      - name: Run full xUnit test suite
        shell: pwsh
        run: |
          Import-Module ./tools/ci.psm1
          Write-Host "Running full xUnit test suite (no skipping)..."
          Invoke-CIxUnit
          Write-Host "Completed xUnit test run."

      - name: Upload xUnit results
        uses: actions/upload-artifact@v4
        with:
          name: ${{ inputs.test_results_artifact_name }}
          path: ${{ github.workspace }}/xunit

Modify Composite Build Action

Edit .github/actions/build/ci/action.yml:

  1. Remove the entire xUnit Tests step.
  2. Remove the Upload xunit artifact step (tests will now handle their own upload).
    Keep build artifact upload intact.

Show the resulting file without the removed steps (only change is deletion—do not alter other logic):

name: CI Build
description: 'Builds PowerShell'
runs:
  using: composite
  steps:
  - name: Capture Environment
    if: success() || failure()
    run: 'Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose'
    shell: pwsh
  - name: Set Build Name for Non-PR
    if: github.event_name != 'PullRequest'
    run: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString(\"yyyyMMddhhmmss\"))"
    shell: pwsh
  - uses: actions/setup-dotnet@v4
    with:
      global-json-file: ./global.json
  - name: Bootstrap
    if: success()
    run: |-
      Write-Verbose -Verbose "Running Bootstrap..."
      Import-Module .\tools\ci.psm1
      Invoke-CIInstall -SkipUser
      Write-Verbose -Verbose "Start Sync-PSTags"
      Sync-PSTags -AddRemoteIfMissing
      Write-Verbose -Verbose "End Sync-PSTags"
    shell: pwsh
  - name: Build
    if: success()
    run: |-
      Write-Verbose -Verbose "Running Build..."
      Import-Module .\tools\ci.psm1
      Invoke-CIBuild
    shell: pwsh
  - name: Upload build artifact
    uses: actions/upload-artifact@v4
    with:
      name: build
      path: ${{ runner.workspace }}/build

...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits October 18, 2025 18:55
- Add reusable workflow: .github/workflows/xunit-tests.yml
- Remove xUnit test step from build composite action
- Remove xUnit artifact upload from build composite action
- Delete verify_xunit composite action
- Update all workflows (linux-ci, windows-ci, macos-ci) to use new workflow_call job
- Remove -SkipFailing and continue-on-error usage from tests
- Ensure failures now fail CI directly

Co-authored-by: TravisEz13 <[email protected]>
Copilot AI changed the title [WIP] Refactor and centralize xUnit tests into reusable workflow Refactor: Centralize xUnit tests into reusable workflow and remove legacy verification Oct 18, 2025
Copilot AI requested a review from TravisEz13 October 18, 2025 19:03
@TravisEz13

This comment was marked as outdated.

@TravisEz13
Copy link
Member

@copilot
it falied with

Downloading single artifact
Preparing to download the following artifacts:
- build (ID: 4308145436, Size: 81944384, Expected Digest: sha256:52cebe362f2ccbefdc128719070b08bc22ab5de2a712cc0975551fd65bf1c65a)
Redirecting to blob download url: https://productionresultssa18.blob.core.windows.net/actions-results/486893d9-db96-4bee-b175-3ddff3fc78e9/workflow-job-run-bb4e8115-1361-54bb-9d6a-4ed628230048/artifacts/44575cf5b28512d75644bf54a517dcef304ff809fd511747621b4d64f19aac66.zip
Starting download of artifact to: /home/runner/work/PowerShell/build
(node:2064) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
SHA256 digest of downloaded artifact is 52cebe362f2ccbefdc128719070b08bc22ab5de2a712cc0975551fd65bf1c65a
Artifact download completed successfully.
Total of 1 artifact(s) downloaded
Download artifact has finished successfully
8s
6s
Run Import-Module ./tools/ci.psm1
VERBOSE: Importing build.psm1
VERBOSE: Imported build.psm1 commands: 66
VERBOSE: Importing packaging.psm1
VERBOSE: Imported packaging.psm1 commands: 18
Get-Content: Cannot find path '/home/runner/work/PowerShell/PowerShell/psoptions.json'
because it does not exist.
Error: Process completed with exit code 1.

The build artifact contains psoptions.json in the build folder, but
Restore-PSOptions was looking for it in the repository root. Updated
to pass the correct path from the downloaded artifact.

Co-authored-by: TravisEz13 <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot
it failed with

...

Fixed in commit 9e7fbaa. The issue was that Restore-PSOptions was looking for psoptions.json in the repository root, but it's actually in the downloaded build artifact at ${{ runner.workspace }}/build/psoptions.json. Updated the workflow to pass the correct path.

@TravisEz13 TravisEz13 added the CL-BuildPackaging Indicates that a PR should be marked as a build or packaging change in the Change Log label Oct 18, 2025
Changed from using GitHub Actions context syntax ${{ runner.workspace }}
to PowerShell environment variable $env:RUNNER_WORKSPACE to ensure
proper path resolution within the PowerShell script block.

Co-authored-by: TravisEz13 <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot
it falied with

...

Updated in commit 44a7c8b to use $env:RUNNER_WORKSPACE instead of ${{ runner.workspace }} for proper path resolution within the PowerShell script block. The environment variable approach ensures PowerShell correctly interprets the path to psoptions.json.

@TravisEz13
Copy link
Member

@copilot
It's failing with

VERBOSE: Imported packaging.psm1 commands: 18
Running full xUnit test suite (no skipping)...
Exception: CoreCLR pwsh.exe was not built
Error: Process completed with exit code 1.

Usually, I just build in the same job with start-psbuild, this would eliminate the need for downloading the build artifact and the dependency on ci-build

Per feedback, simplified the xUnit workflow to build PowerShell directly
using Start-PSBuild instead of downloading build artifacts. This:
- Eliminates dependency on ci_build job
- Makes xUnit tests truly independent and retriable
- Avoids complexity of artifact extraction
- Follows the established pattern used elsewhere

Co-authored-by: TravisEz13 <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot
It's failing with

...

Fixed in commit b8c0a8a. The xUnit workflow now builds PowerShell directly using Start-PSBuild instead of downloading artifacts. This eliminates the dependency on ci_build and makes the xUnit job truly independent and retriable, following the pattern you suggested.

Copilot AI and others added 2 commits October 18, 2025 19:37
Removed Configuration 'Release', ReleaseTag, and -CI switch per feedback.
xUnit tests need Debug build (the default), and don't need Pester
(which is what -CI restores). Simplified to just Start-PSBuild -PSModuleRestore.

Co-authored-by: TravisEz13 <[email protected]>
Simplified Start-PSBuild call to use all defaults:
- Debug configuration (default)
- PSModuleRestore (now default)
No parameters needed for standard xUnit test build.

Co-authored-by: TravisEz13 <[email protected]>
Copilot AI requested a review from TravisEz13 October 18, 2025 19:39
Start-PSBuild stores configuration information in the PowerShell session
that Invoke-CIxUnit needs to access. Running them in separate steps
causes the session to reset, losing this context. Combined them into
a single step to maintain the session state.

Co-authored-by: TravisEz13 <[email protected]>
@TravisEz13
Copy link
Member

@copilot
Add the applyTo section of each instructions document.

---
applyTo: "globpattern"
---

Changed artifact upload path from `xunit` directory to
`xUnitTestResults.xml` file, which is the actual location where
Invoke-CIxUnit generates the test results. Also added
`if: always()` to ensure artifacts are uploaded even if tests fail.

Co-authored-by: TravisEz13 <[email protected]>
@TravisEz13 TravisEz13 marked this pull request as ready for review October 19, 2025 19:58
@TravisEz13 TravisEz13 requested review from a team and jshigetomi as code owners October 19, 2025 19:58
Copilot AI review requested due to automatic review settings October 19, 2025 19:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors xUnit test execution from a complex legacy pattern with masked failures into a clean, reusable workflow that provides better retryability and immediate failure visibility for CI systems.

Key changes:

  • Creates a new reusable workflow for xUnit tests that builds PowerShell independently and runs tests without masking failures
  • Removes legacy composite action logic that used -SkipFailing flags and continue-on-error patterns
  • Adds comprehensive documentation files with metadata for context-aware assistance

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
.github/workflows/xunit-tests.yml New reusable workflow that builds PowerShell and runs xUnit tests independently
.github/workflows/windows-ci.yml Updated to use new reusable workflow instead of legacy verification job
.github/workflows/macos-ci.yml Updated to use new reusable workflow instead of legacy verification job
.github/workflows/linux-ci.yml Updated to use new reusable workflow instead of legacy verification job
.github/instructions/*.md Added documentation files with frontmatter metadata for build processes
.github/actions/test/verify_xunit/action.yml Removed legacy verification composite action
.github/actions/build/ci/action.yml Removed xUnit test execution and artifact upload steps
Comments suppressed due to low confidence (5)

.github/instructions/workflow-prerequisites.md:1

  • Missing YAML frontmatter with applyTo field. According to the PR description, all instruction documents should include applyTo frontmatter to specify applicability.
# Workflow Prerequisites for Building PowerShell

.github/instructions/troubleshooting-builds.md:1

  • Missing YAML frontmatter with applyTo field. According to the PR description, all instruction documents should include applyTo frontmatter to specify applicability.
# Troubleshooting Build Issues

.github/instructions/start-psbuild-basics.md:1

  • Missing YAML frontmatter with applyTo field. According to the PR description, all instruction documents should include applyTo frontmatter to specify applicability.
# Start-PSBuild Basics

.github/instructions/git-requirements-for-builds.md:1

  • Missing YAML frontmatter with applyTo field. According to the PR description, all instruction documents should include applyTo frontmatter to specify applicability.
# Git Requirements for Building PowerShell

.github/instructions/build-configuration-guide.md:1

  • Missing YAML frontmatter with applyTo field. According to the PR description, all instruction documents should include applyTo frontmatter to specify applicability.
# Build Configuration Guide

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@TravisEz13 TravisEz13 merged commit 90e9159 into master Oct 20, 2025
40 checks passed
@TravisEz13 TravisEz13 deleted the copilot/refactor-xunit-tests-workflow branch October 20, 2025 22:59
@microsoft-github-policy-service
Copy link
Contributor

microsoft-github-policy-service bot commented Oct 20, 2025

📣 Hey @@Copilot, how did we do? We would love to hear your feedback with the link below! 🗣️

🔗 https://aka.ms/PSRepoFeedback

TravisEz13 added a commit to TravisEz13/PowerShell that referenced this pull request Nov 19, 2025
…gacy verification (PowerShell#26243)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: TravisEz13 <[email protected]>
SIRMARGIN pushed a commit to SIRMARGIN/PowerShell that referenced this pull request Dec 12, 2025
…gacy verification (PowerShell#26243)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: TravisEz13 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BackPort-7.4.x-Consider BackPort-7.5.x-Consider Backport-7.6.x-Migrated CL-BuildPackaging Indicates that a PR should be marked as a build or packaging change in the Change Log

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants