diff --git a/AGENTS.md b/AGENTS.md index d42cc90..8c6d9a9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,13 +37,13 @@ Use [`docs/README.md`](docs/README.md) as the entrypoint for CC:Tweaked, CraftOS - Preserve `periphemu` guards used for CraftOS-PC emulation; see [`docs/craftos_pc_glossary.md`](docs/craftos_pc_glossary.md) for upstream emulator references. - Global CraftOS-PC save data lives in `~/Library/Application Support/CraftOS-PC` on macOS and `~/.local/share/craftos-pc` on Linux. Repo launchers use local state instead: `.craftos/` for TrapOS and `.craftos-vanilla/` for vanilla CraftOS-PC. See [`docs/install-craftos-pc.md`](docs/install-craftos-pc.md). - TrapOS ships as packages, each described by `packages//ccpm.json` (`name`, `version`, `dependencies`, `files`, `autostart`); `packages/index.json` lists them. Source files stay in place — descriptors only reference them. To ship a new file, add it to the right package's `files` (and `autostart` if it is a server). `packages/trapos/ccpm.json` is the full OS meta-package. See [ADR-0010](docs/adrs/adr-0010-ccpm-package-manager.md). -- `install-ccpm.lua` is the one-time wget bootstrap. It installs only `trapos-core`/`ccpm`, seeds the default `guillaumearm/cc-libs` registry as a `gitea` registry on `git.trapcloud.fr` tracking `master` (or `next` with `--beta`), and tells users to run `ccpm update` then `ccpm install trapos`. The legacy `github` registry type still resolves but is deprecated. +- `install-trapos.lua` is the one-time wget bootstrap (public entry point `https://trapos.trapcloud.fr/install`). It installs `trapos-core`/`ccpm`, seeds the default `guillaumearm/cc-libs` registry as a `gitea` registry on `git.trapcloud.fr` tracking `master` (the only channel), then runs `ccpm update` + `ccpm install trapos` and `trapos-postinstall`. Pass `--cpm-only` to stop after the ccpm bootstrap. The legacy `github` registry type still resolves but is deprecated. - `ccpm` (in `trapos-core`) is the package manager: `apis/libccpm.lua` is the testable core (factory with injectable `http`/`stateDir`/`installRoot`), `programs/ccpm.lua` the CLI. State: `/trapos/ccpm.json` (registries), `/trapos/ccpm.lock.json` (installed packages), and `/trapos/ccpm.cache.json` (available packages from `ccpm update`). Never use the word "manifest" in ccpm — it is reserved for the OS manifest. - Add new servers to `startup/servers.lua` as needed. ## Conventions -- Bump the owning `packages//ccpm.json` version (and mirror it in `packages/index.json`) when changing module behavior; programs report it at runtime via `require('/apis/libversion')().forSelf()`. `install-ccpm.lua` is the only file that still carries its own `_VERSION` because it is the wget bootstrap and lives outside the package system. +- Bump the owning `packages//ccpm.json` version (and mirror it in `packages/index.json`) when changing module behavior; programs report it at runtime via `require('/apis/libversion')().forSelf()`. `install-trapos.lua` is the only file that still carries its own `_VERSION` because it is the wget bootstrap and lives outside the package system. - Programs support `-version`/`--version` and `-help`/`--help`; router also supports `-silent`/`--silent`. - French or English comments are fine; match surrounding code. - Commit messages use lightweight conventional style: `topic(scope): description` or `topic: description`. diff --git a/README.md b/README.md index 5e1bf1c..5232a0f 100644 --- a/README.md +++ b/README.md @@ -4,52 +4,47 @@ A small in-game operating system for ComputerCraft / CC:Tweaked, built around a ## Installation -Install `ccpm` first. This is the only step that needs `wget` with a URL: +One command installs everything — it bootstraps `ccpm`, syncs the default +registry (`guillaumearm/cc-libs`), and installs the full TrapOS: ``` -wget run https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/master/install-ccpm.lua +wget run https://trapos.trapcloud.fr/install ``` -Then sync the default registry (`guillaumearm/cc-libs`) and install TrapOS: +When it finishes, run `sandbox --help` to get started, or `trapos-upgrade` to +update later. + +Install only the package manager (no full OS) with `--cpm-only`, then +cherry-pick packages yourself: ``` +wget run https://trapos.trapcloud.fr/install --cpm-only ccpm update -ccpm install trapos +ccpm install trapos-net +ccpm install trapos-ui ``` -Install individual packages instead if you want to cherry-pick: -``` -> ccpm install trapos-net -> ccpm install trapos-ui -``` - -Install `ccpm` from the beta branch (one-time opt-in, asks for confirmation): -``` -wget run https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/next/install-ccpm.lua --beta -``` - -Once `ccpm` is installed from beta, the default registry tracks `next`; `ccpm update` and `ccpm upgrade` keep using that branch. - -After install, every boot shows a colored MOTD with the installed version and branch (lime for stable, orange + `[BETA]` for beta). +After install, every boot shows a colored MOTD with the installed version. ## Packages TrapOS is split into packages, each described by a `packages//ccpm.json`: -- `trapos-core`: the package manager (`ccpm`), event loop, `upgrade`, `events`. +- `trapos-core`: the package manager (`ccpm`), event loop, `trapos-upgrade`, `trapos-postinstall`, `events`. - `trapos-test`: the test framework (`libtest`) and suite runner (`runtest`). - `trapos-boot`: the startup MOTD and autostart server launcher. - `trapos-net`: routed modem networking (`net`, `router`, `ping`, `ping-server`). - `trapos-ui`: the terminal UI toolkit (`libtui`, `tuidemo`). - `trapos-ai`: the AI client for `opencode serve`. -- `trapos`: full TrapOS meta-package (`trapos-boot`, `trapos-net`, `trapos-ui`, `trapos-test`, and `trapos-ai` during beta). +- `trapos`: full TrapOS meta-package (`trapos-boot`, `trapos-net`, `trapos-ui`, `trapos-test`, `trapos-ai`, `trapos-sandbox`). The `trapos` meta-package is the user-facing full install. Package descriptors list files and autostart servers; installed state is tracked under `/trapos`. ## ccpm -`ccpm` is the TrapOS package manager. `install-ccpm.lua` installs it by installing +`ccpm` is the TrapOS package manager. `install-trapos.lua` installs it by installing the required `trapos-core` package and configures the default registry -(`guillaumearm/cc-libs`). +(`guillaumearm/cc-libs`), then (unless `--cpm-only`) installs the full `trapos` +meta-package. ``` ccpm install ccpm reinstall ccpm uninstall @@ -82,7 +77,8 @@ Servers listed in `manifest.autostart` are launched at boot by `startup/servers. - `router`: routes messages. You need to set up a router to use all `apis/net`-based programs and libraries. - `ping`: pings machines using `apis/net`. - `events`: emits and logs computer events. -- `upgrade`: alias for `ccpm upgrade`. +- `trapos-upgrade`: runs `ccpm update` then `ccpm upgrade`; pass `--reboot`/`-r` to reboot after upgrading. +- `trapos-postinstall`: prints the post-install welcome and next steps (run automatically by the installer). ## Development See [DEVELOPMENT.md](./DEVELOPMENT.md) for local development installation. diff --git a/docs/adrs/adr-0005-craftos-pc-harness-and-probes.md b/docs/adrs/adr-0005-craftos-pc-harness-and-probes.md index b5f2be8..3a07432 100644 --- a/docs/adrs/adr-0005-craftos-pc-harness-and-probes.md +++ b/docs/adrs/adr-0005-craftos-pc-harness-and-probes.md @@ -56,7 +56,7 @@ Two safe-exec recipes wrap raw `--headless --exec` with `xpcall`, call `os.shutd - `just craftos-exec ''` — probe against **vanilla CraftOS-PC**. No mounts, no startup scripts. Use this when the question is purely about CC:Tweaked behavior and TrapOS files would be a distraction, or to confirm a behavior is upstream rather than something the dev env layered on. -- `just trapos-install` — drive the full real install (`install-ccpm.lua` → `ccpm update` → `ccpm install trapos`) on a fresh ephemeral state. This is the probe to run when changing anything in the install path itself. +- `just trapos-install` — drive the full real install (`install-trapos.lua`, which bootstraps ccpm then runs `ccpm update` + `ccpm install trapos` itself) on a fresh ephemeral state. This is the probe to run when changing anything in the install path itself. Conventions: @@ -73,7 +73,7 @@ Conventions: - `just repl` is a human-only interactive wrapper around `just trapos --cli`; automation and LLM agents must use `just trapos-exec ''` or `just craftos-exec ''` instead. - `craftos` (GUI) now opens a single unlabelled `Computer 0` window with a top modem attached. Cross-machine testing requires an explicit `periphemu create` call from the shell rather than being implicit on boot — one extra command when you need a peer, no surprise windows or persisted ghost VMs. - `.craftos-vanilla/` is in `.gitignore` alongside `.craftos/`. -- `just trapos-install` is *not* part of `just ci`: it is network-dependent and slower than `just test`. Run it manually when touching `install-ccpm.lua` or ccpm package descriptors. +- `just trapos-install` is *not* part of `just ci`: it is network-dependent and slower than `just test`. Run it manually when touching `install-trapos.lua` or ccpm package descriptors. - Higher CraftOS-PC invocation traffic during agent sessions; cheap enough that this is a good trade. - The harness version becomes a project-level concern. When CC:Tweaked ships breaking changes that require a newer CraftOS-PC build, we bump the minimum version in [`docs/install-craftos-pc.md`](../install-craftos-pc.md) and `check-craftos` keeps contributors honest. - No CI integration yet. Running CraftOS-PC headless in GitHub Actions is feasible (the AppImage works on Ubuntu runners) but is out of scope; the contract is local-only for now. diff --git a/docs/adrs/adr-0010-ccpm-package-manager.md b/docs/adrs/adr-0010-ccpm-package-manager.md index e1b64a8..801ec3c 100644 --- a/docs/adrs/adr-0010-ccpm-package-manager.md +++ b/docs/adrs/adr-0010-ccpm-package-manager.md @@ -8,6 +8,19 @@ Accepted 2026-06-08 +## Update (2026-06-15) + +The beta/stable two-channel model has been retired now that `next` is merged into +`master`: `master` is the only channel. The `wget` bootstrap is renamed +`install-ccpm.lua` → `install-trapos.lua` (public entry point +`https://trapos.trapcloud.fr/install`) and now installs the **full OS** in one shot — +it bootstraps `ccpm`, then runs `ccpm update` + `ccpm install trapos` and a +`trapos-postinstall` welcome. Pass `--cpm-only` to stop after the ccpm bootstrap. The +persisted `branch` field is always `master`; the `--beta` opt-in and its confirmation +prompt are gone. The `upgrade` program is replaced by `trapos-upgrade` (`ccpm update` + +`ccpm upgrade`, with `--reboot`/`-r`). References to `--beta`/`install-ccpm.lua` below +are retained as historical context. + ## Context The previous install flow (a `LIST_FILES` table inside `install.lua` and a single flat diff --git a/docs/ingame-trapos-ai-mcp-guide.md b/docs/ingame-trapos-ai-mcp-guide.md index 08d4639..0719879 100644 --- a/docs/ingame-trapos-ai-mcp-guide.md +++ b/docs/ingame-trapos-ai-mcp-guide.md @@ -7,9 +7,7 @@ Follow this order while playing. On the ComputerCraft computer: ```sh -wget run https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/next/install-ccpm.lua --beta -ccpm update -ccpm install trapos +wget run https://trapos.trapcloud.fr/install ``` If the computer asks to reboot, reboot it. diff --git a/docs/install-craftos-pc.md b/docs/install-craftos-pc.md index ff4e946..840a9ef 100644 --- a/docs/install-craftos-pc.md +++ b/docs/install-craftos-pc.md @@ -90,7 +90,7 @@ On macOS, use the `--rom` form shown above if the command fails with `Could not `just craftos` launches a vanilla CraftOS-PC with no mounts, persistent under `.craftos-vanilla/` (also gitignored). Use it when a probe should not see TrapOS files — for example, to confirm a behavior is upstream rather than TrapOS-specific. -`just trapos-install` exercises the real ccpm bootstrap (`install-ccpm.lua` → `ccpm update` → `ccpm install trapos`) end-to-end on a fresh, ephemeral CraftOS-PC state. Network-dependent and slower than `just test`, so not part of `just ci`. Override the watchdog with `TRAP_CCLIBS_INSTALL_TIMEOUT_SECONDS` (default `60`). +`just trapos-install` exercises the real one-shot installer (`install-trapos.lua`, which bootstraps ccpm then runs `ccpm update` + `ccpm install trapos`) end-to-end on a fresh, ephemeral CraftOS-PC state. Network-dependent and slower than `just test`, so not part of `just ci`. Override the watchdog with `TRAP_CCLIBS_INSTALL_TIMEOUT_SECONDS` (default `60`). For automated probes, use the safe wrappers. They mount the right environment, shut down the machine after completion or Lua errors, and kill the host process diff --git a/docs/opencode_server_guide.md b/docs/opencode_server_guide.md index 72f2a2e..4560a82 100644 --- a/docs/opencode_server_guide.md +++ b/docs/opencode_server_guide.md @@ -14,12 +14,10 @@ CC Computer ## 0. Install TrapOS and the AI package -On a fresh CC computer (beta branch): +On a fresh CC computer: ``` -wget run https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/next/install-ccpm.lua --beta -ccpm update -ccpm install trapos +wget run https://trapos.trapcloud.fr/install ``` ## 1. Start `opencode serve` diff --git a/install-ccpm.lua b/install-trapos.lua similarity index 71% rename from install-ccpm.lua rename to install-trapos.lua index 820ec50..9b22607 100644 --- a/install-ccpm.lua +++ b/install-trapos.lua @@ -1,18 +1,18 @@ -local _VERSION = '5.1.0'; +local _VERSION = '6.0.0'; -local REPO_BASE = 'https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/'; +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-ccpm usage:'); + print('install-trapos usage:'); print(); print('\t\twget run '); - print('\t\twget run --beta'); - print('\t\twget run --stable'); + print('\t\twget run --cpm-only'); end local function readJsonFile(path) @@ -34,17 +34,6 @@ local function writeJsonFile(path, value) return true; end -local function confirmBeta() - print(); - print('You are about to install the BETA branch (next).'); - print('Beta builds may be unstable. Continue? (y/N)'); - write('> '); - local answer = read(); - if not answer then return false; end - answer = answer:lower(); - return answer == 'y' or answer == 'yes'; -end - local function fetchJson(url) local response = http.get(url); if not response then return nil end @@ -54,12 +43,12 @@ local function fetchJson(url) return textutils.unserializeJSON(body); end -local function fetchManifest(branch) - return fetchJson(REPO_BASE .. branch .. '/manifest.json'); +local function fetchManifest() + return fetchJson(REPO_BASE .. 'manifest.json'); end -local function fetchDescriptor(branch, pkg) - return fetchJson(REPO_BASE .. branch .. '/packages/' .. pkg .. '/ccpm.json'); +local function fetchDescriptor(pkg) + return fetchJson(REPO_BASE .. 'packages/' .. pkg .. '/ccpm.json'); end local function ensureProgramsPath() @@ -76,7 +65,7 @@ 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(branch, names) +local function resolvePackages(names) local ordered = {}; local state = {}; -- name -> 'visiting' | 'done' @@ -86,7 +75,7 @@ local function resolvePackages(branch, names) return false, 'dependency cycle detected at ' .. name; end state[name] = 'visiting'; - local desc = fetchDescriptor(branch, name); + local desc = fetchDescriptor(name); if not desc then return false, 'package not found: ' .. name; end @@ -107,79 +96,57 @@ local function resolvePackages(branch, names) end -- Seed/refresh the default ccpm registry so it tracks the install branch. -local function seedCcpmConfig(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 = branch; + r.branch = INSTALL_BRANCH; found = true; end end if not found then - table.insert(cfg.registries, 1, { name = DEFAULT_REGISTRY_NAME, type = 'gitea', branch = branch }); + 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 forceBeta, forceStable = false, false; +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-ccpm v' .. _VERSION); + print('install-trapos v' .. _VERSION); return; elseif a == 'help' or a == '-help' or a == '--help' then printUsage(); return; - elseif a == '--beta' or a == '-beta' then - forceBeta = true; - elseif a == '--stable' or a == '-stable' then - forceStable = true; + elseif a == '--cpm-only' or a == '-cpm-only' then + cpmOnly = true; elseif a ~= nil and a ~= '' then printUsage(); return; end end -local localManifest = readJsonFile(LOCAL_MANIFEST_PATH); -local localBranch = localManifest and localManifest.branch or nil; -local branch; - -if forceBeta then - branch = 'next'; - if localBranch ~= 'next' then - if not confirmBeta() then - print('Aborted.'); - return; - end - end -elseif forceStable then - branch = 'master'; -else - branch = localBranch or 'master'; -end - -print('Fetching manifest from branch: ' .. branch); -local manifest = fetchManifest(branch); +print('Fetching manifest from branch: ' .. INSTALL_BRANCH); +local manifest = fetchManifest(); if not manifest then - print('Failed to fetch or parse manifest.json from ' .. branch); + print('Failed to fetch or parse manifest.json from ' .. INSTALL_BRANCH); return; end local requested = { 'trapos-core' }; -local resolved, resolveErr = resolvePackages(branch, requested); +local resolved, resolveErr = resolvePackages(requested); if not resolved then print('Failed to resolve packages: ' .. resolveErr); return; end -local REPO_PREFIX = REPO_BASE .. branch .. '/'; - -- Legacy file cleanup (pre-manifest installs). fs.delete('ping-server.lua'); fs.delete('ping.lua'); @@ -212,7 +179,7 @@ for _, desc in ipairs(resolved) do seenFile[filePath] = true; allFiles[#allFiles + 1] = filePath; fs.delete(filePath); - shell.execute('wget', REPO_PREFIX .. filePath, filePath); + shell.execute('wget', REPO_BASE .. filePath, filePath); end end for _, srv in ipairs(desc.autostart or {}) do @@ -234,22 +201,34 @@ end writeJsonFile(LOCAL_MANIFEST_PATH, { name = manifest.name or 'TrapOS', version = manifest.version or '?', - branch = branch, + branch = INSTALL_BRANCH, files = allFiles, autostart = autostart, }); writeJsonFile(LOCAL_LOCK_PATH, { packages = lockPackages }); -seedCcpmConfig(branch); +seedCcpmConfig(); ensureProgramsPath(); print(); -print('=> ccpm installed (branch: ' .. branch .. ')'); +print('=> ccpm installed (branch: ' .. INSTALL_BRANCH .. ')'); print('=> Default registry: ' .. DEFAULT_REGISTRY_NAME); -print('=> Run: ccpm update'); -print('=> Run: ccpm install trapos'); + +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'); + if fs.exists('/startup/servers.lua') then shell.execute('/startup/servers.lua'); end +shell.execute('trapos-postinstall'); + shell.setDir(previousDir); diff --git a/just/craftos.just b/just/craftos.just index 9421b84..143f095 100644 --- a/just/craftos.just +++ b/just/craftos.just @@ -177,11 +177,10 @@ craftos-exec code: exit 1 fi -# End-to-end install probe: drive the real ccpm bootstrap -# (install-ccpm.lua -> `ccpm update` -> `ccpm install trapos`) on a fresh, -# ephemeral CraftOS-PC state. Reflects the currently checked-out git branch: -# `master` -> --stable, `next` -> --beta (confirmation stubbed). Other -# branches are rejected because install-ccpm only knows master/next. +# End-to-end install probe: drive the real one-shot installer +# (install-trapos.lua, which bootstraps ccpm then runs `ccpm update` + +# `ccpm install trapos` itself) on a fresh, ephemeral CraftOS-PC state. +# Always installs from the published `master` branch (the only channel). # Network-dependent and slower than `just test`, so not part of `just ci`. # Override timeout with TRAP_CCLIBS_INSTALL_TIMEOUT_SECONDS (default 60). # See ADR-0005. @@ -191,31 +190,15 @@ trapos-install: check-install repo='{{justfile_directory()}}' timeout_seconds="${TRAP_CCLIBS_INSTALL_TIMEOUT_SECONDS:-60}" case "$timeout_seconds" in ''|*[!0-9]*) printf '%s\n' 'TRAP_CCLIBS_INSTALL_TIMEOUT_SECONDS must be a positive integer' >&2; exit 1 ;; esac - branch="$(git -C "$repo" rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')" - case "$branch" in - master) - install_flag='--stable' - stub_read='' - ;; - next) - install_flag='--beta' - stub_read="_G.read = function() return 'y' end; " - ;; - *) - printf '%s\n' "trapos-install only supports the master or next branch (current: ${branch:-unknown}). install-ccpm.lua does not accept other branches." >&2 - exit 1 - ;; - esac - printf '%s\n' "trapos-install: branch=$branch flag=$install_flag" rom_arg=() if [ "$(uname -s)" = "Darwin" ]; then rom_arg=(--rom /Applications/CraftOS-PC.app/Contents/Resources) fi data_dir="$(mktemp -d)" stage_dir="$(mktemp -d)" - cp "$repo/install-ccpm.lua" "$stage_dir/install-ccpm.lua" + cp "$repo/install-trapos.lua" "$stage_dir/install-trapos.lua" tmp="$(mktemp)" - exec_code="${stub_read}shell.run('/staging/install-ccpm', '$install_flag'); shell.run('/programs/ccpm', 'update'); local ok = shell.run('/programs/ccpm', 'install', 'trapos'); if ok then print('__TRAPOS_INSTALL_OK__') end; os.shutdown()" + exec_code="shell.run('/staging/install-trapos'); if fs.exists('/trapos/manifest.json') and fs.exists('/trapos/ccpm.lock.json') then print('__TRAPOS_INSTALL_OK__') end; os.shutdown()" craftos --directory "$data_dir" --headless "${rom_arg[@]}" --mount-ro "/staging=$stage_dir" --exec "$exec_code" >"$tmp" 2>&1 & pid="$!" ( sleep "$timeout_seconds"; kill -TERM "$pid" >/dev/null 2>&1 ) & diff --git a/manifest.json b/manifest.json index ba01cec..a7d4b5d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "TrapOS", "version": "0.9.3", - "branch": "next", + "branch": "master", "packages": [ "trapos" ] diff --git a/packages/index.json b/packages/index.json index e282efd..a52b036 100644 --- a/packages/index.json +++ b/packages/index.json @@ -1,6 +1,6 @@ { "packages": { - "trapos-core": "0.5.1", + "trapos-core": "0.6.0", "trapos-test": "0.2.1", "trapos-boot": "0.3.3", "trapos-net": "0.3.0", diff --git a/packages/trapos-core/ccpm.json b/packages/trapos-core/ccpm.json index f1a267a..6e5be4c 100644 --- a/packages/trapos-core/ccpm.json +++ b/packages/trapos-core/ccpm.json @@ -1,6 +1,6 @@ { "name": "trapos-core", - "version": "0.5.1", + "version": "0.6.0", "description": "TrapOS base: package manager, event loop, upgrade and event tools", "dependencies": [], "files": [ @@ -8,7 +8,8 @@ "apis/libccpm.lua", "apis/libversion.lua", "programs/ccpm.lua", - "programs/upgrade.lua", + "programs/trapos-upgrade.lua", + "programs/trapos-postinstall.lua", "programs/events.lua" ], "autostart": [] diff --git a/programs/trapos-postinstall.lua b/programs/trapos-postinstall.lua new file mode 100644 index 0000000..c19efa5 --- /dev/null +++ b/programs/trapos-postinstall.lua @@ -0,0 +1,50 @@ +local createVersion = require('/apis/libversion'); + +local function printUsage() + print('trapos-postinstall usage:'); + print(); + print('\t\ttrapos-postinstall'); + print('\t\ttrapos-postinstall version'); + print('\t\ttrapos-postinstall help'); +end + +local command = ...; + +if command == 'version' or command == '-version' or command == '--version' then + print('v' .. createVersion().forSelf()); + return; +end + +if command == 'help' or command == '-help' or command == '--help' then + printUsage(); + return; +end + +if command ~= nil and command ~= '' then + printUsage(); + return; +end + +local function readManifestVersion() + if not fs.exists('/trapos/manifest.json') then return nil; end + local f = fs.open('/trapos/manifest.json', 'r'); + if not f then return nil; end + local data = f.readAll(); + f.close(); + local manifest = data and textutils.unserializeJSON(data); + return manifest and manifest.version or nil; +end + +local version = readManifestVersion(); + +print(); +if version then + print('=> TrapOS v' .. version .. ' installed.'); +else + print('=> TrapOS installed.'); +end +print(); +print('Get started:'); +print(' sandbox --help -- open the sandbox help'); +print(' trapos-upgrade -- update and upgrade TrapOS'); +print(); diff --git a/programs/trapos-upgrade.lua b/programs/trapos-upgrade.lua new file mode 100644 index 0000000..bf72fea --- /dev/null +++ b/programs/trapos-upgrade.lua @@ -0,0 +1,38 @@ +local createVersion = require('/apis/libversion'); + +local function printUsage() + print('trapos-upgrade usage:'); + print(); + print('\t\ttrapos-upgrade'); + print('\t\ttrapos-upgrade --reboot'); + print('\t\ttrapos-upgrade version'); + print('\t\ttrapos-upgrade help'); +end + +local rawArgs = table.pack(...); +local reboot = false; + +for i = 1, rawArgs.n do + local a = rawArgs[i]; + if a == 'version' or a == '-version' or a == '--version' then + print('v' .. createVersion().forSelf()); + return; + elseif a == 'help' or a == '-help' or a == '--help' then + printUsage(); + return; + elseif a == '--reboot' or a == '-reboot' or a == '-r' then + reboot = true; + elseif a ~= nil and a ~= '' then + printUsage(); + return; + end +end + +shell.execute('ccpm', 'update'); +shell.execute('ccpm', 'upgrade'); + +if reboot then + print(); + print('=> Rebooting...'); + os.reboot(); +end diff --git a/programs/upgrade.lua b/programs/upgrade.lua deleted file mode 100644 index ca9e552..0000000 --- a/programs/upgrade.lua +++ /dev/null @@ -1,28 +0,0 @@ -local createVersion = require('/apis/libversion'); - -local function printUsage() - print('upgrade usage:'); - print(); - print('\t\tupgrade'); - print('\t\tupgrade version'); - print('\t\tupgrade help'); -end - -local command = ...; - -if command == 'version' or command == '-version' or command == '--version' then - print('v' .. createVersion().forSelf()); - return; -end - -if command == 'help' or command == '-help' or command == '--help' then - printUsage(); - return; -end - -if command ~= nil and command ~= '' then - printUsage(); - return; -end - -shell.execute('ccpm', 'upgrade'); diff --git a/tests/install-ccpm.lua b/tests/install-trapos.lua similarity index 56% rename from tests/install-ccpm.lua rename to tests/install-trapos.lua index 67bbb5c..ff1fb69 100644 --- a/tests/install-ccpm.lua +++ b/tests/install-trapos.lua @@ -43,31 +43,29 @@ local function fakeHttp(routes) }; end -testlib.test('programs path resolves the ccpm command', function() - local previous = shell.path(); - shell.setPath('/rom/programs:/programs'); - local resolved = shell.resolveProgram('ccpm'); - shell.setPath(previous); - testlib.assertEquals(resolved, 'programs/ccpm.lua'); -end); +local INSTALL_BASE = 'https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/master/'; -testlib.test('fresh beta bootstrap leaves ccpm runnable without trapos-boot', function() - local root = '/install-ccpm-test/fresh-beta'; - fs.delete(root); - fs.makeDir(root); - - local base = 'https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/next/'; - local routes = { - [base .. 'manifest.json'] = textutils.serializeJSON({ name = 'TrapOS', version = 'test' }), - [base .. 'packages/trapos-core/ccpm.json'] = textutils.serializeJSON({ +local function installRoutes() + return { + [INSTALL_BASE .. 'manifest.json'] = textutils.serializeJSON({ name = 'TrapOS', version = 'test' }), + [INSTALL_BASE .. 'packages/trapos-core/ccpm.json'] = textutils.serializeJSON({ name = 'trapos-core', version = '1', dependencies = {}, files = { 'apis/libccpm.lua', 'programs/ccpm.lua' }, }), - [base .. 'apis/libccpm.lua'] = 'libccpm-body', - [base .. 'programs/ccpm.lua'] = 'ccpm-body', + [INSTALL_BASE .. 'apis/libccpm.lua'] = 'libccpm-body', + [INSTALL_BASE .. 'programs/ccpm.lua'] = 'ccpm-body', }; +end + +-- Run install-trapos.lua inside a sandboxed env. `args` is a list of CLI args. +-- Returns { calls, root } for assertions. +local function runInstall(root, args) + fs.delete(root); + fs.makeDir(root); + + local routes = installRoutes(); local calls = { executes = {}, path = '/rom/programs', @@ -85,9 +83,11 @@ testlib.test('fresh beta bootstrap leaves ccpm runnable without trapos-boot', fu setDir = function(path) calls.dir = path; end, path = function() return calls.path; end, setPath = function(path) calls.path = path; end, - execute = function(program, url, target) - calls.executes[#calls.executes + 1] = { program = program, url = url, target = target }; + execute = function(program, ...) + local rest = { ... }; + calls.executes[#calls.executes + 1] = { program = program, args = rest }; if program ~= 'wget' then return true; end + local url, target = rest[1], rest[2]; local body = routes[url]; if not body then return false; end local dir = parentDir(target); @@ -100,22 +100,64 @@ testlib.test('fresh beta bootstrap leaves ccpm runnable without trapos-boot', fu }, }, { __index = _G }); - local chunk, loadErr = loadfile('/trapos/install-ccpm.lua', 't', env); + local chunk, loadErr = loadfile('/trapos/install-trapos.lua', 't', env); if not chunk then error(loadErr, 0); end - local ok, err = pcall(chunk, '--beta'); + local ok, err = pcall(chunk, table.unpack(args)); if not ok then error(err, 0); end + return calls; +end + +local function findExecute(calls, program, firstArg) + for _, call in ipairs(calls.executes) do + if call.program == program then + if firstArg == nil or call.args[1] == firstArg then + return true; + end + end + end + return false; +end + +testlib.test('programs path resolves the ccpm command', function() + local previous = shell.path(); + shell.setPath('/rom/programs:/programs'); + local resolved = shell.resolveProgram('ccpm'); + shell.setPath(previous); + testlib.assertEquals(resolved, 'programs/ccpm.lua'); +end); + +testlib.test('fresh bootstrap seeds a gitea master registry and runs ccpm', function() + local root = '/install-trapos-test/full'; + local calls = runInstall(root, {}); + testlib.assertTrue(fs.exists(root .. '/programs/ccpm.lua')); testlib.assertTrue(string.find(calls.path, '/programs', 1, true)); - for _, call in ipairs(calls.executes) do - testlib.assertTrue(call.program ~= '/startup/servers.lua'); - end local f = fs.open(root .. '/trapos/ccpm.json', 'r'); local config = textutils.unserializeJSON(f.readAll()); f.close(); - testlib.assertEquals(config.registries[1].branch, 'next'); + testlib.assertEquals(config.registries[1].branch, 'master'); testlib.assertEquals(config.registries[1].type, 'gitea'); end); +testlib.test('default install runs update, install trapos and postinstall', function() + local root = '/install-trapos-test/default'; + local calls = runInstall(root, {}); + + testlib.assertTrue(findExecute(calls, 'ccpm', 'update')); + testlib.assertTrue(findExecute(calls, 'ccpm', 'install')); + testlib.assertTrue(findExecute(calls, 'trapos-postinstall')); +end); + +testlib.test('--cpm-only stops after the ccpm bootstrap', function() + local root = '/install-trapos-test/cpm-only'; + local calls = runInstall(root, { '--cpm-only' }); + + testlib.assertTrue(fs.exists(root .. '/programs/ccpm.lua')); + testlib.assertTrue(not findExecute(calls, 'ccpm', 'update')); + testlib.assertTrue(not findExecute(calls, 'ccpm', 'install')); + testlib.assertTrue(not findExecute(calls, 'trapos-postinstall')); +end); + testlib.run();