merging master to official 202.5 RC#4388
Conversation
…rent repo wont build
"Value" that allows the user to change the current value of a variable, this function now works for string variables
…, currently working on date variables
…ports date time variables.
…umn is now hidden aswell
…able_customization_simplification
…ficial-Releases/Official-Release-2025.4 Releases/published official releases/official release 2025.4
…n with mapped output type value
…ease Handled infinte waiting issue with Log Appender
…ficial-Releases/Official-Release-2025.4 Releases/published official releases/official release 2025.4
…tomization_simplification
…//github.com/Ginger-Automation/Ginger into runset_variable_customization_simplification
1- fixed loading of runset config having new Ginger Play configs 2- fixing Ginger Play pop up which hide behind app 3- improving Ginger Play icon style 4- aligning Ginger play icon in diffrent pages
MSSQL Fix Test
Not using GetConnectionString method
…tomation/Ginger into BugFix/JavaExplorerFix
…controlled-command-line Do not assign the Expression to mValueCalculated if it was failed to …
Bug fix/java explorer fix
…Issue BugFix/StoretoEnabled Issue solved
Upgraded Magick.NET-Q16-AnyCPU nugget to 14.10.0
…ilityIssueForIOS Mobile accessibility issue for ios - fix
WalkthroughThis PR performs a major refactoring of the Ginger automation codebase, including migration of console driver functionality from GingerCore to GingerCoreNET namespace, removal of legacy Java agent implementation files, deletion of HTML-based execution reports, enhancement of variable mapping with a new Value output type and CurrentEffectiveValue property, refactoring of database connection string handling, and various UI updates across actions and variable pages. The PR also updates project dependencies and version metadata. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 33
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (14)
Ginger/Ginger/ApplicationModelsLib/POMModels/POMWizardLib/LearnWizard/POMLearnConfigWizardPage.xaml (1)
74-88: UI improvement enhances clarity.The addition of an explicit "AI Integration Options" label improves the discoverability and understanding of the AI features section. The horizontal layout with the icon is clean and consistent with modern UI patterns.
Optional: Consider localization for the label text
While the hard-coded "AI Integration Options" text is consistent with other labels in this file (e.g., "Page Elements Setup Method:", "Elements Learning Settings:"), consider using a localized resource for better internationalization support:
-<TextBlock - Text="AI Integration Options" +<TextBlock + Text="{Binding Source={StaticResource AIIntegrationOptionsLabel}, Path=Content}"This is a minor suggestion since the file currently uses mixed approaches for label text.
Ginger/Ginger/UserControlsLib/TwoLevelMenuLib/TwoLevelMenuPage.xaml.cs (1)
248-268: Avoid duplicate method call to improve performance.The method
GingerPlayUtils.GetGingerPlayGatewayURLIfConfigured()is called twice: once in the guard condition (Line 248) and again when constructing the URL (Line 254). Store the result in a local variable to avoid redundant calls.🔎 Proposed refactor to cache the gateway URL
- if (GingerPlayUtils.IsGingerPlayEnabled() && !string.IsNullOrEmpty(GingerPlayUtils.GetGingerPlayGatewayURLIfConfigured())) + string gatewayUrl = GingerPlayUtils.GetGingerPlayGatewayURLIfConfigured(); + if (GingerPlayUtils.IsGingerPlayEnabled() && !string.IsNullOrEmpty(gatewayUrl)) { try { Process.Start(new ProcessStartInfo { - FileName = GingerPlayUtils.GetGingerPlayGatewayURLIfConfigured() + "gingerplay/#/playHome", + FileName = gatewayUrl + "gingerplay/#/playHome", UseShellExecute = true }); } catch (Exception ex) { MessageBox.Show("Unable to open the link: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } }Ginger/Ginger/WizardLib/WizardWindow.xaml.cs (1)
491-491: Use ERROR log level for exception logging.Per coding guidelines, exceptions should be logged using
eLogLevel.ERRORrather thaneLogLevel.DEBUG.As per coding guidelines, use
Reporter.ToLog(eLogLevel.ERROR, message)for logging errors.🔎 Proposed fixes
catch (Exception ex) { - Reporter.ToLog(eLogLevel.DEBUG, "Error while starting the timer", ex); + Reporter.ToLog(eLogLevel.ERROR, "Error while starting the timer", ex); }catch (Exception ex) { - Reporter.ToLog(eLogLevel.DEBUG, "Error while stopping the timer", ex); + Reporter.ToLog(eLogLevel.ERROR, "Error while stopping the timer", ex); }Also applies to: 520-520
Ginger/GingerCore/GeneralLib/General.cs (1)
397-403: Consider adding defensive null check for consistency.The else branch could benefit from similar defensive coding. On line 399,
propertyInfo.GetValue(comboBox.Items[i])could return null, and calling.ToString()on a null value would throw aNullReferenceException.🔎 Proposed fix for defensive null handling
else { PropertyInfo propertyInfo = (comboBox.Items[i]).GetType().GetProperty(specificValueField); - if (propertyInfo != null && propertyInfo.GetValue((comboBox.Items[i])).ToString() == value) + if (propertyInfo != null) { - return true; + var propertyValue = propertyInfo.GetValue(comboBox.Items[i]); + if (propertyValue != null && propertyValue.ToString() == value) + { + return true; + } } }Ginger/GingerCoreCommon/VariablesLib/VariableDynamic.cs (1)
117-132: Inconsistency betweenSupportSetValueandSetValueimplementation.Setting
SupportSetValuetofalse(line 117) while keeping aSetValuemethod that returnstrue(lines 128-132) creates an inconsistency:
- The base class
SetValuewill returnfalsewithout calling this override whenSupportSetValueisfalseGetSupportedOperations()throwsNotImplementedException(lines 123-126) instead of returning an empty list or appropriate operationsIf
SetValueis truly not supported, consider:
- Returning an empty list from
GetSupportedOperations()instead of throwing- Or removing/adjusting the
SetValueoverride to align with the flag🔎 Suggested fix for GetSupportedOperations
public override List<VariableBase.eSetValueOptions> GetSupportedOperations() { - throw new NotImplementedException(); + // SetValue is not supported for dynamic variables, only ResetValue + return [VariableBase.eSetValueOptions.ResetValue]; }Ginger/GingerCore/Drivers/MainFrame/Terminal.cs (1)
41-50: Remove unused constructor parameters.The
RowsCountandColumnsCountparameters are accepted in the constructor but never used in the body. Removing them will improve code clarity. Update the constructor signature and the call site inMainFrameDriver.csline 174.Ginger/Ginger/Drivers/DriversWindows/DriverWindowHandler.cs (1)
51-60: Consider adding null checks for defensive coding.The dynamic type resolution logic relies on several chained property accesses that could result in
NullReferenceException:
agentOperations.Agentmay be null if the cast on line 52 succeeds but the object'sAgentproperty is nullGetDriverWindowName()could return null or empty, resulting in an invalid type name"Ginger.Drivers.DriversWindows."While the outer try-catch handles exceptions, adding explicit null checks would provide clearer error messages.
🔎 Proposed defensive checks
DriverBase driver = args.Driver; AgentOperations agentOperations = (AgentOperations)args.DataObject; +if (agentOperations?.Agent == null) +{ + throw new InvalidOperationException("AgentOperations or Agent is null"); +} -string classname = "Ginger.Drivers.DriversWindows." + ((IDriverWindow)driver).GetDriverWindowName(agentOperations.Agent.DriverType); +string windowName = ((IDriverWindow)driver).GetDriverWindowName(agentOperations.Agent.DriverType); +if (string.IsNullOrEmpty(windowName)) +{ + throw new InvalidOperationException($"Driver window name not found for DriverType: {agentOperations.Agent.DriverType}"); +} +string classname = "Ginger.Drivers.DriversWindows." + windowName;Ginger/GingerCoreCommon/GingerCoreCommon.csproj (1)
38-38: Remove duplicate LiteDB reference with conflicting version.The project declares LiteDB 5.0.21 via PackageReference (line 38) but also includes an explicit assembly Reference pointing to LiteDB 4.1.4 (lines 62–64). The assembly reference takes precedence at runtime, causing a mismatch between the declared and actual dependency. Mixing LiteDB 4.1.4 with 5.0.x causes breaking changes. Remove the assembly reference at lines 62–64.
Ginger/GingerCore/Drivers/JavaDriverLib/JavaDriver.cs (1)
2741-2757: Still a potential NRE onElementInfo.ElementType.Contains("JEditor")You’ve added null checks for
ElementInfo.ElementTypein several JEditor branches, but the guard at Line 2743 still calls.Contains("JEditor")without a null-check:if (ElementInfo.XPath != "/" && !ElementInfo.ElementType.Contains("JEditor")) //?????????If
ElementTypeis ever null (which the other new guards suggest is possible), this path will throw. Consider tightening this condition:Proposed fix
- if (ElementInfo.XPath != "/" && !ElementInfo.ElementType.Contains("JEditor"))//????????? + if (ElementInfo.XPath != "/" && + !string.IsNullOrEmpty(ElementInfo.ElementType) && + !ElementInfo.ElementType.Contains("JEditor"))Or equivalently using the null-propagation operator:
if (ElementInfo.XPath != "/" && !(ElementInfo.ElementType?.Contains("JEditor") ?? false)) { ... }Ginger/GingerCoreNET/ActionsLib/RobotFramework/ActRobot.cs (1)
92-121: Password variables are correctly excluded, but JSON is still built unsafely by handThe new
Where(vrb => vrb is not VariablePasswordString)filters for solution, business-flow, and activity scopes do the right thing in terms of not leaking password variables into the Robot JSON parameter file. The duplicate-key check also ensures BF/activity vars don’t override each other silently.However,
WriteVariablesToJSONFile_V2still concatenates JSON manually:sbr.Append("\""); sbr.Append(paramLine.key); sbr.Append("\""); ... sbr.Append(paramLine.value);If any key or value contains
",\, newlines, etc., the generated JSON can become invalid or misinterpreted. Since you’re already assembling aList<GingerParam>, it would be safer and simpler to serialize that list with a JSON serializer and write the resulting string, instead of manual concatenation.Consider refactoring
WriteVariablesToJSONFile_V2to serializegingerParamsLstvia a JSON library (or at least centralize proper escaping) so special characters are handled correctly.Also applies to: 123-166
Ginger/GingerCore/Actions/ActLaunchJavaWSApplication.cs (1)
552-627: Jar selection by Java version is good; re-check instrumentation detection withJavaAgent_V25.jarThe new logic in
ValidateArgumentsthat:
- Gets
java.exe’s version,- Maps
majorVersion <= 8→GingerAgent.jarandmajorVersion > 8→JavaAgent_V25.jar, and- Verifies the chosen jar exists,
is a solid way to split old vs. new JVM behavior and fail fast when the agent jar is missing.
In
PerformAttachGingerAgent, for Java 8 and below you still passAgentJarPath=<...>\GingerAgent.jarwhile actually running-jar "<...>\GingerAgentStarter.jar", which matches the historical pattern. For newer JVMs, bothAgentJarPathand-jaruseJavaAgent_V25.jar.One thing to double-check:
IsInstrumentationModuleLoadedstill looks only for"GingerAgent.jar"viahandle.exe:var processHandle = Process.Start(new ProcessStartInfo() { FileName = handleExePath, Arguments = String.Format(" -accepteula -p {0} GingerAgent.jar", id), ... }); ... if (cliOut.Contains("GingerAgent.jar")) { return true; }If, in the Java > 8 path, only
JavaAgent_V25.jarends up loaded into the target process andGingerAgent.jaris never present, this check will always returnfalse. That will:
- Make the 30s wait loop after
mAttachAgentTask.Wait()always run to timeout, and- Prevent you from skipping already-instrumented processes during
WaitForAppWindowTitle.Consider either:
- Ensuring
JavaAgent_V25.jarstill loadsGingerAgent.jarinto the target process, or- Updating
IsInstrumentationModuleLoadedto look for both jar names (or a more generic token) so detection works for the new agent as well.Functionally attach still proceeds, but this may degrade UX and the reliability of “already attached” detection.
Also applies to: 835-842, 1225-1233
Ginger/GingerCore/GingerCore.csproj (1)
3-4: Project settings and Open3270 reference look consistent; consider future framework alignment
- Turning off
EnableDefaultCompileItemsandenableDefaultPageItemsmatches the explicit<Compile>and<Page>includes defined later in the file; just keep in mind that any new source/XAML files must be added to the project file explicitly.- The simplified
Open3270reference withHintPath="..\Ginger\DLLs\Open3270.dll"is reasonable as long as that DLL location is kept in sync with your packaging/runtime layout.Separately, this project still targets
net8.0-windows7.0while the current guideline is to align on .NET 8 with Windows 10.0.17763.0 compatibility. If Windows 7 support is no longer required, consider moving tonet8.0-windows10.0.17763.0in a dedicated compatibility/infra PR.Also applies to: 30-31, 92-94
Ginger/Ginger/Actions/ActionEditPage.xaml.cs (1)
1041-1101: Guard against nullActionOutputValueUserPreferencesto avoid NRE
columnPreferencesis nullable but used without a null‑check:
- Inside the loop you’re protected by
try/catch, but at Line 1095 (descriptionCheckBox.IsChecked = columnPreferences.Contains("Description", StringComparison.OrdinalIgnoreCase)) a nullActionOutputValueUserPreferenceswill throw aNullReferenceExceptionand can break the Action Edit page for users with no saved preferences.Consider normalizing
columnPreferencesonce when you read it, e.g.:Proposed fix
- private string? columnPreferences; + private string? columnPreferences; ... - columnPreferences = WorkSpace.Instance.UserProfile.ActionOutputValueUserPreferences; + columnPreferences = WorkSpace.Instance.UserProfile.ActionOutputValueUserPreferences ?? string.Empty; ... - CheckBox descriptionCheckBox = new CheckBox - { - Content = "Description", - IsChecked = columnPreferences.Contains("Description", StringComparison.OrdinalIgnoreCase) - }; + CheckBox descriptionCheckBox = new CheckBox + { + Content = "Description", + IsChecked = columnPreferences.Contains("Description", StringComparison.OrdinalIgnoreCase) + };(Or add a local
var prefs = columnPreferences ?? string.Empty;and useprefsfor all.Contains(...)calls.)Also applies to: 1163-1221
Ginger/GingerCore/Drivers/MainFrame/MainFrameDriver.cs (1)
123-138: Consider consistent Dispatcher/status behavior on connection failuresThe new flow improves robustness (
isConnectednull‑checksMFE, and failures are logged), but there are two edge cases worth verifying:
- If
IsServerListeningis false,Launchdriverlogs a status and returns without ever settingDispatcheror raisingDriverStatusChanged.- If
ConnectToMainframe()returnsfalse,mDriverWindowis nulled but again no dispatcher is assigned and no status/event is raised.If any caller assumes
Dispatcheris always non‑null afterStartDriver()(even when startup fails), or relies on aDriverStatusChangedevent to detect failure, these paths could cause subtle issues.You may want to:
- Assign
Dispatcher = new DummyDispatcher()before early returns, and/or- Invoke
OnDriverMessage(eDriverMessageType.DriverStatusChanged)on failure as well, to keep behavior consistent with the success and exception paths.Also applies to: 163-208
♻️ Duplicate comments (1)
Ginger/GingerCoreNET/Database/DatabaseOperations.cs (1)
372-372: Pre-existing security scan finding - not introduced by current changes.The resource injection finding at line 372 relates to connection string construction from database configuration values. This is inherent to database connection operations and not introduced by the current refactoring.
| # Ginger Automation AI Coding Agent Guide | ||
|
|
||
| ## Project Overview | ||
| Ginger is a comprehensive automation IDE supporting multiple platforms (Web, Mobile, APIs, Java, Windows, PowerBuilder, Linux/Unix, Mainframe). It uses a drag-and-drop approach enabling users with or without coding skills to create automation flows. | ||
|
|
||
| ## Core Architecture Concepts | ||
|
|
||
| ### 1. Execution Hierarchy | ||
| The fundamental execution structure follows this hierarchy: | ||
| ``` | ||
| Solution → RunSet → Runner → BusinessFlow → Activity → Action (Act) | ||
| ``` | ||
|
|
||
| - **BusinessFlow**: Top-level test scenario container (`GingerCoreCommon/Repository/BusinessFlowLib/BusinessFlow.cs`) | ||
| - **Activity**: Group of related actions within a BusinessFlow (`GingerCoreCommon/Repository/BusinessFlowLib/Activity.cs`) | ||
| - **Act/Action**: Individual executable step (`GingerCoreCommon/Actions/Act.cs`) | ||
| - **Agent**: Driver wrapper that connects to target applications (`GingerCoreCommon/RunLib/Agent.cs`) | ||
|
|
||
| ### 2. Key Project Structure | ||
| - **Ginger/**: Main WPF application (UI) | ||
| - **GingerCoreCommon/**: Shared models and base classes | ||
| - **GingerCoreNET/**: .NET execution engine and CLI components | ||
| - **GingerRuntime/**: Console application entry point | ||
| - **GingerCore/**: Legacy core components (being migrated to GingerCoreNET) | ||
|
|
||
| ### 3. Driver Architecture | ||
| Each platform has dedicated drivers under `GingerCore/Drivers/`: | ||
| - `SeleniumDriver` for web automation | ||
| - `JavaDriver` for Java applications | ||
| - `ConsoleDriverLib` for command-line interfaces | ||
| - `WindowsAutomation` for Windows applications | ||
| - Agents (`Agent.cs`) act as wrappers providing unified interface | ||
|
|
||
| ## Development Workflows | ||
|
|
||
| ### Building & Testing | ||
| ```powershell | ||
| # Main build (Windows) | ||
| dotnet build Ginger/Ginger.sln | ||
|
|
||
| # Run tests | ||
| ./TestDotNetFramework.ps1 # Framework tests | ||
| ./CLITests.ps1 # CLI functionality tests | ||
|
|
||
| # Console execution | ||
| cd Ginger/GingerRuntime/bin/Release/net8.0/publish/ | ||
| dotnet GingerConsole.dll run -s "path/to/solution" -e "Environment" -r "RunSet" | ||
| ``` | ||
|
|
||
| ### CLI Commands | ||
| **Windows:** | ||
| - `ginger.exe help` or `dotnet ginger.dll help` - Show CLI help | ||
| - `ginger.exe run -s <solution> -e <env> -r <runset>` - Execute automation | ||
| - `ginger.exe analyze -s <solution>` - Analyze solution | ||
| - `ginger.exe grid --port <port>` - Start grid mode | ||
|
|
||
| **Linux:** | ||
| - `dotnet gingerruntime.dll help` - Show CLI help | ||
| - `dotnet gingerruntime.dll run -s <solution> -e <env> -r <runset>` - Execute automation | ||
| - `dotnet gingerruntime.dll analyze -s <solution>` - Analyze solution | ||
| - `dotnet gingerruntime.dll grid --port <port>` - Start grid mode | ||
|
|
||
| ## Project Conventions | ||
|
|
||
| ### 1. File Organization | ||
| - Actions inherit from `Act` class and implement platform-specific logic | ||
| - Repository objects use `[IsSerializedForLocalRepository]` attribute for persistence | ||
| - Test files end with `Test.cs` and use MSTest framework with `[TestClass]` and `[TestMethod]` | ||
|
|
||
| ### 2. Naming Patterns | ||
| - Actions: `Act{PlatformType}{ActionType}` (e.g., `ActBrowserElement`, `ActConsoleCommand`) | ||
| - Drivers: `{Platform}Driver` (e.g., `SeleniumDriver`, `JavaDriver`) | ||
| - Pages/Windows: `{Feature}Page` or `{Feature}Window` | ||
|
|
||
| ### 3. Error Handling | ||
| - Use `Reporter.ToLog(eLogLevel.ERROR, message)` for logging | ||
| - Actions set `act.Error` and `act.Status = eRunStatus.Failed` on failure | ||
| - Execution continues based on `Activity.ActionRunOption` setting | ||
|
|
||
| ## Integration Points | ||
|
|
||
| ### 1. Solution Structure | ||
| Solutions are XML-based with this folder structure: | ||
| ``` | ||
| Solution/ | ||
| ├── BusinessFlows/ # Test scenarios | ||
| ├── Activities/ # Reusable activity groups | ||
| ├── Actions/ # Shared actions | ||
| ├── Agents/ # Driver configurations | ||
| ├── Environments/ # Environment settings | ||
| ├── DataSources/ # Test data | ||
| └── Applications/ # Target application definitions | ||
| ``` | ||
|
|
||
| ### 2. Execution Engine | ||
| - `RunsetExecutor` orchestrates execution (`GingerCoreNET/Run/RunsetExecutor.cs`) | ||
| - `GingerExecutionEngine` handles individual runner execution | ||
| - Execution logging via `ExecutionLogger` implementations | ||
| - Supports parallel execution across multiple agents | ||
|
|
||
| ### 3. Plugin Architecture | ||
| - Legacy drivers implement `DriverBase` | ||
| - New plugins use `Agent.eAgentType.Service` with `PluginId`/`ServiceId` | ||
| - External integrations via `ALM` (Application Lifecycle Management) connectors | ||
| - Plugin packages defined by `Ginger.PluginPackage.json` manifest | ||
| - Managed through `PluginsManager` (`GingerCoreNET/PlugInsLib/PluginsManager.cs`) | ||
|
|
||
| ## Critical Development Notes | ||
|
|
||
| - **Target Framework**: .NET 8.0 with Windows 10.0.17763.0 compatibility | ||
| - **WPF Dependencies**: Main UI uses WPF; console components are cross-platform | ||
| - **XML Serialization**: Repository objects serialize to XML using custom attributes | ||
| - **Multi-Threading**: Execution engine supports parallel runners; use thread-safe patterns | ||
| - **Memory Management**: Dispose drivers properly; avoid memory leaks in long-running executions | ||
|
|
||
| ## Testing Strategy | ||
| - Unit tests in `*Test.cs` files use dependency injection patterns | ||
| - Integration tests use `TestResources/Solutions/` for test data | ||
| - CLI tests verify command-line interface functionality | ||
| - Use `mConsoleMessages` pattern for testing CLI output capture | ||
|
|
||
| When working with this codebase, focus on the execution hierarchy and understand how Actions flow through Activities within BusinessFlows, executed by Agents that wrap platform-specific drivers. No newline at end of file |
There was a problem hiding this comment.
Fix Markdown formatting to comply with style guide.
The file has systematic formatting violations that should be corrected before merge:
- Blank lines around headings (MD022): All heading levels lack blank lines above their introductory text or preceding sections
- Blank lines around code blocks (MD031): Multiple fenced code blocks lack surrounding blank lines
- Language identifiers in code blocks (MD040): Both
Solution → RunSet...blocks (lines 10, 84) are missing language identifiers - Trailing newline (MD047): File should end with a single newline
🔎 Proposed fixes for Markdown formatting
Add blank lines before all headings and major sections, specify language identifiers, and add trailing newline. Example pattern:
-## Project Overview
-Ginger is a comprehensive automation IDE...
+
+## Project Overview
+
+Ginger is a comprehensive automation IDE...For code blocks without a language:
-```
-Solution → RunSet → Runner → BusinessFlow → Activity → Action (Act)
-```
+
+```
+Solution → RunSet → Runner → BusinessFlow → Activity → Action (Act)
+```Apply these patterns consistently throughout lines 1–122 and ensure the file ends with a newline.
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
8-8: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
10-10: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
10-10: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
19-19: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
26-26: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
36-36: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
37-37: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
50-50: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
65-65: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
70-70: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
75-75: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
82-82: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
84-84: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
84-84: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
95-95: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
101-101: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
116-116: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
122-122: Files should end with a single newline character
(MD047, single-trailing-newline)
🤖 Prompt for AI Agents
.github/copilot-instructions.md lines 1-122: the Markdown violates
MD022/MD031/MD040/MD047; fix by adding a blank line before each heading and
before/after every fenced code block, add explicit language identifiers (e.g.,
"text" or "bash") to the two code blocks at the Solution → RunSet... occurrences
(around lines ~10 and ~84), ensure all fenced blocks have surrounding blank
lines, and terminate the file with a single trailing newline.
| Each platform has dedicated drivers under `GingerCore/Drivers/`: | ||
| - `SeleniumDriver` for web automation | ||
| - `JavaDriver` for Java applications | ||
| - `ConsoleDriverLib` for command-line interfaces | ||
| - `WindowsAutomation` for Windows applications | ||
| - Agents (`Agent.cs`) act as wrappers providing unified interface |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for JavaDriver, SeleniumDriver, ConsoleDriver classes
echo "=== Searching for class definitions ==="
find . -name "*.cs" -type f | head -100 | xargs grep -l "class JavaDriver\|class SeleniumDriver\|class.*ConsoleDriver\|class WindowsAutomation" 2>/dev/null | head -20
echo ""
echo "=== Checking for ConsoleDriverLib directory ==="
find . -type d -name "*Console*" 2>/dev/null | head -20
echo ""
echo "=== Checking directory structure under Drivers ==="
find . -type d -path "*/Drivers/*" 2>/dev/null | head -30Repository: Ginger-Automation/Ginger
Length of output: 2062
🏁 Script executed:
#!/bin/bash
# Search for specific class definitions in discovered directories
echo "=== JavaDriver in GingerCore/Drivers/JavaDriverLib ==="
find ./Ginger/GingerCore/Drivers/JavaDriverLib -name "*.cs" -type f -exec grep -l "class.*JavaDriver" {} \; 2>/dev/null
echo ""
echo "=== ConsoleDriver in GingerCore/Drivers/ConsoleDriverLib ==="
find ./Ginger/GingerCore/Drivers/ConsoleDriverLib -name "*.cs" -type f -exec grep -l "class.*ConsoleDriver" {} \; 2>/dev/null
echo ""
echo "=== ConsoleDriver in GingerCoreNET/Drivers/CoreDrivers/Console ==="
find ./Ginger/GingerCoreNET/Drivers/CoreDrivers/Console -name "*.cs" -type f -exec grep -l "class.*ConsoleDriver\|class.*Console" {} \; 2>/dev/null
echo ""
echo "=== SeleniumDriver location ==="
find ./Ginger -name "*SeleniumDriver*.cs" -type f 2>/dev/null | head -5
echo ""
echo "=== WindowsAutomation ==="
find ./Ginger -name "*WindowsAutomation*.cs" -type f 2>/dev/null | head -5
find ./Ginger -type d -name "*Windows*" 2>/dev/null | grep -i driver | head -10Repository: Ginger-Automation/Ginger
Length of output: 733
🏁 Script executed:
#!/bin/bash
# Check contents of legacy ConsoleDriverLib
echo "=== Files in GingerCore/Drivers/ConsoleDriverLib ==="
find ./Ginger/GingerCore/Drivers/ConsoleDriverLib -type f -name "*.cs" 2>/dev/null | head -20
echo ""
echo "=== Check if ConsoleDriverLib directory is empty or deprecated ==="
ls -la ./Ginger/GingerCore/Drivers/ConsoleDriverLib 2>/dev/null
echo ""
echo "=== Verify copilot-instructions.md current content ==="
cat ./Ginger/.github/copilot-instructions.md 2>/dev/null | head -50Repository: Ginger-Automation/Ginger
Length of output: 498
🏁 Script executed:
#!/bin/bash
echo "=== Full copilot-instructions.md content ==="
cat ./Ginger/.github/copilot-instructions.md
echo ""
echo "=== Check for WindowsDriver or Windows-related driver classes ==="
find ./Ginger/GingerCore/Drivers/WindowsLib -name "*.cs" -type f -exec grep -l "class.*Windows" {} \; 2>/dev/null | head -5Repository: Ginger-Automation/Ginger
Length of output: 325
🏁 Script executed:
#!/bin/bash
echo "=== Find copilot-instructions.md in the repo ==="
find ./Ginger -name "copilot-instructions.md" -type f 2>/dev/null
echo ""
echo "=== Search all .github directories ==="
find ./Ginger -type d -name ".github" 2>/dev/nullRepository: Ginger-Automation/Ginger
Length of output: 156
🏁 Script executed:
#!/bin/bash
echo "=== Check if .github directory exists anywhere ==="
find . -type d -name ".github" 2>/dev/null
echo ""
echo "=== Search for any copilot or instructions files ==="
find ./Ginger -iname "*copilot*" -o -iname "*instruction*" 2>/dev/null | grep -v node_modules
echo ""
echo "=== Check git status and recent files ==="
cd ./Ginger && git ls-files | grep -i copilot | head -10Repository: Ginger-Automation/Ginger
Length of output: 225
🏁 Script executed:
#!/bin/bash
echo "=== Contents of .github directory ==="
ls -la ./.github/
echo ""
echo "=== Check if copilot-instructions.md file exists ==="
test -f ./.github/copilot-instructions.md && echo "File exists" || echo "File does not exist"
echo ""
echo "=== Try different paths ==="
find . -name "*.md" -path "*/.github/*" 2>/dev/nullRepository: Ginger-Automation/Ginger
Length of output: 611
🏁 Script executed:
#!/bin/bash
echo "=== Full copilot-instructions.md content ==="
cat ./.github/copilot-instructions.md
echo ""
echo "=== Check specific line range (27-32) ==="
sed -n '27,32p' ./.github/copilot-instructions.mdRepository: Ginger-Automation/Ginger
Length of output: 5791
Update driver paths to reflect actual post-refactoring locations.
Lines 27–32 reference driver locations that are now outdated. SeleniumDriver and ConsoleDriver implementations have been migrated to GingerCoreNET:
SeleniumDriveris located inGingerCoreNET/Drivers/CoreDrivers/Web/Selenium/, notGingerCore/Drivers/- Console driver implementations (
DOSConsoleDriver,UnixShellDriver) are located inGingerCoreNET/Drivers/CoreDrivers/Console/, not underGingerCore/Drivers/ConsoleDriverLib JavaDriverremains inGingerCore/Drivers/JavaDriverLib/(accurate)- Update
WindowsAutomationtoWindowsDriverfor consistency with actual class names and the{Platform}Drivernaming pattern
🤖 Prompt for AI Agents
.github/copilot-instructions.md around lines 27 to 32: update the outdated
driver path references to the post-refactor locations and naming: change the
SeleniumDriver path to GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/, move
Console drivers to GingerCoreNET/Drivers/CoreDrivers/Console/ (include
DOSConsoleDriver and UnixShellDriver), keep JavaDriver at
GingerCore/Drivers/JavaDriverLib/, and rename WindowsAutomation to WindowsDriver
to follow the {Platform}Driver naming pattern.
| # Visual Studio Version 18 | ||
| VisualStudioVersion = 18.0.11205.157 |
There was a problem hiding this comment.
Verify Visual Studio 2026 (Version 18) requirement is intentional.
The solution file has been updated to target Visual Studio 2026 (Version 18), which began rolling out on November 11, 2025. Ensure this is intentional for the 2025 RC release, as it may impact developers who haven't upgraded to Visual Studio 2026 yet. While the MinimumVisualStudioVersion remains at 10.0.40219.1 allowing older VS versions to open the solution, some features may require the newer version.
🤖 Prompt for AI Agents
In Ginger/Ginger.sln around lines 3 to 4, the solution file now lists
VisualStudioVersion = 18.0.11205.157 (Visual Studio 2026); verify this is
intentional for the 2025 RC release and either confirm and document the
requirement or revert to the previous supported IDE version to avoid forcing
upgrades. If intentional: keep the version but add a note to the repo
README/CONTRIBUTING that VS 2026 is required for certain features and update
CI/docs; if not intentional: change VisualStudioVersion to the prior supported
value (e.g., 17.x) and ensure MinimumVisualStudioVersion remains unchanged, then
run a quick solution open/test on VS 17 to validate compatibility.
| // Apply initial visibility according to current selected command | ||
| if (ConsoleActionComboBox.SelectedItem != null) | ||
| { | ||
| UpdateVisibilityForCommand((ActConsoleCommand.eConsoleCommand)Enum.Parse(typeof(ActConsoleCommand.eConsoleCommand), ConsoleActionComboBox.SelectedValue?.ToString())); | ||
| } |
There was a problem hiding this comment.
Add error handling for enum parsing.
The code directly parses ConsoleActionComboBox.SelectedValue?.ToString() to an enum without validation, which could throw ArgumentException if the value is unexpected.
🔎 Proposed fix with safer enum parsing
// Apply initial visibility according to current selected command
if (ConsoleActionComboBox.SelectedItem != null)
{
- UpdateVisibilityForCommand((ActConsoleCommand.eConsoleCommand)Enum.Parse(typeof(ActConsoleCommand.eConsoleCommand), ConsoleActionComboBox.SelectedValue?.ToString()));
+ if (Enum.TryParse<ActConsoleCommand.eConsoleCommand>(ConsoleActionComboBox.SelectedValue?.ToString(), out var command))
+ {
+ UpdateVisibilityForCommand(command);
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Apply initial visibility according to current selected command | |
| if (ConsoleActionComboBox.SelectedItem != null) | |
| { | |
| UpdateVisibilityForCommand((ActConsoleCommand.eConsoleCommand)Enum.Parse(typeof(ActConsoleCommand.eConsoleCommand), ConsoleActionComboBox.SelectedValue?.ToString())); | |
| } | |
| // Apply initial visibility according to current selected command | |
| if (ConsoleActionComboBox.SelectedItem != null) | |
| { | |
| if (Enum.TryParse<ActConsoleCommand.eConsoleCommand>(ConsoleActionComboBox.SelectedValue?.ToString(), out var command)) | |
| { | |
| UpdateVisibilityForCommand(command); | |
| } | |
| } |
🤖 Prompt for AI Agents
In Ginger/Ginger/Actions/ActionEditPages/ActConsoleCommandEditPage.xaml.cs
around lines 67 to 71, the code parses ConsoleActionComboBox.SelectedValue
directly to ActConsoleCommand.eConsoleCommand which can throw if the value is
invalid; change this to safely attempt parsing (use Enum.TryParse with
ignoreCase true) after confirming SelectedValue is not null, and if TryParse
fails handle it by using a sensible default or returning early and
logging/alerting the issue so UpdateVisibilityForCommand is only called with a
valid enum value.
| if (driver is not ConsoleDriverBase) | ||
| { | ||
| Reporter.ToLog(eLogLevel.ERROR, "ConsoleDriverWindow: driver is null"); | ||
| } |
There was a problem hiding this comment.
Fix misleading error message.
The error message says "driver is null" but the check is actually validating that the driver is of type ConsoleDriverBase. This makes debugging confusing.
🔎 Proposed fix for accurate error message
if (driver is not ConsoleDriverBase)
{
- Reporter.ToLog(eLogLevel.ERROR, "ConsoleDriverWindow: driver is null");
+ Reporter.ToLog(eLogLevel.ERROR, "ConsoleDriverWindow: driver is not ConsoleDriverBase");
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (driver is not ConsoleDriverBase) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, "ConsoleDriverWindow: driver is null"); | |
| } | |
| if (driver is not ConsoleDriverBase) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, "ConsoleDriverWindow: driver is not ConsoleDriverBase"); | |
| } |
🤖 Prompt for AI Agents
In Ginger/Ginger/Drivers/DriversWindows/ConsoleDriverWindow.xaml.cs around lines
121-124, the logged message "ConsoleDriverWindow: driver is null" is misleading
because the condition checks that driver is not a ConsoleDriverBase; update the
error log to accurately state that the driver is not a ConsoleDriverBase (e.g.,
"ConsoleDriverWindow: driver is not a ConsoleDriverBase") and include the actual
driver type or "null" in the message to aid debugging.
| case eDBTypes.MSSQL: | ||
| var builder = new SqlConnectionStringBuilder | ||
| { | ||
| DataSource = TNSCalculated | ||
| }; | ||
|
|
||
| if (!string.IsNullOrEmpty(Database.Name)) builder.InitialCatalog = Database.Name; | ||
| builder.IntegratedSecurity = false; | ||
| builder.UserID = UserCalculated; | ||
| builder.Password = EncryptionHandler.DecryptwithKey(PassCalculated); | ||
| Database.ConnectionString = builder.ConnectionString; | ||
| break; |
There was a problem hiding this comment.
Inconsistent password handling in MSSQL connection string.
Line 283 decrypts the password directly and assigns it to the builder, which differs from the placeholder-based approach ({PASS}) used for other database types and in the GetConnectionString method. This inconsistency makes the code harder to maintain and may bypass the centralized ReplacePasswordInConnectionString logic.
🔎 Proposed fix to use placeholder approach
case eDBTypes.MSSQL:
var builder = new SqlConnectionStringBuilder
{
DataSource = TNSCalculated
};
if (!string.IsNullOrEmpty(Database.Name)) builder.InitialCatalog = Database.Name;
builder.IntegratedSecurity = false;
- builder.UserID = UserCalculated;
- builder.Password = EncryptionHandler.DecryptwithKey(PassCalculated);
+ builder.UserID = "{USER}";
+ builder.Password = "{PASS}";
Database.ConnectionString = builder.ConnectionString;
break;🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/Database/DatabaseOperations.cs around lines 274 to 285
the MSSQL branch directly decrypts and assigns the password to the
SqlConnectionStringBuilder (line ~283), which is inconsistent with the
placeholder-based pattern used elsewhere; change this to set the connection
string with a {PASS} placeholder (do not assign the decrypted password to
builder.Password), assign builder.ConnectionString to Database.ConnectionString,
and then call the existing ReplacePasswordInConnectionString (or equivalent
centralized method) to inject the decrypted password so MSSQL follows the same
placeholder-based flow as other DB types.
| case eDBTypes.PostgreSQL: | ||
| oConn = new NpgsqlConnection(connectConnectionString); | ||
| string postgreSQLHost = TNSCalculated; | ||
| int? port = null; | ||
| if (TNSCalculated.Contains(':', StringComparison.Ordinal)) | ||
| { | ||
| var parts = TNSCalculated.Split(':', 2); | ||
| postgreSQLHost = parts[0]; | ||
| if (int.TryParse(parts[1], out int p)) port = p; | ||
| } | ||
| ValidateHostPort(postgreSQLHost, port); | ||
|
|
||
| var pg = new NpgsqlConnectionStringBuilder | ||
| { | ||
| Host = postgreSQLHost, | ||
| Database = Database.Name ?? string.Empty, | ||
| Username = UserCalculated, | ||
| Password = EncryptionHandler.DecryptwithKey(PassCalculated) | ||
| }; | ||
| if (port.HasValue) pg.Port = port.Value; | ||
| Database.ConnectionString = pg.ConnectionString; | ||
|
|
||
| oConn = new NpgsqlConnection(pg.ConnectionString); | ||
| oConn.Open(); | ||
| break; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Significant code duplication between CreateConnectionString and Connect.
The PostgreSQL (lines 419-442) and MySQL (lines 472-499) connection string construction logic is duplicated from the CreateConnectionString method. This violates the DRY principle and creates a maintenance burden when connection string logic needs to be updated.
Consider calling GetConnectionString() or CreateConnectionString() instead of rebuilding the connection strings inline.
🔎 Example refactor for PostgreSQL
case eDBTypes.PostgreSQL:
- string postgreSQLHost = TNSCalculated;
- int? port = null;
- if (TNSCalculated.Contains(':', StringComparison.Ordinal))
- {
- var parts = TNSCalculated.Split(':', 2);
- postgreSQLHost = parts[0];
- if (int.TryParse(parts[1], out int p)) port = p;
- }
- ValidateHostPort(postgreSQLHost, port);
-
- var pg = new NpgsqlConnectionStringBuilder
- {
- Host = postgreSQLHost,
- Database = Database.Name ?? string.Empty,
- Username = UserCalculated,
- Password = EncryptionHandler.DecryptwithKey(PassCalculated)
- };
- if (port.HasValue) pg.Port = port.Value;
- Database.ConnectionString = pg.ConnectionString;
-
- oConn = new NpgsqlConnection(pg.ConnectionString);
+ oConn = new NpgsqlConnection(GetConnectionString());
oConn.Open();
break;Apply similar changes for MySQL.
Also applies to: 472-499
| private SqlConnectionStringBuilder GetMSSQLConnectionStringBuilder() | ||
| { | ||
| //if (string.IsNullOrEmpty(Database.ConnectionString)) | ||
| { | ||
| var builder = new SqlConnectionStringBuilder(); | ||
| builder.DataSource = TNSCalculated; | ||
| if (!string.IsNullOrEmpty(Database.Name)) builder.InitialCatalog = Database.Name; | ||
| builder.IntegratedSecurity = false; | ||
| builder.UserID = UserCalculated; | ||
| builder.Password = EncryptionHandler.DecryptwithKey(PassCalculated); | ||
| return builder; | ||
| } | ||
| //else | ||
| //{ | ||
| // // GetConnectionString will replace {USER} and {PASS} placeholders and decrypt the password | ||
| // string connStr = ConnectionStringCalculated; | ||
| // connStr = connStr.Replace("{USER}", UserCalculated); | ||
| // connStr = ReplacePasswordInConnectionString(connStr); | ||
|
|
||
| // // Parse and normalise the connection string using SqlConnectionStringBuilder | ||
| // var parsedBuilder = new SqlConnectionStringBuilder(connStr); | ||
| // return parsedBuilder; | ||
| //} | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Remove commented code.
Lines 574-594 contain extensive commented-out code that appears to be an abandoned alternative implementation. This should be removed to improve code readability.
🔎 Proposed cleanup
private SqlConnectionStringBuilder GetMSSQLConnectionStringBuilder()
{
- //if (string.IsNullOrEmpty(Database.ConnectionString))
- {
- var builder = new SqlConnectionStringBuilder();
- builder.DataSource = TNSCalculated;
- if (!string.IsNullOrEmpty(Database.Name)) builder.InitialCatalog = Database.Name;
- builder.IntegratedSecurity = false;
- builder.UserID = UserCalculated;
- builder.Password = EncryptionHandler.DecryptwithKey(PassCalculated);
- return builder;
- }
- //else
- //{
- // // GetConnectionString will replace {USER} and {PASS} placeholders and decrypt the password
- // string connStr = ConnectionStringCalculated;
- // connStr = connStr.Replace("{USER}", UserCalculated);
- // connStr = ReplacePasswordInConnectionString(connStr);
-
- // // Parse and normalise the connection string using SqlConnectionStringBuilder
- // var parsedBuilder = new SqlConnectionStringBuilder(connStr);
- // return parsedBuilder;
- //}
+ var builder = new SqlConnectionStringBuilder();
+ builder.DataSource = TNSCalculated;
+ if (!string.IsNullOrEmpty(Database.Name)) builder.InitialCatalog = Database.Name;
+ builder.IntegratedSecurity = false;
+ builder.UserID = UserCalculated;
+ builder.Password = EncryptionHandler.DecryptwithKey(PassCalculated);
+ return builder;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private SqlConnectionStringBuilder GetMSSQLConnectionStringBuilder() | |
| { | |
| //if (string.IsNullOrEmpty(Database.ConnectionString)) | |
| { | |
| var builder = new SqlConnectionStringBuilder(); | |
| builder.DataSource = TNSCalculated; | |
| if (!string.IsNullOrEmpty(Database.Name)) builder.InitialCatalog = Database.Name; | |
| builder.IntegratedSecurity = false; | |
| builder.UserID = UserCalculated; | |
| builder.Password = EncryptionHandler.DecryptwithKey(PassCalculated); | |
| return builder; | |
| } | |
| //else | |
| //{ | |
| // // GetConnectionString will replace {USER} and {PASS} placeholders and decrypt the password | |
| // string connStr = ConnectionStringCalculated; | |
| // connStr = connStr.Replace("{USER}", UserCalculated); | |
| // connStr = ReplacePasswordInConnectionString(connStr); | |
| // // Parse and normalise the connection string using SqlConnectionStringBuilder | |
| // var parsedBuilder = new SqlConnectionStringBuilder(connStr); | |
| // return parsedBuilder; | |
| //} | |
| } | |
| private SqlConnectionStringBuilder GetMSSQLConnectionStringBuilder() | |
| { | |
| var builder = new SqlConnectionStringBuilder(); | |
| builder.DataSource = TNSCalculated; | |
| if (!string.IsNullOrEmpty(Database.Name)) builder.InitialCatalog = Database.Name; | |
| builder.IntegratedSecurity = false; | |
| builder.UserID = UserCalculated; | |
| builder.Password = EncryptionHandler.DecryptwithKey(PassCalculated); | |
| return builder; | |
| } |
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/Database/DatabaseOperations.cs around lines 572-595,
remove the commented-out alternative implementation block (lines ~574-594) so
only the active GetMSSQLConnectionStringBuilder implementation remains; ensure
no orphaned comment markers remain and keep the existing return path intact
(SqlConnectionStringBuilder with DataSource, InitialCatalog, IntegratedSecurity,
UserID, and decrypted Password).
| catch (Exception e) | ||
| { | ||
| throw e; | ||
| throw; | ||
| } |
There was a problem hiding this comment.
Missing error logging before rethrow.
Line 902 rethrows an exception without logging it first, which is inconsistent with other catch blocks in this file that use Reporter.ToLog before rethrowing (e.g., lines 561, 650, 727, 815, 868, 1005).
As per coding guidelines: Use Reporter.ToLog(eLogLevel.ERROR, message) for logging errors.
🔎 Proposed fix
catch (Exception e)
{
+ Reporter.ToLog(eLogLevel.ERROR, $"Failed to query table {Table} column {Column} with where clause: {Where}", e);
throw;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| catch (Exception e) | |
| { | |
| throw e; | |
| throw; | |
| } | |
| catch (Exception e) | |
| { | |
| Reporter.ToLog(eLogLevel.ERROR, $"Failed to query table {Table} column {Column} with where clause: {Where}", e); | |
| throw; | |
| } |
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/Database/DatabaseOperations.cs around lines 900 to 903,
the catch block rethrows the exception without logging it; update the catch to
call Reporter.ToLog(eLogLevel.ERROR, ...) before throwing so errors are
consistent with other blocks (lines 561, 650, etc.). Log a clear message
including the context (e.g., which database operation failed) and include the
exception details (message/stack) via the exception object, then rethrow the
exception as currently done.
| Dispatcher.Invoke(() => | ||
| { | ||
| OnDriverMessage(eDriverMessageType.CloseDriverWindow); | ||
| }); | ||
|
|
||
| // Give window time to close gracefully | ||
| Thread.Sleep(500); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Replace blocking Thread.Sleep with async synchronization.
The code uses Thread.Sleep(500) to wait for window closure, which blocks the thread unnecessarily. Consider using an event-based or async pattern for cleaner shutdown coordination.
Alternative approach using TaskCompletionSource
// In ConsoleDriverBase, add a field:
private TaskCompletionSource<bool> _windowClosedTcs;
// In CloseDriver:
if (ShowWindow && Dispatcher != null)
{
try
{
_windowClosedTcs = new TaskCompletionSource<bool>();
Dispatcher.Invoke(() =>
{
OnDriverMessage(eDriverMessageType.CloseDriverWindow);
});
// Wait up to 1 second for window to close
_windowClosedTcs.Task.Wait(TimeSpan.FromSeconds(1));
}
catch (Exception ex)
{
Reporter.ToLog(eLogLevel.DEBUG, "Error closing driver window from driver", ex);
}
}
// In ConsoleDriverWindow.xaml.cs Closing handler, signal completion:
// mConsoleDriver?.SignalWindowClosed();
Description
Type of Change
Checklist
[IsSerializedForLocalRepository]where neededReporter.ToLog()patternSummary by CodeRabbit
Release Notes
New Features
Bug Fixes
UI/UX Improvements
✏️ Tip: You can customize this high-level summary in your review settings.