Skip to content

feat: server management commands - #68

Merged
loadinglucian merged 10 commits into
mainfrom
feat/server-management-commands
Nov 8, 2025
Merged

feat: server management commands#68
loadinglucian merged 10 commits into
mainfrom
feat/server-management-commands

Conversation

@loadinglucian

@loadinglucian loadinglucian commented Nov 8, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added server:install, server:logs, and server:run CLI commands; new server-install and demo-site provisioning playbooks.
  • Improvements

    • Stricter distro validation and clearer server info display before actions.
    • Safer delete workflow with confirmation and warning if cloud resources may remain.
    • Real-time streamed command output and replay snippets; consolidated provisioning/rollback flow.
    • Shorter, clearer timeout/error notices; tighter image filtering by supported distributions.
  • Documentation

    • Normalized architecture rule comment headers for clearer formatting.

- server-install.sh: Install Caddy, PHP 8.4, PHP-FPM, Git, Bun on Debian/Ubuntu
- demo-site.sh: Create deployer user, configure permissions, setup demo site
- server:install - Install and prepare server for PHP applications
- server:logs - View server logs (system and detected services)
- server:run - Execute arbitrary commands on remote servers
- Add server management methods to ServersTrait
- Extend Distribution enum with family() and isSupported() methods
- Update DistributionFamily enum for better organization
Register ServerInstallCommand, ServerLogsCommand, and ServerRunCommand
in the Symfony application command registry
…ment

- Modify ServerAddCommand, ServerDeleteCommand, ServerInfoCommand
- Update ServerProvisionDigitalOceanCommand integration
- Enhance DigitalOceanAccountService for better server provisioning
- Refine comment structure guidelines in architecture rules
- Update exception handling patterns and layer responsibilities
- Improve documentation consistency across codebase
@coderabbitai

coderabbitai Bot commented Nov 8, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds three server CLI commands (install, logs, run), two shell playbooks (server-install, demo-site), enum-driven distribution validation, new server-info helpers in ServersTrait, updates several server command flows to use playbook-based server-info, and tightens messaging and error/display paths.

Changes

Cohort / File(s) Summary
Docs (comment markers)
\.cursor/rules/01-architecture.mdc
Normalized comment structure tokens: replaced section/subsection/paragraph markers with // {h2}, // {h3}, // {p} and adjusted divider lines.
Exception display
\.cursor/rules/04-exceptions.mdc
Switched several error display calls from $this->io->error(...) to $this->nay(...) and adjusted catch-block messages/formatting.
New server commands
app/Console/Server/ServerInstallCommand.php, app/Console/Server/ServerLogsCommand.php, app/Console/Server/ServerRunCommand.php
Added server:install (provision + demo-site + verify), server:logs (interactive journalctl + fallback logs), and server:run (stream remote command and replay).
Server add / provision / delete adjustments
app/Console/Server/ServerAddCommand.php, app/Console/Server/ServerProvisionDigitalOceanCommand.php, app/Console/Server/ServerDeleteCommand.php
ServerAdd: added PlaybooksTrait, replaced SSH verify with getServerInfo() handling. Provision: unified droplet readiness/IP flow, introduced keep/rollback flag and getServerInfo validation before inventory add. Delete: added $destroyed flag, prompt to remove inventory on destroy failure, updated messages and cloud-run warning.
Server info command
app/Console/Server/ServerInfoCommand.php
Removed helper methods getServerInfo() and displayServerInfo() (functionality moved into ServersTrait).
Traits
app/Traits/PlaybooksTrait.php, app/Traits/ServersTrait.php
PlaybooksTrait: shortened SSH timeout message. ServersTrait: now uses PlaybooksTrait, added getServerInfo(), validateServerDistribution(), and displayServerInfo() plus Distribution enum usage.
Enums & filtering
app/Enums/Distribution.php, app/Enums/DistributionFamily.php, app/Services/DigitalOcean/DigitalOceanAccountService.php
Distribution: added family() and isSupported(). DistributionFamily: removed FEDORA/REDHAT/AMAZON cases and added names() helper. DigitalOcean image filtering now uses Distribution::tryFrom(...) + isSupported().
App wiring
app/SymfonyApp.php
Registered the three new server commands and added their imports.
Playbooks (new)
playbooks/server-install.sh, playbooks/demo-site.sh
Added server-install.sh (DEBIAN/UBUNTU provisioning with apt/dpkg retry logic, installs Caddy, PHP 8.4, Git, Bun; outputs YAML) and demo-site.sh (creates demo site, configures Caddy & PHP-FPM, writes DEPLOYER_OUTPUT_FILE).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant InstallCmd as ServerInstallCommand
    participant Servers as ServersTrait
    participant Playbooks as PlaybooksTrait
    participant SSH as SSHHost
    participant Verifier as verifyInstallation()

    User->>InstallCmd: run server:install
    InstallCmd->>Servers: selectServer()
    InstallCmd->>Servers: getServerInfo(server)
    Servers->>Playbooks: executePlaybook("server-info")
    Playbooks->>SSH: run playbook → returns info
    Servers->>Servers: validateServerDistribution(info)
    InstallCmd->>Playbooks: executePlaybook("server-install", env)
    Playbooks->>SSH: provision packages/services
    InstallCmd->>Playbooks: executePlaybook("demo-site", env)
    Playbooks->>SSH: configure demo site
    InstallCmd->>Verifier: verifyInstallation(url)
    Verifier->>SSH: HTTP GET → response
    Verifier-->>InstallCmd: success | warning | failure
    InstallCmd-->>User: display results + replay
Loading
sequenceDiagram
    actor User
    participant LogsCmd as ServerLogsCommand
    participant Servers as ServersTrait
    participant ProcSvc as getProcessedServices()
    participant Retriever as retrieveServiceLogs()
    participant SSH as SSHHost

    User->>LogsCmd: run server:logs
    LogsCmd->>Servers: selectServer()
    LogsCmd->>Servers: getServerInfo(server)
    LogsCmd->>ProcSvc: getProcessedServices(info)
    User-->>LogsCmd: choose service (all|system|specific)
    LogsCmd->>Retriever: retrieveServiceLogs(...selected...)
    Retriever->>SSH: run journalctl
    alt journalctl returns data
        SSH-->>Retriever: logs
    else
        Retriever->>SSH: read /var/log/* fallback
        SSH-->>Retriever: traditional logs
    end
    LogsCmd-->>User: display logs + replay
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus review on:
    • app/Console/Server/ServerInstallCommand.php — provisioning flow, env composition, verification.
    • app/Traits/ServersTrait.php — getServerInfo/validation/display and enum interactions.
    • playbooks/server-install.sh — apt retry/dpkg lock, distro branches, service configuration and YAML output.
    • app/Console/Server/ServerLogsCommand.php — journalctl invocation, unit pattern generation, and fallback logic.
    • app/Enums/DistributionFamily.php — ensure removed enum cases have no lingering references.

Possibly related PRs

  • bigpixelrocket/deployer-php#62 — Related server-info work and playbook/command changes (strong overlap with getServerInfo/displayServerInfo).
  • bigpixelrocket/deployer-php#64 — Trait consolidation and server-command refactors; overlaps with PlaybooksTrait/ServersTrait changes.
  • bigpixelrocket/deployer-php#63 — Playbook output and server-info parsing changes; likely touches the same playbook execution and parsing surfaces.

Poem

🐇
I hopped through lines of bash and php,
Spun up demo sites and configs with glee.
Enums aligned, logs I chased,
Commands deployed — then I munched a pea.
Tiny rabbit cheers: "Deploy and be free!" 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: server management commands' accurately and concisely summarizes the main change: introducing new server-related console commands (Install, Logs, Run, and related modifications to support them).
Docstring Coverage ✅ Passed Docstring coverage is 94.44% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/server-management-commands

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
app/Enums/DistributionFamily.php (1)

12-24: Consider removing single-case enum.

DistributionFamily now only has one case (DEBIAN) after removing FEDORA, REDHAT, and AMAZON. A single-case enum provides limited value over a string constant. The names() helper also adds complexity for a single-value array.

If only Debian-family distributions are supported currently, consider:

  • Option 1: Remove the enum entirely and use string literals
  • Option 2: Keep the enum if additional families are planned soon
playbooks/server-install.sh (2)

41-47: Code duplication with demo-site.sh.

The run_cmd() function is identical to the one in playbooks/demo-site.sh (lines 35-41). Consider extracting common helpers to a shared library file that both playbooks can source.

# shared-helpers.sh
run_cmd() {
    if [[ $DEPLOYER_PERMS == 'root' ]]; then
        "$@"
    else
        sudo -n "$@"
    fi
}

Then source it in both playbooks:

source "$(dirname "$0")/shared-helpers.sh"

129-331: Significant code duplication between Ubuntu and Debian branches.

The Ubuntu (lines 133-211) and Debian (lines 212-299) branches share ~90% identical logic. The main differences are:

  • Ubuntu: add-apt-repository -y ppa:ondrej/php (line 168)
  • Debian: Sury PHP repository setup (lines 245-260)

Consider extracting common logic into helper functions:

setup_base_repos() {
    # Caddy repo setup (common)
}

setup_php_repo_ubuntu() {
    # Ubuntu-specific PPA
}

setup_php_repo_debian() {
    # Debian-specific Sury
}

install_all_packages() {
    setup_base_repos
    case $DEPLOYER_DISTRO in
        ubuntu) setup_php_repo_ubuntu ;;
        debian) setup_php_repo_debian ;;
    esac
    # Common package installation
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c411fd2 and 4ddb15e.

📒 Files selected for processing (17)
  • .cursor/rules/01-architecture.mdc (1 hunks)
  • .cursor/rules/04-exceptions.mdc (8 hunks)
  • app/Console/Server/ServerAddCommand.php (3 hunks)
  • app/Console/Server/ServerDeleteCommand.php (3 hunks)
  • app/Console/Server/ServerInfoCommand.php (1 hunks)
  • app/Console/Server/ServerInstallCommand.php (1 hunks)
  • app/Console/Server/ServerLogsCommand.php (1 hunks)
  • app/Console/Server/ServerProvisionDigitalOceanCommand.php (1 hunks)
  • app/Console/Server/ServerRunCommand.php (1 hunks)
  • app/Enums/Distribution.php (1 hunks)
  • app/Enums/DistributionFamily.php (1 hunks)
  • app/Services/DigitalOcean/DigitalOceanAccountService.php (1 hunks)
  • app/SymfonyApp.php (2 hunks)
  • app/Traits/PlaybooksTrait.php (1 hunks)
  • app/Traits/ServersTrait.php (3 hunks)
  • playbooks/demo-site.sh (1 hunks)
  • playbooks/server-install.sh (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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

Files:

  • app/Enums/Distribution.php
  • app/Traits/PlaybooksTrait.php
  • app/Console/Server/ServerInfoCommand.php
  • app/Console/Server/ServerAddCommand.php
  • app/SymfonyApp.php
  • app/Services/DigitalOcean/DigitalOceanAccountService.php
  • app/Traits/ServersTrait.php
  • app/Enums/DistributionFamily.php
  • app/Console/Server/ServerLogsCommand.php
  • app/Console/Server/ServerProvisionDigitalOceanCommand.php
  • app/Console/Server/ServerRunCommand.php
  • app/Console/Server/ServerInstallCommand.php
  • app/Console/Server/ServerDeleteCommand.php
🧠 Learnings (21)
📚 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 : Use SymfonyStyle consistently for all user-facing console output

Applied to files:

  • app/SymfonyApp.php
  • app/Console/Server/ServerLogsCommand.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
  • app/Console/Server/ServerLogsCommand.php
  • .cursor/rules/04-exceptions.mdc
📚 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
  • app/Console/Server/ServerLogsCommand.php
  • .cursor/rules/04-exceptions.mdc
📚 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 handle user interaction (input/output) and orchestrate Services

Applied to files:

  • app/SymfonyApp.php
  • app/Console/Server/ServerLogsCommand.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
  • app/Console/Server/ServerLogsCommand.php
  • app/Console/Server/ServerRunCommand.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 are responsible for console styling, error formatting, and user prompts

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-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} : Total rule corpus target: <3000 tokens (~600–800 lines)

Applied to files:

  • .cursor/rules/01-architecture.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: Architecture/Patterns 80–120 lines

Applied to files:

  • .cursor/rules/01-architecture.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} : After each section header, include: "All rules MANDATORY unless marked optional."

Applied to files:

  • .cursor/rules/01-architecture.mdc
  • .cursor/rules/04-exceptions.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} : Before commit: confirm critical rules are still emphasized (without repetition)

Applied to files:

  • .cursor/rules/01-architecture.mdc
  • .cursor/rules/04-exceptions.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 : Format comment sections as headers/subheaders/paragraphs and separate them with a single newline

Applied to files:

  • .cursor/rules/01-architecture.mdc
📚 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: Group related functions into comment-separated sections

Applied to files:

  • .cursor/rules/01-architecture.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 : Use comments to separate sections of code and to explain or summarize complex logic; avoid commenting the obvious

Applied to files:

  • .cursor/rules/01-architecture.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} : Include a single "All rules MANDATORY" statement per file

Applied to files:

  • .cursor/rules/04-exceptions.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} : Start each rule file with YAML front matter containing alwaysApply: true

Applied to files:

  • .cursor/rules/04-exceptions.mdc
📚 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 : Validation errors and business exceptions should bubble up to Commands for display

Applied to files:

  • .cursor/rules/04-exceptions.mdc
📚 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 : Services return exceptions or structured data for Commands to handle

Applied to files:

  • .cursor/rules/04-exceptions.mdc
📚 Learning: 2025-10-24T20:00:14.534Z
Learnt from: CR
Repo: bigpixelrocket/deployer-php PR: 0
File: .cursor/rules/02-tests.mdc:0-0
Timestamp: 2025-10-24T20:00:14.534Z
Learning: Layer strategy: CLI Commands → integration tests; Business Services → unit tests (mocked); Utilities/Helpers → unit tests

Applied to files:

  • .cursor/rules/04-exceptions.mdc
🧬 Code graph analysis (8)
app/Console/Server/ServerAddCommand.php (3)
app/Traits/ServersTrait.php (1)
  • getServerInfo (48-64)
app/Repositories/ServerRepository.php (1)
  • create (48-65)
app/Contracts/BaseCommand.php (1)
  • nay (190-194)
app/Services/DigitalOcean/DigitalOceanAccountService.php (1)
app/Enums/Distribution.php (1)
  • isSupported (37-43)
app/Traits/ServersTrait.php (5)
app/DTOs/ServerDTO.php (1)
  • ServerDTO (7-19)
app/Services/IOService.php (3)
  • info (469-472)
  • displayDeets (538-561)
  • writeln (458-464)
app/Traits/PlaybooksTrait.php (1)
  • executePlaybook (46-181)
app/Contracts/BaseCommand.php (1)
  • nay (190-194)
app/Enums/Distribution.php (2)
  • displayName (48-60)
  • isSupported (37-43)
app/Enums/DistributionFamily.php (1)
app/Enums/Distribution.php (1)
  • family (26-32)
playbooks/server-install.sh (1)
playbooks/demo-site.sh (1)
  • run_cmd (36-42)
app/Console/Server/ServerLogsCommand.php (5)
app/DTOs/ServerDTO.php (1)
  • ServerDTO (7-19)
app/Console/Server/ServerRunCommand.php (3)
  • AsCommand (15-118)
  • configure (27-33)
  • execute (39-117)
app/Traits/ServersTrait.php (2)
  • selectServer (181-219)
  • displayServerDeets (224-244)
app/Services/IOService.php (4)
  • getOptionOrPrompt (84-132)
  • promptText (200-216)
  • promptSelect (296-312)
  • writeln (458-464)
app/Services/SSHService.php (1)
  • executeCommand (69-102)
app/Console/Server/ServerRunCommand.php (4)
app/Contracts/BaseCommand.php (3)
  • BaseCommand (29-235)
  • nay (190-194)
  • showCommandReplay (201-234)
app/Traits/ServersTrait.php (2)
  • selectServer (181-219)
  • displayServerDeets (224-244)
app/Services/IOService.php (4)
  • getOptionOrPrompt (84-132)
  • promptText (200-216)
  • writeln (458-464)
  • write (448-451)
app/Services/SSHService.php (1)
  • executeCommand (69-102)
app/Console/Server/ServerDeleteCommand.php (2)
app/Contracts/BaseCommand.php (1)
  • yay (181-185)
app/Services/IOService.php (1)
  • writeln (458-464)
🔇 Additional comments (8)
.cursor/rules/01-architecture.mdc (1)

127-139: LGTM! Documentation structure improvements.

The hierarchical comment markers (h1, h2, h3, p) provide clearer structure for the coding guidelines documentation.

app/Traits/PlaybooksTrait.php (1)

114-114: LGTM! Improved timeout message clarity.

The simplified introductory sentence makes the timeout notification more direct while the subsequent bullet points still provide actionable guidance.

app/Enums/Distribution.php (1)

34-43: LGTM! Clear distribution support gating.

The method correctly identifies UBUNTU and DEBIAN as the only supported distributions, which aligns with the current implementation scope.

app/Services/DigitalOcean/DigitalOceanAccountService.php (1)

100-111: LGTM! Improved type safety with enum-based filtering.

The refactoring from in_array() to Distribution::tryFrom() + isSupported() centralizes distribution validation logic and improves type safety.

playbooks/server-install.sh (2)

87-123: LGTM! Robust retry logic with proper error discrimination.

The implementation correctly:

  • Differentiates lock-related errors from other failures
  • Uses exponential backoff to prevent tight retry loops
  • Fails fast on non-lock errors
  • Provides clear user feedback

348-410: LGTM! Clean validation and YAML output generation.

The PHP version validation correctly uses awk for numeric comparison, and the YAML output generation properly handles errors and provides structured results.

.cursor/rules/04-exceptions.mdc (1)

1-220: LGTM! Documentation aligned with error handling conventions.

The updates consistently reflect the migration from $this->io->error() to $this->nay() and emphasize avoiding redundant "Failed to" prefixes. These changes align with the architectural guidelines.

app/Console/Server/ServerProvisionDigitalOceanCommand.php (1)

183-190: LGTM! Enhanced validation with server info retrieval.

Replacing SSH verification with getServerInfo() provides more comprehensive validation by confirming both SSH connectivity and distribution support in a single step. The error handling correctly propagates failure codes.

Comment thread app/Console/Server/ServerLogsCommand.php
Comment thread app/Console/Server/ServerProvisionDigitalOceanCommand.php Outdated
Comment thread app/Enums/Distribution.php
Comment thread playbooks/demo-site.sh
Add cleanup of DigitalOcean droplet when server creation fails after
droplet provisioning, preventing orphaned cloud resources.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ddb15e and 1c66e0d.

📒 Files selected for processing (1)
  • app/Console/Server/ServerProvisionDigitalOceanCommand.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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

Files:

  • app/Console/Server/ServerProvisionDigitalOceanCommand.php
🧬 Code graph analysis (1)
app/Console/Server/ServerProvisionDigitalOceanCommand.php (3)
app/Traits/ServersTrait.php (1)
  • getServerInfo (48-64)
app/Repositories/ServerRepository.php (1)
  • create (48-65)
app/Contracts/BaseCommand.php (1)
  • nay (190-194)
🔇 Additional comments (1)
app/Console/Server/ServerProvisionDigitalOceanCommand.php (1)

199-199: LGTM: Cleaner error message.

The simplified error message is appropriate since ServerRepository::create() already throws exceptions with descriptive messages like "Server '{$server->name}' already exists".

Comment thread app/Console/Server/ServerProvisionDigitalOceanCommand.php Outdated
The php_fastcgi directive requires a double slash (unix//) prefix for Unix
socket paths. Without the second slash, Caddy would fail to connect to
PHP-FPM, causing all PHP requests to return 502 errors.

This ensures the socket path is correctly formatted as unix//run/php/...
instead of unix/run/php/...

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/Enums/Distribution.php (1)

23-43: Consider reducing duplication between family() and isSupported().

Both methods encode the same knowledge (UBUNTU and DEBIAN are supported) using identical match patterns. This creates a maintenance burden—adding support for a new distribution family requires updating both methods in sync.

Consider refactoring to establish a single source of truth. One approach:

 public function family(): DistributionFamily
 {
+    if (!$this->isSupported()) {
+        throw new \RuntimeException("Distribution '{$this->value}' is not supported. Use isSupported() to check before calling family()");
+    }
+
     return match ($this) {
         self::UBUNTU, self::DEBIAN => DistributionFamily::DEBIAN,
-        default => throw new \RuntimeException("Distribution '{$this->value}' is not supported. Use isSupported() to check before calling family()"),
     };
 }

Alternatively, define a private static array of supported distributions that both methods reference.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c66e0d and 995bc9a.

📒 Files selected for processing (1)
  • app/Enums/Distribution.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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

Files:

  • app/Enums/Distribution.php
🧬 Code graph analysis (1)
app/Enums/Distribution.php (1)
app/Enums/DistributionFamily.php (1)
  • DistributionFamily (12-19)
🔇 Additional comments (1)
app/Enums/Distribution.php (1)

23-32: Past concern addressed - exception-based approach is correct.

The previous review flagged the misleading DistributionFamily::DEBIAN fallback. The current implementation correctly throws a RuntimeException for unsupported distributions, which ensures fail-fast behavior and prevents bugs from bypassed validation.

Refactor post-provisioning steps into a single try-finally block with
automatic rollback on any failure. This ensures droplets are never left
orphaned if errors occur during:

- Waiting for droplet activation
- Retrieving IP address
- Verifying server connectivity and distribution
- Adding server to inventory

Uses a $shouldKeepDroplet flag that only becomes true when all steps
complete successfully. Converts getServerInfo() int returns to exceptions
to ensure they trigger the catch block and rollback.

Consolidates 4 separate rollback calls into 1 location for easier
maintenance and guaranteed cleanup.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
playbooks/demo-site.sh (1)

234-234: Fix Caddy php_fastcgi unix socket path.

The php_fastcgi directive requires the unix socket path in the form unix//…. The current code produces unix/run/... (single slash), which prevents Caddy from connecting to PHP-FPM, causing all PHP requests to return 502 errors.

Apply this diff:

-		php_fastcgi unix/${php_fpm_socket}
+		php_fastcgi unix//${php_fpm_socket}

Based on learnings from past review comments.

🧹 Nitpick comments (2)
playbooks/demo-site.sh (2)

21-21: Consider adding set -e for stricter error handling.

While set -o pipefail is good for catching pipeline failures, adding set -e would make the script exit immediately on any command failure, providing additional safety. This is especially important for a provisioning script where partial execution can leave the system in an inconsistent state.

Apply this diff:

-set -o pipefail
+set -eo pipefail

54-54: Strengthen PHP-FPM user parsing.

The awk '{print $3}' assumes the config format is user = value ; with spaces. If the format varies (e.g., user=value;), this will fail silently and fall back to 'apache', which may be incorrect.

Consider using a more robust pattern:

-			user=$(grep -E '^\s*user\s*=' "$config_file" | awk '{print $3}' | tr -d ';')
+			user=$(grep -E '^\s*user\s*=' "$config_file" | sed -E 's/^\s*user\s*=\s*([^;[:space:]]+).*/\1/')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 995bc9a and 27020de.

📒 Files selected for processing (2)
  • app/Console/Server/ServerProvisionDigitalOceanCommand.php (1 hunks)
  • playbooks/demo-site.sh (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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

Files:

  • app/Console/Server/ServerProvisionDigitalOceanCommand.php
🔇 Additional comments (9)
playbooks/demo-site.sh (4)

36-42: LGTM!

The run_cmd helper cleanly abstracts permission handling and the use of sudo -n prevents password prompts, which is appropriate for automated provisioning.


80-134: LGTM!

The user and group management logic is well-structured with proper error handling. The approach of adding web server users to the deployer group and restarting services to apply group membership changes is correct. The warning for missing PHP-FPM users prevents hard failures while alerting operators.


136-184: LGTM!

The demo site setup correctly creates the directory structure, generates a simple PHP file, and applies appropriate ownership and permissions (750 for directories, 640 for files) to enable group-based access by Caddy and PHP-FPM.


266-282: LGTM!

The main execution flow is clean and sequential. The YAML output properly reports the provisioning status and key details as documented in the header comments.

app/Console/Server/ServerProvisionDigitalOceanCommand.php (5)

140-144: LGTM!

The shouldKeepDroplet flag with a fail-closed default (false) ensures automatic rollback unless all provisioning steps succeed. This is a safe and correct approach.


145-168: LGTM!

The droplet initialization flow correctly sequences: wait for readiness → retrieve IP → create server DTO → display details. Moving the server DTO creation inside the try block ensures it's only created for active droplets and is available for both validation and display.


170-181: Excellent rollback protection.

The flow now properly validates the server and adds it to inventory within the try block. The shouldKeepDroplet flag is only set to true after successful inventory addition, ensuring automatic rollback if any step fails (SSH validation, distribution check, or inventory persistence). This addresses the orphaned droplet concerns from previous reviews.

Based on learnings from past review comments about missing rollback logic.


182-188: LGTM!

The exception handling and finally block work together correctly: exceptions are caught and displayed, then the finally block ensures rollback occurs if shouldKeepDroplet is still false. This guarantees cleanup even if an unexpected exception occurs.


190-192: LGTM!

The final failure check correctly prevents the command from showing success output and command replay when rollback occurred. This ensures the command's exit code accurately reflects the provisioning outcome.

Comment thread playbooks/demo-site.sh
Comment on lines +69 to +75
get_php_fpm_service() {
if [[ $DEPLOYER_FAMILY == 'debian' ]]; then
echo 'php8.4-fpm'
else
echo 'php-fpm'
fi
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid hardcoding PHP version.

The function returns php8.4-fpm for Debian, but the installed PHP version may differ (e.g., 8.3, 8.5). This would cause service restart commands to fail silently or target the wrong service.

Consider dynamically detecting the PHP version:

 get_php_fpm_service() {
 	if [[ $DEPLOYER_FAMILY == 'debian' ]]; then
-		echo 'php8.4-fpm'
+		# Detect installed PHP-FPM version
+		local php_version
+		php_version=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null || echo "8.4")
+		echo "php${php_version}-fpm"
 	else
 		echo 'php-fpm'
 	fi
 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant