Skip to content

fix windows build & run - #27

Merged
inercia merged 20 commits into
inercia:mainfrom
zx9597446:main
Oct 30, 2025
Merged

fix windows build & run#27
inercia merged 20 commits into
inercia:mainfrom
zx9597446:main

Conversation

@zx9597446

Copy link
Copy Markdown
Contributor

I had problems to build and run on windows.

made some fix with it. wonder if you accept PRs.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @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

  • Windows Daemonization: The daemonization logic has been refactored into platform-specific files (cmd/daemon_unix.go and cmd/daemon_windows.go). This change specifically addresses the incompatibility of syscall.SysProcAttr.Setsid on Windows by providing a Windows-appropriate implementation.
  • Enhanced Windows Command Execution: The command runner (pkg/command/runner_exec.go) now includes improved logic for executing commands on Windows. This involves direct execution for common Windows shells (CMD, PowerShell) to prevent issues with temporary script files and ensures proper handling of output, including setting UTF-8 encoding for batch scripts.
  • Platform-Aware Timeout Handling: Timeout mechanisms have been made platform-specific. The Unix timeout command is now only utilized on Unix-like systems, while Windows environments will consistently rely on context-based timeouts, as the native Windows timeout command serves a different purpose (pausing execution).
  • Dynamic Shell Argument Resolution: New functions (getShellCommandArgs) have been introduced to dynamically determine the correct command-line arguments for various shells (e.g., /c for CMD, -Command for PowerShell, -c for Unix shells) based on the operating system, improving cross-platform compatibility.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread cmd/daemon_unix.go Outdated
Comment on lines +30 to +40
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)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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)
	}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address this.

Comment thread cmd/daemon_windows.go Outdated
Comment on lines +30 to +40
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)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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)
	}
}

Comment thread cmd/daemon_windows.go Outdated
Comment thread pkg/command/command_exec.go
Comment thread pkg/command/command_exec_windows.go Outdated
Comment on lines +1 to +12
//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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Comment thread pkg/command/platform_windows.go Outdated
Comment on lines +35 to +39
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This implementation correctly identifies that the Windows timeout command is not suitable for limiting execution time. This is a good platform-specific consideration.

Comment thread pkg/command/runner_exec.go Outdated
Comment on lines +85 to +88
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")) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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")
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable comment, right?

@inercia inercia left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your continuation @zx9597446 . PLease address sopme of the comments.

Comment thread cmd/daemon_windows.go Outdated
Comment thread pkg/command/command_exec.go
Comment thread pkg/command/runner_exec.go Outdated
Comment on lines +85 to +88
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")) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable comment, right?

Comment thread cmd/daemon_unix.go Outdated
Comment on lines +30 to +40
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)
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address this.

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.
@zx9597446

Copy link
Copy Markdown
Contributor Author

@gemini-code-assist
can you review updates please?

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread pkg/command/runner_exec.go Outdated
Comment thread cmd/daemon_windows.go Outdated
Comment thread cmd/daemon_windows.go
zx9597446 and others added 3 commits October 30, 2025 10:13
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.
@zx9597446

Copy link
Copy Markdown
Contributor Author

@gemini-code-assist

@gemini-code-assist

Copy link
Copy Markdown
Contributor

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 command in a new comment. I'll then provide a comprehensive review of the changes.

@zx9597446

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread cmd/daemon_windows.go Outdated
Comment thread pkg/command/platform_unix.go Outdated
Comment thread pkg/command/platform_windows.go Outdated
Comment thread pkg/command/runner_exec.go Outdated
zx9597446 and others added 4 commits October 30, 2025 10:36
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.
@zx9597446

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread cmd/daemon.go
Comment on lines +3 to +7
import (
"fmt"
"os"
"os/exec"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To use strings.HasPrefix as suggested in another comment for more robust flag parsing, you'll need to import the strings package.

Suggested change
import (
"fmt"
"os"
"os/exec"
)
import (
"fmt"
"os"
"os/exec"
"strings"
)

Comment thread cmd/daemon.go
Comment on lines +25 to +29
for _, arg := range args {
if arg != "--daemon" {
newArgs = append(newArgs, arg)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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=".

Suggested change
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)
}
}

Comment thread cmd/daemon_windows.go Outdated

// 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}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This line has inconsistent indentation (spaces instead of a tab). Please run gofmt on the file to fix the formatting.

Suggested change
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.DETACHED_PROCESS}
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.DETACHED_PROCESS}

Comment thread pkg/command/platform_unix.go Outdated

import (
"strings"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This blank line between import groups is unnecessary. Running gofmt would remove it.

Comment thread pkg/command/platform_unix.go Outdated

// shouldUseUnixTimeoutCommand returns whether to use the Unix-style timeout command
func shouldUseUnixTimeoutCommand() bool {
return common.CheckExecutableExists("timeout")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This line has inconsistent indentation (spaces instead of a tab). Please run gofmt on the file to fix the formatting.

Suggested change
return common.CheckExecutableExists("timeout")
return common.CheckExecutableExists("timeout")

Comment thread pkg/command/platform_windows.go Outdated
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This line has inconsistent indentation (spaces instead of a tab). Please run gofmt on the file to fix the formatting.

Suggested change
return false
return false

Comment thread pkg/command/runner_exec.go Outdated
// This helps avoid the temporary script file issue on Windows where cmd shows version info
configShell := getShell(shell)
shellLower := strings.ToLower(configShell)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This blank line is unnecessary. Running gofmt would remove it.

Comment thread pkg/command/runner_exec.go Outdated
// Format the command with proper shell syntax
// Format the command with proper shell syntax and file extension
var scriptContent strings.Builder

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This blank line is unnecessary. Running gofmt would remove it.

Comment thread pkg/command/runner_exec.go Outdated
// Get the combined output in case stdout doesn't capture everything
stdoutStr := stdout.String()
stderrStr := stderr.String()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This blank line is unnecessary. Running gofmt would remove it.

Comment thread pkg/command/runner_exec.go Outdated
Comment on lines +259 to +260


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These extra blank lines are unnecessary. Running gofmt would remove them.

Comment thread pkg/command/platform_unix.go Outdated
Comment thread cmd/daemon_unix.go Outdated
Comment thread cmd/daemon_windows.go Outdated
Comment thread pkg/command/platform_windows.go Outdated
@inercia
inercia merged commit d436f22 into inercia:main Oct 30, 2025
1 check passed
@inercia

inercia commented Oct 30, 2025

Copy link
Copy Markdown
Owner

Thanks for your contribution @zx9597446

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants