10 KiB
Drone 多平台打包与 IPFS 发布
当前 .drone.yml 不再构建或推送 Docker 镜像,也不依赖镜像仓库账号密码。流水线依次编译 linux/amd64、linux/arm64、windows/amd64、windows/arm64、darwin/amd64、darwin/arm64,为六个平台保留独立压缩包,逐个上传并请求 pin,再发布一个四文件的下载索引目录。
流水线保留原有头部和代理,不添加 type、platform 或顶层 trigger;源码改由 Drone 在步骤执行前默认克隆,不再维护自定义 clone 步骤。package 步骤内完成打包后直接执行 IPFS 上传,让最终下载地址直接显示在打包日志末尾,不再按构建事件跳过上传。Dockerfile 不参与此流水线,原有本地 Windows 单 EXE 打包入口保持不变。调整后的克隆与触发结果仍需在实际 Drone 环境验证。
流水线与平台包
编译先执行一次 go mod download,再串行构建六个平台。流水线和脚本默认均为 BUILD_JOBS=1(同一时间一个平台)、GO_BUILD_PARALLELISM=1(每平台 go build -p 1),默认 GOMAXPROCS=1。前两者分别允许 1–6、1–16,显式增加并发需观察容器内存。任何平台失败都会等待同批任务结束后退出,不生成成功元数据。不改流水线头部或触发条件,不增加 privileged/宿主机挂载;未配置跨构建缓存,新的临时工作区仍可能重新下载依赖。实际提速幅度需同一 runner 冷/热缓存对比,不能按并发数推定。
- 使用 Drone 默认克隆取得当前构建源码;不再启动 Alpine 容器安装 Git、写入
.netrc或手工补取和校验提交。 - 使用 Node 22.19.0 执行前端生产构建。
- 使用 Go 1.25.0、
CGO_ENABLED=0依次编译六个平台,内嵌前端页面及版本、完整提交 ID、构建时间。 - 执行发布脚本测试,并用 Linux amd64 程序在隔离临时数据库和端口上验证版本、启动、网页资源及登录,然后生成六个独立压缩包。
- 在同一
package步骤中,打包成功后直接上传 IPFS,并打印最终下载地址。所有进入此步骤且通过测试、验证和打包的构建都执行上传,包括手动构建、PR 和未设置事件变量的构建。上传失败会使package步骤失败,不打印成功下载汇总。按用户要求,令牌直接配置在 YAML 中,不依赖 Drone Secret。
| 系统 | 架构 | 文件名 |
|---|---|---|
| Linux | amd64 | linkport-<VERSION>-linux-amd64.tar.gz |
| Linux | arm64 | linkport-<VERSION>-linux-arm64.tar.gz |
| Windows | amd64 | linkport-<VERSION>-windows-amd64.zip |
| Windows | arm64 | linkport-<VERSION>-windows-arm64.zip |
| macOS | amd64 | linkport-<VERSION>-darwin-amd64.tar.gz |
| macOS | arm64 | linkport-<VERSION>-darwin-arm64.tar.gz |
二进制保存在 bin/build-cache/ci-release/<系统>-<架构>/;六个压缩包保存在 bin/releases/ci/。已有输出目录会使打包失败,不会自动清理之前的产物;Drone 应使用新的构建工作区。 恢复 Linux、Windows、macOS 的 amd64/arm64 全部六个目标,已有旧包不删除。
每包包含 linkport 或 linkport.exe、VERSION、BUILDINFO.json、二进制 SHA256SUMS.txt、README 和构建/测试文档。顶层另保留 release.json(当前平台清单、版本、提交及校验值)和 SHA256SUMS.txt(所有压缩包及清单的校验值)。不打包真实 data/、数据库、密钥、源码、node_modules 或仓库凭据。
tag 必须为 v<VERSION>,例如 VERSION=0.3.0 对应 v0.3.0;不匹配时失败。交叉编译不代表六个平台全部完成原生运行验证;CI 仅运行 Linux amd64 冒烟,不连接真实上游数据库或 SSH 服务。
IPFS 配置与下载
成功日志末尾以 === IPFS upload complete: download URLs === 集中列出下载总页、六个包和四份索引/清单的完整地址;每个 HTTP(S) URL 独占一行,便于复制。当前配置使用 https://ipfs.freeicu.top/ipfs/<CID>;原始 IPFS URI 放在此汇总之前。
package 的 IPFS_API 固定为 https://ipfs.freeicu.top,不再要求 ipfs_api Secret。
此节点要求认证。按用户要求,IPFS_TOKEN 直接配置在 YAML 中,不依赖 Drone Secret。仓库读者及 Git 历史读者可获取令牌,应限制访问并在泄露时轮换,不要在其他文档或日志中重复令牌。
上传日志在每次请求前显示包序号、总数和文件名,随后显示 add 接受结果;这只表示请求了 pin,并非独立确认 pin 状态。六包和索引全部成功后输出同源网关的 Download page 地址,以及六个平台包、四个索引/清单文件的 HTTP(S) 下载直链,保留原 ipfs:// URI。包直链使用独立包 CID,并附带 filename 参数保留文件名,不误拼到索引目录下。失败时不输出最终下载页或直链。API 所在域名需同时提供 /ipfs/ 网关,脚本不额外探测该地址。
上传只使用 /api/v0/add,显式发送 User-Agent: linkport-ci-release/1。保留代理环境和 TLS 证书校验;Bearer token 仅通过 curl stdin 配置传入,不进入命令行参数或日志,禁用隐式 curl 配置以免本机配置改变安全选项。
- 先核对全部六包、平台元数据与原始校验清单;校验失败不发起上传。
- 六个 archive 各发送一次 multipart 请求,均设置
pin=true、cid-version=1、wrap-with-directory=false,校验响应恰好包含对应文件 Name 和有效 CIDv1。每个包分别请求 pin,并保留各自 CID。 - 生成
ipfs-index.json,包含version、commit、buildTime和packages数组;每包包含os、arch、file、cid、sha256、uri,其中 URI 为ipfs://<包CID>。 - 生成安全转义的
index.html,列出六包及 SHA256。包下载链接使用绝对网关路径/ipfs/<包CID>?filename=<encodeURIComponent(file)>,不使用../;页面同时提供release.json、SHA256SUMS.txt和ipfs-index.json相对链接。 - 将
index.html、ipfs-index.json、原release.json和原SHA256SUMS.txt四个小文件一起上传,设置wrap-with-directory=true、pin=true、cid-version=1,验证四个文件 Name/CID 和索引目录 CID。完整发布共七个 add 请求(六包各一次,加一次四文件索引请求)。
生成文件及两份原清单的上传副本放在受检的系统临时目录,索引请求完成或失败后清理;原六包及两份清单始终保留、不改写。任何请求失败、响应缺项/重复/无效 CID 都使发布失败,不写最终成功回执。可输出已完成单包的 CID,便于诊断;部分成功不代表全部发布成功,也不会自动撤销已请求的 pin。已有最终回执时,在网络请求前拒绝覆盖,请保留旧回执并使用新的发布目录。
全部成功后写入 bin/releases/ci.ipfs.json:{ cid, files } 的 cid 是索引目录 CID,files 共十项。前六项为完整包索引条目,URI 是各自 ipfs://<包CID>;后四项为 { file, uri },URI 为 ipfs://<索引CID>/<文件名>。这是相对旧八文件同目录方案的发布格式变化,消费者不能再假设 <索引CID>/<包文件名> 存在;没有应用配置或数据库迁移。
纯函数 createIpfsIndex(manifest, archiveEntries) 从 scripts/ci-release.mjs 导出,返回 { index, html },不读写文件、不访问网络、不修改输入。manifest 为六平台 release.json 对象,脚本不接受临时单包或三包 manifest;archiveEntries 必须恰好包含六项 { file, cid, sha256 },函数校验名称、平台、校验值及 CID,再从 manifest 补齐平台信息。修改目标范围时需同步调整编译脚本和发布脚本的目标列表及测试。
**pin 与备份边界:**索引文本中的包 CID/URI 不是 IPFS DAG 链接。pin 索引目录只保留四个索引/清单文件,不会递归 pin 六个包;六个包依靠各自 add 的 pin=true 请求保留。迁移或备份必须保存并处理全部六个包 CID 和索引 CID(共七个 CID),不能只备份索引 CID。脚本确认 add 成功响应,不额外查询 pin 状态。节点 pin 不等于公共网关完整可用,也不等于备份;Drone 临时工作区可能被回收。
历史节点验证状态(2026-09-24,旧六平台)
以下保留旧六包方案的历史实测记录,不代表当前代码已完成真实上传验证;已有旧包不删除。
据当时的实测记录,/api/v0/add 不带认证返回 401,带认证空文件请求返回 400 file argument 'path' is required。/api/v0/version 使用应用 User-Agent 返回 200,而 Python-urllib 默认 User-Agent 被 Cloudflare 1010 拦截;此前的 403 不代表上传不可用。object/new、files/stat、dag/put 均返回 404,发布实现不依赖这些组合目录接口。
117MB 八文件整批上传连接中断,单个约 18MB 包上传返回 HTTP 200,因此改用小请求逐包发布;尚未确定 Cloudflare 的具体大小上限,也不认定中断原因已被证实。当前六个 archive 已逐个上传 HTTP 200,均请求 pin=true;/api/v0/pin/ls?arg=<本次CID> 返回 404,无法独立查询 pin 状态,不能声称已独立确认递归 pin。
该域名公共网关对六包的首 1024 字节 Range 请求均返回 206,内容与本地一致;完整下载曾中途 IncompleteRead,主代理正在分块校验,不能据此声称完整公网下载已验证。索引目录发布及最终真实 CID、完整下载校验结果由主代理补充。本次脚本测试使用本机 HTTP fixture;只读调用纯函数验证真实进度 JSON 可生成六包索引,不发起真实网络请求或修改 bin 产物。
网络与克隆
流水线不设置 HTTP/HTTPS 代理;软件源、Git、npm、Go 依赖和 IPFS API 均由构建环境直接访问。
源码由 Drone 默认克隆阶段直接取得,其网络、仓库凭据和构建提交检出由 Drone 负责;流水线不再关闭默认克隆,也不再自行处理 DRONE_NETRC_*、创建认证文件或补取提交。自定义克隆原有的 SHA-1/SHA-256 协商和提交一致性检查不再由仓库脚本单独保证。
宿主机 Docker daemon 在流水线启动前拉取工具镜像,也需能够直接访问镜像仓库。仓库配置不会修改宿主机网络设置。
没有构建记录时检查 Drone 仓库启用、Gitea webhook 和 runner;源码克隆失败时检查 Drone 默认克隆日志、仓库集成以及 runner 到 Git 服务的网络;已经生成包但发布失败时检查 package 日志中的上传部分、令牌配置和 Kubo API 访问权限。