Catalog seeding, blank-unique fix, slides rotation, console repair
This commit is contained in:
@@ -201,6 +201,7 @@ var
|
||||
// the check takes a few seconds, runs hidden, and gives no sign of life.
|
||||
CheckingPage: TOutputProgressWizardPage;
|
||||
PluginPage: TInputOptionWizardPage;
|
||||
CatalogPage: TInputOptionWizardPage;
|
||||
PluginNames: TArrayOfString;
|
||||
PluginPageReady: Boolean;
|
||||
DbPageReady: Boolean;
|
||||
@@ -397,7 +398,21 @@ begin
|
||||
if Trim(PluginNames[I]) <> '' then
|
||||
PluginPage.Add(PluginLabel(Trim(PluginNames[I])));
|
||||
|
||||
DbChoicePage := CreateInputOptionPage(PluginPage.ID,
|
||||
// Offered, not forced. A site that machines nothing inherits 21 machine types
|
||||
// it will never use, and a catalog nobody wanted is just clutter in every
|
||||
// dropdown. Ticked by default because the alternative is retyping a vendor and
|
||||
// model list another site already built.
|
||||
CatalogPage := CreateInputOptionPage(PluginPage.ID,
|
||||
'Starter data', 'Load the shared equipment catalog?',
|
||||
'Vendors, model numbers and the type lists, as built up across the other ' +
|
||||
'sites. It only ADDS - nothing existing is changed or removed - and it can ' +
|
||||
'be loaded later with "flask seed catalog". No assets, locations or people ' +
|
||||
'are included; this is catalogue information only.',
|
||||
False, False);
|
||||
CatalogPage.Add('Load the shared vendor and model catalog');
|
||||
CatalogPage.Values[0] := True;
|
||||
|
||||
DbChoicePage := CreateInputOptionPage(CatalogPage.ID,
|
||||
'Database', 'Where should ShopDB-Flask store its data?',
|
||||
'Most sites already run MySQL for the existing shopdb application. If so, ' +
|
||||
'choose the second option - installing a second server would collide on ' +
|
||||
@@ -1177,6 +1192,8 @@ begin
|
||||
' -OnFailure never' +
|
||||
' -ClientIpSource ' + ClientIpSourceArg +
|
||||
' -SitePlugins "' + SelectedPlugins + '"';
|
||||
if CatalogPage.Values[0] then
|
||||
Common := Common + ' -SeedCatalog';
|
||||
|
||||
// Subpath deployment: an IIS Application under the existing site instead of a
|
||||
// site of its own. Appended here because Pascal has no conditional expression.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('menu','status','start','stop','restart','logs','open','backup',
|
||||
'check','sessions','plugins','add-plugin','verify','uninstall')]
|
||||
'check','repair','sessions','plugins','add-plugin','verify','uninstall')]
|
||||
[string] $Command = 'menu',
|
||||
[string] $Path = '',
|
||||
# Machine-readable output for 'check'. The people running this are expected to
|
||||
@@ -577,11 +577,101 @@ function Invoke-Check {
|
||||
$env:FLASK_APP = 'shopdb'
|
||||
try { & $flask db-utils preflight 2>&1 | ForEach-Object { Say " $_" } }
|
||||
finally { Pop-Location }
|
||||
|
||||
# Surface an unfinished install HERE, where somebody is already looking,
|
||||
# rather than leaving them to infer it from 500s on unrelated pages.
|
||||
$state = Test-Provisioned
|
||||
if ($state.SchemaCurrent -eq $false -or $state.Seeded -eq $false) {
|
||||
Say ''
|
||||
Say ' THIS SERVER IS NOT FULLY PROVISIONED' 'Red'
|
||||
foreach ($detail in $state.Detail) { Say (" - {0}" -f $detail) 'Red' }
|
||||
Say ' Most pages will answer 500 until this is fixed. Run:' 'Yellow'
|
||||
Say ' shopdb-admin.ps1 repair' 'White'
|
||||
}
|
||||
|
||||
Say ''
|
||||
Say ' For help from an AI assistant, paste the output of:' 'DarkGray'
|
||||
Say ' shopdb-admin.ps1 check -Json' 'White'
|
||||
}
|
||||
|
||||
function Test-Provisioned {
|
||||
"""Is the schema current and the reference data seeded?"""
|
||||
# A server whose migrations or seeds never completed does not fail politely:
|
||||
# it answers 500 on most pages and 404 on settings that were never created,
|
||||
# which reads as a broken application rather than an unfinished install. One
|
||||
# site was stood up that way and spent a morning being debugged as a bug.
|
||||
$result = @{ SchemaCurrent = $true; Seeded = $true; Detail = @() }
|
||||
|
||||
$out = Invoke-Flask @('db','current')
|
||||
$current = ($out | Out-String)
|
||||
$head = (Invoke-Flask @('db','heads') | Out-String)
|
||||
if ($script:LastFlaskExit -ne 0) {
|
||||
$result.SchemaCurrent = $false
|
||||
$result.Detail += 'could not read the schema version'
|
||||
} elseif ($current -notmatch '\(head\)' -and $head.Trim()) {
|
||||
# `db current` appends "(head)" when the database is at the newest
|
||||
# revision. Its absence means migrations are outstanding.
|
||||
$result.SchemaCurrent = $false
|
||||
$result.Detail += 'database schema is behind the application'
|
||||
}
|
||||
|
||||
# Sentinel seeds. permissions, settings and the reference data are each
|
||||
# created by a `flask seed` command, and their absence is what produced the
|
||||
# 404s on settings keys at a site whose install never finished.
|
||||
$seedOut = (Invoke-Flask @('db-utils','seed-state') | Out-String)
|
||||
if ($seedOut -match 'No such command') {
|
||||
# An older build with no seed-state. Report UNKNOWN rather than healthy:
|
||||
# claiming a clean bill of health from a check that did not run is how a
|
||||
# broken server passes inspection.
|
||||
$result.Seeded = $null
|
||||
$result.Detail += 'seed state could not be checked (older build)'
|
||||
} elseif ($script:LastFlaskExit -ne 0 -or $seedOut -match 'MISSING') {
|
||||
$result.Seeded = $false
|
||||
$result.Detail += 'seed data is missing (permissions, settings or reference data)'
|
||||
}
|
||||
return $result
|
||||
}
|
||||
|
||||
function Invoke-Repair {
|
||||
Head 'Repair provisioning'
|
||||
Say ' Brings the database up to the application: migrations, plugin'
|
||||
Say ' migrations, and the seed data. Every step is idempotent, so running'
|
||||
Say ' this on a healthy server changes nothing.'
|
||||
Say ''
|
||||
|
||||
$steps = @(
|
||||
@{ Name = 'core schema'; Args = @('db','upgrade') },
|
||||
@{ Name = 'plugin schemas'; Args = @('plugin','upgrade-all') },
|
||||
@{ Name = 'permissions'; Args = @('seed','permissions') },
|
||||
@{ Name = 'settings'; Args = @('seed','settings') },
|
||||
@{ Name = 'reference data'; Args = @('seed','reference-data') }
|
||||
)
|
||||
$failed = @()
|
||||
foreach ($step in $steps) {
|
||||
Say (" {0} ..." -f $step.Name) 'White'
|
||||
$out = Invoke-Flask $step.Args
|
||||
if ($script:LastFlaskExit -ne 0) {
|
||||
$failed += $step.Name
|
||||
Say (" FAILED (exit {0})" -f $script:LastFlaskExit) 'Red'
|
||||
$out | Select-Object -Last 6 | ForEach-Object { Say " $_" 'Red' }
|
||||
} else {
|
||||
Say ' done' 'Green'
|
||||
}
|
||||
}
|
||||
|
||||
if ($failed.Count -gt 0) {
|
||||
Say ''
|
||||
Say (" {0} step(s) failed: {1}" -f $failed.Count, ($failed -join ', ')) 'Red'
|
||||
Say ' Nothing further was skipped - each step ran independently.' 'Red'
|
||||
Say ' Send the output above, or run: shopdb-admin.ps1 check -Json' 'Red'
|
||||
return
|
||||
}
|
||||
|
||||
Say ''
|
||||
Say ' Provisioning complete. Restarting the application.' 'Green'
|
||||
Restart-App
|
||||
}
|
||||
|
||||
function Show-Sessions {
|
||||
Head 'Worker processes'
|
||||
$w = Get-CimInstance Win32_Process -Filter "Name='w3wp.exe'" -EA SilentlyContinue
|
||||
@@ -856,6 +946,7 @@ switch ($Command) {
|
||||
'open' { Open-Site }
|
||||
'backup' { Backup-Db $Path }
|
||||
'check' { Invoke-Check }
|
||||
'repair' { Invoke-Repair }
|
||||
'sessions' { Show-Sessions }
|
||||
'plugins' { Show-Plugins }
|
||||
'add-plugin'{ Add-Plugin $Path }
|
||||
|
||||
@@ -127,6 +127,10 @@ param(
|
||||
# client reads as 127.0.0.1: the GE-Enforce IP allowlist, the dashboard
|
||||
# visitor-location lookup and per-host login rate limiting all break quietly.
|
||||
[ValidateSet('direct','proxy')] [string] $ClientIpSource = 'direct',
|
||||
# Load the shared vendor/model catalog during stage 3. Offered by the
|
||||
# wizard rather than assumed: a site that machines nothing does not want
|
||||
# 21 machine types cluttering its dropdowns.
|
||||
[switch] $SeedCatalog,
|
||||
# Required before this installer will alter an installation it did not
|
||||
# create. Everything here is built for a greenfield server: it makes its own
|
||||
# Python, its own venv and its own IIS objects, and its upgrade path assumes
|
||||
@@ -1747,6 +1751,14 @@ build to get a matching pair, then send the install log to support.
|
||||
Invoke-Native $Flask @('seed',$seed) "seed $seed"
|
||||
}
|
||||
|
||||
# Additive and idempotent, so a re-run adds only what is genuinely new
|
||||
# and never overwrites a description somebody corrected here.
|
||||
if ($SeedCatalog) {
|
||||
Write-Log 'flask seed catalog (shared vendor and model catalog)'
|
||||
$catalogOut = Invoke-Native $Flask @('seed','catalog') 'seed catalog' -OkExit 0,1
|
||||
$catalogOut | Select-Object -Last 12 | ForEach-Object { Write-Log " $_" }
|
||||
}
|
||||
|
||||
# Plugin registry starts empty on a fresh box. apply-profile resolves the
|
||||
# declared set's hard-dependency closure and installs + enables it in
|
||||
# dependency order, idempotently (ADR-013). It fails loudly if the profile
|
||||
|
||||
Reference in New Issue
Block a user