feat(installer): install full trapos by default
This commit is contained in:
parent
f8e15d2267
commit
e10a1d5976
@ -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/<name>/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/<name>/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/<name>/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`.
|
||||
|
||||
42
README.md
42
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/<name>/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 <package> ccpm reinstall <package> ccpm uninstall <package>
|
||||
@ -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.
|
||||
|
||||
@ -56,7 +56,7 @@ Two safe-exec recipes wrap raw `--headless --exec` with `xpcall`, call `os.shutd
|
||||
|
||||
- `just craftos-exec '<lua>'` — 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 '<lua>'` or `just craftos-exec '<lua>'` 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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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 <install-url>');
|
||||
print('\t\twget run <install-url> --beta');
|
||||
print('\t\twget run <install-url> --stable');
|
||||
print('\t\twget run <install-url> --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);
|
||||
|
||||
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);
|
||||
@ -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 ) &
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "TrapOS",
|
||||
"version": "0.9.3",
|
||||
"branch": "next",
|
||||
"branch": "master",
|
||||
"packages": [
|
||||
"trapos"
|
||||
]
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": []
|
||||
|
||||
50
programs/trapos-postinstall.lua
Normal file
50
programs/trapos-postinstall.lua
Normal file
@ -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();
|
||||
38
programs/trapos-upgrade.lua
Normal file
38
programs/trapos-upgrade.lua
Normal file
@ -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
|
||||
@ -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');
|
||||
@ -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();
|
||||
Loading…
Reference in New Issue
Block a user