feat: make LegacyParityApp playout live-only

This commit is contained in:
2026-07-28 14:52:59 +09:00
parent 11d3849933
commit ea96a08ad5
23 changed files with 456 additions and 270 deletions

View File

@@ -14,6 +14,10 @@ public partial class App : Application
internal static bool IsDevelopmentLiveLaunchRequested { get; private set; }
internal static string? DevelopmentLiveStartupFailureCode { get; private set; }
internal static string? DevelopmentLiveStartupFailureMessage { get; private set; }
public App()
{
InitializeComponent();
@@ -43,40 +47,31 @@ public partial class App : Application
_mainInstance = registeredInstance;
_mainInstance.Activated += OnMainInstanceActivated;
#if DEBUG
const bool isDebugBuild = true;
#else
const bool isDebugBuild = false;
#endif
#if SOURCE_ONLY_RUNTIME
const bool isSourceOnlyBuild = true;
#else
const bool isSourceOnlyBuild = false;
#endif
var launchArguments = isSourceOnlyBuild
? null
: DevelopmentLiveLaunchBootstrap.ResolveLaunchArguments(
args.Arguments,
Environment.GetCommandLineArgs());
IsDevelopmentLiveLaunchRequested = string.Equals(
launchArguments,
DevelopmentLiveLaunchBootstrap.ExactLaunchArgument,
StringComparison.Ordinal);
var developmentLive = DevelopmentLiveLaunchBootstrap.TryApply(
launchArguments,
isDebugBuild);
IsDevelopmentLiveLaunch = developmentLive.IsApplied;
System.Diagnostics.Debug.WriteLine(
$"Development Live bootstrap: {developmentLive.Code}");
// A fresh clone cannot become a full runtime inside the already compiled
// source-only process. Show only the native two-folder setup window; the
// reviewed initializer builds the verified runtime for the next F5.
var showFirstRunSetup = isSourceOnlyBuild && isDebugBuild;
_window = showFirstRunSetup
? new FirstRunSetupWindow()
: new MainWindow();
#if SOURCE_ONLY_RUNTIME
IsDevelopmentLiveLaunchRequested = false;
IsDevelopmentLiveLaunch = false;
DevelopmentLiveStartupFailureCode = null;
DevelopmentLiveStartupFailureMessage = null;
_window = new FirstRunSetupWindow();
#else
var launchArguments = DevelopmentLiveLaunchBootstrap.ResolveLaunchArguments(
args.Arguments,
Environment.GetCommandLineArgs());
var developmentLive = DevelopmentLiveLaunchBootstrap.TryApply(launchArguments);
IsDevelopmentLiveLaunchRequested = developmentLive.IsRequested;
IsDevelopmentLiveLaunch = developmentLive.IsApplied;
DevelopmentLiveStartupFailureCode = developmentLive.IsLaunchAllowed
? null
: developmentLive.Code;
DevelopmentLiveStartupFailureMessage = developmentLive.FailureMessage;
System.Diagnostics.Debug.WriteLine(
$"Development Live bootstrap: {developmentLive.Code}");
_window = new MainWindow();
#endif
_window.Activate();
}

View File

@@ -205,7 +205,7 @@
Text="External legacy runtime paths are enabled. Cuts files remain in the selected folder and database credentials remain in the current user's LocalAppData overlay." />
<Error Condition="'$(LegacyRuntimeAssetsValidationEnabled)' == 'true' and
!Exists('$(LegacyRuntimeSourceRoot)')"
Text="Legacy runtime source root was not found: $(LegacyRuntimeSourceRoot). Run scripts\Initialize-ExistingDevelopmentPc.ps1 to create the verified local Required binding, or use /p:LegacyRuntimeAssetsMode=Auto for a source-only DryRun build." />
Text="Legacy runtime source root was not found: $(LegacyRuntimeSourceRoot). Run scripts\Initialize-ExistingDevelopmentPc.ps1 to create the verified local Required binding, or use /p:LegacyRuntimeAssetsMode=Auto for a source-only setup build." />
<Error Condition="'$(LegacyRuntimeAssetsValidationEnabled)' == 'true' and
!Exists('$(LegacyCutsSourceRoot)')"
Text="Legacy Cuts source directory was not found: $(LegacyCutsSourceRoot)." />

View File

@@ -32,21 +32,33 @@ public sealed partial class MainWindow
{
try
{
if (_developmentLiveStartupBlocked)
{
_playoutInitializationError =
App.DevelopmentLiveStartupFailureMessage ??
"Live playout startup validation failed.";
return;
}
var explicitLocalConfigurationExists = File.Exists(
PlayoutOptionsLoader.DefaultPath);
_playoutOptions =
_isDevelopmentLiveLaunch &&
!_developmentLiveStartupBlocked
_isDevelopmentLiveLaunch
? PlayoutOptionsLoader.LoadDevelopmentLive(
baseDirectory: AppContext.BaseDirectory,
operatorSceneDirectory:
_appliedOperatorSettings.SceneDirectory)
: PlayoutOptionsLoader.Load(
operatorSceneDirectory: _appliedOperatorSettings.SceneDirectory,
operatorBackgroundDirectory: _appliedOperatorSettings.BackgroundDirectory,
forceSafeDryRun: IsSourceOnlyBuild ||
_isDevelopmentLiveLaunchRequested ||
_developmentLiveStartupBlocked);
operatorBackgroundDirectory:
_appliedOperatorSettings.BackgroundDirectory);
if (_playoutOptions.Mode != PlayoutMode.Live)
{
throw new PlayoutConfigurationException(
"The application requires a validated Live playout configuration. " +
"DryRun fallback is disabled.");
}
ResetRefreshState();
var startupComposition = LegacyParityStartupCompositionResolver.Resolve(
_playoutOptions,
@@ -75,6 +87,7 @@ public sealed partial class MainWindow
}
catch (Exception exception)
{
_developmentLiveStartupBlocked = true;
_playoutInitializationWarning = null;
_playoutInitializationError = exception is PlayoutConfigurationException
? exception.Message

View File

@@ -64,6 +64,9 @@ public sealed partial class MainWindow : Window
{
_isDevelopmentLiveLaunch = App.IsDevelopmentLiveLaunch;
_isDevelopmentLiveLaunchRequested = App.IsDevelopmentLiveLaunchRequested;
_developmentLiveStartupBlocked =
_isDevelopmentLiveLaunchRequested &&
!_isDevelopmentLiveLaunch;
// Capture and remove Gate A bearer material before XAML can create WebView2.
// Only the native process keeps the capability digest for this launch.
_playoutLaunchAuthorization =
@@ -119,9 +122,15 @@ public sealed partial class MainWindow : Window
IOperatorCatalogSchemaValidationService operatorCatalogSchemaValidationService;
CorePagePlanProvider? fixedPagePlanProvider = null;
string? initializationError = null;
var databaseInitializationSucceeded = false;
try
{
if (_developmentLiveStartupBlocked)
{
throw new InvalidOperationException(
App.DevelopmentLiveStartupFailureMessage ??
"Live playout startup validation failed.");
}
if (IsSourceOnlyBuild)
{
throw new InvalidOperationException(
@@ -198,14 +207,16 @@ public sealed partial class MainWindow : Window
new LegacyStockMasterIdentityValidationService(_databaseRuntime.Executor);
operatorCatalogSchemaValidationService =
new LegacyOperatorCatalogSchemaValidationService(_databaseRuntime.Executor);
databaseInitializationSucceeded = true;
}
catch
{
_databaseRuntime = null;
fixedPagePlanProvider = null;
initializationError = IsSourceOnlyBuild
? "소스 전용 DryRun에서는 데이터베이스 연결을 사용하지 않습니다."
initializationError = _developmentLiveStartupBlocked
? App.DevelopmentLiveStartupFailureMessage ??
"Live playout startup validation failed."
: IsSourceOnlyBuild
? "소스 전용 설정 빌드에서는 데이터베이스 연결을 사용하지 않습니다."
: "데이터베이스가 설정되지 않았습니다. 로컬 설정을 확인하세요.";
stockLookup = new UnavailableStockLookup(initializationError);
industrySelectionService = new UnavailableIndustrySelectionService(
@@ -241,14 +252,6 @@ public sealed partial class MainWindow : Window
DataQueryExecutor.Reset();
}
if (_isDevelopmentLiveLaunch && !databaseInitializationSucceeded)
{
// Development Live is one fail-closed startup transaction. A
// missing or partially initialized protected DB runtime must not
// leave the independently armed playout gate eligible for use.
_developmentLiveStartupBlocked = true;
}
_industryWorkflow = new LegacyIndustrySelectionWorkflow(
industrySelectionService,
actionLayout: runtimeUiCatalogs.IndustryLayout);

View File

@@ -3,10 +3,6 @@
"MBN_STOCK_WEBVIEW.LegacyParityApp - Development Live (Package)": {
"commandName": "MsixPackage",
"commandLineArgs": "--development-live"
},
"MBN_STOCK_WEBVIEW.LegacyParityApp - Explicit DryRun (Package)": {
"commandName": "MsixPackage",
"commandLineArgs": ""
}
}
}