Persist config files across Opus package updates
The Opus package manager deletes the entire package directory on update (fs.delete(packageDir)) before re-downloading files. This wiped all config files (.manager_config, .client_config, etc.) that were stored inside packages/inventory-manager/. Fix: add _configPath() helper to every program that resolves config file paths to usr/config/inventory-manager/ when running under Opus, which lives outside the package directory and survives updates. Falls back to the local _path() for standalone (non-Opus) use. Updated files: - inventoryManager.lua, manager/config.lua, inventoryClient.lua, inventoryWebBridge.lua, dropperController.lua, craftingTurtle.lua - .package install script: saves configs to usr/config/inventory-manager/ - autorun/startup.lua: checks both persistent and package dirs - startup/client.lua: uses persistent dir for .client_setup/.dropper_config - web/server/Dockerfile: switch health check to wget (from prior fix)
This commit is contained in:
11
.package
11
.package
@@ -8,7 +8,8 @@
|
|||||||
"%.bak$", "^listDevicesByType%.lua$",
|
"%.bak$", "^listDevicesByType%.lua$",
|
||||||
},
|
},
|
||||||
install = [[
|
install = [[
|
||||||
local pkgDir = fs.combine("packages", "inventory-manager")
|
local cfgDir = "usr/config/inventory-manager"
|
||||||
|
if not fs.isDir(cfgDir) then fs.makeDir(cfgDir) end
|
||||||
local function ask(prompt, default)
|
local function ask(prompt, default)
|
||||||
if default and #default > 0 then
|
if default and #default > 0 then
|
||||||
write(prompt .. " [" .. default .. "]: ")
|
write(prompt .. " [" .. default .. "]: ")
|
||||||
@@ -47,14 +48,14 @@
|
|||||||
dropperName = dropperName,
|
dropperName = dropperName,
|
||||||
barrelName = barrelName,
|
barrelName = barrelName,
|
||||||
}
|
}
|
||||||
local f = fs.open(fs.combine(pkgDir, ".manager_config"), "w")
|
local f = fs.open(fs.combine(cfgDir, ".manager_config"), "w")
|
||||||
f.write(textutils.serialiseJSON(cfg))
|
f.write(textutils.serialiseJSON(cfg))
|
||||||
f.close()
|
f.close()
|
||||||
print("Saved manager config.")
|
print("Saved manager config.")
|
||||||
|
|
||||||
if serverUrl and #serverUrl > 0 then
|
if serverUrl and #serverUrl > 0 then
|
||||||
local bcfg = { serverUrl = serverUrl }
|
local bcfg = { serverUrl = serverUrl }
|
||||||
local bf = fs.open(fs.combine(pkgDir, ".webbridge_config"), "w")
|
local bf = fs.open(fs.combine(cfgDir, ".webbridge_config"), "w")
|
||||||
bf.write(textutils.serialiseJSON(bcfg))
|
bf.write(textutils.serialiseJSON(bcfg))
|
||||||
bf.close()
|
bf.close()
|
||||||
print("Saved web bridge config.")
|
print("Saved web bridge config.")
|
||||||
@@ -75,7 +76,7 @@
|
|||||||
if dropperName and #dropperName > 0 then cfg.dropperName = dropperName end
|
if dropperName and #dropperName > 0 then cfg.dropperName = dropperName end
|
||||||
if barrelName and #barrelName > 0 then cfg.barrelName = barrelName end
|
if barrelName and #barrelName > 0 then cfg.barrelName = barrelName end
|
||||||
|
|
||||||
local f = fs.open(fs.combine(pkgDir, ".client_config"), "w")
|
local f = fs.open(fs.combine(cfgDir, ".client_config"), "w")
|
||||||
f.write(textutils.serialiseJSON(cfg))
|
f.write(textutils.serialiseJSON(cfg))
|
||||||
f.close()
|
f.close()
|
||||||
print("Saved client config.")
|
print("Saved client config.")
|
||||||
@@ -86,7 +87,7 @@
|
|||||||
local serverUrl = ask("Web server URL", "http://localhost")
|
local serverUrl = ask("Web server URL", "http://localhost")
|
||||||
|
|
||||||
local cfg = { serverUrl = serverUrl }
|
local cfg = { serverUrl = serverUrl }
|
||||||
local f = fs.open(fs.combine(pkgDir, ".webbridge_config"), "w")
|
local f = fs.open(fs.combine(cfgDir, ".webbridge_config"), "w")
|
||||||
f.write(textutils.serialiseJSON(cfg))
|
f.write(textutils.serialiseJSON(cfg))
|
||||||
f.close()
|
f.close()
|
||||||
print("Saved web bridge config.")
|
print("Saved web bridge config.")
|
||||||
|
|||||||
@@ -8,17 +8,23 @@ local peripheral = _G.peripheral
|
|||||||
local shell = _ENV.shell
|
local shell = _ENV.shell
|
||||||
|
|
||||||
local BASE = 'packages/inventory-manager'
|
local BASE = 'packages/inventory-manager'
|
||||||
|
local CFG = 'usr/config/inventory-manager'
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- Determine role from config files written during install
|
-- Determine role from config files written during install
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
|
local function cfgExists(name)
|
||||||
|
return fs.exists(fs.combine(CFG, name))
|
||||||
|
or fs.exists(fs.combine(BASE, name))
|
||||||
|
end
|
||||||
|
|
||||||
local role
|
local role
|
||||||
if fs.exists(fs.combine(BASE, '.manager_config')) then
|
if cfgExists('.manager_config') then
|
||||||
role = 'manager'
|
role = 'manager'
|
||||||
elseif fs.exists(fs.combine(BASE, '.client_config')) then
|
elseif cfgExists('.client_config') then
|
||||||
role = 'client'
|
role = 'client'
|
||||||
elseif fs.exists(fs.combine(BASE, '.webbridge_config')) then
|
elseif cfgExists('.webbridge_config') then
|
||||||
role = 'bridge'
|
role = 'bridge'
|
||||||
elseif _G.turtle then
|
elseif _G.turtle then
|
||||||
role = 'turtle'
|
role = 'turtle'
|
||||||
|
|||||||
@@ -31,7 +31,17 @@ local CRAFT_SLOTS = {1, 2, 3, 5, 6, 7, 9, 10, 11}
|
|||||||
local _baseDir = fs.getDir(shell.getRunningProgram())
|
local _baseDir = fs.getDir(shell.getRunningProgram())
|
||||||
local function _path(rel) return fs.combine(_baseDir, rel) end
|
local function _path(rel) return fs.combine(_baseDir, rel) end
|
||||||
|
|
||||||
local TURTLE_CONFIG_FILE = _path(".turtle_config")
|
-- Persistent config path (survives Opus package updates)
|
||||||
|
local _PERSIST_DIR = "usr/config/inventory-manager"
|
||||||
|
local function _configPath(rel)
|
||||||
|
if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then
|
||||||
|
if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end
|
||||||
|
return fs.combine(_PERSIST_DIR, rel)
|
||||||
|
end
|
||||||
|
return _path(rel)
|
||||||
|
end
|
||||||
|
|
||||||
|
local TURTLE_CONFIG_FILE = _configPath(".turtle_config")
|
||||||
|
|
||||||
local function loadConfig()
|
local function loadConfig()
|
||||||
if not fs.exists(TURTLE_CONFIG_FILE) then return end
|
if not fs.exists(TURTLE_CONFIG_FILE) then return end
|
||||||
|
|||||||
@@ -11,7 +11,18 @@ local POLL_INTERVAL = 0.5 -- how often to check the dropper
|
|||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
local _baseDir = fs.getDir(shell.getRunningProgram())
|
local _baseDir = fs.getDir(shell.getRunningProgram())
|
||||||
local CONFIG_FILE = fs.combine(_baseDir, ".dropper_config")
|
|
||||||
|
-- Persistent config path: survives Opus package updates
|
||||||
|
local _PERSIST_DIR = "usr/config/inventory-manager"
|
||||||
|
local function _configPath(rel)
|
||||||
|
if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then
|
||||||
|
if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end
|
||||||
|
return fs.combine(_PERSIST_DIR, rel)
|
||||||
|
end
|
||||||
|
return fs.combine(_baseDir, rel)
|
||||||
|
end
|
||||||
|
|
||||||
|
local CONFIG_FILE = _configPath(".dropper_config")
|
||||||
|
|
||||||
if fs.exists(CONFIG_FILE) then
|
if fs.exists(CONFIG_FILE) then
|
||||||
local f = fs.open(CONFIG_FILE, "r")
|
local f = fs.open(CONFIG_FILE, "r")
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ local DROPPER_ANNOUNCE_INTERVAL = 30 -- seconds between dropper announcements
|
|||||||
local _baseDir = fs.getDir(shell.getRunningProgram())
|
local _baseDir = fs.getDir(shell.getRunningProgram())
|
||||||
local function _path(rel) return fs.combine(_baseDir, rel) end
|
local function _path(rel) return fs.combine(_baseDir, rel) end
|
||||||
|
|
||||||
|
-- Persistent config path: survives Opus package updates
|
||||||
|
local _PERSIST_DIR = "usr/config/inventory-manager"
|
||||||
|
local function _configPath(rel)
|
||||||
|
if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then
|
||||||
|
if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end
|
||||||
|
return fs.combine(_PERSIST_DIR, rel)
|
||||||
|
end
|
||||||
|
return _path(rel)
|
||||||
|
end
|
||||||
|
|
||||||
-- Override dofile to load modules into our _ENV so they inherit
|
-- Override dofile to load modules into our _ENV so they inherit
|
||||||
-- Opus's require/package (CC:Tweaked dofile uses _G instead).
|
-- Opus's require/package (CC:Tweaked dofile uses _G instead).
|
||||||
local _ccDofile = dofile
|
local _ccDofile = dofile
|
||||||
@@ -39,7 +49,7 @@ local function dofile(path) -- luacheck: ignore
|
|||||||
else error(err, 2) end
|
else error(err, 2) end
|
||||||
end
|
end
|
||||||
|
|
||||||
local CLIENT_CONFIG_FILE = _path(".client_config")
|
local CLIENT_CONFIG_FILE = _configPath(".client_config")
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- Modules
|
-- Modules
|
||||||
|
|||||||
@@ -15,6 +15,17 @@
|
|||||||
local _baseDir = fs.getDir(shell.getRunningProgram())
|
local _baseDir = fs.getDir(shell.getRunningProgram())
|
||||||
local function _path(rel) return fs.combine(_baseDir, rel) end
|
local function _path(rel) return fs.combine(_baseDir, rel) end
|
||||||
|
|
||||||
|
-- Persistent config path: survives Opus package updates by storing
|
||||||
|
-- user data in usr/config/inventory-manager/ instead of the package dir.
|
||||||
|
local _PERSIST_DIR = "usr/config/inventory-manager"
|
||||||
|
local function _configPath(rel)
|
||||||
|
if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then
|
||||||
|
if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end
|
||||||
|
return fs.combine(_PERSIST_DIR, rel)
|
||||||
|
end
|
||||||
|
return _path(rel)
|
||||||
|
end
|
||||||
|
|
||||||
-- Write crash info to a file so we can always read it
|
-- Write crash info to a file so we can always read it
|
||||||
local function _crashLog(err)
|
local function _crashLog(err)
|
||||||
local f = fs.open(_path(".crash.log"), "w")
|
local f = fs.open(_path(".crash.log"), "w")
|
||||||
@@ -42,7 +53,7 @@ end
|
|||||||
local log = dofile(_path("lib/log.lua"))
|
local log = dofile(_path("lib/log.lua"))
|
||||||
local ui = dofile(_path("lib/ui.lua"))
|
local ui = dofile(_path("lib/ui.lua"))
|
||||||
local itemDB = dofile(_path("lib/itemDB.lua"))
|
local itemDB = dofile(_path("lib/itemDB.lua"))
|
||||||
itemDB.init(_path(".item_names.db"))
|
itemDB.init(_configPath(".item_names.db"))
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- Load modules (factory pattern → shared context)
|
-- Load modules (factory pattern → shared context)
|
||||||
|
|||||||
@@ -24,7 +24,17 @@ local ORDER_CHANNEL = 4201
|
|||||||
local _baseDir = fs.getDir(shell.getRunningProgram())
|
local _baseDir = fs.getDir(shell.getRunningProgram())
|
||||||
local function _path(rel) return fs.combine(_baseDir, rel) end
|
local function _path(rel) return fs.combine(_baseDir, rel) end
|
||||||
|
|
||||||
local CONFIG_FILE = _path(".webbridge_config")
|
-- Persistent config path: survives Opus package updates
|
||||||
|
local _PERSIST_DIR = "usr/config/inventory-manager"
|
||||||
|
local function _configPath(rel)
|
||||||
|
if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then
|
||||||
|
if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end
|
||||||
|
return fs.combine(_PERSIST_DIR, rel)
|
||||||
|
end
|
||||||
|
return _path(rel)
|
||||||
|
end
|
||||||
|
|
||||||
|
local CONFIG_FILE = _configPath(".webbridge_config")
|
||||||
local API_KEY = nil -- optional API key for server auth
|
local API_KEY = nil -- optional API key for server auth
|
||||||
|
|
||||||
local function loadConfig()
|
local function loadConfig()
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ C.SMELT_RESERVE = 128
|
|||||||
C.DEFRAG_INTERVAL = 600
|
C.DEFRAG_INTERVAL = 600
|
||||||
C.COMPOST_INTERVAL = 3
|
C.COMPOST_INTERVAL = 3
|
||||||
C.ALERT_INTERVAL = 15
|
C.ALERT_INTERVAL = 15
|
||||||
C.CACHE_FILE = _path(".inventory_cache")
|
C.CACHE_FILE = _configPath(".inventory_cache")
|
||||||
C.SMELTER_MONITOR_SIDE = "top"
|
C.SMELTER_MONITOR_SIDE = "top"
|
||||||
C.DISABLED_RECIPES_FILE = _path(".disabled_recipes")
|
C.DISABLED_RECIPES_FILE = _configPath(".disabled_recipes")
|
||||||
|
|
||||||
-- Network
|
-- Network
|
||||||
C.BROADCAST_CHANNEL = 4200
|
C.BROADCAST_CHANNEL = 4200
|
||||||
@@ -71,7 +71,17 @@ C.SLOT_OUTPUT = 3
|
|||||||
-- Config file loader
|
-- Config file loader
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
local CONFIG_FILE = _path(".manager_config")
|
-- Persistent config path: survives Opus package updates
|
||||||
|
local _PERSIST_DIR = "usr/config/inventory-manager"
|
||||||
|
local function _configPath(rel)
|
||||||
|
if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then
|
||||||
|
if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end
|
||||||
|
return fs.combine(_PERSIST_DIR, rel)
|
||||||
|
end
|
||||||
|
return _path(rel)
|
||||||
|
end
|
||||||
|
|
||||||
|
local CONFIG_FILE = _configPath(".manager_config")
|
||||||
|
|
||||||
function C.loadConfig()
|
function C.loadConfig()
|
||||||
if not fs.exists(CONFIG_FILE) then return end
|
if not fs.exists(CONFIG_FILE) then return end
|
||||||
@@ -124,7 +134,7 @@ C.LOW_STOCK_ALERTS = dofile(_path("data/alerts.lua"))
|
|||||||
|
|
||||||
-- Recipe book: merges built-in recipes + user-learned recipes
|
-- Recipe book: merges built-in recipes + user-learned recipes
|
||||||
local recipeBook = dofile(_path("lib/recipeBook.lua"))
|
local recipeBook = dofile(_path("lib/recipeBook.lua"))
|
||||||
recipeBook.init(_path(".recipes.db"))
|
recipeBook.init(_configPath(".recipes.db"))
|
||||||
recipeBook.loadLegacyCrafting(dofile(_path("data/craftable.lua")))
|
recipeBook.loadLegacyCrafting(dofile(_path("data/craftable.lua")))
|
||||||
recipeBook.loadLegacySmelting(dofile(_path("data/smeltable.lua")))
|
recipeBook.loadLegacySmelting(dofile(_path("data/smeltable.lua")))
|
||||||
C.recipeBook = recipeBook
|
C.recipeBook = recipeBook
|
||||||
|
|||||||
@@ -2,8 +2,13 @@
|
|||||||
-- Auto-updates from git then launches inventoryClient + dropperController in parallel
|
-- Auto-updates from git then launches inventoryClient + dropperController in parallel
|
||||||
|
|
||||||
local REPO_RAW = "https://git.spatulaa.com/MayaTheShy/Inventory-Manager-CC/raw/branch/main"
|
local REPO_RAW = "https://git.spatulaa.com/MayaTheShy/Inventory-Manager-CC/raw/branch/main"
|
||||||
local SETUP_FILE = ".client_setup" -- marks first-run complete
|
|
||||||
local DROPPER_CONFIG = ".dropper_config"
|
-- Persistent config directory (survives Opus package updates)
|
||||||
|
local PERSIST_DIR = "usr/config/inventory-manager"
|
||||||
|
if not fs.isDir(PERSIST_DIR) then fs.makeDir(PERSIST_DIR) end
|
||||||
|
|
||||||
|
local SETUP_FILE = fs.combine(PERSIST_DIR, ".client_setup")
|
||||||
|
local DROPPER_CONFIG = fs.combine(PERSIST_DIR, ".dropper_config")
|
||||||
|
|
||||||
-- Files to download (destination -> repo path)
|
-- Files to download (destination -> repo path)
|
||||||
local FILES = {
|
local FILES = {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
|||||||
EXPOSE 3001
|
EXPOSE 3001
|
||||||
|
|
||||||
HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --retries=3 \
|
HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --retries=3 \
|
||||||
CMD wget -qO- http://localhost:3001/api/health || exit 1
|
CMD wget --spider -q http://localhost:3001/api/health || exit 1
|
||||||
|
|
||||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
|
|||||||
Reference in New Issue
Block a user