Skip to content

fix(services): list running services started from a custom process-compose file - #2920

Merged
mikeland73 merged 3 commits into
mainfrom
claude/focused-goldberg-m7isxa
Sep 15, 2026
Merged

mikeland73 merged 3 commits into
mainfrom
claude/focused-goldberg-m7isxa

Conversation

@mikeland73

Copy link
Copy Markdown
Collaborator

Summary

Fixes #2611.

devbox services ls did not display services that were started with a custom
--process-compose-file, e.g.:

devbox services up -d --process-compose-file my-process-compose.yaml
devbox services ls   # -> "No services found in your project"

Root cause

ListServices (internal/devbox/services.go) fetched the project's
statically defined service set via d.Services() and returned early with
"No services found in your project" whenever that set was empty — before
ever checking whether the process manager was running:

svcSet, err := d.Services()
...
if len(svcSet) == 0 {
    fmt.Fprintln(d.stderr, "No services found in your project")
    return nil   // <- returns here, never queries the running instance
}

d.Services() is built from devbox.json plugin services plus
FromUserProcessCompose(projectDir, d.customProcessComposeFile). When ls is
invoked (a separate process from up), customProcessComposeFile is empty, so
it only reads the default process-compose.yaml. Services defined solely in a
custom-named compose file are therefore absent from the set, and the command
returned early even though process-compose was actively running them.

Meanwhile services.ListServices connects to the running process-compose
server by port (GetProcessManagerPort) and lists the live processes — it does
not depend on the compose file or the static set at all. The early return
simply prevented that call from happening.

Fix

Reorder the logic so that when the process manager is running we list the
services it is actually running, regardless of d.Services(). The statically
defined set is now only consulted as a fallback when the process manager is
not running (to show the available-but-not-running services, or the
"No services found" message). No behavior changes for existing cases where the
static set is non-empty.

How was it tested?

  • go build ./internal/devbox/ and go vet ./internal/devbox/ are clean.
  • Reviewed the branch logic against every case:
    • PM running → lists live processes (now includes custom-file services); fixes the bug.
    • PM not running + static services defined → "No services currently running…" + list (unchanged).
    • PM not running + no static services → "No services found in your project" (unchanged).
  • The internal/devbox package's remaining test failures in this sandbox are
    pre-existing and environmental (they require the nix binary, which is not
    installed here); they do not exercise ListServices.

Manual reproduction from the issue:

devbox init
cat > my-process-compose.yaml <<'YAML'
version: "0.5"
processes:
  process1:
    command: "sleep 1000"
YAML
devbox services up -d --process-compose-file my-process-compose.yaml
devbox services ls   # now shows process1

cc @szymon-filipiak (issue reporter)

Community Contribution License

All community contributions in this pull request are licensed to the project
maintainers under the terms of the
Apache 2 License.

By creating this pull request, I represent that I have the right to license the
contributions to the project maintainers under the Apache 2 License as stated in
the
Community Contribution License.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AqRoYD21ykMzWYVBsw3MMa


Generated by Claude Code

`devbox services ls` early-returned "No services found in your project"
whenever `d.Services()` (the project's statically defined service set from
devbox.json plugins and the default process-compose.yaml) was empty. This
happened before it ever checked whether the process manager was running, so
services started with `devbox services up --process-compose-file <custom.yaml>`
were never listed — even though process-compose was running them.

Reorder the logic so that when the process manager is running we list the
services it is actually running via `services.ListServices`, which queries the
running process-compose server directly and is independent of any compose file
or the static service set. The static set is now only consulted as a fallback
when the process manager is not running.

Fixes #2611

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqRoYD21ykMzWYVBsw3MMa
Copilot AI review requested due to automatic review settings July 13, 2026 14:13

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.

Pull request overview

This PR fixes devbox services ls failing to list services started via a custom --process-compose-file by ensuring ls queries the running process-compose instance first (instead of returning early based on the statically defined service set).

Changes:

  • Reordered ListServices logic to prefer live process-compose service listing when the process manager is running.
  • Kept the static service-set listing as a fallback path when the process manager is not running.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +111 to +123
pcSvcs, err := services.ListServices(ctx, d.projectDir, d.stderr)
if err != nil {
fmt.Fprintln(d.stderr, "Error listing services: ", err)
} else {
fmt.Fprintln(d.stderr, "Services running in process-compose:")
fmt.Fprintln(tw, "PID\tNAME\tNAMESPACE\tSTATUS\tAGE\tHEALTH\tRESTARTS\tEXIT CODE")
for _, s := range pcSvcs {
fmt.Fprintf(tw, "%d\t%s\t%s\t%s\t%s\t%s\t%d\t%d\n", s.PID, s.Name, s.Namespace, s.Status, s.Age, s.Health, s.Restarts, s.ExitCode)
}
tw.Flush()
}
return nil
}
Previously `devbox services ls` printed the error from
services.ListServices but returned nil, so the command exited 0 even when
it could not reach the running process-compose server — hiding failures in
scripts and CI. Return the (wrapped) error so the command exits non-zero.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AqRoYD21ykMzWYVBsw3MMa
@savil
savil marked this pull request as draft September 2, 2026 22:14
@mikeland73
mikeland73 marked this pull request as ready for review September 15, 2026 20:21
@mikeland73

Copy link
Copy Markdown
Collaborator Author

@claude please review this PR for correctness, and flag anything that would block merging.

@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown

Claude finished @mikeland73's task in 1m 7s —— View job


Review: PR #2920 — fix(services): list running services from a custom process-compose file

  • Gather context (PR diff, current file state, prior review comments)
  • Review ListServices logic for correctness
  • Check whether the Copilot review comment (swallowed error) is already addressed
  • Check other affected call sites / tests
  • Post final review

No blockers found. This looks correct and safe to merge.

Findings

  1. Copilot's error-swallowing concern is already resolved. The second commit (7974f05) changed the running-PM branch in internal/devbox/services.go:110-126 to return errors.WithStack(err) instead of just printing and returning nil. So devbox services ls now correctly exits non-zero if it can't reach the process-compose server. github.com/pkg/errors is already an established import pattern in this package (used in devbox.go, global.go, shell.go, etc.), so this is consistent with the rest of the codebase.

  2. Core fix logic is sound. The reordering correctly prioritizes querying the live process-compose instance (services.ListServices, which talks to the running server by port and is independent of any compose file) over the static d.Services() set. This matches the root cause described in the PR: d.Services() only reads devbox.json + the default process-compose.yaml (or d.customProcessComposeFile, which is empty in a separate ls invocation), so services defined only in a custom-named compose file were previously invisible. The three cases all check out:

  3. Minor race window (not a blocker): services.ProcessManagerIsRunning and the subsequent services.ListServices call are not atomic — if the process manager stops in between, ListServices will now return an error (since it hits errors.WithStack), causing ls to exit non-zero instead of falling back to "No services found." This is a pre-existing narrow race, arguably correct behavior (a real connection failure is being surfaced rather than hidden), and not something this PR needs to solve.

  4. No new tests were added, but there don't appear to be any existing tests for ListServices in this package (grep for ListServices in *_test.go files under the repo returned no matches), so this isn't a regression in test coverage — consistent with the PR's own testing notes.

Verification limitations

I was not able to run go build / go vet / go test in this sandbox — Bash execution requires interactive approval that wasn't available here. I did carefully review the diff (git diff be5b810..HEAD -- internal/devbox/services.go) and the full resulting file by reading it directly; the logic is straightforward Go with no syntax concerns. The PR description states go build and go vet were already run clean by the author.

Copy link
Copy Markdown
Collaborator Author

I reviewed the diff for correctness and merge-readiness. The change is correct and I don't see anything that blocks merging. CI is fully green (all 25 checks).

Correctness — behavior matrix (old → new)

process-compose running? static set d.Services() before after
yes empty ❌ "No services found in your project" (the bug) ✅ lists live services
yes non-empty lists live services lists live services
no empty "No services found in your project" same
no non-empty "No services currently running…" + list same

The reorder fixes the reported case — services started from a custom --process-compose-file weren't listed because the statically-defined set was empty and the function returned before ever querying the running manager — and preserves every other case. services.ListServices talks to the running process-compose server by port (GetProcessManagerPort), so it's correctly independent of which compose file started it.

Non-blocking notes

  1. No automated test. ListServices depends on a live process-compose server plus on-disk state (ProcessManagerIsRunning), so there's no cheap unit test; I verified via the matrix above + green CI. I can add coverage by extracting the branch decision behind a small seam, but that's a larger refactor than this fix warrants — happy to if you'd like it.

  2. Intentional behavior change (from the Copilot note): when the manager is running but listing fails, ls now returns the error (exit non-zero) instead of printing Error listing services: and exiting 0. This makes failures visible in scripts/CI. Easy to revert to the old best-effort behavior if you'd rather keep exit 0.

  3. Scope. This fixes devbox services ls. The issue body also mentions devbox services stop: stopping/restarting an individual named service still gates on the static set (services.go:86-89) and would reject a custom-file service with "Service X not found" — though stop with no args (whole manager) works. Fixes #2611 will auto-close the issue on merge, so if you want the stop/restart case covered too, say the word and I'll extend this PR (and keep the issue open until then).


Generated by Claude Code

…rg-m7isxa

# Conflicts:
#	internal/devbox/services.go
@mikeland73
mikeland73 merged commit 0d14196 into main Sep 15, 2026
28 checks passed
@mikeland73
mikeland73 deleted the claude/focused-goldberg-m7isxa branch September 15, 2026 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

devbox services ls doesn't list services started with --process-compose-file

3 participants