Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions app/Contracts/BaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ abstract class BaseCommand extends Command
protected OutputInterface $output;
protected SymfonyStyle $io;

/**
* Create a new BaseCommand with the application's services and repositories.
*
* The constructor accepts and stores dependencies (environment and inventory services,
* process and prompting helpers, server/site repositories, SSH service, and the DI container)
* used by this command and its subclasses.
*/
public function __construct(
// Framework
protected readonly Container $container,
Expand Down Expand Up @@ -80,7 +87,15 @@ protected function configure(): void
}

/**
* Initialize IO and services.
* Prepare console IO and initialize environment, inventory, and repositories.
*
* Sets the command's input/output properties, creates a SymfonyStyle IO helper,
* applies any custom paths provided via the `--env` and `--inventory` options,
* loads the corresponding files, and populates the servers and sites repositories
* from the loaded inventory.
*
* @param InputInterface $input The current console input.
* @param OutputInterface $output The current console output.
*/
protected function initialize(InputInterface $input, OutputInterface $output): void
{
Expand Down Expand Up @@ -142,4 +157,4 @@ protected function execute(InputInterface $input, OutputInterface $output): int

return Command::SUCCESS;
}
}
}
9 changes: 7 additions & 2 deletions app/DTOs/SiteDTO.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
readonly class SiteDTO
{
/**
* @param array<int, string> $servers
* Create a SiteDTO containing the site's domain, repository, branch, and associated servers.
*
* @param string $domain The site's domain name (e.g. example.com).
* @param string $repo The repository URL or identifier for the site.
* @param string $branch The repository branch to deploy (e.g. main).
* @param array<int, string> $servers Ordered list of server hostnames or addresses associated with the site.
*/
public function __construct(
public string $domain,
Expand All @@ -16,4 +21,4 @@ public function __construct(
public array $servers,
) {
}
}
}
48 changes: 32 additions & 16 deletions app/Repositories/SiteRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ final class SiteRepository
// -------------------------------------------------------------------------------

/**
* Set the inventory service instance to use for storage operations.
* Configure the repository with an InventoryService and load site entries from storage.
*
* Loads the value stored under the repository's PREFIX key into the internal sites cache
* ($this->sites). If the stored value is not an array, an empty array is persisted under
* the PREFIX key and loaded into the cache.
*/
public function loadInventory(InventoryService $inventory): void
{
Expand All @@ -43,7 +47,10 @@ public function loadInventory(InventoryService $inventory): void
}

/**
* Create a new site in the inventory.
* Add a new site to the inventory storage ensuring the site's domain is unique.
*
* @param SiteDTO $site The site to store; its domain must not already exist in inventory.
* @throws \RuntimeException If the inventory has not been loaded or a site with the same domain already exists.
*/
public function create(SiteDTO $site): void
{
Expand All @@ -60,8 +67,11 @@ public function create(SiteDTO $site): void
}

/**
* Find a site by domain.
*/
* Retrieve the site matching the given domain.
*
* @throws \RuntimeException If the inventory has not been loaded via loadInventory().
* @return SiteDTO|null The SiteDTO for the matching domain, or `null` if no match is found.
*/
public function findByDomain(string $domain): ?SiteDTO
{
$this->assertInventoryLoaded();
Expand All @@ -76,9 +86,9 @@ public function findByDomain(string $domain): ?SiteDTO
}

/**
* Get all sites from the inventory.
* Retrieve all stored sites as SiteDTO objects.
*
* @return array<int, SiteDTO>
* @return array<int, SiteDTO> An array of SiteDTO objects.
*/
public function all(): array
{
Expand All @@ -93,7 +103,11 @@ public function all(): array
}

/**
* Delete a site from the inventory.
* Remove the site with the given domain from the stored inventory.
*
* If no site matches the domain, the inventory remains unchanged.
*
* @param string $domain The domain of the site to remove.
*/
public function delete(string $domain): void
{
Expand All @@ -116,9 +130,9 @@ public function delete(string $domain): void
// -------------------------------------------------------------------------------

/**
* Ensure inventory service is loaded before operations.
* Asserts that the repository's inventory service has been loaded.
*
* @throws \RuntimeException If inventory is not set
* @throws \RuntimeException If the inventory service has not been loaded.
* @phpstan-assert !null $this->inventory
*/
private function assertInventoryLoaded(): void
Expand All @@ -129,9 +143,10 @@ private function assertInventoryLoaded(): void
}

/**
* Convert SiteDTO to array for storage.
* Serialize a SiteDTO into an associative array suitable for inventory storage.
*
* @return array<string, mixed>
* @param SiteDTO $site The site DTO to serialize.
* @return array<string, mixed> Associative array with keys `domain`, `repo`, `branch`, and `servers`.
*/
private function dehydrateSiteDTO(SiteDTO $site): array
{
Expand All @@ -144,10 +159,11 @@ private function dehydrateSiteDTO(SiteDTO $site): array
}

/**
* Hydrate a SiteDTO from inventory data.
*
* @param array<string, mixed> $data
*/
* Create a SiteDTO from raw inventory data.
*
* @param array<string,mixed> $data Raw associative array from inventory.
* @return SiteDTO A SiteDTO where `domain`, `repo`, and `branch` are strings (empty string if missing or not a string) and `servers` is an array of strings (empty array if missing or invalid).
*/
private function hydrateSiteDTO(array $data): SiteDTO
{
$domain = $data['domain'] ?? '';
Expand All @@ -162,4 +178,4 @@ private function hydrateSiteDTO(array $data): SiteDTO
servers: is_array($servers) ? array_values(array_filter($servers, 'is_string')) : [],
);
}
}
}
15 changes: 12 additions & 3 deletions app/Services/ProcessService.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,24 @@
*/
final readonly class ProcessService
{
/**
* Initialize the service with a filesystem utility used for directory validation and inspection.
*
* @param FilesystemService $fs Filesystem utility used to validate working directories and perform filesystem checks.
*/
public function __construct(
private FilesystemService $fs,
) {
}

/**
* Execute a shell command and return the Process instance.
* Execute the given command in the specified working directory and return the executed Process instance.
*
* @param list<string> $command
* @param list<string> $command The command and its arguments.
* @param string $cwd The working directory in which to execute the command.
* @param float $timeout Process timeout in seconds.
* @return Process The Symfony Process instance after execution.
* @throws \InvalidArgumentException If `$command` is empty or `$cwd` is not a directory.
*/
public function run(array $command, string $cwd, float $timeout = 3.0): Process
{
Expand All @@ -37,4 +46,4 @@ public function run(array $command, string $cwd, float $timeout = 3.0): Process

return $process;
}
}
}
29 changes: 25 additions & 4 deletions app/Services/VersionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
*/
class VersionService
{
/**
* Create a VersionService configured with process and filesystem services and package/fallback version.
*
* @param string $packageName The Composer package name to query for version information (default 'bigpixelrocket/deployer-php').
* @param string $fallbackVersion The version to return when no other source provides one (default 'dev-main').
*/
public function __construct(
private readonly ProcessService $proc,
private readonly FilesystemService $fs,
Expand Down Expand Up @@ -109,7 +115,10 @@ public function isGitRepository(string $projectRoot): bool
}

/**
* Get exact git tag if HEAD is tagged.
* Retrieve the exact Git tag name that points to HEAD, if present.
*
* @param string $projectRoot Path to the Git repository root.
* @return string|null The exact tag name that points to HEAD, or `null` if HEAD is not tagged or an error occurs.
*/
public function getExactGitTag(string $projectRoot): ?string
{
Expand All @@ -127,7 +136,12 @@ public function getExactGitTag(string $projectRoot): ?string
}

/**
* Get git describe version (tag + commit info).
* Determine a human-readable Git reference for the repository at the given path.
*
* Attempts to run `git describe --tags --always` and returns the trimmed output on success.
*
* @param string $projectRoot Path to the repository root where the Git command will run.
* @return string|null The described reference (tag, tag+commit, or short commit) if available, `null` otherwise.
*/
public function getGitDescribeVersion(string $projectRoot): ?string
{
Expand All @@ -145,7 +159,14 @@ public function getGitDescribeVersion(string $projectRoot): ?string
}

/**
* Get current branch with short commit hash.
* Produce the current Git branch combined with the short commit hash.
*
* Returns a string in the format "branch@commit" where `branch` is the current branch name
* and `commit` is the short commit hash. Returns `null` if the repository information cannot
* be determined or an error occurs.
*
* @param string $projectRoot Path to the repository root.
* @return string|null The branch and short commit separated by '@', or `null` if unavailable.
*/
public function getBranchWithCommit(string $projectRoot): ?string
{
Expand All @@ -164,4 +185,4 @@ public function getBranchWithCommit(string $projectRoot): ?string

return null;
}
}
}
14 changes: 13 additions & 1 deletion tests/Fixtures/TestConsoleCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ class TestConsoleCommand extends BaseCommand

private array $testArgs = [];

/**
* Create a TestConsoleCommand instance with the required service and repository dependencies.
*
* @param Container $container Dependency injection container.
* @param EnvService $env Environment service.
* @param InventoryService $inventory Inventory management service.
* @param ProcessService $proc Process execution service.
* @param PrompterService $prompter Interactive prompt service.
* @param ServerRepository $servers Repository for server records.
* @param SiteRepository $sites Repository for site records.
* @param SSHService $ssh SSH service for remote execution.
*/
public function __construct(
Container $container,
EnvService $env,
Expand Down Expand Up @@ -237,4 +249,4 @@ private function testPromptSearchWrapper(): void
{
$this->promptSearch('Test:', fn ($q) => ['a', 'b']);
}
}
}
Loading
Loading