Skip to content

Write the connect service's EnvironmentFile= as a bare path - #783

Merged
jeremy merged 3 commits into
mainfrom
fix-connect-envfile-quoting
Sep 30, 2026
Merged

jeremy merged 3 commits into
mainfrom
fix-connect-envfile-quoting

Conversation

@jeremy

@jeremy jeremy commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

basecamp connect service install wrote

EnvironmentFile=-"/home/<user>/.config/basecamp/connect/<profile>/service.env"

EnvironmentFile= is not a word-split setting like ExecStart= or Environment=: systemd takes the rest of the line as one path, expands specifiers, and does nothing else. It does not remove quotes and does not decode C escapes. The quoted value is read as a relative path beginning with ", so systemd logs EnvironmentFile= path is not absolute, ignoring and the file never loads. A credential put in service.env, which is what install tells people to do, never reached the connector.

Change

  • EnvironmentFile= is written as a bare path with % doubled. A space needs no escaping here, and there is no escape to use: \x20 stays four literal characters.
  • connectServiceEnvFile refuses a path holding a newline, carriage return or NUL (only reachable through an odd XDG_CONFIG_HOME), because this line has no way to carry one and it would otherwise split into two directives. Same refusal connectServiceEnv already makes for the Environment= values. It also refuses a relative path. GlobalConfigDir keeps a relative XDG_CONFIG_HOME as it is, and systemd ignores a relative EnvironmentFile=. It also refuses *, ?, [ and \, because systemd reads the path as a glob.
  • Install now warns when the unit runs a binary under /mise/installs/, /.asdf/installs/ or /nix/store/. connectServiceExecutable already prefers the PATH name over /proc/self/exe, so a mise shim stays a shim. But a shell with a versioned bin directory on PATH itself (mise activate, or a Go binary installed with mise's Go) pins a path that the next upgrade deletes, and then the unit fails to start at its next restart. The warning is a substring match on those three layouts, so it can miss other version managers. It does not block install.

The other directives the same function writes were checked and are correct as they were. ExecStart= and Environment= are both unquoted by systemd, so the quoting stays. Description= carries only the validated profile name. No WorkingDirectory= is written.

Verification

Tested on systemd 261 (Arch, thelio) with a throwaway user unit and a directory named a b/50%off:

EnvironmentFile= line Result
-"/…/a b/50%%off/s.env" (old form) ignored, EnvironmentFiles= empty
-/…/a b/50%%off/s.env (new form) loaded
-/…/a\x20b/50%%off/s.env not decoded, file not found
-/…/a b/50%off/s.env %o expanded as a specifier, wrong path

Next I loaded the Environment= and EnvironmentFile= lines that connectServiceUnit rendered with this change, using PATH=/opt/50%off/bin:/usr/bin, XDG_CONFIG_HOME=/tmp/x y and an env file under a b/50%off. They went into a oneshot unit running /usr/bin/env, and all three values reached the process exactly.

TestConnectServiceUnitWritesTheEnvironmentFileAsABarePath checks the exact rendered line for a normal path and for one with a space and a %. It fails against the old quoting. These tests are //go:build linux, like the rest of the file. make check passed on Linux.

Not done

  • Not in this PR: a test that runs systemd-analyze verify on the generated unit, which the card suggested. CI and macOS dev machines have no systemd manager to run it against, and systemd-analyze --user verify already fails to start on a plain SSH session on thelio (Failed to initialize manager). A test that skips wherever it cannot run would add nothing. The exact-line assertion holds the output, and the systemd behavior it depends on is recorded in the table above.

EnvironmentFile= takes the rest of the line as one path. systemd does not
remove quotes there or decode C escapes, so the quoted path install wrote
was read as a relative path beginning with a quote and ignored on every
start ("EnvironmentFile= path is not absolute, ignoring"): a credential
put in service.env never reached the connector. Write it bare, doubling
% for specifiers; a space needs nothing. Refuse a config directory holding
a line break, which the line has no way to carry.

Also say at install when the unit runs a binary inside a version
manager's install directory (mise or asdf installs, the Nix store), which
the next upgrade deletes and the unit then fails to start.
Copilot AI balanced review requested due to automatic review settings September 22, 2026 22:06
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-22T22:25:52.303624Z ba19f01 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added commands CLI command implementations tests Tests (unit and e2e) labels Sep 22, 2026

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Relative XDG_CONFIG_HOME values can still generate an invalid environment-file path.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Fixes systemd EnvironmentFile= rendering and warns about unstable version-managed executable paths.

Changes:

  • Emits bare, percent-escaped environment-file paths.
  • Rejects line-breaking path characters.
  • Adds version-manager warnings and tests.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File Description
internal/​commands/​connect_service.go Updates unit rendering, validation, and executable warnings.
internal/​commands/​connect_service_test.go Tests path rendering, validation, and warnings.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/commands/connect_service.go Resolved

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61ac86716c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/commands/connect_service.go Resolved
A relative XDG_CONFIG_HOME is kept as it is, and systemd ignores a
relative EnvironmentFile=, so install would report success while the
file never loaded.
Copilot AI review requested due to automatic review settings September 22, 2026 22:15
Copilot AI previously approved these changes Sep 22, 2026

Copilot AI 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.

Copilot review overview

🟢 Approved

The implementation addresses the systemd parsing failure with focused validation and adequate regression coverage.

Review effort: Balanced
Findings: None

Resolved since last review (1)

systemd reads EnvironmentFile= as a glob, so * ? [ or a backslash in the
config directory would name other files or none.
Copilot AI review requested due to automatic review settings September 22, 2026 22:21
Copilot AI dismissed their stale review, a newer Copilot review was requested September 22, 2026 22:21

Copilot AI 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.

Copilot review overview

🟢 Approved

The implementation addresses the systemd parsing issue with focused validation and regression coverage.

Review effort: Balanced
Findings: None

@jeremy

jeremy commented Sep 22, 2026

Copy link
Copy Markdown
Member Author

Converged on ba19f01: CI is green, and Copilot and Codex have both reviewed this head with no open findings. Both review threads were fixed and resolved: Copilot's on relative paths in 18a38ae and Codex's on glob characters in ba19f01. Ready for a human to merge.

@jeremy
jeremy merged commit e8a16e9 into main Sep 30, 2026
31 checks passed
@jeremy
jeremy deleted the fix-connect-envfile-quoting branch September 30, 2026 03:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands CLI command implementations tests Tests (unit and e2e)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants