diff --git a/.build/README.md b/.build/README.md index 536da28a9d..29bd640692 100644 --- a/.build/README.md +++ b/.build/README.md @@ -1,16 +1,20 @@ -# DSC Resource Integration Test Optimization +# .build scripts -This document describes the script used to dynamically determine whether DSC -resource integration tests should run in Azure Pipelines. +Documentation for the SqlServerDsc module build and pipeline scripts. -## What the Script Does +## `Test-ShouldRunDscResourceIntegrationTests.ps1` -The `Test-ShouldRunDscResourceIntegrationTests.ps1` script analyzes git +This script dynamically determines whether DSC resource integration tests +should run in Azure Pipelines. + +### What the Script Does + +The [`Test-ShouldRunDscResourceIntegrationTests.ps1`](./Test-ShouldRunDscResourceIntegrationTests.ps1) script analyzes git changes between two references and determines if DSC resource integration tests need to run. It automatically discovers which public commands are used by DSC resources and classes, then checks if any relevant files have been modified. -## How It Works +### How It Works The script checks for changes to: @@ -23,26 +27,53 @@ The script checks for changes to: 1. **Integration Tests**: DSC resource integration test files under `tests/Integration/Resources/` -## Usage +### Parameters + +| Parameter | Type | Default | Purpose | +|-----------|------|---------|---------| +| `BaseBranch` | String | `'origin/main'` | Base branch to compare against | +| `CurrentBranch` | String | `'HEAD'` | Current branch or commit to compare | +| `UseMergeBase` | Switch | `$false` | Use merge-base to compute diff base | + +### Outputs + + +| Output | Type | Description | +|--------|------|-------------| +| Return value | Boolean | `$true` when the monitored categories have relevant changes between the specified refs, `$false` when no such changes are detected | + + +### Usage -### Azure Pipelines +#### Azure Pipelines The Azure Pipelines task sets an output variable that downstream stages can use to conditionally run DSC resource integration tests. The script returns a boolean value that the pipeline captures, e.g.: + ```yaml - powershell: | - $shouldRun = ./.build/Test-ShouldRunDscResourceIntegrationTests.ps1 -BaseBranch $targetBranch -CurrentBranch HEAD + $shouldRun = & ./.build/Test-ShouldRunDscResourceIntegrationTests.ps1 -BaseBranch $targetBranch -CurrentBranch HEAD -UseMergeBase Write-Host "##vso[task.setvariable variable=ShouldRunDscResourceIntegrationTests;isOutput=true]$shouldRun" displayName: 'Determine if DSC resource tests should run' + name: determineShouldRun ``` + -Downstream stages reference this output variable using the pattern: -`dependencies.JobName.outputs['StepName.VariableName']` to gate their -execution based on whether DSC resource tests should run. +Downstream stages reference this output variable in a stage `condition:` +using the pattern: + +```yaml +condition: | +and( + succeeded(), + eq(lower(dependencies.stageName.outputs['jobName.taskName.ShouldRunDscResourceIntegrationTests']), 'true') +) +``` + -### Command Line +#### Command Line ```powershell # Basic usage (compares current HEAD with origin/main) @@ -52,9 +83,3 @@ execution based on whether DSC resource tests should run. .build/Test-ShouldRunDscResourceIntegrationTests.ps1 -BaseBranch 'origin/dev' \ -CurrentBranch 'feature-branch' ``` - -## Dynamic Discovery - -The script automatically discovers public commands used by DSC resources by -scanning source files, eliminating the need to maintain hardcoded lists. -This ensures accuracy and reduces maintenance overhead. diff --git a/.build/Test-ShouldRunDscResourceIntegrationTests.ps1 b/.build/Test-ShouldRunDscResourceIntegrationTests.ps1 index 64e1e4ecb4..75c8d11664 100644 --- a/.build/Test-ShouldRunDscResourceIntegrationTests.ps1 +++ b/.build/Test-ShouldRunDscResourceIntegrationTests.ps1 @@ -17,12 +17,20 @@ .PARAMETER CurrentBranch The current branch or commit to compare. Default is 'HEAD'. + .PARAMETER UseMergeBase + When specified, compares the current branch against the merge-base with the base branch + instead of directly comparing against the base branch. This is useful for comparing + only the changes introduced by the current branch. + .EXAMPLE Test-ShouldRunDscResourceIntegrationTests .EXAMPLE Test-ShouldRunDscResourceIntegrationTests -BaseBranch 'origin/main' -CurrentBranch 'HEAD' + .EXAMPLE + Test-ShouldRunDscResourceIntegrationTests -BaseBranch 'origin/main' -CurrentBranch 'HEAD' -UseMergeBase + .OUTPUTS System.Boolean. Returns $true if DSC resource integration tests should run, $false otherwise. #> @@ -35,7 +43,11 @@ param [Parameter()] [System.String] - $CurrentBranch = 'HEAD' + $CurrentBranch = 'HEAD', + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseMergeBase ) <# @@ -137,7 +149,8 @@ function Get-PublicCommandsUsedByDscResources .DESCRIPTION This function retrieves the list of files that have been modified between two git references using git diff. It handles various scenarios including - different diff syntax and untracked files. + different diff syntax and untracked files. Optionally can compare against + the merge-base of the two references. .PARAMETER From The source git reference (branch, commit, tag). @@ -145,9 +158,16 @@ function Get-PublicCommandsUsedByDscResources .PARAMETER To The target git reference (branch, commit, tag). + .PARAMETER UseMergeBase + When specified, finds the merge-base between From and To references and + compares To against that merge-base instead of directly against From. + .EXAMPLE Get-ChangedFiles -From 'origin/main' -To 'HEAD' + .EXAMPLE + Get-ChangedFiles -From 'origin/main' -To 'HEAD' -UseMergeBase + .OUTPUTS System.String[]. Array of file paths that have been changed. #> @@ -162,23 +182,46 @@ function Get-ChangedFiles [Parameter(Mandatory = $true)] [System.String] - $To + $To, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseMergeBase ) try { + $compareFrom = $From + + # If UseMergeBase is specified, find the merge-base between From and To + if ($UseMergeBase) + { + Write-Verbose "Finding merge-base between $From and $To" + $mergeBase = & git merge-base $From $To 2>&1 + if ($LASTEXITCODE -eq 0 -and $mergeBase) + { + $compareFrom = $mergeBase.Trim() + Write-Verbose "Using merge-base: $compareFrom" + } + else + { + Write-Warning "Failed to find merge-base between $From and $To. Falling back to direct comparison. Exit code: $LASTEXITCODE. Output: $mergeBase" + $compareFrom = $From + } + } + # Try different git diff approaches $gitDiffOutput = $null # First, try the standard diff - $gitDiffOutput = & git diff --name-only "$From..$To" 2>&1 + $gitDiffOutput = & git diff --name-only "$compareFrom..$To" 2>&1 if ($LASTEXITCODE -eq 0 -and $gitDiffOutput) { return $gitDiffOutput | Where-Object -FilterScript { $_ -and $_.Trim() } } # If that fails, try without the range syntax - $gitDiffOutput = & git diff --name-only $From $To 2>&1 + $gitDiffOutput = & git diff --name-only $compareFrom $To 2>&1 if ($LASTEXITCODE -eq 0 -and $gitDiffOutput) { return $gitDiffOutput | Where-Object -FilterScript { $_ -and $_.Trim() } @@ -194,7 +237,7 @@ function Get-ChangedFiles } } - Write-Warning "Failed to get git diff between $From and $To. Exit code: $LASTEXITCODE. Output: $gitDiffOutput" + Write-Warning "Failed to get git diff between $compareFrom and $To. Exit code: $LASTEXITCODE. Output: $gitDiffOutput" return @() } catch @@ -347,6 +390,11 @@ function Get-PrivateFunctionsUsedByClassResources .PARAMETER CurrentBranch The current branch or commit to compare. Default is 'HEAD'. + .PARAMETER UseMergeBase + When specified, compares the current branch against the merge-base with the base branch + instead of directly comparing against the base branch. This is useful for comparing + only the changes introduced by the current branch. + .PARAMETER SourcePath The source path containing the source code directories. Default is 'source'. @@ -356,6 +404,9 @@ function Get-PrivateFunctionsUsedByClassResources .EXAMPLE Test-ShouldRunDscResourceIntegrationTests -BaseBranch 'origin/main' -CurrentBranch 'feature-branch' + .EXAMPLE + Test-ShouldRunDscResourceIntegrationTests -BaseBranch 'origin/main' -CurrentBranch 'feature-branch' -UseMergeBase + .OUTPUTS System.Boolean. Returns $true if DSC resource integration tests should run, $false otherwise. #> @@ -372,13 +423,24 @@ function Test-ShouldRunDscResourceIntegrationTests [System.String] $CurrentBranch = 'HEAD', + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseMergeBase, + [Parameter()] [System.String] $SourcePath = 'source' ) Write-Host "##[section]Analyzing DSC Resource Integration Test Requirements" - Write-Host "Analyzing changes between $BaseBranch and $CurrentBranch..." + if ($UseMergeBase) + { + Write-Host "Analyzing changes introduced by $CurrentBranch since merge-base with $BaseBranch..." + } + else + { + Write-Host "Analyzing changes between $BaseBranch and $CurrentBranch..." + } Write-Host "" # Get list of public commands used by DSC resources dynamically @@ -386,7 +448,7 @@ function Test-ShouldRunDscResourceIntegrationTests Write-Host "Discovered $($PublicCommandsUsedByDscResources.Count) public commands used by DSC resources and classes." Write-Host "" - $changedFiles = Get-ChangedFiles -From $BaseBranch -To $CurrentBranch + $changedFiles = Get-ChangedFiles -From $BaseBranch -To $CurrentBranch -UseMergeBase:$UseMergeBase if (-not $changedFiles) { @@ -477,7 +539,7 @@ function Test-ShouldRunDscResourceIntegrationTests # If script is run directly (not imported), execute the main function if ($MyInvocation.InvocationName -ne '.') { - $shouldRun = Test-ShouldRunDscResourceIntegrationTests -BaseBranch $BaseBranch -CurrentBranch $CurrentBranch + $shouldRun = Test-ShouldRunDscResourceIntegrationTests -BaseBranch $BaseBranch -CurrentBranch $CurrentBranch -UseMergeBase:$UseMergeBase # Provide clear final result with appropriate color coding Write-Host "##[section]Test Requirements Decision" diff --git a/.github/instructions/dsc-community-style-guidelines-markdown.instructions.md b/.github/instructions/dsc-community-style-guidelines-markdown.instructions.md index d473d9fded..d6564beccb 100644 --- a/.github/instructions/dsc-community-style-guidelines-markdown.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines-markdown.instructions.md @@ -5,7 +5,8 @@ applyTo: "**/*.md" # Markdown Style Guidelines -- Wrap lines at word boundaries when over 80 characters +- Wrap lines at word boundaries when over 80 characters (except tables/code blocks) - Use 2 spaces for indentation - Use '1.' for all items in ordered lists (1/1/1 numbering style) - Surround fenced code blocks with blank lines +- Disable `MD013` rule by adding a comment for tables/code blocks exceeding 80 characters diff --git a/.vscode/settings.json b/.vscode/settings.json index 6c1387be60..1e6fedf669 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -86,7 +86,10 @@ "sqlps", "securestring", "encryptorname", - "wsfc" + "wsfc", + "SOURCEBRANCH", + "SOURCEBRANCHNAME", + "setvariable" ], "cSpell.ignorePaths": [ ".git" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ac71a55cd..c61dafd5c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,7 +75,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 resources, public commands used by resources, or related components. - Unit tests, QA tests, and command integration tests continue to run for all changes. - - Provides time savings for non-DSC changes while maintaining coverage. ## [17.1.0] - 2025-05-22 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 24bdf700ee..ec32615048 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -91,28 +91,30 @@ stages: inputs: targetType: 'inline' script: | - # Set the target branch for comparison - if ($env:SYSTEM_PULLREQUEST_TARGETBRANCH) { + # Determine if we should run DSC resource integration tests based on branch logic + $shouldRun = $false + + if ($env:SYSTEM_PULLREQUEST_TARGETBRANCH) + { $targetBranch = "origin/$env:SYSTEM_PULLREQUEST_TARGETBRANCH" - } else { - $targetBranch = "origin/$(defaultBranch)" + Write-Host "Pull request detected, targeting branch $targetBranch. Using script analysis to determine test requirements." + $shouldRun = & ./.build/Test-ShouldRunDscResourceIntegrationTests.ps1 -BaseBranch $targetBranch -CurrentBranch 'HEAD' + } + elseif ($env:BUILD.SOURCEBRANCHNAME -eq '$(defaultBranch)') + { + Write-Host "Target is default branch ($(defaultBranch)). Always running DSC resource integration tests." + $shouldRun = $true + } + else + { + Write-Host "Target is non-default branch ($env:BUILD.SOURCEBRANCH). Using script analysis to determine test requirements against default branch." + $shouldRun = & ./.build/Test-ShouldRunDscResourceIntegrationTests.ps1 -BaseBranch "origin/$(defaultBranch)" -CurrentBranch 'HEAD' -UseMergeBase } - - Write-Output "Target branch: $targetBranch" - Write-Output "Current branch: HEAD" - - # Run the script to determine if DSC resource integration tests should run - $shouldRun = ./.build/Test-ShouldRunDscResourceIntegrationTests.ps1 -BaseBranch $targetBranch -CurrentBranch HEAD - - # Debug: print computed value before exporting as output - Write-Host "Computed ShouldRunDscResourceIntegrationTests: $shouldRun" - - # Normalize to lowercase string so conditions can use a simple string comparison - $shouldRunNormalized = ([string] $shouldRun).ToLower() - Write-Host "Normalized ShouldRunDscResourceIntegrationTests: $shouldRunNormalized" # Set Azure DevOps output variable for pipeline conditions - Write-Host "##vso[task.setvariable variable=ShouldRunDscResourceIntegrationTests;isOutput=true]$shouldRunNormalized" + Write-Host "##vso[task.setvariable variable=ShouldRunDscResourceIntegrationTests;isOutput=true]$shouldRun" + + Write-Host "Variable ShouldRunDscResourceIntegrationTests is set to: $shouldRun" pwsh: true - job: Test_HQRM @@ -447,7 +449,7 @@ stages: condition: | and( succeeded(), - eq(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests'], 'true') + eq(lower(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests']), 'true') ) jobs: - job: Test_Integration @@ -545,7 +547,7 @@ stages: condition: | and( succeeded(), - eq(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests'], 'true') + eq(lower(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests']), 'true') ) jobs: - job: Test_Integration @@ -640,7 +642,7 @@ stages: condition: | and( succeeded(), - eq(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests'], 'true') + eq(lower(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests']), 'true') ) jobs: - job: Test_Integration @@ -716,7 +718,7 @@ stages: condition: | and( succeeded(), - eq(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests'], 'true') + eq(lower(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests']), 'true') ) jobs: - job: Test_Integration @@ -783,7 +785,7 @@ stages: condition: | and( succeeded(), - eq(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests'], 'true') + eq(lower(dependencies.Quality_Test_and_Unit_Test.outputs['Determine_DSC_Resource_Test_Requirements.determineDscResourceTests.ShouldRunDscResourceIntegrationTests']), 'true') ) jobs: - job: Test_Integration