feat: server info command - #62
Conversation
Add PlaybookHelpersTrait providing executePlaybook() method for: - SSH-based playbook execution on remote servers - Environment variable injection - YAML output parsing and error handling - Integration with IOService for user feedback
Add server:info command to display server information: - Gathers distro, permissions, and listening ports via playbook - Displays formatted server details and services - Uses server-info.sh playbook for remote execution - Integrates with existing server selection flow
- Clarify playbooks must return parsable YAML as final output - Document error detection via YAML parsing failure - Add progress message guidelines (stderr vs stdout) - Update bash rules with quality gates section
- Add composer bash and bash:check scripts for shfmt - Add npm format:bash and check:bash scripts - Update .editorconfig for bash formatting consistency
WalkthroughThis pull request introduces a new Changes
Sequence DiagramsequenceDiagram
actor User
participant ServerInfoCommand
participant PlaybookHelpersTrait
participant SSHService
participant server-info.sh
participant ServerInfoTrait
User->>ServerInfoCommand: Execute server:info --server=name
ServerInfoCommand->>ServerInfoCommand: selectServer()
ServerInfoCommand->>PlaybookHelpersTrait: executePlaybook(server, "server-info")
PlaybookHelpersTrait->>SSHService: Execute script over SSH
SSHService->>server-info.sh: Run remotely
server-info.sh->>server-info.sh: detect_distro()
server-info.sh->>server-info.sh: check_permissions()
server-info.sh->>server-info.sh: get_listening_ports()
server-info.sh-->>SSHService: Return YAML output
SSHService-->>PlaybookHelpersTrait: SSH output
PlaybookHelpersTrait->>PlaybookHelpersTrait: Parse YAML
PlaybookHelpersTrait-->>ServerInfoCommand: Parsed array or error code
ServerInfoCommand->>ServerInfoTrait: displayServerInfo(array)
ServerInfoTrait->>ServerInfoTrait: Format and map distro/permissions
ServerInfoTrait-->>User: Display server information
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Key areas requiring attention:
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/Traits/ServerInfoTrait.php (1)
63-79: Consider conditionally displaying the Services section.Currently,
displayDeets(['Services' => $services])is called even when$servicesis empty (lines 78), which will render "Services:" with no items. Consider wrapping this in a condition:- $this->io->displayDeets(['Services' => $services]); - $this->io->writeln(''); + if (count($services) > 0) { + $this->io->displayDeets(['Services' => $services]); + $this->io->writeln(''); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
.cursor/rules/05-bash.mdc(1 hunks).cursor/rules/06-playbooks.mdc(5 hunks).editorconfig(1 hunks)app/Console/Server/ServerAddCommand.php(1 hunks)app/Console/Server/ServerInfoCommand.php(1 hunks)app/Console/Site/SiteAddCommand.php(1 hunks)app/SymfonyApp.php(2 hunks)app/Traits/PlaybookHelpersTrait.php(1 hunks)app/Traits/ServerInfoTrait.php(1 hunks)composer.json(1 hunks)package.json(1 hunks)playbooks/server-info.sh(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.php
📄 CodeRabbit inference engine (.cursor/rules/00-main.mdc)
**/*.php: Eliminate single-use methods: inline if a method is called only once
Cache computed values: initialize expensive calculations in the constructor
Avoid method call overhead: prefer direct property access when appropriate
**/*.php: Adhere to PSR-12, enable strict_types, and leverage PHP 8.x features (union types, match, attributes, readonly)
Use explicit return types, including generic-like annotations where applicable (e.g., Collection<int, User>)
Prefer Symfony components (e.g., Filesystem, Process) over native PHP functions for testability
Always import classes via use statements; only use root FQDNs for core exceptions (e.g., \InvalidArgumentException, \RuntimeException). Do not use inline FQDNs for non-root namespaces
Create objects via $container->build(ClassName::class) everywhere except DTOs, value objects, and pure data structures
Use minimalist DocBlocks documenting description, parameters, and return types for classes and functions
Follow the specified comment structure with section headers/subheaders and spacing; remove obsolete comments with removed code
Files:
app/Console/Server/ServerAddCommand.phpapp/Console/Site/SiteAddCommand.phpapp/Traits/ServerInfoTrait.phpapp/Traits/PlaybookHelpersTrait.phpapp/SymfonyApp.phpapp/Console/Server/ServerInfoCommand.php
**/*Command.php
📄 CodeRabbit inference engine (.cursor/rules/01-architecture.mdc)
**/*Command.php: Commands handle user interaction (I/O), orchestration, styling, error formatting, and prompts using SymfonyStyle
Commands must not contain business logic; delegate to Services
Commands must not invoke other commands (no proxy commands)
Files:
app/Console/Server/ServerAddCommand.phpapp/Console/Site/SiteAddCommand.phpapp/Console/Server/ServerInfoCommand.php
**/*{Command,Service}.php
📄 CodeRabbit inference engine (.cursor/rules/01-architecture.mdc)
All dependencies should be expressed in constructor signatures; avoid circular dependencies
Files:
app/Console/Server/ServerAddCommand.phpapp/Console/Site/SiteAddCommand.phpapp/Console/Server/ServerInfoCommand.php
{composer.json,package.json}
📄 CodeRabbit inference engine (.cursor/rules/00-main.mdc)
Check composer.json and package.json for installed packages before any task
Files:
package.jsoncomposer.json
**/SymfonyApp.php
📄 CodeRabbit inference engine (.cursor/rules/01-architecture.mdc)
Register console commands in SymfonyApp.php
Files:
app/SymfonyApp.php
🧠 Learnings (17)
📚 Learning: 2025-10-24T20:03:23.947Z
Learnt from: CR
Repo: bigpixelrocket/deployer-php PR: 0
File: .cursor/rules/rules.mdc:0-0
Timestamp: 2025-10-24T20:03:23.947Z
Learning: Applies to **/*.{md,mdc} : Before commit: confirm critical rules are still emphasized (without repetition)
Applied to files:
.cursor/rules/05-bash.mdc
📚 Learning: 2025-09-22T11:10:45.309Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/02-code-quality.mdc:0-0
Timestamp: 2025-09-22T11:10:45.309Z
Learning: Applies to {app,tests}/**/*.php : Run Pint (code style fixer) on all changed PHP files before completing a task
Applied to files:
.cursor/rules/05-bash.mdc
📚 Learning: 2025-10-24T20:03:23.947Z
Learnt from: CR
Repo: bigpixelrocket/deployer-php PR: 0
File: .cursor/rules/rules.mdc:0-0
Timestamp: 2025-10-24T20:03:23.947Z
Learning: Applies to **/*.{md,mdc} : Target rule density per file: Language style (Bash/PHP) 60–100 lines
Applied to files:
.cursor/rules/05-bash.mdc
📚 Learning: 2025-10-24T19:59:22.873Z
Learnt from: CR
Repo: bigpixelrocket/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-10-24T19:59:22.873Z
Learning: Applies to bin/deployer : Application entry point is bin/deployer
Applied to files:
.cursor/rules/06-playbooks.mdc
📚 Learning: 2025-09-21T08:52:38.782Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/00-main.mdc:0-0
Timestamp: 2025-09-21T08:52:38.782Z
Learning: Applies to **/{composer.json,package.json} : Plan implementation using features supported by the major versions specified in composer.json and package.json
Applied to files:
composer.json
📚 Learning: 2025-09-21T08:52:38.782Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/00-main.mdc:0-0
Timestamp: 2025-09-21T08:52:38.782Z
Learning: Applies to **/{composer.json,package.json} : Check composer.json and package.json for installed packages before starting any task
Applied to files:
composer.json
📚 Learning: 2025-10-24T19:58:34.899Z
Learnt from: CR
Repo: bigpixelrocket/deployer-php PR: 0
File: .cursor/rules/00-main.mdc:0-0
Timestamp: 2025-10-24T19:58:34.899Z
Learning: Applies to {composer.json,package.json} : Check composer.json and package.json for installed packages before any task
Applied to files:
composer.json
📚 Learning: 2025-10-24T19:59:22.873Z
Learnt from: CR
Repo: bigpixelrocket/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-10-24T19:59:22.873Z
Learning: Applies to **/SymfonyApp.php : Register console commands in SymfonyApp.php
Applied to files:
app/SymfonyApp.php
📚 Learning: 2025-10-24T20:01:06.242Z
Learnt from: CR
Repo: bigpixelrocket/deployer-php PR: 0
File: .cursor/rules/03-commands.mdc:0-0
Timestamp: 2025-10-24T20:01:06.242Z
Learning: Applies to src/Command/**/*Command.php : Option naming: --server/--site select existing resources; --name defines a new resource property; follow the provided table for common options.
Applied to files:
app/SymfonyApp.phpapp/Console/Server/ServerInfoCommand.php
📚 Learning: 2025-09-22T11:10:21.459Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-09-22T11:10:21.459Z
Learning: Applies to app/**/Command/**/*.php : Commands receive Services via constructor injection
Applied to files:
app/SymfonyApp.php
📚 Learning: 2025-09-22T11:10:21.459Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-09-22T11:10:21.459Z
Learning: Applies to app/**/Command/**/*.php : Commands may only depend on Services (not other Commands)
Applied to files:
app/SymfonyApp.php
📚 Learning: 2025-09-22T11:10:21.459Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-09-22T11:10:21.459Z
Learning: Applies to app/**/Command/**/*.php : Commands handle user interaction (input/output) and orchestrate Services
Applied to files:
app/SymfonyApp.php
📚 Learning: 2025-09-22T11:10:21.459Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-09-22T11:10:21.459Z
Learning: Applies to app/**/Command/**/*.php : Commands must not duplicate orchestration logic—extract to shared Services
Applied to files:
app/SymfonyApp.php
📚 Learning: 2025-09-22T11:10:21.459Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-09-22T11:10:21.459Z
Learning: Applies to app/**/Command/**/*.php : Commands must not contain business logic—delegate to Services
Applied to files:
app/SymfonyApp.php
📚 Learning: 2025-09-22T11:10:21.459Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-09-22T11:10:21.459Z
Learning: Applies to app/**/@(Service|Services)/**/*.php : Extract complex orchestration shared by multiple Commands into dedicated Services
Applied to files:
app/SymfonyApp.php
📚 Learning: 2025-09-22T11:10:21.459Z
Learnt from: CR
Repo: deployer-plus/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-09-22T11:10:21.459Z
Learning: Applies to app/**/Command/**/*.php : Only Commands perform console input/output operations
Applied to files:
app/SymfonyApp.php
📚 Learning: 2025-10-24T19:59:22.873Z
Learnt from: CR
Repo: bigpixelrocket/deployer-php PR: 0
File: .cursor/rules/01-architecture.mdc:0-0
Timestamp: 2025-10-24T19:59:22.873Z
Learning: Applies to **/*Command.php : Commands must not contain business logic; delegate to Services
Applied to files:
app/SymfonyApp.php
🧬 Code graph analysis (4)
app/Console/Server/ServerAddCommand.php (2)
app/Services/IOService.php (3)
writeln(463-469)hr(517-523)displayDeets(543-566)app/Traits/ServerHelpersTrait.php (1)
displayServerDeets(97-115)
app/Console/Site/SiteAddCommand.php (2)
app/Services/IOService.php (1)
writeln(463-469)app/Traits/SiteHelpersTrait.php (1)
displaySiteDeets(95-118)
app/Traits/ServerInfoTrait.php (3)
app/DTOs/ServerDTO.php (1)
ServerDTO(7-19)app/Services/IOService.php (3)
IOService(30-626)displayDeets(543-566)writeln(463-469)app/Traits/PlaybookHelpersTrait.php (1)
executePlaybook(39-121)
app/Traits/PlaybookHelpersTrait.php (6)
app/DTOs/ServerDTO.php (1)
ServerDTO(7-19)app/Services/FilesystemService.php (2)
FilesystemService(28-168)readFile(52-55)app/Services/IOService.php (2)
IOService(30-626)promptSpin(439-452)app/Services/SSHService.php (3)
SSHService(40-268)executeCommand(69-86)executeScript(95-120)app/Traits/KeyHelpersTrait.php (2)
resolvePrivateKeyPath(46-52)KeyHelpersTrait(19-137)app/Traits/SiteHelpersTrait.php (1)
SiteHelpersTrait(22-119)
🪛 Shellcheck (0.11.0)
playbooks/server-info.sh
[warning] 117-117: proto appears unused. Verify use (or export if used externally).
(SC2034)
[warning] 117-117: recvq appears unused. Verify use (or export if used externally).
(SC2034)
[warning] 117-117: sendq appears unused. Verify use (or export if used externally).
(SC2034)
[warning] 117-117: foreign appears unused. Verify use (or export if used externally).
(SC2034)
🔇 Additional comments (24)
app/Console/Site/SiteAddCommand.php (1)
158-159: Verify: redundant blank line afterdisplaySiteDeets.The
displaySiteDeets()method (fromSiteHelpersTrait) already emits a blank line internally (line 117 of the trait). Adding$this->io->writeln('')on line 159 creates double spacing. Confirm this is intentional for visual hierarchy, or remove the duplicate..editorconfig (1)
19-22: Shell script formatting configuration aligns with project standards.The EditorConfig rules for
*.shfiles (tabs with no space indentation) align with the new shfmt tooling incomposer.json/package.json(using-i 0for tab indentation) and match the bash style guide in.cursor/rules/05-bash.mdc.app/Console/Server/ServerAddCommand.php (1)
151-152: Verify: redundant blank line afterdisplayServerDeets.Similar to
SiteAddCommand, thedisplayServerDeets()method already emits a blank line internally (line 114 ofServerHelpersTrait). Adding$this->io->writeln('')on line 152 creates double spacing. Confirm this is intentional or remove the duplicate.package.json (1)
5-8: Verify shfmt is available and script naming is consistent.The npm scripts reference
shfmt, but this tool is not listed in thepackage.jsondependencies or devDependencies. Additionally, the script names here (format:bash,check:bash) differ fromcomposer.json(bash,bash:check). Confirm that shfmt is installed globally or as a project dependency, and standardize script names across both files for consistency.composer.json (1)
64-70: Verify shfmt availability and standardize script names across build files.The composer scripts reference
shfmt, which is not listed in thecomposer.jsondependencies. shfmt is a shell script formatter typically installed via a package manager or npm, not Composer. Ensure it's available in the project environment. Additionally, consider standardizing script names betweencomposer.json(usingbash,bash:check) andpackage.json(usingformat:bash,check:bash) for consistency across the development workflow..cursor/rules/05-bash.mdc (1)
117-127: Quality gates documentation appropriately guides bash formatting workflow.The new "Quality Gates" section clearly documents the requirement to run
composer bashandcomposer bash:checkbefore task completion. This aligns with the newly added scripts incomposer.jsonand reinforces consistent shell script formatting practices..cursor/rules/06-playbooks.mdc (1)
18-18: Playbook YAML output contract clearly documented and exemplified.The additions establish a clear, elegant error-detection pattern: playbooks emit final YAML to stdout for success, or plain-text error messages to stdout for failures. YAML parsing failures automatically surface as errors. The comprehensive examples and patterns (including stderr for progress, stdout for YAML) provide clear guidance for implementing compliant playbooks. The reference to
server-info.shanchors documentation to implementation.Also applies to: 47-47, 81-99, 119-148, 200-200
playbooks/server-info.sh (7)
1-3: Excellent bash foundation and error handling setup.The shebang (
#!/usr/bin/env bash), strict mode (set -o pipefail), and early environment variable exports follow the playbook standards from.cursor/rules/06-playbooks.mdcprecisely.
13-31: Distro detection is robust and follows best practices.The function checks
/etc/os-releasefirst (modern standard), then falls back to legacy release files. Case-insensitive matching (grep -qi) handles variations in capitalization. Unknown fallback provides safe default. The pattern adheres to bash style (using[[ ... ]]over[ ... ]and proper quoting).
37-45: Permissions check correctly detects three privilege levels.Uses EUID check for root,
sudo -n truefor passwordless sudo access, and implicit fallback to 'none'. The function correctly asserts three states as documented: root, sudo, none.
57-76: Idempotent tool installation with proper conditional logic.The function checks for permissions first (early return if 'none') and verifies tool availability before attempting installation. Debian/Ubuntu vs. RedHat/Amazon split is correct, and the
dnffallback for newer systems is appropriate.DEBIAN_FRONTEND=noninteractiveprevents blocking prompts per playbook rules.
81-131: Listening ports enumeration handles both ss and netstat with proper permissions.The dual-path implementation (preferring
sswhere available, falling back tonetstat) is practical. Both paths respectDEPLOYER_PERMSfor conditional sudo usage. Regex extraction patterns correctly isolate port numbers and process names. Sorting and uniqueness ensure consistent output. Output format (port:process) is parseable and suitable for downstream YAML conversion.
117-117: Shellcheck warnings for unused variables are false positives.Shellcheck reports
proto,recvq,sendq,foreignas unused in thereadstatement (line 117). These are intentionally named to align with netstat column positions, though only some columns are used in the subsequent logic. The pattern is idiomatic bash—all fields must be declared inread, even if unused. This is safe and can be suppressed.
137-162: YAML output structure aligns with playbook contracts; empty ports case handled.The
main()function outputs valid YAML with distro, permissions, and ports sections. The heredoc using<<- EOFcorrectly handles indentation stripping. The while-loop inline ports formatting withIFS=:properly convertsport:processlines into YAML. The fallback to{}for empty ports (line 160) ensures valid YAML output even when no ports are listening, consistent with.cursor/rules/06-playbooks.mdcrequirement ("empty array[]or empty object{}").app/SymfonyApp.php (1)
13-13: LGTM! Command registration follows conventions.The import and registration of
ServerInfoCommandfollow established patterns and align with the coding guidelines for command registration.Also applies to: 142-142
app/Traits/ServerInfoTrait.php (2)
1-19: LGTM! Trait declaration follows conventions.The trait is properly structured with strict types, clear documentation of requirements, and appropriate use of PlaybookHelpersTrait.
26-33: LGTM! Clean delegation to playbook execution.The method correctly delegates to
executePlaybookwith appropriate parameters and maintains the proper return type.app/Traits/PlaybookHelpersTrait.php (4)
1-27: LGTM! Trait setup is correct.The trait properly declares dependencies, imports necessary classes, and uses
KeyHelpersTraitfor SSH key resolution.
45-47: LGTM! Path resolution is appropriate.The playbook path construction correctly resolves relative to the project root, and
readFilewill throw if the playbook doesn't exist.
62-97: LGTM! Robust error handling.The method properly handles SSH key resolution, runtime exceptions, and non-zero exit codes with clear error messages and appropriate failure returns.
99-121: LGTM! Comprehensive YAML parsing with helpful error output.The YAML parsing properly validates the result type and provides detailed error information including raw output for debugging failed parses.
app/Console/Server/ServerInfoCommand.php (3)
1-21: LGTM! Command structure follows conventions.The command properly extends
BaseCommand, uses appropriate traits, and follows the AsCommand attribute pattern. Aligns with coding guidelines for command architecture.Based on learnings
26-31: LGTM! Option configuration follows naming conventions.The
--serveroption correctly usesVALUE_REQUIREDfor selecting an existing server resource, consistent with option naming guidelines.Based on learnings
37-82: LGTM! Execute method follows command architecture guidelines.The execution flow properly orchestrates services and traits:
- Handles user interaction (server selection)
- Delegates to services/traits for data retrieval and display
- Contains no business logic
- Provides clear command hint for reusability
Adheres to all command coding guidelines.
Based on learnings
Summary by CodeRabbit
Release Notes
New Features
Chores