This guide is for developers who want to contribute to HPort or understand its internal architecture.
HPort follows a clean architecture pattern with a clear separation of concerns.
src/ItsNameless.HPort: The Core Library.Services: High-level business logic (e.g.,ContainerService). These coordinate actions.Repositories: Infrastructure access layers.ServerRepository: Handles Hetzner Cloud API calls and SSH execution.ContainerRepository: Abstraction for Docker operations on remote servers.
Models: Data Transfer Objects (DTOs) and Domain Models.
src/ItsNameless.HPortCli: The CLI Application.- Uses
DotMake.CommandLinefor parsing arguments. - Acts as a thin wrapper around the Core Library.
- Uses
test/:ItsNameless.HPort.Test: Unit tests for the core.ItsNameless.HPort.IntegrationTest: End-to-end tests against real Hetzner API.
- Interface Generation: We use a Source Generator (
[GenerateAutoInterface]) to automatically keep interfaces in sync with their implementations. - File System Abstraction: All file I/O uses
System.IO.Abstractions(IFileSystem). Never useSystem.IO.Filedirectly; this ensures code is testable. - SSH Command Centralization: All shell commands executed on remote servers are defined in
ServerRepository.Commands.cs. Do not hardcode strings in logic methods.
- Clone:
git clone https://github.com/ItsNameless/hport.git
- Restore:
dotnet restore
- Build:
dotnet build
Unit tests are fast and do not require external resources. They use NSubstitute for mocking.
dotnet test test/ItsNameless.HPort.TestIntegration tests provision real resources on Hetzner Cloud. They will cost money to run.
- Create a file named
HETZNER_TOKENintest/ItsNameless.HPort.IntegrationTest/. - Paste a valid API token into that file.
- Run the tests:
dotnet test test/ItsNameless.HPort.IntegrationTest
Note: Integration tests are designed to clean up after themselves, but always check your Hetzner Cloud Console manually if a test crashes to ensure no orphan servers are left running.
- The project uses
.editorconfigto enforce styles. - Max Line Length: 80 characters.
- Naming: Standard C# conventions (PascalCase for public, camelCase for private/local).
(To be defined)