fix: inherit environment in daemon subprocess calls (#876)
The daemon's exec.Command calls were not explicitly setting cmd.Env, causing subprocesses to fail when the daemon process doesn't have the expected PATH environment variable. This manifests as: Warning: failed to fetch deacon inbox: exec: "gt": executable file not found in $PATH When the daemon is started by mechanisms with minimal environments (launchd, systemd, or shells without full PATH), executables like gt, bd, git, and sqlite3 couldn't be found. The fix adds cmd.Env = os.Environ() to all 15 subprocess calls across three files, ensuring they inherit the daemon's full environment. Affected commands: - gt mail inbox/delete/send (lifecycle requests, notifications) - bd sync/show/list/activity (beads operations) - git fetch/pull (workspace pre-sync) - sqlite3 (convoy completion queries) Fixes #875 Co-authored-by: Jackson Cantrell <cantrelljax@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -87,6 +88,7 @@ func (w *ConvoyWatcher) run() {
|
||||
func (w *ConvoyWatcher) watchActivity() error {
|
||||
cmd := exec.CommandContext(w.ctx, "bd", "activity", "--follow", "--town", "--json")
|
||||
cmd.Dir = w.townRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find bd executable
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
@@ -168,6 +170,7 @@ func (w *ConvoyWatcher) getTrackingConvoys(issueID string) []string {
|
||||
`, safeIssueID, safeIssueID)
|
||||
|
||||
queryCmd := exec.Command("sqlite3", "-json", dbPath, query)
|
||||
queryCmd.Env = os.Environ() // Inherit PATH to find sqlite3 executable
|
||||
var stdout bytes.Buffer
|
||||
queryCmd.Stdout = &stdout
|
||||
|
||||
@@ -200,6 +203,7 @@ func (w *ConvoyWatcher) checkConvoyCompletion(convoyID string) {
|
||||
strings.ReplaceAll(convoyID, "'", "''"))
|
||||
|
||||
queryCmd := exec.Command("sqlite3", "-json", dbPath, convoyQuery)
|
||||
queryCmd.Env = os.Environ() // Inherit PATH to find sqlite3 executable
|
||||
var stdout bytes.Buffer
|
||||
queryCmd.Stdout = &stdout
|
||||
|
||||
@@ -224,6 +228,7 @@ func (w *ConvoyWatcher) checkConvoyCompletion(convoyID string) {
|
||||
|
||||
checkCmd := exec.Command("gt", "convoy", "check", convoyID)
|
||||
checkCmd.Dir = w.townRoot
|
||||
checkCmd.Env = os.Environ() // Inherit PATH to find gt executable
|
||||
var checkStdout, checkStderr bytes.Buffer
|
||||
checkCmd.Stdout = &checkStdout
|
||||
checkCmd.Stderr = &checkStderr
|
||||
|
||||
@@ -1101,6 +1101,7 @@ Manual intervention may be required.`,
|
||||
|
||||
cmd := exec.Command("gt", "mail", "send", witnessAddr, "-s", subject, "-m", body) //nolint:gosec // G204: args are constructed internally
|
||||
cmd.Dir = d.config.TownRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find gt executable
|
||||
if err := cmd.Run(); err != nil {
|
||||
d.logger.Printf("Warning: failed to notify witness of crashed polecat: %v", err)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ func (d *Daemon) ProcessLifecycleRequests() {
|
||||
// Get mail for deacon identity (using gt mail, not bd mail)
|
||||
cmd := exec.Command("gt", "mail", "inbox", "--identity", "deacon/", "--json")
|
||||
cmd.Dir = d.config.TownRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find gt executable
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
@@ -576,6 +577,7 @@ func (d *Daemon) syncWorkspace(workDir string) {
|
||||
fetchCmd := exec.Command("git", "fetch", "origin")
|
||||
fetchCmd.Dir = workDir
|
||||
fetchCmd.Stderr = &stderr
|
||||
fetchCmd.Env = os.Environ() // Inherit PATH to find git executable
|
||||
if err := fetchCmd.Run(); err != nil {
|
||||
errMsg := strings.TrimSpace(stderr.String())
|
||||
if errMsg == "" {
|
||||
@@ -592,6 +594,7 @@ func (d *Daemon) syncWorkspace(workDir string) {
|
||||
pullCmd := exec.Command("git", "pull", "--rebase", "origin", defaultBranch)
|
||||
pullCmd.Dir = workDir
|
||||
pullCmd.Stderr = &stderr
|
||||
pullCmd.Env = os.Environ() // Inherit PATH to find git executable
|
||||
if err := pullCmd.Run(); err != nil {
|
||||
errMsg := strings.TrimSpace(stderr.String())
|
||||
if errMsg == "" {
|
||||
@@ -608,6 +611,7 @@ func (d *Daemon) syncWorkspace(workDir string) {
|
||||
bdCmd := exec.Command("bd", "sync")
|
||||
bdCmd.Dir = workDir
|
||||
bdCmd.Stderr = &stderr
|
||||
bdCmd.Env = os.Environ() // Inherit PATH to find bd executable
|
||||
if err := bdCmd.Run(); err != nil {
|
||||
errMsg := strings.TrimSpace(stderr.String())
|
||||
if errMsg == "" {
|
||||
@@ -625,6 +629,7 @@ func (d *Daemon) closeMessage(id string) error {
|
||||
// Use gt mail delete to actually remove the message
|
||||
cmd := exec.Command("gt", "mail", "delete", id)
|
||||
cmd.Dir = d.config.TownRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find gt executable
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -662,6 +667,7 @@ func (d *Daemon) getAgentBeadState(agentBeadID string) (string, error) {
|
||||
func (d *Daemon) getAgentBeadInfo(agentBeadID string) (*AgentBeadInfo, error) {
|
||||
cmd := exec.Command("bd", "show", agentBeadID, "--json")
|
||||
cmd.Dir = d.config.TownRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find bd executable
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
@@ -799,6 +805,7 @@ func (d *Daemon) checkRigGUPPViolations(rigName string) {
|
||||
// Pattern: <prefix>-<rig>-polecat-<name> (e.g., gt-gastown-polecat-Toast)
|
||||
cmd := exec.Command("bd", "list", "--type=agent", "--json")
|
||||
cmd.Dir = d.config.TownRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find bd executable
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
@@ -874,6 +881,7 @@ Action needed: Check if agent is alive and responsive. Consider restarting if st
|
||||
|
||||
cmd := exec.Command("gt", "mail", "send", witnessAddr, "-s", subject, "-m", body)
|
||||
cmd.Dir = d.config.TownRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find gt executable
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
d.logger.Printf("Warning: failed to notify witness of GUPP violation: %v", err)
|
||||
@@ -897,6 +905,7 @@ func (d *Daemon) checkOrphanedWork() {
|
||||
func (d *Daemon) checkRigOrphanedWork(rigName string) {
|
||||
cmd := exec.Command("bd", "list", "--type=agent", "--json")
|
||||
cmd.Dir = d.config.TownRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find bd executable
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
@@ -970,6 +979,7 @@ Action needed: Either restart the agent or reassign the work.`,
|
||||
|
||||
cmd := exec.Command("gt", "mail", "send", witnessAddr, "-s", subject, "-m", body)
|
||||
cmd.Dir = d.config.TownRoot
|
||||
cmd.Env = os.Environ() // Inherit PATH to find gt executable
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
d.logger.Printf("Warning: failed to notify witness of orphaned work: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user