7.9 KiB
Plan: trapos-create-disk + ccpm prune/autoprune
Context
Today programs/trapos-create-installer-disk.lua only builds an install floppy disk
(its startup.lua installs TrapOS on a blank computer). We want one disk-creation tool
that can also produce reinstall and uninstall disks, and we want a real "remove
TrapOS" path. ComputerCraft's ccpm uninstall trapos alone is insufficient: it only deletes
the trapos meta-package's own files and its dependency guard blocks cascading removal.
The fix is two-sided:
- Teach
ccpmto remove orphaned dependencies (prune), auto-pruned onuninstall, soccpm uninstall traposcleanly wipes the whole tree and the/traposstate dir. - Rename/generalize the disk creator to emit install / reinstall / uninstall disks whose
startup.luaperforms the matching action on the booted target.
Decisions (settled with user)
- Program rename:
programs/trapos-create-installer-disk.lua→programs/trapos-create-disk.lua. Bare command prints help. Flags select the disk variant:--install,--reinstall,--uninstall. Keepversionandhelp. - No separate
trapos-uninstall.lua. Everything routes throughccpm uninstall trapos. ccpmgains a newexplicitflag per lock entry, aprunecommand, autoprune on uninstall (--no-pruneto disable), interactive y/N confirm with--yes/-y, and state-dir deletion when empty.ccpmnever auto-reboots — the diskstartup.luaowns reboot/eject.
Part A — ccpm changes
A1. explicit flag (apis/libccpm.lua, api.install ~L397)
When writing a lock entry, set explicit = (item.name == pkg). Deps pulled in get
explicit = false. Preserve an existing true (re-installing a dep that was previously
explicit must not demote it). Legacy/migration: a lock entry with no explicit field is
treated as explicit = true (never auto-prune pre-existing installs).
A2. api.prune(opts) (new, apis/libccpm.lua)
- Compute the set of packages reachable from every explicit root by walking
dependencies(reuse the traversal idea fromapi.resolve, L314). - Orphans = installed packages not in the reachable set.
- Remove the whole orphan set atomically: delete each orphan's
files, drop all their lock entries in one pass, thenwriteLock+writeOsState. This deliberately bypasses the per-package dependents guard inapi.uninstall(orphans depend on each other but none are reachable from a root). Return the list of pruned names.
A3. Autoprune in api.uninstall (apis/libccpm.lua L439)
- Add
opts.prune(default behavior = prune). After removingpkg(existing logic L463-469), if pruning is enabled callapi.pruneand merge its removed list into the result/log. libccpm.uninstallstays non-interactive (CLI owns the prompt) sotests/ccpm.luakeep working.
A4. State-dir cleanup when empty (apis/libccpm.lua)
- After any uninstall/prune that leaves
lock.packagesempty, delete the entire state dir (stateDir, default/trapos) so the machine is blank and the reinstall guardfs.exists('/trapos')passes. Also remove now-empty generated dirs created by the installer (/programs,/apis,/servers,/startup) if empty. Guard the path so a custom teststateDiris still removed safely.
A5. CLI wiring (programs/ccpm.lua)
uninstall|remove|rmhandler (L102): parse--no-pruneand--yes/-yfrom args. Unless--yes, print what will be removed (pkg+ a dry-run prune list) and read a y/N line via a small localconfirm()helper (no read() helper exists in the repo — add one usingread()/io.read). Pass{ prune = not noPrune, log = logLine }toccpm.uninstall.- Add
prunecommand →ccpm.prune({ log = logLine }), printing pruned packages or "nothing to prune". - Update
printUsage()to listprune,uninstall [--yes] [--no-prune].
Part B — disk creator
B1. Generalize apis/libinstallerdisk.lua
-
create(opts)takesopts.mode = 'install' | 'reinstall' | 'uninstall'(defaultinstall). -
buildAutorun(mode)emits the variantstartup.lua. ReusefindTargetDrive, labeling, andDEFAULT_INSTALL_URL(https://os.trapcloud.fr/install). Rename disk label to "TrapOS Disk". -
install mode: current behavior unchanged — clone
/.settingsonto the disk, autorun guardsif fs.exists('/trapos') then return, restores settings,wget run INSTALL_URL. Only this mode clones settings. -
uninstall mode autorun:
if not fs.exists('/trapos') then print('TrapOS already removed.') else shell.run('ccpm','uninstall','trapos','--yes') end print('TrapOS removed. Eject the disk to finish.') os.pullEvent('disk_eject'); os.reboot() -
reinstall mode autorun (marker breaks the post-install loop, since
install-traposreboots with the disk still inserted):local MARKER = '<diskMount>/.trapos-reinstalled' if fs.exists(MARKER) then print('Reinstall complete. Eject the disk.') os.pullEvent('disk_eject'); os.reboot(); return end -- write MARKER if fs.exists('/trapos') then shell.run('ccpm','uninstall','trapos','--yes') end shell.run('wget','run', INSTALL_URL) -- installer reboots/.settingsis left untouched by uninstall, so the machine keeps its own config — no clone.
B2. Rewrite programs/trapos-create-disk.lua
- Replace positional command parsing with flag parsing (pattern from
programs/ai.luaL4-26). - Bare invocation (or
help) →printUsage()documenting--install/--reinstall/--uninstall. - Keep
versionviarequire('/apis/libversion')().forSelf()and the localprintColored. - Map flag →
createInstallerDisk().create({ mode = ... }); reuse existing success/warning output (label, settingsCopied/Warning, insert-and-reboot hint), adjusting copy per mode.
Part C — packaging / metadata
packages/trapos-core/ccpm.json: renameprograms/trapos-create-installer-disk.lua→programs/trapos-create-disk.luainfiles; bumpversion.packages/index.json: bumptrapos-coreversion to match.- Delete the old
programs/trapos-create-installer-disk.luafile (currently uncommitted/new).
Files to modify
apis/libccpm.lua—explicitflag,api.prune, autoprune inapi.uninstall, empty-state cleanup.programs/ccpm.lua—prunecommand, uninstall--yes/--no-prune+ confirm helper, usage.apis/libinstallerdisk.lua— mode-parameterizedcreate/buildAutorun, label rename.programs/trapos-create-disk.lua— new (renamed fromtrapos-create-installer-disk.lua).packages/trapos-core/ccpm.json,packages/index.json— file rename + version bump.
Tests (repo uses apis/libtest, __TRAPOS_TEST_OK__ on pass)
tests/ccpm.lua— add:- install marks target
explicit=true, depsexplicit=false. pruneremoves orphans not reachable from explicit roots; keeps reachable ones.uninstall trapos(with deps) autoprunes the whole tree;--no-prune/{prune=false}removes only the target.- uninstalling the last package deletes the (test) state dir.
- legacy entry without
explicitis not pruned.
- install marks target
- Rename
tests/installer-disk.lua→tests/create-disk.lua; addbuildAutorunassertions per mode: install contains settings-copy-before-wget; uninstall containsccpm uninstall trapos --yes+disk_ejectand nowget; reinstall contains marker check +uninstall --yes+wget run.
Verification
- Run the suite:
tests/ccpm.luaandtests/create-disk.lua(look for__TRAPOS_TEST_OK__). - Manually inspect generated
startup.luafor each mode via the newbuildAutoruntests. - In-world (if available): build an install disk on a blank computer (unchanged path), then a
reinstall disk and an uninstall disk on an installed computer; confirm uninstall wipes
/traposand waits for eject, and reinstall completes then waits for eject.