Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open Solution From Command Line Support #3958

Merged
merged 8 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 148 additions & 23 deletions Ginger/Ginger/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ limitations under the License.
using Amdocs.Ginger.Common.Telemetry;
using Amdocs.Ginger.CoreNET.log4netLib;
using Amdocs.Ginger.CoreNET.RunLib;
using Amdocs.Ginger.CoreNET.RunLib.CLILib;
using Amdocs.Ginger.Repository;
using CommandLine;
using Ginger.BusinessFlowWindows;
using Ginger.ReporterLib;
using Ginger.SourceControl;
Expand All @@ -32,6 +34,7 @@ limitations under the License.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

Expand Down Expand Up @@ -240,47 +243,158 @@ public static void OnAutomateBusinessFlowEvent(AutomateEventArgs.eEventType even
}
}


CLIProcessor cliProcessor;
GokulBothe99 marked this conversation as resolved.
Show resolved Hide resolved

// Main entry point to Ginger UI/CLI
private void Application_Startup(object sender, StartupEventArgs e)
/// <summary>
/// Handles the startup sequence of the application. Initializes logging, workspace,
/// processes command-line arguments, and determines the running mode (UI or execution).
/// </summary>
private async void Application_Startup(object sender, StartupEventArgs e)
{
Amdocs.Ginger.CoreNET.log4netLib.GingerLog.InitLog4Net();
InitLogging();

bool startGrid = ShouldStartGrid(e.Args);
WorkSpace.Init(new WorkSpaceEventHandler(), startGrid);

var parserResult = ParseCommandLineArguments(e.Args);

bool startGrid = e.Args.Length == 0; // no need to start grid if we have args
WorkSpace.Init(new WorkSpaceEventHandler(), startGrid);
if (e.Args.Length != 0)
DoOptions doOptions = ExtractDoOptions(parserResult);

if (IsExecutionMode(e.Args, doOptions))
{
WorkSpace.Instance.RunningInExecutionMode = true;
Reporter.ReportAllAlsoToConsole = true; //needed so all reporting will be added to Console
Reporter.ReportAllAlsoToConsole = true;
}
// add additional classes from Ginger and GingerCore
InitClassTypesDictionary();

InitializeGingerCore();

if (!WorkSpace.Instance.RunningInExecutionMode)
{
ProcessGingerUIStartup(doOptions);
}
else
{
await RunNewCLI(parserResult);
}
}
GokulBothe99 marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Initializes the logging mechanism for the application using log4net.
/// </summary>
private void InitLogging()
{
Amdocs.Ginger.CoreNET.log4netLib.GingerLog.InitLog4Net();
}

/// <summary>
/// Determines whether the application should start with the grid view.
/// Returns true if there are no command-line arguments.
/// </summary>
/// <param name="args">Command-line arguments.</param>
/// <returns>True if no arguments are provided, otherwise false.</returns>
private bool ShouldStartGrid(string[] args)
{
return args.Length == 0;
}

/// <summary>
/// Parses command-line arguments and returns the result.
/// If no arguments are provided, returns null.
/// </summary>
/// <param name="args">Command-line arguments.</param>
/// <returns>ParserResult containing parsed arguments or null.</returns>
private ParserResult<object> ParseCommandLineArguments(string[] args)
{
cliProcessor = new CLIProcessor();
return args.Length != 0 ? cliProcessor.ParseArguments(args) : null;
}

/// <summary>
/// Extracts the DoOptions object from the parser result if available and the operation is 'open'.
/// Otherwise, returns null.
/// </summary>
/// <param name="parserResult">Parsed command-line arguments.</param>
/// <returns>DoOptions object or null.</returns>
private DoOptions ExtractDoOptions(ParserResult<object> parserResult)
{
if (parserResult?.Value is DoOptions tempOptions && tempOptions.Operation == DoOptions.DoOperation.open)
{
return tempOptions;
}
return null;
}

/// <summary>
/// Determines if the application is in execution mode based on command-line arguments and DoOptions.
/// Returns true if there are arguments and DoOptions is null.
/// </summary>
/// <param name="args">Command-line arguments.</param>
/// <param name="doOptions">DoOptions extracted from the parsed arguments.</param>
/// <returns>True if the application is in execution mode, otherwise false.</returns>
private bool IsExecutionMode(string[] args, DoOptions doOptions)
{
return args.Length != 0 && doOptions == null;
}

/// <summary>
/// Initializes various core components of Ginger such as class types, workspace, and telemetry.
/// Logs the startup information.
/// </summary>
private void InitializeGingerCore()
{
InitClassTypesDictionary();
WorkSpace.Instance.InitWorkspace(new GingerWorkSpaceReporter(), new DotNetFrameworkHelper());
WorkSpace.Instance.InitTelemetry();

Amdocs.Ginger.CoreNET.log4netLib.GingerLog.PrintStartUpInfo();
}

/// <summary>
/// Processes the startup for the Ginger UI. Initializes logging, checks the user profile settings,
/// hides the console window, and loads the last solution if applicable.
/// </summary>
/// <param name="doOptions">DoOptions object containing user-specific startup options.</param>
private void ProcessGingerUIStartup(DoOptions doOptions)
{
if (WorkSpace.Instance.UserProfile != null && WorkSpace.Instance.UserProfile.AppLogLevel == eAppReporterLoggingLevel.Debug)
{
GingerLog.StartCustomTraceListeners();
}

HideConsoleWindow();
bool CheckAutoLoadSolution = false;

if (!WorkSpace.Instance.RunningInExecutionMode)
try
{
if (WorkSpace.Instance.UserProfile != null && WorkSpace.Instance.UserProfile.AppLogLevel == eAppReporterLoggingLevel.Debug)
if (WorkSpace.Instance.UserProfile != null)
{
CheckAutoLoadSolution = WorkSpace.Instance.UserProfile.AutoLoadLastSolution;
}

if (doOptions != null)
{
WorkSpace.Instance.UserProfile.AutoLoadLastSolution = false;
}

StartGingerUI();

if (doOptions != null && !string.IsNullOrWhiteSpace(doOptions.Solution))
{
GingerLog.StartCustomTraceListeners();
DoOptionsHandler.Run(doOptions);
}
HideConsoleWindow();
StartGingerUI();// start regular Ginger UI
}
else
finally
{
RunNewCLI(e.Args);
if (doOptions != null)
{
WorkSpace.Instance.UserProfile.AutoLoadLastSolution = CheckAutoLoadSolution;
}
GokulBothe99 marked this conversation as resolved.
Show resolved Hide resolved
}
}




[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();

Expand All @@ -302,13 +416,24 @@ public static void ShowConsoleWindow()
ShowWindow(handle, SW_SHOW);
}

private async void RunNewCLI(string[] args)
private async Task RunNewCLI(ParserResult<object> parserResult)
{
CLIProcessor cLIProcessor = new CLIProcessor();
await cLIProcessor.ExecuteArgs(args);

// do proper close !!!
System.Windows.Application.Current.Shutdown(Environment.ExitCode);
try
{
if (parserResult != null)
{
await cliProcessor.ProcessParsedArguments(parserResult);
}
}
catch (Exception ex)
{
Reporter.ToLog(eLogLevel.ERROR, "Error occurred while processing command-line arguments", ex);
}
finally
{
System.Windows.Application.Current.Shutdown(Environment.ExitCode);
}

}

public void StartGingerUI()
Expand Down
31 changes: 24 additions & 7 deletions Ginger/GingerCoreNET/RunLib/CLILib/CLIProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,30 @@ public async Task ExecuteArgs(string[] args)
args = newArgs;
}

await ParseArgs(args);
ParserResult<object> parserResult = args.Length != 0 ? ParseArguments(args) : null;
await ProcessParsedArguments(parserResult);

}

///<summary>
/// Parses the command line arguments and returns the parsed result.
/// </summary>
/// <param name="args">The command line arguments.</param>
/// <returns>The parsed result of the command line arguments.</returns>
public ParserResult<object> ParseArguments(string[] args)
{
var parser = new Parser(settings =>
{
settings.IgnoreUnknownArguments = true;
});

return parser.ParseArguments<RunOptions, GridOptions, ConfigFileOptions, DynamicOptions, ScriptOptions, SCMOptions, VersionOptions, ExampleOptions, DoOptions>(args);
}



private async Task ParseArgs(string[] args)

public async Task ProcessParsedArguments(ParserResult<object> parserResult)
{
// FIXME: failing with exc of obj state
// Do not show default version
Expand All @@ -72,17 +90,16 @@ private async Task ParseArgs(string[] args)
settings.IgnoreUnknownArguments = true;
});

int result = await parser.ParseArguments<RunOptions, GridOptions, ConfigFileOptions, DynamicOptions, ScriptOptions, SCMOptions, VersionOptions, ExampleOptions, DoOptions>(args).MapResult(
int result = await parserResult.MapResult(
GokulBothe99 marked this conversation as resolved.
Show resolved Hide resolved
async (RunOptions opts) => await HandleRunOptions(opts),
async (GridOptions opts) => await HanldeGridOption(opts),
async (GridOptions opts) => await HandleGridOption(opts),
async (ConfigFileOptions opts) => await HandleFileOptions("config", opts.FileName, opts.VerboseLevel),
async (DynamicOptions opts) => await HandleFileOptions("dynamic", opts.FileName, opts.VerboseLevel),
async (ScriptOptions opts) => await HandleFileOptions("script", opts.FileName, opts.VerboseLevel),
async (VersionOptions opts) => await HandleVersionOptions(opts),
async (ExampleOptions opts) => await HandleExampleOptions(opts),
async (DoOptions opts) => await HandleDoOptions(opts),


async errs => await HandleCLIParseError(errs)
);

Expand All @@ -99,7 +116,7 @@ private async Task<int> HandleDoOptions(DoOptions opts)
{
try
{
DoOptionsHanlder.Run(opts);
DoOptionsHandler.Run(opts);
return 0;
}
catch (Exception ex)
Expand Down Expand Up @@ -325,7 +342,7 @@ private async Task<int> HandleFileOptions(string fileType, string fileName, eVer
}


private async Task<int> HanldeGridOption(GridOptions gridOptions)
private async Task<int> HandleGridOption(GridOptions gridOptions)
{
WorkSpace.Instance.GingerCLIMode = eGingerCLIMode.grid;
return await Task.Run(() =>
Expand Down
3 changes: 2 additions & 1 deletion Ginger/GingerCoreNET/RunLib/CLILib/DoOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public enum DoOperation
{
analyze,
info,
clean
clean,
open
GokulBothe99 marked this conversation as resolved.
Show resolved Hide resolved
}

[Option('o', "operation", Required = true, HelpText = "Select operation to run on solution")]
Expand Down
47 changes: 45 additions & 2 deletions Ginger/GingerCoreNET/RunLib/CLILib/DoOptionsHanlder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ limitations under the License.
using Ginger.AnalyzerLib;
using GingerCore;
using System;
using System.IO;
using System.Text;

namespace Amdocs.Ginger.CoreNET.RunLib.CLILib
{
class DoOptionsHanlder
public static class DoOptionsHandler
{
internal static void Run(DoOptions opts)
public static void Run(DoOptions opts)
{
switch (opts.Operation)
{
Expand All @@ -40,6 +41,9 @@ internal static void Run(DoOptions opts)
case DoOptions.DoOperation.info:
DoInfo(opts.Solution);
break;
case DoOptions.DoOperation.open:
DoOpen(opts.Solution);
break;
}
}

Expand All @@ -57,6 +61,45 @@ private static void DoInfo(string solution)
Reporter.ToLog(eLogLevel.INFO, stringBuilder.ToString());
}

private static void DoOpen(string solutionFolder)
{
try
{
// Check if solutionFolder is null or empty
if (string.IsNullOrWhiteSpace(solutionFolder))
{
Reporter.ToLog(eLogLevel.ERROR, "The provided solution folder path is null or empty.");
return;
}

// Check if the folder path contains the solution file name
if (solutionFolder.Contains("Ginger.Solution.xml"))
{
solutionFolder = Path.GetDirectoryName(solutionFolder)?.Trim() ?? string.Empty;

if (string.IsNullOrEmpty(solutionFolder))
{
Reporter.ToLog(eLogLevel.ERROR, "Invalid solution folder path derived from the solution file.");
return;
}
}

// Check if the directory exists
if (!Directory.Exists(solutionFolder))
{
Reporter.ToLog(eLogLevel.ERROR, $"The provided folder path '{solutionFolder}' does not exist.");
return;
}

// Attempt to open the solution
WorkSpace.Instance.OpenSolution(solutionFolder);
}
catch (Exception ex)
{
// Handle any other unexpected errors
Reporter.ToLog(eLogLevel.ERROR, $"An unexpected error occurred while opening the solution in folder '{solutionFolder}'. Error: {ex.Message}");
}
}
private static void DoAnalyze(string solution)
{
WorkSpace.Instance.OpenSolution(solution);
Expand Down
Loading