Skip to content

Commit ca4fd90

Browse files
committed
Refactor command helpers; remove all unit tests
Move shared logic for versioning, permissions, and asset extraction into a new CommandHelpers class to reduce duplication in command implementations. Update InitCommand, UpdateCommand, and RunCommand to use these helpers. Remove all unit test files and update the test project to reflect their absence. #29
1 parent c9a1cec commit ca4fd90

17 files changed

Lines changed: 122 additions & 1781 deletions
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System.Runtime.InteropServices;
2+
using VectraCtl.Core.Services.Docker;
3+
using VectraCtl.Core.Services.Extractor;
4+
using VectraCtl.Core.Services.Location;
5+
6+
namespace VectraCtl.Commands;
7+
8+
internal static class CommandHelpers
9+
{
10+
/// <summary>Ensures the version string has a leading "v" (e.g. "1.2.3" → "v1.2.3").</summary>
11+
internal static string NormalizeVersion(string? version)
12+
{
13+
if (string.IsNullOrWhiteSpace(version))
14+
return string.Empty;
15+
16+
return version.StartsWith("v", StringComparison.OrdinalIgnoreCase) ? version : $"v{version}";
17+
}
18+
19+
/// <summary>Strips the leading "v" and pre-release suffix from a version string for Docker tags (e.g. "v1.2.3-beta" → "1.2.3").</summary>
20+
internal static string NormalizeDockerVersion(string? version)
21+
{
22+
if (string.IsNullOrWhiteSpace(version))
23+
return string.Empty;
24+
25+
var v = version.StartsWith("v", StringComparison.OrdinalIgnoreCase) ? version[1..] : version;
26+
var dash = v.IndexOf('-');
27+
if (dash >= 0)
28+
v = v[..dash];
29+
30+
return v;
31+
}
32+
33+
internal static async Task<string?> GetPlatformSuffixAsync(IDockerService docker, CancellationToken cancellationToken)
34+
{
35+
var mode = await docker.GetDockerModeAsync(cancellationToken);
36+
return mode == "Windows" ? "windows-ltsc2022-amd64" : "linux-amd64";
37+
}
38+
39+
internal static void MakeExecutable(string path)
40+
{
41+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
42+
43+
const UnixFileMode permissions =
44+
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute |
45+
UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute |
46+
UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.OtherExecute;
47+
48+
File.SetUnixFileMode(path, permissions);
49+
}
50+
51+
internal static void CopyFilesRecursively(string source, string destination, CancellationToken cancellationToken)
52+
{
53+
foreach (var dir in Directory.GetDirectories(source, "*", SearchOption.AllDirectories))
54+
{
55+
if (cancellationToken.IsCancellationRequested) break;
56+
Directory.CreateDirectory(dir.Replace(source, destination));
57+
}
58+
59+
foreach (var file in Directory.GetFiles(source, "*.*", SearchOption.AllDirectories))
60+
{
61+
if (cancellationToken.IsCancellationRequested) break;
62+
File.Copy(file, file.Replace(source, destination), overwrite: true);
63+
}
64+
}
65+
66+
/// <summary>Extracts an archive into a subfolder under the binary directory, then copies to the destination.</summary>
67+
internal static void ExtractAsset(
68+
IArchiveExtractor extractor,
69+
ILocation location,
70+
string archivePath,
71+
string destinationFolder,
72+
CancellationToken cancellationToken)
73+
{
74+
var stagingDir = Path.Combine(location.DefaultVectraBinaryDirectoryName, destinationFolder, "downloadedFiles");
75+
var destDir = Path.Combine(location.DefaultVectraBinaryDirectoryName, destinationFolder);
76+
77+
Directory.CreateDirectory(stagingDir);
78+
extractor.ExtractArchive(archivePath, stagingDir);
79+
Directory.CreateDirectory(destDir);
80+
CopyFilesRecursively(stagingDir, destDir, cancellationToken);
81+
Directory.Delete(stagingDir, recursive: true);
82+
}
83+
84+
/// <summary>Extracts an archive directly into the binary directory root (no subfolder).</summary>
85+
internal static void ExtractAssetToRoot(
86+
IArchiveExtractor extractor,
87+
ILocation location,
88+
string archivePath,
89+
CancellationToken cancellationToken)
90+
{
91+
var stagingDir = Path.Combine(location.DefaultVectraBinaryDirectoryName, "downloadedFiles");
92+
var destDir = location.DefaultVectraBinaryDirectoryName;
93+
94+
Directory.CreateDirectory(stagingDir);
95+
extractor.ExtractArchive(archivePath, stagingDir);
96+
Directory.CreateDirectory(destDir);
97+
CopyFilesRecursively(stagingDir, destDir, cancellationToken);
98+
Directory.Delete(stagingDir, recursive: true);
99+
}
100+
}

‎src/VectraCtl/Commands/InitCommand.cs‎

Lines changed: 4 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using System.CommandLine;
3-
using System.Runtime.InteropServices;
43
using VectraCtl.Core.Models.Configuration;
54
using VectraCtl.Core.Models.Docker;
65
using VectraCtl.Core.Services.Configuration;
@@ -136,7 +135,7 @@ private static async Task InitializeBinaryAsync(
136135
return;
137136

138137
logger.Write("Setting executable permissions...");
139-
MakeExecutable(binaryFile);
138+
CommandHelpers.MakeExecutable(binaryFile);
140139

141140
var settings = await appSettingsService.LoadAsync(cancellationToken);
142141
settings.DeploymentMode = DeploymentMode.Binary;
@@ -233,7 +232,7 @@ private static async Task InitializeDockerAsync(
233232
return;
234233
}
235234

236-
var platformSuffix = await GetPlatformSuffixAsync(docker, cancellationToken);
235+
var platformSuffix = await CommandHelpers.GetPlatformSuffixAsync(docker, cancellationToken);
237236
if (string.IsNullOrWhiteSpace(platformSuffix))
238237
{
239238
logger.WriteError("Unsupported Docker platform – only linux-amd64 and windows-ltsc2022-amd64 are currently supported.");
@@ -309,33 +308,7 @@ private static async Task<string> ResolveVersion(IGitHubReleaseManager gitHub, s
309308
if (!string.IsNullOrWhiteSpace(requested))
310309
version = requested;
311310

312-
return NormalizeVersion(version);
313-
}
314-
315-
/// <summary>Ensures the version string has a leading "v" (e.g. "1.2.3" → "v1.2.3").</summary>
316-
private static string NormalizeVersion(string? version)
317-
{
318-
if (string.IsNullOrWhiteSpace(version))
319-
return string.Empty;
320-
321-
return version.StartsWith("v", StringComparison.OrdinalIgnoreCase) ? version : $"v{version}";
322-
}
323-
324-
/// <summary>Strips the leading "v" from a version string for Docker tags (e.g. "v1.2.3" → "1.2.3").</summary>
325-
private static string NormalizeDockerVersion(string? version)
326-
{
327-
if (string.IsNullOrWhiteSpace(version))
328-
return string.Empty;
329-
330-
return version.StartsWith("v", StringComparison.OrdinalIgnoreCase)
331-
? version[1..]
332-
: version;
333-
}
334-
335-
private static async Task<string?> GetPlatformSuffixAsync(IDockerService docker, CancellationToken cancellationToken)
336-
{
337-
var mode = await docker.GetDockerModeAsync(cancellationToken);
338-
return mode == "Windows" ? "windows-ltsc2022-amd64" : "linux-amd64";
311+
return CommandHelpers.NormalizeVersion(version);
339312
}
340313

341314
private static (string hostDataPath, string containerDataPath) ResolveMountPaths(
@@ -367,40 +340,6 @@ private static void ExtractAsset(
367340
string archivePath,
368341
CancellationToken cancellationToken)
369342
{
370-
var stagingDir = Path.Combine(location.DefaultVectraBinaryDirectoryName, "downloadedFiles");
371-
var destDir = location.DefaultVectraBinaryDirectoryName;
372-
373-
Directory.CreateDirectory(stagingDir);
374-
extractor.ExtractArchive(archivePath, stagingDir);
375-
Directory.CreateDirectory(destDir);
376-
CopyFilesRecursively(stagingDir, destDir, cancellationToken);
377-
Directory.Delete(stagingDir, recursive: true);
378-
}
379-
380-
private static void CopyFilesRecursively(string source, string destination, CancellationToken cancellationToken)
381-
{
382-
foreach (var dir in Directory.GetDirectories(source, "*", SearchOption.AllDirectories))
383-
{
384-
if (cancellationToken.IsCancellationRequested) break;
385-
Directory.CreateDirectory(dir.Replace(source, destination));
386-
}
387-
388-
foreach (var file in Directory.GetFiles(source, "*.*", SearchOption.AllDirectories))
389-
{
390-
if (cancellationToken.IsCancellationRequested) break;
391-
File.Copy(file, file.Replace(source, destination), overwrite: true);
392-
}
393-
}
394-
395-
private static void MakeExecutable(string path)
396-
{
397-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
398-
399-
const UnixFileMode permissions =
400-
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute |
401-
UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute |
402-
UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.OtherExecute;
403-
404-
File.SetUnixFileMode(path, permissions);
343+
CommandHelpers.ExtractAssetToRoot(extractor, location, archivePath, cancellationToken);
405344
}
406345
}

‎src/VectraCtl/Commands/RunCommand.cs‎

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -261,19 +261,9 @@ private static async Task<string> ResolveDockerVersionAsync(IGitHubReleaseManage
261261
return NormalizeDockerVersion(version);
262262
}
263263

264-
private static async Task<string?> GetPlatformSuffixAsync(IDockerService docker, CancellationToken cancellationToken)
265-
{
266-
var mode = await docker.GetDockerModeAsync(cancellationToken);
267-
return mode == "Windows" ? "windows-ltsc2022-amd64" : "linux-amd64";
268-
}
264+
private static Task<string?> GetPlatformSuffixAsync(IDockerService docker, CancellationToken cancellationToken)
265+
=> CommandHelpers.GetPlatformSuffixAsync(docker, cancellationToken);
269266

270267
private static string NormalizeDockerVersion(string? version)
271-
{
272-
if (string.IsNullOrWhiteSpace(version))
273-
return string.Empty;
274-
275-
return version.StartsWith("v", StringComparison.OrdinalIgnoreCase)
276-
? version[1..]
277-
: version;
278-
}
268+
=> CommandHelpers.NormalizeDockerVersion(version);
279269
}

‎src/VectraCtl/Commands/UpdateCommand.cs‎

Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using System.CommandLine;
3-
using System.Runtime.InteropServices;
43
using VectraCtl.Core.Services.Extractor;
54
using VectraCtl.Core.Services.Github;
65
using VectraCtl.Core.Services.Location;
@@ -102,7 +101,7 @@ await DownloadValidateAndExtractAsync(
102101

103102
logger.Write($"Vectra gateway updated to {targetVersion} successfully.");
104103

105-
MakeExecutable(binaryFile);
104+
CommandHelpers.MakeExecutable(binaryFile);
106105
}
107106

108107
// -------------------------------------------------------------------------
@@ -143,7 +142,7 @@ private static async Task<bool> DownloadValidateAndExtractAsync(
143142
}
144143

145144
logger.Write("Extracting archive...");
146-
ExtractAsset(extractor, location, archivePath, "gateway", cancellationToken);
145+
CommandHelpers.ExtractAsset(extractor, location, archivePath, "gateway", cancellationToken);
147146
return true;
148147
}
149148

@@ -159,7 +158,7 @@ private static (bool isUpdateAvailable, string targetVersion) ResolveUpdate(
159158
string? requestedVersion,
160159
string latestVersion)
161160
{
162-
var target = NormalizeVersion(
161+
var target = CommandHelpers.NormalizeVersion(
163162
string.IsNullOrWhiteSpace(requestedVersion) ? latestVersion : requestedVersion);
164163

165164
return (IsNewerVersion(target, currentVersion), target);
@@ -206,55 +205,6 @@ private static string StripVersionPrefix(string version)
206205
return version.StartsWith("v", StringComparison.OrdinalIgnoreCase) ? version[1..] : version;
207206
}
208207

209-
private static string NormalizeVersion(string? version)
210-
{
211-
if (string.IsNullOrWhiteSpace(version))
212-
return string.Empty;
213-
214-
return version.StartsWith("v", StringComparison.OrdinalIgnoreCase) ? version : $"v{version}";
215-
}
216-
217-
private static void ExtractAsset(
218-
IArchiveExtractor extractor,
219-
ILocation location,
220-
string archivePath,
221-
string destinationFolder,
222-
CancellationToken cancellationToken)
223-
{
224-
var stagingDir = Path.Combine(location.DefaultVectraBinaryDirectoryName, destinationFolder, "downloadedFiles");
225-
var destDir = Path.Combine(location.DefaultVectraBinaryDirectoryName, destinationFolder);
226-
227-
Directory.CreateDirectory(stagingDir);
228-
extractor.ExtractArchive(archivePath, stagingDir);
229-
Directory.CreateDirectory(destDir);
230-
CopyFilesRecursively(stagingDir, destDir, cancellationToken);
231-
Directory.Delete(stagingDir, recursive: true);
232-
}
208+
private static string NormalizeVersion(string? version) => CommandHelpers.NormalizeVersion(version);
233209

234-
private static void CopyFilesRecursively(string source, string destination, CancellationToken cancellationToken)
235-
{
236-
foreach (var dir in Directory.GetDirectories(source, "*", SearchOption.AllDirectories))
237-
{
238-
if (cancellationToken.IsCancellationRequested) break;
239-
Directory.CreateDirectory(dir.Replace(source, destination));
240-
}
241-
242-
foreach (var file in Directory.GetFiles(source, "*.*", SearchOption.AllDirectories))
243-
{
244-
if (cancellationToken.IsCancellationRequested) break;
245-
File.Copy(file, file.Replace(source, destination), overwrite: true);
246-
}
247-
}
248-
249-
private static void MakeExecutable(string path)
250-
{
251-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
252-
253-
const UnixFileMode permissions =
254-
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute |
255-
UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute |
256-
UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.OtherExecute;
257-
258-
File.SetUnixFileMode(path, permissions);
259-
}
260210
}

0 commit comments

Comments
 (0)