start spawns a new process. Both foreground and background processes use a long-lived context so the process survives the HTTP request lifecycle. The background flag only affects client-side polling behavior.
(req workspacesdk.StartProcessRequest, chatID string)
| 98 | // the HTTP request lifecycle. The background flag only affects |
| 99 | // client-side polling behavior. |
| 100 | func (m *manager) start(req workspacesdk.StartProcessRequest, chatID string) (*process, error) { |
| 101 | m.mu.Lock() |
| 102 | if m.closed { |
| 103 | m.mu.Unlock() |
| 104 | return nil, xerrors.New("manager is closed") |
| 105 | } |
| 106 | m.mu.Unlock() |
| 107 | |
| 108 | id := uuid.New().String() |
| 109 | logger := m.logger |
| 110 | if chatID != "" { |
| 111 | logger = logger.With(slog.F("chat_id", chatID)) |
| 112 | } |
| 113 | |
| 114 | // Use a cancellable context so Close() can terminate |
| 115 | // all processes. context.Background() is the parent so |
| 116 | // the process is not tied to any HTTP request. |
| 117 | ctx, cancel := context.WithCancel(context.Background()) |
| 118 | cmd := m.execer.CommandContext(ctx, "sh", "-c", req.Command) |
| 119 | cmd.Dir = m.resolveWorkDir(req.WorkDir) |
| 120 | cmd.Stdin = nil |
| 121 | cmd.SysProcAttr = procSysProcAttr() |
| 122 | |
| 123 | // WaitDelay ensures cmd.Wait returns promptly after |
| 124 | // the process is killed, even if child processes are |
| 125 | // still holding the stdout/stderr pipes open. |
| 126 | cmd.WaitDelay = 5 * time.Second |
| 127 | |
| 128 | buf := NewHeadTailBuffer() |
| 129 | cmd.Stdout = buf |
| 130 | cmd.Stderr = buf |
| 131 | |
| 132 | // Build the process environment. If the manager has an |
| 133 | // updateEnv hook (provided by the agent), use it to get the |
| 134 | // full agent environment including GIT_ASKPASS, CODER_* vars, |
| 135 | // etc. Otherwise fall back to the current process env. |
| 136 | baseEnv := os.Environ() |
| 137 | if m.updateEnv != nil { |
| 138 | updated, err := m.updateEnv(baseEnv) |
| 139 | if err != nil { |
| 140 | logger.Warn( |
| 141 | context.Background(), |
| 142 | "failed to update command environment, falling back to os env", |
| 143 | slog.Error(err), |
| 144 | ) |
| 145 | } else { |
| 146 | baseEnv = updated |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | // Always set cmd.Env explicitly so that req.Env overrides |
| 151 | // are applied on top of the full agent environment. |
| 152 | cmd.Env = baseEnv |
| 153 | for k, v := range req.Env { |
| 154 | cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v)) |
| 155 | } |
| 156 | // Propagate the chat ID so child processes (e.g. |
| 157 | // GIT_ASKPASS) can send it back to the server. |
nothing calls this directly
no test coverage detected