-
Notifications
You must be signed in to change notification settings - Fork 0
feat: inventory crud #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
15706fb
9fdec2e
a1b7cac
63f950f
952d36c
cac417f
16b33c7
53af1a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| meticulously catalog and analyze all the changes |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| in this branch, compared to the branch it's based on |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| in this Git working tree, staged or unstaged |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| provide a detailed report but don't make any changes yet. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| review everything thoroughly with a focus on where these fall short of our development, architecture and testing rules |
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,270 @@ | ||||||||||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| declare(strict_types=1); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| namespace Bigpixelrocket\DeployerPHP\Services; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| use Symfony\Component\Filesystem\Filesystem; | ||||||||||||||||||||||||||||||||||||
| use Symfony\Component\Yaml\Yaml; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Inventory file CRUD operations. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @example | ||||||||||||||||||||||||||||||||||||
| * $inventory = App::build(InventoryService::class); | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * // Store values using dot notation | ||||||||||||||||||||||||||||||||||||
| * $inventory->set('servers.production.host', 'example.com'); | ||||||||||||||||||||||||||||||||||||
| * $inventory->set('servers.production.user', 'deployer'); | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * // Or set entire object at once | ||||||||||||||||||||||||||||||||||||
| * $inventory->set('servers.production', ['host' => 'example.com', 'user' => 'deployer']); | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * // Retrieve values at any depth | ||||||||||||||||||||||||||||||||||||
| * $inventory->get('servers.production.host'); // 'example.com' | ||||||||||||||||||||||||||||||||||||
| * $inventory->get('servers.production'); // ['host' => 'example.com', 'user' => 'deployer'] | ||||||||||||||||||||||||||||||||||||
| * $inventory->get('servers'); // ['production' => ['host' => 'example.com', 'user' => 'deployer']] | ||||||||||||||||||||||||||||||||||||
| * $inventory->get('servers.staging'); // null | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * // Check if path exists | ||||||||||||||||||||||||||||||||||||
| * if ($inventory->has('servers.production')) { | ||||||||||||||||||||||||||||||||||||
| * // Path exists | ||||||||||||||||||||||||||||||||||||
| * } | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * // Delete path | ||||||||||||||||||||||||||||||||||||
| * $inventory->delete('servers.production'); | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| class InventoryService | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| private readonly string $inventoryPath; | ||||||||||||||||||||||||||||||||||||
| private readonly string $inventoryDir; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| public function __construct( | ||||||||||||||||||||||||||||||||||||
| private readonly Filesystem $filesystem, | ||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||
| $this->inventoryPath = rtrim((string) getcwd(), '/').'/.deployer/inventory.yml'; | ||||||||||||||||||||||||||||||||||||
| $this->inventoryDir = dirname($this->inventoryPath); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||
| // Public | ||||||||||||||||||||||||||||||||||||
| // ------------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Set a value using dot notation path. | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| public function set(string $path, mixed $value): void | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $inventory = $this->readInventory(); | ||||||||||||||||||||||||||||||||||||
| $segments = $this->parsePath($path); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $this->setByPath($inventory, $segments, $value); | ||||||||||||||||||||||||||||||||||||
| $this->writeInventory($inventory); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Get a value using dot notation path. | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| public function get(string $path): mixed | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $inventory = $this->readInventory(); | ||||||||||||||||||||||||||||||||||||
| $segments = $this->parsePath($path); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return $this->getByPath($inventory, $segments); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Get the entire inventory structure. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @return array<string, mixed> | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| public function getAll(): array | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| return $this->readInventory(); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Check if a path exists using dot notation. | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| public function has(string $path): bool | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $inventory = $this->readInventory(); | ||||||||||||||||||||||||||||||||||||
| $segments = $this->parsePath($path); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return $this->hasByPath($inventory, $segments); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Delete a value using dot notation path. | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| public function delete(string $path): void | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $inventory = $this->readInventory(); | ||||||||||||||||||||||||||||||||||||
| $segments = $this->parsePath($path); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $this->unsetByPath($inventory, $segments); | ||||||||||||||||||||||||||||||||||||
| $this->writeInventory($inventory); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||
| // Private | ||||||||||||||||||||||||||||||||||||
| // ------------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||
| // Dot Notation Helpers | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Parse dot notation path into array segments. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @return array<int, string> | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function parsePath(string $path): array | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| return explode('.', $path); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Get value from nested array using dot notation path segments. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @param array<string, mixed> $data | ||||||||||||||||||||||||||||||||||||
| * @param array<int, string> $segments | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function getByPath(array $data, array $segments): mixed | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $current = $data; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach ($segments as $segment) { | ||||||||||||||||||||||||||||||||||||
| if (!is_array($current) || !array_key_exists($segment, $current)) { | ||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| $current = $current[$segment]; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return $current; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Set value in nested array using dot notation path segments. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @param array<string, mixed> $data | ||||||||||||||||||||||||||||||||||||
| * @param array<int, string> $segments | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function setByPath(array &$data, array $segments, mixed $value): void | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $current = &$data; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach ($segments as $segment) { | ||||||||||||||||||||||||||||||||||||
| if (!is_array($current)) { | ||||||||||||||||||||||||||||||||||||
| $current = []; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!array_key_exists($segment, $current)) { | ||||||||||||||||||||||||||||||||||||
| $current[$segment] = []; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $current = &$current[$segment]; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $current = $value; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Check if path exists in nested array using dot notation path segments. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @param array<string, mixed> $data | ||||||||||||||||||||||||||||||||||||
| * @param array<int, string> $segments | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function hasByPath(array $data, array $segments): bool | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $current = $data; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| foreach ($segments as $segment) { | ||||||||||||||||||||||||||||||||||||
| if (!is_array($current) || !array_key_exists($segment, $current)) { | ||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| $current = $current[$segment]; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Remove path from nested array using dot notation path segments. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @param array<string, mixed> $data | ||||||||||||||||||||||||||||||||||||
| * @param array<int, string> $segments | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function unsetByPath(array &$data, array $segments): bool | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| if (empty($segments)) { | ||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $lastSegment = array_pop($segments); | ||||||||||||||||||||||||||||||||||||
| $current = &$data; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Navigate to parent of target | ||||||||||||||||||||||||||||||||||||
| foreach ($segments as $segment) { | ||||||||||||||||||||||||||||||||||||
| if (!is_array($current) || !array_key_exists($segment, $current)) { | ||||||||||||||||||||||||||||||||||||
| return false; // Path doesn't exist | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| $current = &$current[$segment]; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!is_array($current) || !array_key_exists($lastSegment, $current)) { | ||||||||||||||||||||||||||||||||||||
| return false; // Target doesn't exist | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| unset($current[$lastSegment]); | ||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||
| // File Operations | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Read inventory YAML into a structured array. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @return array<string, mixed> | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function readInventory(): array | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $path = $this->inventoryPath; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!$this->filesystem->exists($path)) { | ||||||||||||||||||||||||||||||||||||
| return []; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $raw = $this->filesystem->readFile($path); | ||||||||||||||||||||||||||||||||||||
| $parsed = Yaml::parse($raw); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** @var array<string, mixed> $result */ | ||||||||||||||||||||||||||||||||||||
| $result = is_array($parsed) ? $parsed : []; | ||||||||||||||||||||||||||||||||||||
| return $result; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+238
to
+244
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Undefined Filesystem::readFile call will fatal at runtime.
Apply this diff to fix the problem: - $raw = $this->filesystem->readFile($path);
+ $raw = @file_get_contents($path);
+ if ($raw === false) {
+ throw new \RuntimeException("Failed to read inventory file at {$path}");
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||
| * Persist inventory data to YAML file. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * @param array<string, mixed> $inventory | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| private function writeInventory(array $inventory): void | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| $path = $this->inventoryPath; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!$this->filesystem->exists($this->inventoryDir)) { | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| $this->filesystem->mkdir($this->inventoryDir, 0775); | ||||||||||||||||||||||||||||||||||||
| } catch (\Throwable $e) { | ||||||||||||||||||||||||||||||||||||
| throw new \RuntimeException("Unable to create inventory directory: {$this->inventoryDir}", 0, $e); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| $yaml = Yaml::dump($inventory, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| $this->filesystem->dumpFile($path, $yaml); | ||||||||||||||||||||||||||||||||||||
| } catch (\Throwable $e) { | ||||||||||||||||||||||||||||||||||||
| throw new \RuntimeException("Failed to write inventory file at {$path}", 0, $e); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Docblocks need parameter/return tags per repo rules.
Per the repository’s PHP guidelines, every method docblock must include the parameter and return annotations. All of the public methods here are missing those tags. Please add the required annotations across the class.
As per coding guidelines
Here’s an example of the expected format:
/** * Set a value using dot notation path. + * + * @param string $path + * @param mixed $value + * + * @return void */📝 Committable suggestion
🤖 Prompt for AI Agents