-
Notifications
You must be signed in to change notification settings - Fork 125
Closed
Labels
ciContinuous integrationContinuous integrationenhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers
Milestone
Description
Summary
Create a new CIHelpers.psm1 module to centralize CI platform detection and output formatting, eliminating duplicate code across security and linting scripts.
Problem
Multiple scripts duplicate CI platform detection and output logic:
GitHub Actions output patterns (inline in scripts):
scripts/security/Test-DependencyPinning.ps1lines 784-808:if ($env:GITHUB_OUTPUT) { "dependency-report=$ReportPath" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding UTF8 "compliance-score=$($Report.ComplianceScore)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding UTF8 } if ($env:GITHUB_STEP_SUMMARY) { Copy-Item -Path $summaryPath -Destination $env:GITHUB_STEP_SUMMARY -Force }
scripts/extension/Package-Extension.ps1lines 555-561:if ($env:GITHUB_OUTPUT) { "version=$packageVersion" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 "vsix-file=$($vsixFile.Name)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 }
Azure DevOps output patterns:
scripts/security/Test-DependencyPinning.ps1lines 813-822:if ($env:TF_BUILD -eq 'True' -or $env:AZURE_PIPELINES -eq 'True') { Write-Output "##vso[task.setvariable variable=dependencyReport;isOutput=true]$ReportPath" Write-Output "##vso[task.setvariable variable=complianceScore;isOutput=true]$($Report.ComplianceScore)" Write-Output "##vso[artifact.upload containerfolder=dependency-pinning;artifactname=dependency-pinning-report]$ReportPath" }
scripts/security/Test-SHAStaleness.ps1lines 709-718:$Message = "##vso[task.logissue type=warning;sourcepath=$($Dep.File);][$($Dep.Severity)] $($Dep.Message)" Write-Output "##vso[task.complete result=SucceededWithIssues]"
scripts/security/Update-ActionSHAPinning.ps1lines 424-438:Write-Output "##vso[task.logissue type=warning;sourcepath=$sourcePath]$message" Write-Output "##vso[task.complete result=SucceededWithIssues]"
Existing pattern to follow:
scripts/linting/Modules/LintingHelpers.psm1 already provides GitHub-specific helpers:
Write-GitHubStepSummarySet-GitHubOutputSet-GitHubEnvWrite-GitHubAnnotation
Solution
Create scripts/lib/Modules/CIHelpers.psm1 with unified CI platform support.
Module Structure
# CIHelpers.psm1
#
# Purpose: Unified CI platform detection and output formatting
# Supports: GitHub Actions, Azure DevOps, local development
#region Platform Detection
function Get-CIPlatform {
<#
.SYNOPSIS
Detects the current CI platform.
.OUTPUTS
[string] 'GitHub', 'AzureDevOps', or 'Local'
#>
if ($env:GITHUB_ACTIONS -eq 'true') { return 'GitHub' }
if ($env:TF_BUILD -eq 'True' -or $env:AZURE_PIPELINES -eq 'True') { return 'AzureDevOps' }
return 'Local'
}
function Test-CIEnvironment {
<#
.SYNOPSIS
Returns true if running in any CI environment.
#>
return (Get-CIPlatform) -ne 'Local'
}
#endregion
#region Output Variables
function Set-CIOutput {
<#
.SYNOPSIS
Sets an output variable for the current CI platform.
.PARAMETER Name
Variable name.
.PARAMETER Value
Variable value.
.PARAMETER IsOutput
For Azure DevOps, marks as output variable (default: true).
#>
param(
[Parameter(Mandatory)]
[string]$Name,
[Parameter(Mandatory)]
[string]$Value,
[switch]$IsOutput
)
switch (Get-CIPlatform) {
'GitHub' {
if ($env:GITHUB_OUTPUT) {
"$Name=$Value" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding UTF8
}
}
'AzureDevOps' {
$outputFlag = if ($IsOutput) { ';isOutput=true' } else { '' }
Write-Output "##vso[task.setvariable variable=$Name$outputFlag]$Value"
}
'Local' {
Write-Verbose "CI Output: $Name=$Value"
}
}
}
#endregion
#region Step Summary
function Write-CIStepSummary {
<#
.SYNOPSIS
Writes content to the CI step summary.
.PARAMETER Content
Markdown content to append.
.PARAMETER Path
Path to a file containing summary content.
#>
param(
[Parameter(ParameterSetName='Content')]
[string]$Content,
[Parameter(ParameterSetName='File')]
[string]$Path
)
$summaryContent = if ($Path) { Get-Content -Path $Path -Raw } else { $Content }
switch (Get-CIPlatform) {
'GitHub' {
if ($env:GITHUB_STEP_SUMMARY) {
$summaryContent | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append -Encoding UTF8
}
}
'AzureDevOps' {
Write-Output "##[section]$summaryContent"
}
'Local' {
Write-Host $summaryContent
}
}
}
#endregion
#region Logging and Annotations
function Write-CIAnnotation {
<#
.SYNOPSIS
Writes a CI annotation (warning/error) with optional file location.
#>
param(
[Parameter(Mandatory)]
[ValidateSet('warning', 'error', 'notice')]
[string]$Type,
[Parameter(Mandatory)]
[string]$Message,
[string]$File,
[int]$Line
)
switch (Get-CIPlatform) {
'GitHub' {
$location = if ($File) { "file=$File" + $(if ($Line) { ",line=$Line" }) } else { '' }
Write-Output "::$Type $location::$Message"
}
'AzureDevOps' {
$vsoType = if ($Type -eq 'notice') { 'info' } else { $Type }
$sourcePath = if ($File) { ";sourcepath=$File" } else { '' }
Write-Output "##vso[task.logissue type=$vsoType$sourcePath]$Message"
}
'Local' {
$color = switch ($Type) { 'error' { 'Red' } 'warning' { 'Yellow' } default { 'Cyan' } }
Write-Host "[$Type] $Message" -ForegroundColor $color
}
}
}
function Set-CITaskResult {
<#
.SYNOPSIS
Sets the CI task result status.
#>
param(
[Parameter(Mandatory)]
[ValidateSet('Succeeded', 'SucceededWithIssues', 'Failed')]
[string]$Result
)
switch (Get-CIPlatform) {
'GitHub' {
if ($Result -eq 'Failed') { exit 1 }
}
'AzureDevOps' {
Write-Output "##vso[task.complete result=$Result]"
}
}
}
#endregion
#region Artifact Upload
function Publish-CIArtifact {
<#
.SYNOPSIS
Uploads an artifact to the CI system.
#>
param(
[Parameter(Mandatory)]
[string]$Path,
[Parameter(Mandatory)]
[string]$Name,
[string]$ContainerFolder
)
switch (Get-CIPlatform) {
'GitHub' {
# GitHub Actions artifact upload requires actions/upload-artifact
Write-Verbose "GitHub artifact: $Name at $Path (use actions/upload-artifact)"
}
'AzureDevOps' {
$folder = if ($ContainerFolder) { "containerfolder=$ContainerFolder;" } else { '' }
Write-Output "##vso[artifact.upload ${folder}artifactname=$Name]$Path"
}
'Local' {
Write-Verbose "Artifact: $Name at $Path"
}
}
}
#endregion
Export-ModuleMember -Function @(
'Get-CIPlatform'
'Test-CIEnvironment'
'Set-CIOutput'
'Write-CIStepSummary'
'Write-CIAnnotation'
'Set-CITaskResult'
'Publish-CIArtifact'
)File Location
Create at: scripts/lib/Modules/CIHelpers.psm1
This location:
- Places shared modules in
lib/Modules/following existing convention - Keeps CI helpers separate from linting-specific helpers
- Allows import via
Import-Module (Join-Path $PSScriptRoot "../lib/Modules/CIHelpers.psm1")
Validation
Create corresponding tests at scripts/tests/lib/CIHelpers.Tests.ps1 covering:
- Platform detection with mocked environment variables
- Output formatting for each platform
- Annotation formatting
Acceptance Criteria
-
CIHelpers.psm1created atscripts/lib/Modules/CIHelpers.psm1 - All exported functions have comment-based help
- Functions support GitHub Actions, Azure DevOps, and local execution
- Module passes PSScriptAnalyzer
- Unit tests provide coverage for all exported functions
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
ciContinuous integrationContinuous integrationenhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers