diff --git a/build/.editorconfig b/build/.editorconfig new file mode 100644 index 0000000..4e03cce --- /dev/null +++ b/build/.editorconfig @@ -0,0 +1,5 @@ +[*.cs] +# Nuke build fields use attribute-based reflection injection and cannot follow +# the standard private field naming convention with underscore prefix. +dotnet_naming_rule.private_fields_should_start_with_underscore.severity = none +dotnet_naming_rule.private_static_fields_should_start_with_s_underscore.severity = none diff --git a/build/Build.cs b/build/Build.cs index 072052e..c66bbe7 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -17,7 +17,7 @@ using static Nuke.Common.Tools.ReportGenerator.ReportGeneratorTasks; [ShutdownDotNetAfterServerBuild] -class Build : NukeBuild +internal class Build : NukeBuild { /// Support plugins are available for: /// - JetBrains ReSharper https://nuke.build/resharper @@ -28,30 +28,30 @@ class Build : NukeBuild public static int Main() => Execute(x => x.Compile); [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] - readonly string Configuration = IsLocalBuild ? "Debug" : "Release"; + private readonly string Configuration = IsLocalBuild ? "Debug" : "Release"; [Parameter("Enable code coverage collection")] - readonly bool CoverageEnabled = true; + private readonly bool CoverageEnabled = true; [Parameter("Verbosity level for build output")] - readonly DotNetVerbosity BuildVerbosity = DotNetVerbosity.minimal; + private readonly DotNetVerbosity BuildVerbosity = DotNetVerbosity.minimal; [Solution(GenerateProjects = false)] - readonly Solution Solution; + private readonly Solution Solution; [GitVersion(NoFetch = true, Framework = "net10.0")] [CanBeNull] - readonly GitVersion GitVersion; + private readonly GitVersion GitVersion; - AbsolutePath SourceDirectory => RootDirectory / "src"; - AbsolutePath TestsDirectory => RootDirectory / "tests"; - AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; - AbsolutePath TestResultsDirectory => ArtifactsDirectory / "test-results"; - AbsolutePath CoverageDirectory => ArtifactsDirectory / "coverage"; - AbsolutePath PublishDirectory => ArtifactsDirectory / "publish"; - AbsolutePath PackagesDirectory => ArtifactsDirectory / "packages"; + private AbsolutePath SourceDirectory => RootDirectory / "src"; + private AbsolutePath TestsDirectory => RootDirectory / "tests"; + private AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; + private AbsolutePath TestResultsDirectory => ArtifactsDirectory / "test-results"; + private AbsolutePath CoverageDirectory => ArtifactsDirectory / "coverage"; + private AbsolutePath PublishDirectory => ArtifactsDirectory / "publish"; + private AbsolutePath PackagesDirectory => ArtifactsDirectory / "packages"; - Target Clean => _ => _ + private Target Clean => _ => _ .Before(Restore) .Executes(() => { @@ -60,14 +60,14 @@ class Build : NukeBuild ArtifactsDirectory.CreateOrCleanDirectory(); }); - Target Restore => _ => _ + private Target Restore => _ => _ .Executes(() => { DotNetRestore(s => s .SetProjectFile(Solution)); }); - Target Compile => _ => _ + private Target Compile => _ => _ .DependsOn(Restore) .Executes(() => { @@ -81,7 +81,7 @@ class Build : NukeBuild .SetVerbosity(BuildVerbosity)); }); - Target Test => _ => _ + private Target Test => _ => _ .DependsOn(Compile) .Produces(TestResultsDirectory / "*.trx") .Produces(CoverageDirectory / "*.xml") @@ -110,7 +110,7 @@ class Build : NukeBuild DotNetTest(testSettings); }); - Target IntegrationTest => _ => _ + private Target IntegrationTest => _ => _ .DependsOn(Compile) .Produces(TestResultsDirectory / "integration-*.trx") .Executes(() => @@ -129,7 +129,7 @@ class Build : NukeBuild .SetProjectFile(project))); }); - Target Coverage => _ => _ + private Target Coverage => _ => _ .DependsOn(Test) .Produces(CoverageDirectory / "*.xml") .Executes(() => @@ -143,7 +143,7 @@ class Build : NukeBuild } }); - Target CoverageReport => _ => _ + private Target CoverageReport => _ => _ .DependsOn(Coverage) .Produces(CoverageDirectory / "report" / "**/*") .Executes(() => @@ -161,7 +161,7 @@ class Build : NukeBuild Log.Information("Coverage report generated: {Report}", CoverageDirectory / "report" / "index.html"); }); - Target Pack => _ => _ + private Target Pack => _ => _ .DependsOn(Compile) .Produces(PackagesDirectory / "*.nupkg") .Executes(() => @@ -176,7 +176,7 @@ class Build : NukeBuild .SetVerbosity(BuildVerbosity)); }); - Target Publish => _ => _ + private Target Publish => _ => _ .DependsOn(Compile) .Produces(PublishDirectory / "**/*") .Executes(() => @@ -199,19 +199,19 @@ class Build : NukeBuild .SetOutput(PublishDirectory / project.Name))); }); - Target Format => _ => _ + private Target Format => _ => _ .Executes(() => { DotNet($"format \"{Solution}\" --verbosity {Verbosity}"); }); - Target FormatVerify => _ => _ + private Target FormatVerify => _ => _ .Executes(() => { DotNet($"format \"{Solution}\" --verify-no-changes --verbosity {Verbosity}"); }); - Target Lint => _ => _ + private Target Lint => _ => _ .DependsOn(Restore) .Executes(() => { @@ -224,12 +224,12 @@ class Build : NukeBuild .SetVerbosity(BuildVerbosity)); }); - Target Default => _ => _ + private Target Default => _ => _ .DependsOn(Restore) .DependsOn(Compile) .DependsOn(Test); - Target Full => _ => _ + private Target Full => _ => _ .DependsOn(Clean) .DependsOn(Restore) .DependsOn(Compile) @@ -238,7 +238,7 @@ class Build : NukeBuild .DependsOn(Pack) .DependsOn(Publish); - Target CI => _ => _ + private Target CI => _ => _ .DependsOn(Clean) .DependsOn(Restore) .DependsOn(Compile) diff --git a/src/Core/PokManager.Application/Configuration/ArkConfigurationDefaults.cs b/src/Core/PokManager.Application/Configuration/ArkConfigurationDefaults.cs index 60b2fb3..e6ac0e7 100644 --- a/src/Core/PokManager.Application/Configuration/ArkConfigurationDefaults.cs +++ b/src/Core/PokManager.Application/Configuration/ArkConfigurationDefaults.cs @@ -120,7 +120,7 @@ public static Dictionary GetDefaults() { "bUseSingleplayerSettings", new ArkConfigSetting("False", "Advanced", "Use singleplayer settings multipliers") }, }; } - + /// /// Represents an ARK configuration setting with metadata. /// @@ -129,7 +129,7 @@ public class ArkConfigSetting public string DefaultValue { get; } public string Category { get; } public string Description { get; } - + public ArkConfigSetting(string defaultValue, string category, string description) { DefaultValue = defaultValue; diff --git a/src/Core/PokManager.Application/DependencyInjection/ApplicationServiceRegistration.cs b/src/Core/PokManager.Application/DependencyInjection/ApplicationServiceRegistration.cs index c28e704..8a61746 100644 --- a/src/Core/PokManager.Application/DependencyInjection/ApplicationServiceRegistration.cs +++ b/src/Core/PokManager.Application/DependencyInjection/ApplicationServiceRegistration.cs @@ -1,6 +1,6 @@ -using Microsoft.Extensions.DependencyInjection; -using FluentValidation; using System.Reflection; +using FluentValidation; +using Microsoft.Extensions.DependencyInjection; namespace PokManager.Application.DependencyInjection; diff --git a/src/Core/PokManager.Application/UseCases/InstanceLifecycle/CreateInstance/CreateInstanceRequestValidator.cs b/src/Core/PokManager.Application/UseCases/InstanceLifecycle/CreateInstance/CreateInstanceRequestValidator.cs index bd82bce..da7cc3e 100644 --- a/src/Core/PokManager.Application/UseCases/InstanceLifecycle/CreateInstance/CreateInstanceRequestValidator.cs +++ b/src/Core/PokManager.Application/UseCases/InstanceLifecycle/CreateInstance/CreateInstanceRequestValidator.cs @@ -4,7 +4,7 @@ namespace PokManager.Application.UseCases.InstanceLifecycle.CreateInstance; public class CreateInstanceRequestValidator : AbstractValidator { - private static readonly string[] ValidMapNames = new[] + private static readonly string[] s_validMapNames = new[] { "TheIsland", "TheCenter", @@ -34,7 +34,7 @@ public CreateInstanceRequestValidator() RuleFor(x => x.MapName) .NotEmpty().WithMessage("Map name cannot be empty") - .Must(BeAValidMapName).WithMessage($"Map name must be one of: {string.Join(", ", ValidMapNames)}"); + .Must(BeAValidMapName).WithMessage($"Map name must be one of: {string.Join(", ", s_validMapNames)}"); RuleFor(x => x.MaxPlayers) .InclusiveBetween(1, 127).WithMessage("Max players must be between 1 and 127"); @@ -71,6 +71,6 @@ public CreateInstanceRequestValidator() private bool BeAValidMapName(string mapName) { - return ValidMapNames.Contains(mapName, StringComparer.OrdinalIgnoreCase); + return s_validMapNames.Contains(mapName, StringComparer.OrdinalIgnoreCase); } } diff --git a/src/Core/PokManager.Application/UseCases/InstanceManagement/SaveWorld/SaveWorldHandler.cs b/src/Core/PokManager.Application/UseCases/InstanceManagement/SaveWorld/SaveWorldHandler.cs index e97ca1e..db7a395 100644 --- a/src/Core/PokManager.Application/UseCases/InstanceManagement/SaveWorld/SaveWorldHandler.cs +++ b/src/Core/PokManager.Application/UseCases/InstanceManagement/SaveWorld/SaveWorldHandler.cs @@ -1,8 +1,8 @@ +using System.Diagnostics; using PokManager.Application.Models; using PokManager.Application.Ports; using PokManager.Domain.Common; using PokManager.Domain.Enumerations; -using System.Diagnostics; namespace PokManager.Application.UseCases.InstanceManagement.SaveWorld; diff --git a/src/Core/PokManager.Domain/Entities/Instance.cs b/src/Core/PokManager.Domain/Entities/Instance.cs index 0159473..a24043c 100644 --- a/src/Core/PokManager.Domain/Entities/Instance.cs +++ b/src/Core/PokManager.Domain/Entities/Instance.cs @@ -15,7 +15,7 @@ public class Instance(string instanceId, string sessionName, string mapName, int public string? ContainerId { get; set; } // State transition rules - private static readonly Dictionary> AllowedTransitions = new() + private static readonly Dictionary> s_allowedTransitions = new() { [InstanceState.Created] = new() { InstanceState.Starting }, [InstanceState.Stopped] = new() { InstanceState.Starting, InstanceState.Deleted }, @@ -28,7 +28,7 @@ public class Instance(string instanceId, string sessionName, string mapName, int public Result TransitionTo(InstanceState newState) { - if (!AllowedTransitions.TryGetValue(State, out var allowedStates)) + if (!s_allowedTransitions.TryGetValue(State, out var allowedStates)) { return Result.Failure($"No transitions defined for state {State}"); } diff --git a/src/Core/PokManager.Domain/ValueObjects/BackupId.cs b/src/Core/PokManager.Domain/ValueObjects/BackupId.cs index cb26d07..78b5710 100644 --- a/src/Core/PokManager.Domain/ValueObjects/BackupId.cs +++ b/src/Core/PokManager.Domain/ValueObjects/BackupId.cs @@ -5,7 +5,7 @@ namespace PokManager.Domain.ValueObjects; public sealed class BackupId : IEquatable { - private static readonly Regex ValidationPattern = new( + private static readonly Regex s_validationPattern = new( @"^([a-zA-Z0-9_-]+)_backup_(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})$", RegexOptions.Compiled); @@ -29,7 +29,7 @@ public static Result Create(string value) return Result.Failure("BackupId cannot be empty or whitespace."); } - var match = ValidationPattern.Match(value); + var match = s_validationPattern.Match(value); if (!match.Success) { return Result.Failure( diff --git a/src/Core/PokManager.Domain/ValueObjects/InstanceId.cs b/src/Core/PokManager.Domain/ValueObjects/InstanceId.cs index cef2f5f..29bd7ca 100644 --- a/src/Core/PokManager.Domain/ValueObjects/InstanceId.cs +++ b/src/Core/PokManager.Domain/ValueObjects/InstanceId.cs @@ -5,7 +5,7 @@ namespace PokManager.Domain.ValueObjects; public sealed class InstanceId : IEquatable { - private static readonly Regex ValidationPattern = new(@"^[a-zA-Z0-9_-]+$", RegexOptions.Compiled); + private static readonly Regex s_validationPattern = new(@"^[a-zA-Z0-9_-]+$", RegexOptions.Compiled); private const int MaxLength = 64; public string Value { get; } @@ -27,7 +27,7 @@ public static Result Create(string value) return Result.Failure($"InstanceId cannot exceed {MaxLength} characters."); } - if (!ValidationPattern.IsMatch(value)) + if (!s_validationPattern.IsMatch(value)) { return Result.Failure("InstanceId must contain only alphanumeric characters, hyphens, and underscores."); } diff --git a/src/Core/PokManager.Domain/ValueObjects/ServerPassword.cs b/src/Core/PokManager.Domain/ValueObjects/ServerPassword.cs index f5fa0cb..1fe18fb 100644 --- a/src/Core/PokManager.Domain/ValueObjects/ServerPassword.cs +++ b/src/Core/PokManager.Domain/ValueObjects/ServerPassword.cs @@ -5,7 +5,7 @@ namespace PokManager.Domain.ValueObjects; public sealed class ServerPassword : IEquatable { - private static readonly Regex ValidationPattern = new(@"^[a-zA-Z0-9]+$", RegexOptions.Compiled); + private static readonly Regex s_validationPattern = new(@"^[a-zA-Z0-9]+$", RegexOptions.Compiled); private const int MinLength = 4; private const int MaxLength = 64; @@ -33,7 +33,7 @@ public static Result Create(string value) return Result.Failure($"ServerPassword cannot exceed {MaxLength} characters."); } - if (!ValidationPattern.IsMatch(value)) + if (!s_validationPattern.IsMatch(value)) { return Result.Failure("ServerPassword must contain only alphanumeric characters."); } diff --git a/src/Hosting/PokManager.ServiceDefaults/Extensions.cs b/src/Hosting/PokManager.ServiceDefaults/Extensions.cs index 1c4f9b3..3942434 100644 --- a/src/Hosting/PokManager.ServiceDefaults/Extensions.cs +++ b/src/Hosting/PokManager.ServiceDefaults/Extensions.cs @@ -110,7 +110,7 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app) { // Health checks are enabled in all environments for deployment verification // In production, consider adding authentication or IP restrictions if needed - + // All health checks must pass for app to be considered ready to accept traffic after starting app.MapHealthChecks(HealthEndpointPath); diff --git a/src/Infrastructure/PokManager.Infrastructure.Docker/Models/DockerComposeConfig.cs b/src/Infrastructure/PokManager.Infrastructure.Docker/Models/DockerComposeConfig.cs index 3150753..f385dac 100644 --- a/src/Infrastructure/PokManager.Infrastructure.Docker/Models/DockerComposeConfig.cs +++ b/src/Infrastructure/PokManager.Infrastructure.Docker/Models/DockerComposeConfig.cs @@ -30,7 +30,7 @@ public class DockerComposeConfig public int RestartNoticeMinutes { get; set; } public string TimeZone { get; set; } = string.Empty; public string MemoryLimit { get; set; } = string.Empty; - + /// /// Path to the docker-compose file /// diff --git a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DiskBasedInstanceDiscoveryService.cs b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DiskBasedInstanceDiscoveryService.cs index c3da8f4..cde69f4 100644 --- a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DiskBasedInstanceDiscoveryService.cs +++ b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DiskBasedInstanceDiscoveryService.cs @@ -1,7 +1,7 @@ +using Microsoft.Extensions.Logging; using PokManager.Application.Ports; using PokManager.Domain.Common; using PokManager.Infrastructure.Docker.Models; -using Microsoft.Extensions.Logging; namespace PokManager.Infrastructure.Docker.Services; diff --git a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerComposeParser.cs b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerComposeParser.cs index cdf6b27..c28e482 100644 --- a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerComposeParser.cs +++ b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerComposeParser.cs @@ -34,18 +34,18 @@ public class DockerComposeParser .Build(); var doc = deserializer.Deserialize>(yaml); - + if (!doc.ContainsKey("services")) return null; var services = (Dictionary)doc["services"]; var firstService = services.Values.FirstOrDefault(); - + if (firstService == null) return null; var service = (Dictionary)firstService; - + var config = new DockerComposeConfig { ConfigFilePath = filePath diff --git a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerInstanceDiscoveryService.cs b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerInstanceDiscoveryService.cs index 057879b..15bf20d 100644 --- a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerInstanceDiscoveryService.cs +++ b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerInstanceDiscoveryService.cs @@ -30,7 +30,7 @@ public async Task>> DiscoverInstancesAsync(Cancella try { var containers = await _dockerService.ListContainersAsync(cancellationToken); - + // Extract instance IDs from container names (remove 'asa_' prefix) var instanceIds = containers .Where(c => c.Name.StartsWith("asa_", StringComparison.OrdinalIgnoreCase)) diff --git a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerPokManagerClient.cs b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerPokManagerClient.cs index 46dee9f..2388642 100644 --- a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerPokManagerClient.cs +++ b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/DockerPokManagerClient.cs @@ -194,9 +194,9 @@ public async Task> StartInstanceAsync(string instanceId, Cancellati { var containerName = $"asa_{instanceId}"; var success = await _dockerService.StartContainerAsync(containerName, cancellationToken); - - return success - ? Result.Success() + + return success + ? Result.Success() : Result.Failure($"Failed to start instance '{instanceId}'"); } catch (Exception ex) @@ -211,9 +211,9 @@ public async Task> StopInstanceAsync(string instanceId, StopInstanc { var containerName = $"asa_{instanceId}"; var success = await _dockerService.StopContainerAsync(containerName, cancellationToken); - - return success - ? Result.Success() + + return success + ? Result.Success() : Result.Failure($"Failed to stop instance '{instanceId}'"); } catch (Exception ex) @@ -228,9 +228,9 @@ public async Task> RestartInstanceAsync(string instanceId, RestartI { var containerName = $"asa_{instanceId}"; var success = await _dockerService.RestartContainerAsync(containerName, cancellationToken); - - return success - ? Result.Success() + + return success + ? Result.Success() : Result.Failure($"Failed to restart instance '{instanceId}'"); } catch (Exception ex) @@ -272,7 +272,7 @@ private async Task> GetContainerEnvironmentAsync(stri await process.WaitForExitAsync(cancellationToken); var envVars = new Dictionary(); - + try { var envArray = JsonSerializer.Deserialize(output); @@ -315,7 +315,7 @@ private async Task> GetContainerStateAsync(string con await process.WaitForExitAsync(cancellationToken); var state = new Dictionary(); - + try { var stateJson = JsonSerializer.Deserialize(output); @@ -546,7 +546,7 @@ public async Task>> GetLogsAsync(string instanceI var containerName = $"asa_{instanceId}"; var lines = options?.MaxLines ?? 100; var logs = await _dockerService.GetContainerLogsAsync(containerName, lines, cancellationToken); - + // Parse logs into LogEntry objects (simple implementation for now) var logEntries = logs.Split('\n') .Where(line => !string.IsNullOrWhiteSpace(line)) diff --git a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/LocalDockerComposeService.cs b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/LocalDockerComposeService.cs index 7307ba3..9bcb289 100644 --- a/src/Infrastructure/PokManager.Infrastructure.Docker/Services/LocalDockerComposeService.cs +++ b/src/Infrastructure/PokManager.Infrastructure.Docker/Services/LocalDockerComposeService.cs @@ -1,7 +1,7 @@ -using PokManager.Domain.Common; -using PokManager.Application.Ports; -using Microsoft.Extensions.Logging; using System.Diagnostics; +using Microsoft.Extensions.Logging; +using PokManager.Application.Ports; +using PokManager.Domain.Common; namespace PokManager.Infrastructure.Docker.Services; diff --git a/src/Infrastructure/PokManager.Infrastructure.PokManager/PokManager/Parsers/BackupListParser.cs b/src/Infrastructure/PokManager.Infrastructure.PokManager/PokManager/Parsers/BackupListParser.cs index 5e840fa..31d4160 100644 --- a/src/Infrastructure/PokManager.Infrastructure.PokManager/PokManager/Parsers/BackupListParser.cs +++ b/src/Infrastructure/PokManager.Infrastructure.PokManager/PokManager/Parsers/BackupListParser.cs @@ -12,12 +12,12 @@ namespace PokManager.Infrastructure.PokManager.PokManager.Parsers; /// public class BackupListParser : IPokManagerOutputParser> { - private static readonly Regex BackupPattern = new( + private static readonly Regex s_backupPattern = new( @"backup_(?.+?)_(?\d{8})_(?