16 KiB
- 基于 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 | 否 | 扩展字段 |
示例:
{
"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)
{
"version": "1.0.0",
"artifacts": [
{
"path": "linux/x86_64/myapp",
"type": "executable"
}
],
"checksums": {
"linux/x86_64/myapp": "sha256:e3b0c442..."
}
}
- 多文件压缩包(Windows)
{
"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 文件
{
"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 | 可选,是否自动解压 |
示例:
"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 作业)按以下步骤更新仓库:
- 遍历仓库根目录下每个项目的
metadata.json。 - 访问
official_url,获取所有可用版本列表及最新版本。 - 对比已存储版本,找出缺失的新版本。
- 对于每个新版本,根据
fetch_rules(或内置逻辑)匹配下载 URL,下载对应的二进制/压缩包。 - 按
os/arch/any规则存放到{group}/{project-id}/{version}/{os}/{arch}/下。 - 计算文件哈希,生成或更新该版本的
version.json。 - 更新项目级
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 等 |
示例:
{
"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 复制而来,并增加本地特有字段:
{
"version": "1.2.1",
"installed_at": "2026-06-15T14:25:00Z",
"local_checksums": {
"linux/x86_64/myapp": "sha256:..."
},
... // 原始远程字段(artifacts, release_date 等)
}
5. 核心操作流程
5.1 安装新版本(更新)
- 从远程 WebDAV 仓库下载指定版本的所有相关文件到
versions/{new_version}/,保持远程目录结构(包括any/any/和具体平台目录)。 - 同时下载
version.json并保存为.version.json,添加installed_at时间戳。 - 更新
.meta/install.json:- 将
new_version加入installed_versions(若未存在)。 - 设置
current_version为new_version(若需要立即激活)。 - 添加历史记录(
action: "install"或"switch")。
- 将
- 更新项目根目录下的
current符号链接指向versions/{new_version}。 - 如果
artifacts中有type="archive"且unpack=true的条目,则解压到版本目录下的特定子目录(如extracted/),并在.version.json中记录解压后的入口点路径。 - 根据
options中的清理策略删除旧版本(保留最近 N 个版本或保留指定天数)。
5.2 回退到已安装的旧版本
- 确认目标旧版本存在于
installed_versions中。 - 修改
current符号链接指向versions/{old_version}。 - 更新
.meta/install.json:- 将
current_version改为old_version。 - 添加历史记录(
action: "rollback"或"switch",可记录from原版本)。
- 将
- 无需重新下载任何文件。
5.3 卸载特定版本
- 如果卸载的是当前激活版本,先切换到另一个已安装版本(或提示用户)。
- 删除
versions/{version}整个目录。 - 从
install.json的installed_versions数组中移除该版本。 - 添加历史记录(
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/ 结构,便于在不同平台间迁移或共享缓存。