feat(executor): implement command executor with timeout and retry #186

Merged
fuzzy merged 2 commits from feature/implement-command-executor into main 2026-07-06 08:29:28 +00:00
Owner

What

Implement the command executor in internal/executor/executor.go:

  • RunActions() executes matched rules' commands asynchronously (#72)
  • Commands run via /bin/sh -c for full shell support (#70)
  • stdout/stderr captured via CombinedOutput (#71)
  • Per-action configurable timeout via timeout field, defaults to 30s (#73)
  • Retry logic via retry field — retries N times on failure (#74)
  • Template interpolation applied before execution
  • All results logged with rule/action context

Extended models.Action with Timeout and Retry fields.

Why

The final step in the webhook pipeline. Rules match → templates render → commands execute. This closes the loop from webhook receipt to action execution.

Testing

  • Unit tests pass — 5 tests: success, fail, template, multiple rules, retry
  • Integration tests pass — N/A
  • Manual testing performed — go test ./... and pre-commit pass
  • New tests added for new functionality — executor_test.go

Breaking Changes

  • Action struct gained timeout and retry fields (backwards compatible: zero-values mean defaults)

Notes

  • Each rule spawns its own goroutine; actions within a rule run sequentially
  • Default timeout is 30 seconds if not specified
  • Invalid timeout strings silently fall back to default
  • Retry count includes the initial attempt (retry: 2 = 1 initial + 2 retries)

Closes #75
Closes #70
Closes #71
Closes #72
Closes #73
Closes #74

## What Implement the command executor in `internal/executor/executor.go`: - `RunActions()` executes matched rules' commands asynchronously (#72) - Commands run via `/bin/sh -c` for full shell support (#70) - stdout/stderr captured via `CombinedOutput` (#71) - Per-action configurable timeout via `timeout` field, defaults to 30s (#73) - Retry logic via `retry` field — retries N times on failure (#74) - Template interpolation applied before execution - All results logged with rule/action context Extended `models.Action` with `Timeout` and `Retry` fields. ## Why The final step in the webhook pipeline. Rules match → templates render → commands execute. This closes the loop from webhook receipt to action execution. ## Testing - [x] Unit tests pass — 5 tests: success, fail, template, multiple rules, retry - [ ] Integration tests pass — N/A - [x] Manual testing performed — `go test ./...` and pre-commit pass - [x] New tests added for new functionality — `executor_test.go` ## Breaking Changes - `Action` struct gained `timeout` and `retry` fields (backwards compatible: zero-values mean defaults) ## Notes - Each rule spawns its own goroutine; actions within a rule run sequentially - Default timeout is 30 seconds if not specified - Invalid timeout strings silently fall back to default - Retry count includes the initial attempt (retry: 2 = 1 initial + 2 retries) Closes #75 Closes #70 Closes #71 Closes #72 Closes #73 Closes #74
Add RunActions() that:
- Executes commands via /bin/sh -c for full shell support
- Captures stdout/stderr via CombinedOutput
- Runs each rule's actions in a goroutine (async per rule)
- Supports per-action timeout (default 30s)
- Supports per-action retry with configurable attempts
- Logs execution results with leveled logger
- Interpolates templates before execution

Added Timeout and Retry fields to Action model.

Closes #70
Closes #71
Closes #72
Closes #73
Closes #74
docs: mark Implement command executor tasks as complete
All checks were successful
CI / Lint (golangci-lint) (pull_request) Successful in 1m38s
CI / Pre-commit Hooks (pull_request) Successful in 2m44s
CI / Test (pull_request) Successful in 1m21s
CI / Pre-commit Hooks (push) Successful in 2m36s
CI / Lint (golangci-lint) (push) Successful in 1m35s
CI / Test (push) Successful in 1m22s
c15e10d4c5
Closes #75
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-07-06 08:25:02 +00:00
the.auditor left a comment

Review

Solid command executor implementation. All 12 tests pass (5 executor + 7 template). Lint and vet clean.

Design highlights:

  • RunActions spawns goroutines per rule, actions run sequentially within each rule
  • Template interpolation via Interpolate() before execution
  • /bin/sh -c for full shell support
  • CombinedOutput captures both stdout/stderr
  • Timeout via context.WithTimeout, parseTimeout handles empty/invalid gracefully
  • Retry logic with properly scoped goroutine closure (func(r models.Rule))

Models: Action extended with Timeout string and Retry int (zero-value defaults). Backward compatible.

Non-blocking:

  • Result.Action field in the struct is never populated. Consider removing or populating it for completeness.

No blocking issues.

APPROVED

## Review Solid command executor implementation. All 12 tests pass (5 executor + 7 template). Lint and vet clean. **Design highlights**: - `RunActions` spawns goroutines per rule, actions run sequentially within each rule - Template interpolation via `Interpolate()` before execution - `/bin/sh -c` for full shell support - `CombinedOutput` captures both stdout/stderr - Timeout via `context.WithTimeout`, `parseTimeout` handles empty/invalid gracefully - Retry logic with properly scoped goroutine closure (`func(r models.Rule)`) **Models**: `Action` extended with `Timeout string` and `Retry int` (zero-value defaults). Backward compatible. **Non-blocking**: - `Result.Action` field in the struct is never populated. Consider removing or populating it for completeness. **No blocking issues.** **APPROVED**
fuzzy merged commit c15e10d4c5 into main 2026-07-06 08:29:28 +00:00
fuzzy deleted branch feature/implement-command-executor 2026-07-06 08:29:29 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/ttheart!186
No description provided.