Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec5264915b | ||
|
|
ca2878c3e9 |
254
README.md
Normal file
254
README.md
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
# lib-core.git
|
||||||
|
|
||||||
|
Native libgit2-Wrapper, der synchrone und asynchrone Git-Operationen an Lua
|
||||||
|
exponiert. Bindet libgit2 statisch (vendored, ADR-0047) und folgt dem
|
||||||
|
Native-Lib-Pattern aus ADR-0046: Die Lib wird als Shared-Library geladen und
|
||||||
|
über `sporel_lib_init(L, api)` initialisiert, wobei der Engine-API-Struct
|
||||||
|
(u. a. der Async-Worker-Pool) hereingereicht wird. Transport ausschließlich
|
||||||
|
über **HTTPS** — kein SSH (hält die statische Link-Surface klein und vermeidet
|
||||||
|
libssh2 + Crypto-Abhängigkeiten). Für private Gitea-Repos liefert ein
|
||||||
|
HTTP-Basic-Credential-Callback den Token (Username literal `token`).
|
||||||
|
|
||||||
|
**Version:** 0.2.0
|
||||||
|
**Lib-ID:** lib-core.git
|
||||||
|
**Requires:** (none — native; nutzt den Engine-Async-Pool via `engine_api`)
|
||||||
|
**Tags:** git, libgit2, native, clone, async, ls-remote, gitea, https
|
||||||
|
|
||||||
|
## Topology
|
||||||
|
|
||||||
|
<!-- topology:start (auto-generated; do not edit) -->
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
this["lib-core.git"]
|
||||||
|
engine["engine.*"]
|
||||||
|
this --> engine
|
||||||
|
```
|
||||||
|
<!-- topology:end -->
|
||||||
|
|
||||||
|
Native-Lib: bindet libgit2 statisch ein und greift über den
|
||||||
|
`sporel_engine_api`-Struct auf den Engine-Async-Worker-Pool zu
|
||||||
|
(`async_submit` / `async_is_done` / `async_take_result`). Die einzige
|
||||||
|
libgit2-Berührung von einem Nicht-Lua-Thread ist der `ls-remote`-Worker;
|
||||||
|
libgit2 ist nach `git_libgit2_init` voll thread-safe.
|
||||||
|
|
||||||
|
## Transport & Auth
|
||||||
|
|
||||||
|
- **HTTPS only** — kein SSH-Transport. Backend ist WinHTTP (Windows) bzw.
|
||||||
|
OpenSSL/mbedTLS (Linux) je nach libgit2-Autodetection.
|
||||||
|
- **HTTP-Basic-Credential-Callback** — fordert ein Remote Credentials an,
|
||||||
|
liefert der Callback `username="token"`, `password=<token>` für
|
||||||
|
Gitea-Token-Auth. Andere Credential-Typen (SSH-Keys) werden mit
|
||||||
|
`GIT_PASSTHROUGH` abgelehnt. Ohne konfigurierten Token wird ebenfalls
|
||||||
|
durchgereicht, sodass libgit2 den rohen "authentication required"-Fehler
|
||||||
|
an den Aufrufer surfaced.
|
||||||
|
- **Token-Quelle** — bei `sporel_lib_init` wird der Token aus der
|
||||||
|
Umgebungsvariable `SPOREL_GITEA_TOKEN` gelatcht (leer/ungesetzt →
|
||||||
|
anonym). `git.set_token(t)` überschreibt diesen Default zur Laufzeit.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### `git.clone(url, dest, ref?)`
|
||||||
|
|
||||||
|
**Syntax:** `git.clone(url: string, dest: string, ref?: string) -> ok: boolean, err: string|nil`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
local git = require("lib-core.git")
|
||||||
|
local ok, err = git.clone("https://git.davoryn.de/calic/foo.git", "/tmp/foo", "v0.2.0")
|
||||||
|
if not ok then engine.print("clone failed: "..err) end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** Klont `url` nach `dest`. Ist `ref` gesetzt, wird dieser
|
||||||
|
Branch nach dem Clone ausgecheckt (`checkout_branch`). Nutzt den
|
||||||
|
HTTP-Basic-Credential-Callback. Liefert `true, nil` bei Erfolg, sonst
|
||||||
|
`false, <libgit2-Fehlermeldung>`.
|
||||||
|
|
||||||
|
### `git.checkout(dir, ref)`
|
||||||
|
|
||||||
|
**Syntax:** `git.checkout(dir: string, ref: string) -> ok: boolean, err: string|nil`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
local ok, err = git.checkout("/tmp/foo", "v0.2.0")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** Öffnet das Repo in `dir`, löst `ref` auf (Tag, Branch oder
|
||||||
|
SHA via `git_revparse_single`), checkt den Tree mit `GIT_CHECKOUT_SAFE` aus
|
||||||
|
und setzt HEAD detached auf den Commit. Liefert `true, nil` bei Erfolg, sonst
|
||||||
|
`false, <Fehlermeldung>`.
|
||||||
|
|
||||||
|
### `git.describe(dir)`
|
||||||
|
|
||||||
|
**Syntax:** `git.describe(dir: string) -> desc: string|nil, err: string|nil`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
local desc, err = git.describe("/tmp/foo") -- z. B. "v0.2.0-3-g1a2b3c4-dirty"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** `git describe --tags` auf den Workdir. Strategie
|
||||||
|
`GIT_DESCRIBE_TAGS`, mit `-dirty`-Suffix bei uncommitteten Änderungen.
|
||||||
|
Liefert den Describe-String + `nil`, oder `nil, <Fehlermeldung>`.
|
||||||
|
|
||||||
|
### `git.tags(dir)`
|
||||||
|
|
||||||
|
**Syntax:** `git.tags(dir: string) -> tags: string[]|nil, err: string|nil`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
local tags, err = git.tags("/tmp/foo")
|
||||||
|
for _, t in ipairs(tags or {}) do engine.print(t) end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** Listet alle lokalen Tags des Repos in `dir`
|
||||||
|
(`git_tag_list`). Liefert ein Array von Tag-Namen + `nil`, oder
|
||||||
|
`nil, "not a repo"` / `nil, <Fehlermeldung>`.
|
||||||
|
|
||||||
|
### `git.has_ref(dir, ref)`
|
||||||
|
|
||||||
|
**Syntax:** `git.has_ref(dir: string, ref: string) -> boolean`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
if git.has_ref("/tmp/foo", "v0.2.0") then ... end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** `true` iff `ref` (Tag, Branch oder SHA) im Repo unter `dir`
|
||||||
|
auflösbar ist. `false`, wenn das Repo nicht geöffnet werden kann oder `ref`
|
||||||
|
nicht auflösbar ist. Wirft keinen Fehler.
|
||||||
|
|
||||||
|
### `git.is_repo(dir)`
|
||||||
|
|
||||||
|
**Syntax:** `git.is_repo(dir: string) -> boolean`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
if not git.is_repo("/tmp/foo") then git.clone(url, "/tmp/foo") end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** `true` iff `dir` ein öffenbares Git-Repository ist, sonst
|
||||||
|
`false`. Wirft keinen Fehler.
|
||||||
|
|
||||||
|
### `git.fetch(url, dir)`
|
||||||
|
|
||||||
|
**Syntax:** `git.fetch(url: string, dir: string) -> ok: boolean, err: string|nil`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
local ok, err = git.fetch("https://git.davoryn.de/calic/foo.git", "/tmp/foo")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** Anonym-Remote-Fetch in das Repo unter `dir` — erzeugt ein
|
||||||
|
detached anonymous remote für `url` und fetcht, **ohne** die Refspecs von
|
||||||
|
`origin` zu mutieren. Nutzt den HTTP-Basic-Credential-Callback. Liefert
|
||||||
|
`true, nil` bei Erfolg, sonst `false, <Fehlermeldung>`.
|
||||||
|
|
||||||
|
### `git.set_token(t)`
|
||||||
|
|
||||||
|
**Syntax:** `git.set_token(t: string|nil) -> void`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
git.set_token(engine.config("gitea_token")) -- override env-default
|
||||||
|
git.set_token(nil) -- zurück auf anonym
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** Überschreibt den In-Memory-Gitea-HTTP-Basic-Token, den der
|
||||||
|
Credential-Callback verwendet. `nil` oder leerer String setzt auf anonym
|
||||||
|
zurück. Source-of-Truth bei Lib-Init ist die Env-Variable
|
||||||
|
`SPOREL_GITEA_TOKEN`; dieser Setter lässt den Launcher (oder jeden Consumer)
|
||||||
|
den Default zur Laufzeit überschreiben — etwa mit `engine.config("gitea_token")`,
|
||||||
|
sobald die Engine-Bindings live sind.
|
||||||
|
|
||||||
|
### `git.start_ls_remote_tags(url)`
|
||||||
|
|
||||||
|
**Syntax:** `git.start_ls_remote_tags(url: string) -> handle: userdata`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
local h = git.start_ls_remote_tags("https://git.davoryn.de/calic/foo.git")
|
||||||
|
-- später, pro Frame:
|
||||||
|
if git.is_done(h) then
|
||||||
|
local res = git.take_result(h)
|
||||||
|
-- res.tags / res.error+res.kind
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** Startet ein **asynchrones** `ls-remote --tags` gegen `url`
|
||||||
|
über den Engine-Async-Worker-Pool und liefert sofort ein Lua-owned
|
||||||
|
Handle (Userdata). Der Worker berührt den Lua-State nicht; das Handle wird
|
||||||
|
mit `is_done` gepollt und mit `take_result` eingelöst. Peeled-Tag-Duplikate
|
||||||
|
(`^{}`) werden herausgefiltert. Wirft, wenn der Engine-API-Struct nicht
|
||||||
|
verdrahtet ist (`sporel_lib_init`-Bug). Bildet zusammen mit `is_done` +
|
||||||
|
`take_result` das Async-Triple.
|
||||||
|
|
||||||
|
### `git.is_done(handle)`
|
||||||
|
|
||||||
|
**Syntax:** `git.is_done(handle: userdata) -> boolean`
|
||||||
|
|
||||||
|
**Description:** `true`, sobald der Async-`ls-remote`-Worker für `handle`
|
||||||
|
fertig ist. Non-blocking — pro Frame pollen, bis `true`. Wirft bei
|
||||||
|
Nicht-Userdata oder nicht verdrahtetem Engine-API.
|
||||||
|
|
||||||
|
### `git.take_result(handle)`
|
||||||
|
|
||||||
|
**Syntax:** `git.take_result(handle: userdata) -> result: table|nil, err: string|nil`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```lua
|
||||||
|
local res = git.take_result(h)
|
||||||
|
if res.tags then
|
||||||
|
for _, t in ipairs(res.tags) do engine.print(t) end
|
||||||
|
else
|
||||||
|
engine.print(("ls-remote %s: %s"):format(res.kind, res.error))
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Description:** Löst ein **fertiges** Async-Handle ein und gibt das
|
||||||
|
Engine-Handle frei (danach ist `handle` verbraucht). Bei Erfolg:
|
||||||
|
`{ tags = { "v0.1.0", "v0.2.0", ... } }`. Bei Fehler:
|
||||||
|
`{ error = <Fehlermeldung>, kind = <kind> }` mit
|
||||||
|
`kind ∈ { "auth-required", "not-found", "network", "other" }` —
|
||||||
|
abgeleitet aus `GIT_EAUTH` bzw. Pattern-Matching auf die libgit2-Fehlermeldung
|
||||||
|
(401/Authentication → `auth-required`, 404/Not Found → `not-found`,
|
||||||
|
Net/HTTP/SSL-Fehlerklasse → `network`, sonst → `other`). Ist das Handle noch
|
||||||
|
nicht fertig, liefert die Funktion `nil, "not done"`.
|
||||||
|
|
||||||
|
## Async-Pattern
|
||||||
|
|
||||||
|
Das Async-Triple ist non-blocking und für den Per-Frame-Poll im
|
||||||
|
Engine-Game-Loop ausgelegt:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- einmalig starten:
|
||||||
|
local h = git.start_ls_remote_tags(url)
|
||||||
|
|
||||||
|
-- in on_update, pro Frame:
|
||||||
|
if h and git.is_done(h) then
|
||||||
|
local res = git.take_result(h)
|
||||||
|
h = nil
|
||||||
|
if res.tags then
|
||||||
|
-- Tags verarbeiten
|
||||||
|
elseif res.kind == "auth-required" then
|
||||||
|
-- Token setzen und erneut versuchen
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Die synchronen Operationen (`clone`, `checkout`, `describe`, `tags`,
|
||||||
|
`has_ref`, `is_repo`, `fetch`) blockieren den aufrufenden Thread und sind für
|
||||||
|
Tooling- / CLI-Kontexte gedacht, nicht für den Hot-Loop.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Native-Lib (`manifest.lib`: `"native": true`). Gebaut via CMake
|
||||||
|
(`CMakeLists.txt`), libgit2 wird vendored statisch gelinkt (ADR-0047). Das
|
||||||
|
Artefakt (`sporel_git.dll` bzw. `.so`) exportiert `sporel_lib_init`, das die
|
||||||
|
Lua-Funktionstabelle aufbaut und den Engine-API-Struct latcht.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- ADR-0046 (Native-Lib-Pattern — `sporel_lib_init`, Engine-API-Struct, Async-Pool)
|
||||||
|
- ADR-0047 (libgit2-Vendoring — statischer Link, HTTPS-only)
|
||||||
|
- ADR-0001 (engine knows verbs, libs bring nouns)
|
||||||
|
- Quelle: `src/sporel_git.c`
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "lib-core.git",
|
"id": "lib-core.git",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"api_min": "0.1",
|
"api_min": "0.1",
|
||||||
"native": true,
|
"native": true,
|
||||||
"deps": []
|
"deps": []
|
||||||
|
|||||||
BIN
sporel_git.dll
BIN
sporel_git.dll
Binary file not shown.
@@ -33,6 +33,15 @@
|
|||||||
* sporel_lib_init; used by the async wrappers below. */
|
* sporel_lib_init; used by the async wrappers below. */
|
||||||
static const sporel_engine_api *g_api = NULL;
|
static const sporel_engine_api *g_api = NULL;
|
||||||
|
|
||||||
|
/* Optional Gitea / HTTP-Basic token. Populated at sporel_lib_init from
|
||||||
|
* SPOREL_GITEA_TOKEN. When set, every libgit2 transport request that
|
||||||
|
* asks for credentials gets username="token", password=g_token via the
|
||||||
|
* cred-acquire callback. Empty string = anonymous (callback declines).
|
||||||
|
*
|
||||||
|
* Read once at init; no per-call override yet. A future engine.config
|
||||||
|
* binding can refresh it (rebind via a setter) when settings UI lands. */
|
||||||
|
static char g_token[512] = "";
|
||||||
|
|
||||||
/* Call once per process; thread-safe via libgit2's own internal flag. */
|
/* Call once per process; thread-safe via libgit2's own internal flag. */
|
||||||
static void ensure_libgit2_init(void) {
|
static void ensure_libgit2_init(void) {
|
||||||
static int initialized = 0;
|
static int initialized = 0;
|
||||||
@@ -42,6 +51,45 @@ static void ensure_libgit2_init(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* HTTP-Basic credential callback. libgit2 invokes this when the remote
|
||||||
|
* demands auth. We service USERPASS_PLAINTEXT (Gitea token via
|
||||||
|
* Basic-Auth, username literally "token") and decline everything else
|
||||||
|
* (SSH-key types are unsupported since we ship no SSH transport). */
|
||||||
|
static int sporel_cred_acquire_cb(git_credential **out,
|
||||||
|
const char *url,
|
||||||
|
const char *username_from_url,
|
||||||
|
unsigned int allowed_types,
|
||||||
|
void *payload) {
|
||||||
|
(void)url; (void)username_from_url; (void)payload;
|
||||||
|
if (!(allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)) {
|
||||||
|
return GIT_PASSTHROUGH;
|
||||||
|
}
|
||||||
|
if (g_token[0] == '\0') {
|
||||||
|
/* No token configured — fall through so libgit2 surfaces the
|
||||||
|
* raw "authentication required" error to the caller. */
|
||||||
|
return GIT_PASSTHROUGH;
|
||||||
|
}
|
||||||
|
return git_credential_userpass_plaintext_new(out, "token", g_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Map a libgit2 error to a stable "kind" string the Lua side surfaces
|
||||||
|
* in result.kind. Lets the launcher distinguish local-only repos
|
||||||
|
* (404 with creds) from need-auth (401 without creds) from genuine
|
||||||
|
* network failures. Pattern-matches the error message because
|
||||||
|
* libgit2's error-class taxonomy varies across HTTP backends. */
|
||||||
|
static const char *classify_git_error(int rc, const git_error *e) {
|
||||||
|
const char *msg = (e && e->message) ? e->message : "";
|
||||||
|
if (rc == GIT_EAUTH) return "auth-required";
|
||||||
|
if (strstr(msg, "401") || strstr(msg, "authentication required")
|
||||||
|
|| strstr(msg, "Authentication")) return "auth-required";
|
||||||
|
if (strstr(msg, "404") || strstr(msg, "Not Found")
|
||||||
|
|| strstr(msg, "not found")) return "not-found";
|
||||||
|
if (e && (e->klass == GIT_ERROR_NET || e->klass == GIT_ERROR_HTTP
|
||||||
|
|| e->klass == GIT_ERROR_SSL))
|
||||||
|
return "network";
|
||||||
|
return "other";
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Synchronous bindings ------------------------------------------------ */
|
/* ---- Synchronous bindings ------------------------------------------------ */
|
||||||
|
|
||||||
/* git.clone(url, dest_dir, ref) -> ok, err
|
/* git.clone(url, dest_dir, ref) -> ok, err
|
||||||
@@ -54,6 +102,7 @@ static int l_clone(lua_State *L) {
|
|||||||
git_repository *repo = NULL;
|
git_repository *repo = NULL;
|
||||||
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
|
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
|
||||||
if (ref) opts.checkout_branch = ref;
|
if (ref) opts.checkout_branch = ref;
|
||||||
|
opts.fetch_opts.callbacks.credentials = sporel_cred_acquire_cb;
|
||||||
|
|
||||||
int rc = git_clone(&repo, url, dest, &opts);
|
int rc = git_clone(&repo, url, dest, &opts);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
@@ -215,6 +264,7 @@ static int l_fetch(lua_State *L) {
|
|||||||
if (git_remote_create_anonymous(&remote, repo, url) != 0) goto err;
|
if (git_remote_create_anonymous(&remote, repo, url) != 0) goto err;
|
||||||
|
|
||||||
git_fetch_options fopts = GIT_FETCH_OPTIONS_INIT;
|
git_fetch_options fopts = GIT_FETCH_OPTIONS_INIT;
|
||||||
|
fopts.callbacks.credentials = sporel_cred_acquire_cb;
|
||||||
int rc = git_remote_fetch(remote, NULL, &fopts, NULL);
|
int rc = git_remote_fetch(remote, NULL, &fopts, NULL);
|
||||||
git_remote_free(remote);
|
git_remote_free(remote);
|
||||||
git_repository_free(repo);
|
git_repository_free(repo);
|
||||||
@@ -247,6 +297,7 @@ typedef struct {
|
|||||||
/* outputs: */
|
/* outputs: */
|
||||||
int ok;
|
int ok;
|
||||||
char err[512];
|
char err[512];
|
||||||
|
char kind[32]; /* "auth-required" | "not-found" | "network" | "other" */
|
||||||
int tag_count;
|
int tag_count;
|
||||||
char tags[GIT_ASYNC_MAX_TAGS][GIT_ASYNC_TAG_LEN];
|
char tags[GIT_ASYNC_MAX_TAGS][GIT_ASYNC_TAG_LEN];
|
||||||
} git_async_ctx;
|
} git_async_ctx;
|
||||||
@@ -261,6 +312,7 @@ static void do_ls_remote_tags(void *vctx) {
|
|||||||
if (rc != 0) goto fail;
|
if (rc != 0) goto fail;
|
||||||
|
|
||||||
git_remote_callbacks cbs = GIT_REMOTE_CALLBACKS_INIT;
|
git_remote_callbacks cbs = GIT_REMOTE_CALLBACKS_INIT;
|
||||||
|
cbs.credentials = sporel_cred_acquire_cb;
|
||||||
rc = git_remote_connect(remote, GIT_DIRECTION_FETCH, &cbs, NULL, NULL);
|
rc = git_remote_connect(remote, GIT_DIRECTION_FETCH, &cbs, NULL, NULL);
|
||||||
if (rc != 0) goto fail;
|
if (rc != 0) goto fail;
|
||||||
|
|
||||||
@@ -289,6 +341,7 @@ fail:;
|
|||||||
const git_error *e = git_error_last();
|
const git_error *e = git_error_last();
|
||||||
snprintf(c->err, sizeof c->err, "%s",
|
snprintf(c->err, sizeof c->err, "%s",
|
||||||
e && e->message ? e->message : "ls_remote failed");
|
e && e->message ? e->message : "ls_remote failed");
|
||||||
|
snprintf(c->kind, sizeof c->kind, "%s", classify_git_error(rc, e));
|
||||||
c->ok = 0;
|
c->ok = 0;
|
||||||
if (remote) git_remote_free(remote);
|
if (remote) git_remote_free(remote);
|
||||||
}
|
}
|
||||||
@@ -301,6 +354,22 @@ typedef struct {
|
|||||||
git_async_ctx ctx; /* Lua-owned via userdata */
|
git_async_ctx ctx; /* Lua-owned via userdata */
|
||||||
} git_handle_userdata;
|
} git_handle_userdata;
|
||||||
|
|
||||||
|
/* git.set_token(token | nil) -> nil
|
||||||
|
* Replaces the in-memory Gitea HTTP-Basic token used by the credential
|
||||||
|
* callback. Passing nil / empty string reverts to anonymous. Source of
|
||||||
|
* truth at lib-init is SPOREL_GITEA_TOKEN env; the launcher (or any
|
||||||
|
* consumer) calls this to override with engine.config("gitea_token")
|
||||||
|
* once the Lua engine bindings are live. */
|
||||||
|
static int l_set_token(lua_State *L) {
|
||||||
|
if (lua_isnoneornil(L, 1)) {
|
||||||
|
g_token[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const char *t = luaL_checkstring(L, 1);
|
||||||
|
snprintf(g_token, sizeof g_token, "%s", t);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* git.start_ls_remote_tags(url) -> handle (userdata) */
|
/* git.start_ls_remote_tags(url) -> handle (userdata) */
|
||||||
static int l_start_ls_remote_tags(lua_State *L) {
|
static int l_start_ls_remote_tags(lua_State *L) {
|
||||||
if (!g_api) {
|
if (!g_api) {
|
||||||
@@ -349,6 +418,8 @@ static int l_take_result(lua_State *L) {
|
|||||||
} else {
|
} else {
|
||||||
lua_pushstring(L, u->ctx.err);
|
lua_pushstring(L, u->ctx.err);
|
||||||
lua_setfield(L, -2, "error");
|
lua_setfield(L, -2, "error");
|
||||||
|
lua_pushstring(L, u->ctx.kind);
|
||||||
|
lua_setfield(L, -2, "kind");
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -359,6 +430,12 @@ SPOREL_EXPORT int sporel_lib_init(lua_State *L, const sporel_engine_api *api) {
|
|||||||
g_api = api;
|
g_api = api;
|
||||||
ensure_libgit2_init();
|
ensure_libgit2_init();
|
||||||
|
|
||||||
|
/* Latch the Gitea token from env, if present. Empty/unset → anon. */
|
||||||
|
const char *tok = getenv("SPOREL_GITEA_TOKEN");
|
||||||
|
if (tok && *tok) {
|
||||||
|
snprintf(g_token, sizeof g_token, "%s", tok);
|
||||||
|
}
|
||||||
|
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
|
|
||||||
lua_pushcfunction(L, l_clone); lua_setfield(L, -2, "clone");
|
lua_pushcfunction(L, l_clone); lua_setfield(L, -2, "clone");
|
||||||
@@ -372,6 +449,7 @@ SPOREL_EXPORT int sporel_lib_init(lua_State *L, const sporel_engine_api *api) {
|
|||||||
lua_pushcfunction(L, l_start_ls_remote_tags); lua_setfield(L, -2, "start_ls_remote_tags");
|
lua_pushcfunction(L, l_start_ls_remote_tags); lua_setfield(L, -2, "start_ls_remote_tags");
|
||||||
lua_pushcfunction(L, l_is_done); lua_setfield(L, -2, "is_done");
|
lua_pushcfunction(L, l_is_done); lua_setfield(L, -2, "is_done");
|
||||||
lua_pushcfunction(L, l_take_result); lua_setfield(L, -2, "take_result");
|
lua_pushcfunction(L, l_take_result); lua_setfield(L, -2, "take_result");
|
||||||
|
lua_pushcfunction(L, l_set_token); lua_setfield(L, -2, "set_token");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user