v0.32.0
Added
-
A launch that attaches to a workspace devpod already has now says how far
behind its checkout is.dl owner/repo@branchagainst a running workspace
runs no git and never has -- that is the decision that makes a hot attach hot --
so the checkout inside the container is whatever you left there, and nothing
said so at the moment you were about to conclude something from it. One session
went the whole way to "this devcontainer boots correctly" against a container
built before the repository had a.devcontainer/at all.The line is computed from
dl's own clone and reaches no network:Workspace devlaunch-main-3j1t: its checkout is 37 commits behind origin/main as this workspace last fetched it, and a launch of a workspace devpod already has fetches nothing. Run 'git fetch' inside it, or 'dl <workspace> rm' and launch again, if you meant to run against the branch as it is now.A fetch on attach was the other candidate and would have been the wrong
trade. The claim here is deliberately narrow: how the checkout stands against
theorigin/<branch>in that clone, of whatever age the last fetch left it. It
is onerev-listagainst a local repository, single-digit milliseconds beside
the 0.43s to 0.74s onedevpod statusalready costs on the same path, and it
cannot be wrong about a remote it never asked. Being behind a week-old ref is
already the whole of what points you atgit fetchorrm.Commits of your own are named as yours rather than counted as staleness, and
four shapes stay silent: a checkout that agrees with its ref, one that is only
ahead, a workspace whose clone is gone, anddl <workspace-id>by bare name,
which carries no branch to be behind. -
Every
devpod upsays whether it forwarded dotfiles, naming the repository
and script it passed or saying that devpod's context options name none.dl
readsDOTFILES_URLfromdevpod context optionsand from nowhere else -- not
from the process environment, and not from~/.devpod/config.yaml, which it only
ever stats -- and it used to forward or omit the flags in silence either way.
That silence is what turned one report of "the dotfiles never landed" into a
fortnight of three plausible causes and no observation to cut between them. An
attach that runs noupprints neither line, because it asked devpod for
nothing. -
docs/cli.mdnow names the cause ofinject agent … exit status 126. devpod
picks its agent binary by globbinguname -aforarm, anduname -acarries
the container's hostname, so a workspace whose branch containsalarm,warm,
charm,swarm,harmorarmaturegets the arm64 agent on an x86 host and a
launch that dies saying only "not executable".dlwrites the workspace id into
that hostname itself, so it is one of the ways the name gets there. The entry
carries the one-line check, the way to unblock a container that is already in
that state, and the reason a recreate undoes it. The match itself is devpod's and
is not fixed here.
Changed
dl <ws> resetstopped describing itself as a clean slate. Its help line and
the README table said "Clean slate: remove everything, recreate", which reads as
a promise about the checkout thatresetdoes not keep: it passes devpod's
--reset, and the source removal that flag additionally performs applies only to
a workspace devpod cloned itself, wheredlhands devpod a local folder. Both
now say what it does -- recreate the container and its volumes -- and
docs/cli.mdanddocs/workspaces.mdgained the sentence thatrmis the only
verb which refreshes git state.
Fixed
-
dl --prunereclaims the per-workspace launch locks, which nothing short
of--purgeremoving the whole cache directory had ever reached. Twodlruns
launching one workspace serialize on an empty file under
<cache>/devlaunch/launch-locks/<workspace-id>.lock; every workspace ever
launched left one and no removal path took it away. Measured on one host: 18 of
26 entries named no workspacedevpod liststill returns, against 8 live
workspaces, the oldest three weeks old.Zero bytes of disk, and still the thing worth fixing. A directory that only
ever grows is one nobody can read as a description of anything, and "nothing
devlaunch causes to exist accumulates unreclaimed" is the rule the rest of the
cleanup contract is written to.The precondition is the volumes' precondition, asked of the same listing: no
workspacedevpod listreturns carries that id, re-asked under the acting
pass's own second listing so the approved set can shrink between the plan and
the act and can never grow. -
Unlinking a lock file is safe now, which it was not before. Removing an
flock'd file is the self-defeating move: a process holding the old inode
excludes nobody, while new arrivals lock a fresh file at the same path and walk
past it, and twodevpod ups of one workspace run against each other. The rule
used to be that no lock file is ever deleted, which is what made the leak
above permanent.What replaced the rule is one question asked after every
flock, on both
sides: is the path still naming the inode I just locked? An acquisition that
has lost its file queues again against the live one rather than believing
itself alone, and a sweep that has lost its file re-opens rather than unlinking
whatever the path names by then. Both halves are needed. A sweep that only took
the lock would still have unlinked a live launch's file, because an inode
somebody else already unlinked has no holders and locking one always succeeds.The unlink is then performed while holding the lock, and never queues for it: a
lock a launch is holding is left standing, reported as held, and reclaimed by
the next prune. That is not counted as a failure and does not change the exit
code, because it is the guard working.--purgeis not the only other unlinker, it is the other unlinker that holds
no lock. It removes the cache directory entire, launch locks with it, which is
what makes "somebody replaced this path" an ordinary event here rather than a
devlaunch bug, and why the question above is asked every time rather than as a
belt over a brace. -
devpod's own per-workspace lock is left alone, deliberately, and the
cleanup contract now says so in a table row rather than by omission. The same
measurement found 70 of 78 stale entries under
~/.devpod/contexts/default/locks. They stay: it is another program's lock,
taken by processes devlaunch does not run, and the revalidation above is a
promise devlaunch can only make about a file only devlaunch opens. It is the
same filedl <ws> killhas always been careful never to unlink.