Repository navigation
CI CD Pipeline
Relevant source files
The following files were used as context for generating this wiki page:
This page documents the GitHub Actions CI/CD pipeline that automatically builds, tests, and validates the OfficeScrubC2R module on every push and pull request. The pipeline ensures code quality, PowerShell Gallery compatibility, and module functionality before publication.
For information about the manual build process, see Building from Source. For publication procedures, see Release Process.
The CI/CD pipeline is defined in .github/workflows/ci.yml:1-109 and consists of two parallel jobs: test and validate. Both jobs run on windows-latest runners and execute on pushes to main/master/develop branches, pull requests to these branches, or manual workflow dispatch.
Pipeline Objectives:
- Verify that
OfficeScrubNative.dllcompiles successfully - Confirm the PowerShell module loads without errors
- Validate module manifest and metadata
- Ensure PowerShell Gallery publication requirements are met
- Check for blocked files and security issues
Sources: .github/workflows/ci.yml:1-109
graph TB
Trigger["Workflow Triggers:<br/>- push (main/master/develop)<br/>- pull_request<br/>- workflow_dispatch"]
subgraph TestJob["Test Job (windows-latest)"]
T1["Checkout Code<br/>actions/checkout@v3"]
T2["Setup PowerShell<br/>Display $PSVersionTable"]
T3["Build Native DLL<br/>build.ps1 -Clean"]
T4["Test Module Import<br/>Import-Module OfficeScrubC2R.psd1"]
T5["Test Module Functions<br/>Get-InstalledOfficeProducts"]
T6["Validate Manifest<br/>Test-ModuleManifest"]
end
subgraph ValidateJob["Validate Job (windows-latest)"]
V1["Checkout Code<br/>actions/checkout@v3"]
V2["Build DLL<br/>build.ps1"]
V3["Test Manifest<br/>Test-ModuleManifest"]
V4["Check Required Files<br/>LICENSE, README.md, etc."]
V5["Verify No Blocked Files<br/>Zone.Identifier check"]
end
Trigger --> TestJob
Trigger --> ValidateJob
T1 --> T2
T2 --> T3
T3 --> T4
T4 --> T5
T5 --> T6
V1 --> V2
V2 --> V3
V3 --> V4
V4 --> V5
The pipeline executes two jobs in parallel to maximize efficiency. Both jobs are independent and can fail individually, providing granular feedback on different aspects of module quality.
Sources: .github/workflows/ci.yml:3-8, .github/workflows/ci.yml:10-109
The test job validates core module functionality through five sequential steps. It is defined in .github/workflows/ci.yml:11-63.
| Step | Purpose | Command | Exit Criteria |
|---|---|---|---|
| Checkout code | Clone repository | actions/checkout@v3 |
Repository files available |
| Setup PowerShell | Display version info | $PSVersionTable |
PowerShell version confirmed |
| Build native DLL | Compile C# library | .\build.ps1 -Clean |
OfficeScrubNative.dll exists |
| Test module import | Load module | Import-Module .\OfficeScrubC2R.psd1 -Force -Verbose |
Module loaded successfully |
| Test module functions | Execute commands | Get-Command -Module OfficeScrubC2R |
Functions exported |
| Validate manifest | Check metadata | Test-ModuleManifest -Path .\OfficeScrubC2R.psd1 |
Manifest valid |
The build step .github/workflows/ci.yml:24-30 executes build.ps1 -Clean and verifies DLL creation:
.\build.ps1 -Clean
if (-not (Test-Path ".\OfficeScrubNative.dll")) {
throw "DLL was not created"
}This ensures the native layer compiles successfully before proceeding to module tests.
The import test .github/workflows/ci.yml:32-36 verifies that the PowerShell module loads without errors and that the native DLL integration works:
Import-Module .\OfficeScrubC2R.psd1 -Force -Verbose
Get-Module OfficeScrubC2RThe -Verbose flag provides detailed loading information for troubleshooting.
The function test .github/workflows/ci.yml:38-54 verifies exported commands and attempts detection (which fails gracefully without admin rights):
$commands = Get-Command -Module OfficeScrubC2R
Write-Host "Available commands: $($commands.Name -join ', ')"
# Test detection only (safe to run in CI)
try {
Get-InstalledOfficeProducts -ErrorAction Stop
}
catch {
Write-Host "Expected error (no admin): $_"
}The test intentionally catches the admin rights error, as CI runners don't have elevated privileges for Office manipulation.
The manifest validation step .github/workflows/ci.yml:56-62 extracts and displays key metadata:
$manifest = Test-ModuleManifest -Path .\OfficeScrubC2R.psd1
Write-Host "Module version: $($manifest.Version)"
Write-Host "Module name: $($manifest.Name)"
Write-Host "PowerShell version: $($manifest.PowerShellVersion)"This confirms the manifest is well-formed and readable by PowerShell's module system.
Sources: .github/workflows/ci.yml:11-63
The validate job ensures PowerShell Gallery publication requirements are met. It is defined in .github/workflows/ci.yml:64-109.
sequenceDiagram
participant GH as "GitHub Actions"
participant Checkout as "actions/checkout@v3"
participant Build as "build.ps1"
participant Manifest as "Test-ModuleManifest"
participant Files as "Required Files Check"
participant Zone as "Zone.Identifier Check"
GH->>Checkout: Clone repository
Checkout->>Build: Execute build script
Build->>Build: Compile OfficeScrubNative.dll
Build->>Manifest: Validate manifest structure
Manifest->>Manifest: Parse OfficeScrubC2R.psd1
Manifest->>Files: Check required files
Files->>Files: Verify: psd1, psm1, dll, LICENSE, README.md
Files->>Zone: Scan for blocked files
Zone->>Zone: Get-Item -Stream Zone.Identifier
Zone->>GH: Report success/failure
The required files check .github/workflows/ci.yml:84-99 ensures all necessary files for PowerShell Gallery publication are present:
$requiredFiles = @(
'OfficeScrubC2R.psd1',
'OfficeScrubC2R.psm1',
'OfficeScrubNative.dll',
'LICENSE',
'README.md'
)
foreach ($file in $requiredFiles) {
if (-not (Test-Path $file)) {
throw "Required file missing: $file"
}
}| File | Purpose |
|---|---|
OfficeScrubC2R.psd1 |
Module manifest with metadata |
OfficeScrubC2R.psm1 |
Main PowerShell module |
OfficeScrubNative.dll |
Native performance layer |
LICENSE |
MIT license text |
README.md |
Documentation and usage guide |
The zone identifier check .github/workflows/ci.yml:101-108 scans all files for the alternate data stream Zone.Identifier, which Windows applies to downloaded files. This stream can cause PowerShell Gallery publication failures:
Get-ChildItem -Recurse -File | ForEach-Object {
if ((Get-Item $_.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue)) {
throw "Blocked file found: $($_.FullName)"
}
}Files with Zone.Identifier are considered "blocked" and must be unblocked using Unblock-File before publication.
Sources: .github/workflows/ci.yml:64-109
The standalone validation script .github/scripts/Validate-Module.ps1:1-92 provides a comprehensive validation suite that can be run locally or in CI. It performs eight validation checks and generates a summary report.
graph LR
Start["Validate-Module.ps1"]
subgraph Checks["Validation Checks"]
C1["Manifest<br/>Test-ModuleManifest"]
C2["DLL<br/>Test-Path OfficeScrubNative.dll"]
C3["Import<br/>Import-Module"]
C4["Commands<br/>Get-Command"]
C5["Aliases<br/>Get-Alias"]
C6["Required Files<br/>LICENSE, README.md, etc."]
C7["Help<br/>Get-Help"]
end
Report["Results Report<br/>PASS/FAIL/SKIP"]
Summary["Summary: X/Y Passed"]
Start --> Checks
C1 --> Report
C2 --> Report
C3 --> Report
C4 --> Report
C5 --> Report
C6 --> Report
C7 --> Report
Report --> Summary
Manifest Check .github/scripts/Validate-Module.ps1:6-13:
try {
$manifest = Test-ModuleManifest -Path .\OfficeScrubC2R.psd1 -ErrorAction Stop
$results['Manifest'] = "PASS (v$($manifest.Version))"
}
catch {
$results['Manifest'] = "FAIL: $_"
}DLL Check .github/scripts/Validate-Module.ps1:15-21:
if (Test-Path .\OfficeScrubNative.dll) {
$results['DLL'] = "PASS"
}
else {
$results['DLL'] = "FAIL: Not found"
}Module Import Check .github/scripts/Validate-Module.ps1:23-40:
try {
Import-Module .\OfficeScrubC2R.psd1 -Force -ErrorAction Stop
$results['Import'] = "PASS"
# Test commands
$commands = Get-Command -Module OfficeScrubC2R
$results['Commands'] = "PASS ($($commands.Count) exported)"
# Test aliases
$aliases = Get-Alias | Where-Object { $_.ModuleName -eq 'OfficeScrubC2R' }
$results['Aliases'] = "PASS ($($aliases.Count) aliases)"
}
catch {
$results['Import'] = "FAIL: $_"
$results['Commands'] = "SKIP"
$results['Aliases'] = "SKIP"
}Required Files Check .github/scripts/Validate-Module.ps1:42-55:
$requiredFiles = @('LICENSE', 'README.md', 'CHANGELOG.md', 'CONTRIBUTING.md')
$missingFiles = @()
foreach ($file in $requiredFiles) {
if (-not (Test-Path $file)) {
$missingFiles += $file
}
}
if ($missingFiles.Count -eq 0) {
$results['Required Files'] = "PASS"
}
else {
$results['Required Files'] = "FAIL: Missing $($missingFiles -join ', ')"
}Help Documentation Check .github/scripts/Validate-Module.ps1:57-69:
try {
$help = Get-Help Invoke-OfficeScrubC2R -ErrorAction Stop
if ($help.Synopsis) {
$results['Help'] = "PASS"
}
else {
$results['Help'] = "FAIL: No synopsis"
}
}
catch {
$results['Help'] = "FAIL: $_"
}The script generates a color-coded report .github/scripts/Validate-Module.ps1:71-90:
$maxKeyLength = ($results.Keys | Measure-Object -Property Length -Maximum).Maximum
foreach ($key in $results.Keys | Sort-Object) {
$value = $results[$key]
$color = if ($value -like "PASS*") { 'Green' }
elseif ($value -like "FAIL*") { 'Red' }
else { 'Yellow' }
Write-Host ("{0,-$($maxKeyLength + 2)}: {1}" -f $key, $value) -ForegroundColor $color
}
# Summary
$passed = ($results.Values | Where-Object { $_ -like "PASS*" }).Count
$total = $results.Count
Write-Host "`n=== Summary: $passed/$total checks passed ==="
if ($passed -eq $total) {
Write-Host "`nModule is ready for publication!" -ForegroundColor Green
}Sources: .github/scripts/Validate-Module.ps1:1-92
The CI pipeline enforces PowerShell Gallery publication requirements through multiple validation layers. These requirements ensure the module installs and functions correctly when downloaded via Install-Module.
| Requirement | Validation | Location |
|---|---|---|
| Valid manifest |
Test-ModuleManifest must succeed |
.github/workflows/ci.yml:76-82 |
| Required files present | psd1, psm1, dll, LICENSE, README.md | .github/workflows/ci.yml:84-99 |
| No blocked files | No Zone.Identifier streams |
.github/workflows/ci.yml:101-108 |
| Import succeeds |
Import-Module without errors |
.github/workflows/ci.yml:32-36 |
| Functions exported |
Get-Command -Module returns commands |
.github/workflows/ci.yml:38-54 |
| Help available |
Get-Help returns synopsis |
.github/scripts/Validate-Module.ps1:57-69 |
The Test-ModuleManifest cmdlet validates:
- Syntax: PowerShell data file structure
- Required fields: ModuleVersion, GUID, Author, Description
- File references: RootModule, RequiredAssemblies exist
- Version constraints: PowerShellVersion, DotNetFrameworkVersion
Manifest validation runs in both jobs .github/workflows/ci.yml:56-62, .github/workflows/ci.yml:76-82 to catch issues early.
Windows applies Zone.Identifier alternate data streams to files downloaded from the internet. PowerShell Gallery requires all files to be unblocked. The CI pipeline fails if any file contains this stream .github/workflows/ci.yml:101-108.
Detection command:
Get-Item $_.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinueRemediation (local development):
Get-ChildItem -Recurse | Unblock-FileSources: .github/workflows/ci.yml:64-109, .github/scripts/Validate-Module.ps1:1-92
The CI pipeline integrates with the build system and module structure:
graph TB
Source["Source Code:<br/>OfficeScrubC2R-Native.cs"]
subgraph BuildSystem["Build System"]
BuildScript["build.ps1<br/>Compilation Script"]
CSC["csc.exe<br/>.NET Framework"]
DotNet["dotnet build<br/>.NET Core"]
end
subgraph Artifacts["Build Artifacts"]
DLL["OfficeScrubNative.dll"]
CoreDLL["OfficeScrubNative.Core.dll"]
end
subgraph CIPipeline["CI Pipeline (ci.yml)"]
TestJob["Test Job:<br/>Build + Import + Functions"]
ValidateJob["Validate Job:<br/>Files + Manifest"]
end
subgraph ValidationScripts["Validation"]
ValidateScript["Validate-Module.ps1<br/>Comprehensive Checks"]
VerifyBuild["Verify-NativeBuild.ps1<br/>Build Verification"]
end
subgraph Distribution["Distribution"]
Gallery["PowerShell Gallery<br/>Install-Module"]
GitHub["GitHub Releases<br/>Manual Download"]
end
Source --> BuildScript
BuildScript --> CSC
BuildScript --> DotNet
CSC --> DLL
DotNet --> CoreDLL
BuildScript --> CIPipeline
DLL --> CIPipeline
CoreDLL --> CIPipeline
CIPipeline --> ValidationScripts
ValidationScripts --> Distribution
Both CI jobs invoke build.ps1 to compile the native DLL:
-
Test job: .github/workflows/ci.yml:24-30 runs
.\build.ps1 -Clean -
Validate job: .github/workflows/ci.yml:72-74 runs
.\build.ps1
The -Clean parameter forces a full rebuild by removing existing artifacts.
The validation script can be invoked:
- Locally: Developers run before committing changes
- CI pipeline: Could be integrated as a dedicated step
- Pre-release: Final check before PowerShell Gallery publication
Manual execution:
.\.github\scripts\Validate-Module.ps1Successful CI runs indicate the module is ready for:
-
PowerShell Gallery publication via
Publish-Module - GitHub Releases as downloadable ZIP archives
-
Local installation via
Import-Modulefrom filesystem
The validation job specifically ensures PowerShell Gallery requirements are met, preventing publication failures.
Sources: .github/workflows/ci.yml:1-109, .github/scripts/Validate-Module.ps1:1-92
The pipeline succeeds when:
- Both jobs complete with exit code 0
-
OfficeScrubNative.dllcompiles without errors - Module imports successfully in PowerShell
- All exported functions are available
- Manifest validation passes
- All required files are present
- No files contain
Zone.Identifierstreams
| Failure | Job | Cause | Resolution |
|---|---|---|---|
| DLL not created | test | Compilation error in C# code | Fix syntax errors in OfficeScrubC2R-Native.cs |
| Module import fails | test | Manifest error or DLL load failure | Check manifest references and assembly loading |
| Missing required file | validate | File not committed to repository | Add missing file and commit |
| Blocked file detected | validate | File has Zone.Identifier stream |
Run Unblock-File before committing |
| Manifest invalid | test, validate | Syntax error or missing required field | Validate manifest locally with Test-ModuleManifest
|
GitHub Actions provides:
- Workflow run logs: Detailed output from each step
- Job summaries: Pass/fail status for each job
- Artifact downloads: Build outputs (if configured)
- Email notifications: On workflow failure (if configured)
Navigate to the Actions tab in the GitHub repository to view workflow runs.
Sources: .github/workflows/ci.yml:1-109
Developers can run validation checks locally before pushing changes to catch issues early.
# 1. Build the native DLL
.\build.ps1 -Clean
# 2. Run comprehensive validation
.\.github\scripts\Validate-Module.ps1
# 3. Test module import
Import-Module .\OfficeScrubC2R.psd1 -Force -Verbose
# 4. Check for blocked files
Get-ChildItem -Recurse -File | ForEach-Object {
if ((Get-Item $_.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue)) {
Write-Warning "Blocked file: $($_.FullName)"
}
}
# 5. Validate manifest
Test-ModuleManifest -Path .\OfficeScrubC2R.psd1Before committing changes that affect module functionality:
- Run
.\build.ps1 -Cleansuccessfully - Run
.\.github\scripts\Validate-Module.ps1with all PASS - Import module without errors
- Test changed functionality
- Update
CHANGELOG.mdif applicable - Verify no blocked files
This local validation prevents CI failures and accelerates the review process.
Sources: .github/scripts/Validate-Module.ps1:1-92, .github/workflows/ci.yml:1-109