Conversation
- 扩展分支限制,添加对 dev 和 beta 分支的支持 - 文档同步任务触发条件从 PR 合并调整为针对 beta 和 master 分支的 push - 调整 fetch-depth 为 0,并新增 `base_branch` 参数以支持分支差异分析 - 修订文档同步任务的提示与输出格式,优化 push 变更处理
|
Junie is failed! Details: Check job logs for more details |
- 在更新服务中引入 `IncludePrerelease` 常量,以控制是否包含预发布版本 - 更新 UI 文本,提示默认仅检查稳定版
There was a problem hiding this comment.
Pull request overview
This PR updates the repo’s CI/CD to support a dev → beta → master branching model, adds contributor documentation, and introduces automated beta/stable release pipelines driven by VERSION.md.
Changes:
- Added new CI workflow and expanded existing automation triggers to run across
dev,beta, andmaster. - Implemented automated beta releases (
beta-release.yml) and stable releases (release.yml) with generated release notes fromVERSION.md. - Added development/contribution documentation (
CONTRIBUTING.md) and linked it from the README.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| VERSION.md | Introduces a structured, release-driven version/changelog source. |
| README.md | Links to the new contribution/development guide. |
| PrismCore.csproj | Sets a default Platform to x64 when unspecified (CI/build stabilization). |
| CONTRIBUTING.md | Documents branching, versioning (VERSION.md format), and release rules. |
| .github/workflows/release.yml | New master-only stable release pipeline + dev changelog cleanup job. |
| .github/workflows/beta-release.yml | New beta-only prerelease pipeline. |
| .github/workflows/ci.yml | New matrix CI build for dev/beta/master pushes and PRs. |
| .github/workflows/junie.yml | Adjusts triggers and switches docs-sync to run on push to beta/master. |
| .github/scripts/versionmd.sh | Adds parsing/extraction utilities for VERSION.md and note generation. |
| .github/scripts/prepare-master.sh | Generates stable release metadata/notes and release gating logic. |
| .github/scripts/prepare-beta.sh | Generates beta tag/version and delta release notes vs prior beta tag. |
| .github/scripts/cleanup-dev.sh | Automates removing released entries from dev’s VERSION.md after stable release. |
| .github/release-body-footer.md | Provides a shared footer appended to release notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| - name: 下载安装包 | ||
| uses: actions/download-artifact@v4 | ||
| with: |
There was a problem hiding this comment.
当前这一步会下载本次工作流的“所有” artifacts(包括 prepare 阶段上传的 release-notes / released-entries)。由于后面发布步骤使用 files: artifacts/*,这些说明文件也会被当作 Release 资产上传,导致 Release 里出现多余的 .md/.txt 资产。建议仅下载打包产物(例如使用 download-artifact 的 pattern: release-* / 指定 name,或把说明 artifacts 下载到单独目录并在 files 中排除)。
| with: | |
| with: | |
| pattern: release-* |
| - name: 下载安装包 | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| path: artifacts | ||
| merge-multiple: true | ||
|
|
There was a problem hiding this comment.
这里会下载本次工作流的“所有” artifacts(包含 prepare 阶段上传的 beta-release-notes)。后续 files: artifacts/* 会把这些说明 .md 也作为 Release 资产上传,产生多余资产。建议只下载打包产物(例如 pattern: beta-* / 指定 name),把发布说明单独下载到 notes 目录即可。
- 将模型配置从硬编码改为使用环境变量 `JUNIE_MODEL` - 简化和统一模型参数管理,提升工作流可维护性
.github/scripts/versionmd.sh
Outdated
| return 1 | ||
| fi | ||
|
|
||
| echo "$version_line" | sed -E 's/^Version:[[:space:]]*//' |
There was a problem hiding this comment.
| echo "$version_line" | sed -E 's/^Version:[[:space:]]*//' | |
| echo "$version_line" | sed -E 's/^Version:[[:space:]]*//; s/[[:space:]]*$//' |
The grep allows trailing whitespace ([[:space:]]*$) but sed only strips the prefix, leaving potential trailing spaces in the version string used for tag names.
.github/scripts/versionmd.sh
Outdated
| if [[ "$line" =~ ^##[[:space:]]+待发布 ]]; then | ||
| in_unreleased="true" | ||
| current_section="" | ||
| continue | ||
| fi | ||
|
|
||
| if [[ "$line" =~ ^##[[:space:]]+ ]] && [[ "$line" != "## 待发布" ]]; then |
There was a problem hiding this comment.
| if [[ "$line" =~ ^##[[:space:]]+待发布 ]]; then | |
| in_unreleased="true" | |
| current_section="" | |
| continue | |
| fi | |
| if [[ "$line" =~ ^##[[:space:]]+ ]] && [[ "$line" != "## 待发布" ]]; then | |
| if [[ "$line" =~ ^##[[:space:]]+待发布[[:space:]]*$ ]]; then | |
| in_unreleased="true" | |
| current_section="" | |
| continue | |
| fi | |
| if [[ "$line" =~ ^##[[:space:]]+ ]] && [[ "$line" != "## 待发布" ]]; then |
The first regex is a prefix match (^##[[:space:]]+待发布) — a heading like ## 待发布xxx would incorrectly enter the unreleased block. Anchor it to end-of-string for safety.
- 为 release 和 beta 工作流新增 `pattern` 参数,分别支持匹配 `release-win-*` 和 `beta-win-*` 文件 - 修正 `versionmd.sh` 中版本号和未发布条
.github/scripts/versionmd.sh
Outdated
| case "$line" in | ||
| "### 新增") | ||
| current_section="新增" | ||
| ;; | ||
| "### 变更") | ||
| current_section="变更" | ||
| ;; | ||
| "### 修复") | ||
| current_section="修复" | ||
| ;; | ||
| "### 其他") | ||
| current_section="其他" | ||
| ;; |
There was a problem hiding this comment.
Duplicate logic: section names are hardcoded here but already defined in VERSIONMD_SECTIONS. If a section is added/renamed in the array, this case will silently miss it. Consider iterating VERSIONMD_SECTIONS dynamically instead, e.g.:
local matched=false
for s in "${VERSIONMD_SECTIONS[@]}"; do
if [[ "$line" == "### $s" ]]; then
current_section="$s"
matched=true
break
fi
done
if [[ "$matched" == "true" ]]; then continue; fi| dev_entries_file="$(mktemp)" | ||
| extract_entries VERSION.md "$dev_entries_file" | ||
|
|
||
| remaining_entries_file="$(mktemp)" | ||
| multiset_subtract "$dev_entries_file" "$released_entries_file" "$remaining_entries_file" |
There was a problem hiding this comment.
Temp files from mktemp are never cleaned up. Consider adding a trap at the top of the script for cleanup on exit, consistent with good shell hygiene:
trap 'rm -f "$dev_entries_file" "$remaining_entries_file"' EXIT| current_entries_file="$(mktemp)" | ||
| extract_entries "$VERSION_FILE" "$current_entries_file" | ||
|
|
||
| previous_entries_file="$(mktemp)" |
There was a problem hiding this comment.
Same as in cleanup-dev.sh: temp files (current_entries_file, previous_entries_file, previous_version_file) are never cleaned up. Add a trap ... EXIT for cleanup.
- 为 `cleanup-dev.sh` 和 `prepare-beta.sh` 脚本新增 `trap` 机制,在退出时清理临时文件 - 优化 `versionmd.sh` 的 section 解析逻辑,提升代码可读性和灵活性
.github/scripts/versionmd.sh
Outdated
|
|
||
| local section | ||
| for section in "${VERSIONMD_SECTIONS[@]}"; do | ||
| mapfile -t section_items < <(awk -F '\t' -v target="$section" '$1 == target { print $2 }' "$entries_file") |
There was a problem hiding this comment.
| mapfile -t section_items < <(awk -F '\t' -v target="$section" '$1 == target { print $2 }' "$entries_file") | |
| mapfile -t section_items < <(awk -F '\t' -v target="$section" '$1 == target { sub(/^[^\t]*\t/, ""); print }' "$entries_file") |
print $2 only prints the second tab-delimited field. If an entry contains a tab, it gets truncated. Use { sub(/^[^\t]*\t/, ""); print } instead. Same issue in write_versionmd at line 140.
.github/scripts/versionmd.sh
Outdated
| local section | ||
| for section in "${VERSIONMD_SECTIONS[@]}"; do | ||
| echo "### $section" | ||
| awk -F '\t' -v target="$section" '$1 == target { print "- " $2 }' "$entries_file" |
There was a problem hiding this comment.
| awk -F '\t' -v target="$section" '$1 == target { print "- " $2 }' "$entries_file" | |
| awk -F '\t' -v target="$section" '$1 == target { sub(/^[^\t]*\t/, ""); print "- " $0 }' "$entries_file" |
Same print $2 truncation issue as in write_notes. Use sub to strip the section prefix instead.
Views/UpdatePage.xaml
Outdated
| <TextBlock Text="检查并安装最新版本(默认仅稳定版,不接收预发布)" | ||
| Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> |
There was a problem hiding this comment.
| <TextBlock Text="检查并安装最新版本(默认仅稳定版,不接收预发布)" | |
| Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> | |
| <TextBlock Text="检查并安装最新版本" | |
| Foreground="{ThemeResource TextFillColorSecondaryBrush}"/> |
This implementation detail ("不接收预发布") is user-facing UI text. Users don't need to know about prerelease channels; keep it concise like the original.
- 修正 `versionmd.sh` 中 section 解析逻辑,移除多余前缀以提升灵活性 - 更新 `UpdatePage.xaml` 文本描述,简化更新信息显示内容
| push: | ||
| branches: [master] | ||
| branches: [dev, beta, master] |
There was a problem hiding this comment.
The push trigger now matches dev, beta, and master. Combined with docs-sync (filters beta/master) and fix-ci (triggers on workflow_run), pushes to dev will start this workflow but no job condition matches push to dev, resulting in a no-op workflow run. Consider restricting the push trigger to branches: [beta, master] to avoid unnecessary workflow runs.
Helpers/UpdateService.cs
Outdated
| { | ||
| private const string RepoUrl = "https://github.com/WSXYT/PrismCore"; | ||
| private const string ProxyBaseUrl = "https://gemini.435535.xyz"; | ||
| private const bool IncludePrerelease = false; // 默认仅稳定版更新,不接收 beta/预发布 |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
- 新增更新通道设置(稳定版/预发布版)并调整默认值逻辑 - 重构 `UpdateService` 支持动态通道切换并优化实现 - 更新 `AppSettings` 增加通道相关配置项 - 更新 UI 与视图模型以支持通道选择功能 - 修正部分逻辑默认值与描述
App.xaml.cs
Outdated
| var settings = AppSettings.Instance; | ||
| var recommendedChannel = UpdateService.GetRecommendedChannel(); | ||
| if (settings.LastInstalledChannel != recommendedChannel) | ||
| { | ||
| settings.UpdateChannel = recommendedChannel; | ||
| settings.LastInstalledChannel = recommendedChannel; | ||
| } |
There was a problem hiding this comment.
Duplicate logic: This channel-recommendation block is identical to the one in UpdateViewModel constructor. Extract it into a shared method (e.g., on UpdateService or AppSettings) to avoid divergence.
Helpers/UpdateService.cs
Outdated
| private static bool IsCurrentBuildPrerelease() | ||
| { | ||
| try | ||
| { | ||
| var mgr = new UpdateManager(new GithubSource(RepoUrl, null, false)); | ||
| if (mgr.IsInstalled && mgr.CurrentVersion is { } v) | ||
| return IsPrereleaseVersion(v.ToString()); | ||
| } | ||
| catch { /* 非 Velopack 安装环境 */ } |
There was a problem hiding this comment.
new UpdateManager(new GithubSource(...)) is repeated in GetCurrentVersion, IsVelopackInstalled, and now here. Each call likely makes a network/disk probe. Consider extracting a shared helper or caching the result to reduce duplication and overhead.
Helpers/UpdateService.cs
Outdated
| public UpdateService(bool includePrerelease) | ||
| { | ||
| _includePrerelease = includePrerelease; |
There was a problem hiding this comment.
The _includePrerelease field is stored but only used inside the constructor to build _sources. It can be removed; just use the parameter directly in the collection initializer.
- 重构 `UpdateService`,简化安装版本检测与通道推断逻辑 - 新增 `ResolveAndPersistRecommendedChannel` 方法统一通道设置流程 - 调整 `App.xaml.cs` 与视图模型以适配通道设置优化 - 更新工作流注释内容,提升可读性
| partial void OnSelectedUpdateChannelChanged(int value) | ||
| { | ||
| _settings.UpdateChannel = value; | ||
| } |
There was a problem hiding this comment.
| partial void OnSelectedUpdateChannelChanged(int value) | |
| { | |
| _settings.UpdateChannel = value; | |
| } | |
| partial void OnSelectedUpdateChannelChanged(int value) | |
| { | |
| _settings.UpdateChannel = value; | |
| _cachedUpdate = null; | |
| _activeUpdateService = null; | |
| LatestVersion = "未检查"; | |
| IsUpToDate = false; | |
| StatusMessage = ""; | |
| OnPropertyChanged(nameof(HasUpdate)); | |
| } |
When the user switches channel after a check, _cachedUpdate and _activeUpdateService still reference the old channel. The "Update" button could apply a wrong-channel update. Invalidate cached state here.
Helpers/UpdateService.cs
Outdated
| private static bool IsInstalledByVelopack(out string version) | ||
| { | ||
| version = string.Empty; | ||
| try | ||
| { | ||
| var mgr = CreateProbeManager(); | ||
| if (!mgr.IsInstalled) return false; | ||
| if (mgr.CurrentVersion is { } v) version = v.ToString(); | ||
| return true; | ||
| } | ||
| catch { /* 非 Velopack 安装环境 */ } |
There was a problem hiding this comment.
Returns true with version = "" when mgr.IsInstalled is true but CurrentVersion is null. Callers like IsCurrentBuildPrerelease then pass an empty string to IsPrereleaseVersion, silently returning false. Consider returning false when version is unavailable, or documenting this contract clearly.
| if (IsUpdating || _cachedUpdate == null || _activeUpdateService == null) return; | ||
| IsUpdating = true; | ||
| UpdateProgress = 0; | ||
| StatusMessage = "正在下载更新..."; | ||
|
|
||
| try | ||
| { | ||
| await _updateService.DownloadAndApplyAsync(_cachedUpdate, progress => | ||
| await _activeUpdateService.DownloadAndApplyAsync(_cachedUpdate, progress => |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| // 更新模式:0=不检查, 1=仅检查, 2=自动安装 | ||
| public int UpdateMode { get => Get("update_mode", 0); set => Set("update_mode", value); } | ||
| // 更新模式:0=不检查, 1=仅检查, 2=自动安装(默认) | ||
| public int UpdateMode { get => Get("update_mode", 2); set => Set("update_mode", value); } |
There was a problem hiding this comment.
Bug: The default UpdateMode is now 2 (auto-install), causing the application to update and restart on launch without user consent for new or unconfigured existing users.
Severity: HIGH
Suggested Fix
Revert the default value for UpdateMode in Models/AppSettings.cs back to 0 to preserve the previous opt-in behavior. Alternatively, implement migration logic to handle the setting for existing users and consider prompting users before an automatic restart.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: Models/AppSettings.cs#L117
Potential issue: The default value for the `UpdateMode` setting has been changed from
`0` (do not check for updates) to `2` (check and auto-install). For new users or
existing users who have never configured this setting, the application will now
automatically download and apply updates on startup. This process includes an
application restart, which occurs without any user prompt or confirmation, potentially
interrupting the user's workflow.
- 重构 `UpdateService`,分离版本检测与安装检查逻辑 - 更新 `UpdateViewModel` 以支持动态通道切换时的状态清理 - 优化更新检查流程,避免过期检查结果影响
| public static bool IsVelopackInstalled | ||
| { | ||
| get | ||
| { | ||
| try | ||
| { | ||
| var mgr = new UpdateManager(new GithubSource(RepoUrl, null, true)); | ||
| return mgr.IsInstalled; | ||
| } | ||
| catch { return false; } | ||
| } | ||
| get => IsInstalledByVelopack(); | ||
| } |
There was a problem hiding this comment.
This property creates a new UpdateManager on every access. Consider converting to a method (IsVelopackInstalled()) to signal the cost to callers, consistent with how GetCurrentVersion() is a method.
| var settings = AppSettings.Instance; | ||
| var activeChannel = UpdateService.ResolveAndPersistRecommendedChannel(settings); | ||
| var svc = new UpdateService(activeChannel == 1); |
There was a problem hiding this comment.
ResolveAndPersistRecommendedChannel is also called in UpdateViewModel constructor. If the ViewModel is created before this runs, channel resolution happens twice, creating redundant UpdateManager instances. Consider calling it once at a single entry point.
| var service = new UpdateService(SelectedUpdateChannel == 1); | ||
| _activeUpdateService = service; | ||
| var update = await service.CheckForUpdateAsync(); |
There was a problem hiding this comment.
A new UpdateService is created on every check. Since the channel doesn't change mid-check, consider reusing _activeUpdateService when the channel hasn't changed, avoiding repeated allocations of source arrays and managers.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (IsChecking) return; | ||
| IsChecking = true; | ||
| StatusMessage = "正在检查更新..."; | ||
|
|
||
| try | ||
| { | ||
| _cachedUpdate = await _updateService.CheckForUpdateAsync(); | ||
| if (_cachedUpdate != null) | ||
| var service = new UpdateService(SelectedUpdateChannel == 1); | ||
| _activeUpdateService = service; | ||
| var update = await service.CheckForUpdateAsync(); |
There was a problem hiding this comment.
CheckForUpdateAsync 在开始检查/发生异常时没有清空 _cachedUpdate(以及与之配套的 _activeUpdateService)。如果上一次检查已缓存了更新信息,本次检查失败后 HasUpdate 仍可能保持为 true,进而允许用户用“过期的 UpdateInfo + 新的/异常的 UpdateService”执行更新。建议在发起检查前先清空缓存(或在各个 catch 分支中显式清空并触发 HasUpdate 变更),确保失败场景不会保留旧更新结果。
| - name: Restore | ||
| run: > | ||
| dotnet restore PrismCore.csproj | ||
| -r ${{ matrix.rid }} | ||
| -p:Platform=${{ matrix.platform }} | ||
|
|
||
| - name: Build | ||
| run: > | ||
| dotnet build PrismCore.csproj | ||
| -c Release | ||
| --no-restore | ||
| -r ${{ matrix.rid }} | ||
| -p:Platform=${{ matrix.platform }} | ||
| -p:WindowsPackageType=None |
There was a problem hiding this comment.
dotnet restore 和 dotnet build --no-restore 使用了不同的 MSBuild 属性集:build 额外传了 -p:WindowsPackageType=None,但 restore 没有。NuGet 还原结果(project.assets.json)是属性敏感的,这种不一致可能导致后续 --no-restore 构建出现“assets file doesn't have a target…”之类的问题。建议把 -p:WindowsPackageType=None 也加到 Restore 步骤(或移除 --no-restore 让 build 重新还原)。
- UpdateMode 默认值从 2 改为 0(避免未经用户同意自动更新) - IsVelopackInstalled 从属性改为方法,避免每次访问创建 UpdateManager - 移除 UpdateViewModel 中重复的 ResolveAndPersistRecommendedChannel 调用 - 检查更新前和失败时清除缓存,防止使用过期更新信息 - 复用 UpdateService 实例(通道未变时不重复创建) - CI Restore 步骤添加 -p:WindowsPackageType=None 与 Build 保持一致 Co-authored-by: WSXYT <102407247+WSXYT@users.noreply.github.com>
Summary of Changes
CI Workflows:
dev,beta, andmaster).betaandmasterbranch pushes.0and introducedbase_branchparameter for branch diff analysis.win-x64architecture to fix build issues.Documentation:
README.Automation:
dev,beta, andmasterbranches.Please verify: