Skip to content

fix(daemon): capture the installing shell's PATH in the service unit - #10

Closed
peterkc wants to merge 4 commits into
mainfrom
fix/install-service-path
Closed

peterkc wants to merge 4 commits into
mainfrom
fix/install-service-path

Conversation

@peterkc

@peterkc peterkc commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

gortex daemon install-service writes the launchd plist with a fixed PATH (/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin) and the systemd unit with no PATH. The supervised daemon then cannot find language servers installed elsewhere, such as dotnet in /usr/local/share/dotnet or tools in ~/go/bin. A user who adds them to the plist by hand loses them on the next install-service.

This change captures the installing shell's PATH into the unit, the same way xdgServiceEnv already captures XDG_*.

Before (install-service)
  launchd plist: PATH=/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin (fixed)
  systemd unit:  no PATH (systemd default)

After (install-service)
  shell PATH --> absolute entries, first occurrence, shell order
    launchd: missing old defaults appended at the end
    systemd: Environment="PATH=..." only when non-empty

Changes

  • New servicePath helper in daemon_service.go. It keeps only absolute entries, because an empty or relative entry such as . would resolve against the service's working directory. It drops duplicates and keeps the shell's order. Entries that do not exist yet are kept, so a tool installed later is still found.
  • launchd: the four old defaults are appended when missing. An empty or all-relative PATH renders the old value exactly.
  • systemd: the PATH assignment goes through systemdEnvValue and is written only when something was captured. Otherwise systemd's default applies, as before. The macOS defaults are not added on Linux.
  • No other variables are captured. In particular, GORTEX_DAEMON_HTTP_TOKEN stays out of the plist, which is written with mode 0644. A test checks this.
  • The Windows task is unchanged. As with XDG_*, re-run install-service to pick up a changed PATH.
  • Review follow-ups:
    • servicePath drops an entry that contains CR or LF, because it would split the systemd Environment= line.
    • The systemd renderer now quotes the whole KEY=value assignment (Environment="PATH=/a b"), because systemd recognizes a quote only at the start of an item. This also applies to the existing XDG_* lines, and the TestRenderSystemdUnit_PropagatesXDG assertion changes to that form.
    • An assignment that contains a backslash or a quote is quoted the same way, because systemd applies C-style escapes such as \t to unquoted text and treats an unquoted quote as the start of a quoted section.
  • TestRenderSystemdUnit_NoXDG now sets an empty PATH, so it still tests the "nothing captured" case instead of inheriting the test runner's PATH. Its assertions are unchanged.

Testing

  • All tests pass (go test -race ./...)
  • New tests added for new functionality
  • Benchmarks run if performance-relevant
  • go test -race -run 'TestRenderLaunchdPlist|TestRenderSystemdUnit|TestXDGServiceEnv|TestServicePath|TestSystemdEnvValue' ./cmd/gortex/, go vet and golangci-lint run ./cmd/gortex/ pass. When the defaults are put before the shell's entries, 2 of the new tests fail. Not tested: a live service install on macOS or Linux.

Checklist

  • Code follows existing patterns in the codebase
  • No unnecessary abstractions added
  • Language extractor includes Meta["methods"] for interfaces (if applicable)
  • Methods have EdgeMemberOf edges to their containing type (if applicable)

The launchd plist hard-coded PATH to /usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin
and the systemd unit set none, so language servers installed elsewhere (for
example /usr/local/share/dotnet or ~/go/bin) were not found by the supervised
daemon, and a hand-edited PATH was lost on the next install-service.

install-service now captures the absolute entries of the installing shell's
PATH, without duplicates and in the shell's order, like xdgServiceEnv does for
XDG_*. launchd appends any missing old defaults, so an empty PATH renders the
old value. systemd gets Environment=PATH only when something was captured. No
other variables are captured.
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Essentials
  • Run ID: 208ed100-ae11-45c0-96bc-ee63b1dbfc1b
📥 Commits

Reviewing files that changed from the base of the PR and between b42eb3f and db6bd6e.

📒 Files selected for processing (2)
  • cmd/gortex/daemon_service.go
  • cmd/gortex/daemon_service_test.go

Included review availability: This review used your included allowance. 3 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour. Your free on-demand review promotion remains active until October 9, 2026 at 6:00 PM UTC.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (9)
  • GitHub Check: build-linux-static
  • GitHub Check: build-onnx
  • GitHub Check: test (ubuntu-latest, 1.27)
  • GitHub Check: benchmark
  • GitHub Check: govulncheck
  • GitHub Check: test (macos-latest, 1.27)
  • GitHub Check: test (windows-latest, 1.27)
  • GitHub Check: lint
  • GitHub Check: skill-drift
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: peterkc
Repo: peterkc/gortex PR: 10
File: cmd/gortex/daemon_service.go:630-630
Timestamp: 2026-10-02T23:27:43.014Z
Learning: For systemd Environment= rendering in cmd/gortex/daemon_service.go, extract_first_word with EXTRACT_CUNESCAPE applies C-style escapes outside quotes too, and unquoted single or double quotes start quoted sections. systemdEnvValue must quote the whole KEY=value assignment when it contains whitespace, backslashes, or either quote character, and escape backslashes and double quotes inside the double-quoted assignment.
Learnt from: peterkc
Repo: peterkc/gortex

Timestamp: 2026-10-02T22:30:45.711Z
Learning: In cmd/gortex/daemon_service.go, servicePath deliberately drops PATH entries containing CR or LF rather than failing install-service. This capture policy excludes those entries from both systemd units and launchd plists. Do not justify this policy by claiming that Unix directory names cannot contain CR or LF.
🔇 Additional comments (2)
cmd/gortex/daemon_service.go (1)

139-143: LGTM!

Also applies to: 146-146

cmd/gortex/daemon_service_test.go (1)

286-294: LGTM!


📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • Service installations now preserve valid absolute entries from the installing shell’s PATH, retaining their order and removing duplicates and entries containing line breaks. Launchd services add standard paths when needed; systemd services omit PATH when no valid entries are available.
    • Systemd environment assignments containing whitespace, backslashes, or double quotes are now escaped and quoted as complete assignments. Percent-sign escaping remains supported.

Walkthrough

Service-unit generation captures absolute shell PATH entries, removes duplicates, and excludes entries containing CR or LF. Launchd adds default directories when missing. Systemd includes PATH only when valid entries remain and escapes each complete environment assignment.

Changes

Service PATH capture

Layer / File(s) Summary
Normalize captured PATH
cmd/gortex/daemon_service.go, cmd/gortex/daemon_service_test.go
servicePath filters entries, preserves first-occurrence order, and appends supplied defaults. Tests cover filtering, ordering, defaults, and empty output.
Apply PATH to service formats
cmd/gortex/daemon_service.go, cmd/gortex/daemon_service_test.go
Launchd uses captured PATH with its defaults. Systemd emits PATH only when valid entries remain and escapes complete environment assignments. Tests cover renderer output and empty PATH cases.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to db6bd

The service captures PATH as intended, with launchd defaults and escaped systemd output. No concrete issue remains that should block merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: capturing the installing shell's PATH in the service unit. It exceeds the preferred 50-character length at 68 characters, but the length guideline is state…
Description check ✅ Passed The description directly explains the PATH capture behavior, launchd and systemd changes, security considerations, tests, and known testing limits.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Essentials

Run ID: ae03604f-b8d6-4705-8dd2-243d19dba426

📥 Commits

Reviewing files that changed from the base of the PR and between 84e009b and ab27f7c.

📒 Files selected for processing (2)
  • cmd/gortex/daemon_service.go
  • cmd/gortex/daemon_service_test.go

Included review availability: This review used your included allowance. 4 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour. Your free on-demand review promotion remains active until October 9, 2026 at 6:00 PM UTC.

📜 Review details
⏰ Context from checks skipped due to timeout. (10)
  • GitHub Check: test (ubuntu-latest, 1.27)
  • GitHub Check: test (macos-latest, 1.27)
  • GitHub Check: lint
  • GitHub Check: build-linux-static
  • GitHub Check: trivy-fs
  • GitHub Check: benchmark
  • GitHub Check: test (windows-latest, 1.27)
  • GitHub Check: build-onnx
  • GitHub Check: govulncheck
  • GitHub Check: skill-drift

Comment thread cmd/gortex/daemon_service.go Outdated
Comment thread cmd/gortex/daemon_service.go
A captured PATH entry with a line break would split the systemd unit's
Environment= line into a separate directive. Such an entry cannot name a
usable directory, so servicePath now skips it like a relative entry.
systemd.syntax(7) recognizes a quote only at the start of an item, so
Environment=KEY="a b" does not unquote the value. When a captured PATH or
XDG_* value contains whitespace, quote the complete KEY=value assignment
instead, as systemd.exec(5) shows: Environment="KEY=a b".

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Essentials
  • Run ID: bf2162ee-7ca0-4e42-bfc3-4c9ca37d16aa
📥 Commits

Reviewing files that changed from the base of the PR and between ab27f7c and b42eb3f.

📒 Files selected for processing (2)
  • cmd/gortex/daemon_service.go
  • cmd/gortex/daemon_service_test.go

Included review availability: This review used your included allowance. 4 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour. Your free on-demand review promotion remains active until October 9, 2026 at 6:00 PM UTC.

📜 Review details
⏰ Context from checks skipped due to timeout. (9)
  • GitHub Check: benchmark
  • GitHub Check: govulncheck
  • GitHub Check: test (windows-latest, 1.27)
  • GitHub Check: test (ubuntu-latest, 1.27)
  • GitHub Check: build-linux-static
  • GitHub Check: test (macos-latest, 1.27)
  • GitHub Check: lint
  • GitHub Check: skill-drift
  • GitHub Check: build-onnx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: peterkc
Repo: peterkc/gortex

Timestamp: 2026-10-02T22:30:45.711Z
Learning: In cmd/gortex/daemon_service.go, servicePath deliberately drops PATH entries containing CR or LF rather than failing install-service. This capture policy excludes those entries from both systemd units and launchd plists. Do not justify this policy by claiming that Unix directory names cannot contain CR or LF.

Comment thread cmd/gortex/daemon_service.go
systemd applies C-style escapes to unquoted Environment= text, so a PATH
entry such as /opt/a\tools would reach the daemon with a tab in it. An
unquoted quote character would start a quoted section. systemdEnvValue
now also quotes the whole assignment when it contains a backslash or a
quote, and escapes backslashes and double quotes inside the quotes.
@peterkc

peterkc commented Oct 3, 2026

Copy link
Copy Markdown
Owner Author

Continued upstream as zzet#864.

@peterkc peterkc closed this Oct 3, 2026
@peterkc
peterkc deleted the fix/install-service-path branch October 3, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant