resetComputer.lua

204 lines · 4.9 KB

Open raw

resetComputer.lua

Copy & run

wget https://perlytiara.github.io/turtles.tips/raw/programs/perlytiara/resetComputer/resetComputer.lua
1-- resetComputer.lua
2-- Safely reset a ComputerCraft computer/turtle to factory defaults by wiping the local HDD.
3-- Preserves ROM and any mounted disks. Supports confirmation, dry-run, and keep-list.
4
5local function printUsage()
6 print("Usage: resetComputer [options]")
7 print("")
8 print("Options:")
9 print(" --yes, -y Proceed without interactive confirmation")
10 print(" --dry-run Show what would be deleted, but do not delete")
11 print(" --keep <path> Preserve a file/dir (can be used multiple times)")
12 print(" --clear-label Clear the computer label after reset")
13 print(" --no-reboot Do not reboot automatically after reset")
14 print(" --help, -h Show this help")
15end
16
17local function normalizePath(path)
18 if not path or path == "" then return "/" end
19 if string.sub(path, 1, 1) ~= "/" then
20 return "/" .. path
21 end
22 return path
23end
24
25local function parseArgs(tArgs)
26 local options = {
27 assumeYes = false,
28 dryRun = false,
29 clearLabel = false,
30 noReboot = false,
31 keeps = {},
32 help = false,
33 }
34
35 local i = 1
36 while i <= #tArgs do
37 local a = tArgs[i]
38 if a == "--yes" or a == "-y" then
39 options.assumeYes = true
40 elseif a == "--dry-run" then
41 options.dryRun = true
42 elseif a == "--clear-label" then
43 options.clearLabel = true
44 elseif a == "--no-reboot" then
45 options.noReboot = true
46 elseif a == "--help" or a == "-h" then
47 options.help = true
48 elseif a == "--keep" then
49 if i == #tArgs then
50 print("Error: --keep requires a path argument")
51 options.help = true
52 break
53 end
54 local keepPath = normalizePath(tArgs[i + 1])
55 options.keeps[keepPath] = true
56 i = i + 1
57 else
58 -- Unrecognized positional may be treated as keep for convenience
59 local keepPath = normalizePath(a)
60 options.keeps[keepPath] = true
61 end
62 i = i + 1
63 end
64
65 return options
66end
67
68local function isKept(path, keeps)
69 if keeps[path] then return true end
70 -- Also keep if any ancestor is marked keep
71 -- and if any kept path is a child of this path, we skip deleting parent entirely
72 for keepPath, _ in pairs(keeps) do
73 if string.sub(path, 1, #keepPath) == keepPath then
74 return true
75 end
76 if string.sub(keepPath, 1, #path) == path and (string.len(keepPath) > string.len(path)) then
77 return true
78 end
79 end
80 return false
81end
82
83local function listTopLevelDeletions(keeps)
84 local toDelete = {}
85 for _, name in ipairs(fs.list("/")) do
86 local full = "/" .. name
87 local drive = fs.getDrive(full)
88 if drive == "hdd" then
89 if not isKept(full, keeps) then
90 table.insert(toDelete, full)
91 end
92 else
93 -- Skip ROM and mounted disks
94 end
95 end
96 table.sort(toDelete)
97 return toDelete
98end
99
100local function deletePath(path)
101 if fs.isReadOnly(path) then return false, "read-only" end
102 if not fs.exists(path) then return true end
103 local ok, err = pcall(function()
104 fs.delete(path)
105 end)
106 if not ok then return false, err end
107 return true
108end
109
110local function confirmPrompt(summary)
111 print("")
112 print(summary)
113 print("Type 'RESET' to confirm, or anything else to cancel.")
114 write("> ")
115 local input = read()
116 return input == "RESET"
117end
118
119-- capture program arguments at the top-level chunk (varargs allowed here)
120local topArgs = {...}
121
122local function main(tArgs)
123 local opts = parseArgs(tArgs)
124 if opts.help then
125 printUsage()
126 return
127 end
128
129 term.clear()
130 term.setCursorPos(1, 1)
131 print("Reset Computer / Turtle")
132 print("This will wipe the local HDD (excluding ROM and mounted disks).")
133
134 local toDelete = listTopLevelDeletions(opts.keeps)
135 if #toDelete == 0 then
136 print("Nothing to delete (local HDD already clean or everything kept).")
137 return
138 end
139
140 print("")
141 print("Targets:")
142 for _, p in ipairs(toDelete) do
143 print(" " .. p)
144 end
145
146 if opts.dryRun then
147 print("")
148 print("Dry-run: No changes made.")
149 return
150 end
151
152 local proceed = opts.assumeYes
153 if not proceed then
154 proceed = confirmPrompt("About to delete " .. tostring(#toDelete) .. " top-level item(s).")
155 end
156 if not proceed then
157 print("Cancelled.")
158 return
159 end
160
161 print("")
162 print("Deleting...")
163 local failures = 0
164 for _, p in ipairs(toDelete) do
165 local ok, err = deletePath(p)
166 if ok then
167 print("deleted " .. p)
168 else
169 print("FAILED " .. p .. " (" .. tostring(err) .. ")")
170 failures = failures + 1
171 end
172 end
173
174 if opts.clearLabel then
175 local ok, _ = pcall(function()
176 if os.setComputerLabel then
177 os.setComputerLabel(nil)
178 end
179 end)
180 if ok then
181 print("Cleared computer label.")
182 end
183 end
184
185 print("")
186 if failures == 0 then
187 print("Reset complete.")
188 else
189 print("Reset complete with " .. tostring(failures) .. " failure(s).")
190 end
191
192 if not opts.noReboot then
193 print("Rebooting in 2 seconds...")
194 sleep(2)
195 os.reboot()
196 else
197 print("Skipping reboot as requested.")
198 end
199end
200
201main(topArgs)
202
203
204