- 基于 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 服务暴露的根路径 └─ {project-id}/ # 项目目录(唯一标识) ├─ metadata.json # 项目级元数据文件 └─ {semver}/ # 版本目录(如 1.2.3 或 v1.2.3) ├─ version.json # 版本级元数据文件(推荐) ├─ any/any/ # 平台无关文件目录(保留字) │ └─ ... # 单文件或压缩包 ├─ {os}/ # 操作系统(如 linux, windows, darwin) │ └─ {arch}/ # 架构(如 x86_64, aarch64) │ └─ ... # 具体二进制文件或压缩包 └─ ... ``` #### 2.1 命名规则 - `project-id`:小写字母、数字、连字符(-),长度 ≤ 64。 - `semver`:符合 [Semantic Versioning 2.0]( 的版本号,推荐不含 `v` 前缀。 - `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 | 是 | 项目唯一标识,与目录名相同 | | `official_url` | string | 是 | 项目官网地址(用于定期拉取新版本) | | `description` | string | 否 | 项目简述 | | `latest_version` | string | 是 | 仓库中已存储的最新版本号 | | `versions` | array of strings | 是 | 仓库中已存储的所有版本号列表 | | `last_fetch_check` | string (ISO 8601) | 否 | 上次检查官网更新的时间戳 | | `fetch_rules` | array of objects | 否 | 自动拉取时的匹配规则(见 3.3) | | `extra` | object | 否 | 扩展字段 | **示例**: ```json { "project_id": "myapp", "official_url": "", "description": "An example tool", "latest_version": "1.2.1", "versions": ["1.0.0", "1.2.0", "1.2.1"], "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` 规则存放到 `{project-id}/{version}/{os}/{arch}/` 下。 6. 计算文件哈希,生成或更新该版本的 `version.json`。 7. 更新项目级 `metadata.json`(`versions`、`latest_version`、`last_fetch_check`)。 --- ## 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/` 结构,便于在不同平台间迁移或共享缓存。