local _VERSION = '6.1.0'; local REPO_BASE = 'https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/master/'; local LOCAL_STATE_DIR = '/trapos'; local LOCAL_MANIFEST_PATH = '/trapos/manifest.json'; local LOCAL_CONFIG_PATH = '/trapos/ccpm.json'; local LOCAL_LOCK_PATH = '/trapos/ccpm.lock.json'; local DEFAULT_REGISTRY_NAME = 'guillaumearm/cc-libs'; local INSTALL_BRANCH = 'master'; local function printUsage() print('install-trapos usage:'); print(); print('\t\twget run '); print('\t\twget run --cpm-only'); end local function readJsonFile(path) if not fs.exists(path) then return nil; end local f = fs.open(path, 'r'); if not f then return nil; end local data = f.readAll(); f.close(); if not data or data == '' then return nil; end return textutils.unserializeJSON(data); end local function writeJsonFile(path, value) fs.makeDir(LOCAL_STATE_DIR); local f = fs.open(path, 'w'); if not f then return false; end f.write(textutils.serializeJSON(value)); f.close(); return true; end local function fetchJson(url) local response = http.get(url); if not response then return nil end local body = response.readAll(); response.close(); if not body or body == '' then return nil end return textutils.unserializeJSON(body); end local function fetchManifest() return fetchJson(REPO_BASE .. 'manifest.json'); end local function fetchDescriptor(pkg) return fetchJson(REPO_BASE .. 'packages/' .. pkg .. '/ccpm.json'); end local function ensureProgramsPath() local current = shell.path(); for entry in string.gmatch(current, '[^:]+') do if entry == '/programs' then return; end end if current == '' then shell.setPath('/programs'); return; end shell.setPath(current .. ':/programs'); end -- Resolve a list of package names + their dependencies into install order -- (deps first). Returns an ordered list of descriptors or nil, err. local function resolvePackages(names) local ordered = {}; local state = {}; -- name -> 'visiting' | 'done' local function visit(name) if state[name] == 'done' then return true end if state[name] == 'visiting' then return false, 'dependency cycle detected at ' .. name; end state[name] = 'visiting'; local desc = fetchDescriptor(name); if not desc then return false, 'package not found: ' .. name; end for _, dep in ipairs(desc.dependencies or {}) do local ok, err = visit(dep); if not ok then return false, err end end state[name] = 'done'; ordered[#ordered + 1] = desc; return true; end for _, name in ipairs(names) do local ok, err = visit(name); if not ok then return nil, err end end return ordered; end -- Seed/refresh the default ccpm registry so it tracks the install branch. local function seedCcpmConfig() local cfg = readJsonFile(LOCAL_CONFIG_PATH) or { registries = {} }; cfg.registries = cfg.registries or {}; local found = false; for _, r in ipairs(cfg.registries) do if r.name == DEFAULT_REGISTRY_NAME then r.type = 'gitea'; r.branch = INSTALL_BRANCH; found = true; end end if not found then table.insert(cfg.registries, 1, { name = DEFAULT_REGISTRY_NAME, type = 'gitea', branch = INSTALL_BRANCH }); end writeJsonFile(LOCAL_CONFIG_PATH, cfg); end local rawArgs = table.pack(...); local cpmOnly = false; for i = 1, rawArgs.n do local a = rawArgs[i]; if a == 'version' or a == '-version' or a == '--version' then print('install-trapos v' .. _VERSION); return; elseif a == 'help' or a == '-help' or a == '--help' then printUsage(); return; elseif a == '--cpm-only' or a == '-cpm-only' then cpmOnly = true; elseif a ~= nil and a ~= '' then printUsage(); return; end end print('Fetching manifest from branch: ' .. INSTALL_BRANCH); local manifest = fetchManifest(); if not manifest then print('Failed to fetch or parse manifest.json from ' .. INSTALL_BRANCH); return; end local requested = { 'trapos-core' }; local explicitBootstrap = {}; if cpmOnly then for _, name in ipairs(requested) do explicitBootstrap[name] = true; end end local resolved, resolveErr = resolvePackages(requested); if not resolved then print('Failed to resolve packages: ' .. resolveErr); return; end -- Legacy file cleanup (pre-manifest installs). fs.delete('ping-server.lua'); fs.delete('ping.lua'); fs.delete('cube.lua'); fs.delete('router.lua'); fs.delete('servers/cube-startup.lua'); fs.delete('programs/cube.lua'); fs.delete('programs/goo.lua'); fs.delete('servers/cube-server.lua'); fs.delete('servers/cube-boot.lua'); local previousDir = shell.dir(); shell.setDir('/'); fs.makeDir('/programs'); fs.makeDir('/apis'); fs.makeDir('/startup'); fs.makeDir('/daemons'); fs.makeDir(LOCAL_STATE_DIR); local allFiles = {}; local seenFile = {}; local autostart = {}; local seenAuto = {}; local lockPackages = {}; for _, desc in ipairs(resolved) do for _, filePath in ipairs(desc.files or {}) do if not seenFile[filePath] then seenFile[filePath] = true; allFiles[#allFiles + 1] = filePath; fs.delete(filePath); -- Sources live under packages// in the repo but install flat, so -- fetch from the package path and write to the flat destination. shell.execute('wget', REPO_BASE .. 'packages/' .. desc.name .. '/' .. filePath, filePath); end end for _, srv in ipairs(desc.autostart or {}) do if not seenAuto[srv] then seenAuto[srv] = true; autostart[#autostart + 1] = srv; end end lockPackages[desc.name] = { version = desc.version, registry = DEFAULT_REGISTRY_NAME, files = desc.files or {}, dependencies = desc.dependencies or {}, autostart = desc.autostart or {}, explicit = explicitBootstrap[desc.name] == true, }; end -- Aggregated OS state for motd/daemons/upgrade (unchanged consumers). writeJsonFile(LOCAL_MANIFEST_PATH, { name = manifest.name or 'TrapOS', version = manifest.version or '?', branch = INSTALL_BRANCH, files = allFiles, autostart = autostart, }); writeJsonFile(LOCAL_LOCK_PATH, { packages = lockPackages }); seedCcpmConfig(); ensureProgramsPath(); print(); print('=> ccpm installed (branch: ' .. INSTALL_BRANCH .. ')'); print('=> Default registry: ' .. DEFAULT_REGISTRY_NAME); if cpmOnly then print('=> Run: ccpm update'); print('=> Run: ccpm install trapos'); shell.setDir(previousDir); return; end -- Full install: sync the registry and install the TrapOS meta-package. shell.execute('ccpm', 'update'); shell.execute('ccpm', 'install', 'trapos'); shell.execute('trapos-postinstall'); shell.setDir(previousDir); print(); print('=> Rebooting...'); os.reboot();