Centralize build output and generate OS-specific startup.json

- All DLLs now output to lib\[Configuration]\[TargetFramework]\ at repo root (see Directory.Build.props)
- .gitignore updated to exclude /lib/
- On first run, startup.json is auto-generated with OS-appropriate default paths (Windows, Linux, macOS, or portable)
- Removes null/example config; generated config is immediately usable and clearly documented
- Extensive new documentation: build output, startup.json logic, visual guides, and code proofs
- Publish profile now deletes existing files for clean deploys
- No breaking changes: existing startup.json files are preserved
- Improves first-run UX, deployment, and cross-platform consistency
This commit is contained in:
2026-02-26 14:21:26 -05:00
parent 5bdb7d53c3
commit 8f860a8ec3
35 changed files with 2218 additions and 46 deletions
+388
View File
@@ -0,0 +1,388 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Converts C# files from block-scoped to file-scoped namespaces and fixes System namespace conflicts.
.DESCRIPTION
This script converts all C# files in MediaBrowser.Model from block-scoped namespaces
to file-scoped namespaces, and replaces System using directives with global::System
to resolve conflicts with MediaBrowser.Model.System namespace.
.PARAMETER Path
The root path to search for C# files. Defaults to "MediaBrowser.Model"
.PARAMETER DryRun
If specified, shows what would be changed without actually modifying files
.PARAMETER Verbose
Shows detailed processing information
.PARAMETER BackupFiles
Creates .bak files before conversion
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -DryRun
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -BackupFiles
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -Path "MediaBrowser.Model\Dlna" -Verbose
#>
param(
[string]$Path = "MediaBrowser.Model",
[switch]$DryRun,
[switch]$BackupFiles
)
$ErrorActionPreference = "Stop"
$VerbosePreference = if ($PSBoundParameters.ContainsKey('Verbose')) { 'Continue' } else { 'SilentlyContinue' }
function Get-IndentString {
param([string]$Line)
if ($Line -match '^(\s+)') {
return $Matches[1]
}
return ""
}
function Get-IndentLevel {
param([string]$IndentString)
# Count spaces (4 spaces = 1 level) or tabs (1 tab = 1 level)
$spaces = ($IndentString.ToCharArray() | Where-Object { $_ -eq ' ' }).Count
$tabs = ($IndentString.ToCharArray() | Where-Object { $_ -eq "`t" }).Count
return [Math]::Max($tabs, [Math]::Floor($spaces / 4))
}
function Remove-OneIndentLevel {
param([string]$Line, [ref]$IndentChar, [ref]$IndentSize)
if ([string]::IsNullOrWhiteSpace($Line)) {
return $Line
}
$indent = Get-IndentString -Line $Line
if ($indent.Length -eq 0) {
return $Line
}
# Detect indent character and size on first indented line
if ($IndentChar.Value -eq $null) {
if ($indent[0] -eq "`t") {
$IndentChar.Value = "`t"
$IndentSize.Value = 1
} else {
$IndentChar.Value = ' '
# Detect indent size (usually 4 spaces)
$IndentSize.Value = 4
if ($indent.Length -ge 2) {
# Try to detect actual indent size
for ($i = 1; $i -le 8; $i++) {
if ($indent.Length % $i -eq 0) {
$IndentSize.Value = $i
break
}
}
}
}
Write-Verbose "Detected indent: '$($IndentChar.Value)' x $($IndentSize.Value)"
}
# Remove one level of indentation
if ($IndentChar.Value -eq "`t") {
if ($Line -match '^\t(.*)') {
return $Matches[1]
}
} else {
$pattern = "^" + (' ' * $IndentSize.Value) + '(.*)'
if ($Line -match $pattern) {
return $Matches[1]
}
}
return $Line
}
function Test-FileScopedNamespace {
param([string]$Content)
# Check if already using file-scoped namespace (semicolon after namespace declaration)
return $Content -match 'namespace\s+[\w\.]+\s*;'
}
function Test-BlockScopedNamespace {
param([string]$Content)
# Check for block-scoped namespace
return $Content -match 'namespace\s+[\w\.]+\s*\r?\n?\s*\{'
}
function Add-GlobalPrefix {
param([string]$Content)
# System namespaces to prefix (ordered from most specific to least)
$systemNamespaces = @(
'System\.Xml\.Serialization',
'System\.ComponentModel\.DataAnnotations',
'System\.ComponentModel',
'System\.Collections\.Generic',
'System\.Collections\.Concurrent',
'System\.Collections',
'System\.Diagnostics\.CodeAnalysis',
'System\.Diagnostics',
'System\.Globalization',
'System\.Linq\.Expressions',
'System\.Linq',
'System\.Text\.Json\.Serialization',
'System\.Text\.Json',
'System\.Text\.RegularExpressions',
'System\.Text',
'System\.Threading\.Tasks',
'System\.Threading',
'System\.Runtime\.Serialization',
'System\.Runtime\.CompilerServices',
'System\.Runtime',
'System\.Net\.Http',
'System\.Net\.Sockets',
'System\.Net\.WebSockets',
'System\.Net\.Mime',
'System\.Net',
'System\.IO\.Compression',
'System\.IO',
'System\.Security',
'System' # Must be last to avoid partial matches
)
foreach ($ns in $systemNamespaces) {
# Don't add global:: if it's already there
# Match: "using System.X" but not "using global::System.X"
$pattern = '(\s+using\s+)(?!global::)(' + $ns + ')(\s|;)'
$replacement = '${1}global::${2}${3}'
$Content = $Content -replace $pattern, $replacement
}
return $Content
}
function Convert-ToFileScopedNamespace {
param(
[string]$FilePath,
[switch]$DryRun,
[switch]$CreateBackup
)
$content = Get-Content -Path $FilePath -Raw
$originalContent = $content
$modified = $false
# Check current state
$hasFileScopedNS = Test-FileScopedNamespace -Content $content
$hasBlockScopedNS = Test-BlockScopedNamespace -Content $content
if ($hasFileScopedNS) {
Write-Verbose " Already using file-scoped namespace"
# But still add global:: prefixes if needed
$contentWithGlobal = Add-GlobalPrefix -Content $content
if ($contentWithGlobal -ne $content) {
$content = $contentWithGlobal
$modified = $true
Write-Host " [UPDATED] Added global:: prefixes" -ForegroundColor Yellow
} else {
Write-Host " [SKIP] Already correct" -ForegroundColor Gray
return $false
}
}
elseif (-not $hasBlockScopedNS) {
Write-Host " [SKIP] No namespace found" -ForegroundColor Gray
return $false
}
else {
Write-Verbose " Converting block-scoped to file-scoped namespace"
# Extract namespace name and line
if ($content -match 'namespace\s+([\w\.]+)\s*\r?\n?\s*\{') {
$namespaceName = $Matches[1]
Write-Verbose " Namespace: $namespaceName"
} else {
Write-Warning " Could not parse namespace"
return $false
}
# Step 1: Convert namespace declaration to file-scoped
$content = $content -replace '(namespace\s+[\w\.]+)\s*\r?\n?\s*\{', '$1;'
$modified = $true
# Step 2: Add global:: prefixes to System using directives
$content = Add-GlobalPrefix -Content $content
# Step 3: Parse into lines for indentation removal
$lines = $content -split '\r?\n'
$hasWindowsLineEndings = $originalContent -match '\r\n'
# Find namespace declaration line
$namespaceLineIndex = -1
for ($i = 0; $i -lt $lines.Length; $i++) {
if ($lines[$i] -match '^namespace\s+[\w\.]+\s*;') {
$namespaceLineIndex = $i
break
}
}
if ($namespaceLineIndex -lt 0) {
Write-Warning " Could not find namespace declaration after conversion"
return $false
}
# Step 4: Remove one level of indentation from all lines after namespace
$indentChar = $null
$indentSize = 4
$indentCharRef = [ref]$indentChar
$indentSizeRef = [ref]$indentSize
for ($i = $namespaceLineIndex + 1; $i -lt $lines.Length; $i++) {
$lines[$i] = Remove-OneIndentLevel -Line $lines[$i] -IndentChar $indentCharRef -IndentSize $indentSizeRef
}
# Step 5: Remove the closing brace of the namespace
# Find the last non-empty, non-whitespace line that's just a closing brace
$closingBraceIndex = -1
for ($i = $lines.Length - 1; $i -gt $namespaceLineIndex; $i--) {
$trimmed = $lines[$i].Trim()
if ($trimmed -eq '}') {
$closingBraceIndex = $i
break
}
elseif ($trimmed -ne '' -and $trimmed -ne '}') {
# Found non-brace content, stop looking
break
}
}
if ($closingBraceIndex -ge 0) {
Write-Verbose " Removing closing brace at line $closingBraceIndex"
$lines = $lines[0..($closingBraceIndex-1)] + $lines[($closingBraceIndex+1)..($lines.Length-1)]
}
# Step 6: Rejoin lines
$lineEnding = if ($hasWindowsLineEndings) { "`r`n" } else { "`n" }
$content = ($lines | Where-Object { $_ -ne $null }) -join $lineEnding
# Step 7: Clean up trailing whitespace and ensure single newline at end
$content = $content.TrimEnd()
$content += $lineEnding
Write-Host " [CONVERTED] Block-scoped → File-scoped" -ForegroundColor Green
}
# Apply changes
if ($modified -and $content -ne $originalContent) {
if ($DryRun) {
Write-Host " [DRY-RUN] Would save changes" -ForegroundColor Cyan
return $true
}
if ($CreateBackup) {
$backupPath = "$FilePath.bak"
Copy-Item -Path $FilePath -Destination $backupPath -Force
Write-Verbose " Created backup: $backupPath"
}
# Write with correct encoding (UTF-8 without BOM for .cs files)
[System.IO.File]::WriteAllText($FilePath, $content, [System.Text.UTF8Encoding]::new($false))
return $true
}
return $false
}
# Main script
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "C# File-Scoped Namespace Converter" -ForegroundColor Cyan
Write-Host "Fixes MediaBrowser.Model.System namespace conflicts" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host
if ($DryRun) {
Write-Host "DRY RUN MODE - No files will be modified" -ForegroundColor Yellow
Write-Host
}
if ($BackupFiles) {
Write-Host "Backup mode enabled - .bak files will be created" -ForegroundColor Yellow
Write-Host
}
if (-not (Test-Path $Path)) {
Write-Error "❌ Path not found: $Path"
exit 1
}
# Find all C# files
$files = Get-ChildItem -Path $Path -Filter "*.cs" -Recurse | Where-Object {
$_.FullName -notmatch "\\obj\\" -and
$_.FullName -notmatch "\\bin\\" -and
$_.FullName -notmatch "\\Properties\\AssemblyInfo\.cs$"
}
Write-Host "Found $($files.Count) C# files in $Path" -ForegroundColor Cyan
Write-Host
$convertedCount = 0
$skippedCount = 0
$errorCount = 0
$updatedCount = 0
foreach ($file in $files) {
$relativePath = $file.FullName.Replace((Get-Location).Path, "").TrimStart([char]92, [char]47)
Write-Host "[Processing] $relativePath" -ForegroundColor White
try {
$result = Convert-ToFileScopedNamespace -FilePath $file.FullName -DryRun:$DryRun -CreateBackup:$BackupFiles
if ($result) {
if ($result -eq "updated") {
$updatedCount++
} else {
$convertedCount++
}
} else {
$skippedCount++
}
}
catch {
Write-Host " [ERROR] $_" -ForegroundColor Red
Write-Verbose $_.ScriptStackTrace
$errorCount++
}
}
Write-Host
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "Summary:" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host " Total files: $($files.Count)" -ForegroundColor White
Write-Host " Converted: $convertedCount" -ForegroundColor Green
Write-Host " Updated (global::): $updatedCount" -ForegroundColor Yellow
Write-Host " Skipped: $skippedCount" -ForegroundColor Gray
Write-Host " Errors: $errorCount" -ForegroundColor Red
if ($DryRun) {
Write-Host
Write-Host "This was a DRY RUN. Run without -DryRun to apply changes." -ForegroundColor Yellow
} elseif ($convertedCount -gt 0 -or $updatedCount -gt 0) {
Write-Host
Write-Host "Conversion complete! Next steps:" -ForegroundColor Green
Write-Host " 1. Review changes with: git diff" -ForegroundColor White
Write-Host " 2. Build project: dotnet build MediaBrowser.Model\MediaBrowser.Model.csproj" -ForegroundColor White
Write-Host " 3. If successful: git add . && git commit -m `"Convert to file-scoped namespaces`"" -ForegroundColor White
}
Write-Host ("=" * 80) -ForegroundColor Cyan
exit $errorCount
+195
View File
@@ -0,0 +1,195 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Fixes StyleCop warnings in C# files.
.DESCRIPTION
Automatically fixes common StyleCop warnings:
- SA1413: Adds trailing commas to multi-line initializers
- SA1515: Adds blank lines before single-line comments
- SA1518: Ensures files end with single newline
.PARAMETER Path
The root path to search for C# files. Defaults to "MediaBrowser.Model"
.PARAMETER DryRun
If specified, shows what would be changed without actually modifying files
.EXAMPLE
.\FixStyleCopWarnings.ps1 -DryRun
#>
param(
[string]$Path = "MediaBrowser.Model",
[switch]$DryRun
)
$ErrorActionPreference = "Stop"
function Fix-TrailingCommas {
param([string]$Content)
$modified = $false
$lines = $content -split '\r?\n'
for ($i = 0; $i -lt $lines.Length - 1; $i++) {
$currentLine = $lines[$i]
$nextLine = $lines[$i + 1]
# Check if current line ends with a property/value without comma
# and next line closes the initializer
if ($currentLine -match '^\s+\w+\s*=\s*.+[^,]\s*$' -and $nextLine -match '^\s*[}\)]') {
$lines[$i] = $currentLine.TrimEnd() + ','
$modified = $true
}
}
if ($modified) {
return ($lines -join "`n")
}
return $Content
}
function Fix-BlankLinesBeforeComments {
param([string]$Content)
$modified = $false
$lines = $content -split '\r?\n'
$newLines = @()
for ($i = 0; $i -lt $lines.Length; $i++) {
$currentLine = $lines[$i]
# Check if this is a single-line comment
if ($currentLine -match '^\s+//\s+\w') {
# Check if previous line exists and is not blank
if ($i -gt 0) {
$prevLine = $lines[$i - 1]
$prevLineIsBlank = [string]::IsNullOrWhiteSpace($prevLine)
$prevLineIsOpenBrace = $prevLine -match '\{\s*$'
$prevLineIsComment = $prevLine -match '^\s*//'
# Add blank line if previous line is not blank, not open brace, not another comment
if (-not $prevLineIsBlank -and -not $prevLineIsOpenBrace -and -not $prevLineIsComment) {
$newLines += ""
$modified = $true
}
}
}
$newLines += $currentLine
}
if ($modified) {
return ($newLines -join "`n")
}
return $Content
}
function Fix-FileEnding {
param([string]$Content)
# Ensure file ends with exactly one newline
$trimmed = $Content.TrimEnd("`r", "`n", " ", "`t")
return $trimmed + "`n"
}
function Fix-StyleCopWarnings {
param(
[string]$FilePath,
[switch]$DryRun
)
$content = Get-Content -Path $FilePath -Raw
$originalContent = $content
# Apply fixes
$content = Fix-TrailingCommas -Content $content
$content = Fix-BlankLinesBeforeComments -Content $content
$content = Fix-FileEnding -Content $content
# Restore Windows line endings if original had them
if ($originalContent -match '\r\n') {
$content = $content -replace '(?<!\r)\n', "`r`n"
}
if ($content -ne $originalContent) {
if ($DryRun) {
Write-Host " [DRY-RUN] Would fix StyleCop warnings" -ForegroundColor Cyan
return $true
}
[System.IO.File]::WriteAllText($FilePath, $content, [System.Text.UTF8Encoding]::new($false))
Write-Host " [FIXED]" -ForegroundColor Green
return $true
}
Write-Host " [SKIP] No warnings to fix" -ForegroundColor Gray
return $false
}
# Main script
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "StyleCop Warning Fixer" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host
if ($DryRun) {
Write-Host "DRY RUN MODE - No files will be modified" -ForegroundColor Yellow
Write-Host
}
if (-not (Test-Path $Path)) {
Write-Error "Path not found: $Path"
exit 1
}
$files = Get-ChildItem -Path $Path -Filter "*.cs" -Recurse | Where-Object {
$_.FullName -notmatch "\\obj\\" -and
$_.FullName -notmatch "\\bin\\"
}
Write-Host "Found $($files.Count) C# files in $Path" -ForegroundColor Cyan
Write-Host
$fixedCount = 0
$skippedCount = 0
$errorCount = 0
foreach ($file in $files) {
$relativePath = $file.FullName.Replace((Get-Location).Path, "").TrimStart([char]92, [char]47)
Write-Host "[Processing] $relativePath" -ForegroundColor White
try {
$fixed = Fix-StyleCopWarnings -FilePath $file.FullName -DryRun:$DryRun
if ($fixed) {
$fixedCount++
} else {
$skippedCount++
}
}
catch {
Write-Host " [ERROR] $_" -ForegroundColor Red
$errorCount++
}
}
Write-Host
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "Summary:" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host " Total files: $($files.Count)" -ForegroundColor White
Write-Host " Fixed: $fixedCount" -ForegroundColor Green
Write-Host " Skipped: $skippedCount" -ForegroundColor Gray
Write-Host " Errors: $errorCount" -ForegroundColor Red
if ($DryRun) {
Write-Host
Write-Host "This was a DRY RUN. Run without -DryRun to apply changes." -ForegroundColor Yellow
}
Write-Host ("=" * 80) -ForegroundColor Cyan
exit $errorCount
+64
View File
@@ -0,0 +1,64 @@
# Rollback to .NET 10 Script
# This script reverts all .NET 11 changes back to .NET 10
Write-Host "Rolling back to .NET 10..." -ForegroundColor Yellow
# Revert all .csproj files from net11.0 to net10.0
$files = Get-ChildItem -Path . -Filter "*.csproj" -Recurse | Where-Object {
$_.FullName -notlike "*\obj\*" -and $_.FullName -notlike "*\bin\*"
}
foreach ($file in $files) {
$content = Get-Content $file.FullName -Raw
if ($content -match '<TargetFramework>net11\.0</TargetFramework>') {
$content = $content -replace '<TargetFramework>net11\.0</TargetFramework>', '<TargetFramework>net10.0</TargetFramework>'
Set-Content -Path $file.FullName -Value $content -NoNewline
Write-Host "Reverted: $($file.Name)" -ForegroundColor Green
}
}
Write-Host "`nUpdating Directory.Packages.props..." -ForegroundColor Yellow
# Read Directory.Packages.props
$packagesFile = "Directory.Packages.props"
$content = Get-Content $packagesFile -Raw
# Revert Microsoft packages
$content = $content -replace 'Microsoft\.AspNetCore\.Authorization" Version="11\.0\.1"', 'Microsoft.AspNetCore.Authorization" Version="10.0.3"'
$content = $content -replace 'Microsoft\.AspNetCore\.Mvc\.Testing" Version="11\.0\.1"', 'Microsoft.AspNetCore.Mvc.Testing" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Data\.Sqlite" Version="11\.0\.1"', 'Microsoft.Data.Sqlite" Version="10.0.3"'
$content = $content -replace 'Microsoft\.EntityFrameworkCore" Version="11\.0\.1"', 'Microsoft.EntityFrameworkCore" Version="10.0.3"'
$content = $content -replace 'Microsoft\.EntityFrameworkCore\.Design" Version="11\.0\.1"', 'Microsoft.EntityFrameworkCore.Design" Version="10.0.3"'
$content = $content -replace 'Microsoft\.EntityFrameworkCore\.Relational" Version="11\.0\.1"', 'Microsoft.EntityFrameworkCore.Relational" Version="10.0.3"'
$content = $content -replace 'Microsoft\.EntityFrameworkCore\.Sqlite" Version="11\.0\.1"', 'Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.3"'
$content = $content -replace 'Microsoft\.EntityFrameworkCore\.Tools" Version="11\.0\.1"', 'Microsoft.EntityFrameworkCore.Tools" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Caching\.Abstractions" Version="11\.0\.1"', 'Microsoft.Extensions.Caching.Abstractions" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Caching\.Memory" Version="11\.0\.1"', 'Microsoft.Extensions.Caching.Memory" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Configuration\.Abstractions" Version="11\.0\.1"', 'Microsoft.Extensions.Configuration.Abstractions" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Configuration\.Binder" Version="11\.0\.1"', 'Microsoft.Extensions.Configuration.Binder" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.DependencyInjection" Version="11\.0\.1"', 'Microsoft.Extensions.DependencyInjection" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Diagnostics\.HealthChecks\.EntityFrameworkCore" Version="11\.0\.1"', 'Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Hosting\.Abstractions" Version="11\.0\.1"', 'Microsoft.Extensions.Hosting.Abstractions" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Http" Version="11\.0\.1"', 'Microsoft.Extensions.Http" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Logging" Version="11\.0\.1"', 'Microsoft.Extensions.Logging" Version="10.0.3"'
$content = $content -replace 'Microsoft\.Extensions\.Options" Version="11\.0\.1"', 'Microsoft.Extensions.Options" Version="10.0.3"'
# Revert Serilog packages
$content = $content -replace 'Serilog\.AspNetCore" Version="11\.0\.1"', 'Serilog.AspNetCore" Version="10.0.0"'
$content = $content -replace 'Serilog\.Settings\.Configuration" Version="11\.0\.1"', 'Serilog.Settings.Configuration" Version="10.0.0"'
# Revert System packages
$content = $content -replace 'System\.Text\.Json" Version="11\.0\.1"', 'System.Text.Json" Version="10.0.3"'
# Revert Npgsql with comment
$content = $content -replace 'Npgsql\.EntityFrameworkCore\.PostgreSQL" Version="11\.0\.0-preview\.1"',
'Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />' + "`n <!-- Note: Using 9.0.2 with EF Core 10 - version constraint warnings expected"
Set-Content -Path $packagesFile -Value $content -NoNewline
Write-Host "Updated Directory.Packages.props" -ForegroundColor Green
Write-Host "`nRollback complete!" -ForegroundColor Cyan
Write-Host "Run these commands to restore and build:" -ForegroundColor Cyan
Write-Host " dotnet restore Jellyfin.sln" -ForegroundColor White
Write-Host " dotnet build Jellyfin.sln /p:TreatWarningsAsErrors=false" -ForegroundColor White
+82
View File
@@ -0,0 +1,82 @@
# PostgreSQL Migration Verification Script
Write-Host "=== Jellyfin PostgreSQL Migration Verification ===" -ForegroundColor Cyan
Write-Host ""
# Check if Docker is available
$dockerAvailable = $false
try {
$null = docker --version 2>&1
$dockerAvailable = $true
Write-Host "Docker is available" -ForegroundColor Green
} catch {
Write-Host "Docker is not available" -ForegroundColor Yellow
}
Write-Host ""
Write-Host "--- Step 1: Check Migration Files ---" -ForegroundColor Cyan
$migrationPath = "src\Jellyfin.Database\Jellyfin.Database.Providers.Postgres\Migrations"
if (Test-Path $migrationPath) {
$migrations = Get-ChildItem -Path $migrationPath -Filter "*.cs" -Exclude "*Designer.cs", "*Snapshot.cs", "*Factory.cs"
Write-Host "Found $($migrations.Count) migration(s):" -ForegroundColor Green
foreach ($migration in $migrations) {
Write-Host " - $($migration.Name)" -ForegroundColor Gray
}
} else {
Write-Host "Migration path not found" -ForegroundColor Red
exit 1
}
Write-Host ""
Write-Host "--- Step 2: Build PostgreSQL Database Provider ---" -ForegroundColor Cyan
Push-Location "src\Jellyfin.Database\Jellyfin.Database.Providers.Postgres"
try {
dotnet build --configuration Release
if ($LASTEXITCODE -eq 0) {
Write-Host "PostgreSQL provider built successfully" -ForegroundColor Green
} else {
Write-Host "Build failed" -ForegroundColor Red
Pop-Location
exit 1
}
} finally {
Pop-Location
}
Write-Host ""
Write-Host "--- Step 3: List Migrations ---" -ForegroundColor Cyan
Push-Location "src\Jellyfin.Database\Jellyfin.Database.Providers.Postgres"
try {
Write-Host "Running: dotnet ef migrations list" -ForegroundColor Gray
dotnet ef migrations list
} finally {
Pop-Location
}
Write-Host ""
Write-Host "--- Step 4: Generate SQL Script ---" -ForegroundColor Cyan
Push-Location "src\Jellyfin.Database\Jellyfin.Database.Providers.Postgres"
try {
$sqlOutputPath = "..\..\..\migration-script.sql"
Write-Host "Generating SQL script..." -ForegroundColor Gray
dotnet ef migrations script --output $sqlOutputPath --idempotent
if (Test-Path $sqlOutputPath) {
$sqlContent = Get-Content $sqlOutputPath -Raw
$lineCount = ($sqlContent -split "`n").Count
Write-Host "SQL script generated successfully ($lineCount lines)" -ForegroundColor Green
Write-Host "Location: $((Resolve-Path $sqlOutputPath).Path)" -ForegroundColor Gray
} else {
Write-Host "Failed to generate SQL script" -ForegroundColor Red
}
} catch {
Write-Host "Error generating SQL script: $_" -ForegroundColor Red
} finally {
Pop-Location
}
Write-Host ""
Write-Host "=== Verification Complete ===" -ForegroundColor Cyan