mirror of
https://github.com/sbroenne/mcp-server-excel.git
synced 2026-09-19 07:53:08 +08:00
352b1da895
Reverts the scratch verification commit; confirms PAT push authentication and ruleset bypass work end-to-end for the release-automation fix.
251 lines
8.9 KiB
PowerShell
251 lines
8.9 KiB
PowerShell
#!/usr/bin/env pwsh
|
|
<#
|
|
.SYNOPSIS
|
|
Check that CLI command switch statements handle all action strings from ActionExtensions.cs
|
|
|
|
.DESCRIPTION
|
|
This script verifies that each CLI Command's "action switch" statement handles all the
|
|
action strings defined in ActionExtensions.cs for the corresponding enum. This prevents
|
|
bugs like ChartCommand missing "create-from-table" case (found 2026-02-01).
|
|
|
|
.EXAMPLE
|
|
.\scripts\check-cli-action-coverage.ps1
|
|
.\scripts\check-cli-action-coverage.ps1 -Verbose
|
|
|
|
.NOTES
|
|
Created to catch missing CLI switch cases that check-cli-coverage.ps1 misses.
|
|
That script only checks if a CLI command EXISTS for each enum.
|
|
This script checks if the CLI command HANDLES all actions for that enum.
|
|
#>
|
|
|
|
param(
|
|
[switch]$Verbose
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$rootDir = Split-Path -Parent $PSScriptRoot
|
|
|
|
Write-Host "CLI Action Switch Coverage Check" -ForegroundColor Cyan
|
|
Write-Host "====================================" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
$actionExtensionsPath = Join-Path $rootDir "src\ExcelMcp.Core\Models\Actions\ActionExtensions.cs"
|
|
$cliCommandsDir = Join-Path $rootDir "src\ExcelMcp.CLI\Commands"
|
|
|
|
if (-not (Test-Path $actionExtensionsPath)) {
|
|
Write-Host "ActionExtensions.cs not found: $actionExtensionsPath" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
if (-not (Test-Path $cliCommandsDir)) {
|
|
Write-Host "CLI Commands directory not found: $cliCommandsDir" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
# Map enum names to CLI command file names
|
|
# Pattern: ChartAction -> ChartCommand.cs
|
|
$enumToCliCommand = @{
|
|
# Direct mappings (enum name matches command name)
|
|
"ChartAction" = "ChartCommand.cs"
|
|
"ChartConfigAction" = "ChartConfigCommand.cs"
|
|
"ConnectionAction" = "ConnectionCommand.cs"
|
|
"ConditionalFormatAction" = "ConditionalFormatCommand.cs"
|
|
"DataModelAction" = "DataModelCommand.cs"
|
|
"DataModelRelAction" = "DataModelRelCommand.cs"
|
|
"NamedRangeAction" = "NamedRangeCommand.cs"
|
|
"PowerQueryAction" = "PowerQueryCommand.cs"
|
|
"PivotTableAction" = "PivotTableCommand.cs"
|
|
"SlicerAction" = "SlicerCommand.cs"
|
|
"TableAction" = "TableCommand.cs"
|
|
"VbaAction" = "VbaCommand.cs"
|
|
|
|
# Naming exceptions
|
|
"WorksheetAction" = "SheetCommand.cs"
|
|
"WorksheetStyleAction" = "SheetCommand.cs" # Style actions handled via sheet command with --style flag
|
|
|
|
# Range variants all use RangeCommand
|
|
"RangeAction" = "RangeCommand.cs"
|
|
"RangeEditAction" = "RangeCommand.cs"
|
|
"RangeFormatAction" = "RangeCommand.cs"
|
|
"RangeLinkAction" = "RangeCommand.cs"
|
|
|
|
# PivotTable variants
|
|
"PivotTableFieldAction" = "PivotTableCommand.cs"
|
|
"PivotTableCalcAction" = "PivotTableCommand.cs"
|
|
|
|
# Table variants
|
|
"TableColumnAction" = "TableCommand.cs"
|
|
}
|
|
|
|
# Enums that don't have direct CLI switch coverage (handled differently)
|
|
$excludedEnums = @(
|
|
"FileAction" # CLI uses session subcommands, not action switch
|
|
)
|
|
|
|
# Actions that can safely fall through to default (no specific args required)
|
|
# ALL other actions MUST have explicit switch cases
|
|
$actionsAllowingDefault = @{
|
|
"ChartAction" = @("list", "read")
|
|
"ChartConfigAction" = @() # All require specific args
|
|
"PowerQueryAction" = @("list", "refresh-all")
|
|
"WorksheetAction" = @("list")
|
|
"WorksheetStyleAction" = @() # All require specific args
|
|
"TableAction" = @("list")
|
|
"TableColumnAction" = @() # All require specific args
|
|
"VbaAction" = @("list")
|
|
"ConnectionAction" = @("list")
|
|
"DataModelAction" = @("list-tables", "read-info", "refresh")
|
|
"DataModelRelAction" = @("list-relationships")
|
|
"NamedRangeAction" = @("list")
|
|
"SlicerAction" = @("list-slicers", "list-table-slicers")
|
|
"ConditionalFormatAction" = @() # All require specific args
|
|
"PivotTableAction" = @("list")
|
|
"PivotTableFieldAction" = @() # All require specific args
|
|
"PivotTableCalcAction" = @() # All require specific args
|
|
"RangeAction" = @("get-used-range")
|
|
"RangeEditAction" = @() # All require specific args
|
|
"RangeFormatAction" = @() # All require specific args
|
|
"RangeLinkAction" = @() # All require specific args
|
|
}
|
|
|
|
# Parse ActionExtensions.cs to extract action strings for each enum
|
|
function Get-ActionStringsFromExtensions {
|
|
param([string]$Content)
|
|
|
|
$result = @{}
|
|
|
|
# Match each ToActionString method and extract enum name and action mappings
|
|
# Pattern: public static string ToActionString(this EnumName action) => action switch { ... };
|
|
$methodPattern = 'public\s+static\s+string\s+ToActionString\(this\s+(\w+Action)\s+action\)\s*=>\s*action\s+switch\s*\{([^}]+)\}'
|
|
$matches = [regex]::Matches($Content, $methodPattern, [System.Text.RegularExpressions.RegexOptions]::Singleline)
|
|
|
|
foreach ($match in $matches) {
|
|
$enumName = $match.Groups[1].Value
|
|
$switchBody = $match.Groups[2].Value
|
|
|
|
# Extract action strings: EnumName.Value => "action-string"
|
|
$actionPattern = '\w+\.\w+\s*=>\s*"([^"]+)"'
|
|
$actionMatches = [regex]::Matches($switchBody, $actionPattern)
|
|
|
|
$actions = @()
|
|
foreach ($actionMatch in $actionMatches) {
|
|
$actions += $actionMatch.Groups[1].Value
|
|
}
|
|
|
|
$result[$enumName] = $actions
|
|
}
|
|
|
|
return $result
|
|
}
|
|
|
|
# Parse CLI Command file to extract handled action strings from switch
|
|
function Get-HandledActionsFromCommand {
|
|
param([string]$FilePath)
|
|
|
|
if (-not (Test-Path $FilePath)) {
|
|
return @()
|
|
}
|
|
|
|
$content = Get-Content $FilePath -Raw
|
|
|
|
# Find the "action switch" block
|
|
# Pattern: object? args = action switch { ... };
|
|
$switchPattern = 'object\?\s+args\s*=\s*action\s+switch\s*\{([^;]+)\};'
|
|
$match = [regex]::Match($content, $switchPattern, [System.Text.RegularExpressions.RegexOptions]::Singleline)
|
|
|
|
if (-not $match.Success) {
|
|
return @()
|
|
}
|
|
|
|
$switchBody = $match.Groups[1].Value
|
|
|
|
# Extract action strings: "action-string" => ...
|
|
$actionPattern = '"([^"]+)"\s*=>'
|
|
$actionMatches = [regex]::Matches($switchBody, $actionPattern)
|
|
|
|
$actions = @()
|
|
foreach ($actionMatch in $actionMatches) {
|
|
$actions += $actionMatch.Groups[1].Value
|
|
}
|
|
|
|
return $actions
|
|
}
|
|
|
|
# Read ActionExtensions.cs
|
|
$extensionsContent = Get-Content $actionExtensionsPath -Raw
|
|
$enumActionStrings = Get-ActionStringsFromExtensions -Content $extensionsContent
|
|
|
|
Write-Host "Found $($enumActionStrings.Count) action enums in ActionExtensions.cs" -ForegroundColor Gray
|
|
Write-Host ""
|
|
|
|
$totalMissing = 0
|
|
$totalChecked = 0
|
|
|
|
foreach ($enumName in $enumActionStrings.Keys | Sort-Object) {
|
|
# Skip excluded enums
|
|
if ($excludedEnums -contains $enumName) {
|
|
if ($Verbose) {
|
|
Write-Host "$enumName -> (excluded, CLI handles differently)" -ForegroundColor Gray
|
|
}
|
|
continue
|
|
}
|
|
|
|
# Get CLI command file
|
|
if (-not $enumToCliCommand.ContainsKey($enumName)) {
|
|
Write-Host "$enumName -> No CLI mapping defined (add to `$enumToCliCommand)" -ForegroundColor Yellow
|
|
continue
|
|
}
|
|
|
|
$commandFile = $enumToCliCommand[$enumName]
|
|
$commandPath = Join-Path $cliCommandsDir $commandFile
|
|
|
|
if (-not (Test-Path $commandPath)) {
|
|
Write-Host "$enumName -> CLI file not found: $commandFile" -ForegroundColor Yellow
|
|
continue
|
|
}
|
|
|
|
# Get expected and actual actions
|
|
$expectedActions = $enumActionStrings[$enumName]
|
|
$handledActions = Get-HandledActionsFromCommand -FilePath $commandPath
|
|
|
|
# Filter out actions that can safely fall through to default (list, read, etc.)
|
|
if ($actionsAllowingDefault.ContainsKey($enumName)) {
|
|
$allowedDefaults = $actionsAllowingDefault[$enumName]
|
|
$expectedActions = $expectedActions | Where-Object { $_ -notin $allowedDefaults }
|
|
}
|
|
|
|
# Find missing actions
|
|
$missingActions = $expectedActions | Where-Object { $_ -notin $handledActions }
|
|
|
|
$totalChecked++
|
|
|
|
if ($missingActions.Count -gt 0) {
|
|
$totalMissing += $missingActions.Count
|
|
Write-Host "$enumName -> $commandFile" -ForegroundColor Red
|
|
foreach ($missing in $missingActions) {
|
|
Write-Host " Missing: `"$missing`"" -ForegroundColor Yellow
|
|
}
|
|
} else {
|
|
if ($Verbose) {
|
|
Write-Host "$enumName -> $commandFile ($($expectedActions.Count) actions)" -ForegroundColor Green
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
|
|
if ($totalMissing -gt 0) {
|
|
Write-Host "MISSING CLI SWITCH CASES DETECTED!" -ForegroundColor Red
|
|
Write-Host " Total missing: $totalMissing cases across $totalChecked enums checked" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host "Action Required:" -ForegroundColor Yellow
|
|
Write-Host " 1. Add missing case to the CLI command's 'action switch' statement" -ForegroundColor Yellow
|
|
Write-Host " 2. Include all required args for that action (match daemon's *Args class)" -ForegroundColor Yellow
|
|
Write-Host " 3. Example: `"create-from-table`" => new { tableName = settings.TableName, ... }" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "All $totalChecked CLI commands handle their action strings!" -ForegroundColor Green
|
|
exit 0
|