diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 23fabdd..fa5be7a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -54,15 +54,23 @@ jobs: - name: Verify archives (checksum and plugin.json) run: node scripts/validate-index.mjs lithe-plugin-index.json --verify-archives ${{ steps.base.outputs.args }} - - name: Check the schema copy matches Lithe + - name: Check the index contract has not changed + # validate-index.mjs follows the index rules of the Lithe plugin + # contract, published in lithe-plugin-docs. A different schema there + # means the rules changed; see docs/maintainers.md. + env: + SCHEMA_URL: https://raw.githubusercontent.com/Lithe-IDEA/lithe-plugin-docs/main/contract/v1/plugin-index.schema.json + SCHEMA_SHA256: 6771cb0e8c1ba101e6fc90b436de1b74f057ff0d58cc90f92e669edc9a102fc6 run: | - url="https://raw.githubusercontent.com/1lck/Lithe-IDEA/preview/shared/contracts/plugins/v1/plugin-index.schema.json" - status=$(curl -sS -o "$RUNNER_TEMP/upstream-schema.json" -w '%{http_code}' "$url") + status=$(curl -sS -o "$RUNNER_TEMP/plugin-index.schema.json" -w '%{http_code}' "$SCHEMA_URL") if [ "$status" = "404" ]; then - echo "::notice::Lithe preview does not publish plugin-index.schema.json yet; skipping drift check." + echo "::notice::lithe-plugin-docs does not publish plugin-index.schema.json yet; skipping contract check." elif [ "$status" != "200" ]; then - echo "::warning::Could not fetch the upstream schema (HTTP $status)." - elif ! diff -u "$RUNNER_TEMP/upstream-schema.json" schema/plugin-index.schema.json; then - echo "::error::schema/plugin-index.schema.json differs from Lithe preview. Update the copy, the fixtures in scripts/fixtures and validate-index.mjs together." - exit 1 + echo "::warning::Could not fetch the published schema (HTTP $status)." + else + actual=$(sha256sum "$RUNNER_TEMP/plugin-index.schema.json" | cut -d' ' -f1) + if [ "$actual" != "$SCHEMA_SHA256" ]; then + echo "::error::The published plugin-index.schema.json changed (SHA-256 $actual). Update validate-index.mjs, scripts/fixtures and SCHEMA_SHA256 together; see docs/maintainers.md." + exit 1 + fi fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 359c5be..422a1af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,9 +17,8 @@ - 插件图标填在条目的 `icon` 字段,指向插件仓库内固定到提交的 SVG/PNG(`raw.githubusercontent.com////…`);没有填写时 README 使用默认图标。 - 插件的 bug 与功能请求请提到插件自己的仓库,本仓库只处理收录相关事务。 -第一次开发语言插件,先读[LSP 插件完整生命周期](docs/lsp-plugin-lifecycle.md), -再按[构建、运行与测试接入指南](docs/build-tool-integration.md)扩展能力, -最后按[发布文档](docs/publishing.md)提交收录。 +怎么写插件(开发指南、LSP 与构建工具教程、插件契约和 SDK)见 +[lithe-plugin-docs](https://github.com/Lithe-IDEA/lithe-plugin-docs);写好以后按[发布文档](docs/publishing.md)提交收录。 审查标准见 [docs/maintainers.md](docs/maintainers.md#审查清单)。 @@ -48,21 +47,19 @@ Lithe 会把仓库地址 `https://github.com/Lithe-IDEA/Lithe-store` 解析为 | 文档 | 内容 | | --- | --- | -| [LSP 插件完整生命周期](docs/lsp-plugin-lifecycle.md) | 从 Lua 模板开始:清单、服务器打包、激活、状态目录、停止、升级与卸载 | -| [构建、运行与测试接入](docs/build-tool-integration.md) | 可用的执行提供者示例、工程后端边界,以及开发中的 API 3 状态 | +| [lithe-plugin-docs](https://github.com/Lithe-IDEA/lithe-plugin-docs) | 插件开发:开发指南、LSP 与构建工具教程、插件契约与 JSON Schema、SDK 下载 | | [docs/publishing.md](docs/publishing.md) | 插件作者:如何打包、发布 Release、提交收录和发布新版本 | | [docs/versioning-and-updates.md](docs/versioning-and-updates.md) | 版本号如何追踪、Lithe 如何选择可安装版本、用户如何更新/回退 | -| [docs/maintainers.md](docs/maintainers.md) | 维护者:审查清单、下架与撤回、同步 Lithe 契约 | +| [docs/maintainers.md](docs/maintainers.md) | 维护者:审查清单、下架与撤回、跟进插件契约变化 | ## 仓库结构 ```text lithe-plugin-index.json # 唯一的数据文件,Lithe 客户端读取它 -schema/plugin-index.schema.json # 索引 JSON Schema(Lithe 契约 v1 的副本) scripts/validate-index.mjs # 与 Lithe Rust Core 同规则的校验 + 下载包校验 scripts/track-releases.mjs # 追踪已收录插件的新 Release 并生成版本条目 scripts/render-readme.mjs # 根据索引生成 README 的插件总览 -scripts/store.test.mjs # 校验脚本的测试(fixtures 复制自 Lithe 主仓库) +scripts/store.test.mjs # 校验脚本的测试(fixtures 复制自插件契约) assets/ # README 用到的图片(无图标插件的默认图标) .github/workflows/ # validate(PR/推送/每周)、track-releases(每 6 小时)、sync-readme(main 更新后同步 README) ``` diff --git a/README.md b/README.md index 8223e87..a80b6ab 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@

🧩 浏览插件 · ⬇️ 安装方式 · + 📚 开发插件 · 📤 发布插件 · 🤝 参与贡献

@@ -91,7 +92,7 @@ ## 🤝 参与贡献 -想收录自己的插件、发布新版本或报告有问题的插件,请看 [CONTRIBUTING.md](CONTRIBUTING.md)。 +想写插件,先看 [lithe-plugin-docs](https://github.com/Lithe-IDEA/lithe-plugin-docs)。想收录自己的插件、发布新版本或报告有问题的插件,请看 [CONTRIBUTING.md](CONTRIBUTING.md)。 上方的插件总览由 `scripts/render-readme.mjs` 根据 [`lithe-plugin-index.json`](lithe-plugin-index.json) 生成,索引合并到 main 后由 CI 自动同步。 ## 📄 许可 diff --git a/docs/build-tool-integration.md b/docs/build-tool-integration.md deleted file mode 100644 index 39aa6d4..0000000 --- a/docs/build-tool-integration.md +++ /dev/null @@ -1,188 +0,0 @@ -# 为语言插件接入构建、运行与测试 - -LSP 服务器负责语言分析,构建工具负责项目导入、编译、依赖解析与产物。 -一个实用的语言插件应把这两条链路分别验证,再验证它们在保存、切换项目和停止时能配合工作。 -先完成[LSP 插件生命周期教程](lsp-plugin-lifecycle.md),再按本篇增加执行能力。 -本篇的可操作示例以 macOS 声明式插件宿主为范围;Windows 仍有独立扩展实现, -不能凭 Core 校验通过推断已有相同的运行/测试宿主能力。 - -## 1. 先选择当前能发布的接入方式 - -本文于 2026-10-17 对照 Lithe 主仓库及 Lua 模板核对。API 3 的构建系统接口仍在开发; -下面区分已支持的声明与 Core 中尚未形成产品闭环的接口。发布前还要核实目标宿主版本。 - -| 需求 | 当前接入方式 | 能做什么、边界在哪里 | -| --- | --- | --- | -| 补全、跳转、诊断 | API 1/2 `languageServers` | 按 LSP 提供能力,不自动声明编译和测试 | -| 运行文件或固定项目命令 | API 1/2 `executionProviders[].run` | 命令与 argv 模板,工作目录为工作区根;不提供通用目标选择或构建模型 | -| 运行整个工程、文件所在范围或指定测试 | API 1/2 `executionProviders[].test` | 测试项目标记、文件发现和命令规划;不是完整语义测试发现与结构化结果协议 | -| 官方 Java 项目的运行/测试/调试 | 官方 Java 插件的 API 2 `hostIntegration: jdt` | 宿主已有 JDT 集成,不能复制为社区通用扩展点 | -| 通用 Build 工具窗口、目标与依赖模型、增量同步 | API 3 `buildSystems`,开发中 | Core 验证/规划接口预览,尚不能作为市场可安装的社区构建插件发布 | - -当需求只是 `lua <当前文件>`、`go test ./...` 这类固定命令,可以使用现有执行提供者。 -当需求需要 Maven/Gradle/Cargo 的模块、profile、target、classpath、依赖树或任务动态发现, -应使用成熟构建后端和完整模型;不要把一个 `run` 命令包装成已经完成工程集成。 - -## 2. 给 Lua 模板加上运行能力 - -在生命周期教程的最小清单中,替换下面三个顶层字段,其他字段保留: - -```json -{ - "permissions": ["languageServer.launch", "process.run"], - "activationEvents": ["onLanguage:lua", "onRun:lua"], - "executionProviders": [ - { - "languageID": "lua", - "run": { - "executable": { "command": "lua" }, - "arguments": ["${file}", "${userArguments}"] - } - } - ] -} -``` - -这也是[Lua 模板](https://github.com/Lithe-IDEA/lithe-plugin-lua)实际采用的方式。 -用户需要另行安装 Lua 解释器;随包提供的 LuaLS 不等于解释器。 -`process.run` 声明运行权限,`onRun:lua` 声明按运行操作激活,二者都不能省略。 -测试声明同理需要 `process.test` 和 `onTest:`。 - -`executable` 只能选一个:裸 `command` 交给宿主工具路径发现,或使用宿主已经支持的 -项目 `toolchain` ID,例如 Go 的 `project-go`。插件不能发明一个 toolchain ID 就指望宿主实现。 - -所有参数都是独立 argv 元素,不经过 shell: - -| 模板 | 含义与范围 | -| --- | --- | -| `${file}` | 工作区相对路径;以 `-` 开头时宿主加 `./`,适用于运行、文件测试和用例测试 | -| `${fileDirectory}` | 文件所在目录,根目录为 `.`,其他为 `./…` | -| `${userArguments}` | 仅用于运行,必须独占一个元素,展开为用户参数数组 | -| `${testName}` | 指定测试用例名,去除首尾空白 | -| `${testName:regex}` | 将用例名的正则特殊字符转义,适合测试筛选参数 | - -不要在数组里写 `"lua ${file} && echo done"`、额外 shell 引号或依赖变量替换; -那只是一个普通参数。路径中的空格也不需要作者再加引号。API 1/2 执行提供者没有可自行 -设置的 `cwd`、`${projectDir}` 或通用构建设置字段,工作目录固定是工作区根。 - -## 3. 用 Go 说明三个测试范围 - -下面是一个 **Go 语言插件**的顶层字段片段。它来自现有 Go 提供者的命令结构; -需要与声明 `go` 语言及其服务器的清单合并,不能直接粘贴到 Lua 插件中使用。 - -```json -{ - "permissions": ["languageServer.launch", "process.run", "process.test"], - "activationEvents": ["onLanguage:go", "onRun:go", "onTest:go"], - "executionProviders": [ - { - "languageID": "go", - "run": { - "executable": { "toolchain": "project-go" }, - "arguments": ["run", "${file}", "${userArguments}"] - }, - "test": { - "frameworkID": "go", - "workspaceLabel": "All Go Tests", - "projectMarkers": ["go.mod", "go.work"], - "testFilePatterns": ["*_test.go"], - "executable": { "toolchain": "project-go" }, - "workspaceArguments": ["test", "./..."], - "fileArguments": ["test", "${fileDirectory}"], - "testCaseArguments": [ - "test", - { "value": "${fileDirectory}", "when": "fileSelected" }, - { "value": "./...", "when": "noFileSelected" }, - "-run", - "^${testName:regex}$" - ] - } - } - ] -} -``` - -- 工作区有任一 `projectMarkers` 才被识别为测试项目。`testFilePatterns` 是忽略大小写、 - 只支持 `*` 的文件名模式,不是正则或任意 glob。 -- 工作区范围运行 `go test ./...`;“文件”范围运行文件所在的包,符合 Go 的测试单位, - 不承诺只执行该文件。 -- 用例范围有选中文件时限定包,没有文件时使用 `./...`;引用 `${file}` 或 - `${fileDirectory}` 的用例参数必须用 `when: "fileSelected"` 保护。 -- 声明式发现返回工作区和匹配文件,不会仅凭上述字段解析任意框架的测试函数、 - 测试结果树或调试入口。要得到这些能力,需要对应后端及宿主消费支持。 - -这里的 `go run <文件>` 也只运行选中文件,不能替代需要多个源码文件的包运行。 -需求超出模板能力时,应如实说明边界,或等待完整构建模型接入。 - -## 4. 将项目事实留给成熟后端 - -先检查你选用的语言服务器或项目后端是否已经支持工程导入、构建命令和运行目标。 -例如 Java 的模块、classpath、测试/调试目标应使用相应 Java 后端给出的事实, -不要由插件另读构建文件再拼一套容易漂移的 classpath。其他语言也应复用其成熟工具链。 - -一个完整的工程工作流需要确认以下顺序: - -```text -保存文档 → 等待后端接收并同步工程变更 → 构建目标 → 检查构建结果 - → 取得本次构建后的运行路径/产物 → 启动所选入口 -``` - -构建失败时不能继续运行旧产物;项目切换后不能用旧模型启动新项目;取消后不能把迟到结果 -覆盖到新一次操作。上述职责需要真实后端与宿主配合,声明一条命令并不会自动完成。 - -构建工具正常解析项目依赖时,可能需要访问网络并写用户的工具缓存;这与启动插件时临时下载 -语言服务器不同。语言服务器和随包运行时应在打包阶段准备好。项目依赖、构建产物、 -Gradle daemon 状态、Maven 仓库或 Cargo target 等应由对应工具放到其用户缓存或工作区, -不能写入插件包或应用安装目录。每个后台进程都要有明确的停止与清理策略。 - -## 5. API 3 构建系统接口:目前能研究什么 - -**当前状态:开发中的 Core 接口,不是可发布的完整宿主功能。** -截至本次核对,已提交基线支持 API 1/2;开发工作区新增 API 3 的清单验证与纯规划模块。 -macOS/Windows 尚无通用 Build 工具窗口对它的完整消费链路,市场可安装版本选择也暂时跳过 -API 3。因此不要把自己的正式插件升到 API 3,再让用户通过安装市场获得尚未实现的能力。 - -如果你参与 Core/宿主开发,可以阅读开发版本中的 `buildSystems` 契约及 Cargo、Go、 -Gradle、Maven fixtures,并用下列问题检查设计。它们是接口评估项,不能替代产品验收: - -| 模型部分 | 需要回答的问题 | -| --- | --- | -| 检测 | 如何发现根工程、嵌套模块、wrapper,如何排除生成目录? | -| 工具与运行时 | wrapper/系统命令如何选择,所需 JDK/其他运行时如何发现和验证? | -| 选项 | profile、target、模式、平台等如何进入明确的参数与环境,而不是拼接 shell? | -| 工程模型 | 模块、目标、依赖和运行入口来自哪个成熟后端,如何解码与校验? | -| 任务 | 支持哪些范围、是否需要用户参数、是否执行项目代码或访问网络? | -| 同步 | 哪些配置变更触发重导入,如何取消旧请求并拒绝过期结果? | -| 测试与诊断 | 发现、执行、结果映射由谁提供,路径如何对应用户源码? | -| 生命周期 | 谁启动进程、监督超时、终止任务并清理状态,宿主是否已经消费这些计划? | - -Core 的计划接口本身不启动进程、不监视文件、不持久化选择,也不会显示界面。 -即使 fixture 能生成正确命令,宿主仍须实现执行、权限/确认、取消、状态、结果和工具窗口。 -不要把内部 dispatcher 命令写成社区插件可以调用的宿主脚本 API。 - -`languageServers[].hostIntegration` 的 JDT 集成与开发中的构建 `hostIntegration` 是不同接口, -都不是社区作者任意命名即可扩展的机制。现有 JDT 路径限官方插件; -不要复制官方 ID 或修改 `vendor` 来绕过限制。未来正式开放的能力以目标版本契约和宿主为准。 - -## 6. 验证完整工作流 - -打包与清单校验完成后,还应在目标平台、实际宿主中完成: - -1. 分别准备单文件、多文件、嵌套工程和带空格路径的项目,明确插件支持的范围。 -2. 保存修改后运行/测试,确认使用当前文件和目标;将源码改错,确认错误能被看到, - 完整构建集成应阻止继续启动旧产物。 -3. 覆盖工作区、文件和指定用例范围,核对实际命令、工作目录、输出与退出码。 -4. 缺少解释器、工具链版本不符、wrapper 无执行权限或依赖下载失败时,错误能定位到原因。 -5. 取消耗时任务,随后再运行;切换项目与关闭工作区,确认旧任务、锁与后端进程没有残留。 - 若宿主或工具尚不能做到,应报告限制,不能只在文档里宣称已经支持。 -6. 禁用/卸载语言插件后检查该语言的运行、测试与 LSP 都处于正确状态。 - 插件声明的语言由插件负责,不能假定缺失能力会回退到宿主内置实现。 -7. 清理验收启动的应用和辅助进程,再按[LSP 生命周期检查表](lsp-plugin-lifecycle.md#7-把停止失败和卸载也当作功能验收) - 验证重启、升级和卸载。 - -命令规划测试证明参数正确,进程冒烟验证证明工具能够启动,真实项目验收才能证明用户工作流 -成立。发布说明应明确已经验证的平台、宿主、语言服务器及构建工具版本。 - -相关入口:[生命周期教程](lsp-plugin-lifecycle.md) · [发布社区插件](publishing.md) · -[版本追踪](versioning-and-updates.md) · -[Lithe 插件契约](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/plugins/v1/README.md)。 diff --git a/docs/lsp-plugin-lifecycle.md b/docs/lsp-plugin-lifecycle.md deleted file mode 100644 index 3b346ed..0000000 --- a/docs/lsp-plugin-lifecycle.md +++ /dev/null @@ -1,385 +0,0 @@ -# 从零构建一个完整生命周期的 LSP 插件 - -这篇教程面向想给 Lithe 增加语言支持的插件作者。我们用可运行的 -[Lua Support 模板](https://github.com/Lithe-IDEA/lithe-plugin-lua)走完选型、清单、语言服务器打包、 -安装与激活、日常运行、停止、升级和卸载;构建、运行与测试的接入另见 -[构建工具接入指南](build-tool-integration.md)。发布步骤见[发布社区插件](publishing.md)。 - -本文的安装与验收路径以 **macOS 的社区插件市场(Lithe 0.6.1 起)**为基线。 -API 2 示例需要所用宿主明确支持 API 2;不要只凭应用版本号推断支持情况。 -索引能描述 Windows/Linux 的包,不代表对应宿主已经实现相同的插件安装与运行流程。 -Lua 模板目前只提供 macOS arm64、x64 两个包。 - -## 1. 先确定插件与宿主各自负责什么 - -LSP(Language Server Protocol,语言服务器协议)让编辑器通过独立进程获得补全、诊断、 -跳转等能力。Lithe 的声明式插件用 JSON 描述要启动的程序;宿主读取清单、发现运行时并管理 -进程及 LSP 会话。你不需要编写宿主内的 Swift/JavaScript 插件入口。 - -```text -开发与构建:固定上游版本 → 下载并校验 → 组装各平台插件包 → 发布 zip → 索引收录 -用户安装:下载 zip → 校验摘要与清单 → 确认程序及权限 → 安装并记录完整性摘要 -正常运行:启用 → 打开对应语言的文件 → 启动服务器 → 初始化 LSP → 编辑与诊断 -结束使用:禁用/关闭工作区 → 宿主停止会话和进程;更新/卸载 → 替换或移除包 -``` - -| 对象 | 插件作者负责 | Lithe 负责 | -| --- | --- | --- | -| 语言识别 | 文件扩展名、完整文件名、项目标记 | 匹配文件并选择提供者 | -| 语言服务器 | 上游选型、许可、固定版本、启动入口、正确参数 | 校验声明、按需启动、LSP 通信与停止 | -| 运行时 | 声明依赖,或在允许的 API 中随包提供 | 按声明发现、检查并启动运行时 | -| 运行与测试 | 声明命令、参数和测试范围 | 生成计划、执行与展示结果 | -| 可写数据 | 指定上游使用的日志、索引和状态路径 | 为 API 2 状态模板提供平台可写目录 | -| 发布与维护 | 各平台产物、版本升级、兼容与回退验证 | 下载校验、安装审查、插件启停 | - -先选成熟且许可允许分发的语言服务器。项目导入、依赖解析、符号和模块模型应尽量由该语言的 -成熟后端提供。不要为了“接上 LSP”再复制一套工程模型。LSP 支持的具体功能仍取决于服务器 -能力与宿主支持;安装服务器不会自动带来构建、测试、DAP 调试或自定义界面。 - -## 2. 跑通原版模板,再改成自己的插件 - -本地需要 Git、Node.js 22+、`tar`、`zip` 和 `unzip`。打包工具使用 Node.js, -不代表目标语言服务器需要 Node.js;LuaLS 是平台相关的原生程序。 - -```bash -git clone https://github.com/Lithe-IDEA/lithe-plugin-lua.git -cd lithe-plugin-lua -node scripts/package.mjs -unzip -l dist/lua-support-0.3.0-darwin-arm64.zip -shasum -a 256 dist/lua-support-0.3.0-darwin-arm64.zip -``` - -这里的 `0.3.0` 对应本教程使用的模板版本;上游更新后,产物名以模板清单和脚本输出为准。 -脚本会重新生成 `dist/`,下载两个 macOS 架构的 LuaLS、核对固定 SHA-256,并输出每个 zip -的 SHA-256。Linux CI 可以组装 macOS 包,但这只证明打包成功,不能替代在目标 Mac 上运行。 - -模板源码与产物的关系: - -```text -源码仓库 -├── io.github.lithe-idea.lua-support/ -│ ├── plugin.json -│ └── bin/lithe-lua-language-server # 保留可执行权限的启动器 -├── language-server.json # 构建输入:版本、各平台 URL 与 SHA-256 -├── scripts/package.mjs # 下载、校验、解包、组装 zip -└── .github/workflows/ # PR 校验与标签发布 - -产物 lua-support-0.3.0-darwin-arm64.zip -└── io.github.lithe-idea.lua-support/ - ├── plugin.json - ├── bin/lithe-lua-language-server - └── language-server/ # 完整上游资源,不只是可执行文件 - ├── bin/lua-language-server - └── … # 上游依赖、脚本和许可文件 -``` - -派生自己的仓库后,同步修改以下位置: - -1. 包目录名、`plugin.json` 的 `id`、显示名、版本与 `vendor`,使用你自己的稳定反向域名。 -2. `scripts/package.mjs` 的 `PACKAGE_DIR`、`ASSET_PREFIX`,以及与新服务器对应的资源检查。 -3. `language-server.json` 的版本、各平台 URL、摘要和许可信息。它是模板脚本自己的输入, - 不是宿主自动解释的统一下载 API;更换清单结构时也要修改打包脚本。 -4. 启动器中的包内入口、参数和缓存命名空间。不能继续写入 Lua 模板的缓存目录。 -5. CI 中读取清单的目录名、Release 说明、README、许可证与第三方声明。 - -不要只换包目录名:模板 CI 中也有原插件 ID,启动器中也有状态路径。 - -## 3. 写一个最小、完整的 LSP 清单 - -下面是 API 1 的纯 LSP 示例。它沿用模板的目录布局,先只接入 LuaLS;如要运行 Lua 程序, -再按[构建工具指南](build-tool-integration.md)添加运行提供者。示例中的作者和 ID 要换成自己的。 - -```json -{ - "schemaVersion": 2, - "id": "io.github.alice.lua-support", - "displayName": "Lua Support", - "version": "0.1.0", - "pluginAPIVersion": 1, - "vendor": { - "id": "io.github.alice", - "displayName": "Alice", - "signatureRequirement": "none" - }, - "permissions": ["languageServer.launch"], - "activationEvents": ["onLanguage:lua"], - "languages": [ - { - "id": "lua", - "displayName": "Lua", - "fileExtensions": ["lua"], - "projectFileNames": [".luarc.json", ".luarc.jsonc"] - } - ], - "languageServers": [ - { - "languageID": "lua", - "languageIdentifier": "lua", - "launch": { - "kind": "packaged", - "path": "bin/lithe-lua-language-server" - }, - "arguments": [], - "validationArguments": [] - } - ] -} -``` - -三个版本不要混淆:`schemaVersion: 2` 是清单格式,`pluginAPIVersion: 1` 是宿主需要支持的 -插件 API 等级,`version: "0.1.0"` 是你的插件版本。上游 LuaLS 的版本另记在构建输入中。 -只声明实际需要的最低 API;API 1 的服务器参数是字面量,不展开 API 2 模板。 - -`languages[].id`、`languageServers[].languageID` 和 `onLanguage:` 必须对应。 -声明服务器需要 `languageServer.launch` 权限及激活事件;每个语言最多一个服务器。 -事件集合必须与实际提供者精确对应,不能随意增加未使用的事件。权限是能力声明, -不是限制外部程序磁盘或网络访问的操作系统沙箱。 -启用只表示允许激活,打开项目中相应语言的文档才触发服务器启动。 -文件后缀不等于 LSP 的文档标识,必要时单独设置 `languageIdentifier`。 - -### 选择启动方式 - -| 方式 | 适用场景 | 作者要做的事 | -| --- | --- | --- | -| API 1 `packaged` | 随包提供原生服务器或启动器 | 声明相对路径,打包全部资源并保留可执行权限 | -| API 1 `systemCommand` | 明确要求用户自行安装的服务器 | 声明裸命令候选和正确探测参数,记录用户安装要求 | -| API 2 `runtime` | 包内 JS/JAR 由 Node.js/Java 启动 | 声明包内入口及发现的运行时最低版本,或声明随包运行时 | - -例如,下面是 `languageServers[]` 中一个服务器声明的替代方案,要求用户预先安装 `gopls`, -完整清单还必须声明 `go` 语言及相应权限、事件: - -```json -{ - "languageID": "go", - "launch": { "kind": "systemCommand", "commands": ["gopls"] }, - "arguments": ["serve"], - "validationArguments": ["version"], - "runtimeDependencies": [{ "tool": "go" }] -} -``` - -探测参数必须能自行以退出码 0 结束,不能用会持续运行的 LSP 启动参数。若上游没有合适探测 -方式,可以像 Lua 模板一样用空数组,让 LSP 握手决定是否可用。通过探测仍不代表补全已经正常。 - -随包分发时优先使用固定、可复验的服务器。`systemCommand` 不会替用户安装工具,也不能作为 -启动时下载服务器的变通方案。运行时依赖与主入口是两回事:例如随包服务器调用 Go 时, -`runtimeDependencies` 让宿主发现 Go 并传递环境;API 1 支持 `node`、`go`,API 2 增加 `java`。 - -## 4. 在构建阶段准备完整语言服务器 - -沿用模板的处理顺序: - -1. 固定上游发行版本和每个平台/架构的 HTTPS 归档地址,核实许可证及再分发条件。 -2. 将归档 SHA-256 写入版本控制。摘要应通过上游校验资料或经过审查的下载确认, - 构建时不能现下载、现算一个摘要就当作预期值。 -3. 下载后先核对摘要,再解压到临时组装目录。缓存命中也必须重新校验;不匹配立即失败。 -4. 复制服务器所需的全部文件:二进制、脚本、资源、动态库、许可证等;检查声明入口存在。 -5. 检查归档没有符号链接、越界路径和开发机绝对路径,保留启动器与二进制的可执行权限。 -6. 按平台生成 zip,再计算最终 zip 的 SHA-256,交给市场索引固定。 - -上游归档摘要与最终 zip 摘要用途不同:前者保证构建输入,后者保证用户拿到同一个发布包。 -更换语言服务器时不能只替换下载地址,必须同步检查解压布局、入口、依赖、探测方式及许可。 -模板中的归档处理适配 LuaLS 的 tar.gz;其他格式需要在自己的打包脚本中明确实现。 - -社区插件使用自己的 ID 和 `vendor.signatureRequirement: "none"`,通过第三方市场源的 -摘要校验、用户确认和加载时内容复查安装。不要伪造 Lithe 发布者签名,也不要使用保留的 -`dev.lithe.*` 命名空间。官方包的发布者签名是另一条信任路径,签名必须在完整组装后完成。 - -## 5. 将可写状态与安装文件分开 - -Lithe 会在加载时复查社区包内容。服务器如果将日志、索引、锁或生成文件写回插件目录, -下次加载就可能失败;写入应用安装包还会破坏应用签名与增量更新基线。 - -| 数据 | 放在哪里 | 需要注意的生命周期 | -| --- | --- | --- | -| 服务器、运行时、默认配置、许可证 | 只读插件包 | 随插件版本安装和替换 | -| 日志、可重建缓存 | 平台用户缓存目录 | 设置容量/轮转策略,记录清理方法 | -| 项目索引、锁、项目相关状态 | 按工作区隔离的可写目录 | 不同项目、工作树不能共用同一份可变索引 | -| 用户源码与构建产物 | 用户工作区或构建工具指定目录 | 卸载插件不应误删用户工程 | - -API 1 的 Lua 模板通过启动器设置缓存目录,并使用 `exec` 将启动器替换为真实服务器。 -不要把服务器放到后台、`nohup` 或自行创建常驻守护进程,否则宿主难以完整停止它。 -标准输出只承载 LSP 的 JSON-RPC 帧,启动器提示与日志应写标准错误或日志文件。 - -模板中的自定义缓存路径属于启动器自己的约定,**不能假定宿主卸载时自动清理**。 -需要说明残留内容及安全删除方法。若自己的服务器维护工作区索引,还要增加稳定的工作区隔离, -不要直接复用模板的单一缓存目录保存所有项目索引。 - -### API 2:让宿主提供状态目录 - -目标宿主支持 API 2 时,可以将上面的 Lua 示例升到 `pluginAPIVersion: 2`, -把 `languageServers` 替换为以下值,直接运行包内 LuaLS 并传入宿主状态目录: - -```json -[ - { - "languageID": "lua", - "languageIdentifier": "lua", - "launch": { - "kind": "packaged", - "path": "language-server/bin/lua-language-server" - }, - "arguments": [ - "--logpath=${stateDir}", - "--metapath=${workspaceStateDir}" - ], - "validationArguments": [] - } -] -``` - -`${stateDir}` 是宿主为该插件服务器管理的可写状态目录,`${workspaceStateDir}` 进一步 -按工作区隔离。`${pluginDir}` 是只读包根路径;`${arch}` 的值是 `arm64` 或 `x86_64`, -与 Release 资产里的 `x64` 拼写不同。状态模板不能放进 `validationArguments`。 -不要将这些模板写进 API 1 的参数,宿主会按原样传给进程。 - -如果服务器需要 Node.js/Java,API 2 的 `runtime` 可免去启动器。以下是服务器声明中的 -`launch` 片段,入口要替换成自己的真实资源,服务器专用参数仍放在 `arguments`: - -```json -{ - "kind": "runtime", - "tool": "node", - "minimumVersion": "18.17", - "entry": "language-server/server.js" -} -``` - -宿主负责发现并检查运行时;不要再在 `runtimeDependencies` 中重复声明同一个启动工具。 -`runtime` 不声明 `validationArguments`,运行时探测由宿主执行。若改为 `packagedRuntime`, -该字段与 `minimumVersion` 互斥,插件要随包提供运行时、依赖和许可,并分别验证目标架构。 - -API 2 也支持 `initializationOptions` 和声明式 `settings`。只传上游真实支持的选项; -`${setting:}` 只能引用已声明设置,值必须符合类型和范围。设置是插件级、跨工作区共享, -修改后宿主重启相应服务器。详细格式见[正式契约](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/plugins/v1/README.md#plugin-settings-api-2)。 - -## 6. 本地验证、安装与激活 - -先运行模板现有 CI 中的两层检查:`node scripts/package.mjs` 组装产物,随后用 Lithe Store -的 `readPackageManifests` 和 `checkManifest` 检查包路径、ID、版本和社区信任声明。 -可参考模板的 [CI 工作流](https://github.com/Lithe-IDEA/lithe-plugin-lua/blob/main/.github/workflows/ci.yml)。 -这些检查不会启动服务器,也不等同于完整的 Core 清单语义校验。 -在插件仓库根目录可以按 CI 的方式本地检查产物;下面的相邻目录应使用一个尚未占用的名字: - -```bash -git clone --depth 1 https://github.com/Lithe-IDEA/Lithe-store.git ../lithe-store-validation -node --input-type=module <<'JS' -import { readdirSync, readFileSync } from "node:fs"; -import { readPackageManifests, checkManifest } from "../lithe-store-validation/scripts/validate-index.mjs"; -const manifest = JSON.parse(readFileSync("io.github.lithe-idea.lua-support/plugin.json", "utf8")); -for (const name of readdirSync("dist")) { - const { manifests, unsafe } = readPackageManifests("dist/" + name, manifest.id); - const problems = [...unsafe, ...checkManifest(manifests, { id: manifest.id }, manifest)]; - if (problems.length) throw new Error(name + ": " + problems.join("; ")); - console.log("validated " + name); -} -JS -``` - -派生模板后,把这里读取 `plugin.json` 的目录名也换成自己的 ID。 -如果错误来自跨引用、模板上下文或 API 等级,还需对照契约并通过目标宿主的安装校验。 - -要在正式版 Lithe 中验收社区包,使用[发布流程](publishing.md)在自己的公开 GitHub 仓库 -发布 zip,再准备自己仓库根目录的 `lithe-plugin-index.json`。索引格式与 Store 相同, -下载 URL 指向该仓库的 Release 资产,摘要固定最终 zip;不必先申请进入公共 Store。 -在 Lithe 的“管理插件仓库…”中加入你的仓库 URL,即可通过自己的源安装测试。 -不要把正式版的“从磁盘安装”当作绕过社区来源与确认流程的开发入口;`none` 社区包走市场源。 -开发宿主的本地包例外属于单独信任路径,不能替代社区安装验收。 - -安装前先禁用与你的插件声明同一语言的其他插件,避免提供者冲突。然后: - -1. 在插件市场选择你的源和版本,确认显示的程序、权限、来源都符合预期。 - 安装或更新后按界面提示**重启 Lithe**,让新版本进入本次启动的插件图。 -2. 打开一个小项目中的 `.lua` 文件。应当出现补全、悬停、跳转和诊断; - 不要只检查进程“已运行”。 -3. 保存修改,确认诊断更新;关闭并重开文件,确认没有重复服务器和重复诊断。 -4. 从 Dock 启动 Lithe 再试一次,避免依赖终端启动时偶然继承的 PATH。 -5. 若增加运行/测试,分别验证;LuaLS 已正常工作并不代表用户的 `lua` 已安装。 - -启动后的协议顺序也应能解释清楚:宿主发送 `initialize`,服务器返回能力后接收 -`initialized`,随后按协商能力通过 `didOpen`、`didChange`、`didSave`、`didClose` 同步文档, -补全等请求和诊断沿同一会话传输。正常停止先 `shutdown`,再 `exit`;初始化未完成或关闭 -超时时,宿主可以强停进程。当前 macOS 默认初始化/请求超时为 30 秒、关闭超时为 2 秒。 -取消请求通过 `$/cancelRequest` 通知服务器,服务器仍可能完成计算,不能将取消当作进程已退出。 - -可用这个文件进行最小交互验收: - -```lua ----@param name string -local function greet(name) - return "Hello, " .. name -end - -print(greet("Lithe")) -``` - -悬停查看 `greet` 的签名,跳到定义,再将调用参数改成数字,观察类型诊断。 -具体提示随 LuaLS 版本和项目配置变化,验收时应记录所用版本和配置。 - -## 7. 把停止、失败和卸载也当作功能验收 - -声明式插件没有让作者注册任意 `activate()` / `deactivate()` 回调的机制。 -宿主管理会话、进程和停用;作者负责让服务器保持前台运行、遵守 LSP 生命周期, -并把所有子进程和状态设计成可以停止、重启和隔离。当前 Core 的强停路径面向直接子进程, -不能假定会自动终止服务器任意派生的整棵进程树。下面是发布前需要执行的验收, -不是声明后即可免测的保证。 - -| 操作或故障 | 要检查的结果 | -| --- | --- | -| 仅安装/启用,不打开该语言 | 不因安装就提前启动语言服务器 | -| 编辑、保存、关闭/重开文件 | 文档同步和诊断正常,没有重复会话 | -| 快速切换两个项目/工作区 | 旧结果不串到新项目,索引与锁不互相占用 | -| 服务器异常退出或初始化失败 | 宿主能显示可定位的失败;恢复操作不会留下重复进程 | -| 缺少 Node.js/Go/Java 或版本不足 | 给出可操作的运行时错误,不在启动时自行下载 | -| 修改 API 2 设置 | 新会话使用新值,旧进程退出 | -| 禁用插件、关闭工作区、退出 Lithe | 相应会话与进程被停止,启动器/辅助子进程不残留 | -| 卸载与重新安装 | 卸载先停用,重启后删除包和宿主管理的插件状态;自定义缓存有明确说明 | -| 篡改安装包内一个文件后重新加载 | 内容复查拒绝加载,不能靠服务器自行修改清单“修复” | - -已在本次启动中注册的声明式插件可即时启用/禁用;本次启动后新装的插件要等重启。 -安装、更新、回滚及卸载的文件/版本转换按界面提示在下次启动完成, -卸载请求会先停用已注册插件。验收需要覆盖重启前和重启后的状态。 - -验收完成后关闭测试启动的应用、服务器和子进程。不要把关闭编辑器标签等同于卸载服务器, -也不要假定崩溃后一定会自动重试或所有平台都有相同恢复策略。发现生命周期问题时, -在插件仓库记录日志、宿主版本、包版本、架构和复现步骤;宿主进程管理问题再提交到 Lithe。 - -## 8. 发布、更新与回退演练 - -按[发布社区插件](publishing.md)递增插件版本、发布正式 Release 并首次提交收录。 -模板的 Release 工作流会校验 `v<版本>` 与清单一致;原版示例是 -`node scripts/package.mjs --expect-tag v0.3.0`,派生后使用自己的版本。 - -每次更新服务器或运行时,都要更新固定输入和许可,重建对应平台包,走完交互及生命周期验收。 -发布后的 zip 不可替换,同一版本不能重用;修复要发布新版本。新增可运行程序会触发新的 -安装确认,具体规则见[版本追踪与更新](versioning-and-updates.md)。 - -发布前至少演练一次“旧版 → 新版 → 恢复旧版逻辑”: - -- 保留旧版本资产和索引记录,确保旧宿主仍有兼容版本可装。 -- 新旧服务器使用不兼容索引格式时,隔离状态或提供安全重建;不能让旧版读损坏状态。 -- macOS 的回滚功能使用安装记录中保留的上一版本,重新验证该包并在重启后切换, - 不提供任意历史版本选择,也不会恢复服务器索引或用户数据。应实际演练该路径。 -- 当前版本有问题时,发布更高版本的修复包,必要时在新包里恢复上一个稳定服务器; - 同时申请撤回问题版本,不能只依赖用户机器恰好保留上一版本。 -- 卸载重装也要验证。源码、用户构建产物与自定义缓存的处理必须说明,避免误删用户数据。 - -## 9. 按失败阶段排查 - -| 现象 | 优先检查 | -| --- | --- | -| 市场看不到或不可安装 | 源地址、当前平台资产、`pluginAPIVersion` 是否被宿主支持 | -| `plugin.archive.checksumMismatch` | 是否替换过 Release 资产;索引固定的是否为最终 zip 摘要 | -| `plugin.manifest.invalid` | 未知字段、schema/API 混淆、ID/引用、API 2 字段误放到 API 1 | -| `plugin.permission.undeclared` / `plugin.activation.invalid` | 提供者、权限与事件是否一一对应 | -| `plugin.launch.missing` | 相对路径、大小写、打包完整性与可执行权限 | -| `plugin.runtime.missing` | 运行时发现、最低版本、Dock 启动环境 | -| 有进程但无法补全 | stdio 是否被日志污染、LSP 握手、文档语言标识与上游配置 | -| 用过一次后拒绝加载 | 服务器是否向包内写了日志、索引、锁或生成文件 | -| 禁用后仍有进程 | 启动器是否使用 `exec`,是否后台化或产生未清理辅助进程 | -| 能补全但无法构建/测试 | 是否实际声明并验证了独立的运行/测试或构建集成 | - -字段全集、API 等级和稳定错误码以 Lithe 的 -[插件契约](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/plugins/v1/README.md)及 -[清单 Schema](https://github.com/1lck/Lithe-IDEA/blob/preview/shared/contracts/plugins/v1/plugin-manifest.schema.json)为准。 -跟随 preview 阅读时要区分已发布能力与开发中的契约,发布前以目标宿主实际支持为准。 diff --git a/docs/maintainers.md b/docs/maintainers.md index 3fa5db7..a248adc 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -47,18 +47,20 @@ CI(`Validate index`)已经自动保证: 不要为了“修正”而修改已发布版本的 `downloadURL` 或 `sha256`:Lithe 以它们确认包的身份, 修改等于换包,只能通过新版本发布。 -## 同步 Lithe 契约 +## 跟进插件契约变化 -`schema/plugin-index.schema.json`、`scripts/fixtures/` 是 Lithe 主仓库 -`shared/contracts/plugins/v1/` 的副本,`scripts/validate-index.mjs` 实现与 -`rust/lithe-core/src/plugins/marketplace.rs` 相同的条目规则(本仓库更严格:Lithe -会忽略的无效条目在这里直接让 CI 失败)。 +`scripts/validate-index.mjs` 实现与 Lithe Core(`rust/lithe-core/src/plugins/marketplace.rs`) +相同的索引条目规则(本仓库更严格:Lithe 会忽略的无效条目在这里直接让 CI 失败); +`scripts/fixtures/` 复制自插件契约的 `fixtures/marketplace/`。插件契约的公开副本在 +[lithe-plugin-docs](https://github.com/Lithe-IDEA/lithe-plugin-docs) 的 `contract/v1/`,本仓库不再保存 Schema 副本。 -`Validate index` 会比较 schema 副本与 Lithe `preview` 分支;不一致时 CI 失败。此时: +`Validate index` 会检查 lithe-plugin-docs 上 `plugin-index.schema.json` 的 SHA-256 是否等于 +工作流里记录的值;不一致说明索引契约变了,CI 失败。此时: -1. 复制新的 `plugin-index.schema.json` 与 `fixtures/marketplace/{index,invalid-index-entries}.json`; -2. 按 Lithe 的 README 更新 `validate-index.mjs`; -3. 运行 `node --test scripts/store.test.mjs`,所有 fixture 用例必须通过。 +1. 对照 lithe-plugin-docs 的提交记录查看 Schema 和契约 README 改了什么; +2. 复制新的 `fixtures/marketplace/{index,invalid-index-entries}.json`,按契约 README 更新 `validate-index.mjs`; +3. 运行 `node --test scripts/store.test.mjs`,所有 fixture 用例必须通过; +4. 把 `.github/workflows/validate.yml` 里的 `SCHEMA_SHA256` 换成新 Schema 的 SHA-256。 如果契约升级到新的 `schemaVersion`,旧版 Lithe 会拒绝整个索引,需要先与主仓库商定迁移方案 (例如并行发布新文件名),不要直接改 `lithe-plugin-index.json` 的 `schemaVersion`。 diff --git a/docs/publishing.md b/docs/publishing.md index 5749877..d9f3d68 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -2,9 +2,10 @@ 这份文档面向插件作者:从打包到被 Lithe 用户看到的完整流程。 -还没有可安装的插件包时,先跟着[LSP 插件完整生命周期教程](lsp-plugin-lifecycle.md) +还没有可安装的插件包时,先跟着 lithe-plugin-docs 的 +[LSP 插件完整生命周期教程](https://github.com/Lithe-IDEA/lithe-plugin-docs/blob/main/guides/lsp-plugin-lifecycle.md) 完成清单、语言服务器构建和运行验收;需要接入命令、测试或构建后端时,阅读 -[构建、运行与测试接入指南](build-tool-integration.md)。 +[构建、运行与测试接入指南](https://github.com/Lithe-IDEA/lithe-plugin-docs/blob/main/guides/build-tool-integration.md)。 ```text 你的插件仓库 Lithe-store Lithe 用户 @@ -106,7 +107,7 @@ shasum -a 256 zig-support-2.0.0-darwin-arm64.zip } ``` - 字段说明见 [schema/plugin-index.schema.json](../schema/plugin-index.schema.json)。`homepage`、 + 字段说明见 [plugin-index.schema.json](https://github.com/Lithe-IDEA/lithe-plugin-docs/blob/main/contract/v1/plugin-index.schema.json)。`homepage`、 `localizedDescriptions`、`tags`、`icon` 可选;`icon` 只能是同一仓库的 raw 文件。 API 3 的版本项还必须在 `pluginAPIVersion` 旁加入与包内相同的能力表,例如: diff --git a/schema/plugin-index.schema.json b/schema/plugin-index.schema.json deleted file mode 100644 index 4d4f53a..0000000 --- a/schema/plugin-index.schema.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://lithe.dev/contracts/plugins/v1/plugin-index.schema.json", - "title": "Lithe plugin marketplace index (plugin contract v1)", - "description": "Shape of a marketplace source index. Semantic rules that JSON Schema cannot express (download URLs inside the entry's own repository, duplicate IDs and versions, the official namespace, platform and API filtering) are defined in README.md and enforced by plugins.marketplace.validateIndex.", - "type": "object", - "additionalProperties": false, - "required": ["schemaVersion", "plugins"], - "properties": { - "schemaVersion": { "const": 1 }, - "plugins": { "type": "array", "items": { "$ref": "#/$defs/plugin" } } - }, - "$defs": { - "https": { "type": "string", "pattern": "^https://" }, - "plugin": { - "type": "object", - "additionalProperties": false, - "required": ["id", "name", "author", "repository", "description", "license", "versions"], - "properties": { - "id": { "type": "string", "pattern": "^[a-z0-9.-]+$" }, - "name": { "type": "string", "minLength": 1 }, - "author": { "type": "string", "minLength": 1 }, - "repository": { "type": "string", "pattern": "^https://github\\.com/[A-Za-z0-9._-]+/[A-Za-z0-9._-]+/?$" }, - "homepage": { "$ref": "#/$defs/https" }, - "description": { "type": "string", "minLength": 1 }, - "localizedDescriptions": { - "description": "Translations of description keyed by language tag (for example zh-Hans). Hosts show the one matching the interface language and fall back to description.", - "type": "object", - "propertyNames": { "pattern": "^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$" }, - "additionalProperties": { "type": "string", "minLength": 1 } - }, - "tags": { "type": "array", "items": { "type": "string", "pattern": "^\\S+$" } }, - "license": { "type": "string", "minLength": 1 }, - "icon": { "type": "string", "pattern": "^https://raw\\.githubusercontent\\.com/" }, - "versions": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/version" } } - } - }, - "version": { - "type": "object", - "additionalProperties": false, - "required": ["version", "pluginAPIVersion", "releaseDate", "downloads"], - "if": { "properties": { "pluginAPIVersion": { "const": 3 } } }, - "then": { "required": ["requires"] }, - "properties": { - "version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" }, - "pluginAPIVersion": { "type": "integer", "minimum": 1 }, - "requires": { - "description": "API level 3: the package manifest's requires, including buildProviders: 1 when any buildSystems[].provider is present (own or shared language server), so hosts can tell before downloading whether they run every capability it needs. Capability IDs this contract does not define are allowed; such a version is never available.", - "type": "object", - "minProperties": 1, - "propertyNames": { "pattern": "^[a-z][A-Za-z0-9]{0,63}$" }, - "additionalProperties": { "type": "integer", "minimum": 1 } - }, - "releaseDate": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" }, - "downloads": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/download" } } - }, - "allOf": [ - { - "if": { "properties": { "pluginAPIVersion": { "maximum": 2 } } }, - "then": { "not": { "required": ["requires"] } } - } - ] - }, - "download": { - "type": "object", - "additionalProperties": false, - "required": ["platforms", "downloadURL", "sha256"], - "properties": { - "platforms": { "type": "array", "minItems": 1, "items": { "type": "string", "pattern": "^[a-z0-9]+-[a-z0-9]+$" } }, - "downloadURL": { "type": "string", "pattern": "^https://github\\.com/[^/]+/[^/]+/releases/download/[^/]+/[^/]+\\.zip$" }, - "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" } - } - } - } -} diff --git a/scripts/store.test.mjs b/scripts/store.test.mjs index 323a286..ab44917 100644 --- a/scripts/store.test.mjs +++ b/scripts/store.test.mjs @@ -9,7 +9,7 @@ import { END, START, categoryOf, renderOverview, renderReadme, systemsOf, trunca import { addVersion, platformsForAsset, versionFromRelease } from "./track-releases.mjs"; import { checkManifest, formatIndex, inspectArchive, validateEntry, validateIndex, verifyArchives } from "./validate-index.mjs"; -// Fixtures are copied from Lithe-IDEA shared/contracts/plugins/v1/fixtures/marketplace +// Fixtures are copied from the plugin contract (lithe-plugin-docs contract/v1/fixtures/marketplace) // so this validator reports the same code as Lithe's Rust Core for each case. const fixtureIndex = JSON.parse(readFileSync(new URL("./fixtures/index.json", import.meta.url))); const invalidCases = JSON.parse(readFileSync(new URL("./fixtures/invalid-index-entries.json", import.meta.url))).cases;