lip.toml
lip 包是根部帶有包文件 lip.toml 的歸檔。lip.toml 給出包的名稱和版本,並針對每個變體、運行端和平臺
描述 lip 要安裝的內容:依賴、要放置的文件、要安裝的 mod 以及要運行的命令。
規範性定義見協議的 lip.toml(5) 頁。
lip.toml 是格式 4 的 TOML 1.0 文件,由以 TOML 編寫的 JSON Schema
lip.schema.toml
描述。lip 也可讀取 lip v0.x 包的 tooth.json(格式 1 到 3),參見早期格式。
歸檔
包位於一個歸檔中,歸檔是一棵根部帶有 lip.toml 的文件和目錄樹:
- git 歸檔:git 倉庫的一個提交,通常是某個版本標籤所指的提交。只有其中的普通文件和目錄屬於 該歸檔(不含符號鏈接和子模塊);
- ZIP 歸檔:一個 ZIP 文件,可通過 HTTPS 下載,也可位於你的磁盤上。一個文件是否為 ZIP 歸檔只看
內容,與文件名無關。其條目必須是普通文件或目錄,路徑以
/分隔,不得以/開頭,也不得包含..段或\; - 目錄歸檔:你磁盤上的一個目錄,以其內容作為歸檔。
示例
一個 mod,以附加到每個 GitHub Release 的 ZIP 文件發佈,支持 Windows x64 上的服務端和客戶端:
format = 4
id = "github.com/example/MyMod"
version = "1.0.0"
[info]
name = "My Mod"
description = "A short description"
tags = ["utility"]
[[variants.""]]
side = "server"
platform = "win-x64"
preserve = ["mods/MyMod/config/"]
[variants."".deps]
"github.com/LiteLDev/LeviLamina" = ">=1.0.0 <2.0.0"
"github.com/example/MyLib" = "^0.3.0"
[[variants."".assets]]
type = "mcmod"
urls = ["https://github.com/example/MyMod/releases/download/v{{version}}/MyMod-server-win-x64.zip"]
[[variants.""]]
side = "client"
platform = "win-x64"
preserve = ["mods/MyMod/config/"]
[variants."".deps]
"github.com/LiteLDev/LeviLamina" = ">=1.0.0 <2.0.0"
"github.com/example/MyLib" = "^0.3.0"
[[variants."".assets]]
type = "mcmod"
urls = ["https://github.com/example/MyMod/releases/download/v{{version}}/MyMod-client-win-x64.zip"]版本和版本範圍都是字符串,因此總要加引號。含有 ASCII 字母、數字、_ 和 - 以外字符的鍵也要加引號,例如 deps 中的包 id 或默認變體 "":[[variants.""]] 為默認變體添加一個條目。
字段
lip.toml 不得包含日期時間、日期、時間,或浮點數 inf、nan。此處未描述的鍵會被忽略。
頂層
| 字段 | 類型 | 必填 | 說明 |
|---|---|---|---|
format | int | 是 | 必須為 4。 |
id | string | 是 | 包 ID,必須是合法的 Go 模塊路徑,例如 github.com/user/repo。比較時不區分 ASCII 大小寫。 |
version | string | 是 | 包的版本,不帶前導 v 的 SemVer 2.0.0 版本。 |
info | object | 否 | 供人閱讀的信息。參見 Info。 |
variants | object | 是 | 把每個變體名映射到其條目。參見變體。 |
對於 git 倉庫中的包,id 必須是該倉庫的路徑:https://github.com/user/repo.git 中的
lip.toml 必須寫 id = "github.com/user/repo"。在版本標籤 v<version> 處,version 必須
等於標籤版本。否則該提交不是包,lip 不會安裝它。
Info
| 字段 | 類型 | 說明 |
|---|---|---|
name | string | 顯示名稱。 |
description | string | 簡短描述。 |
tags | string[] | 標籤。參見推薦標籤。 |
logo | string | 徽標圖片的 URL,必須以 https:// 開頭;其響應(直接或經重定向後)即為該圖片。 |
lip 不使用 info。
變體
variants 把每個變體名映射到一個非空的條目數組。變體 "" 是默認變體;其他名稱須匹配
^[a-z0-9_]+(/[a-z0-9_]+)?$,例如 lua 或 quickjs。用戶在來源後加上 #<variant> 來選擇變體,例如
lip install github.com/LiteLDev/LegacyScriptEngine#lua;不帶 # 的來源選擇 ""。
每個條目恰好適用於一個運行端和一個平臺。lip 解析包時,取所選變體中 side 和 platform
與工作區環境完全相同的那個條目。沒有通配符:支持多個平臺的包要為每個平臺各寫一個條目。
若該變體沒有這樣的條目,則該包不是該環境下的候選。同一變體中的任意兩個條目不得具有相同的
side 和 platform。
條目
| 字段 | 類型 | 必填 | 說明 |
|---|---|---|---|
side | string | 是 | "server" 或 "client"。 |
platform | string | 是 | .NET RID,例如 win-x64。 |
deps | object | 否 | 參見依賴。 |
assets | array | 否 | 參見資源。 |
preserve | string[] | 否 | 需保留文件的 glob:安裝時不覆蓋已存在的文件,卸載時也不刪除它。 |
purge | string[] | 否 | 卸載時額外刪除的路徑的 glob。匹配到的目錄會連同其下所有內容一起刪除。 |
hooks | object | 否 | 參見鉤子。 |
依賴
deps 把 <包 ID> 或 <包 ID>#<variant> 映射到版本範圍:
[variants."".deps]
"github.com/LiteLDev/LeviLamina" = ">=1.0.0 <2.0.0"
"github.com/LiteLDev/LegacyScriptEngine#lua" = "^0.9.0"對每個依賴,lip 從該倉庫的版本標籤中安裝一個版本在範圍內、且有同一環境條目的版本(及變體)。從 git
引用(例如 @branch:main)安裝的包,只要版本在範圍內,且該倉庫有 lip 可為該依賴安裝的版本,也能滿足對同一包
ID 和變體的依賴。
版本範圍採用 Semver for .NET 3.0.0 中
SemVersionRange.Parse 在默認選項下的語法和語義,例如 ^1.2.0、~1.2.0、>=1.0.0 <2.0.0、
1.2.* 或 >=1.0.0 || 0.9.0。不帶運算符的完整版本(如 1.2.3)只匹配該版本。
資源
assets 列出包的文件從哪裡來。每個資源都有一個 type:
| 類型 | 字段 | 說明 |
|---|---|---|
self | place(可選) | lip.toml 自身所在的歸檔,例如標籤處的倉庫。 |
zip | urls、place(可選) | 通過 HTTPS 下載的 ZIP 歸檔。 |
mcmod | urls | 通過 HTTPS 下載、包含一個 LeviLamina mod 的 ZIP 歸檔;它沒有 place。參見 Mod。 |
urls 是同一文件的 HTTPS URL 組成的非空數組,按順序嘗試。一個條目最多有一個 mcmod 資源。資源按
順序寫入;兩個資源或放置規則寫入同一路徑時,以後者為準。
放置規則
place 的每個成員都是一條帶有 src 和 dest 的放置規則,把資源歸檔中的文件寫入工作區。src 是歸檔中的路徑,dest 是
相對於工作區的路徑。兩者都以 / 分隔,不得以 / 或盤符開頭,也不得包含 //、.. 段或 \。
放置規則的形式由 src 決定:
- glob 形式:
src含有*、?或[。所有與 glob 匹配的文件都寫到dest(""或以/結尾)之下,並保留其相對於src中開頭那些不含通配符的段的路徑。不匹配任何文件的 glob 不寫入任何 內容。 - 目錄形式:
src為""或以/結尾。src之下的所有文件都寫到dest(""或以/結尾) 之下,保留其相對於src的路徑。src之下必須至少有一個文件。 - 文件形式:其他情況。文件
src寫入dest,dest不得以/結尾。該文件必須存在。
[[variants."".assets]]
type = "zip"
urls = ["https://example.com/tool-1.0.0-win-x64.zip"]
place = [
{ src = "bin/", dest = "tools/" },
{ src = "README.md", dest = "tools/README.md" },
{ src = "plugins/*.dll", dest = "tools/plugins/" },
]包根部的 lip.toml 永遠不會被寫入。
glob
preserve、purge 以及 glob 形式的放置規則使用 POSIX fnmatch 模式,按段逐一匹配:
* 匹配除 / 以外的任意字符(包括開頭的 .),? 匹配除 / 以外的單個字符,[...] 匹配集合中的
單個字符。** 段匹配零個或多個段;以 / 結尾的模式匹配一個目錄及其下的所有內容。沒有轉義字符。
glob 不得為空、不得以 / 開頭,也不得包含 // 或 \。
preserve 和 purge 匹配的是相對於工作區的路徑,對 mod 也是如此:應寫
mods/MyMod/config/,而不是 config/。purge 永遠不會匹配工作區根目錄下的 mods
或 .lip,也不會匹配 .lip 之下的任何內容;mods 之下的路徑不會僅因 mods 匹配而匹配:
mods/ 和 */ 不會刪除 mods 之下的任何內容。
鉤子
| 字段 | 類型 | 說明 |
|---|---|---|
pre_install | string[] | 寫入文件之前運行的命令。 |
post_install | string[] | 寫入文件之後運行的命令。 |
pre_uninstall | string[] | 刪除文件之前運行的命令。 |
post_uninstall | string[] | 刪除文件之後運行的命令。 |
命令在工作區根目錄中以 lip 的環境變量運行:工作區環境的平臺為 Windows 平臺時以
cmd /s /c "<命令>" 運行,使 cmd 按原樣運行該命令,否則以 sh -c <命令> 運行,sh 通過 PATH 查找;
在 Windows 以外的系統上,Windows 平臺的命令通過 Wine 運行(參見
運行服務器),此時含有 " 的命令無法按原樣到達 cmd。以非零退出碼退出的命令會使操作失敗。操作失敗時,lip 會撤銷其文件改動,但不會撤銷鉤子命令已經做過的事,因此鉤子命令應當可以
安全地再次運行。
模板
與 lip v0.37.0 的 tooth.json 一樣,lip.toml 中除鍵以外的每個字符串都是
Scriban 語言的模板。lip 在讀取文件之前
先渲染每個模板,因此上文所述的一切,例如版本檢查以及 URL 和 glob 的校驗,都作用於渲染後的值。變量是按原樣書寫的
頂層字段:{{version}}、{{id}}、{{format}}、{{info.name}}、{{variants}},以及你添加的任何其他頂層鍵。
變量保存的是字段的原樣內容,其中的模板不會先被渲染。
version = "1.2.3"
[[variants."".assets]]
type = "mcmod"
urls = ["https://github.com/example/MyMod/releases/download/v{{version}}/MyMod-{{version}}.zip"]模板中可以使用任何 Scriban 表達式,例如 {{version | string.replace "." "_"}}。未定義的變量渲染為空。
無法解析或渲染失敗的模板(例如 {{ if }})會使該歸檔不是包。要原樣寫出 {{,請寫成 {{ "{{" }}。
lip pkg view 顯示渲染後的包文件,狀態文件中記錄的也是渲染後的包文件。
Mod
帶有 mcmod 資源的條目是 mod;否則該包是文件包,只放置文件。
mcmod 資源的歸檔根部必須有該 mod 的 LeviLamina manifest.json,它是一個 JSON 對象,其 name 須匹配
^[A-Za-z0-9][A-Za-z0-9._-]*$。這個 name 就是 mod 名稱。lip 把整個歸檔按原路徑寫入
mods/<mod 名称>/。
lip 只讀取 manifest.json 的 name,用來命名 mod 目錄。它不檢查也不修改其中的任何其他內容:
manifest.json 與其他文件一樣,按歸檔中的原樣寫入 mod 目錄。讓其 dependencies 與條目的
deps 保持一致由你自己負責。缺少 manifest.json,或其中沒有有效的 name,會在任何修改之前
使安裝失敗。
兩個 mod 名稱相同(不區分 ASCII 大小寫)的 mod 不能安裝在同一個工作區中。
條目在 mcmod 資源之外還可以有其他資源,例如一個把文件放到 mods/ 之外的 zip 資源。
身份
已安裝包的身份是其包 ID,當變體不為 "" 時再加上 #<variant>,例如
github.com/LiteLDev/LegacyScriptEngine#lua。一個工作區中每個身份至多有一個包;
lip list 顯示身份,lip uninstall 也接受
身份。
身份來自包的 id,而不是安裝來源:lip.toml 寫有 id = "github.com/user/repo" 的 ZIP 文件,
與來自該倉庫的包身份相同,因此兩者不能同時安裝。
環境
工作區的環境是一個二元組 (side, platform):
- 運行端(side):
server或client,即工作區是遊戲的哪一端。lip 只管理服務端工作區,因此始終使用server。客戶端工作區由 LeviLauncher 等啟動器管理;若工作區的狀態文件記錄的是client,lip 會拒絕處理。 - 平臺(platform):一個 .NET RID,始終為
win-x64, 無論 lip 運行在什麼系統上。在 Windows 以外的系統上,lip 通過 Wine 運行工作區的程序(參見 運行服務器)。Linux 版服務器不是另一個平臺,而是github.com/LiteLDev/bds的變體linux,其條目的平臺是win-x64。早期 lip 寫下的、記錄linux-x64的狀態文件會被當作win-x64的狀態文件讀取 (參見狀態文件)。以win開頭的平臺是 Windows 平臺。
早期格式
lip v0.39 的 lip.toml 同樣是格式 4,但把運行端寫作 platform、把平臺寫作 runtime,例如
platform = "server" 和 runtime = "win-x64"。lip 不讀取這樣的條目:含有 runtime、或 platform 為
"server" 或 "client" 的條目會使該歸檔不是包,並給出消息,提示把 platform 改名為 side、把
runtime 改名為 platform。把每個條目的這兩個鍵改名即可,其餘內容保持不變。
lip v0.37.0 及更早版本的包沒有 lip.toml,而是有格式版本 1、2 或 3 的 tooth.json。lip 仍會安裝它們,
但從不寫入它們;請使用 lip pkg migrate 把你的包轉換為 lip.toml。帶有
lip.toml 的歸檔從 lip.toml 讀取,不論它是否還有 tooth.json;其他格式的 tooth.json 不會被讀取。
格式 1 和 2 先像 lip v0.37.0 那樣讀取為格式 3。格式 3 的 tooth.json 隨後針對工作區的環境被完整地
轉換為一個包文件,lip 只使用轉換結果:
lip 解析、安裝並記錄到狀態文件中的都是它。安裝時不再保留任何格式 3 的語義。
tooth變為id。version為包的版本:在版本標籤處為標籤版本,而不論tooth.json的version寫的是 什麼;否則為該version。- 保留
info,但其avatar_url以https://開頭時變為logo,否則被丟棄。 - 對每個標籤,將該標籤下
platform為空或與環境的平臺匹配(tooth.json的platform是 RID 模式, 例如win-*)的變體,按 lip v0.37.0 的方式合併為一個對應該環境運行端和平臺的條目。 沒有這類變體的標籤不構成變體。 dependencies原樣變為deps。- 每個資源保留其
type和urls,其placements變為place(見下文)。類型為tar、tgz或uncompressed的資源在lip.toml中不存在,會使該標籤在該環境下沒有變體。 preserve_files變為preserve:\替換為/,不含/的模式加上前綴**/,因此config.json變為**/config.json。remove_files變為purge:\替換為/,不以/結尾的模式還會再加上一個以/結尾的形式,因此logs變為logs和logs/。- scripts 變為
hooks:pre_install仍為pre_install;install和隨後的post_install變為post_install;pre_uninstall和隨後的uninstall變為pre_uninstall;post_uninstall仍為post_uninstall。
放置規則 { "type": ..., "src": ..., "dest": ... } 在 src 和 dest 中的 \ 替換為 /、空段和 . 段被移除
後,變為 place 的一個成員:
| 放置規則 | place 成員 |
|---|---|
type 為 dir | src 和 dest 各自加上 /(為空時除外):目錄形式。 |
type 為 file,src 含 *、? 或 [ | src,以及加上 / 的 dest(為空時除外):glob 形式。 |
type 為 file,其他 src | src 和 dest:文件形式。 |
轉換後在包文件中無效的 glob 或放置規則會被略去。隨後放置規則按 lip.toml 的方式執行:例如,缺失的文件現在會使安裝
失敗,包的 tooth.json 也永遠不會被寫入。
早期格式的包始終是文件包。其身份遵循同樣的規則,例如 github.com/LiteLDev/LegacyScriptEngine#lua。
格式 3 的 schema 為
legacy/v3/tooth.schema.yaml
(格式 1 和 2:legacy/v1/tooth.schema.yaml、legacy/v2/tooth.schema.yaml),
lip v0.37.0 在其包清單頁面
中對其做了說明:
{
"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"] }
}
]
}