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
100 changes: 80 additions & 20 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,19 +228,77 @@ jobs:
/p:Version=$env:BUILD_VERSION `
/p:PublishSingleFile=false `
-o .\artifacts\standalone
- name: Fetch existing update catalog from B2
shell: pwsh
env:
B2_BUCKET: ${{ secrets.B2_BUCKET }}
B2_APPKEY_ID: ${{ secrets.B2_APPKEY_ID }}
B2_APPKEY: ${{ secrets.B2_APPKEY }}
run: |
if (-not (Get-Command python -ErrorAction SilentlyContinue)) {
Write-Host "Python not available, skipping catalog fetch."
exit 0
}
pip install --quiet --cache-dir C:\pip-cache "b2==$env:B2_CLI_VERSION"
b2 account authorize $env:B2_APPKEY_ID $env:B2_APPKEY --quiet
if ($LASTEXITCODE -ne 0) {
Write-Host "B2 auth failed, will create fresh catalog."
exit 0
}
try {
b2 download-file-by-name $env:B2_BUCKET TelegramSearchBot/catalog.json .\artifacts\existing-catalog.json --quiet
Write-Host "Downloaded existing catalog.json for merge."
} catch {
Write-Host "No existing catalog found on B2, will create fresh catalog."
}
b2 account clear
Comment on lines +248 to +254
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

try/catch won't trap b2 failures; native commands return exit codes, not terminating errors.

If b2 download-file-by-name fails (e.g., 404 because the catalog doesn't exist yet), the catch block won't run; the step continues, and the later Test-Path '.\artifacts\existing-catalog.json' may pass against a stale or partial file (b2 sometimes touches the path before failing). Gate on $LASTEXITCODE instead so the "no catalog" log message accurately reflects what happened.

🛠️ Suggested fix
-        try {
-          b2 download-file-by-name $env:B2_BUCKET TelegramSearchBot/catalog.json .\artifacts\existing-catalog.json --quiet
-          Write-Host "Downloaded existing catalog.json for merge."
-        } catch {
-          Write-Host "No existing catalog found on B2, will create fresh catalog."
-        }
+        b2 download-file-by-name $env:B2_BUCKET TelegramSearchBot/catalog.json .\artifacts\existing-catalog.json --quiet 2>$null
+        if ($LASTEXITCODE -eq 0 -and (Test-Path .\artifacts\existing-catalog.json)) {
+          Write-Host "Downloaded existing catalog.json for merge."
+        } else {
+          if (Test-Path .\artifacts\existing-catalog.json) { Remove-Item .\artifacts\existing-catalog.json -Force }
+          Write-Host "No existing catalog found on B2, will create fresh catalog."
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/push.yml around lines 248 - 254, The try/catch around the
b2 call won't catch non-terminating failures; instead run the b2
download-file-by-name command, then check $LASTEXITCODE to determine success,
log "Downloaded existing catalog.json for merge." only on success and log "No
existing catalog found on B2, will create fresh catalog." on failure; use
Test-Path '.\artifacts\existing-catalog.json' only after confirming
$LASTEXITCODE was 0 (or explicitly remove any stale/partial file on failure) and
then call b2 account clear regardless. Ensure you reference the b2
download-file-by-name invocation, $LASTEXITCODE, Test-Path
'.\artifacts\existing-catalog.json', and b2 account clear when making the
changes.

- name: Fetch previous release standalone for step package comparison
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$prevRelease = gh release list --repo ${{ github.repository }} --limit 1 --exclude-drafts --json tagName,name | ConvertFrom-Json
if ($prevRelease.Count -gt 0) {
$prevTag = $prevRelease[0].tagName
$prevVersion = $prevTag -replace '^v', ''
Write-Host "Previous release: $prevTag (version: $prevVersion)"
$prevArchive = ".\artifacts\prev-standalone.zip"
gh release download $prevTag --repo ${{ github.repository }} --pattern "TelegramSearchBot-win-x64-full-*.zip" --dir artifacts\prev-release
$zipFile = Get-ChildItem artifacts\prev-release -Filter "TelegramSearchBot-win-x64-full-*.zip" | Select-Object -First 1
if ($zipFile) {
Expand-Archive -Path $zipFile.FullName -DestinationPath .\artifacts\prev-standalone -Force
Write-Host "PREV_VERSION=$prevVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "PREV_STANDALONE_DIR=artifacts\prev-standalone" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Comment on lines +270 to +271
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Write-Host piped to Out-File writes nothing to $GITHUB_ENV.

Write-Host writes to the information stream and emits no objects to the success pipeline, so Out-File receives nothing and PREV_VERSION / PREV_STANDALONE_DIR are never exported. Subsequent steps will see $env:PREV_VERSION as empty, the if ($env:PREV_VERSION -and ...) guard at line 294 fails, and step packages will silently never be generated — the entire chain-update feature regresses to fallback-only. The same idiom is used correctly elsewhere in this file (e.g., line 44).

🛠️ Proposed fix
-            Write-Host "PREV_VERSION=$prevVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
-            Write-Host "PREV_STANDALONE_DIR=artifacts\prev-standalone" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
+            "PREV_VERSION=$prevVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
+            "PREV_STANDALONE_DIR=artifacts\prev-standalone" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
+            Write-Host "Exported PREV_VERSION=$prevVersion and PREV_STANDALONE_DIR."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Write-Host "PREV_VERSION=$prevVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "PREV_STANDALONE_DIR=artifacts\prev-standalone" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"PREV_VERSION=$prevVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"PREV_STANDALONE_DIR=artifacts\prev-standalone" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "Exported PREV_VERSION=$prevVersion and PREV_STANDALONE_DIR."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/push.yml around lines 270 - 271, The current steps use
Write-Host piped to Out-File which emits nothing to the pipeline, so
PREV_VERSION and PREV_STANDALONE_DIR are not written to $env:GITHUB_ENV; replace
those lines to write directly to the GitHub env file (for example use
Add-Content -Path $env:GITHUB_ENV -Value "PREV_VERSION=$prevVersion" and
Add-Content -Path $env:GITHUB_ENV -Value
"PREV_STANDALONE_DIR=artifacts\prev-standalone") so the variables PREV_VERSION
and PREV_STANDALONE_DIR are actually exported into the environment for later
steps.

Write-Host "Extracted previous standalone from $($zipFile.Name)."
} else {
Write-Host "No previous full zip found; step package will not be generated."
}
} else {
Write-Host "No previous release found; this is the first release."
}
- name: Build Moder.Update feed
shell: pwsh
env:
BUILD_VERSION: ${{ needs.prepare.outputs.build-version }}
run: |
$builderArgs = @(
'--source-dir', '.\artifacts\standalone',
'--output-dir', '.\artifacts\update-feed',
'--target-version', $env:BUILD_VERSION,
'--min-source-version', $env:LEGACY_BRIDGE_VERSION
)
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."
}
dotnet run --project .\TelegramSearchBot.UpdateBuilder\TelegramSearchBot.UpdateBuilder.csproj `
-c Release `
--no-build `
-- `
--source-dir .\artifacts\standalone `
--output-dir .\artifacts\update-feed `
--target-version $env:BUILD_VERSION `
--min-source-version $env:LEGACY_BRIDGE_VERSION
-- @builderArgs
Copy-Item .\moder-update-bin\moder_update_updater.exe .\artifacts\update-feed\moder_update_updater.exe -Force
$catalogPath = '.\artifacts\update-feed\catalog.json'
$catalog = Get-Content $catalogPath -Raw | ConvertFrom-Json
Expand Down Expand Up @@ -415,29 +473,35 @@ jobs:
}
}

function Prune-B2PackageVersions {
function Prune-B2PackagesByCatalog {
param(
[Parameter(Mandatory = $true)]
[string[]]$KeepRelativePaths
[string]$CatalogPath
)

$items = @(Get-B2Versions -B2Uri "b2://$env:B2_BUCKET/TelegramSearchBot/packages/" -Recursive)
if ($items.Count -eq 0) {
if (!(Test-Path $CatalogPath)) {
Write-Host "Catalog not found, skipping reachability prune."
return
}

$keepSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($relativePath in $KeepRelativePaths) {
$normalizedPath = $relativePath.Replace('\', '/')
[void]$keepSet.Add("TelegramSearchBot/packages/$normalizedPath")
$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) {
$normalized = $entry.PackagePath -replace '^packages/', ''
[void]$reachablePaths.Add("TelegramSearchBot/packages/$normalized")
}
}
Write-Host "Catalog has $($catalog.Entries.Count) entries, $($reachablePaths.Count) reachable package paths."

$items = @(Get-B2Versions -B2Uri "b2://$env:B2_BUCKET/TelegramSearchBot/packages/" -Recursive)
if ($items.Count -eq 0) { return }

foreach ($group in ($items | Group-Object fileName)) {
$sortedItems = $group.Group | Sort-Object uploadTimestamp -Descending
if ($keepSet.Contains($group.Name)) {
if ($reachablePaths.Contains($group.Name)) {
$sortedItems = $sortedItems | Select-Object -Skip 1
}

foreach ($item in $sortedItems) {
Remove-B2VersionById -FileId $item.fileId
}
Expand All @@ -463,11 +527,7 @@ jobs:
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

$packageRoot = (Resolve-Path .\artifacts\update-feed\packages).Path
$packageFiles = Get-ChildItem $packageRoot -File -Recurse | ForEach-Object {
[System.IO.Path]::GetRelativePath($packageRoot, $_.FullName)
}
Prune-B2PackageVersions -KeepRelativePaths $packageFiles
Prune-B2PackagesByCatalog -CatalogPath '.\artifacts\update-feed\catalog.json'
Prune-B2FileVersions -RelativePath 'TelegramSearchBot/moder_update_updater.exe'
Prune-B2FileVersions -RelativePath 'TelegramSearchBot/catalog.json'
b2 account clear
Expand Down
10 changes: 10 additions & 0 deletions TelegramSearchBot.Common/Model/Update/UpdateCatalog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace TelegramSearchBot.Common.Model.Update;

public sealed class UpdateCatalog
{
public required string LatestVersion { get; init; }
public required List<UpdateCatalogEntry> Entries { get; init; }
public DateTime LastUpdated { get; init; }
public string? MinRequiredVersion { get; init; }
public string? UpdaterChecksum { get; init; }
}
16 changes: 16 additions & 0 deletions TelegramSearchBot.Common/Model/Update/UpdateCatalogEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace TelegramSearchBot.Common.Model.Update;

public sealed class UpdateCatalogEntry
{
public required string PackagePath { get; init; }
public required string TargetVersion { get; init; }
public required string MinSourceVersion { get; init; }
public string? MaxSourceVersion { get; init; }
public bool IsCumulative { get; init; }
public bool IsAnchor { get; init; }
public int ChainDepth { get; init; }
public required string PackageChecksum { get; init; }
public int FileCount { get; init; }
public long CompressedSize { get; init; }
public long UncompressedSize { get; init; }
}
7 changes: 7 additions & 0 deletions TelegramSearchBot.Common/Model/Update/UpdateFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TelegramSearchBot.Common.Model.Update;

public sealed class UpdateFile
{
public required string RelativePath { get; init; }
public required string NewChecksum { get; init; }
}
14 changes: 14 additions & 0 deletions TelegramSearchBot.Common/Model/Update/UpdateManifest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace TelegramSearchBot.Common.Model.Update;

public sealed class UpdateManifest
{
public required string TargetVersion { get; init; }
public required string MinSourceVersion { get; init; }
public string? MaxSourceVersion { get; init; }
public bool IsAnchor { get; init; }
public bool IsCumulative { get; init; }
public int ChainDepth { get; init; }
public required List<UpdateFile> Files { get; init; }
public required string Checksum { get; init; }
public DateTime CreatedAt { get; init; }
}
Loading
Loading