mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 10:53:33 +08:00
fix: unify desktop updater artifacts (#1320)
This commit is contained in:
30
.github/workflows/desktop-release.yml
vendored
30
.github/workflows/desktop-release.yml
vendored
@@ -70,22 +70,20 @@ jobs:
|
|||||||
- name: Prepare release artifact (Windows)
|
- name: Prepare release artifact (Windows)
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
$installerExe = Get-ChildItem -Path 'apps/dsa-desktop/dist' -Filter '*.exe' `
|
$expectedInstallerName = "daily-stock-analysis-windows-installer-$env:RELEASE_TAG.exe"
|
||||||
| Where-Object { $_.Name -match 'Setup' } `
|
$installerPath = Join-Path 'apps/dsa-desktop/dist' $expectedInstallerName
|
||||||
| Sort-Object LastWriteTime -Descending `
|
$installerExe = Get-Item -Path $installerPath -ErrorAction SilentlyContinue
|
||||||
| Select-Object -First 1
|
|
||||||
if (-not $installerExe) {
|
if (-not $installerExe) {
|
||||||
throw 'No NSIS setup .exe found under apps/dsa-desktop/dist.'
|
throw "No expected NSIS installer found under apps/dsa-desktop/dist: $expectedInstallerName"
|
||||||
}
|
}
|
||||||
$winUnpacked = 'apps/dsa-desktop/dist/win-unpacked'
|
$winUnpacked = 'apps/dsa-desktop/dist/win-unpacked'
|
||||||
if (-not (Test-Path $winUnpacked)) {
|
if (-not (Test-Path $winUnpacked)) {
|
||||||
throw 'No win-unpacked directory found under apps/dsa-desktop/dist.'
|
throw 'No win-unpacked directory found under apps/dsa-desktop/dist.'
|
||||||
}
|
}
|
||||||
New-Item -ItemType Directory -Path 'dist/release-assets' -Force | Out-Null
|
New-Item -ItemType Directory -Path 'dist/release-assets' -Force | Out-Null
|
||||||
$exeTarget = "dist/release-assets/daily-stock-analysis-windows-installer-$env:RELEASE_TAG.exe"
|
$exeTarget = "dist/release-assets/$expectedInstallerName"
|
||||||
$zipTarget = "dist/release-assets/daily-stock-analysis-windows-noinstall-$env:RELEASE_TAG.zip"
|
$zipTarget = "dist/release-assets/daily-stock-analysis-windows-noinstall-$env:RELEASE_TAG.zip"
|
||||||
Copy-Item -Path $installerExe.FullName -Destination $exeTarget -Force
|
Copy-Item -Path $installerExe.FullName -Destination $exeTarget -Force
|
||||||
Copy-Item -Path $installerExe.FullName -Destination "dist/release-assets/$($installerExe.Name)" -Force
|
|
||||||
Compress-Archive -Path $winUnpacked -DestinationPath $zipTarget -CompressionLevel Optimal -Force
|
Compress-Archive -Path $winUnpacked -DestinationPath $zipTarget -CompressionLevel Optimal -Force
|
||||||
Get-ChildItem -Path 'apps/dsa-desktop/dist' -File |
|
Get-ChildItem -Path 'apps/dsa-desktop/dist' -File |
|
||||||
Where-Object { $_.Name -eq 'latest.yml' -or $_.Name -like '*.blockmap' } |
|
Where-Object { $_.Name -eq 'latest.yml' -or $_.Name -like '*.blockmap' } |
|
||||||
@@ -100,8 +98,22 @@ jobs:
|
|||||||
if (-not (Select-String -Path 'dist/release-assets/latest.yml' -Pattern $versionPattern)) {
|
if (-not (Select-String -Path 'dist/release-assets/latest.yml' -Pattern $versionPattern)) {
|
||||||
throw "latest.yml version does not match release tag $env:RELEASE_TAG."
|
throw "latest.yml version does not match release tag $env:RELEASE_TAG."
|
||||||
}
|
}
|
||||||
if (-not (Get-ChildItem -Path 'dist/release-assets' -Filter '*.blockmap' -File)) {
|
$latestText = Get-Content -Path 'dist/release-assets/latest.yml' -Raw
|
||||||
throw 'No blockmap files found under dist/release-assets.'
|
$pathMatch = [regex]::Match($latestText, '(?m)^\s*path:\s*[''"]?([^''"\r\n]+)[''"]?\s*$')
|
||||||
|
if (-not $pathMatch.Success) {
|
||||||
|
throw 'latest.yml missing path field.'
|
||||||
|
}
|
||||||
|
$latestInstallerPath = $pathMatch.Groups[1].Value.Trim()
|
||||||
|
if ($latestInstallerPath -ne $expectedInstallerName) {
|
||||||
|
throw "latest.yml path ($latestInstallerPath) does not match release installer $expectedInstallerName."
|
||||||
|
}
|
||||||
|
$urlMatch = [regex]::Match($latestText, '(?m)^\s*-\s*url:\s*[''"]?([^''"\r\n]+)[''"]?\s*$')
|
||||||
|
if ($urlMatch.Success -and $urlMatch.Groups[1].Value.Trim() -ne $expectedInstallerName) {
|
||||||
|
throw "latest.yml file url ($($urlMatch.Groups[1].Value.Trim())) does not match release installer $expectedInstallerName."
|
||||||
|
}
|
||||||
|
$expectedBlockmapName = "$expectedInstallerName.blockmap"
|
||||||
|
if (-not (Get-ChildItem -Path 'dist/release-assets' -Filter $expectedBlockmapName -File)) {
|
||||||
|
throw "No matching blockmap found under dist/release-assets: $expectedBlockmapName"
|
||||||
}
|
}
|
||||||
Get-ChildItem -Path 'dist/release-assets' -File | Sort-Object Name | Format-Table Name, Length
|
Get-ChildItem -Path 'dist/release-assets' -File | Sort-Object Name | Format-Table Name, Length
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
|
"artifactName": "daily-stock-analysis-windows-installer-v${version}.${ext}",
|
||||||
"publish": [
|
"publish": [
|
||||||
{
|
{
|
||||||
"provider": "github",
|
"provider": "github",
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
<!-- 新条目格式:- [类型] 描述(类型取值:新功能/改进/修复/文档/测试/chore)-->
|
<!-- 新条目格式:- [类型] 描述(类型取值:新功能/改进/修复/文档/测试/chore)-->
|
||||||
<!-- 每条独立一行追加到本段末尾,无需分类标题,合并时冲突最小 -->
|
<!-- 每条独立一行追加到本段末尾,无需分类标题,合并时冲突最小 -->
|
||||||
|
|
||||||
|
- [修复] 统一 Windows 桌面安装包与自动更新元数据文件名,避免 Release 中出现重复安装包并阻断 `latest.yml` 指向不存在附件。
|
||||||
|
|
||||||
## [3.17.1] - 2026-05-16
|
## [3.17.1] - 2026-05-16
|
||||||
|
|
||||||
### 发布亮点
|
### 发布亮点
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ powershell -ExecutionPolicy Bypass -File scripts\build-all.ps1
|
|||||||
- 推送语义化 tag(如 `v3.2.12`)后自动触发
|
- 推送语义化 tag(如 `v3.2.12`)后自动触发
|
||||||
- 在 Actions 页面手动触发并指定 `release_tag`
|
- 在 Actions 页面手动触发并指定 `release_tag`
|
||||||
- 产物:
|
- 产物:
|
||||||
- Windows 安装包:Release 附件会整理为 `daily-stock-analysis-windows-installer-<tag>.exe`,本地 `apps/dsa-desktop/dist/` 中仍是 `*Setup*.exe`
|
- Windows 安装包:Release 附件和本地 `apps/dsa-desktop/dist/` 中统一为 `daily-stock-analysis-windows-installer-<tag>.exe`
|
||||||
- Windows 自动更新元数据:Release 附件会额外保留 electron-builder 原始 `*Setup*.exe`、`latest.yml` 和 `*.blockmap`,供安装版桌面端后台下载与校验更新
|
- Windows 自动更新元数据:Release 附件会额外保留 `latest.yml` 和 `*.blockmap`,供安装版桌面端后台下载与校验更新;普通用户无需手动下载这些元数据
|
||||||
- Windows 免安装包:`daily-stock-analysis-windows-noinstall-<tag>.zip`
|
- Windows 免安装包:`daily-stock-analysis-windows-noinstall-<tag>.zip`
|
||||||
- macOS Intel:`daily-stock-analysis-macos-x64-<tag>.dmg`
|
- macOS Intel:`daily-stock-analysis-macos-x64-<tag>.dmg`
|
||||||
- macOS Apple Silicon:`daily-stock-analysis-macos-arm64-<tag>.dmg`
|
- macOS Apple Silicon:`daily-stock-analysis-macos-arm64-<tag>.dmg`
|
||||||
@@ -112,7 +112,7 @@ npm run build
|
|||||||
./scripts/verify-desktop-updater-artifacts.ps1 -ReleaseTag v$(node -p "require('./apps/dsa-desktop/package.json').version")
|
./scripts/verify-desktop-updater-artifacts.ps1 -ReleaseTag v$(node -p "require('./apps/dsa-desktop/package.json').version")
|
||||||
```
|
```
|
||||||
|
|
||||||
> 预期当前执行环境不支持生成 Windows `Setup*.exe` 时,请在交付说明中明确注明平台限制,并要求指定的 Windows 发布链路复核人补齐该项验证。
|
> 预期当前执行环境不支持生成 Windows NSIS 安装器时,请在交付说明中明确注明平台限制,并要求指定的 Windows 发布链路复核人补齐该项验证。
|
||||||
|
|
||||||
3. 检查更新元数据是否产出
|
3. 检查更新元数据是否产出
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ ls -1 dist/*.yml dist/*.blockmap 2>/dev/null || true
|
|||||||
RELEASE_TAG="v$(node -p \"require('./package.json').version\")"
|
RELEASE_TAG="v$(node -p \"require('./package.json').version\")"
|
||||||
REPO="ZhuLinsen/daily_stock_analysis"
|
REPO="ZhuLinsen/daily_stock_analysis"
|
||||||
|
|
||||||
for f in dist/*latest.yml dist/*.blockmap dist/*Setup*.exe; do
|
for f in dist/*latest.yml dist/*.blockmap dist/daily-stock-analysis-windows-installer-*.exe; do
|
||||||
[ -f \"$f\" ] && echo \"[FOUND] $f\"
|
[ -f \"$f\" ] && echo \"[FOUND] $f\"
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -141,10 +141,9 @@ echo \"Release Tag: $RELEASE_TAG\"
|
|||||||
echo \"Release 地址: https://github.com/$REPO/releases/tag/$RELEASE_TAG\"
|
echo \"Release 地址: https://github.com/$REPO/releases/tag/$RELEASE_TAG\"
|
||||||
echo \"应核对附件是否包含:\"
|
echo \"应核对附件是否包含:\"
|
||||||
echo \"- daily-stock-analysis-windows-installer-*.exe\"
|
echo \"- daily-stock-analysis-windows-installer-*.exe\"
|
||||||
echo \"- *Setup*.exe\"
|
|
||||||
echo \"- latest.yml\"
|
echo \"- latest.yml\"
|
||||||
echo \"- *.blockmap\"
|
echo \"- *.blockmap\"
|
||||||
echo \"并确保 latest.yml 中 version 与 tag 的语义化版本一致\"
|
echo \"并确保 latest.yml 中 version 与 tag 的语义化版本一致,path/url 与安装包附件名一致\"
|
||||||
```
|
```
|
||||||
|
|
||||||
5a. 建议在 PR 描述里记录的“可复核输出”(Windows):
|
5a. 建议在 PR 描述里记录的“可复核输出”(Windows):
|
||||||
@@ -156,14 +155,14 @@ grep -E "^version:" dist/latest.yml
|
|||||||
echo "latest.yml files:"
|
echo "latest.yml files:"
|
||||||
sed -n '1,80p' dist/latest.yml
|
sed -n '1,80p' dist/latest.yml
|
||||||
echo "packaging artifacts:"
|
echo "packaging artifacts:"
|
||||||
ls -1 dist/*.yml dist/*.blockmap dist/*Setup*.exe dist/*installer*.exe 2>/dev/null | sort
|
ls -1 dist/*.yml dist/*.blockmap dist/*installer*.exe 2>/dev/null | sort
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows 发布链路复核清单(在 PR 后由发布团队/维护者执行):
|
Windows 发布链路复核清单(在 PR 后由发布团队/维护者执行):
|
||||||
|
|
||||||
- release/tag 与 `daily-stock-analysis-windows-installer-<tag>.exe` 的版本号一致;
|
- release/tag 与 `daily-stock-analysis-windows-installer-<tag>.exe` 的版本号一致;
|
||||||
- `latest.yml`、`*Setup*.exe`、`*.blockmap` 同 tag 同步出现且可下载;
|
- `latest.yml`、`daily-stock-analysis-windows-installer-<tag>.exe`、`*.blockmap` 同 tag 同步出现且可下载;
|
||||||
- `latest.yml` 中 `version` 与 Release tag 语义一致(去掉 `v` 前缀后比对);
|
- `latest.yml` 中 `version` 与 Release tag 语义一致(去掉 `v` 前缀后比对),且 `path` / `files.url` 与安装包附件名一致;
|
||||||
- 如缺少上述文件或 `release-tag` 不匹配,需标注阻断并补齐 `desktop-release` 打包流程。
|
- 如缺少上述文件或 `release-tag` 不匹配,需标注阻断并补齐 `desktop-release` 打包流程。
|
||||||
|
|
||||||
5. Windows/NSIS 产物与发布附件一致性请在 Windows 环境手动验证(可人工触发发布流程),并在升级后核对运行时文件留存:
|
5. Windows/NSIS 产物与发布附件一致性请在 Windows 环境手动验证(可人工触发发布流程),并在升级后核对运行时文件留存:
|
||||||
@@ -213,7 +212,7 @@ npm install
|
|||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
打包产物位于 `apps/dsa-desktop/dist/`。Windows 安装器会生成 `*Setup*.exe`,安装向导中可选择安装目录。
|
打包产物位于 `apps/dsa-desktop/dist/`。Windows 安装器会生成 `daily-stock-analysis-windows-installer-<tag>.exe`,安装向导中可选择安装目录。
|
||||||
|
|
||||||
## 目录结构
|
## 目录结构
|
||||||
|
|
||||||
@@ -303,7 +302,7 @@ PyInstaller 打包时缺少模块,需要在 `scripts/build-backend.ps1` 中增
|
|||||||
|
|
||||||
Windows 分发现在有两种方式:
|
Windows 分发现在有两种方式:
|
||||||
|
|
||||||
1. 安装包:分发 `apps/dsa-desktop/dist/` 下的 `*Setup*.exe`,用户安装时可自行选择目标目录
|
1. 安装包:分发 `apps/dsa-desktop/dist/` 下的 `daily-stock-analysis-windows-installer-<tag>.exe`,用户安装时可自行选择目标目录
|
||||||
2. 免安装包:将 `apps/dsa-desktop/dist/win-unpacked/` 整个文件夹打包发给用户
|
2. 免安装包:将 `apps/dsa-desktop/dist/win-unpacked/` 整个文件夹打包发给用户
|
||||||
|
|
||||||
使用 `win-unpacked` 免安装包时,用户只需:
|
使用 `win-unpacked` 免安装包时,用户只需:
|
||||||
|
|||||||
@@ -68,14 +68,34 @@ if ($normalizedLatestVersion -ne $normalizedReleaseTag) {
|
|||||||
throw "Version mismatch: latest.yml=$normalizedLatestVersion, expected=$normalizedReleaseTag"
|
throw "Version mismatch: latest.yml=$normalizedLatestVersion, expected=$normalizedReleaseTag"
|
||||||
}
|
}
|
||||||
|
|
||||||
$setupFiles = Get-ChildItem -Path $distDirPath -Filter '*Setup*.exe' -File -ErrorAction SilentlyContinue
|
$expectedInstallerFileName = "daily-stock-analysis-windows-installer-v$normalizedReleaseTag.exe"
|
||||||
if (-not $setupFiles) {
|
|
||||||
throw 'No *Setup*.exe found in dist (NSIS installer is required).'
|
if ($ymlText -match '(?m)^\s*path:\s*[''"]?([^''"\r\n]+)[''"]?\s*$') {
|
||||||
|
$latestInstallerPath = $matches[1].Trim()
|
||||||
|
} else {
|
||||||
|
throw "latest.yml appears invalid: missing path field."
|
||||||
}
|
}
|
||||||
|
|
||||||
$blockmapFiles = Get-ChildItem -Path $distDirPath -Filter '*.blockmap' -File -ErrorAction SilentlyContinue
|
if ($latestInstallerPath -ne $expectedInstallerFileName) {
|
||||||
|
throw "latest.yml path ($latestInstallerPath) does not match expected installer $expectedInstallerFileName."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ymlText -match '(?m)^\s*-\s*url:\s*[''"]?([^''"\r\n]+)[''"]?\s*$') {
|
||||||
|
$latestInstallerUrl = $matches[1].Trim()
|
||||||
|
if ($latestInstallerUrl -ne $expectedInstallerFileName) {
|
||||||
|
throw "latest.yml file url ($latestInstallerUrl) does not match expected installer $expectedInstallerFileName."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$setupFiles = Get-ChildItem -Path $distDirPath -Filter $expectedInstallerFileName -File -ErrorAction SilentlyContinue
|
||||||
|
if (-not $setupFiles) {
|
||||||
|
throw "No expected NSIS installer found in dist: $expectedInstallerFileName"
|
||||||
|
}
|
||||||
|
|
||||||
|
$expectedBlockmapFileName = "$expectedInstallerFileName.blockmap"
|
||||||
|
$blockmapFiles = Get-ChildItem -Path $distDirPath -Filter $expectedBlockmapFileName -File -ErrorAction SilentlyContinue
|
||||||
if (-not $blockmapFiles) {
|
if (-not $blockmapFiles) {
|
||||||
throw 'No *.blockmap found in dist; updater metadata may be incomplete.'
|
throw "No matching blockmap found in dist: $expectedBlockmapFileName"
|
||||||
}
|
}
|
||||||
|
|
||||||
$installerFiles = @()
|
$installerFiles = @()
|
||||||
@@ -101,9 +121,9 @@ if ($releaseAssetsDirWasExplicit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($releaseAssetsDirPath) {
|
if ($releaseAssetsDirPath) {
|
||||||
$installerFiles = Get-ChildItem -Path $releaseAssetsDirPath -Filter 'daily-stock-analysis-windows-installer-*.exe' -File -ErrorAction SilentlyContinue
|
$installerFiles = Get-ChildItem -Path $releaseAssetsDirPath -Filter $expectedInstallerFileName -File -ErrorAction SilentlyContinue
|
||||||
if (-not $installerFiles) {
|
if (-not $installerFiles) {
|
||||||
throw "No daily-stock-analysis-windows-installer-*.exe found in release assets: $releaseAssetsDirPath"
|
throw "No expected Windows installer found in release assets: $expectedInstallerFileName"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Write-Host "[check] release attachment alias check skipped: run after release assets are prepared or pass -ReleaseAssetsDir."
|
Write-Host "[check] release attachment alias check skipped: run after release assets are prepared or pass -ReleaseAssetsDir."
|
||||||
@@ -112,7 +132,8 @@ if ($releaseAssetsDirPath) {
|
|||||||
Write-Host "[check] dist: $distDirPath"
|
Write-Host "[check] dist: $distDirPath"
|
||||||
Write-Host "[check] latest.yml version: $normalizedLatestVersion"
|
Write-Host "[check] latest.yml version: $normalizedLatestVersion"
|
||||||
Write-Host "[check] expected release version: $normalizedReleaseTag"
|
Write-Host "[check] expected release version: $normalizedReleaseTag"
|
||||||
Write-Host "[check] NSIS installers (Setup):"
|
Write-Host "[check] latest.yml installer path: $latestInstallerPath"
|
||||||
|
Write-Host "[check] NSIS installers:"
|
||||||
$setupFiles | ForEach-Object { Write-Host "[found] $($_.Name)" }
|
$setupFiles | ForEach-Object { Write-Host "[found] $($_.Name)" }
|
||||||
if ($installerFiles) {
|
if ($installerFiles) {
|
||||||
Write-Host "[check] release assets: $releaseAssetsDirPath"
|
Write-Host "[check] release assets: $releaseAssetsDirPath"
|
||||||
@@ -122,10 +143,6 @@ if ($installerFiles) {
|
|||||||
Write-Host "[check] blockmaps:"
|
Write-Host "[check] blockmaps:"
|
||||||
$blockmapFiles | ForEach-Object { Write-Host "[found] $($_.Name)" }
|
$blockmapFiles | ForEach-Object { Write-Host "[found] $($_.Name)" }
|
||||||
|
|
||||||
if (-not $ymlText.Contains('path:')) {
|
|
||||||
throw "latest.yml appears invalid: missing path field."
|
|
||||||
}
|
|
||||||
|
|
||||||
$versionInTag = Normalize-SemverText -VersionText $ReleaseTag
|
$versionInTag = Normalize-SemverText -VersionText $ReleaseTag
|
||||||
if ($versionInTag -and $versionInTag -ne $normalizedReleaseTag) {
|
if ($versionInTag -and $versionInTag -ne $normalizedReleaseTag) {
|
||||||
Write-Host "[warn] input release tag ($versionInTag) differs from package version ($normalizedReleaseTag)."
|
Write-Host "[warn] input release tag ($versionInTag) differs from package version ($normalizedReleaseTag)."
|
||||||
|
|||||||
Reference in New Issue
Block a user