wovensnake 0.3.6

A high-performance Python package manager built with Rust.
Documentation
$ErrorActionPreference = "Stop"

$ProjectRoot = Resolve-Path "$PSScriptRoot/.."
$BinaryPath = "$ProjectRoot/target/debug/woven.exe"
$PlaygroundDir = "$ProjectRoot/playground"
$ReportsDir = "$ProjectRoot/reports"
$ReportFile = "$ReportsDir/playground_report.html"

Write-Host "🐍 WovenSnake Playground Automation (v0.2.0)" -ForegroundColor Cyan
Write-Host "-------------------------------------------" -ForegroundColor DarkGray

if (-not (Test-Path $BinaryPath)) {
    Write-Error "Binary not found at $BinaryPath. Run 'cargo build' first."
}

# Ensure reports dir exists
if (-not (Test-Path $ReportsDir)) {
    New-Item -ItemType Directory -Path $ReportsDir | Out-Null
}

# 1. Cleanup
Write-Host "`n[1/12] Cleaning up previous playground..." -ForegroundColor Yellow
Set-Location $ProjectRoot
$PlaygroundDir = "$ProjectRoot/playground_$(Get-Random)"
New-Item -ItemType Directory -Path $PlaygroundDir -Force | Out-Null
Set-Location $PlaygroundDir

# Data Collectors
$script:TableRows = ""
$script:DetailsSections = ""
$script:StepCounter = 1

# Helper to run step
function Run-Step {
    param($Name, $Command, $ArgList)
    $Start = Get-Date
    try {
        Write-Host "  Running: $Name..." -NoNewline
        
        # Set RUST_BACKTRACE=1 for detailed error logs
        $env:RUST_BACKTRACE = "1"
        $env:RUST_LOG = "debug"
        
        # Capture both stdout and stderr
        $ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo
        $ProcessInfo.FileName = $Command
        $ProcessInfo.Arguments = $ArgList -join " "
        $ProcessInfo.RedirectStandardOutput = $true
        $ProcessInfo.RedirectStandardError = $true
        $ProcessInfo.UseShellExecute = $false
        $ProcessInfo.CreateNoWindow = $true
        $ProcessInfo.WorkingDirectory = $PWD.Path
        
        $Process = [System.Diagnostics.Process]::Start($ProcessInfo)
        $Stdout = $Process.StandardOutput.ReadToEnd()
        $Stderr = $Process.StandardError.ReadToEnd()
        $Process.WaitForExit()
        
        $ExitCode = $Process.ExitCode
        $Output = $Stdout + "`n" + $Stderr
        
        $Duration = (Get-Date) - $Start
        if ($ExitCode -eq 0) {
            Write-Host " DONE ($($Duration.TotalSeconds.ToString("N2"))s)" -ForegroundColor Green
        } else {
            Write-Host " FAILED (Exit: $ExitCode) ($($Duration.TotalSeconds.ToString("N2"))s)" -ForegroundColor Red
            Write-Host "    Error details captured in report." -ForegroundColor Gray
        }
        return @{ Success = ($ExitCode -eq 0); Output = $Output; Duration = $Duration; ExitCode = $ExitCode }
    } catch {
        Write-Host " CRITICAL ERROR" -ForegroundColor Red
        return @{ Success = $false; Output = $_.Exception.Message; Duration = (Get-Date) - $Start; ExitCode = -1 }
    }
}

function Add-Result {
    param($Action, $Result, $Duration, $OutputContent)
    
    # A step only passes when the command succeeded and any optional check didn’t fail.
    $CheckPassed = if ($null -eq $Result.Check) { $true } else { $Result.Check }
    $StatusClass = if ($Result.Success -and $CheckPassed) { "pass" } else { "fail" }
    $StatusText = if($StatusClass -eq "pass") { "PASS" } else { "FAIL" }
    $DurationText = $Duration.TotalSeconds.ToString("N2") + "s"

    # Add Table Row
    $script:TableRows += "<tr><td>$script:StepCounter</td><td>$Action</td><td><span class='$StatusClass'>$StatusText</span></td><td>$DurationText</td></tr>`n"
    
    # Add Details
    # Clean ANSI codes from output for HTML display
    $CleanOutput = ($OutputContent | Out-String) -replace "\x1b\[[0-9;]*m", ""
    
    # If it failed, highlight the output
    $PreClass = if($StatusClass -eq "fail") { "output-error" } else { "" }
    
    $script:DetailsSections += @"
    <details>
        <summary>
            <span>$script:StepCounter. $Action</span>
            <span class='$StatusClass'>$StatusText</span>
        </summary>
        <div class="output-content">
            <pre class="$PreClass">$CleanOutput</pre>
        </div>
    </details>
"@
    $script:StepCounter++
}

# 2. Init (Auto-detection)
$ResInit = Run-Step "Initialize Project (Auto-detect Python)" $BinaryPath @("init", "--yes")

# Check if file exists before reading
if (-not (Test-Path "wovenpkg.json")) {
    Write-Host "  CRITICAL: wovenpkg.json was not created!" -ForegroundColor Red
    Write-Host "  Output: $($ResInit.Output)" -ForegroundColor Gray
    exit 1
}

$Config = Get-Content "wovenpkg.json" | ConvertFrom-Json
$SystemPython = (python --version) -replace "Python ", ""
$SystemMajorMinor = $SystemPython.Split(".")[0] + "." + $SystemPython.Split(".")[1]
$ResInit.Check = $Config.python_version -eq $SystemMajorMinor
Add-Result "Init (Auto-detect Python)" $ResInit $ResInit.Duration $ResInit.Output

# 3. Configure Dependencies
Write-Host "  Configuring wovenpkg.json..." -ForegroundColor Gray
$Config.dependencies = @{
    requests = "==2.31.0"
}
$Config | ConvertTo-Json | Set-Content -Path "wovenpkg.json"

# 4. Install
$ResInstall = Run-Step "Install Dependencies" $BinaryPath @("install")
Add-Result "Install Packages" $ResInstall $ResInstall.Duration $ResInstall.Output

# 5. Verify Lockfile Python Version
$Lockfile = Get-Content "wovenpkg.lock" | ConvertFrom-Json
$ResLock = @{ Success = $true; Check = ($null -ne $Lockfile.python_version -and $Lockfile.python_version -eq $Config.python_version); Output = "Lockfile Python Version: $($Lockfile.python_version)"; Duration = [TimeSpan]::Zero }
Add-Result "Verify Lockfile Python" $ResLock $ResLock.Duration $ResLock.Output

# 6. Verify Venv Version Warning
Write-Host "  Simulating Python version mismatch..." -ForegroundColor Gray
$Config.python_version = "3.11" # Force a different version
$Config | ConvertTo-Json | Set-Content -Path "wovenpkg.json"
$ResWarning = Run-Step "Install with Version Mismatch" $BinaryPath @("install")
$ResWarning.Check = $ResWarning.Output -match "Existing virtual environment uses Python"
Add-Result "Venv Version Warning" $ResWarning $ResWarning.Duration $ResWarning.Output

# 7. List (Managed Versions)
$ResList = Run-Step "List Managed Pythons" $BinaryPath @("list")
Add-Result "List Managed Pythons" $ResList $ResList.Duration $ResList.Output

# 8. Run Script
$PyScript = "import requests; print(f'SUCCESS: Requests {requests.__version__}')"
Set-Content "test_app.py" $PyScript
$ResRun = Run-Step "Run Script" $BinaryPath @("run", "python", "test_app.py")
$ResRun.Check = $ResRun.Output -match "SUCCESS"
Add-Result "Run Script" $ResRun $ResRun.Duration $ResRun.Output

# 9. Clean (Standard)
$ResClean = Run-Step "Clean Project" $BinaryPath @("clean")
Add-Result "Clean Project" $ResClean $ResClean.Duration $ResClean.Output

# 10. Clean (Python)
$ResCleanPy = Run-Step "Clean Managed Pythons" $BinaryPath @("clean", "--python")
Add-Result "Clean Managed Pythons" $ResCleanPy $ResCleanPy.Duration $ResCleanPy.Output

# Generate HTML
$HtmlContent = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WovenSnake v0.2.0 Usability Report</title>
    <style>
        body { font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 40px 20px; background-color: #f8f9fa; color: #212529; line-height: 1.6; }
        h1 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 15px; margin-bottom: 30px; letter-spacing: -0.5px; }
        h2 { color: #34495e; margin-top: 40px; }
        .summary { background: white; padding: 25px; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.05); margin-bottom: 30px; border-left: 5px solid #3498db; }
        table { width: 100%; border-collapse: separate; border-spacing: 0; margin: 25px 0; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.05); }
        th, td { padding: 15px 20px; text-align: left; border-bottom: 1px solid #eee; }
        th { background-color: #34495e; color: white; font-weight: 600; text-transform: uppercase; font-size: 0.85em; letter-spacing: 1px; }
        tr:last-child td { border-bottom: none; }
        tr:hover td { background-color: #fdfdfd; }
        .pass { color: #27ae60; font-weight: 700; background-color: rgba(39, 174, 96, 0.1); padding: 5px 10px; border-radius: 20px; display: inline-block; font-size: 0.9em; }
        .fail { color: #e74c3c; font-weight: 700; background-color: rgba(231, 76, 60, 0.1); padding: 5px 10px; border-radius: 20px; display: inline-block; font-size: 0.9em; }
        pre { background: #2d3436; color: #dfe6e9; padding: 20px; border-radius: 8px; overflow-x: auto; font-family: 'Consolas', 'Monaco', monospace; font-size: 0.9em; white-space: pre-wrap; word-wrap: break-word; }
        .output-error { border: 2px solid #e74c3c; background: #2c1a1a; }
        details { background: white; margin-bottom: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); border: 1px solid #eee; }
        summary { padding: 15px 20px; cursor: pointer; font-weight: 600; outline: none; list-style: none; display: flex; align-items: center; justify-content: space-between; transition: background-color 0.2s; border-radius: 8px; }
        summary:hover { background-color: #f8f9fa; }
        summary::-webkit-details-marker { display: none; }
        summary::after { content: '+'; font-size: 1.2em; color: #bdc3c7; }
        details[open] summary { border-radius: 8px 8px 0 0; border-bottom: 1px solid #eee; }
        details[open] summary::after { content: '-'; }
        .output-content { padding: 0 20px 20px; animation: fadeIn 0.3s ease-in-out; }
        @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
        .footer { text-align: center; margin-top: 50px; color: #95a5a6; font-size: 0.9em; }
    </style>
</head>
<body>
    <h1>WovenSnake v0.2.0 Usability Report</h1>
    
    <div class="summary">
        <p><strong>Date:</strong> $(Get-Date)</p>
        <p><strong>Version:</strong> v0.2.0</p>
        <p><strong>Environment:</strong> Windows (PowerShell)</p>
        <p><strong>Features Tested:</strong> Python Auto-detection, Lockfile Versioning, Venv Validation, Managed Python Management.</p>
    </div>

    <h2>Test Execution Log</h2>
    <table>
        <thead>
            <tr>
                <th>Step</th>
                <th>Action</th>
                <th>Status</th>
                <th>Duration</th>
            </tr>
        </thead>
        <tbody>
            $script:TableRows
        </tbody>
    </table>

    <h2>Detailed Outputs</h2>
    $script:DetailsSections

    <div class="footer">
        Generated automatically by WovenSnake Validation Script
    </div>
</body>
</html>
"@

Set-Content -Path $ReportFile -Value $HtmlContent
Write-Host "`nReport generated at $ReportFile" -ForegroundColor Cyan

# Final Cleanup
Write-Host "`n[12/12] Destroying playground..." -ForegroundColor Yellow
Set-Location $ProjectRoot
if (Test-Path $PlaygroundDir) {
    Remove-Item -Recurse -Force $PlaygroundDir -ErrorAction SilentlyContinue
    Write-Host "Playground destroyed successfully." -ForegroundColor Green
}