From 177c2bda45f39c87e9456dc867099b5104b7eb73 Mon Sep 17 00:00:00 2001 From: Axel Meyer Date: Sun, 31 May 2026 01:15:40 +0200 Subject: [PATCH] lib-core.git-test v0.1.0: smoke test for the libgit2 wrapper Exercises the sync API (clone, is_repo, describe, tags) against a public Gitea repo over HTTPS, plus the async ls_remote_tags handshake through the engine async-pool. Designed for SPOREL_CI runs: kicks off the async work in init(), polls in update(), calls engine.exit(0) once the result has been taken. --- init.lua | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ manifest.core | 6 +++ manifest.module | 11 +++++ 3 files changed, 129 insertions(+) create mode 100644 init.lua create mode 100644 manifest.core create mode 100644 manifest.module diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..c45ad0d --- /dev/null +++ b/init.lua @@ -0,0 +1,112 @@ +-- lib-core.git-test/init.lua +-- +-- Smoke test for lib-core.git v0.1.0. Exercises the synchronous API +-- (is_repo, clone, describe, tags) against a real Gitea repo over +-- HTTPS, plus the asynchronous start_ls_remote_tags handshake through +-- the engine async-pool. +-- +-- Designed to run under SPOREL_CI=1 (calls engine.exit on completion). +-- Interactive run just sits on the polling loop and renders nothing. + +local git = require("lib-core.git") + +-- libgit2 sees this as C:\tmp\... on Windows (Win32 path-translation, +-- NOT MSYS2's /tmp -> /c/msys64/tmp). For cross-platform smoke runs we +-- choose a path that exists in the engine's CWD if possible; falling +-- back to the platform tmp is fine because the test runs in CI mode and +-- cleans up after itself. +local CLONE_DIR = (os.getenv("TEMP") or "/tmp") .. "/sporel-git-smoke-clone" +-- Slash normalization for libgit2: it accepts forward slashes on both +-- platforms. +CLONE_DIR = CLONE_DIR:gsub("\\", "/") +-- Public Gitea repo used as the smoke-test target. sporel-meta and +-- sporel-engine are private (require token auth which lib-core.git +-- v0.1.0 does not yet plumb through), but sporel-lib-core.maps is +-- public and conveniently small. +local TARGET_URL = "https://git.davoryn.de/sporel/sporel-lib-core.maps.git" + +-- Cross-platform recursive rmdir via the platform shell. os.execute on +-- Windows invokes cmd.exe (NOT MSYS2-bash) so we branch on path-sep. +local function rmrf(path) + local is_win = package.config:sub(1, 1) == "\\" + if is_win then + local winpath = path:gsub("/", "\\") + os.execute('if exist "' .. winpath .. '" rmdir /S /Q "' .. winpath .. '"') + else + os.execute("rm -rf '" .. path .. "'") + end +end + +-- Module-state for the async leg, populated in init() and polled in update(). +local async_handle = nil +local async_done = false +local frames = 0 + +function init(ctx) + -- Clean state from any previous run. (Done in init() rather than at + -- chunk-load so a hot-reload during dev cycles gets a fresh dir.) + rmrf(CLONE_DIR) + + -- --- Synchronous probes ------------------------------------------------- + -- is_repo of a non-existent path: must return false. (Avoid using /tmp + -- because Windows may have a .git somewhere upstream of it.) + assert(git.is_repo(CLONE_DIR .. "-no-such-subdir") == false, + "is_repo should reject a non-existent dir") + + engine.print("git-test: cloning " .. TARGET_URL) + local ok, err = git.clone(TARGET_URL, CLONE_DIR, "master") + assert(ok, "clone failed: " .. tostring(err)) + assert(git.is_repo(CLONE_DIR), "cloned dir should be a repo") + engine.print("git-test: clone OK") + + local desc, derr = git.describe(CLONE_DIR) + -- describe can legitimately fail on a repo with no annotated tags; + -- treat nil as soft-fail with a marker, not a hard assertion. + if desc then + engine.print("git-test: describe: " .. desc) + else + engine.print("git-test: describe (no tag yet): " .. tostring(derr)) + end + + local tags, terr = git.tags(CLONE_DIR) + assert(tags ~= nil, "tags failed: " .. tostring(terr)) + engine.print("git-test: local tag count = " .. #tags) + + -- --- Kick off async ls-remote ------------------------------------------ + async_handle = git.start_ls_remote_tags(TARGET_URL) + engine.print("git-test: async ls_remote_tags dispatched") +end + +function update(ctx, dt) + frames = frames + 1 + + if async_handle and not async_done then + if git.is_done(async_handle) then + local result = git.take_result(async_handle) + async_handle = nil + async_done = true + if result.tags then + engine.print("git-test: async tag count = " .. #result.tags) + else + engine.print("git-test: async ERROR = " + .. tostring(result.error)) + end + end + end + + -- CI completion: drop out once async finished (or after a generous + -- bound — network hiccup safety). + if os.getenv("SPOREL_CI") == "1" then + if async_done then + engine.print("git-test: PASS (CI)") + engine.exit(0) + elseif frames > 1800 then -- ~30s at 60fps + engine.print("git-test: TIMEOUT after " .. frames .. " frames") + engine.exit(1) + end + end +end + +function render() + -- No visuals; this is a CI smoke. Interactive runs see a blank window. +end diff --git a/manifest.core b/manifest.core new file mode 100644 index 0000000..c6aa3ee --- /dev/null +++ b/manifest.core @@ -0,0 +1,6 @@ +{ + "id": "lib-core.git-test.core", + "kind": "lib", + "version": "0.1.0", + "license": "Proprietary" +} diff --git a/manifest.module b/manifest.module new file mode 100644 index 0000000..0492a29 --- /dev/null +++ b/manifest.module @@ -0,0 +1,11 @@ +{ + "id": "lib-core.git-test", + "version": "0.1.0", + "kind": "module", + "api": "^0.1", + "ci_frames": 1800, + "entry_point": "init.lua", + "deps": [ + {"id": "lib-core.git", "version": "0.1.0"} + ] +}