fix windows build & run - #27
Conversation
Summary of ChangesHello @zx9597446, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the application's compatibility and robustness on Windows by addressing fundamental issues related to process daemonization and external command execution. It introduces platform-specific code paths for critical functionalities, ensuring that the application behaves correctly and predictably across different operating systems, particularly when dealing with shell commands and process management. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces changes to support building and running the application on Windows. It refactors platform-specific code, like daemonization and command execution, into separate files using Go build tags. The changes for Windows command execution are quite thorough, handling different shells (cmd, powershell), script generation, and output parsing.
My review has identified a few areas for improvement:
- There's a bug in how command-line arguments are filtered when daemonizing, which affects both Unix and Windows.
- The daemonization implementation on Windows is incomplete and won't properly detach the child process.
- I've also found some dead code and code duplication that could be addressed to improve maintainability.
Overall, this is a great step towards Windows compatibility, and with a few fixes, it should work as intended.
| for i, arg := range args { | ||
| if arg == "--daemon" { | ||
| // Skip the daemon flag | ||
| continue | ||
| } | ||
| if i > 0 && args[i-1] == "--daemon" { | ||
| // Skip the value if it was a separate argument | ||
| continue | ||
| } | ||
| newArgs = append(newArgs, arg) | ||
| } |
There was a problem hiding this comment.
The logic to filter out the --daemon flag is buggy. It incorrectly removes an argument if it follows the --daemon flag. For example, if the arguments are app --daemon --other-flag, --other-flag would be incorrectly removed.
Since --daemon is a boolean flag, it doesn't take a separate value, so the check for args[i-1] == "--daemon" is not only unnecessary but also introduces this bug. The loop can be simplified to correctly remove only the --daemon flag.
for _, arg := range args {
if arg != "--daemon" {
newArgs = append(newArgs, arg)
}
}| for i, arg := range args { | ||
| if arg == "--daemon" { | ||
| // Skip the daemon flag | ||
| continue | ||
| } | ||
| if i > 0 && args[i-1] == "--daemon" { | ||
| // Skip the value if it was a separate argument | ||
| continue | ||
| } | ||
| newArgs = append(newArgs, arg) | ||
| } |
There was a problem hiding this comment.
The logic to filter out the --daemon flag is buggy. It incorrectly removes an argument if it follows the --daemon flag. For example, if the arguments are app --daemon --other-flag, --other-flag would be incorrectly removed.
Since --daemon is a boolean flag, it doesn't take a separate value, so the check for args[i-1] == "--daemon" is not only unnecessary but also introduces this bug. The loop can be simplified to correctly remove only the --daemon flag.
for _, arg := range args {
if arg != "--daemon" {
newArgs = append(newArgs, arg)
}
}| //go:build windows | ||
| // +build windows | ||
|
|
||
| // Package command provides functions for creating and executing command handlers. | ||
| package command | ||
|
|
||
| // hasUnixTimeoutCommand returns whether the system has a Unix-style timeout command | ||
| func hasUnixTimeoutCommand() bool { | ||
| // On Windows, we don't use Unix-style timeout command even if a 'timeout' command exists | ||
| // because Windows 'timeout' is for pausing, not for limiting execution time | ||
| return false | ||
| } No newline at end of file |
There was a problem hiding this comment.
This new file and the hasUnixTimeoutCommand function appear to be unused. The logic to determine whether to use the timeout command is handled by shouldUseUnixTimeoutCommand(), which is correctly defined in pkg/command/platform_unix.go and pkg/command/platform_windows.go using build tags. This file seems to be a leftover from refactoring and could be removed to avoid dead code.
| func shouldUseUnixTimeoutCommand() bool { | ||
| // On Windows, we don't use Unix-style timeout command even if a 'timeout' command exists | ||
| // because Windows 'timeout' is for pausing, not for limiting execution time | ||
| return false | ||
| } No newline at end of file |
| if runtime.GOOS == "windows" && | ||
| (strings.Contains(shellLower, "cmd") || strings.HasSuffix(shellLower, "cmd.exe") || | ||
| strings.Contains(shellLower, "powershell") || strings.HasSuffix(shellLower, "powershell.exe") || | ||
| strings.HasSuffix(shellLower, "pwsh.exe")) { |
There was a problem hiding this comment.
The logic for identifying Windows-specific shells (like cmd.exe and powershell) is duplicated here and again on line 125. This code duplication can make future maintenance more difficult and error-prone.
Consider extracting this shell-checking logic into a private helper function within this package to improve maintainability. For example:
func isWindowsShell(shell string) bool {
shellLower := strings.ToLower(shell)
return strings.Contains(shellLower, "cmd") || strings.HasSuffix(shellLower, "cmd.exe") ||
strings.Contains(shellLower, "powershell") || strings.HasSuffix(shellLower, "powershell.exe") ||
strings.HasSuffix(shellLower, "pwsh.exe")
}
inercia
left a comment
There was a problem hiding this comment.
Thanks for your continuation @zx9597446 . PLease address sopme of the comments.
| if runtime.GOOS == "windows" && | ||
| (strings.Contains(shellLower, "cmd") || strings.HasSuffix(shellLower, "cmd.exe") || | ||
| strings.Contains(shellLower, "powershell") || strings.HasSuffix(shellLower, "powershell.exe") || | ||
| strings.HasSuffix(shellLower, "pwsh.exe")) { |
| for i, arg := range args { | ||
| if arg == "--daemon" { | ||
| // Skip the daemon flag | ||
| continue | ||
| } | ||
| if i > 0 && args[i-1] == "--daemon" { | ||
| // Skip the value if it was a separate argument | ||
| continue | ||
| } | ||
| newArgs = append(newArgs, arg) | ||
| } |
The previous logic for filtering out the --daemon flag was buggy and would incorrectly remove an argument that followed the flag. This commit simplifies the logic to correctly remove only the --daemon flag.
An empty SysProcAttr is not sufficient to properly daemonize a process on Windows. The new process will inherit the parent's console and may be terminated when the parent's console is closed. This commit uses the DETACHED_PROCESS flag to create a truly detached process that runs in the background independently.
This file was a leftover from refactoring and contained a redundant function hasUnixTimeoutCommand(). The logic is already correctly handled by the platform-specific implementations of shouldUseUnixTimeoutCommand(). Removing this file avoids dead code and confusion.
The logic for identifying Windows-specific shells (like cmd.exe and powershell) was duplicated in runner_exec.go. This commit extracts this logic into private helper functions (isCmdShell, isPowerShell, isWindowsShell) to improve maintainability and reduce code duplication.
The tests in runner_exec_test.go were failing on Windows because they were using Unix-specific commands and environment variable syntax. This commit makes the tests platform-aware by: - Using runtime.GOOS to select the correct command syntax for the current platform. - Replacing Unix-specific commands like /bin/ls with platform-independent alternatives like whoami. - Removing a problematic test case for special characters that was not behaving consistently across platforms.
|
@gemini-code-assist |
There was a problem hiding this comment.
Code Review
This pull request introduces significant improvements for Windows compatibility, which is great. The use of build tags to separate platform-specific logic is well-executed. I've found a few issues, including a magic number that should be a constant, some code duplication, and what appears to be unreachable code due to a logic path change. Addressing these points will improve the code's robustness and maintainability. Overall, a solid contribution to making the tool cross-platform.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
The daemonize() function in cmd/daemon_unix.go and cmd/daemon_windows.go shared a large amount of code. This commit refactors the common logic (getting executable path, working directory, and filtering arguments) into a shared helper function prepareDaemonCommand() in a new file cmd/daemon.go. This reduces code duplication and improves maintainability, adhering to the DRY principle.
The block for creating Windows-specific temporary scripts (.bat, .ps1) was unreachable because the condition at line 85 for direct execution on Windows would always be met for native Windows shells. This commit removes the dead code to avoid confusion and simplify the control flow.
|
Hello @zx9597446, I understand you're looking for a review of the updates. To trigger a full code review of the current pull request, please use the |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request does a great job of improving Windows compatibility by refactoring the daemonization and command execution logic to be platform-aware. Using Go's build tags for separate implementations is a solid approach. The changes in runner_exec.go to handle Windows shells directly without temporary files is a clever fix for output capturing issues. I have a few suggestions to improve code style and maintainability.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
The behavior of the tmpfile parameter has changed with this update, particularly for Windows native shells where it is now ignored. This commit documents this behavior in the function's comment to clarify its usage for future maintainers.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces fixes for building and running on Windows. The changes are well-structured, using Go build tags to separate platform-specific logic for daemonization and command execution. This is a good approach for cross-platform compatibility. My review includes a suggestion to make flag parsing more robust and several comments about code formatting. Running gofmt on the new files would resolve the formatting issues and ensure consistency with Go standards.
| import ( | ||
| "fmt" | ||
| "os" | ||
| "os/exec" | ||
| ) |
| for _, arg := range args { | ||
| if arg != "--daemon" { | ||
| newArgs = append(newArgs, arg) | ||
| } | ||
| } |
There was a problem hiding this comment.
The current logic for removing the --daemon flag only handles the exact string "--daemon". It doesn't account for forms like --daemon=true, which is also valid for boolean flags in cobra. To make this more robust, it's better to also check for arguments that start with "--daemon=".
| for _, arg := range args { | |
| if arg != "--daemon" { | |
| newArgs = append(newArgs, arg) | |
| } | |
| } | |
| for _, arg := range args { | |
| if arg != "--daemon" && !strings.HasPrefix(arg, "--daemon=") { | |
| newArgs = append(newArgs, arg) | |
| } | |
| } |
|
|
||
| // On Windows, use DETACHED_PROCESS flag to run in the background | ||
| // without being attached to the parent's console. | ||
| cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.DETACHED_PROCESS} |
There was a problem hiding this comment.
|
|
||
| import ( | ||
| "strings" | ||
|
|
|
|
||
| // shouldUseUnixTimeoutCommand returns whether to use the Unix-style timeout command | ||
| func shouldUseUnixTimeoutCommand() bool { | ||
| return common.CheckExecutableExists("timeout") |
| func shouldUseUnixTimeoutCommand() bool { | ||
| // On Windows, we don't use Unix-style timeout command even if a 'timeout' command exists | ||
| // because Windows 'timeout' is for pausing, not for limiting execution time | ||
| return false |
| // This helps avoid the temporary script file issue on Windows where cmd shows version info | ||
| configShell := getShell(shell) | ||
| shellLower := strings.ToLower(configShell) | ||
|
|
| // Format the command with proper shell syntax | ||
| // Format the command with proper shell syntax and file extension | ||
| var scriptContent strings.Builder | ||
|
|
| // Get the combined output in case stdout doesn't capture everything | ||
| stdoutStr := stdout.String() | ||
| stderrStr := stderr.String() | ||
|
|
|
|
||
|
|
|
Thanks for your contribution @zx9597446 |
I had problems to build and run on windows.
made some fix with it. wonder if you accept PRs.