11 KiB
Drone 多平台打包与 IPFS 发布
当前 .drone.yml 不再构建或推送 Docker 镜像,也不依赖镜像仓库账号密码。流水线直接编译 linux/amd64、windows/amd64、darwin/arm64 三个目标的 程序,为每个平台保留独立压缩包,逐个上传并分别请求 pin,再发布一个四文件的下载索引目录。
以用户确认能触发的原始配置为基准,仅替换原镜像发布步骤;保留头部、代理和 clone,不添加 type、platform 或顶层 trigger。IPFS 上传步骤只在 push/tag 执行。Dockerfile 不参与此流水线,原有本地 Windows 单 EXE 打包入口保持不变。调整后的触发结果仍需在实际 Drone 环境验证。
流水线与平台包
编译先执行一次 go mod download,再按批次并行构建。默认 BUILD_JOBS=2(同时两个平台)、GO_BUILD_PARALLELISM=2(每平台 go build -p 2),默认 GOMAXPROCS 与后者一致。前两者分别允许 1–6、1–16;内存紧张时均设为 1,增加并发需观察容器内存。任何平台失败都会等待同批任务结束后退出,不生成成功元数据。不改流水线头部或触发条件,不增加 privileged/宿主机挂载;未配置跨构建缓存,新的临时工作区仍可能重新下载依赖。实际提速幅度需同一 runner 冷/热缓存对比,不能按并发数推定。
- 使用新版 Git 的
git clone --no-checkout自动识别 SHA-1/SHA-256 仓库,补取并严格检出DRONE_COMMIT_SHA。 - 使用 Node 22.19.0 执行前端生产构建。
- 使用 Go 1.25.0、
CGO_ENABLED=0依次交叉编译三个平台,内嵌前端页面及版本、完整提交 ID、构建时间。 - 执行发布脚本测试,并用 Linux amd64 程序在隔离临时数据库和端口上验证版本、启动、网页资源及登录,然后生成三个独立压缩包。
- push/tag 上传 IPFS;pull_request 只编译、验证和打包,不执行上传步骤。按用户要求,令牌直接配置在 YAML 中,不依赖 Drone Secret;仓库读者可以看到令牌,跳过上传步骤不代表令牌对 PR 隐藏。
| 系统 | 架构 | 文件名 |
|---|---|---|
| Windows | amd64 | serviceport-<VERSION>-windows-amd64.zip |
| Linux | amd64 | serviceport-<VERSION>-linux-amd64.tar.gz |
| macOS | arm64 | serviceport-<VERSION>-darwin-arm64.tar.gz |
二进制保存在 bin/build-cache/ci-release/<系统>-<架构>/;三个压缩包保存在 bin/releases/ci/,不互相覆盖、不合并成只剩一个平台的包。已有输出目录会使打包失败,不会自动清理之前的产物;Drone 应使用新的构建工作区。 仅保留 linux/amd64、windows/amd64、darwin/arm64 三个发布目标;不再生成 windows/arm64、linux/arm64、darwin/amd64 包,已有旧包不删除。
每包包含 serviceport 或 serviceport.exe、VERSION、BUILDINFO.json、二进制 SHA256SUMS.txt、README 和构建/测试文档。顶层另保留 release.json(三平台清单、版本、提交及校验值)和 SHA256SUMS.txt(所有压缩包及清单的校验值)。不打包真实 data/、数据库、密钥、源码、node_modules 或仓库凭据。macOS 包是未签名、未公证的命令行程序,不是 DMG 安装器。
tag 必须为 v<VERSION>,例如 VERSION=0.3.0 对应 v0.3.0;不匹配时失败。交叉编译不代表三个平台全部完成原生运行验证;CI 仅运行 Linux amd64 冒烟,不连接真实上游数据库或 SSH 服务。
IPFS 配置与下载
upload-ipfs 的 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: serviceport-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 补齐平台信息。主代理可传入三平台发布的 ci.upload-progress.json 的 files 并复用同一实现生成索引。
**pin 与备份边界:**索引文本中的包 CID/URI 不是 IPFS DAG 链接。pin 索引目录只保留四个索引/清单文件,不会递归 pin 三个包;三个包依靠各自 add 的 pin=true 请求保留。迁移或备份必须保存并处理全部三个包 CID 和索引 CID(共四个 CID),不能只备份索引 CID。脚本确认 add 成功响应,不额外查询 pin 状态。节点 pin 不等于公共网关完整可用,也不等于备份;Drone 临时工作区可能被回收。
历史节点验证状态(2026-09-24,旧六平台)
以下保留旧六包方案的历史实测记录,不代表当前三平台方案已完成真实上传验证;新脚本不接受旧六包 manifest,已有旧包不删除。
据主代理提供的实测记录,请求通过 http://192.168.9.1:7893 代理:/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://192.168.9.1:7893 作为 HTTP/HTTPS 代理,大小写变量一致;no_proxy 仅包含 localhost,127.0.0.1,::1。软件源、Git、npm、Go 依赖和 IPFS 请求均继承此配置。代理需要支持 HTTP CONNECT,并能被 runner 和构建容器访问;如果 IPFS API 为内网地址,也需确保当前代理能够访问它。此配置是工具层 HTTP/HTTPS 代理,不是网络层禁止直连的防火墙。
私有仓库使用 Drone 注入的 DRONE_NETRC_*,关闭 shell 跟踪后将认证文件写入克隆容器 HOME,不放进共享源码或发布包。保留 TLS 校验,不再强制 Git 协议 v0。
宿主机 Docker daemon 在流水线启动前拉取工具镜像,仍需单独设置代理。Linux systemd 部署可将以下内容合并到 /etc/systemd/system/docker.service.d/http-proxy.conf,不要覆盖原有配置;若 daemon.json 已设置代理,也需同步核对。
[Service]
Environment="HTTP_PROXY=http://192.168.9.1:7893"
Environment="HTTPS_PROXY=http://192.168.9.1:7893"
Environment="NO_PROXY=localhost,127.0.0.1,::1"
在维护窗口执行 sudo systemctl daemon-reload 和 sudo systemctl restart docker;重启可能影响其他服务。本次仓库修改不自动修改或重启宿主机。
没有构建记录时检查 Drone 仓库启用、Gitea webhook 和 runner;clone 失败时检查该步骤完整日志;已经生成包但发布失败时检查 upload-ipfs 日志、Secret、代理和 Kubo API 访问权限。