Skip to content

Commit 2765bb0

Browse files
JusterZhuclaude
andauthored
feat: generate generalupdate.manifest.json alongside sample project structure (#75)
* feat: generate generalupdate.manifest.json alongside sample project structure When the user clicks 'Generate Sample Project Structure', the tool now also writes generalupdate.manifest.json into the sample output directory. This makes the sample output self-contained — users get both the published binaries and the update manifest in one step. Changes: - SamplePublisherService.PublishAsync: add optional ManifestModel parameter; if provided, serialize and write generalupdate.manifest.json into output root - ConfigViewModel.GenerateSample: build ManifestModel from UI fields (falling back to parsed csproj data via ManifestGeneratorService.FromCsprojInfo) and pass it to PublishAsync Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: use IsNullOrWhiteSpace in FromCsprojInfo and add semver validation to GenerateSample - ManifestGeneratorService.FromCsprojInfo: replace ?? with !string.IsNullOrWhiteSpace() so that empty/unset UI fields correctly fall back to parsed csproj values (MainAppName, AppType, UpdateAppName, UpdatePath). - ConfigViewModel.GenerateSample: run semver validation on ClientVersion and UpgradeClientVersion before writing the manifest, matching the behavior of the Generate pipeline. Fixes review feedback from Copilot on PR #75. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e34f1c7 commit 2765bb0

3 files changed

Lines changed: 58 additions & 6 deletions

File tree

src/Services/ManifestGeneratorService.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,31 @@ public static string GenerateJson(ManifestModel model)
2121
return JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true });
2222
}
2323

24+
/// <summary>
25+
/// Builds a <see cref="ManifestModel"/> from parsed csproj data,
26+
/// with user input overriding where a non-blank value was supplied.
27+
/// Uses <see cref="string.IsNullOrWhiteSpace"/> so that empty strings
28+
/// (the default for unedited UI fields) still fall back to csproj data.
29+
/// </summary>
2430
public static ManifestModel FromCsprojInfo(CsprojInfo client, CsprojInfo? upgrade, ManifestModel? userInput = null)
2531
{
2632
return new ManifestModel
2733
{
28-
MainAppName = userInput?.MainAppName ?? client.AssemblyName,
34+
MainAppName = !string.IsNullOrWhiteSpace(userInput?.MainAppName)
35+
? userInput.MainAppName
36+
: client.AssemblyName,
2937
ClientVersion = userInput?.ClientVersion ?? "",
30-
AppType = userInput?.AppType ?? "Client",
31-
UpdateAppName = userInput?.UpdateAppName ?? upgrade?.AssemblyName ?? "Update.exe",
38+
AppType = !string.IsNullOrWhiteSpace(userInput?.AppType)
39+
? userInput.AppType
40+
: "Client",
41+
UpdateAppName = !string.IsNullOrWhiteSpace(userInput?.UpdateAppName)
42+
? userInput.UpdateAppName
43+
: upgrade?.AssemblyName ?? "Update.exe",
3244
UpgradeClientVersion = userInput?.UpgradeClientVersion ?? "",
3345
ProductId = userInput?.ProductId ?? "",
34-
UpdatePath = userInput?.UpdatePath ?? "update/"
46+
UpdatePath = !string.IsNullOrWhiteSpace(userInput?.UpdatePath)
47+
? userInput.UpdatePath
48+
: "update/"
3549
};
3650
}
3751
}

src/Services/SamplePublisherService.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ public static class SamplePublisherService
1111
/// <summary>
1212
/// Runs <c>dotnet publish</c> for the client and upgrade projects,
1313
/// then assembles the output into the Tools running directory.
14+
/// When <paramref name="manifest"/> is provided, also writes
15+
/// <c>generalupdate.manifest.json</c> into the output root.
1416
/// </summary>
15-
public static async Task<string> PublishAsync(CsprojInfo client, CsprojInfo? upgrade, string updatePath, string? baseOutput = null)
17+
public static async Task<string> PublishAsync(
18+
CsprojInfo client,
19+
CsprojInfo? upgrade,
20+
string updatePath,
21+
string? baseOutput = null,
22+
ManifestModel? manifest = null)
1623
{
1724
var outputRoot = baseOutput ?? Path.Combine(AppContext.BaseDirectory, "sample_output");
1825
if (Directory.Exists(outputRoot))
@@ -33,6 +40,14 @@ public static async Task<string> PublishAsync(CsprojInfo client, CsprojInfo? upg
3340
await RunDotnetPublishAsync(upgrade.CsprojPath, updateDir);
3441
}
3542

43+
// Write manifest.json into the output root if provided
44+
if (manifest != null)
45+
{
46+
var json = ManifestGeneratorService.GenerateJson(manifest);
47+
var manifestPath = Path.Combine(outputRoot, "generalupdate.manifest.json");
48+
await File.WriteAllTextAsync(manifestPath, json);
49+
}
50+
3651
return outputRoot;
3752
}
3853

src/ViewModels/ConfigViewModel.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,30 @@ private async Task GenerateSample()
225225
return;
226226
}
227227

228-
var output = await SamplePublisherService.PublishAsync(client, upgrade, Model.UpdatePath);
228+
// Build manifest from current UI fields, falling back to parsed csproj data
229+
var manifest = ManifestGeneratorService.FromCsprojInfo(client, upgrade,
230+
new ManifestModel
231+
{
232+
MainAppName = Model.MainAppName,
233+
ClientVersion = Model.ClientVersion,
234+
AppType = Model.AppType,
235+
UpdateAppName = Model.UpdateAppName,
236+
UpgradeClientVersion = Model.UpgradeClientVersion,
237+
ProductId = Model.ProductId,
238+
UpdatePath = Model.UpdatePath
239+
});
240+
241+
// Validate semver before writing manifest (same check as the Generate pipeline)
242+
var validateCtx = new PipelineContext();
243+
SemverValidateStep.Validate(manifest.ClientVersion, nameof(manifest.ClientVersion), validateCtx);
244+
SemverValidateStep.Validate(manifest.UpgradeClientVersion, nameof(manifest.UpgradeClientVersion), validateCtx);
245+
if (validateCtx.Errors.Count > 0)
246+
{
247+
Model.StatusText = string.Join("; ", validateCtx.Errors);
248+
return;
249+
}
250+
251+
var output = await SamplePublisherService.PublishAsync(client, upgrade, Model.UpdatePath, manifest: manifest);
229252
Model.StatusText = $"{_loc["Config.SampleGenerated"]}: {output}";
230253

231254
await DialogHelper.ShowInfoAsync(_loc["Config.Title"],

0 commit comments

Comments
 (0)