跳到正文
本頁目錄

鎖文件

lip 使用鎖文件來維護當前工作區狀態。該文件會精確記錄已安裝的包、具體版本、包的來源,以及寫入磁盤的文件。

位置

鎖文件保存在工作區之外、lip 的用戶數據目錄中,因此工作區裡只有各個包的文件:

平臺路徑
Windows%LOCALAPPDATA%\lip\workspaces\<hash>.json
Linux / macOS$XDG_DATA_HOME/lip/workspaces/<hash>.json(默認 ~/.local/share/lip/workspaces/<hash>.json)

<hash> 是工作區絕對路徑(不帶末尾分隔符;Windows 上路徑不區分大小寫,因此先轉為小寫)的 SHA-256,以小寫十六進制表示。鎖文件還會在 workspace 中記錄該路徑;若兩者不一致,lip 會報錯停止,而不會使用其他工作區的鎖。

由於鎖與路徑綁定,移動或重命名工作區目錄後,原來的鎖不會跟隨:lip 會把移動後的工作區視為未安裝任何包。

從工作區遷移

早期版本把鎖保存在工作區根目錄,即 lip_lock.json(格式 4)或 tooth_lock.json(格式 3)。當數據目錄中沒有某個工作區的鎖時,lip 會讀取 lip_lock.json,否則遷移 tooth_lock.json(參見下文)。下一次保存鎖時(安裝、卸載或更新包之後),lip 會把鎖寫入數據目錄,並從工作區刪除這兩個文件。

作用

鎖文件在 lip 中有幾個關鍵用途:

  1. 狀態追蹤:作為工作區當前安裝狀態的唯一真實來源。
  2. 可復現性:記錄每個已安裝包的確切歸檔。當 lip 再次需要某個包時(例如卸載它),會直接打開該歸檔並校驗其標識,而不是重新解析該包。
  3. 卸載清理:lip 會追蹤包寫入的每個文件,使 lip uninstall 能幹淨刪除相關文件,避免工作區出現“陳舊文件”。
  4. 依賴圖:區分用戶顯式安裝的包和作為依賴隱式安裝的包。這樣 lip uninstall 會一併移除不再被任何顯式安裝的包需要的依賴(除非指定了 --no-dependencies),lip list 也能分組顯示這兩類包。
  5. 環境:一旦確定了工作區的平臺和 RID,就記錄下來,使之後的命令使用相同的環境。

鎖文件在一次操作的所有包都安裝或卸載完成之後保存一次。操作失敗時,lip 會撤銷它對工作區的修改,鎖文件保持原樣,因此兩者始終一致(參見內部機制)。

結構

鎖文件是一個 JSON 文件,由 JSON Schema lock.v4.schema.json 描述:

json
{
  "formatVersion": 4,
  "formatUuid": "0c6b977a-64ad-441f-9a29-c7f4b196882f",
  "workspace": "C:\\bds",
  "platform": "server",
  "rid": "win-x64",
  "packages": [
    ...
  ]
}

字段

  • formatVersion:鎖文件格式版本號(當前為 4)。
  • formatUuid:用於確保文件類型正確的唯一標識。
  • workspace:鎖文件所屬工作區的絕對路徑。
  • platform:工作區的平臺。lip 寫入 "server";從 tooth_lock.json 遷移而來的鎖在 lip 保存之前為 null。
  • rid:工作區的 .NET RID。
  • packages:已安裝的包,按安裝順序排列。

包條目

  • name:包的身份:其 manifest.json 的 name,或 tooth.json 包的 tooth 路徑,帶標籤的變體在其後加 #<label>(例如 github.com/LiteLDev/LegacyScriptEngine#lua)。早期版本寫入的鎖對帶標籤的變體只記錄了 tooth 路徑;lip 讀取時會補上標籤。

  • version:已安裝的版本。

  • kind:manifest.json 包為 "mcmod",tooth.json 包為 "tooth"。兩者的安裝與卸載方式相同;kind 說明 manifest 中保存的是什麼。

  • explicit:

    • true:用戶顯式安裝(如 lip install <package>)。
    • false:作為其他包依賴被隱式安裝。
  • chain:包的解析鏈,每一跳一項,包含:

    • ref:該跳解析時的 ref,例如 github.com/user/repo、git 引用形式的 github.com/user/repo@branch:main、https:// URL、上一歸檔內的 ./sub/,或傳給 lip install 的本地文件的 file:/// URI;
    • artifact:在該跳選中的路由條目下標(該跳不是路由文檔時為 null);
    • commit(僅包路徑):該跳倉庫所在的提交:版本標籤的提交,或 git 引用解析到的提交。早期版本寫下的鎖中沒有此項。

    lip update 會重新請求第一跳:分支跟隨到新的頭,標籤重新解析,rev 保持不變。

  • archiveKey:最終歸檔的鍵,例如 github.com/user/repo@1.0.0;帶 git 引用的包路徑為 github.com/user/repo@rev:<commit>。

  • archive:最終歸檔的標識:

    • { "kind": "zip", "sha256": ..., "url": ... }:ZIP 歸檔。url 是下載它的 https:// URL、本地文件的 file:/// URI,或對嵌套在其他歸檔中的 ZIP 文件為 null;包是 ZIP 文件內的一個目錄時,會設置 path。
    • { "kind": "git", "repo": ..., "commit": ..., "path": ... }:git 倉庫的一個提交;倉庫根目錄的 path 為 ""。
    • null:從 tooth_lock.json 遷移而來的條目。

    lip 再次需要已安裝包的文件時,會打開該歸檔並校驗其標識:git 歸檔按記錄的提交獲取(即使它來自一個已經前進的分支),下載的或本地的 ZIP 文件必須仍具有記錄的 SHA-256。卸載則兩者都不需要。

  • files:該包擁有的文件,相對於工作區根目錄,以 / 分隔。服務端的 plugins 是指向 mods 的鏈接,因此 plugins/ 下的文件記為 mods/ 下的路徑;早期版本寫下的鎖中的 plugins/ 路徑會在加載時被改寫。

  • manifest:包的文檔(manifest.json;tooth 包為 tooth.json,lip 需要該包時會再次把它轉換為第 4 版),這樣 lip 無需再次獲取。

示例

json
{
  "name": "LegacyMoney",
  "version": "0.22.0",
  "kind": "mcmod",
  "explicit": true,
  "chain": [
    { "ref": "github.com/LiteLDev/LegacyMoney", "artifact": 0 },
    {
      "ref": "https://github.com/LiteLDev/LegacyMoney/releases/download/v0.22.0/LegacyMoney-server-win-x64.mcmod",
      "artifact": null
    }
  ],
  "archiveKey": "https://github.com/LiteLDev/LegacyMoney/releases/download/v0.22.0/LegacyMoney-server-win-x64.mcmod",
  "archive": {
    "kind": "zip",
    "sha256": "…",
    "url": "https://github.com/LiteLDev/LegacyMoney/releases/download/v0.22.0/LegacyMoney-server-win-x64.mcmod"
  },
  "files": [
    "mods/LegacyMoney/LegacyMoney.dll",
    "mods/LegacyMoney/manifest.json"
  ],
  "manifest": { "...": "..." }
}

舊格式:tooth_lock.json

lip v0.x 在工作區根目錄使用 tooth_lock.json(格式 3)。若數據目錄中沒有鎖、工作區中也沒有 lip_lock.json,但有 tooth_lock.json,lip 會在加載時遷移它,在下一次保存時把鎖寫入數據目錄並刪除 tooth_lock.json。遷移而來的條目類型為 "tooth",沒有 archive;當 lip 需要它們的文件時,會重新解析標籤 path@version。

舊格式由 workspace_state.schema.json 描述:

json
{
  "format_version": 3,
  "format_uuid": "289f771f-2c9a-4d73-9f3f-8492495a924d",
  "packages": [
    {
      "manifest": { "...": "the tooth.json" },
      "variant": "",
      "locked": true,
      "files": ["bin/example.exe"]
    }
  ]
}
  • manifest:包的完整 tooth.json。
  • variant:已安裝變體的標籤。
  • locked:包為顯式安裝時為 true。
  • files:包寫入的文件。