mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-09 14:59:27 +08:00
* refactor: switch workflows from yarn to pnpm
Replace Yarn usage with pnpm in CI workflows to standardize package
management and leverage pnpm's store/cache behavior.
- Use pnpm/action-setup to install pnpm (v) instead of enabling corepack
and preparing Yarn.
- Retrieve pnpm store path and update cache actions to cache the pnpm
store and use pnpm-lock.yaml for cache keys and restores.
- Replace yarn commands with pnpm equivalents across workflows:
install, i18n:sync/translate, format, build:* and tsx invocation.
- Avoid committing lockfile changes by resetting pnpm-lock.yaml instead
of yarn.lock when checking for changes.
- Update install flags: use pnpm install --frozen-lockfile / --install
semantics where appropriate.
These changes unify dependency tooling, improve caching correctness,
and ensure CI uses pnpm-specific lockfile and cache paths.
* build: switch pre-commit hook to pnpm lint-staged
Update .husky/pre-commit to run pnpm lint-staged instead of yarn.
This aligns the pre-commit hook with the project's package manager
and ensures lint-staged runs using pnpm's environment and caching.
* chore(ci): remove pinned pnpm version from GH Action steps
Remove the explicit `with: version: 9` lines from multiple GitHub Actions workflows
(auto-i18n.yml, nightly-build.yml, pr-ci.yml, update-app-upgrade-config.yml,
sync-to-gitcode.yml, release.yml). The workflows still call `pnpm/action-setup@v4`
but no longer hardcode a pnpm version.
This simplifies maintenance and allows the action to resolve an appropriate pnpm
version (or use its default) without needing updates whenever the pinned
version becomes outdated. It reduces churn when bumping pnpm across CI configs
and prevents accidental pin drift between workflow files.
* build: Update pnpm to 10.27.0 and add onlyBuiltDependencies config
* Update @cherrystudio/openai to 6.15.0 and consolidate overrides
* Add @langchain/core to overrides
* Add override for openai-compatible 1.0.27
* build: optimize pnpm config and add missing dependencies
- Comment out shamefully-hoist in .npmrc for better pnpm compatibility
- Add React-related packages to optimizeDeps in electron.vite.config.ts
- Add missing peer dependencies and packages that were previously hoisted
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* build: refine pnpm configuration and dependency management
- Simplify .npmrc to only essential electron mirror config
- Move platform-specific dependencies to devDependencies
- Pin sharp version to 0.34.3 for consistency
- Update sharp-libvips versions to 1.2.4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* reduce app size
* format
* build: remove unnecessary disableOxcRecommendation option from react plugin configuration
* docs: Replace yarn commands with pnpm in documentation and scripts
* Revert "build: optimize pnpm config and add missing dependencies"
This reverts commit acffad31f8.
* build: import dependencies from yarn.lock
* build: Add some phantom dependencies and reorganize dependencies
* build: Keep consistent by removing types of semver
It's not in the previous package.json
* build: Add some phantom dependencies
Keep same version with yarn.lock
* build: Add form-data dependency version 4.0.4
* Add chalk dependency
* build: downgrade some dependencies
Reference: .yarn-state-copy.yml. These phantom dependencies should use top-level package of that version in node_modules
* build: Add phantom dependencies
* build: pin tiptap dependencies to exact versions
Ensure consistent dependency resolution by removing caret ranges and pinning all @tiptap packages to exact version 3.2.0
* chore: pin embedjs dependencies to exact versions
* build: pin @modelcontextprotocol/sdk to exact version 1.23.0
Remove caret from version specifier to prevent automatic upgrades and ensure consistent dependencies
* chore: update @types/node dependency to 22.17.2
Update package.json and pnpm-lock.yaml to use @types/node version 22.17.2 instead of 22.19.3 to maintain consistency across dependencies
* build: move some dependencies to dev deps and pin dependency versions to exact numbers
Remove caret (^) from version ranges to ensure consistent dependency resolution across environments
* chore: move dependencies from prod to dev and update lockfile
Move @ant-design/icons, chalk, form-data, and open from dependencies to devDependencies
Update pnpm-lock.yaml to reflect dependency changes
* build: update package dependencies
- Add new dependencies: md5, @libsql/win32-x64-msvc, @strongtz/win32-arm64-msvc, bonjour-service, emoji-picker-element-data, gray-matter, js-yaml
- Remove redundant dependencies from devDependencies
* build: add cors, katex and pako dependencies
add new dependencies to support cross-origin requests, mathematical notation rendering and data compression
* move some js deps to dev deps
* test: update snapshot tests for Spinner and InputEmbeddingDimension
* chore: exclude .zed directory from biome formatting
* Update @ai-sdk/openai-compatible patch hash
* chore: update @kangfenmao/keyv-storage to version 0.1.3 in package.json and pnpm-lock.yaml
---------
Co-authored-by: icarus <eurfelux@gmail.com>
Co-authored-by: beyondkmp <beyondkmp@gmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: kangfenmao <kangfenmao@qq.com>
431 lines
18 KiB
Markdown
431 lines
18 KiB
Markdown
# Update Configuration System Design Document
|
||
|
||
## Background
|
||
|
||
Currently, AppUpdater directly queries the GitHub API to retrieve beta and rc update information. To support users in China, we need to fetch a static JSON configuration file from GitHub/GitCode based on IP geolocation, which contains update URLs for all channels.
|
||
|
||
## Design Goals
|
||
|
||
1. Support different configuration sources based on IP geolocation (GitHub/GitCode)
|
||
2. Support version compatibility control (e.g., users below v1.x must upgrade to v1.7.0 before upgrading to v2.0)
|
||
3. Easy to extend, supporting future multi-major-version upgrade paths (v1.6 → v1.7 → v2.0 → v2.8 → v3.0)
|
||
4. Maintain compatibility with existing electron-updater mechanism
|
||
|
||
## Current Version Strategy
|
||
|
||
- **v1.7.x** is the last version of the 1.x series
|
||
- Users **below v1.7.0** must first upgrade to v1.7.0 (or higher 1.7.x version)
|
||
- Users **v1.7.0 and above** can directly upgrade to v2.x.x
|
||
|
||
## Automation Workflow
|
||
|
||
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
|
||
|
||
- **Release events (`release: released/prereleased`)**
|
||
- Draft releases are ignored.
|
||
- When GitHub marks the release as _prerelease_, the tag must include `-beta`/`-rc` (with optional numeric suffix). Otherwise the workflow exits early.
|
||
- When GitHub marks the release as stable, the tag must match the latest release returned by the GitHub API. This prevents out-of-order updates when publishing historical tags.
|
||
- If the guard clauses pass, the version is tagged as `latest` or `beta/rc` based on its semantic suffix and propagated to the script through the `IS_PRERELEASE` flag.
|
||
- **Manual dispatch (`workflow_dispatch`)**
|
||
- Required input: `tag` (e.g., `v2.0.1`). Optional input: `is_prerelease` (defaults to `false`).
|
||
- When `is_prerelease=true`, the tag must carry a beta/rc suffix, mirroring the automatic validation.
|
||
- Manual runs still download the latest release metadata so that the workflow knows whether the tag represents the newest stable version (for documentation inside the PR body).
|
||
|
||
### 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 `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 pnpm dependencies are installed inside `main/`.
|
||
4. **Run the update script** – `pnpm 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 `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 `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/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 `x-files/app-upgrade-config` branch. The client automatically selects the optimal mirror based on IP geolocation.
|
||
|
||
### Configuration Structure (Current Implementation)
|
||
|
||
```json
|
||
{
|
||
"lastUpdated": "2025-01-05T00:00:00Z",
|
||
"versions": {
|
||
"1.6.7": {
|
||
"minCompatibleVersion": "1.0.0",
|
||
"description": "Last stable v1.7.x release - required intermediate version for users below v1.7",
|
||
"channels": {
|
||
"latest": {
|
||
"version": "1.6.7",
|
||
"feedUrls": {
|
||
"github": "https://github.com/CherryHQ/cherry-studio/releases/download/v1.6.7",
|
||
"gitcode": "https://gitcode.com/CherryHQ/cherry-studio/releases/download/v1.6.7"
|
||
}
|
||
},
|
||
"rc": {
|
||
"version": "1.6.0-rc.5",
|
||
"feedUrls": {
|
||
"github": "https://github.com/CherryHQ/cherry-studio/releases/download/v1.6.0-rc.5",
|
||
"gitcode": "https://github.com/CherryHQ/cherry-studio/releases/download/v1.6.0-rc.5"
|
||
}
|
||
},
|
||
"beta": {
|
||
"version": "1.6.7-beta.3",
|
||
"feedUrls": {
|
||
"github": "https://github.com/CherryHQ/cherry-studio/releases/download/v1.7.0-beta.3",
|
||
"gitcode": "https://github.com/CherryHQ/cherry-studio/releases/download/v1.7.0-beta.3"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"2.0.0": {
|
||
"minCompatibleVersion": "1.7.0",
|
||
"description": "Major release v2.0 - required intermediate version for v2.x upgrades",
|
||
"channels": {
|
||
"latest": null,
|
||
"rc": null,
|
||
"beta": null
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Future Extension Example
|
||
|
||
When releasing v3.0, if users need to first upgrade to v2.8, you can add:
|
||
|
||
```json
|
||
{
|
||
"2.8.0": {
|
||
"minCompatibleVersion": "2.0.0",
|
||
"description": "Stable v2.8 - required for v3 upgrade",
|
||
"channels": {
|
||
"latest": {
|
||
"version": "2.8.0",
|
||
"feedUrls": {
|
||
"github": "https://github.com/CherryHQ/cherry-studio/releases/download/v2.8.0",
|
||
"gitcode": "https://gitcode.com/CherryHQ/cherry-studio/releases/download/v2.8.0"
|
||
}
|
||
},
|
||
"rc": null,
|
||
"beta": null
|
||
}
|
||
},
|
||
"3.0.0": {
|
||
"minCompatibleVersion": "2.8.0",
|
||
"description": "Major release v3.0",
|
||
"channels": {
|
||
"latest": {
|
||
"version": "3.0.0",
|
||
"feedUrls": {
|
||
"github": "https://github.com/CherryHQ/cherry-studio/releases/latest",
|
||
"gitcode": "https://gitcode.com/CherryHQ/cherry-studio/releases/latest"
|
||
}
|
||
},
|
||
"rc": {
|
||
"version": "3.0.0-rc.1",
|
||
"feedUrls": {
|
||
"github": "https://github.com/CherryHQ/cherry-studio/releases/download/v3.0.0-rc.1",
|
||
"gitcode": "https://gitcode.com/CherryHQ/cherry-studio/releases/download/v3.0.0-rc.1"
|
||
}
|
||
},
|
||
"beta": null
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Field Descriptions
|
||
|
||
- `lastUpdated`: Last update time of the configuration file (ISO 8601 format)
|
||
- `versions`: Version configuration object, key is the version number, sorted by semantic versioning
|
||
- `minCompatibleVersion`: Minimum compatible version that can upgrade to this version
|
||
- `description`: Version description
|
||
- `channels`: Update channel configuration
|
||
- `latest`: Stable release channel
|
||
- `rc`: Release Candidate channel
|
||
- `beta`: Beta testing channel
|
||
- Each channel contains:
|
||
- `version`: Version number for this channel
|
||
- `feedUrls`: Multi-mirror URL configuration
|
||
- `github`: electron-updater feed URL for GitHub mirror
|
||
- `gitcode`: electron-updater feed URL for GitCode mirror
|
||
- `metadata`: Stable mapping info for automation
|
||
- `segmentId`: ID from `config/app-upgrade-segments.json`
|
||
- `segmentType`: Optional flag (`legacy` | `breaking` | `latest`) for documentation/debugging
|
||
|
||
## TypeScript Type Definitions
|
||
|
||
```typescript
|
||
// Mirror enum
|
||
enum UpdateMirror {
|
||
GITHUB = 'github',
|
||
GITCODE = 'gitcode'
|
||
}
|
||
|
||
interface UpdateConfig {
|
||
lastUpdated: string
|
||
versions: {
|
||
[versionKey: string]: VersionConfig
|
||
}
|
||
}
|
||
|
||
interface VersionConfig {
|
||
minCompatibleVersion: string
|
||
description: string
|
||
channels: {
|
||
latest: ChannelConfig | null
|
||
rc: ChannelConfig | null
|
||
beta: ChannelConfig | null
|
||
}
|
||
metadata?: {
|
||
segmentId: string
|
||
segmentType?: 'legacy' | 'breaking' | 'latest'
|
||
}
|
||
}
|
||
|
||
interface ChannelConfig {
|
||
version: string
|
||
feedUrls: Record<UpdateMirror, string>
|
||
// Equivalent to:
|
||
// feedUrls: {
|
||
// github: string
|
||
// gitcode: string
|
||
// }
|
||
}
|
||
```
|
||
|
||
## Segment Metadata & Breaking Markers
|
||
|
||
- **Segment definitions** now live in `config/app-upgrade-segments.json`. Each segment describes a semantic-version range (or exact matches) plus metadata such as `segmentId`, `segmentType`, `minCompatibleVersion`, and per-channel feed URL templates.
|
||
- Each entry under `versions` carries a `metadata.segmentId`. This acts as the stable key that scripts use to decide which slot to update, even if the actual semantic version string changes.
|
||
- Mark major upgrade gateways (e.g., `2.0.0`) by giving the related segment a `segmentType: "breaking"` and (optionally) `lockedVersion`. This prevents automation from accidentally moving that entry when other 2.x builds ship.
|
||
- Adding another breaking hop (e.g., `3.0.0`) only requires defining a new segment in the JSON file; the automation will pick it up on the next run.
|
||
|
||
## Automation Workflow
|
||
|
||
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 `x-files/app-upgrade-config` branch (where the config is hosted).
|
||
2. Runs `pnpm 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 `pnpm 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 aren't published yet). Running without `--config` continues to update the copy in your current working directory (main branch) for documentation purposes.
|
||
|
||
## Version Matching Logic
|
||
|
||
### Algorithm Flow
|
||
|
||
1. Get user's current version (`currentVersion`) and requested channel (`requestedChannel`)
|
||
2. Get all version numbers from configuration file, sort in descending order by semantic versioning
|
||
3. Iterate through the sorted version list:
|
||
- Check if `currentVersion >= minCompatibleVersion`
|
||
- Check if the requested `channel` exists and is not `null`
|
||
- If conditions are met, return the channel configuration
|
||
4. If no matching version is found, return `null`
|
||
|
||
### Pseudocode Implementation
|
||
|
||
```typescript
|
||
function findCompatibleVersion(
|
||
currentVersion: string,
|
||
requestedChannel: UpgradeChannel,
|
||
config: UpdateConfig
|
||
): ChannelConfig | null {
|
||
// Get all version numbers and sort in descending order
|
||
const versions = Object.keys(config.versions).sort(semver.rcompare)
|
||
|
||
for (const versionKey of versions) {
|
||
const versionConfig = config.versions[versionKey]
|
||
const channelConfig = versionConfig.channels[requestedChannel]
|
||
|
||
// Check version compatibility and channel availability
|
||
if (
|
||
semver.gte(currentVersion, versionConfig.minCompatibleVersion) &&
|
||
channelConfig !== null
|
||
) {
|
||
return channelConfig
|
||
}
|
||
}
|
||
|
||
return null // No compatible version found
|
||
}
|
||
```
|
||
|
||
## Upgrade Path Examples
|
||
|
||
### Scenario 1: v1.6.5 User Upgrade (Below 1.7)
|
||
|
||
- **Current Version**: 1.6.5
|
||
- **Requested Channel**: latest
|
||
- **Match Result**: 1.7.0
|
||
- **Reason**: 1.6.5 >= 0.0.0 (satisfies 1.7.0's minCompatibleVersion), but doesn't satisfy 2.0.0's minCompatibleVersion (1.7.0)
|
||
- **Action**: Prompt user to upgrade to 1.7.0, which is the required intermediate version for v2.x upgrade
|
||
|
||
### Scenario 2: v1.6.5 User Requests rc/beta
|
||
|
||
- **Current Version**: 1.6.5
|
||
- **Requested Channel**: rc or beta
|
||
- **Match Result**: 1.7.0 (latest)
|
||
- **Reason**: 1.7.0 version doesn't provide rc/beta channels (values are null)
|
||
- **Action**: Upgrade to 1.7.0 stable version
|
||
|
||
### Scenario 3: v1.7.0 User Upgrades to Latest
|
||
|
||
- **Current Version**: 1.7.0
|
||
- **Requested Channel**: latest
|
||
- **Match Result**: 2.0.0
|
||
- **Reason**: 1.7.0 >= 1.7.0 (satisfies 2.0.0's minCompatibleVersion)
|
||
- **Action**: Directly upgrade to 2.0.0 (current latest stable version)
|
||
|
||
### Scenario 4: v1.7.2 User Upgrades to RC Version
|
||
|
||
- **Current Version**: 1.7.2
|
||
- **Requested Channel**: rc
|
||
- **Match Result**: 2.0.0-rc.1
|
||
- **Reason**: 1.7.2 >= 1.7.0 (satisfies 2.0.0's minCompatibleVersion), and rc channel exists
|
||
- **Action**: Upgrade to 2.0.0-rc.1
|
||
|
||
### Scenario 5: v1.7.0 User Upgrades to Beta Version
|
||
|
||
- **Current Version**: 1.7.0
|
||
- **Requested Channel**: beta
|
||
- **Match Result**: 2.0.0-beta.1
|
||
- **Reason**: 1.7.0 >= 1.7.0, and beta channel exists
|
||
- **Action**: Upgrade to 2.0.0-beta.1
|
||
|
||
### Scenario 6: v2.5.0 User Upgrade (Future)
|
||
|
||
Assuming v2.8.0 and v3.0.0 configurations have been added:
|
||
- **Current Version**: 2.5.0
|
||
- **Requested Channel**: latest
|
||
- **Match Result**: 2.8.0
|
||
- **Reason**: 2.5.0 >= 2.0.0 (satisfies 2.8.0's minCompatibleVersion), but doesn't satisfy 3.0.0's requirement
|
||
- **Action**: Prompt user to upgrade to 2.8.0, which is the required intermediate version for v3.x upgrade
|
||
|
||
## Code Changes
|
||
|
||
### Main Modifications
|
||
|
||
1. **New Methods**
|
||
- `_fetchUpdateConfig(ipCountry: string): Promise<UpdateConfig | null>` - Fetch configuration file based on IP
|
||
- `_findCompatibleChannel(currentVersion: string, channel: UpgradeChannel, config: UpdateConfig): ChannelConfig | null` - Find compatible channel configuration
|
||
|
||
2. **Modified Methods**
|
||
- `_getReleaseVersionFromGithub()` → Remove or refactor to `_getChannelFeedUrl()`
|
||
- `_setFeedUrl()` - Use new configuration system to replace existing logic
|
||
|
||
3. **New Type Definitions**
|
||
- `UpdateConfig`
|
||
- `VersionConfig`
|
||
- `ChannelConfig`
|
||
|
||
### Mirror Selection Logic
|
||
|
||
The client automatically selects the optimal mirror based on IP geolocation:
|
||
|
||
```typescript
|
||
private async _setFeedUrl() {
|
||
const currentVersion = app.getVersion()
|
||
const testPlan = configManager.getTestPlan()
|
||
const requestedChannel = testPlan ? this._getTestChannel() : UpgradeChannel.LATEST
|
||
|
||
// Determine mirror based on IP country
|
||
const ipCountry = await getIpCountry()
|
||
const mirror = ipCountry.toLowerCase() === 'cn' ? 'gitcode' : 'github'
|
||
|
||
// Fetch update config
|
||
const config = await this._fetchUpdateConfig(mirror)
|
||
|
||
if (config) {
|
||
const channelConfig = this._findCompatibleChannel(currentVersion, requestedChannel, config)
|
||
if (channelConfig) {
|
||
// Select feed URL from the corresponding mirror
|
||
const feedUrl = channelConfig.feedUrls[mirror]
|
||
this._setChannel(requestedChannel, feedUrl)
|
||
return
|
||
}
|
||
}
|
||
|
||
// Fallback logic
|
||
const defaultFeedUrl = mirror === 'gitcode'
|
||
? FeedUrl.PRODUCTION
|
||
: FeedUrl.GITHUB_LATEST
|
||
this._setChannel(UpgradeChannel.LATEST, defaultFeedUrl)
|
||
}
|
||
|
||
private async _fetchUpdateConfig(mirror: 'github' | 'gitcode'): Promise<UpdateConfig | null> {
|
||
const configUrl = mirror === 'gitcode'
|
||
? UpdateConfigUrl.GITCODE
|
||
: UpdateConfigUrl.GITHUB
|
||
|
||
try {
|
||
const response = await net.fetch(configUrl, {
|
||
headers: {
|
||
'User-Agent': generateUserAgent(),
|
||
'Accept': 'application/json',
|
||
'X-Client-Id': configManager.getClientId()
|
||
}
|
||
})
|
||
return await response.json() as UpdateConfig
|
||
} catch (error) {
|
||
logger.error('Failed to fetch update config:', error)
|
||
return null
|
||
}
|
||
}
|
||
```
|
||
|
||
## Fallback and Error Handling Strategy
|
||
|
||
1. **Configuration file fetch failure**: Log error, return current version, don't offer updates
|
||
2. **No matching version**: Notify user that current version doesn't support automatic upgrade
|
||
3. **Network exception**: Cache last successfully fetched configuration (optional)
|
||
|
||
## GitHub Release Requirements
|
||
|
||
To support intermediate version upgrades, the following files need to be retained:
|
||
|
||
- **v1.7.0 release** and its latest*.yml files (as upgrade target for users below v1.7)
|
||
- Future intermediate versions (e.g., v2.8.0) need to retain corresponding release and latest*.yml files
|
||
- Complete installation packages for each version
|
||
|
||
### Currently Required Releases
|
||
|
||
| Version | Purpose | Must Retain |
|
||
|---------|---------|-------------|
|
||
| v1.7.0 | Upgrade target for users below 1.7 | ✅ Yes |
|
||
| v2.0.0-rc.1 | RC testing channel | ❌ Optional |
|
||
| v2.0.0-beta.1 | Beta testing channel | ❌ Optional |
|
||
| latest | Latest stable version (automatic) | ✅ Yes |
|
||
|
||
## Advantages
|
||
|
||
1. **Flexibility**: Supports arbitrarily complex upgrade paths
|
||
2. **Extensibility**: Adding new versions only requires adding new entries to the configuration file
|
||
3. **Maintainability**: Configuration is separated from code, allowing upgrade strategy adjustments without releasing new versions
|
||
4. **Multi-source support**: Automatically selects optimal configuration source based on geolocation
|
||
5. **Version control**: Enforces intermediate version upgrades, ensuring data migration and compatibility
|
||
|
||
## Future Extensions
|
||
|
||
- Support more granular version range control (e.g., `>=1.5.0 <1.8.0`)
|
||
- Support multi-step upgrade path hints (e.g., notify user needs 1.5 → 1.8 → 2.0)
|
||
- Support A/B testing and gradual rollout
|
||
- Support local caching and expiration strategy for configuration files
|