-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathAbstractStrategy.cs
More file actions
181 lines (162 loc) · 7.19 KB
/
Copy pathAbstractStrategy.cs
File metadata and controls
181 lines (162 loc) · 7.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
using System;
using System.IO;
using System.Threading.Tasks;
using GeneralUpdate.Core.Differential;
using GeneralUpdate.Core.FileSystem;
using GeneralUpdate.Differential.Abstractions;
using GeneralUpdate.Core.Event;
using GeneralUpdate.Core.Pipeline;
using GeneralUpdate.Core;
using GeneralUpdate.Core.Configuration;
using GeneralUpdate.Core.Network;
using GeneralUpdate.Core.Hooks;
using IUpdateReporter = GeneralUpdate.Core.Download.Reporting.IUpdateReporter;
namespace GeneralUpdate.Core.Strategy
{
public abstract class AbstractStrategy : IStrategy
{
private const string Patchs = "patchs";
protected GlobalConfigInfo _configinfo = new();
/// <summary>Optional hooks for pre/post update callbacks.</summary>
protected IUpdateHooks? Hooks { get; set; }
/// <summary>Optional reporter for update status reporting.</summary>
protected IUpdateReporter? Reporter { get; set; }
/// <summary>Optional binary differ for differential patch updates.</summary>
public IDirtyStrategy? DirtyStrategy { get; set; }
/// <summary>Optional file-level binary differ for patch application.</summary>
public IBinaryDiffer? BinaryDiffer { get; set; }
/// <summary>DiffPipeline for parallel patch application with progress reporting.</summary>
public DiffPipeline? DiffPipeline { get; set; }
public virtual Task StartAppAsync() => throw new NotImplementedException();
public virtual async Task ExecuteAsync()
{
try
{
var status = ReportType.None;
var patchPath = StorageManager.GetTempDirectory(Patchs);
foreach (var version in _configinfo.UpdateVersions)
{
try
{
var context = CreatePipelineContext(version, patchPath);
var pipelineBuilder = BuildPipeline(context);
await pipelineBuilder.Build();
status = ReportType.Success;
}
catch (Exception e)
{
status = ReportType.Failure;
HandleExecuteException(e);
TryRollback();
}
finally
{
await VersionService.Report(_configinfo.ReportUrl
, version.RecordId
, status
, version.AppType
, _configinfo.Scheme
, _configinfo.Token);
}
}
Clear(patchPath);
Clear(_configinfo.TempPath);
await OnExecuteCompleteAsync();
}
catch (Exception e)
{
HandleExecuteException(e);
}
}
public virtual void Create(GlobalConfigInfo parameter) => _configinfo = parameter;
/// <summary>
/// Creates the pipeline context with common and platform-specific parameters.
/// Override this method to add platform-specific context parameters.
/// </summary>
protected virtual PipelineContext CreatePipelineContext(VersionInfo version, string patchPath)
{
var context = new PipelineContext();
// Common parameters
context.Add("ZipFilePath", Path.Combine(_configinfo.TempPath, $"{version.Name}{_configinfo.Format}"));
// Hash middleware
context.Add("Hash", version.Hash);
// Zip middleware
context.Add("Format", _configinfo.Format);
context.Add("Name", version.Name);
context.Add("Encoding", _configinfo.Encoding);
// Patch middleware
context.Add("SourcePath", _configinfo.InstallPath);
context.Add("PatchPath", patchPath);
context.Add("PatchEnabled", _configinfo.PatchEnabled);
// Binary differ for differential patching
context.Add("DirtyStrategy", DirtyStrategy);
context.Add("BinaryDiffer", BinaryDiffer);
// DiffPipeline for parallel patch application with progress reporting
context.Add("DiffPipeline", DiffPipeline);
return context;
}
/// <summary>
/// Builds the pipeline with middleware components.
/// Override this method to customize the pipeline for specific platforms.
/// </summary>
protected abstract PipelineBuilder BuildPipeline(PipelineContext context);
/// <summary>
/// Called after ExecuteAsync completes successfully.
/// Override this method to add platform-specific post-execution logic.
/// </summary>
protected virtual Task OnExecuteCompleteAsync()
{
return Task.CompletedTask;
}
/// <summary>
/// Handles exceptions during execution.
/// Override this method to customize error handling.
/// </summary>
protected virtual void HandleExecuteException(Exception e)
{
GeneralTracer.Error($"Strategy execution exception.", e);
EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message));
}
/// <summary>
/// Checks if a file exists at the specified path.
/// </summary>
protected static string CheckPath(string path, string name)
{
if (string.IsNullOrWhiteSpace(path) || string.IsNullOrWhiteSpace(name))
return string.Empty;
var tempPath = Path.Combine(path, name);
return File.Exists(tempPath) ? tempPath : string.Empty;
}
// ═══ Safe hooks/reporter wrappers (shared by all strategy subclasses) ═══
// Note: Each subclass builds its own UpdateContext via BuildUpdateContext().
// Subclasses should call hooks/reporter through their own context-aware wrappers.
// The Hooks and Reporter properties are declared here so subclasses inherit them
// without redeclaring.
/// <summary>
/// Attempts to restore from backup when a pipeline execution fails.
/// Only restores if a backup directory exists for the current version.
/// </summary>
private void TryRollback()
{
try
{
var backupDir = _configinfo.BackupDirectory;
if (!string.IsNullOrWhiteSpace(backupDir) && Directory.Exists(backupDir))
{
GeneralTracer.Warn($"AbstractStrategy.TryRollback: restoring from backup {backupDir} -> {_configinfo.InstallPath}");
StorageManager.Restore(backupDir, _configinfo.InstallPath);
GeneralTracer.Info("AbstractStrategy.TryRollback: restore completed.");
}
}
catch (Exception ex)
{
GeneralTracer.Error("AbstractStrategy.TryRollback: rollback failed.", ex);
}
}
private static void Clear(string path)
{
if (Directory.Exists(path))
StorageManager.DeleteDirectory(path);
}
}
}