diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 2ed8954..d151498 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -15,7 +15,7 @@ concurrency: jobs: build: - if: github.repository == 'potato47/malatang' + if: github.repository == 'semicoder-dev/malatang' runs-on: macos-26 timeout-minutes: 60 defaults: @@ -67,7 +67,7 @@ jobs: publish: needs: build - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/sdk-v') && github.repository == 'potato47/malatang' + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/sdk-v') && github.repository == 'semicoder-dev/malatang' runs-on: ubuntu-latest timeout-minutes: 10 environment: npm diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7251524..4469405 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ concurrency: cancel-in-progress: false jobs: build: - if: github.repository == 'potato47/malatang' + if: github.repository == 'semicoder-dev/malatang' runs-on: macos-26 timeout-minutes: 90 environment: release @@ -76,7 +76,7 @@ jobs: run: bun scripts/release/signing.ts cleanup publish: needs: build - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.repository == 'potato47/malatang' + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.repository == 'semicoder-dev/malatang' runs-on: ubuntu-latest timeout-minutes: 20 environment: diff --git a/README.md b/README.md index 312c567..561bc7d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # 麻辣烫 · Malatang -> 开发中、未发布:当前源码已取消内置插件,首次进入应用中心,通过官方或自定义市场安装更新,也可从 npm/Git/本地归档手动安装。官方插件已迁入独立 malatang-plugins 仓库;市场线上服务尚未发布。详见 [市场契约](docs/plugin-marketplace.md)。公开 0.4.1 的行为以正式发布文档为准。 +> 组织迁移发行准备:应用 0.4.3 / build 7、SDK 0.3.2 正在验收,公开状态以官网安装页为准。新版官方市场与自动更新使用 `semicoder-dev` 地址,旧版请通过完整 DMG 更新;详见 [0.4.3 说明](release/notes/0.4.3.md)。 基于 FIA 的本地插件应用平台。宿主提供模型、存储和统一界面组件,独立插件组合这些能力,拥有自己的应用页面。 ## 0.4.1 版本 -应用 [0.4.1 / build 5](https://github.com/potato47/malatang/releases/tag/v0.4.1) 与 SDK 0.3.0 已公开发布:Base UI + Tailwind CSS 4、组合式公共组件、manifest SDK 0.3、自定义主题、凭证文件及 Bun 1.4.3。公开产物验证通过,官网内容同步部署单独验收;证据见 [应用发布记录](docs/github-release.md) 和 [SDK 发布记录](docs/npm-release.md)。可见变化与升级要求见 [0.4.1 说明](release/notes/0.4.1.md)。 +应用 [0.4.1 / build 5](https://github.com/semicoder-dev/malatang/releases/tag/v0.4.1) 与 SDK 0.3.0 已公开发布:Base UI + Tailwind CSS 4、组合式公共组件、manifest SDK 0.3、自定义主题、凭证文件及 Bun 1.4.3。公开产物验证通过,官网内容同步部署单独验收;证据见 [应用发布记录](docs/github-release.md) 和 [SDK 发布记录](docs/npm-release.md)。可见变化与升级要求见 [0.4.1 说明](release/notes/0.4.1.md)。 ## 0.4.0 版本(历史) @@ -18,7 +18,7 @@ ## 下载与安装 -官网统一位于 [Semicoder · 麻辣烫](https://semicoder.dev/malatang),[下载与安装](https://semicoder.dev/malatang/docs/installation) 页面提供正式 DMG 下载、发行状态与安装指引。具体版本和校验信息以该页及 [GitHub Releases](https://github.com/potato47/malatang/releases) 为准;`fia-runtime-*` 预发布仅供构建,不是麻辣烫安装包。 +官网统一位于 [Semicoder · 麻辣烫](https://semicoder.dev/malatang),[下载与安装](https://semicoder.dev/malatang/docs/installation) 页面提供正式 DMG 下载、发行状态与安装指引。具体版本和校验信息以该页及 [GitHub Releases](https://github.com/semicoder-dev/malatang/releases) 为准;`fia-runtime-*` 预发布仅供构建,不是麻辣烫安装包。 应用支持 macOS 14+ / Apple Silicon,安装后的应用自带运行时,无需另装 Bun 或 Swift。0.4.1 源码从同一提交构建 FIA,需要下述 Swift 工具链;历史 v0.4.0 的固定归档构建方式仅适用于该旧标签。 diff --git a/bun.lock b/bun.lock index 0944ecf..44b1a67 100644 --- a/bun.lock +++ b/bun.lock @@ -47,7 +47,7 @@ }, "packages/sdk": { "name": "@semicoder/malatang-sdk", - "version": "0.3.1", + "version": "0.3.2", "dependencies": { "@base-ui/react": "^1.9.0", "@tailwindcss/postcss": "^4.3.3", diff --git a/docs/apple-signing-setup.md b/docs/apple-signing-setup.md index 4818656..048b096 100644 --- a/docs/apple-signing-setup.md +++ b/docs/apple-signing-setup.md @@ -1,6 +1,6 @@ # 首次配置 Apple 签名与公证 -`potato47/malatang` 的 Apple 凭据已配置,2026-10-04 [v0.1.0 / build 1](https://github.com/potato47/malatang/releases/tag/v0.1.0) 的[正式工作流](https://github.com/potato47/malatang/actions/runs/37214205671/attempts/2)已通过签名、公证和镜像运行验收。本文保留首次配置步骤,供环境重建或凭据轮换时参考;日常发版复用已有配置,不重新生成更新密钥。不要把证书密码、Apple 专用密码或更新私钥粘贴到聊天、Issue 或仓库文件里。 +`semicoder-dev/malatang` 的 Apple 凭据已配置,2026-10-04 [v0.1.0 / build 1](https://github.com/semicoder-dev/malatang/releases/tag/v0.1.0) 的[正式工作流](https://github.com/semicoder-dev/malatang/actions/runs/37214205671/attempts/2)已通过签名、公证和镜像运行验收。本文保留首次配置步骤,供环境重建或凭据轮换时参考;日常发版复用已有配置,不重新生成更新密钥。不要把证书密码、Apple 专用密码或更新私钥粘贴到聊天、Issue 或仓库文件里。 ## 1. 确认 Apple Developer 资格 @@ -52,7 +52,7 @@ ## 6. 在 GitHub 的 release 环境填写凭据 -打开 [仓库 Environments](https://github.com/potato47/malatang/settings/environments),进入名为 **release** 的环境。没有时点击 **New environment** 创建。 +打开 [仓库 Environments](https://github.com/semicoder-dev/malatang/settings/environments),进入名为 **release** 的环境。没有时点击 **New environment** 创建。 Environment secrets 中点 **Add environment secret**,逐项添加: @@ -87,7 +87,7 @@ Secrets 保存后不会显示原值,这是正常现象。配置完告诉协作 ## 7. 开启 Pages 并验收 -1. 打开 [Pages 设置](https://github.com/potato47/malatang/settings/pages),在 Build and deployment 的 Source 选择 **GitHub Actions**。这里仅托管应用更新数据,固定清单地址为 `https://nobug.space/malatang/updates/latest.json`,无需另设 Custom domain;`potato47.github.io/malatang` 会跨域跳转,不能用作客户端固定更新地址。[官网](https://semicoder.dev/malatang) 与 [安装入口](https://semicoder.dev/malatang/docs/installation) 由 Semicoder 承载,Pages 首页在应用发布后跳转官网,`updates/` 路径保持。 +1. 打开 [Pages 设置](https://github.com/semicoder-dev/malatang/settings/pages),在 Build and deployment 的 Source 选择 **GitHub Actions**。这里仅托管应用更新数据,固定清单地址为 `https://nobug.space/malatang/updates/latest.json`,无需另设 Custom domain;`potato47.github.io/malatang` 会跨域跳转,不能用作客户端固定更新地址。[官网](https://semicoder.dev/malatang) 与 [安装入口](https://semicoder.dev/malatang/docs/installation) 由 Semicoder 承载,Pages 首页在应用发布后跳转官网,`updates/` 路径保持。 首发时 `github-pages` 的部署规则仅允许 `main`;本次只增加精确 `v0.1.0` 标签,保留其他保护。后续发版前检查 `release` 与 `github-pages` 的 allowlist,仅按当次授权放行必要标签,不改为允许全部分支或标签。 2. 发布 workflow 必须已合入 GitHub 默认分支,才会出现手动入口。打开 **Actions → Release Malatang → Run workflow**,选择待验收分支。 3. 这次手动运行会完成构建、签名、公证、应用启动退出检查和更新签名,但**不会公开新版本或部署更新源**。 diff --git a/docs/github-release.md b/docs/github-release.md index 2a5fdbe..fdfcc48 100644 --- a/docs/github-release.md +++ b/docs/github-release.md @@ -1,8 +1,8 @@ # GitHub 构建、正式发布与自动更新 -目标仓库:`potato47/malatang`。官网为 [Semicoder · 麻辣烫](https://semicoder.dev/malatang),用户安装入口固定为 [下载与安装](https://semicoder.dev/malatang/docs/installation)。正式发行的完整 `.dmg` 托管在 GitHub Releases;具体版本和下载以官网安装页及 Release 为准,`fia-runtime-*` 预发布只是框架依赖。 +目标仓库:`semicoder-dev/malatang`。官网为 [Semicoder · 麻辣烫](https://semicoder.dev/malatang),用户安装入口固定为 [下载与安装](https://semicoder.dev/malatang/docs/installation)。正式发行的完整 `.dmg` 托管在 GitHub Releases;具体版本和下载以官网安装页及 Release 为准,`fia-runtime-*` 预发布只是框架依赖。 -自动更新地址继续固定为 `https://nobug.space/malatang/updates/latest.json`,更新数据不随官网迁移。发布脚本仅将该站点首页跳转至 Semicoder,保留 `updates/` 下的清单和不可变历史文件。应用支持 macOS 14+ / Apple Silicon;正式包安装时打开 DMG,将 `Malatang.app` 拖到 Applications 后启动,无需另外安装 Bun。 +从 0.4.3 / build 7 起,自动更新地址为 `https://semicoder-dev.github.io/malatang/updates/latest.json`。组织更新站使用独立 `updates-site` 生成分支,从本次发行开始保留不可变历史;旧 `gh-pages` 和 Release 附件保留原样,不参与新更新链路。旧版须通过完整 DMG 安装新版,不提供旧地址回退或自动迁移。应用支持 macOS 14+ / Apple Silicon;正式包安装时打开 DMG,将 `Malatang.app` 拖到 Applications 后启动,无需另外安装 Bun。 ## 版本规则与发布时机 @@ -68,7 +68,7 @@ SDK 0.3.0 已首次 OIDC 独立发布,标签固定 `1a66de5`,见 [SDK 发布 - `Build Malatang`:`main` 提交、PR 或手动触发。执行 check、测试、`build --dmg`(含挂载镜像、签名完整性和应用启动退出检查),上传测试 DMG、校验文件和报告。应用仅 ad-hoc 签名、关闭更新,DMG 不签名公证;不使用发布密钥。 - `Release Malatang`:`v*` 标签触发正式发布。手动运行也会签名、公证并上传验收产物,但不发布 Release 或 Pages。 - `Publish Malatang SDK`:独立 `sdk-v*` 标签通过 OIDC 发布 `@semicoder/malatang-sdk`;手动运行只验收归档。普通 CI 同时验证 SDK 的 npm 打包和独立插件构建。首次 npm 账号 / Trusted Publisher 配置见 [SDK 发布说明](npm-release.md)。 -- 正式流程:版本校验 → 冻结安装根 workspace → 从同一提交构建 FIA → 框架与应用检查 / 测试 → 临时钥匙串 → Developer ID Application 签名应用与 DMG → 对 DMG 公证 / staple / Gatekeeper 验证 → 挂载后验证应用签名、Gatekeeper 和启动退出 → 签名代码更新 → 安装包与更新逐文件比对 → GitHub Release → 保存更新历史到 `gh-pages` → Pages 部署。 +- 正式流程:版本校验 → 冻结安装根 workspace → 从同一提交构建 FIA → 框架与应用检查 / 测试 → 临时钥匙串 → Developer ID Application 签名应用与 DMG → 对 DMG 公证 / staple / Gatekeeper 验证 → 挂载后验证应用签名、Gatekeeper 和启动退出 → 签名代码更新 → 安装包与更新逐文件比对 → GitHub Release → 保存更新历史到 `updates-site` → Pages 部署。 - 发布按仓库串行运行。已公开 Release 的附件不覆盖;同一 build 的更新内容不可改写;旧标签重跑不得倒退更新源。Pages 部署失败可以重跑发布 job,沿用已发布内容。 ## 首次配置 @@ -77,8 +77,8 @@ SDK 0.3.0 已首次 OIDC 独立发布,标签固定 `1a66de5`,见 [SDK 发布 1. 创建仓库并推送源码。公开仓库可使用免费 GitHub Pages;私有仓库需要支持 Pages 的计划,更新文件仍需能被未登录客户端访问。更新产物包含业务代码,不应包含任何用户数据或凭证。 2. 将 `framework/fia/` 源码、根 workspace 和锁文件一起纳入仓库,CI 从同一提交准备 FIA,再构建应用;无需先公开 FIA 包。旧发行标签的固定归档仅用于历史重现。 -3. Settings → Pages → Source 选择 **GitHub Actions**。项目继承账号已有 `nobug.space` 域名,不另设项目自定义域名。`potato47.github.io` 会重定向到该域名,而 FIA 更新下载拒绝跨域重定向,因此必须直接使用实际 HTTPS 地址;改变域名 / 路径需要更换应用内固定地址并重新发安装包。 -4. 创建 `release` 和 `github-pages` Environments,核对待发布标签及手动验收分支的部署许可。首发时 `github-pages` 原先仅允许 `main`,本次保留其他保护、仅新增精确 `v0.1.0` 标签规则。0.2.0 发布经用户明确许可追加精确 `v0.2.0` 规则,其余保护不变。以后每次发布前检查对应标签的 allowlist,按该次发布授权增加必要规则,不改为允许全部分支或标签。保护发布分支及标签,发布凭据仅供可信源码使用。`gh-pages` 是生成数据分支,不将它合回 `main`。 +3. Settings → Pages → Source 选择 **GitHub Actions**,不配置自定义域名。确认公开地址直接为 `https://semicoder-dev.github.io/malatang/`,不得跨域跳转。更新地址写入完整 DMG;组织不能再设置会使项目 Pages 继承其他域名的账号级自定义域名。 +4. 创建 `release` 和 `github-pages` Environments,核对待发布标签及手动验收分支的部署许可。首发时 `github-pages` 原先仅允许 `main`,本次保留其他保护、仅新增精确 `v0.1.0` 标签规则。0.2.0 发布经用户明确许可追加精确 `v0.2.0` 规则,其余保护不变。以后每次发布前检查对应标签的 allowlist,按该次发布授权增加必要规则,不改为允许全部分支或标签。保护发布分支及标签,发布凭据仅供可信源码使用。`updates-site` 是生成数据分支,不将它合回 `main`。 5. 在 `release` 环境中配置以下 Secrets,内容不要提交到 Git: | Secret | 内容 | @@ -96,13 +96,13 @@ SDK 0.3.0 已首次 OIDC 独立发布,标签固定 `1a66de5`,见 [SDK 发布 首次生成的更新私钥保存在本机 `.fia/release-secrets/update-private.pem`(权限 0600);公钥已写入 `release/config.json`。备份私钥并上传 Secret,不能每次发布重新生成。更换公钥需重新安装可信安装包。可用已登录的 GitHub CLI 安全上传,不在终端输出密钥: ```sh -gh secret set FIA_UPDATE_PRIVATE_KEY --repo potato47/malatang --env release < .fia/release-secrets/update-private.pem -base64 < /path/to/DeveloperID.p12 | gh secret set APPLE_CERTIFICATE_P12 --repo potato47/malatang --env release -gh secret set APPLE_CERTIFICATE_PASSWORD --repo potato47/malatang --env release -gh secret set APPLE_ID --repo potato47/malatang --env release -gh secret set APPLE_TEAM_ID --repo potato47/malatang --env release -gh secret set APPLE_APP_SPECIFIC_PASSWORD --repo potato47/malatang --env release -gh variable set APPLE_SIGNING_IDENTITY --repo potato47/malatang --env release --body 'Developer ID Application: Your Name (TEAMID)' +gh secret set FIA_UPDATE_PRIVATE_KEY --repo semicoder-dev/malatang --env release < .fia/release-secrets/update-private.pem +base64 < /path/to/DeveloperID.p12 | gh secret set APPLE_CERTIFICATE_P12 --repo semicoder-dev/malatang --env release +gh secret set APPLE_CERTIFICATE_PASSWORD --repo semicoder-dev/malatang --env release +gh secret set APPLE_ID --repo semicoder-dev/malatang --env release +gh secret set APPLE_TEAM_ID --repo semicoder-dev/malatang --env release +gh secret set APPLE_APP_SPECIFIC_PASSWORD --repo semicoder-dev/malatang --env release +gh variable set APPLE_SIGNING_IDENTITY --repo semicoder-dev/malatang --env release --body 'Developer ID Application: Your Name (TEAMID)' ``` GitHub runner 使用临时钥匙串,最后恢复默认钥匙串并删除私钥 / 证书。构建任务仅有仓库读取权限;发布任务才有 Release 与 Pages 写入权限。私钥与签名凭据不进入 artifact。 @@ -113,9 +113,9 @@ GitHub runner 使用临时钥匙串,最后恢复默认钥匙串并删除私钥 以下归档与 runtime lock 仅描述历史发行;保留原标签和附件供重现,不作为当前发布前置条件: -0.4.0 / build 4 使用 FIA 官方 [v0.18.0](https://github.com/potato47/fia/releases/tag/v0.18.0),归档来自正式 GitHub Actions 的同一 npm artifact,SHA-256 `faf196b9408cc2fa4b9edfb0145c592a9a87b6fbc0b0245ad6b89c6c0e7ff4e2`,源码 `85206f6f532242fb2dfcfdf2a4b608f131726bf5`。无需复制为麻辣烫的新框架预发布;旧 `fia-runtime-*` 保留供历史版本重现。官方 npm 公开消费与应用正式发行结果分别验收。 +0.4.0 / build 4 使用 FIA 官方 [v0.18.0](https://github.com/semicoder-dev/fia/releases/tag/v0.18.0),归档来自正式 GitHub Actions 的同一 npm artifact,SHA-256 `faf196b9408cc2fa4b9edfb0145c592a9a87b6fbc0b0245ad6b89c6c0e7ff4e2`,源码 `85206f6f532242fb2dfcfdf2a4b608f131726bf5`。无需复制为麻辣烫的新框架预发布;旧 `fia-runtime-*` 保留供历史版本重现。官方 npm 公开消费与应用正式发行结果分别验收。 -0.2.0 所用归档来自 FIA dd430c851192,包含应用子命令和启动等待修复;源码提交、附件名、专用标签及 SHA-256 均在对应历史标签的 runtime lock 中。`0.16.1` 是当时的本地包版本,不表示它与 npm 同版本包内容相同。归档已上传至 [fia-runtime-dd430c851192](https://github.com/potato47/malatang/releases/tag/fia-runtime-dd430c851192),不要重复创建该标签或覆盖附件。 +0.2.0 所用归档来自 FIA dd430c851192,包含应用子命令和启动等待修复;源码提交、附件名、专用标签及 SHA-256 均在对应历史标签的 runtime lock 中。`0.16.1` 是当时的本地包版本,不表示它与 npm 同版本包内容相同。归档已上传至 [fia-runtime-dd430c851192](https://github.com/semicoder-dev/malatang/releases/tag/fia-runtime-dd430c851192),不要重复创建该标签或覆盖附件。 历史重现时核对归档 SHA-256,不重新打包后覆盖同一附件;即使源码没变,工具链也可能改变二进制哈希。当前 FIA 修改随应用候选提交一起构建和验证,不再增加固定归档或 runtime lock。未来框架稳定后拆出及恢复 npm 发布,需届时授权和独立验收。 @@ -130,11 +130,11 @@ GitHub runner 使用临时钥匙串,最后恢复默认钥匙串并删除私钥 5. 按本次授权将最终发布提交合入 `main` 并推送,在该提交创建和推送精确 `v` 标签;复核 `release` / `github-pages` 环境允许该精确标签。GitHub 正式流程生成、验证并公开 DMG 和更新源。 6. 核验公开产物与更新源,完成下述官网同步,分别记录发行和部署状态。已公开条目从待发布清单移出,保留版本说明和发行证据;其他未发布条目继续归集。官网等遗留交付项单独记录,不能因清单移出就宣称全部完成。 -只有明确获准发布且版本范围已确定时,才使用以下示例;`0.4.2` 仅是下一次常规候选,不是现在应执行的任务。在应用仓库执行: +只有明确获准发布且版本范围已确定时,才使用以下示例;`0.4.3` 为本次组织迁移候选,不是现在应执行的任务。在应用仓库执行: ```sh -# 先准备 release/notes/0.4.2.md 和受影响官网内容 -bun run version:app 0.4.2 +# 先准备 release/notes/0.4.3.md 和受影响官网内容 +bun run version:app 0.4.3 bun run release:check bun run check bun run test @@ -142,8 +142,8 @@ bun run build bun run framework:verify # 完成其他受影响检查,审阅并提交;按授权合入 main,确认 HEAD 为已验发布提交后: git push origin main -git tag -a v0.4.2 -m 'Malatang 0.4.2' -git push origin v0.4.2 +git tag -a v0.4.3 -m 'Malatang 0.4.3' +git push origin v0.4.3 ``` 当前脚本只支持无后缀的 `x.y.z`;预发布不能进入稳定更新源。发布提交必须包含对应的 `release/notes/.md`,发布脚本会将其追加到正式 Release 说明。版本分类和说明完整性由维护者按本流程核对:现有版本脚本只校验格式、递增和 build,发布脚本允许缺少说明;本次不新增脚本约束或 CI。 @@ -152,7 +152,7 @@ git push origin v0.4.2 Release 与更新源发布完成后,核对正式 DMG 附件、SHA-256、签名公证报告及匿名下载,必须在同一发布任务同步 Semicoder 安装页、项目介绍、受影响使用指南/迁移说明与 `docs/project-sources.md`,并记录官网内容及部署状态。网站主干推送会独立触发部署,按当次授权执行;受阻时明确记录未完成项与完成条件,不能仅以应用已发布宣称整次交付完成。新 Release notes 包含官网与安装页链接,已公开版本不回写。 -新安装包与签名更新的 `downloadURL` 指向官网安装页。发布校验只允许该地址及旧版 `https://github.com/potato47/malatang/releases/latest`,以便读取不可改写的历史签名清单;不得因此放宽更新文件的固定来源、签名或内容校验。改官网入口不改 `updatesURL`、历史清单或已有 Release 附件。 +新安装包与签名更新的 `downloadURL` 仅允许官网安装页。旧个人 GitHub 下载入口和旧更新域名均拒绝;不可改写的历史清单留在旧数据分支及 Release 中。 FIA 的 `runtimeId` 包含 `updates.downloadURL`,因此此次入口修改会改变新安装包的 runtime ID。已有旧入口验收包应完整安装正式 DMG;未来更高 build 的更新发现 runtime ID 不同也会要求完整安装,不能只用代码更新替换该配置。历史清单可读不表示旧安装包与新运行时兼容。 @@ -162,7 +162,7 @@ FIA 的 `runtimeId` 包含 `updates.downloadURL`,因此此次入口修改会 ## 保留历史与恢复 -`gh-pages/updates/releases//` 是不可变目录,每次部署携带全部历史,避免客户端取到旧清单后下载 404。不要手工删改。接近 Pages 1 GB 限制时脚本在 900 MB 拒绝发布,需要先规划迁移更新源。安装包仅放 Releases,不进入 Pages。 +`updates-site/updates/releases//` 是不可变目录,每次部署携带新更新站的全部历史,避免客户端取到旧清单后下载 404。不要手工删改。接近 Pages 1 GB 限制时脚本在 900 MB 拒绝发布,需要先规划迁移更新源。安装包仅放 Releases,不进入 Pages。 - 构建、公证或签名失败:没有发布 job;修复配置后重跑。 - Release 已公开但 Pages 失败:重跑失败 job;不覆盖已公开安装包。相同清单可重试。 @@ -174,6 +174,6 @@ FIA 的 `runtimeId` 包含 `updates.downloadURL`,因此此次入口修改会 ## 0.3.0 浏览器界面与运行时 -0.3.0 / build 3 接入 FIA 0.17.0 默认浏览器入口、独立会话与原生权限白名单,以及文件对话框连接与等待修复。`release/runtime-lock.json` 已切换到公开 [fia-runtime-4689c707913c](https://github.com/potato47/malatang/releases/tag/fia-runtime-4689c707913c),源码提交 `4689c707913ca31f93b03cfe4deb61466c9e69f2`,归档 SHA-256 `521a07c387efa5b2c7ddbb76807b5c613b5b26cdf6e9c84fb965d27a5dd536b6`;已匿名下载并与验收包逐字节核对。旧归档保留。 +0.3.0 / build 3 接入 FIA 0.17.0 默认浏览器入口、独立会话与原生权限白名单,以及文件对话框连接与等待修复。`release/runtime-lock.json` 已切换到公开 [fia-runtime-4689c707913c](https://github.com/semicoder-dev/malatang/releases/tag/fia-runtime-4689c707913c),源码提交 `4689c707913ca31f93b03cfe4deb61466c9e69f2`,归档 SHA-256 `521a07c387efa5b2c7ddbb76807b5c613b5b26cdf6e9c84fb965d27a5dd536b6`;已匿名下载并与验收包逐字节核对。旧归档保留。 本次发布授权下,`github-pages` 仅新增精确 `v0.3.0` 标签许可,其他保护保持不变。Host 标题栏与菜单变化需要完整 DMG,从 0.2.0 或更早版本升级不能只安装代码更新。SDK 为 0.2.1,manifest 兼容版本仍为 0.2;SDK 与应用发行分别验收。浏览器文件选择在白名单内,不开放钥匙串、屏幕捕获或其他被拒绝的 Native 能力。 diff --git a/docs/npm-release.md b/docs/npm-release.md index 71aa65b..32d9332 100644 --- a/docs/npm-release.md +++ b/docs/npm-release.md @@ -1,6 +1,6 @@ # SDK npm 自动发布 -仓库 `potato47/malatang`;发布包 `@semicoder/malatang-sdk`,源码在 `packages/sdk`。SDK 以 TypeScript / TSX 源码供 Bun 插件工具链使用,包含 types、client、runtime、manifest、UI、主题/组件 CSS、构建/检查工具与 notes/model 模板,不依赖 FIA 源码或应用目录。 +仓库 `semicoder-dev/malatang`;发布包 `@semicoder/malatang-sdk`,源码在 `packages/sdk`。SDK 以 TypeScript / TSX 源码供 Bun 插件工具链使用,包含 types、client、runtime、manifest、UI、主题/组件 CSS、构建/检查工具与 notes/model 模板,不依赖 FIA 源码或应用目录。 ## 包名与版本 @@ -30,6 +30,10 @@ SDK 0.3.0 对应 manifest `sdkVersion: "0.3"`,使用 Base UI / Tailwind CSS 4 0.2.1 当次使用本机 npm 认证,未执行远端 OIDC;该历史边界已由上述 0.3.0 真实 OIDC 验收替代。不要推送 `sdk-v0.2.1` 触发重复发布,未来独立标签必须使用新版本。旧 0.2.0 于 2026-10-05 首次本机发布,归档不改写。 +## 组织迁移配置 + +旧 Trusted Publisher 身份字段不可修改;在最终归档就绪后删除旧连接,并创建组织连接,明确允许 `npm publish`。新连接必须在两天内首次成功发布。环境继续使用精确版本标签许可,不添加长期 npm token。 + ## 首次配置 1. 将本次源码和 `.github/workflows/publish-npm.yml` 合入 GitHub 默认分支。工作流名为 **Publish Malatang SDK**,手动运行始终只验收并上传 `.tgz`,不会发布。 @@ -47,7 +51,7 @@ SDK 0.3.0 对应 manifest `sdkVersion: "0.3"`,使用 Base UI / Tailwind CSS 4 | 字段 | 值 | | -------------------- | --------------------------------------------------------------------- | - | Organization or user | `potato47` | + | Organization or user | `semicoder-dev` | | Repository | `malatang` | | Workflow filename | `publish-npm.yml` | | Environment name | `npm` | diff --git a/docs/plugin-marketplace.md b/docs/plugin-marketplace.md index 034026e..f3533eb 100644 --- a/docs/plugin-marketplace.md +++ b/docs/plugin-marketplace.md @@ -12,7 +12,7 @@ ## 静态索引 v1 -索引 URL 为无凭证、无片段的 HTTPS 地址;重定向仍须 HTTPS。默认地址为 `https://raw.githubusercontent.com/potato47/malatang-plugins/main/marketplace.json`,仓库与线上索引尚未发布。 +索引 URL 为无凭证、无片段的 HTTPS 地址;重定向仍须 HTTPS。默认地址为 `https://raw.githubusercontent.com/semicoder-dev/malatang-plugins/main/marketplace.json`,仓库与线上索引尚未发布。 ```json { @@ -57,4 +57,4 @@ ## 官方仓库 -官方插件独立维护于 `potato47/malatang-plugins`,翻译 `translate` 与随手记 `quick-notes` 保持原 ID。宿主构建、测试和 SDK 归档验证不依赖该仓库。正式索引只列已经发布并验收的归档;首次公开发行已获授权,归档和市场上线结果在发布手册及官方仓库记录;候选归档不等于已上架。 +官方插件独立维护于 `semicoder-dev/malatang-plugins`,翻译 `translate` 与随手记 `quick-notes` 保持原 ID。宿主构建、测试和 SDK 归档验证不依赖该仓库。正式索引只列已经发布并验收的归档;首次公开发行已获授权,归档和市场上线结果在发布手册及官方仓库记录;候选归档不等于已上架。 diff --git a/framework/fia/.github/workflows/publish-npm.yml b/framework/fia/.github/workflows/publish-npm.yml index c845363..cba7d43 100644 --- a/framework/fia/.github/workflows/publish-npm.yml +++ b/framework/fia/.github/workflows/publish-npm.yml @@ -69,7 +69,7 @@ jobs: publish: name: Publish to npm with OIDC needs: build - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.repository == 'potato47/fia' + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.repository == 'semicoder-dev/fia' runs-on: ubuntu-latest timeout-minutes: 10 environment: npm diff --git a/framework/fia/docs/npm-release.md b/framework/fia/docs/npm-release.md index 55bb631..72b5147 100644 --- a/framework/fia/docs/npm-release.md +++ b/framework/fia/docs/npm-release.md @@ -2,7 +2,7 @@ > 当前共仓开发阶段:在宿主仓库内禁用 FIA npm 发布,仅执行框架归档与独立消费验收。以下独立仓库发布流程保留供将来拆出后使用;现有 Trusted Publisher 未迁移。 -GitHub 仓库:`potato47/fia`。npm 包:`@semicoder/fia`,源码在 `packages/cli`。 +GitHub 仓库:`semicoder-dev/fia`。npm 包:`@semicoder/fia`,源码在 `packages/cli`。 ## 0.18.0 GitHub 发布记录(2026-10-09) @@ -32,7 +32,7 @@ Swift 测试显式使用 `--no-parallel`:原生测试共享 NSApplication、 | 字段 | 值 | | -------------------- | ---------------------- | - | Organization or user | `potato47` | + | Organization or user | `semicoder-dev` | | Repository | `fia` | | Workflow filename | `publish-npm.yml` | | Environment name | `npm` | @@ -59,7 +59,7 @@ git tag -a v0.18.1 -m "Release 0.18.1" git push origin v0.18.1 ``` -以上示例假定版本提交已在 `main`;使用特性分支时,先将版本提交合入主分支,再在该提交打标签。仓库尚未配置 remote 时,先配置 `origin` 为 `https://github.com/potato47/fia.git`。也可不指定版本执行 `bun run version:npm`,默认递增 minor 并归零 patch。 +以上示例假定版本提交已在 `main`;使用特性分支时,先将版本提交合入主分支,再在该提交打标签。仓库尚未配置 remote 时,先配置 `origin` 为 `https://github.com/semicoder-dev/fia.git`。也可不指定版本执行 `bun run version:npm`,默认递增 minor 并归零 patch。 `version:npm` 只同步根包、CLI 包、TypeScript、Swift 和 `bun.lock` 的版本;不构建、不发布、不提交、不创建标签。不要只执行 `npm version`,否则这几个版本会不一致。 diff --git a/framework/fia/packages/cli/package.json b/framework/fia/packages/cli/package.json index 4d10136..dbb152c 100644 --- a/framework/fia/packages/cli/package.json +++ b/framework/fia/packages/cli/package.json @@ -4,7 +4,7 @@ "description": "Build macOS applications for humans and agents with a shared API, UI and CLI", "repository": { "type": "git", - "url": "git+https://github.com/potato47/fia.git", + "url": "git+https://github.com/semicoder-dev/fia.git", "directory": "packages/cli" }, "bin": { diff --git a/framework/fia/packages/cli/tests/npm-workflow.test.ts b/framework/fia/packages/cli/tests/npm-workflow.test.ts index edd44e0..4cec564 100644 --- a/framework/fia/packages/cli/tests/npm-workflow.test.ts +++ b/framework/fia/packages/cli/tests/npm-workflow.test.ts @@ -59,7 +59,7 @@ test("release metadata validates all version sources and routes prereleases to n }); const packagePath = resolve(directory, "packages/cli/package.json"); const source = await readFile(packagePath, "utf8"); - await writeFile(packagePath, source.replace("potato47/fia", "other/fia")); + await writeFile(packagePath, source.replace("semicoder-dev/fia", "other/fia")); await expect(checkNPMRelease(directory)).rejects.toThrow("repository.url"); await writeFile(packagePath, source.replace(prerelease, "01.2.3")); await expect(checkNPMRelease(directory)).rejects.toThrow("invalid semantic version"); diff --git a/framework/fia/tools/check-npm-release.ts b/framework/fia/tools/check-npm-release.ts index 340a36f..ed2c663 100644 --- a/framework/fia/tools/check-npm-release.ts +++ b/framework/fia/tools/check-npm-release.ts @@ -16,8 +16,8 @@ export async function checkNPMRelease(directory = repositoryRoot, ref?: string) const cli = JSON.parse(await read("packages/cli/package.json")); if (cli.name !== CLI_PACKAGE_NAME || cli.private === true) throw new Error("Expected the public @semicoder/fia package"); - if (cli.repository?.url !== "git+https://github.com/potato47/fia.git") - throw new Error("npm repository.url must match potato47/fia for trusted publishing"); + if (cli.repository?.url !== "git+https://github.com/semicoder-dev/fia.git") + throw new Error("npm repository.url must match semicoder-dev/fia for trusted publishing"); const version: string = cli.version; compareSemanticVersions(version, version); const versions = { diff --git a/package.json b/package.json index 35e87f6..db49004 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "malatang", - "version": "0.4.2", + "version": "0.4.3", "private": true, "homepage": "https://semicoder.dev/malatang", "workspaces": [ diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 45fa932..d411997 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -2,7 +2,7 @@ > 未发布宿主变更:插件市场与原位更新见 [市场契约](../../docs/plugin-marketplace.md)。当前源码删除 `PluginInfo.builtin`,增加可空 `market` 来源;安装任务增加 `operation/targetVersion`。SDK 包版本暂不调整,公开 0.3.0 尚无这些类型变更。插件 manifest 与模型/KV/UI 契约不变。 -SDK 0.3.0 使用 **Base UI 1.9 + Tailwind CSS 4.3**,manifest `sdkVersion` 为 `0.3`,需要麻辣烫 0.4.1 或以上宿主。0.2 及更早版本插件需迁移并重建;应用与 SDK 独立发行,公开状态见 [npm 发布记录](https://github.com/potato47/malatang/blob/main/docs/npm-release.md)。CLI 使用随应用提供的固定 SDK 归档创建插件。 +SDK 0.3.0 使用 **Base UI 1.9 + Tailwind CSS 4.3**,manifest `sdkVersion` 为 `0.3`,需要麻辣烫 0.4.1 或以上宿主。0.2 及更早版本插件需迁移并重建;应用与 SDK 独立发行,公开状态见 [npm 发布记录](https://github.com/semicoder-dev/malatang/blob/main/docs/npm-release.md)。CLI 使用随应用提供的固定 SDK 归档创建插件。 插件为可信本地代码,命名空间和 CSS 作用域不是安全沙箱。使用 Bun >=1.4.2;React 与 ReactDOM 19 必须精确同版本。宿主提供唯一的 React、Base UI 和公共组件实现。此轮不提供旧 API 或插件兼容层,数据存储键保持不变。 diff --git a/packages/sdk/package.json b/packages/sdk/package.json index ae3bda4..7ff3b1a 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,14 +1,14 @@ { "name": "@semicoder/malatang-sdk", - "version": "0.3.1", + "version": "0.3.2", "description": "Model, storage, UI and plugin build SDK for Malatang", "homepage": "https://semicoder.dev/malatang", "bugs": { - "url": "https://github.com/potato47/malatang/issues" + "url": "https://github.com/semicoder-dev/malatang/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/potato47/malatang.git", + "url": "git+https://github.com/semicoder-dev/malatang.git", "directory": "packages/sdk" }, "files": [ diff --git a/release/config.json b/release/config.json index 90b58e1..137cb3b 100644 --- a/release/config.json +++ b/release/config.json @@ -1,7 +1,7 @@ { - "repository": "potato47/malatang", - "build": 6, - "updatesURL": "https://nobug.space/malatang/updates/latest.json", + "repository": "semicoder-dev/malatang", + "build": 7, + "updatesURL": "https://semicoder-dev.github.io/malatang/updates/latest.json", "publicKey": "wNM+4SLpDEXq3guGbwp6H4LgFdPkW9XDO4pywwFkBQw=", "downloadURL": "https://semicoder.dev/malatang/docs/installation" } diff --git a/release/notes/0.4.3.md b/release/notes/0.4.3.md new file mode 100644 index 0000000..f09e803 --- /dev/null +++ b/release/notes/0.4.3.md @@ -0,0 +1,6 @@ +# 麻辣烫 0.4.3 / build 7 + +- 项目源码、发行和官方插件市场统一由 GitHub 组织 `semicoder-dev` 管理。 +- 自动更新改为 `https://semicoder-dev.github.io/malatang/updates/latest.json`,官方市场指向组织仓库。 +- 本次须下载完整 DMG 替换旧应用;旧地址不提供兼容转发或自动迁移。模型、凭证、KV 和历史不主动清除。 +- SDK 独立发布为 0.3.2,manifest SDK 0.3 与插件 API 保持不变。FIA 独立 npm 继续暂停。 diff --git a/release/notes/unreleased.md b/release/notes/unreleased.md index 0e86f51..4b53215 100644 --- a/release/notes/unreleased.md +++ b/release/notes/unreleased.md @@ -3,3 +3,7 @@ 2026-10-11 已将主题入口统一与插件市场整批发布为应用 **0.4.2 / build 6**、SDK **0.3.1**,官方翻译/随手记 **0.1.0** 及市场已上线。发行内容归档于 [0.4.2](0.4.2.md),证据见[应用发布手册](../../docs/github-release.md)和[SDK 发布手册](../../docs/npm-release.md)。官网部署由 workspace 发行记录单独跟踪。 当前无已完成待发布条目。下一次普通候选仅为 0.4.3,未来发布须重新获得用户授权;FIA 独立 npm 继续暂停。日常开发不改版本或 build。 + +## 组织迁移(0.4.3 发行准备) + +GitHub 项目、官方市场与新更新站切换 `semicoder-dev`;SDK 0.3.2。新更新站独立初始化,旧签名归档不改写。旧版须完整 DMG 安装,不做旧地址兼容。验证与公开发布待本次验收。 diff --git a/scripts/npm/check.ts b/scripts/npm/check.ts index d144f39..fb7d6c5 100644 --- a/scripts/npm/check.ts +++ b/scripts/npm/check.ts @@ -3,7 +3,7 @@ import { fileURLToPath } from "node:url"; export const root = fileURLToPath(new URL("../../", import.meta.url)); export const packageName = "@semicoder/malatang-sdk"; -export const repository = "potato47/malatang"; +export const repository = "semicoder-dev/malatang"; export const registry = "https://registry.npmjs.org"; export function assert(condition: unknown, message: string): asserts condition { diff --git a/scripts/release/config.ts b/scripts/release/config.ts index 2a2712a..f51a335 100644 --- a/scripts/release/config.ts +++ b/scripts/release/config.ts @@ -6,17 +6,15 @@ export { config, pkg }; export const root = new URL("../../", import.meta.url).pathname; export const websiteURL = "https://semicoder.dev/malatang"; const installationURL = `${websiteURL}/docs/installation`; -// Previously signed manifests are immutable; accept only the two known installer pages. -const installerURLs = new Set([installationURL, "https://github.com/potato47/malatang/releases/latest"]); export function assert(condition: unknown, message: string): asserts condition { if (!condition) throw new Error(message); } export function validateConfig() { - assert(config.repository === "potato47/malatang", "Unexpected release repository"); + assert(config.repository === "semicoder-dev/malatang", "Unexpected release repository"); assert(/^\d+\.\d+\.\d+$/.test(pkg.version), "Only stable x.y.z versions can enter the stable update feed"); assert(Number.isSafeInteger(config.build) && config.build > 0, "Release build must be a positive integer"); assert(/^[A-Za-z0-9+/]{43}=$/.test(config.publicKey), "Configure a permanent Ed25519 update public key"); - assert(config.updatesURL === "https://nobug.space/malatang/updates/latest.json", "Unexpected Pages update URL"); + assert(config.updatesURL === "https://semicoder-dev.github.io/malatang/updates/latest.json", "Unexpected Pages update URL"); assert(config.downloadURL === installationURL, "Use the official installation page"); } export type Manifest = { schema: number; identifier: string; version: string; build: number; runtimeId: string; baseURL: string; downloadURL?: string; files: { path: string; size: number; sha256: string }[] }; @@ -31,7 +29,7 @@ export function readManifest(text: string, publicKey = config.publicKey): Manife assert(Number.isSafeInteger(value.build) && value.build > 0 && /^\d+\.\d+\.\d+$/.test(value.version), "Invalid update version"); assert(/^[a-f0-9]{64}$/.test(value.runtimeId), "Invalid runtime ID"); assert(value.baseURL === new URL(`releases/${value.build}/`, config.updatesURL).href, "Unexpected update file origin"); - assert(typeof value.downloadURL === "string" && installerURLs.has(value.downloadURL), "Unexpected installer URL"); + assert(typeof value.downloadURL === "string" && value.downloadURL === installationURL, "Unexpected installer URL"); assert(Array.isArray(value.files) && value.files.length > 0 && value.files.length <= 4096, "Invalid update file count"); const paths = new Set(); let size = 0; for (const file of value.files) { diff --git a/scripts/release/history.ts b/scripts/release/history.ts new file mode 100644 index 0000000..02cf063 --- /dev/null +++ b/scripts/release/history.ts @@ -0,0 +1,9 @@ +import { assert } from "./config"; + +// The organization feed starts a new history; the retired gh-pages branch is never read or modified. +export const updateBranch = "updates-site"; +export async function hasUpdateHistory(listRefs: () => Promise): Promise { + const refs: unknown = JSON.parse(await listRefs()); + assert(Array.isArray(refs) && refs.every(ref => ref && typeof ref.ref === "string"), "Invalid update history response"); + return (refs as { ref: string }[]).some(ref => ref.ref === `refs/heads/${updateBranch}`); +} diff --git a/scripts/release/publish.ts b/scripts/release/publish.ts index 0696dda..a06aa61 100644 --- a/scripts/release/publish.ts +++ b/scripts/release/publish.ts @@ -4,6 +4,7 @@ import { tmpdir } from "node:os"; import { assert, config, pkg, validateConfig, readManifest, websiteURL, root } from "./config"; import { checksum, verifyRelease } from "./verify"; import { stageSite } from "./site"; +import { updateBranch, hasUpdateHistory } from "./history"; validateConfig(); assert(process.env.GITHUB_REPOSITORY === config.repository && process.env.GITHUB_REF === `refs/tags/v${pkg.version}` && process.env.GITHUB_EVENT_NAME === "push", "Publish only from a matching version tag in the application repository"); @@ -25,13 +26,12 @@ try { await run(["tar", "-xzf", join(bundle, "updates.tar.gz"), "-C", updateDirectory]); const manifest = await verifyRelease(updateDirectory); // Recover the complete previous Pages snapshot. A failed API call must never mean an empty history. - const refResult = await gh("api", `repos/${config.repository}/git/matching-refs/heads/gh-pages`); - const refs = JSON.parse(refResult) as { ref: string }[]; + const hasHistory = await hasUpdateHistory(() => gh("api", `repos/${config.repository}/git/matching-refs/heads/${updateBranch}`)); await mkdir(site, { recursive: true }); - await run(["git", "init", "-b", "gh-pages"], site); + await run(["git", "init", "-b", updateBranch], site); await run(["git", "remote", "add", "origin", `https://github.com/${config.repository}.git`], site); - if (refs.some(ref => ref.ref === "refs/heads/gh-pages")) { - await run(["git", "fetch", "--depth=1", "origin", "gh-pages"], site, gitEnv); + if (hasHistory) { + await run(["git", "fetch", "--depth=1", "origin", updateBranch], site, gitEnv); await run(["git", "reset", "--hard", "FETCH_HEAD"], site); } await stageSite(updateDirectory, site); // Monotonic feed / immutable build checks run BEFORE any release mutation. @@ -65,7 +65,7 @@ try { const changes = await run(["git", "status", "--porcelain"], site); if (changes) { await run(["git", "-c", "user.name=github-actions[bot]", "-c", "user.email=41898282+github-actions[bot]@users.noreply.github.com", "commit", "-m", `Publish Malatang ${pkg.version} (${config.build})`], site); - await run(["git", "push", "origin", "HEAD:gh-pages"], site, gitEnv); + await run(["git", "push", "origin", `HEAD:${updateBranch}`], site, gitEnv); } await rm(join(site, ".git"), { recursive: true, force: true }); console.log(`Release ${tag} published; preserved update history is ready for Pages deployment.`); diff --git a/shared/marketplace.ts b/shared/marketplace.ts index 31952d6..80c6130 100644 --- a/shared/marketplace.ts +++ b/shared/marketplace.ts @@ -84,7 +84,7 @@ export const defaultMarkets = () => [ { id: "official", name: "官方插件市场", - url: "https://raw.githubusercontent.com/potato47/malatang-plugins/main/marketplace.json", + url: "https://raw.githubusercontent.com/semicoder-dev/malatang-plugins/main/marketplace.json", enabled: true, index: null, updatedAt: null, diff --git a/tests/npm-release.test.ts b/tests/npm-release.test.ts index 1a3be7c..d1a25f2 100644 --- a/tests/npm-release.test.ts +++ b/tests/npm-release.test.ts @@ -6,7 +6,7 @@ import { assertVersionIncrease } from "../scripts/npm/version"; describe("SDK npm release", () => { test("release tags follow SDK version rather than application version", () => { expect(validatePackage(pkg)).toEqual({ version: pkg.version, tag: `sdk-v${pkg.version}`, filename: `semicoder-malatang-sdk-${pkg.version}.tgz` }); - const env = { GITHUB_ACTIONS: "true", GITHUB_REPOSITORY: "potato47/malatang", GITHUB_EVENT_NAME: "push", GITHUB_REF: `refs/tags/sdk-v${pkg.version}` }; + const env = { GITHUB_ACTIONS: "true", GITHUB_REPOSITORY: "semicoder-dev/malatang", GITHUB_EVENT_NAME: "push", GITHUB_REF: `refs/tags/sdk-v${pkg.version}` }; expect(() => validateTrigger(pkg.version, env)).not.toThrow(); for (const ref of [`refs/tags/v${pkg.version}`, "refs/tags/sdk-v9.9.9", "refs/heads/main"]) { expect(() => validateTrigger(pkg.version, { ...env, GITHUB_REF: ref })).toThrow("Tag and SDK version differ"); diff --git a/tests/release.test.ts b/tests/release.test.ts index f394fda..ee00e22 100644 --- a/tests/release.test.ts +++ b/tests/release.test.ts @@ -6,6 +6,7 @@ import { join } from "node:path"; import { config, pkg, readManifest, type Manifest } from "../scripts/release/config"; import { verifyDirectory, verifyRelease } from "../scripts/release/verify"; import { stageSite } from "../scripts/release/site"; +import { hasUpdateHistory } from "../scripts/release/history"; const temporary = await mkdtemp(join(tmpdir(), "malatang-release-test-")); const original = { ...config }, version = pkg.version; @@ -60,22 +61,25 @@ describe("signed release validation", () => { await writeFile(join(resources, "fia.runtime.json"), JSON.stringify({ runtimeId: "b".repeat(64), app: { version: "0.3.0", build: 3 }, updates: { url: config.updatesURL, publicKey: config.publicKey } })); await expect(verifyRelease(f.directory, app)).rejects.toThrow("different runtimes"); }); - test("migrates the installer page without rejecting signed history or moving update files", async () => { - const previous = await fixture(1, "-legacy-installer"), next = await fixture(2, "-official-installer"), site = join(temporary, "installer-migration"); - const legacy = { ...manifest(1), downloadURL: "https://github.com/potato47/malatang/releases/latest" }; - const previousText = envelope(legacy); - await writeFile(join(previous.directory, "latest.json"), previousText); - config.build = 1; pkg.version = "0.1.0"; await stageSite(previous.directory, site); - expect(await readFile(join(site, "updates/latest.json"), "utf8")).toBe(previousText); - config.build = 2; pkg.version = "0.2.0"; await stageSite(next.directory, site); + test("initializes the organization feed and rejects legacy signed origins", async () => { + const f = await fixture(7, "-organization"), site = join(temporary, "organization-site"); + config.build = 7; pkg.version = "0.7.0"; + await stageSite(f.directory, site); const current = readManifest(await readFile(join(site, "updates/latest.json"), "utf8")); + expect(current.baseURL).toBe("https://semicoder-dev.github.io/malatang/updates/releases/7/"); expect(current.downloadURL).toBe("https://semicoder.dev/malatang/docs/installation"); - expect(current.baseURL).toBe("https://nobug.space/malatang/updates/releases/2/"); - await verifyDirectory(join(site, "updates/releases/1"), legacy); - await verifyDirectory(join(site, "updates/releases/2"), current); - expect(await readFile(join(previous.directory, "latest.json"), "utf8")).toBe(previousText); + expect(() => readManifest(envelope({ ...current, baseURL: "https://nobug.space/malatang/updates/releases/7/" }))).toThrow("origin"); + expect(() => readManifest(envelope({ ...current, downloadURL: "https://github.com/potato47/malatang/releases/latest" }))).toThrow("installer URL"); const landing = await readFile(join(site, "index.html"), "utf8"); - expect(landing).toContain('http-equiv="refresh" content="0;url=https://semicoder.dev/malatang"'); expect(landing).toContain('href="https://semicoder.dev/malatang/docs/installation"'); }); + test("only an explicit missing organization branch starts an empty history", async () => { + expect(await hasUpdateHistory(async () => "[]")).toBe(false); + expect(await hasUpdateHistory(async () => JSON.stringify([{ ref: "refs/heads/updates-site" }]))).toBe(true); + expect(await hasUpdateHistory(async () => JSON.stringify([{ ref: "refs/heads/updates-site-backup" }]))).toBe(false); + await expect(hasUpdateHistory(async () => { throw new Error("GitHub unavailable"); })).rejects.toThrow("GitHub unavailable"); + for (const body of ['{"message":"Not Found"}', '[{}]', 'invalid']) { + await expect(hasUpdateHistory(async () => body)).rejects.toThrow(); + } + }); });