Skip to content

Commit f12cb41

Browse files
HanFengRuYueclaude
andcommitted
feat: v1.6 — 修复自动更新功能并提升版本号
- 修复下载更新时文件锁导致解压失败的问题(fileStream 未在 ZipFile.OpenRead 前释放) - 修复下载更新失败时不提示用户的问题(添加系统托盘通知) - 修复更新内容显示原始 Markdown 的问题(提取 Changelog 部分并用 marked 渲染) - 修复错误消息暴露内部文件路径的安全问题(使用安全的静态消息) - 版本号从 1.5 提升至 1.6 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b9a28cc commit f12cb41

6 files changed

Lines changed: 74 additions & 29 deletions

File tree

.github/workflows/build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
workflow_call:
77
inputs:
88
build_version:
9-
description: 'Version string (e.g. 1.5.202603130200); auto-generated if empty'
9+
description: 'Version string (e.g. 1.6.202603130200); auto-generated if empty'
1010
required: false
1111
type: string
1212
release_tag:
@@ -48,7 +48,7 @@ jobs:
4848
shell: pwsh
4949
run: |
5050
$v = '${{ inputs.build_version }}'
51-
if (-not $v) { $v = "1.5.$(Get-Date -Format 'yyyyMMddHHmm')" }
51+
if (-not $v) { $v = "1.6.$(Get-Date -Format 'yyyyMMddHHmm')" }
5252
"build_version=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
5353
Write-Host "Build version: $v"
5454
@@ -448,7 +448,7 @@ jobs:
448448
$installerProject = "Installer/Installer.wixproj"
449449
450450
# Generate MSI version from build version
451-
# Build version format: 1.5.YYYYMMDDHHmm
451+
# Build version format: 1.6.YYYYMMDDHHmm
452452
# MSI constraints: major < 256, minor < 256, build < 65536
453453
$parts = "${{ steps.ver.outputs.build_version }}".Split('.')
454454
$timestamp = $parts[2] # YYYYMMDDHHmm

.github/workflows/dep-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ jobs:
9191
9292
FORCE="${{ inputs.force_build }}"
9393
if [ -n "$CHANGED" ] || [ "$FORCE" = "true" ]; then
94-
BUILD_VER="1.5.$(date -u +%Y%m%d%H%M)"
94+
BUILD_VER="1.6.$(date -u +%Y%m%d%H%M)"
9595
RELEASE_TAG="auto-${BUILD_VER}"
9696
9797
echo "needs_build=true" >> "$GITHUB_OUTPUT"

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ cd XUnityToolkit-Vue && npx vue-tsc --noEmit
141141

142142
- `dotnet build` auto-runs frontend; skip with `-p:SkipFrontendBuild=true`
143143
- `build.ps1`: downloads bundled assets → extracts XUnity reference DLLs → updates classdata.tpk (requires `gh` CLI) → frontend → TranslatorEndpoint → publish to `Release/win-x64/`; `-SkipDownload` skips all download/extraction steps; cleanup: remove `web.config`, `*.pdb`, `*.staticwebassets.endpoints.json`
144-
- **Versioning:** `build.ps1` auto-generates `1.5.{YYYYMMDDHHmm}` (CI uses `1.5.` prefix) via `-p:InformationalVersion`; **must use `InformationalVersion` not `Version`**`Version` sets `AssemblyVersion` (UInt16 max 65535) which overflows with timestamp
144+
- **Versioning:** `build.ps1` auto-generates `1.6.{YYYYMMDDHHmm}` (CI uses `1.6.` prefix) via `-p:InformationalVersion`; **must use `InformationalVersion` not `Version`**`Version` sets `AssemblyVersion` (UInt16 max 65535) which overflows with timestamp
145145
- **Multi-file publishing:** `PublishSingleFile` removed; `ExcludeFromSingleFile` target removed; LibCpp2IL.dll works naturally in multi-file mode
146146
- **Satellite assemblies:** `SatelliteResourceLanguages=en` strips all language folders (cs/de/fr/ja/ko/etc.) from publish output; WinForms satellite resources are unused (UI is Vue, native dialogs use OS localization)
147147
- **Updater:** `Updater/Updater.csproj` (net10.0, PublishAot); win-x64 only; `--data-dir` CLI arg directs log/backup paths to `paths.Root`

XUnityToolkit-Vue/src/views/SettingsView.vue

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type { AppSettings } from '@/api/types'
3232
import { useThemeStore, accentPresets } from '@/stores/theme'
3333
import type { ThemeMode } from '@/stores/theme'
3434
import { useAutoSave } from '@/composables/useAutoSave'
35+
import { marked } from 'marked'
3536
import { useUpdateStore } from '@/stores/update'
3637
3738
defineOptions({ name: 'SettingsView' })
@@ -120,6 +121,12 @@ const shortVersion = computed(() => {
120121
return match ? match[1] : version.value
121122
})
122123
124+
const changelogHtml = computed(() => {
125+
const md = updateStore.availableInfo?.changelog
126+
if (!md) return ''
127+
return marked.parse(md, { async: false }) as string
128+
})
129+
123130
async function loadVersion() {
124131
try {
125132
const info = await settingsApi.getVersion()
@@ -353,9 +360,9 @@ onUnmounted(() => {
353360
<span class="update-title">更新可用: v{{ updateStore.availableInfo.version }}</span>
354361
</div>
355362

356-
<div v-if="updateStore.availableInfo.changelog" class="update-changelog">
363+
<div v-if="changelogHtml" class="update-changelog">
357364
<div class="changelog-label">更新内容:</div>
358-
<div class="changelog-content">{{ updateStore.availableInfo.changelog }}</div>
365+
<div class="changelog-content" v-html="changelogHtml"></div>
359366
</div>
360367

361368
<div class="update-packages">
@@ -695,7 +702,13 @@ onUnmounted(() => {
695702
.update-title { font-weight: 600; font-size: 14px; }
696703
.update-changelog { margin-bottom: 12px; }
697704
.changelog-label { font-size: 13px; color: var(--text-secondary); margin-bottom: 4px; }
698-
.changelog-content { font-size: 13px; white-space: pre-line; }
705+
.changelog-content {
706+
font-size: 13px;
707+
:deep(ul) { margin: 0; padding-left: 20px; }
708+
:deep(li) { margin: 2px 0; }
709+
:deep(code) { font-size: 12px; padding: 1px 4px; border-radius: 3px; background: var(--bg-muted); }
710+
:deep(p) { margin: 4px 0; }
711+
}
699712
.packages-label { font-size: 13px; color: var(--text-secondary); margin-bottom: 4px; }
700713
.package-item { font-size: 13px; font-family: monospace; }
701714
.packages-total { font-size: 13px; font-weight: 500; margin-top: 4px; }

XUnityToolkit-WebUI/Services/UpdateService.cs

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,13 @@ await File.WriteAllTextAsync(cachePath,
264264
if (asset is not null) downloadSize += asset.Size;
265265
}
266266

267+
var changelog = ExtractChangelog(release.Body);
268+
267269
_lastCheckResult = new UpdateCheckResult
268270
{
269271
UpdateAvailable = changedCount > 0 || deletedCount > 0,
270272
NewVersion = remoteVersion,
271-
Changelog = release.Body,
273+
Changelog = changelog,
272274
DownloadSize = downloadSize,
273275
ChangedPackages = changedPackages.ToList(),
274276
ChangedFileCount = changedCount,
@@ -282,7 +284,7 @@ await File.WriteAllTextAsync(cachePath,
282284
await hubContext.Clients.Group("update").SendAsync("UpdateAvailable", new UpdateAvailableInfo
283285
{
284286
Version = remoteVersion,
285-
Changelog = release.Body,
287+
Changelog = changelog,
286288
DownloadSize = downloadSize,
287289
ChangedPackages = changedPackages.ToList()
288290
}, ct);
@@ -301,7 +303,7 @@ await File.WriteAllTextAsync(cachePath,
301303
catch (Exception ex)
302304
{
303305
logger.LogError(ex, "检查更新失败");
304-
_status = new UpdateStatusInfo { State = UpdateState.Error, Error = ex.Message };
306+
_status = new UpdateStatusInfo { State = UpdateState.Error, Error = "检查更新失败,请检查网络连接" };
305307
await BroadcastStatus();
306308
return _lastCheckResult ?? new UpdateCheckResult();
307309
}
@@ -361,22 +363,24 @@ public async Task DownloadUpdateAsync(CancellationToken ct = default)
361363

362364
var zipPath = Path.Combine(stagingDir, zipName);
363365

364-
// Download with progress
365-
using var response = await client.GetAsync(asset.BrowserDownloadUrl,
366-
HttpCompletionOption.ResponseHeadersRead, token);
367-
response.EnsureSuccessStatusCode();
368-
369-
await using var stream = await response.Content.ReadAsStreamAsync(token);
370-
await using var fileStream = File.Create(zipPath);
371-
var buffer = new byte[81920];
372-
int bytesRead;
373-
while ((bytesRead = await stream.ReadAsync(buffer, token)) > 0)
366+
// Download with progress (scoped to release file handle before extraction)
374367
{
375-
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), token);
376-
downloadedBytes += bytesRead;
377-
_status.DownloadedBytes = downloadedBytes;
378-
_status.Progress = totalBytes > 0 ? (double)downloadedBytes / totalBytes * 100 : 0;
379-
await BroadcastStatus();
368+
using var response = await client.GetAsync(asset.BrowserDownloadUrl,
369+
HttpCompletionOption.ResponseHeadersRead, token);
370+
response.EnsureSuccessStatusCode();
371+
372+
await using var stream = await response.Content.ReadAsStreamAsync(token);
373+
await using var fileStream = File.Create(zipPath);
374+
var buffer = new byte[81920];
375+
int bytesRead;
376+
while ((bytesRead = await stream.ReadAsync(buffer, token)) > 0)
377+
{
378+
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), token);
379+
downloadedBytes += bytesRead;
380+
_status.DownloadedBytes = downloadedBytes;
381+
_status.Progress = totalBytes > 0 ? (double)downloadedBytes / totalBytes * 100 : 0;
382+
await BroadcastStatus();
383+
}
380384
}
381385

382386
logger.LogInformation("已下载: {Name} ({Size:N0} bytes)", zipName, new FileInfo(zipPath).Length);
@@ -471,8 +475,9 @@ await File.WriteAllTextAsync(updateManifestPath,
471475
logger.LogError(ex, "下载更新失败");
472476
if (Directory.Exists(stagingDir))
473477
Directory.Delete(stagingDir, true);
474-
_status = new UpdateStatusInfo { State = UpdateState.Error, Error = ex.Message };
478+
_status = new UpdateStatusInfo { State = UpdateState.Error, Error = "下载更新失败,请重试" };
475479
await BroadcastStatus();
480+
trayService.ShowNotification("XUnityToolkit", "下载更新失败,请重试");
476481
throw;
477482
}
478483
finally
@@ -641,6 +646,33 @@ public async Task AutoCheckOnStartupAsync()
641646
}
642647
}
643648

649+
/// <summary>
650+
/// Extracts the "### Changelog" section from GitHub release body,
651+
/// stripping download links, installation instructions, etc.
652+
/// </summary>
653+
private static string? ExtractChangelog(string? releaseBody)
654+
{
655+
if (string.IsNullOrWhiteSpace(releaseBody)) return null;
656+
657+
var startIndex = releaseBody.IndexOf("### Changelog", StringComparison.OrdinalIgnoreCase);
658+
if (startIndex < 0) return releaseBody; // No Changelog section found, return full body
659+
660+
// Skip past the "### Changelog" line
661+
var contentStart = releaseBody.IndexOf('\n', startIndex);
662+
if (contentStart < 0) return null;
663+
contentStart++; // Skip the newline
664+
665+
// Find the end (next ## or ### section, or end of string)
666+
var endIndex = releaseBody.IndexOf("\n##", contentStart, StringComparison.Ordinal);
667+
668+
var content = endIndex >= 0
669+
? releaseBody[contentStart..endIndex]
670+
: releaseBody[contentStart..];
671+
672+
var trimmed = content.Trim();
673+
return trimmed.Length > 0 ? trimmed : null;
674+
}
675+
644676
private Task BroadcastStatus()
645677
{
646678
return hubContext.Clients.Group("update").SendAsync("UpdateStatus", _status);

build.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ $EndpointProject = Join-Path $ProjectRoot 'TranslatorEndpoint\TranslatorEndpoint
163163
$rid = 'win-x64'
164164
$hasEndpoint = Test-Path $EndpointProject
165165

166-
# Generate version: 1.5.{YYYYMMDDHHmm}
167-
$BuildVersion = "1.5.$(Get-Date -Format 'yyyyMMddHHmm')"
166+
# Generate version: 1.6.{YYYYMMDDHHmm}
167+
$BuildVersion = "1.6.$(Get-Date -Format 'yyyyMMddHHmm')"
168168

169169
# Generate MSI-compatible version: {(YYYY-2024)*12+MM}.{DD}.{HH*60+mm}
170170
# Constraints: major < 256, minor < 256, build < 65536

0 commit comments

Comments
 (0)