pingdotgg / pingdotgg/t3code

[Bug]: `t3 update` / `t3 service install` re-render the service unit and drop every user environment variable, including the documented Bitbucket credentials

Open
#12,626 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

accepted bug via-triage
Dominant language
TypeScript
Stars
23k
Forks
5.9k
Avg merge
11h 14m
Merged PRs (30d)
357

Description

Before submitting
  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.
Area

apps/server

Steps to reproduce
  1. On macOS, run t3 service install.
  2. Follow docs/user/source-control.md for Bitbucket: add T3CODE_BITBUCKET_EMAIL and T3CODE_BITBUCKET_API_TOKEN to the service's environment. The only place launchd takes environment for this job is EnvironmentVariables in ~/Library/LaunchAgents/com.t3tools.t3code.service.plist, so add them there and launchctl bootout / bootstrap.
  3. Confirm Settings → Source Control shows Bitbucket authenticated.
  4. Run t3 update (or run t3 service install again to "repair" the service, as docs/user/background-service.md suggests).
Expected behavior

Environment the user added to the service survives an update or reinstall, or there is a documented place to put service environment that t3 does not overwrite.

Actual behavior

The plist is re-rendered from a fixed template and the user's entries are gone. Bitbucket reverts to "Available. Set T3CODE_BITBUCKET_EMAIL and T3CODE_BITBUCKET_API_TOKEN on the server". The same happens to T3CODE_PORT / T3CODE_HOST pins, so a service pinned off the default port silently moves back to port scanning after an update.

Mechanics:

  • renderBootServicePlist (apps/server/src/cloud/bootService.ts ~L134) emits exactly PATH, T3CODE_HOME, and T3_BOOT_SERVICE_UNIT. renderBootServiceUnit does the same for systemd.
  • install() unconditionally runs writeDurably(unitPath, manager.render(plan)) (~L885). The only value read back from an existing unit is T3CODE_HOME via bootServiceBaseDirOf (~L64).
  • t3 update calls install() on every version switch (apps/server/src/cli/update.ts ~L559, comment: "The unit is rewritten either way").
  • On Linux a systemctl --user edit drop-in would survive this. launchd has no override mechanism, so on macOS there is nowhere to put service environment that t3 will not delete.
  • docs/user/source-control.md tells Bitbucket users to export the variables "in the server's environment" and never mentions the background service; docs/user/background-service.md never mentions environment. Bitbucket is the only provider that needs environment, because it has no CLI to hold credentials.

Suggested fixes (any one would do):

  1. Preserve unknown EnvironmentVariables / Environment= entries when re-rendering an existing unit.
  2. Have the launcher read an optional env file (e.g. $T3CODE_HOME/service.env) and document it.
  3. Store Bitbucket credentials in the server's existing secrets store, settable from Settings → Source Control, so no environment is needed.
Impact

Blocks a workflow (Bitbucket source control cannot be kept working on a macOS background service across updates)

Version or commit

0.0.42 → 0.0.43-nightly.20260919.1962

Environment

macOS 26.6 (Darwin 25.6.0), arm64, background service via launchd

Logs or stack traces
# after `t3 update`, the regenerated plist EnvironmentVariables dict contains only:
PATH, T3CODE_HOME, T3_BOOT_SERVICE_UNIT
Workaround

Keep the variables out of t3's plist: a separate user LaunchAgent that runs launchctl setenv T3CODE_BITBUCKET_EMAIL … ; launchctl setenv T3CODE_BITBUCKET_API_TOKEN … ; launchctl kickstart -k gui/$UID/com.t3tools.t3code.service at login. The service inherits the domain environment and t3 never touches that file.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with apps/server/src/cloud/bootService.ts, especially renderBootServicePlist, renderBootServiceUnit, bootServiceBaseDirOf, and install(), then trace the reinstall call in apps/server/src/cli/update.ts. Compare how existing launchd and systemd units are read and rewritten. Done means user-provided service environment survives update and reinstall on both platforms, with the relevant background-service and source-control documentation updated.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli, devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.