Skip to main content

fnos-publish

飞牛 App 发布助手 —— 交互式勾选,把任意飞牛项目打成 .fpk 并推镜像到 Docker Hub,支持国内 fnOS 与海外 Fygo OS 双平台。当用户说「发布飞牛 app」「打包上架」「打飞牛包」「出 fpk」「推镜像到 docker」「上架应用中心」「发海外版/Fygo」「fnos publish」「打包并上传 docker」时使用。镜像在飞牛 NAS 上原生构建(x86_64),国内直连 Docker Hub 被墙时自动走本地代理 + crane 推送。

跳到安装

来源信息

仓库
go7th/fnos-publish
最近来源活动
2026年7月8日 08:02
检测到的 SKILL.md 语言
中文
星标
0
分支
0

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
fnos-publish
description
飞牛 App 发布助手 —— 交互式勾选,把任意飞牛项目打成 .fpk 并推镜像到 Docker Hub,支持国内 fnOS 与海外 Fygo OS 双平台。当用户说「发布飞牛 app」「打包上架」「打飞牛包」「出 fpk」「推镜像到 docker」「上架应用中心」「发海外版/Fygo」「fnos publish」「打包并上传 docker」时使用。镜像在飞牛 NAS 上原生构建(x86_64),国内直连 Docker Hub 被墙时自动走本地代理 + crane 推送。
# 飞牛 App 发布助手 把任意**飞牛项目**(含 `fnos/manifest` + `Dockerfile` + `fnos/app/docker/docker-compose.yaml`)打包并发布。脚本 `fnos_publish.sh` 按 `manifest` 的 `appname` 自省,同一份逻辑通用于所有同构项目。**交互式勾选**:先用 AskUserQuestion 让用户选清楚,再执行。 脚本路径:`~/.claude/skills/fnos-publish/fnos_publish.sh` ## 交互流程(务必先问清再动手) 用 **AskUserQuestion** 依次收集(能自动确定的就别问): 1. **选 App**(若当前 cwd 已是飞牛项目则默认它;否则在你放飞牛项目的目录下扫描候选,可设 `FNOS_PROJECTS_DIR`) ```bash for d in "${FNOS_PROJECTS_DIR:-.}"/*/; do [ -f "$d/fnos/manifest" ] && \ printf "%s\t%s\n" "$(basename "$d")" "$(grep -E '^display_name' "$d/fnos/manifest" | awk -F= '{print $2}' | xargs)"; done ``` 2. **目标平台**(multiSelect):国内 fnOS / 海外 Fygo OS →`--target inland|oversea|both` 3. **动作**(multiSelect): - 在 NAS 构建镜像(默认开;不勾=`--no-build` 复用已有镜像 tar) - 推镜像到 Docker Hub →`--push` - 国内额外出离线版 →`--offline` - 上传 .fpk 到 NAS →`--upload` 4. 若选了 `--push` 且 `docker` 未登录 Docker Hub → 让用户提供 access token,先非交互登录。 5. 若选了 **海外** 且项目没有 `fnos/manifest.oversea` → 提示是否现在生成英文版 manifest(见下)。 确认后进入项目目录执行,例如: ```bash cd /path/to/your/fnos-project && \ DOCKER_USER=your-dockerhub-user NAS_HOST=your-nas-ip NAS_PASS='****' \ bash ~/.claude/skills/fnos-publish/fnos_publish.sh --target both --push --offline ``` ## 产物(`dist/`) | target | 文件 | 说明 | |--------|------|------| | inland | `<app>-<ver>.fpk` | 国内默认版,image 改写成 `<user>/<app>:<ver>`,应用中心自动从 Docker Hub 拉 | | inland+`--offline` | `<app>-<ver>-offline.fpk` + `<app>-image-<ver>.tar.gz` | 离线版,`pull_policy:never` + 本地 load | | oversea | `<app>-<ver>-oversea.fpk` | 海外版,image 同样走 Docker Hub(海外无墙,天然合适) | | — | `安装说明-*.txt` / `发布说明-海外.txt` | 给用户/审核的说明 | ## 国内 fnOS vs 海外 Fygo OS 差异 | 维度 | 国内 fnOS (`developer.fnnas.com`) | 海外 Fygo OS (`developer.fygonas.com`) | |------|-----------------------------------|----------------------------------------| | 打包工具 | `fnpack build` | `fygopack create` / `fygopack pack` | | 上架 | appcenter-cli / 官方流程 | **邮件提交 `app-review@fygo.io`** | | manifest | `arch=x86_64`、`service_port`… | 示例有 `platform`、`maintainer_url`、`ctl_stop`;建议英文 `display_name`/`desc` | | 语言 | 中文 | en / ja / zh(海外优先英文) | | 镜像仓库 | 国内走阿里云 mirror | Docker Hub(对海外用户无墙,直接用 `<user>/<app>`) | > 本脚本对两端都用「手工 tar 复刻官方 .fpk 结构」出包,`fygopack` 的完整 manifest 字段规范官方文档未给全。若要官方校验,在装有 `fygopack` 的机器上跑一次 `fygopack pack` 核对字段后再提交审核。 ## 🌐 海外 i18n / 语言转化(四层,缺一层就会露中文) 海外包不是「换个英文 manifest」就完事——UI、桌面入口、说明都要英文。脚本已自动处理能自动的部分,其余是**应用侧前置条件**,`--target oversea|both` 时会打印缺失清单: | 层 | 素材 | 谁负责 | |----|------|--------| | 1. 应用信息 | `fnos/manifest.oversea`:英文 `display_name`/`desc`(脚本优先用它) | 你提供 | | 2. 桌面入口 | `fnos/app/ui/config.oversea`:桌面图标 `title` 改英文(脚本自动覆盖进包) | 你提供 | | 3. 前端 UI | 应用必须已接 **i18n**(Umi `locale` / react-i18next)并有 `en-US` 词条 | 应用代码 | | 4. 默认语言 | 脚本自动在海外 compose 的 `environment` 注入 `APP_LOCALE=en-US` + `LANG=en_US.UTF-8` | **脚本自动** | **约定**:前端/后端启动时读环境变量 `APP_LOCALE`(缺省 `zh-CN`),据此选默认语言。这样**同一个 Docker 镜像**既能国内跑中文、海外跑英文,不必构建两份镜像。给各飞牛 app 统一加这段即可: ```ts // 前端(Umi):入口处 const locale = (import.meta as any)?.env?.APP_LOCALE || (window as any).APP_LOCALE || 'zh-CN'; // 由容器注入,海外=en-US setLocale(locale, false); ``` ```python # 后端(FastAPI):读同名变量决定返回文案语言 LOCALE = os.getenv("APP_LOCALE", "zh-CN") ``` > 若某 app 尚未接 i18n,海外包里 UI 仍是中文——这层打包改不了,得先在应用里补 i18n + `en-US` 词条。脚本会明确告警,别忽略。 ### 生成海外素材 - `fnos/manifest.oversea`:从 `fnos/manifest` 复制,`display_name`/`desc` 改英文,其余保留。 - `fnos/app/ui/config.oversea`:从 `fnos/app/ui/config` 复制,`title` 改英文。 - 缺失时脚本用中文素材兜底并告警,Fygo 审核前务必补齐。 ## ⚠️ 陷阱 1:本地 tag vs registry 引用(别手改源 compose) 源 `docker-compose.yaml` 里 `image: <app>:latest` 是**本地构建 tag**。脚本已自动分流:registry 版 sed 改写成 `<user>/<app>:<ver>`,离线版加 `pull_policy: never`。**不要手动改源 compose 的 image 行**。打完可校验: ```bash mkdir -p /tmp/c && tar xzf dist/<app>-<ver>.fpk -C /tmp/c app.tgz && \ tar xzf /tmp/c/app.tgz -C /tmp/c docker/docker-compose.yaml && grep -E 'image:|pull_policy' /tmp/c/docker/docker-compose.yaml # registry 版应是 <user>/<app>:<ver>;离线版应是 <app>:latest + pull_policy: never ``` ## ⚠️ 陷阱 2:国内直连 Docker Hub 上传被定向阻断(脚本已自动绕过) `docker push` 由 daemon 发起,国内直连 `registry-1.docker.io` 时控制面能过,一传大 blob(如 opencv/numpy 层 ~200MB)就在 `Head .../blobs/sha256:...` 处 `EOF`——定向阻断,重试无效;且 Docker Desktop 的 daemon 在 VM 里吃不到宿主 `127.0.0.1` 代理。 **脚本的 `--push` 已内置解法**:探测本地代理(Clash Verge 常见 `7897`,Clash `7890`),命中则用 **`crane`(纯 CLI,直吃 `HTTPS_PROXY`,读 `~/.docker/config.json` 登录态)**经代理直推;未探测到代理才退回 `docker push`。 前置:先登录(token 从 Docker Hub → Account Settings → Personal access tokens,给 Read/Write): ```bash echo '<dckr_pat_...>' | docker login -u <your-dockerhub-user> --password-stdin ``` 可用 `PROXY=http://127.0.0.1:<port>` 显式指定代理端口。 ## 推送后必做 1. 首次推某仓库后,到 Docker Hub 把 `<user>/<app>` 设 **Public**(否则别人无登录拉不到)。 验证:`DOCKER_CONFIG=$(mktemp -d) HTTPS_PROXY=http://127.0.0.1:7897 crane manifest <user>/<app>:<ver>` 能拿到=Public。 2. 清理:解压出的 `dist/<app>-image-<ver>.tar`(~几百 MB)验证后删,保留 `.tar.gz`。 3. 安全:docker access token 用完可到 Docker Hub 吊销重建;`dist/` 别提交进 git。 ## 版本号 单一事实源:`fnos/manifest` 的 `version`(脚本读它当 `<ver>`),与后端 `main.py` 的 `version="…"` 保持一致。升版本先改这两处再打包。 ## 决策提示(给 Claude) - 只改前端/`fnos/` 配置、没动后端 → 勾「复用镜像」(`--no-build`)。 - 改了后端/依赖/Dockerfile → 必须重新构建(默认)。 - 「让别人也能装」→ 必须 `--push` + 仓库设 Public,否则只能发离线版两文件。 - 缺 `NAS_PASS` 会卡在 SSH 密码交互 → 先问用户要密码或让其 `export NAS_PASS`。 - 海外提交前确认英文 manifest 与 Public 仓库就绪。
在 GitHub 查看