kit.program/kit/doc/kit remote 仓库格式设计.md

367 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- 基于 webdav 协议访问。包括**远程仓库存储方案**(基于 WebDAV 协议)与**本地客户端存储方案**。
- 支持存储多种操作系统Linux、Windows、多种架构X86、aarch64
- 支持记录官网地址,方便定期从官网拉取新版本存储。
---
## Part A远程仓库存储方案基于 WebDAV
### 1. 概述
本方案定义了一种基于 WebDAV 协议访问的远程仓库存储格式,用于存储开源项目的目标代码(二进制分发文件)。仓库支持:
- 多种操作系统Linux、Windows、macOS、FreeBSD 等)
- 多种硬件架构x86_64、aarch64、armv7、i386 等)
- 平台无关的二进制文件(如 Java JAR、Python 轮包、纯 JS 文件等)
- 单文件可执行程序与多文件压缩包zip、tar.gz 等)
- 版本管理(语义化版本)与元数据记录
仓库通过 WebDAV 的目录和文件操作进行访问,支持枚举、下载、上传、删除等操作。
### 2. 仓库根目录结构
```
/repo/ # WebDAV 服务暴露的根路径
└─ {group}/ # 分组目录,默认 /modules空值等同 /
└─ {project-id}/ # 项目目录(唯一标识),实际路径为 group/id
├─ metadata.json # 项目级元数据文件
└─ {version}/ # 版本目录(如 1.0、1.1、2.0
├─ version.json # 版本级元数据文件(推荐)
├─ any/any/ # 平台无关文件目录(保留字)
│ └─ ... # 单文件或压缩包
├─ {os}/ # 操作系统(如 linux, windows, darwin
│ └─ {arch}/ # 架构(如 x86_64, aarch64
│ └─ ... # 具体二进制文件或压缩包
└─ ...
```
#### 2.1 命名规则
- `project-id`:来自项目 `meta.json` 中的唯一 `id`,小写字母、数字、连字符(-),长度 ≤ 64。
- `group`:来自项目 `meta.json`,默认 `/modules`;空字符串等同 `/`;支持 `/docker`、`/tmpls`、`/docker/net` 等子路径。
- `version`:来自项目 `meta.json``version`;若为空,发布时根据远程 `metadata.json.version` 尾数 +1远程不存在时初始为 `1.0`
- `os``arch` 使用标准化名称(见下表),`any` 为保留字,表示任意平台。
| 操作系统 | 标准名称 | 架构 | 标准名称 |
| ------- | --------- | ------- | --------- |
| Linux | `linux` | x86_64 | `x86_64` |
| Windows | `windows` | aarch64 | `aarch64` |
| macOS | `darwin` | armv7 | `armv7` |
| FreeBSD | `freebsd` | i386 | `i386` |
| 任意平台 | `any` | 任意架构 | `any` |
#### 2.2 目录说明
- **`any/any/`**:存放与操作系统和架构均无关的文件(如 JAR、纯 Python 脚本、WASM 模块)。客户端在任何平台均可直接使用此目录下的文件。
- **`{os}/{arch}/`**:存放特定平台、特定架构的二进制文件或压缩包。
- 同一版本下可同时包含 `any/any/` 和具体平台目录。
### 3. 元数据文件规范
#### 3.1 项目级元数据:`metadata.json`
位于每个项目根目录UTF-8 编码JSON 格式。
**字段说明**
| 字段名 | 类型 | 必选 | 说明 |
| `project_id` | string | 是 | 项目唯一标识,与目录名相同 |
| `group` | string | 否 | 仓库分组路径,默认 `/modules`,空值等同 `/` |
| `official_url` | string | 是 | 项目官网地址(用于定期拉取新版本) |
| `description` | string | 否 | 项目简述 |
| `version` | string | 是 | 当前远程最新版本号;不再使用 `versions` 数组 |
| `last_fetch_check` | string (ISO 8601) | 否 | 上次检查官网更新的时间戳 |
| `fetch_rules` | array of objects | 否 | 自动拉取时的匹配规则(见 3.3 |
| `extra` | object | 否 | 扩展字段 |
**示例**
```json
{
"project_id": "myapp",
"group": "/modules",
"official_url": "",
"description": "An example tool",
"version": "1.2",
"last_fetch_check": "2026-06-15T12:00:00Z",
"fetch_rules": [...],
"extra": { "license": "Apache-2.0" }
}
```
#### 3.2 版本级元数据:`version.json`
位于每个版本目录下,包含该版本的详细信息和文件清单。
**字段说明**
| 字段名 | 类型 | 必选 | 说明 |
| ----------------------- | ------------------ | ---- | ---------------------------------------------- |
| `version` | string | 是 | 版本号,与目录名一致 |
| `release_date` | string (ISO 8601) | 否 | 官方发布日期 |
| `official_download_url` | string | 否 | 官网的直接下载页或 API 地址 |
| `artifacts` | array of objects | 是 | 该版本包含的所有文件条目(见 3.2.1 |
| `checksums` | object | 否 | 文件路径到哈希值的映射(可选,与 artifacts 可共存) |
| `signatures` | object | 否 | 文件路径到数字签名的映射 |
##### 3.2.1 `artifacts` 数组中每个对象的字段
| 字段名 | 类型 | 必选 | 说明 |
| ----------------- | --------- | ---- | ------------------------------------------------------------ |
| `path` | string | 是 | 相对于版本目录的文件路径,如 `linux/x86_64/myapp``any/any/app.zip` |
| `type` | string | 是 | 文件类型:`executable`(单文件可执行)、`archive`(压缩包)、`library`(库文件)、`data`(资源数据) |
| `archive_format` | string | 否 | 当 `type="archive"` 时,取值为 `zip`、`tar.gz`、`tar.xz` 等 |
| `entry_point` | string | 否 | 当 `type="archive"` 时,指定压缩包内的可执行文件相对路径(如 `bin/app` |
| `unpack` | boolean | 否 | 是否建议客户端自动解压,默认 `false`。若为 `true`,客户端解压后应运行 `entry_point` |
| `checksum` | string | 否 | 该文件的哈希值(格式 `算法:值`,如 `sha256:abc...` |
**示例**
- **单文件可执行Linux**
```json
{
"version": "1.0.0",
"artifacts": [
{
"path": "linux/x86_64/myapp",
"type": "executable"
}
],
"checksums": {
"linux/x86_64/myapp": "sha256:e3b0c442..."
}
}
```
- **多文件压缩包Windows**
```json
{
"version": "2.0.0",
"artifacts": [
{
"path": "windows/x86_64/myapp.zip",
"type": "archive",
"archive_format": "zip",
"entry_point": "myapp/bin/myapp.exe",
"unpack": true
}
]
}
```
- **平台无关 JAR 文件**
```json
{
"version": "3.0.0",
"artifacts": [
{
"path": "any/any/myapp.jar",
"type": "executable"
}
]
}
```
#### 3.3 自动拉取规则(`fetch_rules`
`metadata.json` 中可定义 `fetch_rules` 数组,用于指导自动化工具从官网下载并分类存储。
每个规则对象包含:
| 字段名 | 类型 | 说明 |
| ----------------- | --------- | ------------------------------------------------------------ |
| `url_pattern` | string | 正则表达式,匹配下载 URL |
| `os` | string | 目标操作系统(`linux`、`windows`、`darwin`、`any` 等) |
| `arch` | string | 目标架构(`x86_64`、`aarch64`、`any` 等) |
| `type` | string | 文件类型(`executable` 或 `archive` |
| `archive_format` | string | 可选,当 `type="archive"` 时指定 |
| `entry_point` | string | 可选,压缩包内的入口点 |
| `unpack` | boolean | 可选,是否自动解压 |
**示例**
```json
"fetch_rules": [
{
"url_pattern": ".*-linux-amd64\\.tar\\.gz$",
"os": "linux",
"arch": "x86_64",
"type": "archive",
"archive_format": "tar.gz",
"entry_point": "myapp/bin/myapp",
"unpack": true
},
{
"url_pattern": ".*\\.jar$",
"os": "any",
"arch": "any",
"type": "executable"
}
]
```
### 4. 客户端访问方式
基于 WebDAV 协议,客户端可通过以下 HTTP 方法操作:
- `PROPFIND`列举项目、版本、os/arch 目录。
- `GET`:下载二进制文件或元数据文件。
- `PUT` / `MKCOL`:上传新版本或新文件(用于手动或自动填充)。
- `DELETE`:删除过期版本。
推荐 WebDAV 服务器配置:启用目录列表、支持 CORS、支持 HTTPS。
### 5. 自动化拉取新版本流程
外部工具(如 cron 作业)按以下步骤更新仓库:
1. 遍历仓库根目录下每个项目的 `metadata.json`
2. 访问 `official_url`,获取所有可用版本列表及最新版本。
3. 对比已存储版本,找出缺失的新版本。
4. 对于每个新版本,根据 `fetch_rules`(或内置逻辑)匹配下载 URL下载对应的二进制/压缩包。
5.`os` / `arch` / `any` 规则存放到 `{group}/{project-id}/{version}/{os}/{arch}/` 下。
6. 计算文件哈希,生成或更新该版本的 `version.json`
7. 更新项目级 `metadata.json``version`、`group`、`last_fetch_check`),不再写入 `versions`
---
## Part B本地客户端存储方案用于版本管理与回退
### 1. 概述
客户端从远程 WebDAV 仓库下载项目的目标代码后,需要在本地持久化存储,并支持:
- 多版本共存
- 当前激活版本的快速切换(符号链接)
- 版本回退(无需重新下载)
- 压缩包自动解压与入口点管理
- 元数据记录(安装历史、校验信息)
- 可配置的旧版本清理策略
### 2. 本地根目录结构
客户端约定一个根目录(如 `~/.cache/artifact-repo/``~/.local/share/artifact-repo/`),其下按项目组织:
```
~/.local/share/artifact-repo/
└─ {project-id}/ # 项目目录(与远程 project-id 一致)
├─ .meta/ # 元数据目录(隐藏)
│ ├─ install.json # 本地安装记录
│ └─ history/ # 可选,操作历史 JSON 文件
├─ versions/ # 所有已下载的版本
│ ├─ {semver}/ # 版本目录(如 1.2.0
│ │ ├─ .version.json # 从远程复制的 version.json附加本地信息
│ │ └─ {os}/{arch}/ # 与远程结构一致的实际文件
│ │ └─ ...
│ └─ {semver}/...
└─ current -> versions/{semver} # 符号链接,指向当前激活的版本
```
> **注意**:符号链接可放在项目根目录下,也可放在 `.meta/current`。推荐项目根目录下的 `current`,便于外部脚本直接引用(如 `$REPO_HOME/myapp/current/bin/myapp`)。
### 3. 元数据文件 `install.json`
位于 `.meta/install.json`,记录项目的本地安装状态。
**字段说明**
| 字段名 | 类型 | 说明 |
| -------------------- | ------------------ | ------------------------------------------------------------ |
| `project_id` | string | 项目标识 |
| `installed_versions` | array of strings | 已下载到本地的所有版本号 |
| `current_version` | string | 当前激活的版本号(应与 `current` 符号链接一致) |
| `last_updated` | string (ISO 8601) | 最后一次变更时间(安装、切换、删除) |
| `history` | array of objects | 操作历史记录,每个元素包含 `timestamp`, `action`, `version`, `from`(可选) |
| `options` | object | 客户端配置,如 `auto_cleanup`, `max_versions` 等 |
**示例**
```json
{
"project_id": "myapp",
"installed_versions": ["1.0.0", "1.2.0", "1.2.1"],
"current_version": "1.2.1",
"last_updated": "2026-06-15T14:30:00Z",
"history": [
{
"timestamp": "2026-06-10T09:00:00Z",
"action": "install",
"version": "1.0.0"
},
{
"timestamp": "2026-06-15T14:30:00Z",
"action": "switch",
"version": "1.2.1",
"from": "1.2.0"
}
],
"options": {
"auto_cleanup": true,
"max_versions": 5
}
}
```
### 4. 版本目录中的 `.version.json`
从远程仓库的 `version.json` 复制而来,并增加本地特有字段:
```json
{
"version": "1.2.1",
"installed_at": "2026-06-15T14:25:00Z",
"local_checksums": {
"linux/x86_64/myapp": "sha256:..."
},
... // 原始远程字段artifacts, release_date 等)
}
```
### 5. 核心操作流程
#### 5.1 安装新版本(更新)
1. 从远程 WebDAV 仓库下载指定版本的所有相关文件到 `versions/{new_version}/`,保持远程目录结构(包括 `any/any/` 和具体平台目录)。
2. 同时下载 `version.json` 并保存为 `.version.json`,添加 `installed_at` 时间戳。
3. 更新 `.meta/install.json`
-`new_version` 加入 `installed_versions`(若未存在)。
- 设置 `current_version``new_version`(若需要立即激活)。
- 添加历史记录(`action: "install"` 或 `"switch"`)。
4. 更新项目根目录下的 `current` 符号链接指向 `versions/{new_version}`
5. 如果 `artifacts` 中有 `type="archive"``unpack=true` 的条目,则解压到版本目录下的特定子目录(如 `extracted/`),并在 `.version.json` 中记录解压后的入口点路径。
6. 根据 `options` 中的清理策略删除旧版本(保留最近 N 个版本或保留指定天数)。
#### 5.2 回退到已安装的旧版本
1. 确认目标旧版本存在于 `installed_versions` 中。
2. 修改 `current` 符号链接指向 `versions/{old_version}`
3. 更新 `.meta/install.json`
-`current_version` 改为 `old_version`
- 添加历史记录(`action: "rollback"` 或 `"switch"`,可记录 `from` 原版本)。
4. 无需重新下载任何文件。
#### 5.3 卸载特定版本
1. 如果卸载的是当前激活版本,先切换到另一个已安装版本(或提示用户)。
2. 删除 `versions/{version}` 整个目录。
3.`install.json``installed_versions` 数组中移除该版本。
4. 添加历史记录(`action: "uninstall"`)。
#### 5.4 清理旧版本(自动或手动)
清理策略示例:
- 保留最近 `max_versions` 个版本(不计当前版本)。
- 或删除 `keep_recent_days` 天之前安装且不是当前版本的版本。
- 每次安装新版本后自动触发清理。
### 6. 处理压缩包archive的特别说明
当远程 `artifact``unpack=true` 时:
- 客户端下载压缩包到 `versions/{version}/{path}`
- 立即解压到同目录下的 `_extracted/` 子目录(例如 `versions/1.2.1/linux/x86_64/_extracted/`)。
- 根据 `entry_point``.version.json` 中记录解压后的可执行文件绝对路径(或相对于版本目录的路径)。
- 符号链接 `current` 可以直接指向解压后的入口点文件,而不是整个版本目录。实现方式:
```
current/bin/myapp -> ../versions/1.2.1/_extracted/bin/myapp
```
这样切换版本时只需更新符号链接,无需重新解压。
### 7. 多平台兼容性
客户端在下载时,应根据当前运行的操作系统和架构,仅从远程仓库获取匹配的文件:
- 优先尝试 `{os}/{arch}/`
- 若不存在,回退到 `any/any/`
- 下载后保持远程的相对路径结构,但客户端实际运行时只使用与当前平台相关的文件。
在本地存储中,所有版本目录仍保留完整的 `{os}/{arch}/``any/any/` 结构,便于在不同平台间迁移或共享缓存。