Skip to content

Conversation

@mowirth
Copy link

@mowirth mowirth commented Nov 20, 2025

Description

Fixes #4839.

Replaces the current implementation with a helper implementation to avoid os.

TODOs

Read the Gruntwork contribution guidelines.

  • I authored this code entirely myself
  • I am submitting code based on open source software (e.g. MIT, MPL-2.0, Apache)]
  • I am adding or upgrading a dependency or adapted code and confirm it has a compatible open source license
  • Update the docs.
  • Run the relevant tests successfully, including pre-commit checks.
  • Include release notes. If this PR is backward incompatible, include a migration guide.

Release Notes (draft)

  • Fixed regression regarding the signal handling for subprocesses

Migration Guide

No incompatibilities

Summary by CodeRabbit

  • New Features

    • Added graceful cancellation for running processes so operations can be interrupted cleanly across the app.
  • Tests

    • Improved tests to validate interruption and cancellation behavior on Unix and Windows, with clearer failure diagnostics.
  • Chores

    • Tidied and reorganized imports and minor formatting cleanup across components.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Nov 20, 2025

@mowirth is attempting to deploy a commit to the Gruntwork Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

📝 Walkthrough

Walkthrough

Configure command cancellation to send SIGINT to child processes (instead of forceful termination) by setting Cancel hooks on exec.Cmd in multiple execution layers and updating tests to assert the new signal-based behavior. No public APIs changed.

Changes

Cohort / File(s) Summary
Command execution / formatter
cli/commands/hcl/format/format.go
Replace direct exec invocation with a constructed command and set a Cancel hook that sends SIGINT; use cmd.CombinedOutput() for execution.
Engine process handling
engine/engine.go
Assign cmd.Cancel to send SIGINT when cancelling the engine process; minor newline removal in ReadEngineOutput.
Internal exec wrapper
internal/os/exec/cmd.go
Add SIGINT-based Cancel hook to commands created by the internal Command helper.
Test helpers
test/helpers/test_helpers.go
Set cmd.Cancel to signal SIGINT in ExecWithTestLogger to support graceful interruption during tests.
Shell tests
shell/run_cmd_unix_test.go, shell/run_cmd_windows_test.go
Update assertions to handle process-killed (signal) vs. exit-status scenarios; add explicit failure messages for killed-signal cases.
Imports / minor cleanup
util/shell.go
Consolidate os/exec into the main import block and remove extra blank lines in GetExitCode.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas to focus on:
    • Verify Cancel hooks are always set where contexts may be cancelled and that they run reliably before process reaping.
    • Confirm SIGINT behavior matches upstream (terraform/OpenTofu) expectations across platforms (Unix vs Windows).
    • Ensure tests reliably detect both "killed by signal" and exit-status outcomes on CI agents with different process semantics.

Suggested reviewers

  • denis256

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: Inproper signal handling' directly addresses the main change—fixing signal handling for subprocess interrupts—which aligns with the PR's core objective from issue #4839.
Description check ✅ Passed The description identifies the fixed issue (#4839), summarizes changes, and includes release notes and migration guide. However, the 'Run the relevant tests' TODO remains unchecked despite being required.
Linked Issues check ✅ Passed The code changes implement graceful signal handling via exec.CommandContext and Cancel hooks to send SIGINT, allowing subprocesses to shut down cleanly and unlock remote state, directly addressing issue #4839's requirement.
Out of Scope Changes check ✅ Passed All changes focus on adding syscall-based SIGINT cancellation mechanisms and formatting cleanup in exec, test, and shell files. The import reorganization in util/shell.go is a minor cleanup consistent with the cancellation feature additions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c891a9e and 569e1d2.

📒 Files selected for processing (4)
  • cli/commands/hcl/format/format.go (2 hunks)
  • engine/engine.go (2 hunks)
  • internal/os/exec/cmd.go (2 hunks)
  • test/helpers/test_helpers.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/helpers/test_helpers.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

Review the Go code for quality and correctness. Make sure that the Go code follows best practices, is performant, and is easy to understand and maintain.

Files:

  • engine/engine.go
  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go
🧠 Learnings (3)
📚 Learning: 2025-02-10T13:36:19.542Z
Learnt from: levkohimins
Repo: gruntwork-io/terragrunt PR: 3723
File: cli/commands/stack/action.go:160-160
Timestamp: 2025-02-10T13:36:19.542Z
Learning: The project uses a custom error package `github.com/gruntwork-io/terragrunt/internal/errors` which provides similar functionality to `fmt.Errorf` but includes stack traces. Prefer using this package's error functions (e.g., `errors.Errorf`, `errors.New`) over the standard library's error handling.

Applied to files:

  • engine/engine.go
  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go
📚 Learning: 2025-04-17T13:02:28.098Z
Learnt from: yhakbar
Repo: gruntwork-io/terragrunt PR: 4169
File: cli/commands/hcl/validate/cli.go:29-60
Timestamp: 2025-04-17T13:02:28.098Z
Learning: Avoid shadowing imported packages with local variables in Go code, such as using a variable named `flags` when the `github.com/gruntwork-io/terragrunt/cli/flags` package is imported. Use more specific variable names like `flagSet` instead.

Applied to files:

  • engine/engine.go
  • internal/os/exec/cmd.go
📚 Learning: 2025-08-19T16:05:54.723Z
Learnt from: Resonance1584
Repo: gruntwork-io/terragrunt PR: 4683
File: go.mod:86-90
Timestamp: 2025-08-19T16:05:54.723Z
Learning: When analyzing Go module dependencies for removal, always check for both direct imports and API usage across all Go files in the repository, not just a quick search. The github.com/mattn/go-zglob library is used for filesystem walking and glob expansion in multiple Terragrunt files including util/file.go, format commands, and AWS provider patch functionality.

Applied to files:

  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mowirth
Copy link
Author

mowirth commented Nov 20, 2025

I have some issues running the test locally as I have not installed all dependencies, so I'll be happy if you could give them a run and check if something is not in order.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
shell/run_cmd_windows_test.go (1)

49-69: Update comments to match new expectations (no longer accept signal: killed)

The assertions now reject "signal: killed" and only allow "Failed to execute \"%s" with either "exit status 5" or "exit status 1", which is aligned with the desired graceful termination behavior. However, the surrounding comments still say the process “might … be killed by a signal,” which contradicts the checks.

Consider updating the comments to describe the new, stricter expectation (exit status–based failures only) so they don’t mislead future readers.

internal/os/exec/cmd.go (2)

32-45: Command no longer uses ctx → compile error and potential behavioral ambiguity

Command keeps a ctx context.Context parameter but no longer references it inside the function body after switching to exec.Command(name, args...). In Go this will fail to compile (ctx declared but not used) and also makes it unclear whether ctx is still meant to influence process lifetime.

If you intend to decouple the underlying exec.Cmd from context cancellation and rely solely on RegisterGracefullyShutdown, you still need to use ctx to avoid the compile error — for example:

 func Command(ctx context.Context, name string, args ...string) *Cmd {
-	cmd := &Cmd{
+	// ctx is intentionally not wired into exec.Command; it is used only for higher-level shutdown logic.
+	_ = ctx
+	cmd := &Cmd{
 		Cmd:             exec.Command(name, args...),
 		logger:          log.Default(),
 		filename:        filepath.Base(name),
 		interruptSignal: signal.InterruptSignal,
 	}

Alternatively (and more semantically meaningful), you could store ctx on Cmd or call a registration hook here, but that requires checking all call sites to avoid double registration.


4-18: GracefulCommandContext uses Unix-only syscalls without OS guards — confirmed issue

The review comment is accurate. The GracefulCommandContext function (lines 133–148) directly uses platform-specific code without build constraints:

  • Line 137: cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}Setpgid is Unix-only
  • Line 143: syscall.Kill(-cmd.Process.Pid, signal.InterruptSignal) — negative PID process-group semantics are Unix-only
  • Windows signal: InterruptSignal = nil (will cause runtime errors when passed to syscall.Kill)

The parent file has no //go:build constraints, meaning this code compiles on Windows despite being non-functional there. The function is called in production code paths (engine, git operations, format command).

The codebase already uses OS-specific files with proper build constraints elsewhere (ptty_unix.go/ptty_windows.go), establishing the pattern. Creating cmd_unix.go with //go:build !windows for this function and a cmd_windows.go fallback is the appropriate fix.

🧹 Nitpick comments (1)
test/helpers/test_helpers.go (1)

14-15: Tests now benefit from process‑group‑aware cancellation

Switching ExecWithTestLogger to internalExec.GracefulCommandContext aligns test subprocess handling with the new helper and should give more realistic interrupt behavior for nested processes.

You may optionally consider migrating ExecAndCaptureOutput to use the same helper for consistent cancellation semantics across all test helpers, but this is not required for correctness.

Also applies to: 75-100

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfcf295 and a8f8ccd.

📒 Files selected for processing (8)
  • cli/commands/hcl/format/format.go (2 hunks)
  • engine/engine.go (2 hunks)
  • internal/cas/git.go (2 hunks)
  • internal/os/exec/cmd.go (3 hunks)
  • shell/run_cmd_unix_test.go (1 hunks)
  • shell/run_cmd_windows_test.go (1 hunks)
  • test/helpers/test_helpers.go (2 hunks)
  • util/shell.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

Review the Go code for quality and correctness. Make sure that the Go code follows best practices, is performant, and is easy to understand and maintain.

Files:

  • internal/cas/git.go
  • test/helpers/test_helpers.go
  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go
  • shell/run_cmd_windows_test.go
  • shell/run_cmd_unix_test.go
  • engine/engine.go
  • util/shell.go
🧠 Learnings (4)
📓 Common learnings
Learnt from: jorhett
Repo: gruntwork-io/terragrunt PR: 1234
File: errors/multierror.go:35-39
Timestamp: 2025-09-10T04:41:35.652Z
Learning: In Terragrunt's MultiError.Error() method, checking for the exact string "exit status 2" and returning it directly is not a brittle hack but a semantic fix. Terraform's --detailed-exitcode flag uses exit code 2 to mean "plan succeeded with changes" (not an error), so when multiple modules return this status, it should not be wrapped in "Hit multiple errors:" formatting as that misrepresents successful operations as errors.
📚 Learning: 2025-02-10T13:36:19.542Z
Learnt from: levkohimins
Repo: gruntwork-io/terragrunt PR: 3723
File: cli/commands/stack/action.go:160-160
Timestamp: 2025-02-10T13:36:19.542Z
Learning: The project uses a custom error package `github.com/gruntwork-io/terragrunt/internal/errors` which provides similar functionality to `fmt.Errorf` but includes stack traces. Prefer using this package's error functions (e.g., `errors.Errorf`, `errors.New`) over the standard library's error handling.

Applied to files:

  • internal/cas/git.go
  • test/helpers/test_helpers.go
  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go
  • engine/engine.go
  • util/shell.go
📚 Learning: 2025-04-17T13:02:28.098Z
Learnt from: yhakbar
Repo: gruntwork-io/terragrunt PR: 4169
File: cli/commands/hcl/validate/cli.go:29-60
Timestamp: 2025-04-17T13:02:28.098Z
Learning: Avoid shadowing imported packages with local variables in Go code, such as using a variable named `flags` when the `github.com/gruntwork-io/terragrunt/cli/flags` package is imported. Use more specific variable names like `flagSet` instead.

Applied to files:

  • internal/cas/git.go
  • test/helpers/test_helpers.go
  • internal/os/exec/cmd.go
  • engine/engine.go
  • util/shell.go
📚 Learning: 2025-08-19T16:05:54.723Z
Learnt from: Resonance1584
Repo: gruntwork-io/terragrunt PR: 4683
File: go.mod:86-90
Timestamp: 2025-08-19T16:05:54.723Z
Learning: When analyzing Go module dependencies for removal, always check for both direct imports and API usage across all Go files in the repository, not just a quick search. The github.com/mattn/go-zglob library is used for filesystem walking and glob expansion in multiple Terragrunt files including util/file.go, format commands, and AWS provider patch functionality.

Applied to files:

  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go
  • engine/engine.go
  • util/shell.go
🧬 Code graph analysis (4)
internal/cas/git.go (1)
internal/os/exec/cmd.go (1)
  • GracefulCommandContext (133-148)
test/helpers/test_helpers.go (1)
internal/os/exec/cmd.go (1)
  • GracefulCommandContext (133-148)
cli/commands/hcl/format/format.go (1)
internal/os/exec/cmd.go (1)
  • GracefulCommandContext (133-148)
engine/engine.go (1)
internal/os/exec/cmd.go (1)
  • GracefulCommandContext (133-148)
🔇 Additional comments (6)
util/shell.go (1)

3-13: Import placement change is safe

Moving os/exec into the main import block is purely organizational and has no behavioral impact; usage in this file remains unambiguous.

cli/commands/hcl/format/format.go (1)

21-34: GracefulCommandContext usage in bytesDiff looks correct

Using internal/os/exec.GracefulCommandContext for the diff subprocess preserves existing behavior while making cancellation process‑group‑aware and more graceful. Arguments and error handling are unchanged.

Also applies to: 262-310

internal/cas/git.go (2)

13-14: LGTM! Clean import alias for internal exec package.

The import alias internalExec is clear and avoids shadowing the standard os/exec package that's still needed for exec.LookPath at line 29.


285-285: LGTM! Correct integration with GracefulCommandContext.

The call properly constructs the command by passing the git executable path and combining the subcommand (name) with its arguments. This ensures git operations receive proper interrupt signals via process groups, addressing the state-locking issue described in #4839.

engine/engine.go (2)

17-17: LGTM! Import updated to use internal exec wrapper.

The import change enables graceful signal handling for the engine subprocess, which is essential for proper Terraform state management during interrupts.


502-502: LGTM! Engine process now uses graceful signal handling.

The switch to GracefulCommandContext ensures the engine plugin receives proper interrupt signals, allowing it to clean up resources and unlock state before termination. This is critical for the gRPC plugin lifecycle managed by hashicorp/go-plugin.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
shell/run_cmd_unix_test.go (1)

49-50: Remove or update the outdated comment.

The comment states the process "might either exit with the expected status code or be killed by a signal," but the test code explicitly treats being killed as a failure (lines 54-56). This contradicts the PR's objective of fixing signal handling to ensure graceful termination. The comment appears to describe the old (buggy) behavior rather than the new expected behavior.

Apply this diff to remove the outdated comment:

-	// The process might either exit with the expected status code or be killed by a signal
-	// depending on timing and system conditions
 	expectedExitStatusErr := fmt.Sprintf("Failed to execute \"%s 5\" in .\n\nexit status %d", cmdPath, expectedWait)
🧹 Nitpick comments (1)
shell/run_cmd_unix_test.go (1)

55-55: Consider improving error message formatting for consistency.

The error message formatting "but got\n: %s" produces a newline followed by a colon, which results in:

Expected process to gracefully terminate but got
: signal: killed

For consistency with the other error message (lines 59-60) and better readability, consider using the pattern "but got:\n %s":

-		t.Errorf("Expected process to gracefully terminate but got\n: %s", actualErr.Error())
+		t.Errorf("Expected process to gracefully terminate but got:\n  %s", actualErr.Error())
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8f8ccd and 7ba29a9.

📒 Files selected for processing (1)
  • shell/run_cmd_unix_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

Review the Go code for quality and correctness. Make sure that the Go code follows best practices, is performant, and is easy to understand and maintain.

Files:

  • shell/run_cmd_unix_test.go
🧠 Learnings (1)
📚 Learning: 2025-09-10T04:41:35.652Z
Learnt from: jorhett
Repo: gruntwork-io/terragrunt PR: 1234
File: errors/multierror.go:35-39
Timestamp: 2025-09-10T04:41:35.652Z
Learning: In Terragrunt's MultiError.Error() method, checking for the exact string "exit status 2" and returning it directly is not a brittle hack but a semantic fix. Terraform's --detailed-exitcode flag uses exit code 2 to mean "plan succeeded with changes" (not an error), so when multiple modules return this status, it should not be wrapped in "Hit multiple errors:" formatting as that misrepresents successful operations as errors.

Applied to files:

  • shell/run_cmd_unix_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Pull Request has non-contributor approval
  • GitHub Check: Pull Request has non-contributor approval

@mowirth
Copy link
Author

mowirth commented Nov 26, 2025

@denis256 @ThisGuyCodes @yhakbar Could you please take a look?

@denis256
Copy link
Member

Failed on lint

internal/os/exec/cmd.go:143:16: Error return value of `syscall.Kill` is not checked (errcheck)
			syscall.Kill(-cmd.Process.Pid, signal.InterruptSignal)
			            ^
internal/os/exec/cmd.go:34:32: os/exec.Command must not be called. use os/exec.CommandContext (noctx)
		Cmd:             exec.Command(name, args...),
		                             ^
internal/os/exec/cmd.go:134:21: os/exec.Command must not be called. use os/exec.CommandContext (noctx)
	cmd := exec.Command(name, args...)
	                   ^
internal/os/exec/cmd.go:141:3: missing whitespace above this line (invalid statement above if) (wsl_v5)
		if cmd.Process != nil {
		^
4 issues:
* errcheck: 1
* noctx: 2
* wsl_v5: 1

@sylr sylr mentioned this pull request Dec 3, 2025
@sylr
Copy link

sylr commented Dec 3, 2025

@mowirth could you merge #5168 in your branch ?

@mowirth
Copy link
Author

mowirth commented Dec 3, 2025

@sylr thanks for the help, I integrated it in in my branch.

Regarding the lint errors related to the CommandContext, I don't think there is a way around this - as expained above, CommandContext will always send a SIGKILL to the underlying process.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
internal/os/exec/cmd.go (1)

32-34: Unused ctx parameter after switching to exec.Command.

The ctx parameter is no longer used in this function since the switch from exec.CommandContext to exec.Command. This will also trigger the noctx linter (as noted in PR comments).

Consider either:

  1. Adding _ = ctx or a lint ignore directive with a comment explaining the intentional design, or
  2. Removing the ctx parameter if callers can obtain context-aware behavior via RegisterGracefullyShutdown instead (though this is a breaking API change).
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70e6614 and c78fe80.

📒 Files selected for processing (3)
  • engine/engine.go (2 hunks)
  • internal/os/exec/cmd.go (3 hunks)
  • util/shell.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • util/shell.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

Review the Go code for quality and correctness. Make sure that the Go code follows best practices, is performant, and is easy to understand and maintain.

Files:

  • internal/os/exec/cmd.go
  • engine/engine.go
🧠 Learnings (3)
📚 Learning: 2025-02-10T13:36:19.542Z
Learnt from: levkohimins
Repo: gruntwork-io/terragrunt PR: 3723
File: cli/commands/stack/action.go:160-160
Timestamp: 2025-02-10T13:36:19.542Z
Learning: The project uses a custom error package `github.com/gruntwork-io/terragrunt/internal/errors` which provides similar functionality to `fmt.Errorf` but includes stack traces. Prefer using this package's error functions (e.g., `errors.Errorf`, `errors.New`) over the standard library's error handling.

Applied to files:

  • internal/os/exec/cmd.go
  • engine/engine.go
📚 Learning: 2025-08-19T16:05:54.723Z
Learnt from: Resonance1584
Repo: gruntwork-io/terragrunt PR: 4683
File: go.mod:86-90
Timestamp: 2025-08-19T16:05:54.723Z
Learning: When analyzing Go module dependencies for removal, always check for both direct imports and API usage across all Go files in the repository, not just a quick search. The github.com/mattn/go-zglob library is used for filesystem walking and glob expansion in multiple Terragrunt files including util/file.go, format commands, and AWS provider patch functionality.

Applied to files:

  • internal/os/exec/cmd.go
  • engine/engine.go
📚 Learning: 2025-04-17T13:02:28.098Z
Learnt from: yhakbar
Repo: gruntwork-io/terragrunt PR: 4169
File: cli/commands/hcl/validate/cli.go:29-60
Timestamp: 2025-04-17T13:02:28.098Z
Learning: Avoid shadowing imported packages with local variables in Go code, such as using a variable named `flags` when the `github.com/gruntwork-io/terragrunt/cli/flags` package is imported. Use more specific variable names like `flagSet` instead.

Applied to files:

  • internal/os/exec/cmd.go
  • engine/engine.go
🧬 Code graph analysis (1)
engine/engine.go (1)
internal/os/exec/cmd.go (1)
  • GracefulCommandContext (133-149)
🔇 Additional comments (1)
engine/engine.go (1)

17-17: LGTM - Proper adoption of graceful shutdown helper.

The switch to GracefulCommandContext correctly enables the engine process to receive interrupt signals when the context is cancelled, supporting the goal of allowing Terraform to properly handle interrupts and unlock state.

Also applies to: 502-502

Comment on lines 133 to 148
func GracefulCommandContext(ctx context.Context, name string, args ...string) *exec.Cmd {
cmd := exec.Command(name, args...)

// Put subprocess in its own process group
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

go func() {
<-ctx.Done()

if cmd.Process != nil {
// Send the provided signal to the process group
_ = syscall.Kill(-cmd.Process.Pid, signal.InterruptSignal)
}
}()

return cmd
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Platform portability issue: Unix-only syscall usage.

syscall.SysProcAttr{Setpgid: true} and syscall.Kill(-pid, ...) with negative PID are Unix-specific and will cause compilation failures on Windows. Given the PR notes testing on Windows 11, this needs platform-specific handling.

Consider moving this function to a cmd_unix.go file with build constraints, and providing a Windows-compatible stub (or alternative implementation) in cmd_windows.go:

-func GracefulCommandContext(ctx context.Context, name string, args ...string) *exec.Cmd {
+// cmd_unix.go
+//go:build unix
+
+func GracefulCommandContext(ctx context.Context, name string, args ...string) *exec.Cmd {

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
internal/os/exec/cmd.go around lines 133-148 uses Unix-only syscalls
(SysProcAttr.Setpgid and negative-PID syscall.Kill) which will break Windows
builds; move this implementation into a new file cmd_unix.go with a build tag
"//go:build !windows" and keep the same GracefulCommandContext signature and
behavior there, restrict imports to syscall in that file only, and create a
cmd_windows.go with build tag "//go:build windows" that provides a
Windows-compatible stub/alternative implementation (same function signature)
that does not use Unix syscalls (e.g., omit Setpgid and send signals using
os.Process.Signal or Process.Kill as appropriate) so the package builds on both
platforms.

@jordanst3wart
Copy link

jordanst3wart commented Dec 5, 2025

I think you can just do something like:

	cmd := exec.CommandContext(ctx, "diff", "--label="+filepath.Join("old", path), "--label="+filepath.Join("new/", path), "-u", f1.Name(), f2.Name())
	// override cancel function with KILL with SIGINT
	cmd.Cancel = func() error {
		return cmd.Process.Signal(syscall.SIGINT)
	}
	data, err := cmd.CombinedOutput()

The CommandContext function is:

func CommandContext(ctx context.Context, name string, arg ...string) *Cmd {
	if ctx == nil {
		panic("nil Context")
	}
	cmd := Command(name, arg...)
	cmd.ctx = ctx
	cmd.Cancel = func() error {
		return cmd.Process.Kill()
	}
	return cmd
}

So you can just override the cancel function. You could put that in a wrapper function like you did

@denis256
Copy link
Member

denis256 commented Dec 5, 2025

Branch still fails with

internal/os/exec/cmd.go:34:32: os/exec.Command must not be called. use os/exec.CommandContext (noctx)
		Cmd:             exec.Command(name, args...),
		                             ^
internal/os/exec/cmd.go:134:21: os/exec.Command must not be called. use os/exec.CommandContext (noctx)
	cmd := exec.Command(name, args...)
	                   ^
2 issues:
* noctx: 2

@mowirth
Copy link
Author

mowirth commented Dec 5, 2025

@jordanst3wart Good point, I'll update my fix and this should get rid of the linting messages as well. Thanks a lot!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c78fe80 and c891a9e.

📒 Files selected for processing (5)
  • cli/commands/hcl/format/format.go (2 hunks)
  • engine/engine.go (2 hunks)
  • internal/cas/git.go (3 hunks)
  • internal/os/exec/cmd.go (2 hunks)
  • test/helpers/test_helpers.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • engine/engine.go
  • test/helpers/test_helpers.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

Review the Go code for quality and correctness. Make sure that the Go code follows best practices, is performant, and is easy to understand and maintain.

Files:

  • internal/cas/git.go
  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go
🧠 Learnings (4)
📚 Learning: 2025-07-03T22:05:07.356Z
Learnt from: wakeful
Repo: gruntwork-io/terragrunt PR: 4491
File: test/integration_test.go:4265-4265
Timestamp: 2025-07-03T22:05:07.356Z
Learning: Constants defined in one Go file are accessible from other files in the same package without explicit imports. When reviewing Go code, consider the package-level scope rather than just the individual file scope.

Applied to files:

  • internal/cas/git.go
📚 Learning: 2025-08-19T16:05:54.723Z
Learnt from: Resonance1584
Repo: gruntwork-io/terragrunt PR: 4683
File: go.mod:86-90
Timestamp: 2025-08-19T16:05:54.723Z
Learning: When analyzing Go module dependencies for removal, always check for both direct imports and API usage across all Go files in the repository, not just a quick search. The github.com/mattn/go-zglob library is used for filesystem walking and glob expansion in multiple Terragrunt files including util/file.go, format commands, and AWS provider patch functionality.

Applied to files:

  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go
📚 Learning: 2025-02-10T13:36:19.542Z
Learnt from: levkohimins
Repo: gruntwork-io/terragrunt PR: 3723
File: cli/commands/stack/action.go:160-160
Timestamp: 2025-02-10T13:36:19.542Z
Learning: The project uses a custom error package `github.com/gruntwork-io/terragrunt/internal/errors` which provides similar functionality to `fmt.Errorf` but includes stack traces. Prefer using this package's error functions (e.g., `errors.Errorf`, `errors.New`) over the standard library's error handling.

Applied to files:

  • cli/commands/hcl/format/format.go
  • internal/os/exec/cmd.go
📚 Learning: 2025-04-17T13:02:28.098Z
Learnt from: yhakbar
Repo: gruntwork-io/terragrunt PR: 4169
File: cli/commands/hcl/validate/cli.go:29-60
Timestamp: 2025-04-17T13:02:28.098Z
Learning: Avoid shadowing imported packages with local variables in Go code, such as using a variable named `flags` when the `github.com/gruntwork-io/terragrunt/cli/flags` package is imported. Use more specific variable names like `flagSet` instead.

Applied to files:

  • internal/os/exec/cmd.go
🧬 Code graph analysis (1)
internal/cas/git.go (1)
internal/cas/errors.go (1)
  • WrappedError (34-39)
🔇 Additional comments (1)
internal/cas/git.go (1)

266-266: Good variable rename.

Renaming context to errContext avoids shadowing and improves clarity.

Also applies to: 270-270

@mowirth mowirth reopened this Dec 10, 2025
@mowirth
Copy link
Author

mowirth commented Dec 10, 2025

Apologies for the delay, I've been tied up the last days.

I refactored the fix again and switched to overwriting the cancel function - the strict-lint runs now and the tests look good to.

Please feel free to have another look

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.

Interrupt leaves state locked

5 participants