144 lines
5.8 KiB
C#
144 lines
5.8 KiB
C#
using Sdt.Config;
|
|
using Sdt.Tui;
|
|
using Spectre.Console;
|
|
|
|
try
|
|
{
|
|
// ── Workspace + project discovery ────────────────────────────────────────
|
|
|
|
var workspaceResult = WorkspaceLoader.FindAndLoad();
|
|
var projectResult = ConfigLoader.FindAndLoad();
|
|
var cliArgs = Environment.GetCommandLineArgs().Skip(1).ToArray();
|
|
var forceInit = cliArgs.Any(a => string.Equals(a, "init", StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(a, "--init", StringComparison.OrdinalIgnoreCase));
|
|
|
|
if (forceInit)
|
|
{
|
|
var scan = ConfigBootstrapper.Scan(Directory.GetCurrentDirectory());
|
|
var generated = ConfigBootstrapper.BuildDefaultConfig(scan);
|
|
var path = ConfigBootstrapper.WriteDefaultConfig(scan.ProjectRoot, generated, overwrite: false);
|
|
AnsiConsole.MarkupLine(Theme.Ok($"Initialized config at {path}"));
|
|
projectResult = ConfigLoader.FindAndLoad(scan.ProjectRoot);
|
|
}
|
|
|
|
if (projectResult is null)
|
|
{
|
|
AnsiConsole.MarkupLine($"[bold {Theme.Red}]SDT:[/] [{Theme.Amber}]No devtool.json found[/] in current directory or any parent.");
|
|
var bootstrap = AnsiConsole.Confirm(
|
|
$"[{Theme.Amber}]Generate a default devtool.json for this project now?[/]",
|
|
defaultValue: true);
|
|
if (!bootstrap)
|
|
{
|
|
AnsiConsole.MarkupLine(Theme.Faint("Create a devtool.json in your project root to get started."));
|
|
return 1;
|
|
}
|
|
|
|
var scan = ConfigBootstrapper.Scan(Directory.GetCurrentDirectory());
|
|
AnsiConsole.MarkupLine(Theme.Faint($"Detected project root: {scan.ProjectRoot}"));
|
|
if (scan.ToolFamilies.Count > 0)
|
|
AnsiConsole.MarkupLine(Theme.Faint($"Detected tool families: {string.Join(", ", scan.ToolFamilies)}"));
|
|
|
|
var generated = ConfigBootstrapper.BuildDefaultConfig(scan);
|
|
var preview = ConfigBootstrapper.ToJson(generated);
|
|
AnsiConsole.Write(new Panel(Markup.Escape(preview)).Header("Generated devtool.json preview").BorderStyle(Theme.DimStyle));
|
|
|
|
var confirmWrite = AnsiConsole.Confirm(
|
|
$"[{Theme.Amber}]Write generated devtool.json to {scan.ProjectRoot}?[/]",
|
|
defaultValue: true);
|
|
if (!confirmWrite)
|
|
return 1;
|
|
|
|
var path = ConfigBootstrapper.WriteDefaultConfig(scan.ProjectRoot, generated, overwrite: false);
|
|
AnsiConsole.MarkupLine(Theme.Ok($"Created {path}"));
|
|
projectResult = ConfigLoader.FindAndLoad(scan.ProjectRoot);
|
|
if (projectResult is null)
|
|
{
|
|
AnsiConsole.MarkupLine(Theme.Fail("Generated config could not be reloaded."));
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// ── Main run loop (handles workspace project switching) ────────────────
|
|
|
|
var currentLoaded = projectResult;
|
|
var (workspace, workspaceRoot) = workspaceResult.HasValue
|
|
? (workspaceResult.Value.Config, workspaceResult.Value.WorkspaceRoot)
|
|
: ((WorkspaceConfig?)null, (string?)null);
|
|
|
|
while (true)
|
|
{
|
|
var app = new App(currentLoaded.Config, currentLoaded.ProjectRoot, currentLoaded.Warnings, workspace, workspaceRoot);
|
|
var result = await app.RunAsync();
|
|
|
|
if (result.Reason == AppExitReason.Quit)
|
|
break;
|
|
|
|
// User switched projects — reload config from new root
|
|
if (result.Reason == AppExitReason.SwitchProject && result.NewProjectRoot is not null)
|
|
{
|
|
LoadedProjectConfig? loaded;
|
|
try
|
|
{
|
|
loaded = ConfigLoader.FindAndLoad(result.NewProjectRoot);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AnsiConsole.MarkupLine(Theme.Fail(ex.Message));
|
|
AnsiConsole.MarkupLine(Theme.Faint("Press any key to stay on current project..."));
|
|
Console.ReadKey(intercept: true);
|
|
continue;
|
|
}
|
|
if (loaded is null)
|
|
{
|
|
AnsiConsole.MarkupLine(Theme.Fail($"No devtool.json found at: {result.NewProjectRoot}"));
|
|
AnsiConsole.MarkupLine(Theme.Faint("Press any key to stay on current project..."));
|
|
Console.ReadKey(intercept: true);
|
|
continue; // go back to current app
|
|
}
|
|
|
|
currentLoaded = loaded;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var message = ex.Message;
|
|
var isExpectedMigrationError =
|
|
ex is InvalidOperationException &&
|
|
message.Contains("Legacy targets-only config detected", StringComparison.OrdinalIgnoreCase);
|
|
|
|
AnsiConsole.MarkupLine(Theme.Fail($"Fatal: {message}"));
|
|
if (isExpectedMigrationError)
|
|
{
|
|
var configPath = ConfigLoader.FindConfigPath();
|
|
if (!string.IsNullOrWhiteSpace(configPath))
|
|
{
|
|
var migrate = AnsiConsole.Confirm(
|
|
$"[{Theme.Amber}]Apply automatic migration now (creates backup + converts targets -> workflows)?[/]",
|
|
defaultValue: true);
|
|
if (migrate)
|
|
{
|
|
var result = ConfigLoader.ApplyLegacyTargetMigration(configPath, createBackup: true);
|
|
if (result.Success)
|
|
{
|
|
AnsiConsole.MarkupLine(Theme.Ok("Migration applied successfully."));
|
|
if (!string.IsNullOrWhiteSpace(result.BackupPath))
|
|
AnsiConsole.MarkupLine(Theme.Faint($"Backup: {result.BackupPath}"));
|
|
AnsiConsole.MarkupLine(Theme.Faint("Run sdt.exe again in strict mode."));
|
|
}
|
|
else
|
|
{
|
|
AnsiConsole.MarkupLine(Theme.Fail($"Migration failed: {result.Message}"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isExpectedMigrationError)
|
|
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything);
|
|
|
|
return 1;
|
|
}
|