Repository navigation
Give a clear error when devpod is missing - #71
Merged
Merged
Conversation
On a box without devpod, every workspace command printed a raw FileNotFoundError traceback. run_devpod is the only place the package spawns devpod, so the missing binary is translated there into a DevpodNotInstalled exception, caught once in main, reported as one line on stderr, and exited 127 -- the shell's own "command not found" code. DevpodNotInstalled inherits from Exception, deliberately not from OSError. FileNotFoundError *is* an OSError, and dl has a dozen broad OSError handlers; letting it travel as one meant the attach path reported "Failed to create workspace" instead of naming the real cause. It is also not folded into a returncode. Callers branch on returncode, so a synthetic 127 would send them down paths that assume devpod actually ran. A binary that is absent is a different kind of outcome from a command that ran and failed, and it stays out of that value domain. --help and --version never touch devpod and still work. The completion paths (--repos, --completion-data, --update-cache) keep stdout empty, since that is what the completion machinery parses, and --update-cache now leaves a good cache in place rather than overwriting it with an empty one. Closes #57
Reviewer's GuideIntroduce a dedicated DevpodNotInstalled error and centralized handling to provide a clear, single-line message and exit code 127 when the devpod binary is missing, while preserving existing behavior when devpod runs and fails, and adding comprehensive tests and README documentation around this behavior. Sequence diagram for handling missing devpod in the dl CLIsequenceDiagram
actor User
participant dl as main
participant cli as _run_cli
participant devpod as run_devpod
participant sp as subprocess_run
User->>dl: main()
dl->>cli: _run_cli()
cli->>devpod: run_devpod(args, env, capture)
devpod->>sp: subprocess.run(cmd, ...)
sp-->>devpod: FileNotFoundError
devpod->>dl: DevpodNotInstalled(DEVPOD_MISSING_MESSAGE)
dl-->>User: print(DEVPOD_MISSING_MESSAGE) to stderr
dl-->>User: exit DEVPOD_MISSING_EXIT_CODE (127)
File-Level Changes
Assessment against linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #71 +/- ##
==========================================
+ Coverage 87.15% 87.40% +0.24%
==========================================
Files 10 10
Lines 1425 1437 +12
==========================================
+ Hits 1242 1256 +14
+ Misses 183 181 -2
🚀 New features to boost your workflow:
|
The metadata-hardening merge landed with codespell red on main, which blocks every branch cut from it regardless of what that branch changed.
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #57 on map #51.
run_devpodis the only place the package spawns devpod, so the missing binary is translated there, caught once inmain(), and reported as:Two modelling choices, both deliberate
Not an
OSError.FileNotFoundErroris one, anddl.pyhas ~12 broadexcept OSError/except (RuntimeError, OSError)handlers. Letting the condition travel as anOSErrormeant the attach path reportedFailed to create workspace: [Errno 2]...instead of naming the real cause. Verified by experiment: rebasing the exception onOSErrormakes the survival test fail withassert 1 == 127.Not a returncode. A synthetic
127in theCompletedProcesswould send callers — which all branch onreturncode— down paths that assume devpod ran. An absent binary is a different kind of outcome from a command that ran and failed, so it stays out of that value domain.Exit code 127 is the shell's own "command not found" convention, and
dlotherwise propagates devpod's returncodes verbatim.Verified against the real CLI, not just tests
env PATH=/nonexistent python -m devlaunch.dl <arg>, capturing the three streams separately:--help--version--ls,--repos,--completion-data,--update-cachestdout staying empty matters: the completion machinery parses it, so a half-written JSON document or a stray traceback would land in the user's shell.
--help/--versionare also test-asserted never to callsubprocess.runat all.--update-cachenow exits rather than overwriting a good completion cache with an empty one. Nothing leaks into an interactive shell either —update_cache_backgroundspawns it withstdout/stderrset toDEVNULL.Checked by hand
except Exceptioncan swallow it: the five in the package are inworktree/(git and clone work) plusdl.py:920, which sits after itsrun_devpodcall and wraps only clone cleanup.run_devpodpasses nocwd, soFileNotFoundErrorthere can only mean the executable is absent — the message cannot lie about the cause.git— assessed and left aloneThe ticket allowed covering
git"if it falls out naturally". It does not: git is spawned from ~20 sites across four modules, and there is no traceback to fix — with git absent,dl owner/repo@mainalready prints one line (Repository 'owner/repo': [Errno 2] No such file or directory: 'git') and exits 1. Improving that means a new exception threaded through three modules, or ashutil.whichprobe covering only the clone path. Separate ticket.Verification
pixi run ci-no-covergreen: 480 passed, 8 deselected (+13).pylint10.00/10,ruff,tyclean.pixi.lockuntouched. README documents the hint and the exit code.Summary by Sourcery
Handle missing devpod binary as a dedicated error with a clear message and consistent exit code across the CLI.
Bug Fixes:
Enhancements:
Documentation:
Tests: