refactor(catalog): register focused Harmony plugins and accelerate mainland CI

This commit is contained in:
2026-09-11 10:37:01 +08:00
parent 5802042e10
commit 235cad0733
9 changed files with 86 additions and 32 deletions
+23 -3
View File
@@ -16,10 +16,28 @@ jobs:
permissions: permissions:
contents: write contents: write
steps: steps:
- uses: https://github.com/actions/checkout@v4 - name: Check out catalog from Gitea
- run: corepack enable && corepack pnpm install --frozen-lockfile env:
CHECKOUT_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_SERVER_URL: ${{ gitea.server_url }}
run: |
set -eu
auth_script=$(mktemp)
trap 'rm -f "$auth_script"' EXIT
cat > "$auth_script" <<'AUTH'
#!/bin/sh
case "$1" in
*Username*) printf '%s\n' x-access-token ;;
*) printf '%s\n' "$CHECKOUT_TOKEN" ;;
esac
AUTH
chmod 700 "$auth_script"
GIT_ASKPASS="$auth_script" GIT_TERMINAL_PROMPT=0 git clone --quiet --depth 1 --branch main "$GITEA_SERVER_URL/trove/curios.git" catalog
- name: Register completed release - name: Register completed release
run: node tools/sync-release.mjs --publish working-directory: catalog
run: |
source tools/ci-runtime.sh
node tools/sync-release.mjs --publish
env: env:
GITEA_SERVER_URL: ${{ gitea.server_url }} GITEA_SERVER_URL: ${{ gitea.server_url }}
GITEA_TOKEN: ${{ secrets.PLUGIN_RELEASE_TOKEN }} GITEA_TOKEN: ${{ secrets.PLUGIN_RELEASE_TOKEN }}
@@ -27,3 +45,5 @@ jobs:
CATALOG_REPOSITORY: ${{ gitea.repository }} CATALOG_REPOSITORY: ${{ gitea.repository }}
RELEASE_REPOSITORY: ${{ inputs.repository }} RELEASE_REPOSITORY: ${{ inputs.repository }}
RELEASE_TAG: ${{ inputs.tag }} RELEASE_TAG: ${{ inputs.tag }}
NPM_REGISTRY: ${{ vars.NPM_REGISTRY }}
NODE_MIRROR: ${{ vars.NODE_MIRROR }}
+1
View File
@@ -0,0 +1 @@
registry=https://registry.npmmirror.com
+15 -11
View File
@@ -1,23 +1,27 @@
# Trove Curios # Trove Curios
让桌面,多一点顺手。 **让桌面,多一些可能。**
Trove 官方插件库。发现喜欢的 Curio,装进你的工作台,让常用能力触手可及。 发现专注、轻巧的桌面工具,把常用能力带到手边。
## 小工具,大用途 ## Harmony Mirror
每个 Curio 专注一件事,用轻巧的独立工具承接日常操作,把更多空间留给手头的工作。 让鸿蒙,来到桌面。独立投屏窗口,鼠标与键盘直接操作手机。
### [Harmony Connector](https://git.render.ink/trove-plugins/harmony-connector) [探索 Harmony Mirror →](https://git.render.ink/trove-curios/harmony-mirror)
让设备,更近一步。连接 Mac 与鸿蒙手机,整夹收发照片与文档,随时掌握传输速度;管理应用,在独立窗口中投屏,用鼠标和键盘完成日常操作。 ## Harmony Assistant
## 按你的习惯组合 鸿蒙随行,资料有序。收发完整文件夹,管理应用,让重要资料留在身边。
按需安装,自由搭配。工具各自更新,也能通过服务相互配合,让你的工作方式逐渐长成自己的样子。 [探索 Harmony Assistant →](https://git.render.ink/trove-curios/harmony-assistant)
## 从桌面,开始 ## 带到你的桌面
打开 Trove,发现 Curios,为下一个灵感和下一项任务留出空间。 在 Trove 的「软件源」中添加 Git 仓库:
[探索 Trove](https://trove.render.ink) · [发现 Curios](https://trove.render.ink/curios) · [开发者文档](docs/development.md) ```text
https://git.render.ink/trove/curios.git
```
选择喜欢的 Curio,安装,即刻使用。
+15 -3
View File
@@ -1,6 +1,6 @@
# 官方软件源的维护与发布 # 官方软件源的维护与发布
`trove/curios` 保存官方软件源索引、插件仓库登记和发布工具。底座在 `trove/app`;插件源码与安装包分别位于 `trove-plugins` 下各仓库的代码和 Releases。 `trove/curios` 保存官方软件源索引、插件仓库登记和发布工具。底座在 `trove/app`;插件源码与安装包分别位于 `trove-curios` 下各仓库的代码和 Releases。
## 仓库职责 ## 仓库职责
@@ -16,7 +16,7 @@
## 发布协议 ## 发布协议
插件仓库根目录的 `release.json` 声明仓库、插件 ID 和是否为共享测试套件。普通插件 tag 为 `v<curio.json version>`;测试套件独立打 tag,各测试插件保留自身版本。 插件仓库根目录的 `release.json` 声明仓库、插件 ID。每个仓库只发布一个插件,tag 为 `v<curio.json version>`。
Release 先以草稿创建,上传每个平台的 `.curio` 和 `trove-release.json` 后再发布。描述文件包含仓库、tag、源码 commit、插件 Manifest 摘要,以及附件名、平台、大小和 SHA-256。插件图标以小型图片 Data URL 收录,私有图片也无需额外鉴权。 Release 先以草稿创建,上传每个平台的 `.curio` 和 `trove-release.json` 后再发布。描述文件包含仓库、tag、源码 commit、插件 Manifest 摘要,以及附件名、平台、大小和 SHA-256。插件图标以小型图片 Data URL 收录,私有图片也无需额外鉴权。
@@ -28,7 +28,7 @@ Release 先以草稿创建,上传每个平台的 `.curio` 和 `trove-release.j
## 新增插件 ## 新增插件
1. 在 `trove-plugins` 创建独立仓库,添加源码、`curio.json`、`release.json` 和发布 workflow。 1. 在 `trove-curios` 创建独立仓库,添加源码、`curio.json`、`release.json` 和发布 workflow。
2. 将仓库与 ID 登记到 `source/repositories.json`,授予收录机器人读取该仓库的代码与 Release 的权限。 2. 将仓库与 ID 登记到 `source/repositories.json`,授予收录机器人读取该仓库的代码与 Release 的权限。
3. 在对应平台准备构建环境与 Trove CLI,提交源码,运行插件仓库的手动发布脚本并上传 Release。 3. 在对应平台准备构建环境与 Trove CLI,提交源码,运行插件仓库的手动发布脚本并上传 Release。
@@ -45,3 +45,15 @@ Release 成功但收录失败时,在插件 Actions 手动运行发布 workflow
## 自动化身份 ## 自动化身份
`trove-publisher` 负责跨仓库通信:可以读取源代码、触发源 Actions,并读取已登记插件的 Release;没有底座仓库访问权限,也不能直接改写源代码。源索引写入由源 workflow 自身的任务令牌完成。 `trove-publisher` 负责跨仓库通信:可以读取源代码、触发源 Actions,并读取已登记插件的 Release;没有底座仓库访问权限,也不能直接改写源代码。源索引写入由源 workflow 自身的任务令牌完成。
## 大陆网络环境
发布流程直接从本 Gitea 拉取源代码,不依赖 GitHub 的 checkout/setup-node Action,也不在 Linux runner 上构建 macOS 原生产物。
- npm 默认 `https://registry.npmmirror.com`;仓库级 `.npmrc` 也使用该镜像。
- runner 已有 Node.js 22+ 时直接复用;否则从 `https://npmmirror.com/mirrors/node` 下载固定 Node.js 22.23.2,并校验预置的官方 SHA-256。
- 收录只安装锁定版本的 semver,不下载 Corepack/pnpm,不执行 npm 生命周期脚本。
- Actions Variables `NPM_REGISTRY`、`NODE_MIRROR` 可覆盖镜像地址,未设置时采用上述国内地址。
- Linux job 需要 bash、git、curl、tar、xz 和 CA 证书。runner 容器镜像拉取发生在 workflow 执行前;如更换 runner 镜像,应在 VPS 的 Docker 配置或 runner 配置中使用可访问的镜像仓库。
参考:[npmmirror 使用说明](https://npmmirror.com/)、[Gitea Action 下载机制](https://docs.gitea.com/usage/actions/faq/)。
+4 -12
View File
@@ -3,23 +3,15 @@
"name": "Trove 官方软件源", "name": "Trove 官方软件源",
"repositories": [ "repositories": [
{ {
"repository": "trove-plugins/harmony-connector", "repository": "trove-curios/harmony-mirror",
"ids": [ "ids": [
"dev.trove.harmony-connector" "dev.trove.harmony-mirror"
] ]
}, },
{ {
"repository": "trove-plugins/test-suite", "repository": "trove-curios/harmony-assistant",
"suite": true,
"ids": [ "ids": [
"dev.trove.qa.host", "dev.trove.harmony-assistant"
"dev.trove.qa.isolation",
"dev.trove.qa.self",
"dev.trove.qa.provider",
"dev.trove.qa.consumer",
"dev.trove.qa.optional",
"dev.trove.qa.autostart",
"dev.trove.qa.upgrade"
] ]
} }
] ]
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Source this file in a Linux job after checking out the catalog.
set -euo pipefail
export npm_config_registry="${NPM_REGISTRY:-https://registry.npmmirror.com}"
export COREPACK_NPM_REGISTRY="$npm_config_registry"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
if ! command -v node >/dev/null || ! node -e 'process.exit(Number(process.versions.node.split(".")[0]) >= 22 ? 0 : 1)'; then
case "$(uname -m)" in
x86_64) node_arch=x64; node_sha=d60acfe00a2932254bb0ad20e01b0d74397a0875595de719654b214f4b03f307 ;;
aarch64|arm64) node_arch=arm64; node_sha=fff4078c5def658577f92c88db7db3bc0072924bfb93fe52c1e744a54e94abb8 ;;
*) echo 'Unsupported Linux runner architecture' >&2; exit 1 ;;
esac
node_archive="node-v22.23.2-linux-${node_arch}.tar.xz"
node_cache="${RUNNER_TEMP:-/tmp}/trove-node-22.23.2-${node_arch}"
mkdir -p "$node_cache"
curl --fail --location --retry 3 --connect-timeout 15 --max-time 180 \
"${NODE_MIRROR:-https://npmmirror.com/mirrors/node}/v22.23.2/$node_archive" -o "$node_cache/$node_archive"
# Hashes are pinned from the official nodejs.org SHASUMS256.txt.
printf '%s %s\n' "$node_sha" "$node_cache/$node_archive" | sha256sum --check --status
tar -xJf "$node_cache/$node_archive" -C "$node_cache" --strip-components=1
export PATH="$node_cache/bin:$PATH"
fi
node --version
# The catalog only needs semver; avoid downloading pnpm/Corepack in each job.
npm install --ignore-scripts --no-audit --no-fund --package-lock=false
+1 -1
View File
@@ -20,7 +20,7 @@ files.set('trove-release.json', descriptorBytes);
let release = await api(`${route}/releases/tags/${encodeURIComponent(tag)}`, { missing: true }); let release = await api(`${route}/releases/tags/${encodeURIComponent(tag)}`, { missing: true });
if (!release) release = await api(`${route}/releases`, { method: 'POST', body: { if (!release) release = await api(`${route}/releases`, { method: 'POST', body: {
tag_name: tag, target_commitish: descriptor.commit, name: tag, draft: true, tag_name: tag, target_commitish: descriptor.commit, name: tag, draft: true,
prerelease: config.suite ? false : tag.slice(1).split('+')[0].includes('-'), prerelease: tag.slice(1).split('+')[0].includes('-'),
body: `${config.name} ${tag}\n\n通过 Trove 软件源安装和更新。`, body: `${config.name} ${tag}\n\n通过 Trove 软件源安装和更新。`,
} }); } });
for (const [name, bytes] of files) { for (const [name, bytes] of files) {
+1 -1
View File
@@ -16,7 +16,7 @@ export async function releasePlan(entriesFile, tag) {
for (const entry of entries) { for (const entry of entries) {
const manifest = entry.manifest; const manifest = entry.manifest;
if (!config.ids.includes(manifest.id) || !valid(manifest.version)) throw new Error('Unexpected plugin identity/version'); if (!config.ids.includes(manifest.id) || !valid(manifest.version)) throw new Error('Unexpected plugin identity/version');
if (!config.suite && tag !== `v${manifest.version}`) throw new Error('Release tag must match curio.json'); if (tag !== `v${manifest.version}`) throw new Error('Release tag must match curio.json');
if (!['any', 'aarch64-apple-darwin', 'x86_64-apple-darwin'].includes(entry.artifact.target)) throw new Error('Unsupported package target'); if (!['any', 'aarch64-apple-darwin', 'x86_64-apple-darwin'].includes(entry.artifact.target)) throw new Error('Unsupported package target');
const bytes = await readFile(entry.file); const bytes = await readFile(entry.file);
if (digest(bytes) !== entry.artifact.sha256 || bytes.length !== entry.artifact.size) throw new Error('Package differs from build metadata'); if (digest(bytes) !== entry.artifact.sha256 || bytes.length !== entry.artifact.size) throw new Error('Package differs from build metadata');
+1 -1
View File
@@ -29,7 +29,7 @@ if (object?.type !== 'commit' || object.sha !== descriptor.commit) throw new Err
const additions = []; const identities = new Set(); const additions = []; const identities = new Set();
for (const plugin of descriptor.plugins) { for (const plugin of descriptor.plugins) {
if (!publisher.ids.includes(plugin.id) || !valid(plugin.version) || !validRange(plugin.engine?.trove) || typeof plugin.name !== 'string' || !plugin.name.trim()) throw new Error('Unexpected plugin identity or compatibility range'); if (!publisher.ids.includes(plugin.id) || !valid(plugin.version) || !validRange(plugin.engine?.trove) || typeof plugin.name !== 'string' || !plugin.name.trim()) throw new Error('Unexpected plugin identity or compatibility range');
if (!publisher.suite && tag !== `v${plugin.version}`) throw new Error('Plugin version differs from release tag'); if (tag !== `v${plugin.version}`) throw new Error('Plugin version differs from release tag');
if (!Array.isArray(plugin.artifacts) || !plugin.artifacts.length) throw new Error('Plugin has no artifacts'); if (!Array.isArray(plugin.artifacts) || !plugin.artifacts.length) throw new Error('Plugin has no artifacts');
for (const artifact of plugin.artifacts) { for (const artifact of plugin.artifacts) {
if (!['any', 'aarch64-apple-darwin', 'x86_64-apple-darwin'].includes(artifact.target) || !/^[a-f0-9]{64}$/.test(artifact.sha256) || !Number.isSafeInteger(artifact.size) || artifact.size < 1 || artifact.size > 512 * 1024 * 1024) throw new Error('Invalid artifact metadata'); if (!['any', 'aarch64-apple-darwin', 'x86_64-apple-darwin'].includes(artifact.target) || !/^[a-f0-9]{64}$/.test(artifact.sha256) || !Number.isSafeInteger(artifact.size) || artifact.size < 1 || artifact.size > 512 * 1024 * 1024) throw new Error('Invalid artifact metadata');