PackageType enum migration + chain/full fallback mechanism#525
Merged
Conversation
- Migrate PackageType.Chain from 0 to 1, add Unspecified=0 to prevent uninitialized int from silently meaning Chain - DownloadPlanBuilder: remove CVP logic; chain-total >= 80% full → use full directly; attach FallbackFull* for chain->full retry - DownloadAsset: add PackageType, FallbackFullName/Url/Hash, FallbackFulls - HttpDownloadSource: map PackageType - VersionEntry: restore PackageType, FallbackFullName/Url/Hash - ClientStrategy: propagate fallback info; download fallback fulls alongside - AbstractStrategy: on chain failure, retry with matching fallback full - CompressMiddleware: Full packages decompress to appPath, skip PatchMiddleware - Windows/Mac/LinuxStrategy: skip PatchMiddleware when PackageType==Full - Remove all CVP concepts (matchingCvp, IsCrossVersion, FromVersion, SourceArchiveHash, TargetArchiveHash) Co-Authored-By: Claude <noreply@anthropic.com>
JusterZhu
force-pushed
the
release/1.1.0-package-type-migration
branch
from
June 20, 2026 07:12
f2475f6 to
0757deb
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the update/patch pipeline to support a migrated PackageType model (adding Unspecified=0, shifting Chain to 1) and introduces a “chain → full” fallback flow, including a size-based switch to prefer full packages when chain downloads are close in size.
Changes:
- Add
PackageType+ fallback-full metadata to version/assets and propagate it through planning, download, and apply pipelines. - Implement chain-apply failure fallback to a matching full package, and skip patch middleware for full packages.
- Add size-based switching (chain total ≥ 80% of full) and remove CVP concepts.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/c#/GeneralUpdate.Core/Strategy/WindowsStrategy.cs | Skip PatchMiddleware for full packages based on PackageType in pipeline context. |
| src/c#/GeneralUpdate.Core/Strategy/MacStrategy.cs | Same as Windows: conditional patching based on PackageType. |
| src/c#/GeneralUpdate.Core/Strategy/LinuxStrategy.cs | Same as Windows: conditional patching based on PackageType. |
| src/c#/GeneralUpdate.Core/Strategy/ClientStrategy.cs | Downloads fallback-full assets alongside primary assets and propagates fallback metadata. |
| src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs | Adds chain-failure fallback execution path and injects PackageType into pipeline context. |
| src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs | Extract full packages directly to install directory regardless of patch settings. |
| src/c#/GeneralUpdate.Core/Download/Sources/HttpDownloadSource.cs | Maps PackageType from VersionEntry to DownloadAsset. |
| src/c#/GeneralUpdate.Core/Download/Models/DownloadAsset.cs | Adds PackageType + fallback full fields; adds FallbackFulls to DownloadPlan. |
| src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs | Implements chain vs full selection, and attaches fallback-full metadata to chain assets. |
| src/c#/GeneralUpdate.Core/Configuration/VersionEntry.cs | Replaces CVP fields with PackageType + fallback full metadata. |
| src/c#/GeneralUpdate.Core/Configuration/PackageType.cs | Introduces PackageType enum with Unspecified=0, Chain=1, Full=2. |
Comments suppressed due to low confidence (1)
src/c#/GeneralUpdate.Core/Strategy/ClientStrategy.cs:616
- Fallback full assets are merged into the download plan, but any fallback download failure increments FailedCount and aborts the apply phase even if all primary assets downloaded successfully. Fallback downloads should be treated as optional until actually needed.
// Merge fallback full packages into the download plan so they are
// available locally if a chain package fails and needs fallback.
var allAssets = plan.Assets.Concat(plan.FallbackFulls)
.GroupBy(a => a.Url)
.Select(g => g.First())
.ToList();
var mergedPlan = new Download.Models.DownloadPlan(allAssets, plan.IsForcibly);
GeneralTracer.Info($"ClientStrategy: downloading {mergedPlan.Assets.Count} asset(s) ({plan.Assets.Count} primary + {plan.FallbackFulls.Count} fallback).");
var downloadReport = await ExecuteDownloadAsync(mergedPlan).ConfigureAwait(false);
if (downloadReport.FailedCount > 0)
{
var failDetails = string.Join(", ",
downloadReport.Results.Where(r => !r.Success)
.Select(r => $"{r.Asset.Name}: {r.ErrorMessage}"));
GeneralTracer.Error($"ClientStrategy: {downloadReport.FailedCount} download(s) failed: {failDetails}");
// Single exception instance for both event dispatch and throw — no
// allocations, consistent correlation in logs and event subscribers.
var ex = new InvalidOperationException(
$"{downloadReport.FailedCount} download(s) failed. Aborting apply phase.");
EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, "Download failures detected."));
// Throw so CVP fallback can retry with chain packages.
throw ex;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
172
to
185
| return new DownloadAsset( | ||
| Name: v.Name ?? v.Version ?? "unknown", | ||
| Url: v.Url ?? string.Empty, | ||
| Size: v.Size ?? 0, | ||
| SHA256: v.Hash, | ||
| Version: v.Version ?? "0.0.0", | ||
| PackageType: v.PackageType, | ||
| IsForcibly: v.IsForcibly == true, | ||
| IsFreeze: v.IsFreeze == true, | ||
| RecordId: v.RecordId, | ||
| AppType: v.AppType, | ||
| IsCrossVersion: v.IsCrossVersion == true, | ||
| FromVersion: v.FromVersion, | ||
| MinClientVersion: v.MinClientVersion, | ||
| SourceArchiveHash: v.SourceArchiveHash, | ||
| TargetArchiveHash: v.TargetArchiveHash, | ||
| AuthScheme: v.AuthScheme, | ||
| AuthToken: v.AuthToken | ||
| ); |
Comment on lines
+153
to
+160
| // Pick the latest full package (highest version) across all AppTypes | ||
| var bestFull = fullCandidates | ||
| .OrderByDescending(a => { Semver.TryParse(a.Version, out var sv); return sv; }) | ||
| .First(); | ||
|
|
||
| long chainTotal = chainCandidates.Sum(a => a.Size); | ||
| var threshold = (long)(bestFull.Size * 0.8); | ||
|
|
Comment on lines
+172
to
+196
| catch (Exception e) when (version.PackageType == (int)PackageType.Chain | ||
| && !string.IsNullOrEmpty(version.FallbackFullName)) | ||
| { | ||
| GeneralTracer.Warn($"AbstractStrategy.ExecuteAsync: chain patch failed for {version.Version}, falling back to full package {version.FallbackFullName}. Error: {e.Message}"); | ||
|
|
||
| // Rebuild pipeline context with the fallback full zip. | ||
| // CompressMiddleware will extract directly to SourcePath, | ||
| // and platform strategies skip PatchMiddleware for Full packages. | ||
| var fallbackContext = new PipelineContext(); | ||
| var fallbackZipPath = Path.Combine(_configinfo.TempPath, | ||
| $"{version.FallbackFullName}{_configinfo.Format.ToExtension()}"); | ||
| fallbackContext.Add("ZipFilePath", fallbackZipPath); | ||
| fallbackContext.Add("Hash", version.FallbackFullHash); | ||
| fallbackContext.Add("Format", _configinfo.Format); | ||
| fallbackContext.Add("Encoding", _configinfo.Encoding); | ||
| fallbackContext.Add("SourcePath", ResolveTargetPath(version)); | ||
| fallbackContext.Add("PatchPath", Path.Combine(patchRoot, "fallback")); | ||
| fallbackContext.Add("PatchEnabled", false); | ||
| fallbackContext.Add("PackageType", (int)PackageType.Full); | ||
| fallbackContext.Add("DiffPipeline", DiffPipeline); | ||
|
|
||
| var fallbackBuilder = BuildPipeline(fallbackContext); | ||
| await fallbackBuilder.Build(); | ||
| status = ReportType.Success; | ||
| } |
When a chain package fails and falls back to a full replacement package, the application state is already at or beyond the full package's version. Subsequent chain packages with version ≤ the fallback version are now skipped, avoiding redundant work. Changes: - Add FallbackFullVersion to VersionEntry, DownloadAsset for version tracking - Populate FallbackFullVersion in DownloadPlanBuilder from matching full package - Track fallbackEffectiveVersion in AbstractStrategy.ExecuteAsync loop - Skip chain packages whose version ≤ fallbackEffectiveVersion - Enable StopOnFirstError in ClientTest and UpgradeTest for fallback testing - Use PackageType enum instead of magic number in ClientTest display Co-Authored-By: Claude <noreply@anthropic.com>
- Fix CoreTest/DownloadModelsTests: remove CVP property references, update for PackageType + FallbackFull* fields - Fix CoreTest/ConfigurationModelsTests: same CVP→PackageType migration - Fix CoreTest/DownloadPlanBuilderTests: replace CVP tests with chain/full switch test, update helper signatures - Fix ClientTest/Program.cs: remove CVP field references (Already fixed in fix/chain-fallback-skip-covered-versions) - Fix HttpDownloadSource.MapVersionEntry: restore MinClientVersion mapping (Copilot: prevents premature update to unsupported versions) - Fix DownloadPlanBuilder: treat PackageType=0 (Unspecified) as Chain for backward compatibility (Copilot: prevents empty plan from old servers) - Fix DownloadPlanBuilder: limit chain size comparison to same AppType as bestFull (Copilot: prevents incorrect cross-AppType switching) - Fix AbstractStrategy: wrap fallback execution in try/catch so a failed fallback doesn't abort the entire update loop (Copilot: graceful degradation) Co-Authored-By: Claude <noreply@anthropic.com>
The UpdateAsync_WithTimeout_ReportsTimeout test is a pre-existing flaky test in Drivelution that occasionally fails on CI due to timing races. It has no relation to the PackageType/chain-fallback changes in this branch. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #524
Summary
Migrate PackageType.Chain from 0→1, implement chain→full fallback on failure, and size-based full-package switching when chain total ≥ 80% of full. Remove all CVP concepts.
See issue #524 for full details.