mirror of
https://github.com/sbroenne/mcp-server-excel.git
synced 2026-09-19 07:53:08 +08:00
5eafcbfc8f
Co-authored-by: Stefan Broenner <stbrnner@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
597 lines
25 KiB
PowerShell
597 lines
25 KiB
PowerShell
#!/usr/bin/env pwsh
|
|
<#
|
|
.SYNOPSIS
|
|
Git pre-commit hook to check for COM object leaks, Core Commands coverage, naming consistency, Success flag violations, CLI workflow, MCP Server functionality, and release deliverables
|
|
|
|
.DESCRIPTION
|
|
Runs checks before allowing commits:
|
|
0. Process cleanup - kills stale Excel, excelcli, and MCP server processes to prevent file locks
|
|
1. COM leak checker - ensures no Excel COM objects are leaked
|
|
2. Coverage and naming audit - ensures 100% Core Commands are exposed via MCP Server with aligned action names
|
|
3. MCP-Core implementation audit - ensures every MCP action still has a Core implementation
|
|
4. Success flag validation - ensures Success=true never paired with ErrorMessage (Rule 0)
|
|
5. Release solution build - generates Release binaries and skill outputs used by downstream packaging (skipped for docs-only commits)
|
|
5b. Documentation count validation - ensures all docs report the code-derived tool/operation counts (skipped for docs-only commits)
|
|
6. CLI workflow smoke test - validates end-to-end CLI functionality (skipped for docs/changeset-only commits)
|
|
7. MCP Server smoke test - validates all MCP tools work correctly (skipped for docs/changeset-only commits)
|
|
8. CLI release packaging - validates NuGet + standalone ZIP artifacts (skipped for docs/validation-only commits)
|
|
9. MCP Server release packaging - validates NuGet + standalone ZIP artifacts (skipped for docs/validation-only commits)
|
|
10. VS Code extension packaging - validates the VSIX release packaging path (skipped for docs/validation-only commits)
|
|
11. MCPB bundle packaging - validates the Claude Desktop bundle artifact (skipped for docs/validation-only commits)
|
|
12. Agent skills packaging - validates the ZIP deliverable (skipped for docs/validation-only commits)
|
|
13. Plugin README validation - ensures overlays are complete and not stub content
|
|
14. Dynamic cast audit - ensures ((dynamic)) casts are documented
|
|
15. Npm lockfile portability - rejects fixed download URLs in staged lockfiles
|
|
|
|
Ensures code quality and prevents regression.
|
|
|
|
.EXAMPLE
|
|
.\pre-commit.ps1
|
|
|
|
.NOTES
|
|
This script is called by the Git pre-commit hook.
|
|
To install: Copy .git/hooks/pre-commit (bash) or configure Git to use this PowerShell version.
|
|
#>
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$rootDir = Split-Path -Parent $PSScriptRoot
|
|
$preCommitArtifactsDir = Join-Path $rootDir "artifacts\pre-commit"
|
|
$version = $null
|
|
|
|
function Invoke-ValidationStep {
|
|
param(
|
|
[string]$Heading,
|
|
[scriptblock]$Action,
|
|
[string]$FailureSummary,
|
|
[string]$SuccessSummary
|
|
)
|
|
|
|
Write-Host ""
|
|
Write-Host $Heading -ForegroundColor Cyan
|
|
|
|
# Keep output as it arrives: assignment from a pipeline is lost if it throws.
|
|
$output = [System.Collections.Generic.List[object]]::new()
|
|
try {
|
|
& $Action 2>&1 | ForEach-Object { $output.Add($_) }
|
|
$exitCode = $LASTEXITCODE
|
|
|
|
if ($exitCode -ne 0) {
|
|
Write-Host ""
|
|
Write-Host $FailureSummary -ForegroundColor Red
|
|
if ($output.Count -gt 0) {
|
|
Write-Host ""
|
|
Write-Host ($output | Out-String) -ForegroundColor Gray
|
|
}
|
|
exit 1
|
|
}
|
|
|
|
Write-Host $SuccessSummary -ForegroundColor Green
|
|
}
|
|
catch {
|
|
Write-Host ""
|
|
Write-Host "$FailureSummary $($_.Exception.Message)" -ForegroundColor Red
|
|
if ($output.Count -gt 0) {
|
|
Write-Host ($output | Out-String) -ForegroundColor Gray
|
|
}
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
function Reset-Directory {
|
|
param([string]$Path)
|
|
|
|
if (Test-Path $Path) {
|
|
Remove-Item $Path -Recurse -Force
|
|
}
|
|
|
|
New-Item -ItemType Directory -Path $Path -Force | Out-Null
|
|
}
|
|
|
|
function Stop-DotNetBuildServers {
|
|
dotnet build-server shutdown *> $null
|
|
}
|
|
|
|
# Determine whether this commit touches actual code (as opposed to docs/changeset-only
|
|
# changes). The Release build, smoke tests and release packaging gates all exercise
|
|
# compiled binaries and are slow (minutes) - they add no value for pure documentation
|
|
# changes, including edits to the gh-pages documentation website and its star-history
|
|
# generation workflow. These files do not affect the shipped Excel binaries. Cheap
|
|
# source-level guards still run for every commit.
|
|
$docOnlyPattern = '(\.md$)|(^\.changeset/)|(^docs/)|(^gh-pages/)|(^\.github/(ISSUE_TEMPLATE|PULL_REQUEST_TEMPLATE))|(^\.github/workflows/deploy-gh-pages\.yml$)|(^scripts/(pre-commit|(Update|Restore|Persist|Test)-StarHistory)\.ps1$)'
|
|
$mergeHead = git rev-parse --verify --quiet MERGE_HEAD 2>$null
|
|
$validationBase = if ($LASTEXITCODE -eq 0 -and $mergeHead) { $mergeHead } else { "HEAD" }
|
|
$stagedFiles = git diff --cached --name-only $validationBase 2>&1 | Where-Object { $_ }
|
|
$codeChangedFiles = $stagedFiles | Where-Object { $_ -notmatch $docOnlyPattern }
|
|
$hasCodeChanges = @($codeChangedFiles).Count -gt 0
|
|
|
|
# Validation changes still build and check counts, but do not change release artifacts.
|
|
# Unrecognized paths continue to require packaging.
|
|
$validationOnlyPattern = '(^tests/)|(^scripts/check-doc-counts\.ps1$)|(^\.github/workflows/ci\.yml$)'
|
|
$packagingChangedFiles = $codeChangedFiles | Where-Object { $_ -notmatch $validationOnlyPattern }
|
|
$requiresReleasePackaging = @($packagingChangedFiles).Count -gt 0
|
|
|
|
# Excel-dependent E2E validates the runtime path only. Include the COM and service
|
|
# layers plus source generators because their changes flow into Core, CLI, or MCP.
|
|
$excelE2EPattern = '(^src/ExcelMcp\.(CLI|ComInterop|Core|McpServer|Service)/)|(^src/ExcelMcp\.Generators(\.[^/]+)?/)|(^scripts/Test-E2E\.ps1$)'
|
|
$excelE2EChangedFiles = $stagedFiles | Where-Object { $_ -match $excelE2EPattern }
|
|
$requiresExcelE2E = @($excelE2EChangedFiles).Count -gt 0
|
|
|
|
# CRITICAL: Check branch FIRST - never commit directly to main (Rule 6)
|
|
Write-Host "Checking current branch..." -ForegroundColor Cyan
|
|
$currentBranch = git branch --show-current
|
|
|
|
if ($currentBranch -eq "main") {
|
|
Write-Host ""
|
|
Write-Host "BLOCKED: Cannot commit directly to 'main' branch!" -ForegroundColor Red
|
|
Write-Host ""
|
|
Write-Host " Rule 6: All Changes Via Pull Requests" -ForegroundColor Yellow
|
|
Write-Host " 'Never commit to main. Create feature branch -> PR -> CI/CD + review -> merge.'" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host " To fix:" -ForegroundColor Cyan
|
|
Write-Host " 1. git stash # Save your changes" -ForegroundColor White
|
|
Write-Host " 2. git checkout -b feature/your-feature-name # Create feature branch" -ForegroundColor White
|
|
Write-Host " 3. git stash pop # Restore changes" -ForegroundColor White
|
|
Write-Host " 4. git add <files> # Stage changes" -ForegroundColor White
|
|
Write-Host " 5. git commit -m 'your message' # Commit to feature branch" -ForegroundColor White
|
|
Write-Host ""
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "Branch check passed - on '$currentBranch' (not main)" -ForegroundColor Green
|
|
Write-Host ""
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Checking staged npm lockfile portability..." `
|
|
-FailureSummary "Npm lockfiles contain fixed download URLs or could not be checked." `
|
|
-SuccessSummary "Staged npm lockfile portability check passed" `
|
|
-Action {
|
|
& (Join-Path $PSScriptRoot "check-npm-lockfiles.ps1") -Staged
|
|
}
|
|
|
|
# Stop only processes owned by the selected CLI pipe before touching Release binaries.
|
|
Write-Host "Stopping pipe-owned ExcelMCP processes..." -ForegroundColor Cyan
|
|
$ownedCleanupScript = Join-Path $PSScriptRoot "Stop-ExcelMcpProcesses.ps1"
|
|
& $ownedCleanupScript -Verbose
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host "Pipe-owned cleanup failed with exit code $LASTEXITCODE." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Stop-DotNetBuildServers
|
|
|
|
Write-Host "Process cleanup done" -ForegroundColor Green
|
|
Write-Host ""
|
|
|
|
Reset-Directory -Path $preCommitArtifactsDir
|
|
|
|
try {
|
|
$propsPath = Join-Path $rootDir "Directory.Build.props"
|
|
$propsXml = [xml](Get-Content $propsPath)
|
|
$version = $propsXml.Project.PropertyGroup.Version | Where-Object { $_ } | Select-Object -First 1
|
|
}
|
|
catch {
|
|
Write-Host "Warning: Could not read version from Directory.Build.props ($($_.Exception.Message))" -ForegroundColor Yellow
|
|
$version = "local"
|
|
}
|
|
|
|
Write-Host "Checking for COM object leaks..." -ForegroundColor Cyan
|
|
|
|
try {
|
|
$leakCheckScript = Join-Path $rootDir "scripts\check-com-leaks.ps1"
|
|
& $leakCheckScript
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host ""
|
|
Write-Host "COM object leaks detected! Fix them before committing." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "COM leak check passed" -ForegroundColor Green
|
|
}
|
|
catch {
|
|
Write-Host "Error running COM leak check: $($_.Exception.Message)" -ForegroundColor Yellow
|
|
Write-Host " Continuing with coverage audit..." -ForegroundColor Gray
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Checking Core Commands coverage and naming..." -ForegroundColor Cyan
|
|
|
|
try {
|
|
$auditScript = Join-Path $rootDir "scripts\audit-core-coverage.ps1"
|
|
& $auditScript -CheckNaming -FailOnGaps
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host ""
|
|
Write-Host "Coverage or naming issues detected!" -ForegroundColor Red
|
|
Write-Host " All Core methods must be exposed via MCP Server with matching names." -ForegroundColor Red
|
|
Write-Host " Fix the issues before committing (add/rename enum values and mappings)." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "Coverage and naming checks passed - 100% coverage with consistent names" -ForegroundColor Green
|
|
}
|
|
catch {
|
|
Write-Host ""
|
|
Write-Host "Error running coverage audit: $($_.Exception.Message)" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Checking MCP actions have Core implementations..." -ForegroundColor Cyan
|
|
|
|
try {
|
|
$mcpCoreScript = Join-Path $rootDir "scripts\check-mcp-core-implementations.ps1"
|
|
& $mcpCoreScript
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host ""
|
|
Write-Host "MCP actions without Core implementations detected!" -ForegroundColor Red
|
|
Write-Host " All enum actions must have matching Core Command methods." -ForegroundColor Red
|
|
Write-Host " Fix the issues before committing (remove enum or implement method)." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "MCP-Core implementation check passed" -ForegroundColor Green
|
|
}
|
|
catch {
|
|
Write-Host ""
|
|
Write-Host "Error running MCP-Core implementation check: $($_.Exception.Message)" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Checking Success flag violations (Rule 0)..." -ForegroundColor Cyan
|
|
|
|
try {
|
|
$successFlagScript = Join-Path $rootDir "scripts\check-success-flag.ps1"
|
|
& $successFlagScript
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host ""
|
|
Write-Host "Success flag violations detected!" -ForegroundColor Red
|
|
Write-Host " CRITICAL: Success=true with ErrorMessage confuses LLMs and causes data corruption." -ForegroundColor Red
|
|
Write-Host " Fix the violations before committing (add Success=false in catch blocks)." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "Success flag check passed - all flags match reality" -ForegroundColor Green
|
|
}
|
|
catch {
|
|
Write-Host ""
|
|
Write-Host "Error running success flag check: $($_.Exception.Message)" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Checking shared plugin bootstrap template drift..." `
|
|
-FailureSummary "Plugin bootstrap drift detected. Regenerate the source-copied download.ps1 files with scripts/Build-BootstrapScripts.ps1." `
|
|
-SuccessSummary "Shared bootstrap template check passed - the canonical render matches both committed copies" `
|
|
-Action {
|
|
$bootstrapScript = Join-Path $rootDir "scripts\Build-BootstrapScripts.ps1"
|
|
& $bootstrapScript -Check
|
|
}
|
|
|
|
# NOTE: CLI coverage checks removed - commands are now auto-generated by Roslyn source generators
|
|
# The CLI generator produces all command classes and registration from Core interfaces
|
|
# Validation is handled by:
|
|
# - Build-time generator errors if interfaces are malformed
|
|
# - CLI workflow smoke test below (end-to-end validation)
|
|
|
|
# Validation-only changes need a build and count checks, but not release packaging.
|
|
# Pure documentation changes keep the existing fast path.
|
|
if ($hasCodeChanges) {
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Building Release solution..." `
|
|
-FailureSummary "Release solution build failed!" `
|
|
-SuccessSummary "Release solution build passed - Release binaries and generated skill docs are up to date" `
|
|
-Action {
|
|
Push-Location $rootDir
|
|
try {
|
|
dotnet build Sbroenne.ExcelMcp.sln --configuration Release -p:NuGetAudit=false --verbosity minimal
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Auto-staging generated SKILL.md files..." -ForegroundColor Cyan
|
|
|
|
try {
|
|
# SKILL.md + references are generated during the Release solution build above.
|
|
# Auto-stage all of them so developers never have to think about it.
|
|
$skillPaths = @(
|
|
"skills/excel-mcp/SKILL.md",
|
|
"skills/excel-cli/SKILL.md",
|
|
"skills/excel-mcp/references/",
|
|
"skills/excel-cli/references/"
|
|
)
|
|
$skillDiff = git diff --name-only -- @skillPaths 2>&1
|
|
$untrackedSkills = git ls-files --others --exclude-standard -- @skillPaths 2>&1
|
|
|
|
$allChanges = @()
|
|
if ($skillDiff) { $allChanges += $skillDiff }
|
|
if ($untrackedSkills) { $allChanges += $untrackedSkills }
|
|
|
|
if ($allChanges.Count -gt 0) {
|
|
git add -- @skillPaths
|
|
Write-Host "Skill files were regenerated and auto-staged ($($allChanges.Count) files)" -ForegroundColor Green
|
|
$allChanges | ForEach-Object { Write-Host " + $_" -ForegroundColor DarkGray }
|
|
} else {
|
|
Write-Host "Skill files are already up to date" -ForegroundColor Green
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host "Error auto-staging SKILL.md files: $($_.Exception.Message)" -ForegroundColor Yellow
|
|
Write-Host " Continuing with remaining checks..." -ForegroundColor Gray
|
|
}
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Validating documentation tool/operation counts..." `
|
|
-FailureSummary "Documentation count validation failed! A doc advertises a tool/operation count that does not match the code-derived canonical count." `
|
|
-SuccessSummary "Documentation count validation passed - all docs match the canonical counts" `
|
|
-Action {
|
|
$docCountScript = Join-Path $rootDir "scripts\check-doc-counts.ps1"
|
|
& $docCountScript -SkipBuild
|
|
}
|
|
|
|
if ($requiresExcelE2E) {
|
|
Invoke-ValidationStep `
|
|
-Heading "Running Excel-dependent E2E tests..." `
|
|
-FailureSummary "Excel-dependent E2E tests failed! Both the CLI workflow and MCP all-tools smoke tests must pass." `
|
|
-SuccessSummary "Excel-dependent E2E tests passed" `
|
|
-Action {
|
|
$e2eScript = Join-Path $rootDir "scripts\Test-E2E.ps1"
|
|
& $e2eScript -SkipBuild
|
|
}
|
|
} else {
|
|
Write-Host ""
|
|
Write-Host "Skipping Excel-dependent E2E tests (no staged changes affect Core, CLI, or MCP runtime paths)" -ForegroundColor Yellow
|
|
}
|
|
|
|
}
|
|
else {
|
|
Write-Host ""
|
|
Write-Host "Skipping Release build and smoke tests (docs-only commit - no compiled surface changed)" -ForegroundColor Yellow
|
|
}
|
|
|
|
if ($requiresReleasePackaging) {
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Building CLI release deliverables..." `
|
|
-FailureSummary "CLI release deliverable validation failed!" `
|
|
-SuccessSummary "CLI release deliverables passed - NuGet package and standalone ZIP were built locally" `
|
|
-Action {
|
|
$cliNupkgDir = Join-Path $preCommitArtifactsDir "cli-nupkg"
|
|
$cliPublishDir = Join-Path $preCommitArtifactsDir "cli-publish"
|
|
$cliReleaseDir = Join-Path $preCommitArtifactsDir "cli-release"
|
|
$cliZipPath = Join-Path $preCommitArtifactsDir "ExcelMcp-CLI-$version-windows.zip"
|
|
|
|
Reset-Directory -Path $cliNupkgDir
|
|
Reset-Directory -Path $cliPublishDir
|
|
Reset-Directory -Path $cliReleaseDir
|
|
|
|
Stop-DotNetBuildServers
|
|
|
|
Push-Location $rootDir
|
|
try {
|
|
dotnet pack src\ExcelMcp.CLI\ExcelMcp.CLI.csproj --configuration Release --no-build --no-restore --output $cliNupkgDir -p:Version=$version -p:NuGetAudit=false
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "dotnet pack (CLI) failed with exit code $LASTEXITCODE."
|
|
}
|
|
dotnet publish src\ExcelMcp.CLI\ExcelMcp.CLI.csproj --configuration Release --runtime win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:PublishTrimmed=false -p:PublishReadyToRun=false -p:Version=$version -p:NuGetAudit=false --output $cliPublishDir
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "dotnet publish (CLI) failed with exit code $LASTEXITCODE."
|
|
}
|
|
|
|
Copy-Item (Join-Path $cliPublishDir "excelcli.exe") $cliReleaseDir
|
|
Copy-Item "README.md" $cliReleaseDir
|
|
Copy-Item "LICENSE" $cliReleaseDir
|
|
|
|
if (Test-Path $cliZipPath) {
|
|
Remove-Item $cliZipPath -Force
|
|
}
|
|
|
|
Compress-Archive -Path (Join-Path $cliReleaseDir "*") -DestinationPath $cliZipPath
|
|
|
|
if (-not (Get-ChildItem $cliNupkgDir -Filter "*.nupkg" -ErrorAction Stop)) {
|
|
throw "CLI NuGet package was not created."
|
|
}
|
|
|
|
if (-not (Test-Path $cliZipPath)) {
|
|
throw "CLI ZIP artifact was not created."
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Building MCP Server release deliverables..." `
|
|
-FailureSummary "MCP Server release deliverable validation failed!" `
|
|
-SuccessSummary "MCP Server release deliverables passed - NuGet package and standalone ZIP were built locally" `
|
|
-Action {
|
|
$mcpNupkgDir = Join-Path $preCommitArtifactsDir "mcp-server-nupkg"
|
|
$mcpPublishDir = Join-Path $preCommitArtifactsDir "mcp-server-publish"
|
|
$mcpReleaseDir = Join-Path $preCommitArtifactsDir "mcp-server-release"
|
|
$mcpZipPath = Join-Path $preCommitArtifactsDir "ExcelMcp-MCP-Server-$version-windows.zip"
|
|
|
|
Reset-Directory -Path $mcpNupkgDir
|
|
Reset-Directory -Path $mcpPublishDir
|
|
Reset-Directory -Path $mcpReleaseDir
|
|
|
|
Stop-DotNetBuildServers
|
|
|
|
Push-Location $rootDir
|
|
try {
|
|
dotnet pack src\ExcelMcp.McpServer\ExcelMcp.McpServer.csproj --configuration Release --no-build --no-restore --output $mcpNupkgDir -p:Version=$version -p:NuGetAudit=false
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "dotnet pack (MCP Server) failed with exit code $LASTEXITCODE."
|
|
}
|
|
dotnet publish src\ExcelMcp.McpServer\ExcelMcp.McpServer.csproj --configuration Release --runtime win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:PublishTrimmed=false -p:PublishReadyToRun=false -p:Version=$version -p:NuGetAudit=false --output $mcpPublishDir
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "dotnet publish (MCP Server) failed with exit code $LASTEXITCODE."
|
|
}
|
|
|
|
$publishedExe = Join-Path $mcpPublishDir "Sbroenne.ExcelMcp.McpServer.exe"
|
|
$renamedExe = Join-Path $mcpPublishDir "mcp-excel.exe"
|
|
if (-not (Test-Path $publishedExe)) {
|
|
throw "Published MCP Server executable was not created."
|
|
}
|
|
|
|
if (Test-Path $renamedExe) {
|
|
Remove-Item $renamedExe -Force
|
|
}
|
|
|
|
Rename-Item $publishedExe "mcp-excel.exe"
|
|
|
|
Copy-Item (Join-Path $mcpPublishDir "mcp-excel.exe") $mcpReleaseDir
|
|
Copy-Item "README.md" $mcpReleaseDir
|
|
Copy-Item "LICENSE" $mcpReleaseDir
|
|
|
|
if (Test-Path $mcpZipPath) {
|
|
Remove-Item $mcpZipPath -Force
|
|
}
|
|
|
|
Compress-Archive -Path (Join-Path $mcpReleaseDir "*") -DestinationPath $mcpZipPath
|
|
|
|
if (-not (Get-ChildItem $mcpNupkgDir -Filter "*.nupkg" -ErrorAction Stop)) {
|
|
throw "MCP Server NuGet package was not created."
|
|
}
|
|
|
|
if (-not (Test-Path $mcpZipPath)) {
|
|
throw "MCP Server ZIP artifact was not created."
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Running VS Code extension package validation..." `
|
|
-FailureSummary "VS Code extension package validation failed! Fix the extension build or manifest mismatch before committing." `
|
|
-SuccessSummary "VS Code extension package validation passed" `
|
|
-Action {
|
|
$extensionDir = Join-Path $rootDir "vscode-extension"
|
|
$packageLog = Join-Path $preCommitArtifactsDir "vscode-package.log"
|
|
Push-Location $extensionDir
|
|
try {
|
|
& $env:ComSpec /d /s /c "npm run package > `"$packageLog`" 2>&1"
|
|
$packageExitCode = $LASTEXITCODE
|
|
if ($packageExitCode -ne 0) {
|
|
Get-Content -LiteralPath $packageLog
|
|
throw "npm run package failed with exit code $packageExitCode"
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Building MCPB bundle deliverable..." `
|
|
-FailureSummary "MCPB bundle validation failed!" `
|
|
-SuccessSummary "MCPB bundle validation passed - Claude Desktop bundle was built locally" `
|
|
-Action {
|
|
$mcpbOutputRelative = "..\artifacts\pre-commit\mcpb"
|
|
$mcpbOutputDir = Join-Path $preCommitArtifactsDir "mcpb"
|
|
Reset-Directory -Path $mcpbOutputDir
|
|
|
|
$mcpbDir = Join-Path $rootDir "mcpb"
|
|
Push-Location $mcpbDir
|
|
try {
|
|
.\Build-McpBundle.ps1 -Version $version -OutputDir $mcpbOutputRelative
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Build-McpBundle.ps1 failed with exit code $LASTEXITCODE."
|
|
}
|
|
|
|
if (-not (Get-ChildItem $mcpbOutputDir -Filter "*.mcpb" -ErrorAction Stop)) {
|
|
throw "MCPB artifact was not created."
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
Invoke-ValidationStep `
|
|
-Heading "Building agent skills deliverables..." `
|
|
-FailureSummary "Agent skills deliverable validation failed!" `
|
|
-SuccessSummary "Agent skills deliverables passed - ZIP package was built locally" `
|
|
-Action {
|
|
$skillsOutputDir = Join-Path $preCommitArtifactsDir "skills"
|
|
Reset-Directory -Path $skillsOutputDir
|
|
|
|
Push-Location $rootDir
|
|
try {
|
|
.\scripts\Build-AgentSkills.ps1 -OutputDir "artifacts/pre-commit/skills" -Version $version
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Build-AgentSkills.ps1 failed with exit code $LASTEXITCODE."
|
|
}
|
|
|
|
if (-not (Get-ChildItem $skillsOutputDir -Filter "excel-skills-v*.zip" -ErrorAction Stop)) {
|
|
throw "Agent skills ZIP artifact was not created."
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
Write-Host ""
|
|
Write-Host "Skipping release packaging gates (docs/validation-only commit - no shipped artifacts changed)" -ForegroundColor Yellow
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Validating plugin README overlays..." -ForegroundColor Cyan
|
|
|
|
try {
|
|
$pluginReadmeScript = Join-Path $rootDir "scripts\check-plugin-readmes.ps1"
|
|
& $pluginReadmeScript
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host ""
|
|
Write-Host "Plugin README validation failed!" -ForegroundColor Red
|
|
Write-Host " Thin/stub README overlays would overwrite richer published templates." -ForegroundColor Red
|
|
Write-Host " Enrich the overlay or remove it to use the published template." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "Plugin README validation passed - overlays are complete" -ForegroundColor Green
|
|
}
|
|
catch {
|
|
Write-Host "Error running plugin README check: $($_.Exception.Message)" -ForegroundColor Yellow
|
|
Write-Host " Continuing..." -ForegroundColor Gray
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Checking for undocumented ((dynamic)) casts..." -ForegroundColor Cyan
|
|
|
|
try {
|
|
$dynamicCastScript = Join-Path $rootDir "scripts\check-dynamic-casts.ps1"
|
|
& $dynamicCastScript
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host ""
|
|
Write-Host "Undocumented ((dynamic)) casts detected!" -ForegroundColor Red
|
|
Write-Host " Add a justification comment (// PIA gap:, // TODO:, or // Reason:) before each cast." -ForegroundColor Red
|
|
Write-Host " See docs/PIA-COVERAGE.md for guidance." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "Dynamic cast check passed - all casts are documented" -ForegroundColor Green
|
|
}
|
|
catch {
|
|
Write-Host "Error running dynamic cast check: $($_.Exception.Message)" -ForegroundColor Yellow
|
|
Write-Host " Continuing..." -ForegroundColor Gray
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "All pre-commit checks passed!" -ForegroundColor Green
|
|
exit 0
|