Merge branch 'main' into x-files/app-upgrade-config

This commit is contained in:
beyondkmp 2025-11-14 18:57:13 +08:00
commit c3a22d4ad9
6 changed files with 64 additions and 55 deletions

View File

@ -141,11 +141,11 @@ jobs:
path: main
fetch-depth: 0
- name: Checkout cs-releases branch
- name: Checkout x-files/app-upgrade-config branch
if: steps.check.outputs.should_run == 'true'
uses: actions/checkout@v5
with:
ref: cs-releases
ref: x-files/app-upgrade-config
path: cs
fetch-depth: 0
@ -192,7 +192,7 @@ jobs:
uses: peter-evans/create-pull-request@v7
with:
path: cs
base: cs-releases
base: x-files/app-upgrade-config
branch: chore/update-app-upgrade-config/${{ steps.meta.outputs.safe_tag }}
commit-message: "🤖 chore: sync app-upgrade-config for ${{ steps.meta.outputs.tag }}"
title: "chore: update app-upgrade-config for ${{ steps.meta.outputs.tag }}"
@ -209,4 +209,4 @@ jobs:
- name: No changes detected
if: steps.check.outputs.should_run == 'true' && steps.diff.outputs.changed != 'true'
run: echo "No updates required for cs-releases/app-upgrade-config.json"
run: echo "No updates required for x-files/app-upgrade-config/app-upgrade-config.json"

View File

@ -19,7 +19,7 @@ Currently, AppUpdater directly queries the GitHub API to retrieve beta and rc up
## Automation Workflow
The `cs-releases/app-upgrade-config.json` file is synchronized by the [`Update App Upgrade Config`](../../.github/workflows/update-app-upgrade-config.yml) workflow. The workflow runs the [`scripts/update-app-upgrade-config.ts`](../../scripts/update-app-upgrade-config.ts) helper so that every release tag automatically updates the JSON in `cs-releases`.
The `x-files/app-upgrade-config/app-upgrade-config.json` file is synchronized by the [`Update App Upgrade Config`](../../.github/workflows/update-app-upgrade-config.yml) workflow. The workflow runs the [`scripts/update-app-upgrade-config.ts`](../../scripts/update-app-upgrade-config.ts) helper so that every release tag automatically updates the JSON in `x-files/app-upgrade-config`.
### Trigger Conditions
@ -36,29 +36,29 @@ The `cs-releases/app-upgrade-config.json` file is synchronized by the [`Update A
### Workflow Steps
1. **Guard + metadata preparation** the `Check if should proceed` and `Prepare metadata` steps compute the target tag, prerelease flag, whether the tag is the newest release, and a `safe_tag` slug used for branch names. When any rule fails, the workflow stops without touching the config.
2. **Checkout source branches** the default branch is checked out into `main/`, while the long-lived `cs-releases` branch lives in `cs/`. All modifications happen in the latter directory.
2. **Checkout source branches** the default branch is checked out into `main/`, while the long-lived `x-files/app-upgrade-config` branch lives in `cs/`. All modifications happen in the latter directory.
3. **Install toolchain** Node.js 22, Corepack, and frozen Yarn dependencies are installed inside `main/`.
4. **Run the update script** `yarn tsx scripts/update-app-upgrade-config.ts --tag <tag> --config ../cs/app-upgrade-config.json --is-prerelease <flag>` updates the JSON in-place.
- The script normalizes the tag (e.g., strips `v` prefix), detects the release channel (`latest`, `rc`, `beta`), and loads segment rules from `config/app-upgrade-segments.json`.
- It validates that prerelease flags and semantic suffixes agree, enforces locked segments, builds mirror feed URLs, and performs release-availability checks (GitHub HEAD request for every channel; GitCode GET for latest channels, falling back to `https://releases.cherry-ai.com` when gitcode is delayed).
- After updating the relevant channel entry, the script rewrites the config with semver-sort order and a new `lastUpdated` timestamp.
5. **Detect changes + create PR** if `cs/app-upgrade-config.json` changed, the workflow opens a PR `chore/update-app-upgrade-config/<safe_tag>` against `cs-releases` with a commit message `🤖 chore: sync app-upgrade-config for <tag>`. Otherwise it logs that no update is required.
5. **Detect changes + create PR** if `cs/app-upgrade-config.json` changed, the workflow opens a PR `chore/update-app-upgrade-config/<safe_tag>` against `x-files/app-upgrade-config` with a commit message `🤖 chore: sync app-upgrade-config for <tag>`. Otherwise it logs that no update is required.
### Manual Trigger Guide
1. Open the Cherry Studio repository on GitHub → **Actions** tab → select **Update App Upgrade Config**.
2. Click **Run workflow**, choose the default branch (usually `main`), and fill in the `tag` input (e.g., `v2.1.0`).
3. Toggle `is_prerelease` only when the tag carries a prerelease suffix (`-beta`, `-rc`). Leave it unchecked for stable releases.
4. Start the run and wait for it to finish. Check the generated PR in the `cs-releases` branch, verify the diff in `app-upgrade-config.json`, and merge once validated.
4. Start the run and wait for it to finish. Check the generated PR in the `x-files/app-upgrade-config` branch, verify the diff in `app-upgrade-config.json`, and merge once validated.
## JSON Configuration File Format
### File Location
- **GitHub**: `https://raw.githubusercontent.com/CherryHQ/cherry-studio/refs/heads/cs-releases/app-upgrade-config.json`
- **GitCode**: `https://gitcode.com/CherryHQ/cherry-studio/raw/cs-releases/app-upgrade-config.json`
- **GitHub**: `https://raw.githubusercontent.com/CherryHQ/cherry-studio/refs/heads/x-files/app-upgrade-config/app-upgrade-config.json`
- **GitCode**: `https://gitcode.com/CherryHQ/cherry-studio/raw/x-files/app-upgrade-config/app-upgrade-config.json`
**Note**: Both mirrors provide the same configuration file hosted on the `cs-releases` branch. The client automatically selects the optimal mirror based on IP geolocation.
**Note**: Both mirrors provide the same configuration file hosted on the `x-files/app-upgrade-config` branch. The client automatically selects the optimal mirror based on IP geolocation.
### Configuration Structure (Current Implementation)
@ -222,9 +222,9 @@ interface ChannelConfig {
Starting from this change, `.github/workflows/update-app-upgrade-config.yml` listens to GitHub release events (published + prerelease). The workflow:
1. Checks out the default branch (for scripts) and the `cs-releases` branch (where the config is hosted).
2. Runs `yarn tsx scripts/update-app-upgrade-config.ts --tag <tag> --config ../cs/app-upgrade-config.json` to regenerate the config directly inside the `cs-releases` working tree.
3. If the file changed, it opens a PR against `cs-releases` via `peter-evans/create-pull-request`, with the generated diff limited to `app-upgrade-config.json`.
1. Checks out the default branch (for scripts) and the `x-files/app-upgrade-config` branch (where the config is hosted).
2. Runs `yarn tsx scripts/update-app-upgrade-config.ts --tag <tag> --config ../cs/app-upgrade-config.json` to regenerate the config directly inside the `x-files/app-upgrade-config` working tree.
3. If the file changed, it opens a PR against `x-files/app-upgrade-config` via `peter-evans/create-pull-request`, with the generated diff limited to `app-upgrade-config.json`.
You can run the same script locally via `yarn update:upgrade-config --tag v2.1.6 --config ../cs/app-upgrade-config.json` (add `--dry-run` to preview) to reproduce or debug whatever the workflow does. Passing `--skip-release-checks` along with `--dry-run` lets you bypass the release-page existence check (useful when the GitHub/GitCode pages arent published yet). Running without `--config` continues to update the copy in your current working directory (main branch) for documentation purposes.

View File

@ -19,7 +19,7 @@
## 自动化工作流
`cs-releases/app-upgrade-config.json` 由 [`Update App Upgrade Config`](../../.github/workflows/update-app-upgrade-config.yml) workflow 自动同步。工作流会调用 [`scripts/update-app-upgrade-config.ts`](../../scripts/update-app-upgrade-config.ts) 脚本,根据指定 tag 更新 `cs-releases` 分支上的配置文件。
`x-files/app-upgrade-config/app-upgrade-config.json` 由 [`Update App Upgrade Config`](../../.github/workflows/update-app-upgrade-config.yml) workflow 自动同步。工作流会调用 [`scripts/update-app-upgrade-config.ts`](../../scripts/update-app-upgrade-config.ts) 脚本,根据指定 tag 更新 `x-files/app-upgrade-config` 分支上的配置文件。
### 触发条件
@ -36,29 +36,29 @@
### 工作流步骤
1. **检查与元数据准备**`Check if should proceed` 和 `Prepare metadata` 步骤会计算 tag、prerelease 标志、是否最新版本以及用于分支名的 `safe_tag`。若任意校验失败,工作流立即退出。
2. **检出分支**:默认分支被检出到 `main/`,长期维护的 `cs-releases` 分支则在 `cs/` 中,所有改动都发生在 `cs/`
2. **检出分支**:默认分支被检出到 `main/`,长期维护的 `x-files/app-upgrade-config` 分支则在 `cs/` 中,所有改动都发生在 `cs/`
3. **安装工具链**:安装 Node.js 22、启用 Corepack并在 `main/` 目录执行 `yarn install --immutable`
4. **运行更新脚本**:执行 `yarn tsx scripts/update-app-upgrade-config.ts --tag <tag> --config ../cs/app-upgrade-config.json --is-prerelease <flag>`
- 脚本会标准化 tag去掉 `v` 前缀等)、识别渠道、加载 `config/app-upgrade-segments.json` 中的分段规则。
- 校验 prerelease 标志与语义后缀是否匹配、强制锁定的 segment 是否满足、生成镜像的下载地址,并检查 release 是否已经在 GitHub/GitCode 可用latest 渠道在 GitCode 不可用时会回退到 `https://releases.cherry-ai.com`)。
- 更新对应的渠道配置后,脚本会按 semver 排序写回 JSON并刷新 `lastUpdated`
5. **检测变更并创建 PR**:若 `cs/app-upgrade-config.json` 有变更,则创建 `chore/update-app-upgrade-config/<safe_tag>` 分支,提交信息为 `🤖 chore: sync app-upgrade-config for <tag>`,并向 `cs-releases` 提 PR无变更则输出提示。
5. **检测变更并创建 PR**:若 `cs/app-upgrade-config.json` 有变更,则创建 `chore/update-app-upgrade-config/<safe_tag>` 分支,提交信息为 `🤖 chore: sync app-upgrade-config for <tag>`,并向 `x-files/app-upgrade-config` 提 PR无变更则输出提示。
### 手动触发指南
1. 进入 Cherry Studio 仓库的 GitHub **Actions** 页面,选择 **Update App Upgrade Config** 工作流。
2. 点击 **Run workflow**,保持默认分支(通常为 `main`),填写 `tag`(如 `v2.1.0`)。
3. 只有在 tag 带 `-beta`/`-rc` 后缀时才勾选 `is_prerelease`,稳定版保持默认。
4. 启动运行并等待完成,随后到 `cs-releases` 分支的 PR 查看 `app-upgrade-config.json` 的变更并在验证后合并。
4. 启动运行并等待完成,随后到 `x-files/app-upgrade-config` 分支的 PR 查看 `app-upgrade-config.json` 的变更并在验证后合并。
## JSON 配置文件格式
### 文件位置
- **GitHub**: `https://raw.githubusercontent.com/CherryHQ/cherry-studio/refs/heads/cs-releases/app-upgrade-config.json`
- **GitCode**: `https://gitcode.com/CherryHQ/cherry-studio/raw/cs-releases/app-upgrade-config.json`
- **GitHub**: `https://raw.githubusercontent.com/CherryHQ/cherry-studio/refs/heads/x-files/app-upgrade-config/app-upgrade-config.json`
- **GitCode**: `https://gitcode.com/CherryHQ/cherry-studio/raw/x-files/app-upgrade-config/app-upgrade-config.json`
**说明**:两个镜像源提供相同的配置文件,统一托管在 `cs-releases` 分支上。客户端根据 IP 地理位置自动选择最优镜像源。
**说明**:两个镜像源提供相同的配置文件,统一托管在 `x-files/app-upgrade-config` 分支上。客户端根据 IP 地理位置自动选择最优镜像源。
### 配置结构(当前实际配置)
@ -222,9 +222,9 @@ interface ChannelConfig {
`.github/workflows/update-app-upgrade-config.yml` 会在 GitHub Release包含正常发布与 Pre Release触发
1. 同时 Checkout 仓库默认分支(用于脚本)和 `cs-releases` 分支(真实托管配置的分支)。
2. 在默认分支目录执行 `yarn tsx scripts/update-app-upgrade-config.ts --tag <tag> --config ../cs/app-upgrade-config.json`,直接重写 `cs-releases` 分支里的配置文件。
3. 如果 `app-upgrade-config.json` 有变化,则通过 `peter-evans/create-pull-request` 自动创建一个指向 `cs-releases` 的 PRDiff 仅包含该文件。
1. 同时 Checkout 仓库默认分支(用于脚本)和 `x-files/app-upgrade-config` 分支(真实托管配置的分支)。
2. 在默认分支目录执行 `yarn tsx scripts/update-app-upgrade-config.ts --tag <tag> --config ../cs/app-upgrade-config.json`,直接重写 `x-files/app-upgrade-config` 分支里的配置文件。
3. 如果 `app-upgrade-config.json` 有变化,则通过 `peter-evans/create-pull-request` 自动创建一个指向 `x-files/app-upgrade-config` 的 PRDiff 仅包含该文件。
如需本地调试,可执行 `yarn update:upgrade-config --tag v2.1.6 --config ../cs/app-upgrade-config.json`(加 `--dry-run` 仅打印结果)来复现 CI 行为。若需要暂时跳过 GitHub/GitCode Release 页面是否就绪的校验,可在 `--dry-run` 的同时附加 `--skip-release-checks`。不加 `--config` 时默认更新当前工作目录(通常是 main 分支)下的副本,方便文档/审查。

View File

@ -198,8 +198,8 @@ export enum FeedUrl {
}
export enum UpdateConfigUrl {
GITHUB = 'https://raw.githubusercontent.com/CherryHQ/cherry-studio/refs/heads/cs-releases/app-upgrade-config.json',
GITCODE = 'https://raw.gitcode.com/CherryHQ/cherry-studio/raw/cs-releases/app-upgrade-config.json'
GITHUB = 'https://raw.githubusercontent.com/CherryHQ/cherry-studio/refs/heads/x-files/app-upgrade-config/app-upgrade-config.json',
GITCODE = 'https://raw.gitcode.com/CherryHQ/cherry-studio/raw/x-files/app-upgrade-config/app-upgrade-config.json'
}
export enum UpgradeChannel {

View File

@ -81,6 +81,16 @@ export interface DynamicVirtualListProps<T> extends InheritedVirtualizerOptions
* Hide the scrollbar automatically when scrolling is stopped
*/
autoHideScrollbar?: boolean
/**
* Header content to display above the list
*/
header?: React.ReactNode
/**
* Additional CSS class name for the container
*/
className?: string
}
function DynamicVirtualList<T>(props: DynamicVirtualListProps<T>) {
@ -95,6 +105,8 @@ function DynamicVirtualList<T>(props: DynamicVirtualListProps<T>) {
itemContainerStyle,
scrollerStyle,
autoHideScrollbar = false,
header,
className,
...restOptions
} = props
@ -189,7 +201,7 @@ function DynamicVirtualList<T>(props: DynamicVirtualListProps<T>) {
return (
<ScrollContainer
ref={scrollerRef}
className="dynamic-virtual-list"
className={className ? `dynamic-virtual-list ${className}` : 'dynamic-virtual-list'}
role="region"
aria-label="Dynamic Virtual List"
aria-hidden={!showScrollbar}
@ -200,6 +212,7 @@ function DynamicVirtualList<T>(props: DynamicVirtualListProps<T>) {
...(horizontal ? { width: size ?? '100%' } : { height: size ?? '100%' }),
...scrollerStyle
}}>
{header}
<div
style={{
position: 'relative',

View File

@ -1,4 +1,3 @@
import Scrollbar from '@renderer/components/Scrollbar'
import { DynamicVirtualList } from '@renderer/components/VirtualList'
import { useCreateDefaultSession } from '@renderer/hooks/agents/useCreateDefaultSession'
import { useSessions } from '@renderer/hooks/agents/useSessions'
@ -99,39 +98,36 @@ const Sessions: React.FC<SessionsProps> = ({ agentId }) => {
}
return (
<Container className="sessions-tab">
<AddButton onClick={createDefaultSession} disabled={creatingSession} className="-mt-[4px] mb-[6px]">
{t('agent.session.add.title')}
</AddButton>
{/* h-9 */}
<DynamicVirtualList
list={sessions}
estimateSize={() => 9 * 4}
scrollerStyle={{
// FIXME: This component only supports CSSProperties
overflowX: 'hidden'
}}
autoHideScrollbar>
{(session) => (
<SessionItem
key={session.id}
session={session}
agentId={agentId}
onDelete={() => handleDeleteSession(session.id)}
onPress={() => setActiveSessionId(agentId, session.id)}
/>
)}
</DynamicVirtualList>
</Container>
<StyledVirtualList
className="sessions-tab"
list={sessions}
estimateSize={() => 9 * 4}
// FIXME: This component only supports CSSProperties
scrollerStyle={{ overflowX: 'hidden' }}
autoHideScrollbar
header={
<AddButton onClick={createDefaultSession} disabled={creatingSession} className="-mt-[4px] mb-[6px]">
{t('agent.session.add.title')}
</AddButton>
}>
{(session) => (
<SessionItem
key={session.id}
session={session}
agentId={agentId}
onDelete={() => handleDeleteSession(session.id)}
onPress={() => setActiveSessionId(agentId, session.id)}
/>
)}
</StyledVirtualList>
)
}
const Container = styled(Scrollbar)`
const StyledVirtualList = styled(DynamicVirtualList)`
display: flex;
flex-direction: column;
padding: 12px 10px;
overflow-x: hidden;
height: 100%;
`
` as typeof DynamicVirtualList
export default memo(Sessions)