diff --git a/.gitea/workflows/sync-release.yml b/.gitea/workflows/sync-release.yml index 49255ff..8f2a5ff 100644 --- a/.gitea/workflows/sync-release.yml +++ b/.gitea/workflows/sync-release.yml @@ -16,10 +16,28 @@ jobs: permissions: contents: write steps: - - uses: https://github.com/actions/checkout@v4 - - run: corepack enable && corepack pnpm install --frozen-lockfile + - name: Check out catalog from Gitea + 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 - run: node tools/sync-release.mjs --publish + working-directory: catalog + run: | + source tools/ci-runtime.sh + node tools/sync-release.mjs --publish env: GITEA_SERVER_URL: ${{ gitea.server_url }} GITEA_TOKEN: ${{ secrets.PLUGIN_RELEASE_TOKEN }} @@ -27,3 +45,5 @@ jobs: CATALOG_REPOSITORY: ${{ gitea.repository }} RELEASE_REPOSITORY: ${{ inputs.repository }} RELEASE_TAG: ${{ inputs.tag }} + NPM_REGISTRY: ${{ vars.NPM_REGISTRY }} + NODE_MIRROR: ${{ vars.NODE_MIRROR }} diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..7549542 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npmmirror.com diff --git a/README.md b/README.md index 984457e..a09545c 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,27 @@ # 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,安装,即刻使用。 diff --git a/docs/development.md b/docs/development.md index a039f16..2357c03 100644 --- a/docs/development.md +++ b/docs/development.md @@ -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`;测试套件独立打 tag,各测试插件保留自身版本。 +插件仓库根目录的 `release.json` 声明仓库、插件 ID。每个仓库只发布一个插件,tag 为 `v`。 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 的权限。 3. 在对应平台准备构建环境与 Trove CLI,提交源码,运行插件仓库的手动发布脚本并上传 Release。 @@ -45,3 +45,15 @@ Release 成功但收录失败时,在插件 Actions 手动运行发布 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/)。 diff --git a/source/repositories.json b/source/repositories.json index 79d041f..5fe32c6 100644 --- a/source/repositories.json +++ b/source/repositories.json @@ -3,23 +3,15 @@ "name": "Trove 官方软件源", "repositories": [ { - "repository": "trove-plugins/harmony-connector", + "repository": "trove-curios/harmony-mirror", "ids": [ - "dev.trove.harmony-connector" + "dev.trove.harmony-mirror" ] }, { - "repository": "trove-plugins/test-suite", - "suite": true, + "repository": "trove-curios/harmony-assistant", "ids": [ - "dev.trove.qa.host", - "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" + "dev.trove.harmony-assistant" ] } ] diff --git a/tools/ci-runtime.sh b/tools/ci-runtime.sh new file mode 100644 index 0000000..aba63f0 --- /dev/null +++ b/tools/ci-runtime.sh @@ -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 diff --git a/tools/publish-release.mjs b/tools/publish-release.mjs index 056328e..447579d 100644 --- a/tools/publish-release.mjs +++ b/tools/publish-release.mjs @@ -20,7 +20,7 @@ files.set('trove-release.json', descriptorBytes); let release = await api(`${route}/releases/tags/${encodeURIComponent(tag)}`, { missing: true }); if (!release) release = await api(`${route}/releases`, { method: 'POST', body: { 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 软件源安装和更新。`, } }); for (const [name, bytes] of files) { diff --git a/tools/release-plan.mjs b/tools/release-plan.mjs index 4f3d75a..01e99ad 100644 --- a/tools/release-plan.mjs +++ b/tools/release-plan.mjs @@ -16,7 +16,7 @@ export async function releasePlan(entriesFile, tag) { for (const entry of entries) { const manifest = entry.manifest; 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'); 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'); diff --git a/tools/sync-release.mjs b/tools/sync-release.mjs index 673786b..a4c6ee1 100644 --- a/tools/sync-release.mjs +++ b/tools/sync-release.mjs @@ -29,7 +29,7 @@ if (object?.type !== 'commit' || object.sha !== descriptor.commit) throw new Err const additions = []; const identities = new Set(); 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.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'); 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');