Compare commits

..

2 Commits

Author SHA1 Message Date
Axel Meyer
5bf9a891f8 dep-fetcher 0.1.1: Case F — accept packaged installs without .git when manifest version matches pin 2026-05-31 02:27:43 +02:00
Axel Meyer
ed3e6aceb0 lib-management.dep-fetcher: probe engine.install_root + engine.config
Slice 6 of the dep-fetcher plan wired engine.install_root() and
engine.config(key) as Lua-level bindings. Use them when available;
keep the slice-5 env-var + hardcoded-URL fallbacks so this lib still
loads correctly under engines that pre-date the bindings.

Resolution chain is now:
  install_root : opts -> engine.install_root() -> SPOREL_INSTALL_ROOT
  gitea_base   : opts -> engine.config('gitea_base') -> hardcoded default

No version bump: behavior unchanged for callers that pass opts.* or
that run under engines without the new bindings.
2026-05-31 02:21:43 +02:00
2 changed files with 90 additions and 23 deletions

111
init.lua
View File

@@ -32,6 +32,49 @@
local M = {}
-- =====================================================================
-- Engine binding probes (Slice 6)
--
-- Slice 6 of the dep-fetcher plan wired engine.install_root() and
-- engine.config(key) as Lua-level bindings. We probe for them at
-- call-time (not load-time) so this lib continues to load under
-- engines that pre-date Slice 6 — the fallbacks (env var, hardcoded
-- URL) keep slice-5-compat with unmodified hosts.
-- =====================================================================
local function resolve_install_root(opts)
-- 1. explicit override
if opts and opts.install_root then return opts.install_root end
-- 2. Slice 6 engine binding
if type(engine) == "table"
and type(engine.install_root) == "function" then
local ok, root = pcall(engine.install_root)
if ok and type(root) == "string" and root ~= "" then
return root
end
end
-- 3. Slice 5 stop-gap: env var
return os.getenv("SPOREL_INSTALL_ROOT")
end
local function resolve_gitea_base(opts)
-- 1. explicit override
if opts and opts.gitea_base then return opts.gitea_base end
-- 2. Slice 6 engine.config("gitea_base") (sourced from engine.json)
if type(engine) == "table"
and type(engine.config) == "function" then
local ok, val = pcall(engine.config, "gitea_base")
if ok and type(val) == "string" and val ~= "" then
return val
end
end
-- 3. Hardcoded slice-5 fallback
return "https://git.davoryn.de/sporel"
end
M._resolve_install_root = resolve_install_root -- exposed for tests
M._resolve_gitea_base = resolve_gitea_base -- exposed for tests
-- =====================================================================
-- Pure-Lua JSON decoder (strict subset: object / array / string /
-- number / true / false / null). Sufficient for manifest.lib /
@@ -377,6 +420,17 @@ M._detect_conflicts = detect_conflicts
-- C — dirty : commits-past-tag or working-tree dirty → warn
-- D — wrong-tag : describe != pinned-tag → silent checkout
-- E — broken : describe fails entirely → error
-- F — packaged : lib dir exists, no .git/, manifest.lib version
-- matches pin → accept (cmake-staged install)
local function manifest_version_matches(lib_dir, expected_version)
local f = io.open(lib_dir .. "/manifest.lib", "rb")
if not f then return false end
local content = f:read("*a")
f:close()
local m, _ = json_decode(content)
return m ~= nil and m.version == expected_version
end
local function run_state_check(unique_closure, install_root,
gitea_base, warnings, errors)
local git = require("lib-core.git")
@@ -387,14 +441,21 @@ local function run_state_check(unique_closure, install_root,
local pinned = "v" .. e.version
if not git.is_repo(lib_dir) then
-- Case A: clone @ pinned-tag.
local ok, gerr = git.clone(url, lib_dir, pinned)
if not ok then
table.insert(errors, {
lib_id = e.lib_id,
kind = "net-fail",
message = tostring(gerr),
})
-- No .git dir present. Two sub-cases:
-- F — packaged install: dir exists with a manifest.lib at
-- the right version (cmake --install layout). Accept.
-- A — missing: dir absent or manifest mismatch → clone.
if manifest_version_matches(lib_dir, e.version) then
-- Case F: packaged install, no action needed.
else
local ok, gerr = git.clone(url, lib_dir, pinned)
if not ok then
table.insert(errors, {
lib_id = e.lib_id,
kind = "net-fail",
message = tostring(gerr),
})
end
end
else
local desc, derr = git.describe(lib_dir)
@@ -446,24 +507,28 @@ end
-- ensure_for_module_at — full integrity check.
-- module_manifest_path : absolute path to manifest.module
-- opts:
-- install_root : root of <staged>/libs/ — defaults to env
-- SPOREL_INSTALL_ROOT (slice 6: engine.install_root()).
-- install_root : root of <staged>/libs/. Resolution chain (Slice 6):
-- 1. opts.install_root (explicit override)
-- 2. engine.install_root() if the binding is present
-- 3. SPOREL_INSTALL_ROOT env var (slice-5 stop-gap)
-- check_only : skip per-lib git operations entirely
-- (used by closure-shape unit tests).
-- gitea_base : override default base URL.
-- gitea_base : override default base URL. Resolution chain:
-- 1. opts.gitea_base
-- 2. engine.config("gitea_base") if available
-- 3. hardcoded https://git.davoryn.de/sporel fallback
function M.ensure_for_module_at(module_manifest_path, opts)
opts = opts or {}
local install_root = opts.install_root
or os.getenv("SPOREL_INSTALL_ROOT")
local install_root = resolve_install_root(opts)
if not install_root then
return {
ok = false, conflicts = {}, warnings = {},
errors = {{
lib_id = "<module>",
kind = "config",
message = "no install_root: pass opts.install_root or " ..
"set SPOREL_INSTALL_ROOT (slice 6 will add " ..
"engine.install_root() binding)",
message = "no install_root: pass opts.install_root, " ..
"set SPOREL_INSTALL_ROOT, or run under an engine " ..
"that provides engine.install_root()",
}},
closure = {},
}
@@ -507,8 +572,7 @@ function M.ensure_for_module_at(module_manifest_path, opts)
end
end
local gitea_base = opts.gitea_base
or "https://git.davoryn.de/sporel"
local gitea_base = resolve_gitea_base(opts)
local warnings, errors = {}, {}
run_state_check(unique, install_root, gitea_base, warnings, errors)
@@ -523,17 +587,20 @@ function M.ensure_for_module_at(module_manifest_path, opts)
end
-- Convenience wrapper resolving the module-id via install layout.
-- Slice 6 will replace SPOREL_INSTALL_ROOT with engine.install_root().
-- Resolves install_root via the same Slice 6 chain as ensure_for_module_at:
-- explicit opts → engine.install_root() → SPOREL_INSTALL_ROOT env var.
function M.ensure_for_module(module_id, opts)
opts = opts or {}
local install_root = opts.install_root
or os.getenv("SPOREL_INSTALL_ROOT")
local install_root = resolve_install_root(opts)
if not install_root then
return {
ok = false, conflicts = {}, warnings = {},
errors = {{
lib_id = "<module>", kind = "config",
message = "no install_root for ensure_for_module",
message = "no install_root for ensure_for_module: " ..
"pass opts.install_root, set SPOREL_INSTALL_ROOT, " ..
"or run under an engine providing " ..
"engine.install_root()",
}},
closure = {},
}

View File

@@ -1,6 +1,6 @@
{
"id": "lib-management.dep-fetcher",
"version": "0.1.0",
"version": "0.1.1",
"api_min": "0.1",
"deps": [
{"id": "lib-core.git", "version": "0.1.0"}