包清單(Package Manifest)
lip 包由其歸檔根部的 manifest.json 描述。它就是 LeviLamina 的 manifest.json,
再加上 lip 包格式第 4 版新增的鍵。LeviLamina 會忽略 lip 新增的鍵,lip 也不會改變任何
LeviLamina 鍵的含義。規範性定義見包格式規範。
該格式由 JSON Schema
manifest.v4.schema.json
描述。lip 仍可讀取舊的 tooth.json 格式,參見舊格式:tooth.json。
歸檔
包位於一個歸檔中,歸檔是一棵根部帶有 manifest.json 的文件樹:
- git 歸檔:git 倉庫的一個提交,通常是某個版本標籤所指的提交;
- ZIP 歸檔:一個 ZIP 文件。擴展名為
.mcmod的 ZIP 歸檔稱為 mcmod。一個文件是否為 ZIP 歸檔只看內容,與擴展名無關; - 目錄歸檔:另一個歸檔內的一個目錄(以
/結尾的相對引用)。
單獨的本地目錄不是歸檔:要從磁盤安裝包,請先把它打包為 .mcmod 文件。
manifest.json 必須是 UTF-8 編碼的嚴格 JSON(不允許註釋),頂層必須是對象。
示例
一個 mod,位於 LegacyMoney-server-win-x64.mcmod 的根部:
{
"formatVersion": 4,
"formatUuid": "0c6b977a-64ad-441f-9a29-c7f4b196882f",
"name": "LegacyMoney",
"entry": "LegacyMoney.dll",
"type": "native",
"version": "0.22.0",
"platform": "server",
"rid": "win-x64",
"refs": ["github.com/LiteLDev/LegacyMoney"],
"dependencies": [
{
"name": "iListenAttentively",
"version": "^0.17.0",
"refs": ["github.com/MiracleForest/iListenAttentively"]
}
],
"installDependencies": [
{ "version": ">=26.51.0 <26.52.0", "refs": ["github.com/LiteLDev/LeviLamina"] }
]
}格式標記
| 字段 | 類型 | 說明 |
|---|---|---|
formatVersion | int | 必須為 4。 |
formatUuid | string | 必須為 "0c6b977a-64ad-441f-9a29-c7f4b196882f"。 |
同時帶有兩個標記的文檔是第 4 版文檔。兩個標記都沒有的文檔是普通的 LeviLamina 清單:
lip 只把它當作自包含的具體包接受,忽略其依賴中的 refs 和 installDependencies
(不會安裝其他任何包),也從不把它當作路由文檔。只帶有一個標記或標記值不正確的文檔無效。
路由文檔與具體文檔
含有 artifacts 鍵的文檔是路由文檔。它本身不安裝任何東西,而是根據請求的變體和環境
指向其他歸檔。
不含 artifacts 的文檔是具體文檔,其歸檔稱為具體包。具體包的 name 即其身份;
比較名稱時不區分 ASCII 大小寫。
引用
引用(ref)指向一個歸檔,形式為 target[#variant];spec target 後面還可以跟 git 引用:
spec[#variant][@git-ref]。
| target | 示例 | 指向 |
|---|---|---|
| 相對目錄 | ./quickjs/ | 當前文檔所在歸檔內的一個目錄。 |
| 相對文件 | ./LegacyMoney.mcmod | 當前文檔所在歸檔內的一個 ZIP 文件。 |
| URL | https://example.com/pkg.mcmod | 通過 HTTPS 下載的 ZIP 歸檔(會跟隨重定向)。 |
| spec | github.com/LiteLDev/LeviLamina | git 倉庫 https://<spec>.git。 |
| 帶 git 引用的 spec | github.com/LiteLDev/LeviLamina@branch:main | 該倉庫的一個提交。 |
- 相對 target 以
./開頭,不得包含空段、.段、..段或\。 - spec 必須是合法的 Go module path。其版本是名稱恰為
v<version>的標籤,其中<version>是 SemVer 2.0.0 版本(例如v1.2.3)。其他標籤(例如v1.2)會被忽略。 - git 引用把 spec 固定到一個提交,而不是其各個版本:
@branch:<branch>(分支的頭)、@tag:<tag>(任意標籤,例如@tag:nightly)或@rev:<commit>(完整的 40 位提交 ID)。 它寫在 variant 之後:github.com/LiteLDev/LegacyScriptEngine#lua@branch:develop。只有 spec 引用可以帶 git 引用。 - 不允許其他 scheme,例如
http:和file:。 - variant 必須匹配
^[a-z0-9_]+(/[a-z0-9_]+)?$。不帶#的 ref,其 variant 為""。
路由文檔
| 字段 | 類型 | 必需 | 說明 |
|---|---|---|---|
artifacts | array | 是 | 非空。參見 Artifact。 |
name | string | 否 | 只用於模板。 |
version | string | 否 | SemVer 2.0.0。只用於模板。 |
refs | string[] | 否 | 上游位置,僅供參考。 |
Artifact
| 字段 | 類型 | 必需 | 說明 |
|---|---|---|---|
variant | string | 否 | 默認為 ""。 |
platform | string 或 string[] | 否 | "server"、"client" 或兩者。缺省表示不限。 |
rid | string 或 string[] | 否 | .NET RID。缺省表示不限。 |
refs | string[] | 是 | 非空。按順序作為備選。 |
lip 保留 variant 等於 ref 的 variant、且所有選擇器(platform、rid)都包含當前
環境的條目,再從中選出寫出選擇器鍵最多的那一個。兩個 variant 相同的條目不得寫出
相同的選擇器鍵且取值有交集。
模板
refs 中的字符串可以包含 {{name}}、{{version}}、{{platform}} 和
{{rid}}。{{name}} 和 {{version}} 取文檔中同名鍵的值(缺少該鍵時,該 ref 不產生候選);
{{platform}} 和 {{rid}} 取環境的值。出現其他任何 {{...}} 時,文檔無效。
{
"formatVersion": 4,
"formatUuid": "0c6b977a-64ad-441f-9a29-c7f4b196882f",
"name": "LegacyMoney",
"version": "0.22.0",
"artifacts": [
{
"platform": ["server", "client"],
"rid": "win-x64",
"refs": [
"https://github.com/LiteLDev/LegacyMoney/releases/download/v{{version}}/{{name}}-{{platform}}-{{rid}}.mcmod"
]
}
]
}具體文檔
| 字段 | 類型 | 必需 | 說明 |
|---|---|---|---|
name | string | 是 | 身份。必須匹配 ^[A-Za-z0-9][A-Za-z0-9._-]*$。 |
version | string | 是 | SemVer 2.0.0。位於版本標籤(v<version>)處時必須等於標籤版本。 |
type | string | 是 | "files" 表示文件包;其他任何值都是 LeviLamina 的 mod 類型(例如 "native")。 |
platform | string | 否 | "server"、"client" 或 "universal"(默認)。 |
rid | string | 否 | .NET RID。缺省表示不限。 |
refs | string[] | 否 | 上游位置,僅供參考。 |
dependencies | array | 否 | LeviLamina 依賴。含 refs 的條目也由 lip 安裝。參見依賴。 |
optionalDependencies | array | 否 | LeviLamina 可選依賴。lip 不安裝。 |
conflicts | array | 否 | 不得與本包同時安裝的包。忽略其中的 refs。 |
installDependencies | array | 否 | 只由 lip 安裝的包;LeviLamina 不讀取。refs 必需。 |
placements | array | 否 | 僅限文件包。參見 Placement。 |
preserveFiles | string[] | 否 | 已存在時保留、卸載時也保留的文件的 glob。 |
removeFiles | string[] | 否 | 卸載時額外刪除的路徑的 glob。 |
scripts | object | 否 | 生命週期腳本。參見腳本。 |
entry 等其餘 LeviLamina 鍵,lip 不使用。
只有當 ref 不帶 variant、platform 等於環境平臺或為 "universal"、rid 缺省或等於環境
RID、並且 version 在請求的範圍內時,具體包才是候選。
依賴
| 字段 | 類型 | 必需 | 說明 |
|---|---|---|---|
name | string | 是(installDependencies 中為否) | 被安裝的包必須具有的身份。 |
version | string | 否 | 版本範圍。缺省表示任意版本。 |
refs | string[] | 否(installDependencies 中為是) | 包的位置。出現時必須非空。 |
版本範圍使用 LeviLamina ll::data::VersionRequirement 的語法,例如 ^1.2.0、~1.2、
>=1.0.0 <2.0.0、1.x 或 >=1.0.0 || =0.9.0。不帶運算符的完整版本號(例如 1.2.3)
會被拒絕,請寫成 =1.2.3。
如需依賴某個分支、標籤或提交,而不是正式發佈的版本,請在 refs 中的 spec 上加 git 引用;
version 仍是版本範圍,因為 LeviLamina 也會讀取它:
{
"name": "LeviLamina",
"version": ">=1.5.0",
"refs": ["github.com/LiteLDev/LeviLamina@branch:main"]
}git 引用所指提交處的包必須聲明在範圍內的 version,否則該依賴無法滿足。反過來,從 git
引用安裝的包(作為依賴,或通過 lip install),
只要其版本在依賴的範圍內,也能滿足以同一 spec 和 variant、但不帶 git 引用的依賴;因此工作區
可以運行一個庫的開發構建,而依賴它的正式發佈的模組仍可安裝。
Placement
| 字段 | 類型 | 必需 | 說明 |
|---|---|---|---|
src | string | 是 | 歸檔內的路徑。 |
dest | string | 是 | 工作區內的路徑。 |
兩者都不得以 / 開頭,也不得包含 ..、\ 或盤符。值為 "" 或以 / 結尾時表示目錄:
src 下的全部文件寫入 dest 下;否則表示文件。src 與 dest 的形式必須相同。歸檔根部的
manifest.json 永不寫入。
Glob
* 匹配除 / 外的任意字符;單獨構成一段的 ** 匹配零個或多個段;以 / 結尾的模式匹配
目錄及其下的全部內容。對 mod,glob 相對於 mod 目錄;對文件包,相對於工作區根目錄。
腳本
| 字段 | 類型 | 說明 |
|---|---|---|
preInstall | string[] | 寫入文件前執行的命令。 |
postInstall | string[] | 寫入文件後執行的命令。 |
preUninstall | string[] | 刪除文件前執行的命令。 |
postUninstall | string[] | 刪除文件後執行的命令。 |
命令在工作區根目錄中執行,Windows 上使用 cmd /c,其他平臺使用 sh -c。任一命令以非零碼
退出時,操作失敗。
文件包與 mod
- mod(
type不是"files"):整個歸檔寫入mods/<name>/。服務端的plugins是指向mods的鏈接,因此也可以在plugins/<name>/下看到該 mod。lip 只安裝到服務端工作區; 客戶端工作區由 LeviLauncher 等啟動器管理。 - 文件包(
"type": "files"):按順序執行placements。
{
"formatVersion": 4,
"formatUuid": "0c6b977a-64ad-441f-9a29-c7f4b196882f",
"name": "PeEditor",
"type": "files",
"version": "3.9.0",
"rid": "win-x64",
"placements": [{ "src": "PeEditor.exe", "dest": "PeEditor.exe" }]
}寫入已歸其他已安裝包所有的文件時會報錯。
環境
工作區的環境是一對值 (platform, rid):
- platform:
server或client。lip 只管理服務端工作區,因此總是使用server。客戶端 工作區由實現同一規範的啟動器(如 LeviLauncher)管理;鎖文件記錄為client的工作區會被 lip 拒絕。 - rid:
--rid;否則取鎖文件中的值;否則取 lip 自身運行時的 RID(例如win-x64)。
舊格式:tooth.json
lip v0.x 的包使用 tooth.json(格式版本 1 到 3)。lip 仍然讀取它們,但不再寫入;
請使用 lip migrate 轉換你的包。
lip 在內存中把 tooth.json 轉換為第 4 版的文件包,並像其他包一樣處理它;確切的映射見
規範 §9:
- 歸檔同時含有
tooth.json和manifest.json時,以tooth.json為準。 - ref 的 variant 即變體標籤。當平臺為
client且 ref 不帶 variant 時,若存在標籤為client的變體,則使用它。具有該標籤且與 RID 匹配的各變體會像 lip v0.x 那樣合併。 - 無標籤變體的包身份是
tooth的值,帶標籤變體的包身份是tooth#label:與 lip v0.x 一樣,每個帶標籤的變體都是獨立的包,因此github.com/LiteLDev/LegacyScriptEngine與它 所依賴的#quickjs、#lua變體會並存安裝。 - 若標籤處的
tooth.json聲明的版本與標籤不同,以標籤版本為準,lip 會給出警告。 - 所選變體的依賴按
installDependencies的方式安裝,其版本範圍按 lip v0.x 的方式解釋。 - 每個資源都成為包的一部分:
self是tooth.json自身所在的歸檔(例如標籤處的倉庫),zip、tar、tgz和uncompressed資源在安裝包時下載(並緩存)。放置規則在安裝時與資源中的文件匹配(包括 glob),寫入的 文件與 lip v0.x 相同。 preserve_files、remove_files和腳本分別成為preserveFiles、removeFiles和scripts(install隨postInstall運行,uninstall隨preUninstall運行)。文件的 歸屬、保留與刪除遵循第 4 版的規則。- 鎖以
"kind": "tooth"記錄該tooth.json;卸載或更新該包時,lip 會再次轉換它。
格式 3 的 schema 為
manifest.schema.json
(格式 1 和 2:manifest.v1.schema.json、manifest.v2.schema.json):
{
"format_version": 3,
"format_uuid": "289f771f-2c9a-4d73-9f3f-8492495a924d",
"tooth": "github.com/user/repo",
"version": "1.0.0",
"info": { "name": "My Package", "description": "A short description" },
"variants": [
{
"label": "",
"platform": "win-x64",
"dependencies": { "github.com/other/package": ">=1.0.0" },
"assets": [
{
"type": "self",
"placements": [{ "type": "dir", "src": "bin/", "dest": "bin/" }]
}
],
"preserve_files": ["config/*.json"],
"remove_files": ["temp/"],
"scripts": { "post_install": ["echo installed"] }
}
]
}變體的 platform 是 .NET RID 模式,dependencies 將包路徑映射到
semver 範圍,assets 的類型為
self(倉庫中的文件)、zip、tar、tgz 或 uncompressed(從 urls 下載)。腳本為
pre_install、install/post_install、pre_uninstall/uninstall 和 post_uninstall。