Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 64 additions & 35 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ jobs:
} catch {
Write-Host "No existing catalog found on CDN, will create fresh catalog."
}
- name: Fetch previous release standalone for step package comparison
- name: Fetch previous release standalone for cumulative planning
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -248,7 +248,7 @@ jobs:
$prevTag = $prevRelease[0].tagName
$prevVersion = $prevTag -replace '^v', ''
if ($prevVersion -eq "${{ needs.prepare.outputs.build-version }}") {
Write-Host "Previous release is the current build version; skipping step package generation."
Write-Host "Previous release is the current build version; skipping previous-version cumulative planning."
exit 0
}
Write-Host "Previous release: $prevTag (version: $prevVersion)"
Expand All @@ -261,7 +261,7 @@ jobs:
Add-Content -Path $env:GITHUB_ENV -Value "PREV_STANDALONE_DIR=artifacts\prev-standalone" -Encoding utf8
Write-Host "Extracted previous standalone from $($zipFile.Name)."
} else {
Write-Host "No previous full zip found; step package will not be generated."
Write-Host "No previous full zip found; previous-version cumulative planning will be skipped."
}
} else {
Write-Host "No previous release found; this is the first release."
Expand All @@ -287,7 +287,7 @@ jobs:
New-Item -ItemType Directory -Path artifacts\anchor-release -Force | Out-Null
gh release download $anchorTag --repo ${{ github.repository }} --pattern "TelegramSearchBot-win-x64-full-*.zip" --dir artifacts\anchor-release
if ($LASTEXITCODE -ne 0) {
throw "Failed to download cumulative update anchor $anchorTag; refusing to publish a full-only update feed."
throw "Failed to download cumulative update anchor $anchorTag; refusing to publish without cumulative package from anchor $anchorTag."
}

$zipFile = Get-ChildItem artifacts\anchor-release -Filter "TelegramSearchBot-win-x64-full-*.zip" | Select-Object -First 1
Expand All @@ -297,7 +297,7 @@ jobs:
Add-Content -Path $env:GITHUB_ENV -Value "ANCHOR_STANDALONE_DIR=artifacts\anchor-standalone" -Encoding utf8
Write-Host "Extracted cumulative update anchor from $($zipFile.Name)."
} else {
throw "No anchor full zip found; refusing to publish a full-only update feed."
throw "No anchor full zip found; refusing to publish without cumulative package from anchor $anchorTag."
}
- name: Fetch previous cumulative update package
shell: pwsh
Expand All @@ -315,7 +315,9 @@ jobs:
$baseEntry = @($catalog.Entries | Where-Object {
$_.TargetVersion -eq $env:PREV_VERSION `
-and $_.MinSourceVersion -eq $env:CUMULATIVE_UPDATE_ANCHOR_VERSION `
-and $_.IsCumulative
-and $_.IsCumulative `
-and $_.PackagePath `
-and $_.PackagePath -like '*-cumulative.zst'
} | Sort-Object CompressedSize | Select-Object -First 1)

if ($baseEntry.Count -eq 0 -or -not $baseEntry[0].PackagePath) {
Expand All @@ -325,7 +327,11 @@ jobs:

New-Item -ItemType Directory -Path artifacts\base-cumulative -Force | Out-Null
$basePackagePath = 'artifacts\base-cumulative\base-cumulative.zst'
$uri = "$($env:UPDATE_BASE_URL.TrimEnd('/'))/$($baseEntry[0].PackagePath.TrimStart('/'))"
$uri = if ($baseEntry[0].PackageUrl) {
$baseEntry[0].PackageUrl
} else {
"$($env:UPDATE_BASE_URL.TrimEnd('/'))/$($baseEntry[0].PackagePath.TrimStart('/'))"
}
try {
Invoke-WebRequest -UseBasicParsing -Uri $uri -OutFile $basePackagePath
} catch {
Expand All @@ -339,10 +345,6 @@ jobs:
env:
BUILD_VERSION: ${{ needs.prepare.outputs.build-version }}
run: |
if ($env:BASE_CUMULATIVE_PACKAGE -and (Test-Path $env:BASE_CUMULATIVE_PACKAGE)) {
Write-Host "Base cumulative package is available; historical source packages are not needed."
exit 0
}
if (-not (Test-Path '.\artifacts\existing-catalog.json')) {
Write-Host "No existing catalog found; cumulative source packages will not be used."
exit 0
Expand All @@ -353,7 +355,7 @@ jobs:
$catalog = Get-Content '.\artifacts\existing-catalog.json' -Raw | ConvertFrom-Json
$entriesByVersion = @{}
foreach ($entry in $catalog.Entries) {
if (-not $entry.PackagePath -or $entry.PackagePath -notlike '*-full.zst') {
if (-not $entry.PackagePath -or $entry.PackagePath -notlike '*-cumulative.zst') {
continue
}

Expand All @@ -374,7 +376,7 @@ jobs:
}

if ($entriesByVersion.Count -eq 0) {
Write-Host "No cumulative source full packages found in existing catalog."
Write-Host "No cumulative source packages found in existing catalog."
exit 0
}

Expand All @@ -383,7 +385,11 @@ jobs:
foreach ($key in ($entriesByVersion.Keys | Sort-Object { [Version]$_ })) {
$entry = $entriesByVersion[$key]
$packagePath = Join-Path $sourcePackageDir ("source-$($key.Replace('.', '-')).zst")
$uri = "$($env:UPDATE_BASE_URL.TrimEnd('/'))/$($entry.PackagePath.TrimStart('/'))"
$uri = if ($entry.PackageUrl) {
$entry.PackageUrl
} else {
"$($env:UPDATE_BASE_URL.TrimEnd('/'))/$($entry.PackagePath.TrimStart('/'))"
}
try {
Invoke-WebRequest -UseBasicParsing -Uri $uri -OutFile $packagePath
Write-Host "Downloaded cumulative source package from CDN: $($entry.PackagePath)"
Expand All @@ -395,24 +401,51 @@ jobs:
if ((Get-ChildItem $sourcePackageDir -Filter '*.zst' -File | Measure-Object).Count -gt 0) {
Add-Content -Path $env:GITHUB_ENV -Value "CUMULATIVE_SOURCE_PACKAGE_DIR=$sourcePackageDir" -Encoding utf8
}
- name: Package full release asset
shell: pwsh
env:
BUILD_VERSION: ${{ needs.prepare.outputs.build-version }}
RELEASE_TAG: ${{ needs.prepare.outputs.release-tag }}
run: |
$releaseDir = '.\artifacts\release'
if (Test-Path $releaseDir) {
Remove-Item $releaseDir -Recurse -Force
}
New-Item -ItemType Directory -Path $releaseDir | Out-Null
$packageName = "TelegramSearchBot-win-x64-full-$env:BUILD_VERSION.zip"
$packagePath = Join-Path $releaseDir $packageName
Compress-Archive -Path .\artifacts\standalone\* -DestinationPath $packagePath -CompressionLevel Optimal
$hashPath = "$packagePath.sha512"
$hash = (Get-FileHash $packagePath -Algorithm SHA512).Hash.ToLower()
Set-Content -Path $hashPath -Value "$hash $packageName" -Encoding ascii
$repository = '${{ github.repository }}'
$fullUrl = "https://github.com/$repository/releases/download/$env:RELEASE_TAG/$packageName"
Add-Content -Path $env:GITHUB_ENV -Value "FULL_PACKAGE_NAME=$packageName" -Encoding utf8
Add-Content -Path $env:GITHUB_ENV -Value "FULL_PACKAGE_URL=$fullUrl" -Encoding utf8
Add-Content -Path $env:GITHUB_ENV -Value "FULL_PACKAGE_CHECKSUM=$hash" -Encoding utf8
Add-Content -Path $env:GITHUB_ENV -Value "FULL_PACKAGE_SIZE=$((Get-Item $packagePath).Length)" -Encoding utf8
- name: Build Moder.Update feed
shell: pwsh
env:
BUILD_VERSION: ${{ needs.prepare.outputs.build-version }}
RELEASE_TAG: ${{ needs.prepare.outputs.release-tag }}
run: |
$updateBaseUrl = $env:UPDATE_BASE_URL.TrimEnd('/')
$builderArgs = @(
'--source-dir', '.\artifacts\standalone',
'--output-dir', '.\artifacts\update-feed',
'--target-version', $env:BUILD_VERSION,
'--min-source-version', $env:LEGACY_BRIDGE_VERSION
'--min-source-version', $env:LEGACY_BRIDGE_VERSION,
'--updater-url', "$updateBaseUrl/moder_update_updater.exe",
'--package-base-url', $updateBaseUrl
)
if (Test-Path '.\artifacts\existing-catalog.json') {
$builderArgs += @('--existing-catalog', '.\artifacts\existing-catalog.json')
Write-Host "Passing existing catalog for merge."
}
if ($env:PREV_VERSION -and (Test-Path $env:PREV_STANDALONE_DIR)) {
$builderArgs += @('--prev-source-dir', $env:PREV_STANDALONE_DIR, '--prev-version', $env:PREV_VERSION)
Write-Host "Passing previous version $env:PREV_VERSION for step package generation."
Write-Host "Passing previous version $env:PREV_VERSION for cumulative planning."
}
if ($env:ANCHOR_VERSION -and (Test-Path $env:ANCHOR_STANDALONE_DIR)) {
$builderArgs += @('--anchor-source-dir', $env:ANCHOR_STANDALONE_DIR, '--anchor-version', $env:ANCHOR_VERSION)
Expand All @@ -424,6 +457,15 @@ jobs:
}
Write-Host "Passing cumulative update anchor $env:ANCHOR_VERSION."
}
if ($env:FULL_PACKAGE_URL -and $env:FULL_PACKAGE_CHECKSUM) {
$builderArgs += @(
'--full-package-url', $env:FULL_PACKAGE_URL,
'--full-package-name', $env:FULL_PACKAGE_NAME,
'--full-package-checksum', $env:FULL_PACKAGE_CHECKSUM,
'--full-package-size', $env:FULL_PACKAGE_SIZE
)
Write-Host "Passing GitHub full package URL $env:FULL_PACKAGE_URL."
}
dotnet run --project .\TelegramSearchBot.UpdateBuilder\TelegramSearchBot.UpdateBuilder.csproj `
-c Release `
--no-build `
Expand All @@ -433,11 +475,12 @@ jobs:
$catalogPath = '.\artifacts\update-feed\catalog.json'
$catalog = Get-Content $catalogPath -Raw | ConvertFrom-Json
$catalog | Add-Member -NotePropertyName UpdaterChecksum -NotePropertyValue ((Get-FileHash .\artifacts\update-feed\moder_update_updater.exe -Algorithm SHA512).Hash.ToLower()) -Force
$catalog | Add-Member -NotePropertyName UpdaterUrl -NotePropertyValue "$updateBaseUrl/moder_update_updater.exe" -Force
$catalog | ConvertTo-Json -Depth 10 | Set-Content $catalogPath -Encoding utf8
$catalog = Get-Content $catalogPath -Raw | ConvertFrom-Json
$entryTable = $catalog.Entries |
Sort-Object TargetVersion, PackagePath |
Select-Object TargetVersion, MinSourceVersion, MaxSourceVersion, IsCumulative, FileCount, CompressedSize, PackagePath |
Select-Object TargetVersion, MinSourceVersion, MaxSourceVersion, IsCumulative, PackageFormat, FileCount, CompressedSize, PackagePath, PackageUrl |
Format-Table -AutoSize |
Out-String
Write-Host "Update feed catalog entries:"
Expand All @@ -453,22 +496,6 @@ jobs:
throw "Cumulative update package missing for $env:ANCHOR_VERSION to $env:BUILD_VERSION."
}
}
- name: Package full release asset
shell: pwsh
env:
BUILD_VERSION: ${{ needs.prepare.outputs.build-version }}
run: |
$releaseDir = '.\artifacts\release'
if (Test-Path $releaseDir) {
Remove-Item $releaseDir -Recurse -Force
}
New-Item -ItemType Directory -Path $releaseDir | Out-Null
$packageName = "TelegramSearchBot-win-x64-full-$env:BUILD_VERSION.zip"
$packagePath = Join-Path $releaseDir $packageName
Compress-Archive -Path .\artifacts\standalone\* -DestinationPath $packagePath -CompressionLevel Optimal
$hashPath = "$packagePath.sha512"
$hash = (Get-FileHash $packagePath -Algorithm SHA512).Hash.ToLower()
Set-Content -Path $hashPath -Value "$hash $packageName" -Encoding ascii
- name: Upload update feed artifact
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -636,7 +663,7 @@ jobs:
$catalog = Get-Content $CatalogPath -Raw | ConvertFrom-Json
$reachablePaths = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($entry in $catalog.Entries) {
if ($entry.PackagePath) {
if ($entry.PackagePath -and $entry.PackagePath -like 'packages/*') {
$normalized = $entry.PackagePath -replace '^packages/', ''
[void]$reachablePaths.Add("TelegramSearchBot/packages/$normalized")
}
Expand Down Expand Up @@ -672,7 +699,9 @@ jobs:
}
}

b2 sync .\artifacts\update-feed\packages b2://$env:B2_BUCKET/TelegramSearchBot/packages --quiet
if (Test-Path .\artifacts\update-feed\packages) {
b2 sync .\artifacts\update-feed\packages b2://$env:B2_BUCKET/TelegramSearchBot/packages --quiet
}
b2 upload-file $env:B2_BUCKET .\artifacts\update-feed\moder_update_updater.exe TelegramSearchBot/moder_update_updater.exe
b2 upload-file $env:B2_BUCKET .\artifacts\update-feed\catalog.json TelegramSearchBot/catalog.json

Expand Down
4 changes: 2 additions & 2 deletions Docs/Build_and_Test_Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,10 @@ jobs:

仓库内实际的 `push.yml` 现在维护两条发布线:
- 保留根目录中的最终 ClickOnce bridge,供旧安装版本过渡到新更新链路。
- 每次主分支发布都会生成 `catalog.json`、`packages/` 和 `moder_update_updater.exe`,供 `%LOCALAPPDATA%\TelegramSearchBot\app` 中的独立安装目录继续使用 Moder.Update 协议升级。
- 每次主分支发布都会生成 `catalog.json`、必要的累计更新包和 `moder_update_updater.exe`,供 `%LOCALAPPDATA%\TelegramSearchBot\app` 中的独立安装目录继续使用 Moder.Update 协议升级。
- `push.yml` 会把校验、`telegram-bot-api` 构建、Moder.Update updater 构建、独立包构建和桥接包构建尽量拆成并行 job,最后再统一上传到 Backblaze B2 与 GitHub Releases。
- 同一分支上的发布运行会通过 workflow `concurrency` 串行化,避免并发发布互相覆盖 B2 包或 Release 产物。
- 同一条发布流水线会在新文件上传成功后裁剪 Backblaze B2 上重复或过期的更新包版本,并把 `TelegramSearchBot-win-x64-full-<version>.zip` 全量包上传到 GitHub Releases,便于手动分发和回滚
- 同一条发布流水线会在新文件上传成功后裁剪 Backblaze B2 上重复或过期的更新包版本。普通 `TelegramSearchBot-win-x64-full-<version>.zip` 全量包只上传到 GitHub Releases,并以绝对 URL 写入 `catalog.json` 作为完整包 fallback;B2 不再保存 step 包或自定义 MUP full `.zst` 包

### 监控与日志

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
- **自动更新**:
- `EnableAutoUpdate`: 是否启用内置自更新流程(默认true)
- `UpdateBaseUrl`: 更新目录根地址,默认使用 `https://clickonce.miaostay.com/TelegramSearchBot`
- **说明**: 首次安装仍通过 `Publish.html` 分发桥接版;之后程序会从 `catalog.json`、`packages/` 和 `moder_update_updater.exe` 拉取更新并升级独立安装目录。每次主分支发布也会同步上传一份 `TelegramSearchBot-win-x64-full-*.zip` 到 GitHub Releases,便于手动全量更新或回滚
- **说明**: 首次安装仍通过 `Publish.html` 分发桥接版;之后程序会从 `catalog.json`、累计更新包和 `moder_update_updater.exe` 拉取更新并升级独立安装目录。每次主分支发布也会同步上传一份普通 `TelegramSearchBot-win-x64-full-*.zip` 到 GitHub Releases,便于新用户手动部署、全量更新或回滚

- **AI相关**:
- `OllamaModelName`: 本地模型名称(默认"qwen2.5:72b-instruct-q2_K")
Expand Down
5 changes: 5 additions & 0 deletions TelegramSearchBot.Common/Model/Update/UpdateCatalog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ public sealed class UpdateCatalog
public DateTime LastUpdated { get; init; }
public string? MinRequiredVersion { get; init; }
public string? UpdaterChecksum { get; init; }
public string? UpdaterUrl { get; init; }
public string? FullPackageUrl { get; init; }
public string? FullPackageName { get; init; }
public string? FullPackageChecksum { get; init; }
public long? FullPackageSize { get; init; }
}
2 changes: 2 additions & 0 deletions TelegramSearchBot.Common/Model/Update/UpdateCatalogEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ namespace TelegramSearchBot.Common.Model.Update;
public sealed class UpdateCatalogEntry
{
public required string PackagePath { get; init; }
public string? PackageUrl { get; init; }
public string PackageFormat { get; init; } = UpdatePackageFormats.ModerUpdateZstd;
public required string TargetVersion { get; init; }
public required string MinSourceVersion { get; init; }
public string? MaxSourceVersion { get; init; }
Expand Down
1 change: 1 addition & 0 deletions TelegramSearchBot.Common/Model/Update/UpdateManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public sealed class UpdateManifest
public bool IsCumulative { get; init; }
public int ChainDepth { get; init; }
public required List<UpdateFile> Files { get; init; }
public List<UpdateFile>? SnapshotFiles { get; init; }
public required string Checksum { get; init; }
public DateTime CreatedAt { get; init; }
}
7 changes: 7 additions & 0 deletions TelegramSearchBot.Common/Model/Update/UpdatePackageFormats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TelegramSearchBot.Common.Model.Update;

public static class UpdatePackageFormats
{
public const string ModerUpdateZstd = "moder-update-zst";
public const string Zip = "zip";
}
14 changes: 12 additions & 2 deletions TelegramSearchBot.Test/ModerUpdateIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,15 +353,25 @@ public void CI_UpdateFeed_GeneratesCumulativeDeltaFromAnchor() {
Assert.Contains("'--anchor-version'", pushYmlContent);
Assert.Contains("'--base-cumulative-package'", pushYmlContent);
Assert.Contains("'--cumulative-source-package-dir'", pushYmlContent);
Assert.Contains("'--full-package-url'", pushYmlContent);
Assert.Contains("'--full-package-checksum'", pushYmlContent);
Assert.Contains("'--updater-url'", pushYmlContent);
Assert.Contains("'--package-base-url'", pushYmlContent);
Assert.Contains("Downloaded existing catalog.json from CDN", pushYmlContent);
Assert.Contains("Downloaded base cumulative package from CDN", pushYmlContent);
Assert.Contains("Downloaded cumulative source package from CDN", pushYmlContent);
Assert.Contains("historical source packages are not needed", pushYmlContent);
Assert.Contains("Passing cumulative update anchor", pushYmlContent);
Assert.Contains("Passing GitHub full package URL", pushYmlContent);
Assert.Contains("Update feed catalog entries", pushYmlContent);
Assert.Contains("Cumulative update package missing", pushYmlContent);
Assert.Contains("Failed to download cumulative update anchor", pushYmlContent);
Assert.Contains("refusing to publish a full-only update feed", pushYmlContent);
Assert.Contains("refusing to publish without cumulative package from anchor", pushYmlContent);
Assert.Contains("PackageUrl", pushYmlContent);
Assert.Contains("PackageFormat", pushYmlContent);
Assert.Contains("PackagePath -notlike '*-cumulative.zst'", pushYmlContent);
Assert.DoesNotContain("-or $entry.PackageUrl", pushYmlContent);
Assert.DoesNotContain("step package generation", pushYmlContent);
Assert.DoesNotContain("PackagePath -notlike '*-full.zst'", pushYmlContent);
Assert.DoesNotContain("Fetch existing update catalog from B2", pushYmlContent);
Assert.DoesNotContain("b2 download-file-by-name", pushYmlContent);
Assert.DoesNotContain("Write-Host \"PREV_VERSION=$prevVersion\" | Out-File", pushYmlContent);
Expand Down
Loading
Loading