mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Add merge conflict marker detection to linux-ci workflow and refactor existing actions to use reusable get-changed-files action (#26350)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> Co-authored-by: Travis Plunk <travis.plunk@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Travis Plunk (HE/HIM) <tplunk@ntdev.microsoft.com>
This commit is contained in:
co-authored by
TravisEz13
Travis Plunk
Copilot
Travis Plunk
parent
c12322323d
commit
0c40a84abb
@@ -0,0 +1,84 @@
|
||||
# Merge Conflict Checker
|
||||
|
||||
This composite GitHub Action checks for Git merge conflict markers in files changed in pull requests.
|
||||
|
||||
## Purpose
|
||||
|
||||
Automatically detects leftover merge conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) in pull request files to prevent them from being merged into the codebase.
|
||||
|
||||
## Usage
|
||||
|
||||
### In a Workflow
|
||||
|
||||
```yaml
|
||||
- name: Check for merge conflict markers
|
||||
uses: "./.github/actions/infrastructure/merge-conflict-checker"
|
||||
```
|
||||
|
||||
### Complete Example
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
merge_conflict_check:
|
||||
name: Check for Merge Conflict Markers
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
permissions:
|
||||
pull-requests: read
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Check for merge conflict markers
|
||||
uses: "./.github/actions/infrastructure/merge-conflict-checker"
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **File Detection**: Uses GitHub's API to get the list of files changed in the pull request
|
||||
2. **Marker Scanning**: Reads each changed file and searches for the following markers:
|
||||
- `<<<<<<<` (conflict start marker)
|
||||
- `=======` (conflict separator)
|
||||
- `>>>>>>>` (conflict end marker)
|
||||
3. **Result Reporting**:
|
||||
- If markers are found, the action fails and lists all affected files
|
||||
- If no markers are found, the action succeeds
|
||||
|
||||
## Outputs
|
||||
|
||||
- `files-checked`: Number of files that were checked
|
||||
- `conflicts-found`: Number of files containing merge conflict markers
|
||||
|
||||
## Behavior
|
||||
|
||||
- **Event Support**: Only works with `pull_request` events
|
||||
- **File Handling**:
|
||||
- Checks only files that were added, modified, or renamed
|
||||
- Skips deleted files
|
||||
- Skips binary/unreadable files
|
||||
- Skips directories
|
||||
|
||||
## Example Output
|
||||
|
||||
When conflict markers are detected:
|
||||
|
||||
```
|
||||
❌ Merge conflict markers detected in the following files:
|
||||
- src/example.cs
|
||||
Markers found: <<<<<<<, =======, >>>>>>>
|
||||
- README.md
|
||||
Markers found: <<<<<<<, =======, >>>>>>>
|
||||
|
||||
Please resolve these conflicts before merging.
|
||||
```
|
||||
|
||||
When no markers are found:
|
||||
|
||||
```
|
||||
✅ No merge conflict markers found
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
This action is integrated into the `linux-ci.yml` workflow and runs automatically on all pull requests to ensure code quality before merging.
|
||||
@@ -0,0 +1,36 @@
|
||||
name: 'Check for Merge Conflict Markers'
|
||||
description: 'Checks for Git merge conflict markers in changed files for pull requests'
|
||||
author: 'PowerShell Team'
|
||||
|
||||
outputs:
|
||||
files-checked:
|
||||
description: 'Number of files checked for merge conflict markers'
|
||||
value: ${{ steps.check.outputs.files-checked }}
|
||||
conflicts-found:
|
||||
description: 'Number of files with merge conflict markers'
|
||||
value: ${{ steps.check.outputs.conflicts-found }}
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: "./.github/actions/infrastructure/get-changed-files"
|
||||
|
||||
- name: Check for merge conflict markers
|
||||
id: check
|
||||
shell: pwsh
|
||||
env:
|
||||
CHANGED_FILES_JSON: ${{ steps.changed-files.outputs.files }}
|
||||
run: |
|
||||
# Get changed files from environment variable (secure against injection)
|
||||
$changedFilesJson = $env:CHANGED_FILES_JSON
|
||||
$changedFiles = $changedFilesJson | ConvertFrom-Json
|
||||
|
||||
# Import ci.psm1 and run the check
|
||||
Import-Module "$env:GITHUB_WORKSPACE/tools/ci.psm1" -Force
|
||||
Test-MergeConflictMarker -File $changedFiles -WorkspacePath $env:GITHUB_WORKSPACE
|
||||
|
||||
branding:
|
||||
icon: 'alert-triangle'
|
||||
color: 'red'
|
||||
@@ -31,6 +31,12 @@ runs:
|
||||
Invoke-CIFinish
|
||||
shell: pwsh
|
||||
|
||||
- name: Install Pester
|
||||
run: |-
|
||||
Import-Module ./tools/ci.psm1
|
||||
Install-CIPester
|
||||
shell: pwsh
|
||||
|
||||
- name: Validate Package Names
|
||||
run: |-
|
||||
# Run Pester tests to validate package names
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
applyTo:
|
||||
- "**/*.ps1"
|
||||
- "**/*.psm1"
|
||||
---
|
||||
|
||||
# PowerShell Automatic Variables - Naming Guidelines
|
||||
|
||||
## Purpose
|
||||
|
||||
This instruction provides guidelines for avoiding conflicts with PowerShell's automatic variables when writing PowerShell scripts and modules.
|
||||
|
||||
## What Are Automatic Variables?
|
||||
|
||||
PowerShell has built-in automatic variables that are created and maintained by PowerShell itself. Assigning values to these variables can cause unexpected behavior and side effects.
|
||||
|
||||
## Common Automatic Variables to Avoid
|
||||
|
||||
### Critical Variables (Never Use)
|
||||
|
||||
- **`$matches`** - Contains the results of regular expression matches. Overwriting this can break regex operations.
|
||||
- **`$_`** - Represents the current object in the pipeline. Only use within pipeline blocks.
|
||||
- **`$PSItem`** - Alias for `$_`. Same rules apply.
|
||||
- **`$args`** - Contains an array of undeclared parameters. Don't use as a regular variable.
|
||||
- **`$input`** - Contains an enumerator of all input passed to a function. Don't reassign.
|
||||
- **`$LastExitCode`** - Exit code of the last native command. Don't overwrite unless intentional.
|
||||
- **`$?`** - Success status of the last command. Don't use as a variable name.
|
||||
- **`$$`** - Last token in the last line received by the session. Don't use.
|
||||
- **`$^`** - First token in the last line received by the session. Don't use.
|
||||
|
||||
### Context Variables (Use with Caution)
|
||||
|
||||
- **`$Error`** - Array of error objects. Don't replace, but can modify (e.g., `$Error.Clear()`).
|
||||
- **`$PSBoundParameters`** - Parameters passed to the current function. Read-only.
|
||||
- **`$MyInvocation`** - Information about the current command. Read-only.
|
||||
- **`$PSCmdlet`** - Cmdlet object for advanced functions. Read-only.
|
||||
|
||||
### Other Common Automatic Variables
|
||||
|
||||
- `$true`, `$false`, `$null` - Boolean and null constants
|
||||
- `$HOME`, `$PSHome`, `$PWD` - Path-related variables
|
||||
- `$PID` - Process ID of the current PowerShell session
|
||||
- `$Host` - Host application object
|
||||
- `$PSVersionTable` - PowerShell version information
|
||||
|
||||
For a complete list, see: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_automatic_variables
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ❌ Bad - Using Automatic Variable Names
|
||||
|
||||
```powershell
|
||||
# Bad: $matches is an automatic variable used for regex capture groups
|
||||
$matches = Select-String -Path $file -Pattern $pattern
|
||||
|
||||
# Bad: $args is an automatic variable for undeclared parameters
|
||||
$args = Get-ChildItem
|
||||
|
||||
# Bad: $input is an automatic variable for pipeline input
|
||||
$input = Read-Host "Enter value"
|
||||
```
|
||||
|
||||
### ✅ Good - Using Descriptive Alternative Names
|
||||
|
||||
```powershell
|
||||
# Good: Use descriptive names that avoid conflicts
|
||||
$matchedLines = Select-String -Path $file -Pattern $pattern
|
||||
|
||||
# Good: Use specific names for arguments
|
||||
$arguments = Get-ChildItem
|
||||
|
||||
# Good: Use specific names for user input
|
||||
$userInput = Read-Host "Enter value"
|
||||
```
|
||||
|
||||
## Naming Alternatives
|
||||
|
||||
When you encounter a situation where you might use an automatic variable name, use these alternatives:
|
||||
|
||||
| Avoid | Use Instead |
|
||||
|-------|-------------|
|
||||
| `$matches` | `$matchedLines`, `$matchResults`, `$regexMatches` |
|
||||
| `$args` | `$arguments`, `$parameters`, `$commandArgs` |
|
||||
| `$input` | `$userInput`, `$inputValue`, `$inputData` |
|
||||
| `$_` (outside pipeline) | Use a named parameter or explicit variable |
|
||||
| `$Error` (reassignment) | Don't reassign; use `$Error.Clear()` if needed |
|
||||
|
||||
## How to Check
|
||||
|
||||
### PSScriptAnalyzer Rule
|
||||
|
||||
PSScriptAnalyzer has a built-in rule that detects assignments to automatic variables:
|
||||
|
||||
```powershell
|
||||
# This will trigger PSAvoidAssignmentToAutomaticVariable
|
||||
$matches = Get-Something
|
||||
```
|
||||
|
||||
**Rule ID**: PSAvoidAssignmentToAutomaticVariable
|
||||
|
||||
### Manual Review
|
||||
|
||||
When writing PowerShell code, always:
|
||||
1. Avoid variable names that match PowerShell keywords or automatic variables
|
||||
2. Use descriptive, specific names that clearly indicate the variable's purpose
|
||||
3. Run PSScriptAnalyzer on your code before committing
|
||||
4. Review code for variable naming during PR reviews
|
||||
|
||||
## Examples from the Codebase
|
||||
|
||||
### Example 1: Regex Matching
|
||||
|
||||
```powershell
|
||||
# ❌ Bad - Overwrites automatic $matches variable
|
||||
$matches = [regex]::Matches($content, $pattern)
|
||||
|
||||
# ✅ Good - Uses descriptive name
|
||||
$regexMatches = [regex]::Matches($content, $pattern)
|
||||
```
|
||||
|
||||
### Example 2: Select-String Results
|
||||
|
||||
```powershell
|
||||
# ❌ Bad - Conflicts with automatic $matches
|
||||
$matches = Select-String -Path $file -Pattern $pattern
|
||||
|
||||
# ✅ Good - Clear and specific
|
||||
$matchedLines = Select-String -Path $file -Pattern $pattern
|
||||
```
|
||||
|
||||
### Example 3: Collecting Arguments
|
||||
|
||||
```powershell
|
||||
# ❌ Bad - Conflicts with automatic $args
|
||||
function Process-Items {
|
||||
$args = $MyItems
|
||||
# ... process items
|
||||
}
|
||||
|
||||
# ✅ Good - Descriptive parameter name
|
||||
function Process-Items {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(ValueFromRemainingArguments)]
|
||||
[string[]]$Items
|
||||
)
|
||||
# ... process items
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [PowerShell Automatic Variables Documentation](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_automatic_variables)
|
||||
- [PSScriptAnalyzer Rules](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/README.md)
|
||||
- [PowerShell Best Practices](https://learn.microsoft.com/powershell/scripting/developer/cmdlet/strongly-encouraged-development-guidelines)
|
||||
|
||||
## Summary
|
||||
|
||||
**Key Takeaway**: Always use descriptive, specific variable names that clearly indicate their purpose and avoid conflicts with PowerShell's automatic variables. When in doubt, choose a longer, more descriptive name over a short one that might conflict.
|
||||
@@ -0,0 +1,201 @@
|
||||
---
|
||||
applyTo:
|
||||
- "tools/ci.psm1"
|
||||
- "build.psm1"
|
||||
- "tools/packaging/**/*.psm1"
|
||||
- ".github/**/*.yml"
|
||||
- ".github/**/*.yaml"
|
||||
---
|
||||
|
||||
# Guidelines for PowerShell Code Organization
|
||||
|
||||
## When to Move Code from YAML to PowerShell Modules
|
||||
|
||||
PowerShell code in GitHub Actions YAML files should be kept minimal. Move code to a module when:
|
||||
|
||||
### Size Threshold
|
||||
- **More than ~30 lines** of PowerShell in a YAML file step
|
||||
- **Any use of .NET types** like `[regex]`, `[System.IO.Path]`, etc.
|
||||
- **Complex logic** requiring multiple nested loops or conditionals
|
||||
- **Reusable functionality** that might be needed elsewhere
|
||||
|
||||
### Indicators to Move Code
|
||||
1. Using .NET type accelerators (`[regex]`, `[PSCustomObject]`, etc.)
|
||||
2. Complex string manipulation or parsing
|
||||
3. File system operations beyond basic reads/writes
|
||||
4. Logic that would benefit from unit testing
|
||||
5. Code that's difficult to read/maintain in YAML format
|
||||
|
||||
## Which Module to Use
|
||||
|
||||
### ci.psm1 (`tools/ci.psm1`)
|
||||
**Purpose**: CI/CD-specific operations and workflows
|
||||
|
||||
**Use for**:
|
||||
- Build orchestration (invoking builds, tests, packaging)
|
||||
- CI environment setup and configuration
|
||||
- Test execution and result processing
|
||||
- Artifact handling and publishing
|
||||
- CI-specific validations and checks
|
||||
- Environment variable management for CI
|
||||
|
||||
**Examples**:
|
||||
- `Invoke-CIBuild` - Orchestrates build process
|
||||
- `Invoke-CITest` - Runs Pester tests
|
||||
- `Test-MergeConflictMarker` - Validates files for conflicts
|
||||
- `Set-BuildVariable` - Manages CI variables
|
||||
|
||||
**When NOT to use**:
|
||||
- Core build operations (use build.psm1)
|
||||
- Package creation logic (use packaging.psm1)
|
||||
- Platform-specific build steps
|
||||
|
||||
### build.psm1 (`build.psm1`)
|
||||
**Purpose**: Core build operations and utilities
|
||||
|
||||
**Use for**:
|
||||
- Compiling source code
|
||||
- Resource generation
|
||||
- Build configuration management
|
||||
- Core build utilities (New-PSOptions, Get-PSOutput, etc.)
|
||||
- Bootstrap operations
|
||||
- Cross-platform build helpers
|
||||
|
||||
**Examples**:
|
||||
- `Start-PSBuild` - Main build function
|
||||
- `Start-PSBootstrap` - Bootstrap dependencies
|
||||
- `New-PSOptions` - Create build configuration
|
||||
- `Start-ResGen` - Generate resources
|
||||
|
||||
**When NOT to use**:
|
||||
- CI workflow orchestration (use ci.psm1)
|
||||
- Package creation (use packaging.psm1)
|
||||
- Test execution
|
||||
|
||||
### packaging.psm1 (`tools/packaging/packaging.psm1`)
|
||||
**Purpose**: Package creation and distribution
|
||||
|
||||
**Use for**:
|
||||
- Creating distribution packages (MSI, RPM, DEB, etc.)
|
||||
- Package-specific metadata generation
|
||||
- Package signing operations
|
||||
- Platform-specific packaging logic
|
||||
|
||||
**Examples**:
|
||||
- `Start-PSPackage` - Create packages
|
||||
- `New-MSIPackage` - Create Windows MSI
|
||||
- `New-DotnetSdkContainerFxdPackage` - Create container packages
|
||||
|
||||
**When NOT to use**:
|
||||
- Building binaries (use build.psm1)
|
||||
- Running tests (use ci.psm1)
|
||||
- General utilities
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Keep YAML Minimal
|
||||
```yaml
|
||||
# ❌ Bad - too much logic in YAML
|
||||
- name: Check files
|
||||
shell: pwsh
|
||||
run: |
|
||||
$files = Get-ChildItem -Recurse
|
||||
foreach ($file in $files) {
|
||||
$content = Get-Content $file -Raw
|
||||
if ($content -match $pattern) {
|
||||
# ... complex processing ...
|
||||
}
|
||||
}
|
||||
|
||||
# ✅ Good - call function from module
|
||||
- name: Check files
|
||||
shell: pwsh
|
||||
run: |
|
||||
Import-Module ./tools/ci.psm1
|
||||
Test-SomeCondition -Path ${{ github.workspace }}
|
||||
```
|
||||
|
||||
### Document Functions
|
||||
Always include comment-based help for functions:
|
||||
```powershell
|
||||
function Test-MyFunction
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Brief description
|
||||
.DESCRIPTION
|
||||
Detailed description
|
||||
.PARAMETER ParameterName
|
||||
Parameter description
|
||||
.EXAMPLE
|
||||
Test-MyFunction -ParameterName Value
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string] $ParameterName
|
||||
)
|
||||
# Implementation
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
Use proper error handling in modules:
|
||||
```powershell
|
||||
try {
|
||||
# Operation
|
||||
}
|
||||
catch {
|
||||
Write-Error "Detailed error message: $_"
|
||||
throw
|
||||
}
|
||||
```
|
||||
|
||||
### Verbose Output
|
||||
Use `Write-Verbose` for debugging information:
|
||||
```powershell
|
||||
Write-Verbose "Processing file: $filePath"
|
||||
```
|
||||
|
||||
## Module Dependencies
|
||||
|
||||
- **ci.psm1** imports both `build.psm1` and `packaging.psm1`
|
||||
- **build.psm1** is standalone (minimal dependencies)
|
||||
- **packaging.psm1** imports `build.psm1`
|
||||
|
||||
When adding new functions, consider these import relationships to avoid circular dependencies.
|
||||
|
||||
## Testing Modules
|
||||
|
||||
Functions in modules should be testable:
|
||||
```powershell
|
||||
# Test locally
|
||||
Import-Module ./tools/ci.psm1 -Force
|
||||
Test-MyFunction -Parameter Value
|
||||
|
||||
# Can be unit tested with Pester
|
||||
Describe "Test-MyFunction" {
|
||||
It "Should return expected result" {
|
||||
# Test implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
When moving code from YAML to a module:
|
||||
|
||||
1. ✅ Determine which module is appropriate (ci, build, or packaging)
|
||||
2. ✅ Create function with proper parameter validation
|
||||
3. ✅ Add comment-based help documentation
|
||||
4. ✅ Use `[CmdletBinding()]` for advanced function features
|
||||
5. ✅ Include error handling
|
||||
6. ✅ Add verbose output for debugging
|
||||
7. ✅ Test the function independently
|
||||
8. ✅ Update YAML to call the new function
|
||||
9. ✅ Verify the workflow still works end-to-end
|
||||
|
||||
## References
|
||||
|
||||
- PowerShell Advanced Functions: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_functions_advanced
|
||||
- Comment-Based Help: https://learn.microsoft.com/powershell/scripting/developer/help/writing-help-for-windows-powershell-scripts-and-functions
|
||||
@@ -67,6 +67,20 @@ jobs:
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
merge_conflict_check:
|
||||
name: Check for Merge Conflict Markers
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request' && (startsWith(github.repository_owner, 'azure') || github.repository_owner == 'PowerShell')
|
||||
permissions:
|
||||
pull-requests: read
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Check for merge conflict markers
|
||||
uses: "./.github/actions/infrastructure/merge-conflict-checker"
|
||||
|
||||
ci_build:
|
||||
name: Build PowerShell
|
||||
runs-on: ubuntu-latest
|
||||
@@ -158,6 +172,48 @@ jobs:
|
||||
runner_os: ubuntu-latest
|
||||
test_results_artifact_name: testResults-xunit
|
||||
|
||||
infrastructure_tests:
|
||||
name: Infrastructure Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install Pester
|
||||
shell: pwsh
|
||||
run: |
|
||||
Import-Module ./tools/ci.psm1
|
||||
Install-CIPester
|
||||
|
||||
- name: Run Infrastructure Tests
|
||||
shell: pwsh
|
||||
run: |
|
||||
$testResultsFolder = Join-Path $PWD "testResults"
|
||||
New-Item -ItemType Directory -Path $testResultsFolder -Force | Out-Null
|
||||
|
||||
$config = New-PesterConfiguration
|
||||
$config.Run.Path = './test/infrastructure/'
|
||||
$config.Run.PassThru = $true
|
||||
$config.TestResult.Enabled = $true
|
||||
$config.TestResult.OutputFormat = 'NUnitXml'
|
||||
$config.TestResult.OutputPath = "$testResultsFolder/InfrastructureTests.xml"
|
||||
$config.Output.Verbosity = 'Detailed'
|
||||
|
||||
$result = Invoke-Pester -Configuration $config
|
||||
|
||||
if ($result.FailedCount -gt 0 -or $result.Result -eq 'Failed') {
|
||||
throw "Infrastructure tests failed"
|
||||
}
|
||||
|
||||
- name: Publish Test Results
|
||||
uses: "./.github/actions/test/process-pester-results"
|
||||
if: always()
|
||||
with:
|
||||
name: "InfrastructureTests"
|
||||
testResultsFolder: "${{ github.workspace }}/testResults"
|
||||
|
||||
## Temporarily disable the CodeQL analysis on Linux as it doesn't work for .NET SDK 10-rc.2.
|
||||
# analyze:
|
||||
# name: CodeQL Analysis
|
||||
@@ -180,6 +236,8 @@ jobs:
|
||||
- linux_test_unelevated_ci
|
||||
- linux_test_unelevated_others
|
||||
- linux_packaging
|
||||
- merge_conflict_check
|
||||
- infrastructure_tests
|
||||
# - analyze
|
||||
if: always()
|
||||
uses: PowerShell/compliance/.github/workflows/ready-to-merge.yml@v1.0.0
|
||||
|
||||
@@ -187,6 +187,14 @@ jobs:
|
||||
$macOSRuntime = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') { 'osx-arm64' } else { 'osx-x64' }
|
||||
Start-PSPackage -Type osxpkg -ReleaseTag $releaseTag -MacOSRuntime $macOSRuntime -SkipReleaseChecks
|
||||
shell: pwsh
|
||||
|
||||
- name: Install Pester
|
||||
if: success()
|
||||
run: |-
|
||||
Import-Module ./tools/ci.psm1
|
||||
Install-CIPester
|
||||
shell: pwsh
|
||||
|
||||
- name: Test package contents
|
||||
if: success()
|
||||
run: |-
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
parameters:
|
||||
vmImage: 'windows-latest'
|
||||
jobName: 'Nanoserver_Tests'
|
||||
continueOnError: false
|
||||
|
||||
jobs:
|
||||
|
||||
- job: ${{ parameters.jobName }}
|
||||
variables:
|
||||
scriptName: ${{ parameters.scriptName }}
|
||||
|
||||
pool:
|
||||
vmImage: ${{ parameters.vmImage }}
|
||||
|
||||
displayName: ${{ parameters.jobName }}
|
||||
|
||||
steps:
|
||||
- script: |
|
||||
set
|
||||
displayName: Capture Environment
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: 'Download Build Artifacts'
|
||||
inputs:
|
||||
downloadType: specific
|
||||
itemPattern: |
|
||||
build/**/*
|
||||
downloadPath: '$(System.ArtifactsDirectory)'
|
||||
|
||||
- pwsh: |
|
||||
Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse
|
||||
displayName: 'Capture Artifacts Directory'
|
||||
continueOnError: true
|
||||
|
||||
- pwsh: |
|
||||
Install-module Pester -Scope CurrentUser -Force -MaximumVersion 4.99
|
||||
displayName: 'Install Pester'
|
||||
continueOnError: true
|
||||
|
||||
- pwsh: |
|
||||
Import-Module .\tools\ci.psm1
|
||||
Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json'
|
||||
$options = (Get-PSOptions)
|
||||
$path = split-path -path $options.Output
|
||||
Write-Verbose "Path: '$path'" -Verbose
|
||||
$rootPath = split-Path -path $path
|
||||
Expand-Archive -Path '$(System.ArtifactsDirectory)\build\build.zip' -DestinationPath $rootPath -Force
|
||||
Invoke-Pester -Path ./test/nanoserver -OutputFormat NUnitXml -OutputFile ./test-nanoserver.xml
|
||||
displayName: Test
|
||||
condition: succeeded()
|
||||
|
||||
- task: PublishTestResults@2
|
||||
condition: succeededOrFailed()
|
||||
displayName: Publish Nanoserver Test Results **\test*.xml
|
||||
inputs:
|
||||
testRunner: NUnit
|
||||
testResultsFiles: '**\test*.xml'
|
||||
testRunTitle: nanoserver
|
||||
mergeTestResults: true
|
||||
failTaskOnFailedTests: true
|
||||
@@ -0,0 +1,240 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# NOTE: This test file tests the Test-MergeConflictMarker function which detects Git merge conflict markers.
|
||||
# IMPORTANT: Do NOT use here-strings or literal conflict markers (e.g., "<<<<<<<", "=======", ">>>>>>>")
|
||||
# in this file, as they will trigger conflict marker detection in CI pipelines.
|
||||
# Instead, use string multiplication (e.g., '<' * 7) to dynamically generate these markers at runtime.
|
||||
|
||||
Describe "Test-MergeConflictMarker" {
|
||||
BeforeAll {
|
||||
# Import the module
|
||||
Import-Module "$PSScriptRoot/../../tools/ci.psm1" -Force
|
||||
|
||||
# Create a temporary test workspace
|
||||
$script:testWorkspace = Join-Path $TestDrive "workspace"
|
||||
New-Item -ItemType Directory -Path $script:testWorkspace -Force | Out-Null
|
||||
|
||||
# Create temporary output files
|
||||
$script:testOutputPath = Join-Path $TestDrive "outputs.txt"
|
||||
$script:testSummaryPath = Join-Path $TestDrive "summary.md"
|
||||
}
|
||||
|
||||
AfterEach {
|
||||
# Clean up test files after each test
|
||||
if (Test-Path $script:testWorkspace) {
|
||||
Get-ChildItem $script:testWorkspace -File -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Remove-Item $script:testOutputPath -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item $script:testSummaryPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Context "When no files are provided" {
|
||||
It "Should handle empty file array" {
|
||||
# The function parameter has Mandatory validation which rejects empty arrays by design
|
||||
# This test verifies that behavior
|
||||
$emptyArray = @()
|
||||
{ Test-MergeConflictMarker -File $emptyArray -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath } | Should -Throw -ExpectedMessage "*empty array*"
|
||||
}
|
||||
}
|
||||
|
||||
Context "When files have no conflicts" {
|
||||
It "Should pass for clean files" {
|
||||
$testFile = Join-Path $script:testWorkspace "clean.txt"
|
||||
"This is a clean file" | Out-File $testFile -Encoding utf8
|
||||
|
||||
Test-MergeConflictMarker -File @("clean.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Should -Contain "files-checked=1"
|
||||
$outputs | Should -Contain "conflicts-found=0"
|
||||
|
||||
$summary = Get-Content $script:testSummaryPath -Raw
|
||||
$summary | Should -Match "No Conflicts Found"
|
||||
}
|
||||
}
|
||||
|
||||
Context "When files have conflict markers" {
|
||||
It "Should detect <<<<<<< marker" {
|
||||
$testFile = Join-Path $script:testWorkspace "conflict1.txt"
|
||||
"Some content`n" + ('<' * 7) + " HEAD`nConflicting content" | Out-File $testFile -Encoding utf8
|
||||
|
||||
{ Test-MergeConflictMarker -File @("conflict1.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath } | Should -Throw
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Should -Contain "files-checked=1"
|
||||
$outputs | Should -Contain "conflicts-found=1"
|
||||
}
|
||||
|
||||
It "Should detect ======= marker" {
|
||||
$testFile = Join-Path $script:testWorkspace "conflict2.txt"
|
||||
"Some content`n" + ('=' * 7) + "`nMore content" | Out-File $testFile -Encoding utf8
|
||||
|
||||
{ Test-MergeConflictMarker -File @("conflict2.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath } | Should -Throw
|
||||
}
|
||||
|
||||
It "Should detect >>>>>>> marker" {
|
||||
$testFile = Join-Path $script:testWorkspace "conflict3.txt"
|
||||
"Some content`n" + ('>' * 7) + " branch-name`nMore content" | Out-File $testFile -Encoding utf8
|
||||
|
||||
{ Test-MergeConflictMarker -File @("conflict3.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath } | Should -Throw
|
||||
}
|
||||
|
||||
It "Should detect multiple markers in one file" {
|
||||
$testFile = Join-Path $script:testWorkspace "conflict4.txt"
|
||||
$content = "Some content`n" + ('<' * 7) + " HEAD`nContent A`n" + ('=' * 7) + "`nContent B`n" + ('>' * 7) + " branch`nMore content"
|
||||
$content | Out-File $testFile -Encoding utf8
|
||||
|
||||
{ Test-MergeConflictMarker -File @("conflict4.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath } | Should -Throw
|
||||
|
||||
$summary = Get-Content $script:testSummaryPath -Raw
|
||||
$summary | Should -Match "Conflicts Detected"
|
||||
$summary | Should -Match "conflict4.txt"
|
||||
}
|
||||
|
||||
It "Should detect conflicts in multiple files" {
|
||||
$testFile1 = Join-Path $script:testWorkspace "conflict5.txt"
|
||||
('<' * 7) + " HEAD" | Out-File $testFile1 -Encoding utf8
|
||||
|
||||
$testFile2 = Join-Path $script:testWorkspace "conflict6.txt"
|
||||
('=' * 7) | Out-File $testFile2 -Encoding utf8
|
||||
|
||||
{ Test-MergeConflictMarker -File @("conflict5.txt", "conflict6.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath } | Should -Throw
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Should -Contain "files-checked=2"
|
||||
$outputs | Should -Contain "conflicts-found=2"
|
||||
}
|
||||
}
|
||||
|
||||
Context "When markers are not at line start" {
|
||||
It "Should not detect markers in middle of line" {
|
||||
$testFile = Join-Path $script:testWorkspace "notconflict.txt"
|
||||
"This line has <<<<<<< in the middle" | Out-File $testFile -Encoding utf8
|
||||
|
||||
Test-MergeConflictMarker -File @("notconflict.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Should -Contain "conflicts-found=0"
|
||||
}
|
||||
|
||||
It "Should not detect markers with wrong number of characters" {
|
||||
$testFile = Join-Path $script:testWorkspace "wrongcount.txt"
|
||||
('<' * 6) + " Only 6`n" + ('<' * 8) + " 8 characters" | Out-File $testFile -Encoding utf8
|
||||
|
||||
Test-MergeConflictMarker -File @("wrongcount.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Should -Contain "conflicts-found=0"
|
||||
}
|
||||
}
|
||||
|
||||
Context "When handling special file scenarios" {
|
||||
It "Should skip non-existent files" {
|
||||
Test-MergeConflictMarker -File @("nonexistent.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Should -Contain "files-checked=0"
|
||||
}
|
||||
|
||||
It "Should handle absolute paths" {
|
||||
$testFile = Join-Path $script:testWorkspace "absolute.txt"
|
||||
"Clean content" | Out-File $testFile -Encoding utf8
|
||||
|
||||
Test-MergeConflictMarker -File @($testFile) -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Should -Contain "conflicts-found=0"
|
||||
}
|
||||
|
||||
It "Should handle mixed relative and absolute paths" {
|
||||
$testFile1 = Join-Path $script:testWorkspace "relative.txt"
|
||||
"Clean" | Out-File $testFile1 -Encoding utf8
|
||||
|
||||
$testFile2 = Join-Path $script:testWorkspace "absolute.txt"
|
||||
"Clean" | Out-File $testFile2 -Encoding utf8
|
||||
|
||||
Test-MergeConflictMarker -File @("relative.txt", $testFile2) -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Should -Contain "files-checked=2"
|
||||
$outputs | Should -Contain "conflicts-found=0"
|
||||
}
|
||||
}
|
||||
|
||||
Context "When summary and output generation" {
|
||||
It "Should generate proper GitHub Actions outputs format" {
|
||||
$testFile = Join-Path $script:testWorkspace "test.txt"
|
||||
"Clean file" | Out-File $testFile -Encoding utf8
|
||||
|
||||
Test-MergeConflictMarker -File @("test.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath
|
||||
|
||||
$outputs = Get-Content $script:testOutputPath
|
||||
$outputs | Where-Object {$_ -match "^files-checked=\d+$"} | Should -Not -BeNullOrEmpty
|
||||
$outputs | Where-Object {$_ -match "^conflicts-found=\d+$"} | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It "Should generate markdown summary with conflict details" {
|
||||
$testFile = Join-Path $script:testWorkspace "marked.txt"
|
||||
$content = "Line 1`n" + ('<' * 7) + " HEAD`nLine 3`n" + ('=' * 7) + "`nLine 5"
|
||||
$content | Out-File $testFile -Encoding utf8
|
||||
|
||||
{ Test-MergeConflictMarker -File @("marked.txt") -WorkspacePath $script:testWorkspace -OutputPath $script:testOutputPath -SummaryPath $script:testSummaryPath } | Should -Throw
|
||||
|
||||
$summary = Get-Content $script:testSummaryPath -Raw
|
||||
$summary | Should -Match "# Merge Conflict Marker Check Results"
|
||||
$summary | Should -Match "marked.txt"
|
||||
$summary | Should -Match "\| Line \| Marker \|"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Install-CIPester" {
|
||||
BeforeAll {
|
||||
# Import the module
|
||||
Import-Module "$PSScriptRoot/../../tools/ci.psm1" -Force
|
||||
}
|
||||
|
||||
Context "When checking function exists" {
|
||||
It "Should export Install-CIPester function" {
|
||||
$function = Get-Command Install-CIPester -ErrorAction SilentlyContinue
|
||||
$function | Should -Not -BeNullOrEmpty
|
||||
$function.ModuleName | Should -Be 'ci'
|
||||
}
|
||||
|
||||
It "Should have expected parameters" {
|
||||
$function = Get-Command Install-CIPester
|
||||
$function.Parameters.Keys | Should -Contain 'MinimumVersion'
|
||||
$function.Parameters.Keys | Should -Contain 'MaximumVersion'
|
||||
$function.Parameters.Keys | Should -Contain 'Force'
|
||||
}
|
||||
|
||||
It "Should accept version parameters" {
|
||||
$function = Get-Command Install-CIPester
|
||||
$function.Parameters['MinimumVersion'].ParameterType.Name | Should -Be 'String'
|
||||
$function.Parameters['MaximumVersion'].ParameterType.Name | Should -Be 'String'
|
||||
$function.Parameters['Force'].ParameterType.Name | Should -Be 'SwitchParameter'
|
||||
}
|
||||
}
|
||||
|
||||
Context "When validating real execution" {
|
||||
# These tests only run in CI where we can safely install/test Pester
|
||||
|
||||
It "Should successfully run without errors when Pester exists" {
|
||||
if (!$env:CI) {
|
||||
Set-ItResult -Skipped -Because "Test requires CI environment to safely install Pester"
|
||||
}
|
||||
|
||||
{ Install-CIPester -ErrorAction Stop } | Should -Not -Throw
|
||||
}
|
||||
|
||||
It "Should accept custom version parameters" {
|
||||
if (!$env:CI) {
|
||||
Set-ItResult -Skipped -Because "Test requires CI environment to safely install Pester"
|
||||
}
|
||||
|
||||
{ Install-CIPester -MinimumVersion '4.0.0' -MaximumVersion '5.99.99' -ErrorAction Stop } | Should -Not -Throw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+209
-1
@@ -228,6 +228,45 @@ function Invoke-CIxUnit
|
||||
}
|
||||
}
|
||||
|
||||
# Install Pester module if not already installed with a compatible version
|
||||
function Install-CIPester
|
||||
{
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$MinimumVersion = '5.0.0',
|
||||
[string]$MaximumVersion = '5.99.99',
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
Write-Verbose "Checking for Pester module (required: $MinimumVersion - $MaximumVersion)" -Verbose
|
||||
|
||||
# Check if a compatible version of Pester is already installed
|
||||
$installedPester = Get-Module -Name Pester -ListAvailable |
|
||||
Where-Object { $_.Version -ge $MinimumVersion -and $_.Version -le $MaximumVersion } |
|
||||
Sort-Object -Property Version -Descending |
|
||||
Select-Object -First 1
|
||||
|
||||
if ($installedPester -and -not $Force) {
|
||||
Write-Host "Pester version $($installedPester.Version) is already installed and meets requirements" -ForegroundColor Green
|
||||
return
|
||||
}
|
||||
|
||||
if ($Force) {
|
||||
Write-Host "Installing Pester module (forced)" -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host "Installing Pester module" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
try {
|
||||
Install-Module -Name Pester -Force -SkipPublisherCheck -MaximumVersion $MaximumVersion -ErrorAction Stop
|
||||
Write-Host "Successfully installed Pester module" -ForegroundColor Green
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to install Pester module: $_"
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
||||
# Implement CI 'Test_script'
|
||||
function Invoke-CITest
|
||||
{
|
||||
@@ -621,7 +660,7 @@ function Invoke-CIFinish
|
||||
|
||||
# Install the latest Pester and import it
|
||||
$maximumPesterVersion = '4.99'
|
||||
Install-Module Pester -Force -SkipPublisherCheck -MaximumVersion $maximumPesterVersion
|
||||
Install-CIPester -MinimumVersion '4.0.0' -MaximumVersion $maximumPesterVersion -Force
|
||||
Import-Module Pester -Force -MaximumVersion $maximumPesterVersion
|
||||
|
||||
$testResultPath = Join-Path -Path $env:TEMP -ChildPath "win-package-$channel-$runtime.xml"
|
||||
@@ -977,3 +1016,172 @@ function Invoke-InitializeContainerStage {
|
||||
Write-Host "##vso[build.addbuildtag]$($selectedImage.JobName)"
|
||||
}
|
||||
}
|
||||
|
||||
Function Test-MergeConflictMarker
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Checks files for Git merge conflict markers and outputs results for GitHub Actions.
|
||||
.DESCRIPTION
|
||||
Scans the specified files for Git merge conflict markers (<<<<<<<, =======, >>>>>>>)
|
||||
and generates console output, GitHub Actions outputs, and job summary.
|
||||
Designed for use in GitHub Actions workflows.
|
||||
.PARAMETER File
|
||||
Array of file paths (relative or absolute) to check for merge conflict markers.
|
||||
.PARAMETER WorkspacePath
|
||||
Base workspace path for resolving relative paths. Defaults to current directory.
|
||||
.PARAMETER OutputPath
|
||||
Path to write GitHub Actions outputs. Defaults to $env:GITHUB_OUTPUT.
|
||||
.PARAMETER SummaryPath
|
||||
Path to write GitHub Actions job summary. Defaults to $env:GITHUB_STEP_SUMMARY.
|
||||
.EXAMPLE
|
||||
Test-MergeConflictMarker -File @('file1.txt', 'file2.cs') -WorkspacePath $env:GITHUB_WORKSPACE
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string[]] $File,
|
||||
|
||||
[Parameter()]
|
||||
[string] $WorkspacePath = $PWD,
|
||||
|
||||
[Parameter()]
|
||||
[string] $OutputPath = $env:GITHUB_OUTPUT,
|
||||
|
||||
[Parameter()]
|
||||
[string] $SummaryPath = $env:GITHUB_STEP_SUMMARY
|
||||
)
|
||||
|
||||
Write-Host "Starting merge conflict marker check..." -ForegroundColor Cyan
|
||||
|
||||
Write-Host "Checking $($File.Count) changed files for merge conflict markers" -ForegroundColor Cyan
|
||||
|
||||
# Convert relative paths to absolute paths for processing
|
||||
$absolutePaths = $File | ForEach-Object {
|
||||
if ([System.IO.Path]::IsPathRooted($_)) {
|
||||
$_
|
||||
} else {
|
||||
Join-Path $WorkspacePath $_
|
||||
}
|
||||
}
|
||||
|
||||
$filesWithConflicts = @()
|
||||
$filesChecked = 0
|
||||
|
||||
foreach ($filePath in $absolutePaths) {
|
||||
# Check if file exists (might be deleted)
|
||||
if (-not (Test-Path $filePath)) {
|
||||
Write-Verbose " Skipping deleted file: $filePath"
|
||||
continue
|
||||
}
|
||||
|
||||
# Skip binary files and directories
|
||||
if ((Get-Item $filePath) -is [System.IO.DirectoryInfo]) {
|
||||
continue
|
||||
}
|
||||
|
||||
$filesChecked++
|
||||
|
||||
# Get relative path for display
|
||||
$relativePath = if ($WorkspacePath -and $filePath.StartsWith($WorkspacePath)) {
|
||||
$filePath.Substring($WorkspacePath.Length).TrimStart([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar)
|
||||
} else {
|
||||
$filePath
|
||||
}
|
||||
|
||||
Write-Host " Checking: $relativePath" -ForegroundColor Gray
|
||||
|
||||
# Search for conflict markers using Select-String
|
||||
try {
|
||||
# Git conflict markers are 7 characters followed by a space or end of line
|
||||
# Regex pattern breakdown:
|
||||
# ^ - Matches the start of a line
|
||||
# (<{7}|={7}|>{7}) - Matches exactly 7 consecutive '<', '=', or '>' characters (Git conflict markers)
|
||||
# (\s|$) - Ensures the marker is followed by whitespace or end of line
|
||||
$pattern = '^(<{7}|={7}|>{7})(\s|$)'
|
||||
$matchedLines = Select-String -Path $filePath -Pattern $pattern -AllMatches -ErrorAction Stop
|
||||
|
||||
if ($matchedLines) {
|
||||
# Collect marker details with line numbers (Select-String provides LineNumber automatically)
|
||||
$markerDetails = @()
|
||||
|
||||
foreach ($match in $matchedLines) {
|
||||
$markerDetails += [PSCustomObject]@{
|
||||
Marker = $match.Matches[0].Groups[1].Value
|
||||
Line = $match.LineNumber
|
||||
}
|
||||
}
|
||||
|
||||
$filesWithConflicts += [PSCustomObject]@{
|
||||
File = $relativePath
|
||||
MarkerDetails = $markerDetails
|
||||
}
|
||||
|
||||
Write-Host " ❌ CONFLICT MARKERS FOUND in $relativePath" -ForegroundColor Red
|
||||
foreach ($detail in $markerDetails) {
|
||||
Write-Host " Line $($detail.Line): $($detail.Marker)" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
# Skip files that can't be read (likely binary)
|
||||
Write-Verbose " Skipping unreadable file: $relativePath"
|
||||
}
|
||||
}
|
||||
|
||||
# Output results to GitHub Actions
|
||||
if ($OutputPath) {
|
||||
"files-checked=$filesChecked" | Out-File -FilePath $OutputPath -Append -Encoding utf8
|
||||
"conflicts-found=$($filesWithConflicts.Count)" | Out-File -FilePath $OutputPath -Append -Encoding utf8
|
||||
}
|
||||
|
||||
Write-Host "`nSummary:" -ForegroundColor Cyan
|
||||
Write-Host " Files checked: $filesChecked" -ForegroundColor Cyan
|
||||
Write-Host " Files with conflicts: $($filesWithConflicts.Count)" -ForegroundColor Cyan
|
||||
|
||||
# Create GitHub Actions job summary
|
||||
if ($SummaryPath) {
|
||||
$summaryContent = @"
|
||||
# Merge Conflict Marker Check Results
|
||||
|
||||
## Summary
|
||||
- **Files Checked:** $filesChecked
|
||||
- **Files with Conflicts:** $($filesWithConflicts.Count)
|
||||
|
||||
"@
|
||||
|
||||
if ($filesWithConflicts.Count -gt 0) {
|
||||
Write-Host "`n❌ Merge conflict markers detected in the following files:" -ForegroundColor Red
|
||||
|
||||
$summaryContent += "`n## ❌ Conflicts Detected`n`n"
|
||||
$summaryContent += "The following files contain merge conflict markers:`n`n"
|
||||
|
||||
foreach ($fileInfo in $filesWithConflicts) {
|
||||
Write-Host " - $($fileInfo.File)" -ForegroundColor Red
|
||||
|
||||
$summaryContent += "### 📄 ``$($fileInfo.File)```n`n"
|
||||
$summaryContent += "| Line | Marker |`n"
|
||||
$summaryContent += "|------|--------|`n"
|
||||
|
||||
foreach ($detail in $fileInfo.MarkerDetails) {
|
||||
Write-Host " Line $($detail.Line): $($detail.Marker)" -ForegroundColor Red
|
||||
$summaryContent += "| $($detail.Line) | ``$($detail.Marker)`` |`n"
|
||||
}
|
||||
$summaryContent += "`n"
|
||||
}
|
||||
|
||||
$summaryContent += "`n**Action Required:** Please resolve these conflicts before merging.`n"
|
||||
Write-Host "`nPlease resolve these conflicts before merging." -ForegroundColor Red
|
||||
} else {
|
||||
Write-Host "`n✅ No merge conflict markers found" -ForegroundColor Green
|
||||
$summaryContent += "`n## ✅ No Conflicts Found`n`nAll checked files are free of merge conflict markers.`n"
|
||||
}
|
||||
|
||||
$summaryContent | Out-File -FilePath $SummaryPath -Encoding utf8
|
||||
}
|
||||
|
||||
# Exit with error if conflicts found
|
||||
if ($filesWithConflicts.Count -gt 0) {
|
||||
throw "Merge conflict markers detected in $($filesWithConflicts.Count) file(s)"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user