跳到正文
本頁目錄

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 上的服務端和客戶端:

toml
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。此處未描述的鍵會被忽略。

頂層

字段類型必填說明
formatint是必須為 4。
idstring是包 ID,必須是合法的 Go 模塊路徑,例如 github.com/user/repo。比較時不區分 ASCII 大小寫。
versionstring是包的版本,不帶前導 v 的 SemVer 2.0.0 版本。
infoobject否供人閱讀的信息。參見 Info。
variantsobject是把每個變體名映射到其條目。參見變體。

對於 git 倉庫中的包,id 必須是該倉庫的路徑:https://github.com/user/repo.git 中的 lip.toml 必須寫 id = "github.com/user/repo"。在版本標籤 v<version> 處,version 必須 等於標籤版本。否則該提交不是包,lip 不會安裝它。

Info

字段類型說明
namestring顯示名稱。
descriptionstring簡短描述。
tagsstring[]標籤。參見推薦標籤。
logostring徽標圖片的 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。

條目

字段類型必填說明
sidestring是"server" 或 "client"。
platformstring是.NET RID,例如 win-x64。
depsobject否參見依賴。
assetsarray否參見資源。
preservestring[]否需保留文件的 glob:安裝時不覆蓋已存在的文件,卸載時也不刪除它。
purgestring[]否卸載時額外刪除的路徑的 glob。匹配到的目錄會連同其下所有內容一起刪除。
hooksobject否參見鉤子。

依賴

deps 把 <包 ID> 或 <包 ID>#<variant> 映射到版本範圍:

toml
[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:

類型字段說明
selfplace(可選)lip.toml 自身所在的歸檔,例如標籤處的倉庫。
zipurls、place(可選)通過 HTTPS 下載的 ZIP 歸檔。
mcmodurls通過 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 不得以 / 結尾。該文件必須存在。
toml
[[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_installstring[]寫入文件之前運行的命令。
post_installstring[]寫入文件之後運行的命令。
pre_uninstallstring[]刪除文件之前運行的命令。
post_uninstallstring[]刪除文件之後運行的命令。

命令在工作區根目錄中以 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}},以及你添加的任何其他頂層鍵。 變量保存的是字段的原樣內容,其中的模板不會先被渲染。

toml
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 為 dirsrc 和 dest 各自加上 /(為空時除外):目錄形式。
type 為 file,src 含 *、? 或 [src,以及加上 / 的 dest(為空時除外):glob 形式。
type 為 file,其他 srcsrc 和 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 在其包清單頁面 中對其做了說明:

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"] }
    }
  ]
}