Skip to content

feat: site management add delete - #90

Merged
loadinglucian merged 4 commits into
mainfrom
feat/site-management-add-delete
Nov 17, 2025
Merged

feat: site management add delete#90
loadinglucian merged 4 commits into
mainfrom
feat/site-management-add-delete

Conversation

@loadinglucian

@loadinglucian loadinglucian commented Nov 17, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added PHP version selection when creating new sites
    • Sites are now automatically provisioned on the server with proper configurations
    • Site deletion now removes sites from the server, not just from inventory
    • Added next steps guidance after site deployment (URL, DNS notes, deployment instructions)
  • Improvements

    • Updated command options for better clarity (--domain parameter)
    • Enhanced server readiness validation before provisioning

- Add validateSiteDomain() for domain format and uniqueness
- Add validateSiteBranch() for branch name validation
- Add validateSiteRepo() for git repository URL format
- Add server validation (Caddy and PHP installation checks)
- Implement PHP version selection for site-specific configuration
- Add git repository and branch input handling with validation
- Create site-add.sh playbook for Capistrano-style directory structure
- Configure Caddy virtual host with PHP-FPM integration
- Add site to inventory after successful provisioning
- Add double confirmation (type domain name + yes/no prompt)
- Gracefully handle missing servers in inventory
- Create site-delete.sh playbook for server cleanup
- Remove Caddy virtual host configuration and reload service
- Remove site files and directory structure
- Update inventory after successful removal
…ection

- Improve PHP version selection logic for multi-version environments
- Add support for PHP version-specific extension selection
- Handle default PHP version setting when multiple versions installed
- Better integration with site-specific PHP version requirements
@coderabbitai

coderabbitai Bot commented Nov 17, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The pull request introduces server-side site provisioning and deletion capabilities. It adds playbook execution to the site:add and site:delete commands, allowing them to provision sites on remote servers via Bash playbooks. New features include PHP version selection, server readiness validation, and site directory/configuration setup on the server.

Changes

Cohort / File(s) Summary
Server Install Command
app/Console/Server/ServerInstallCommand.php
Removed unused Distribution import and updated command description.
Site Add Command
app/Console/Site/SiteAddCommand.php
Added PlaybooksTrait integration, PHP version selection flow with CLI option and validation, server readiness check for Caddy and PHP services, replaced gatherSiteDeets() with gatherSiteInfo(), integrated playbook execution for site provisioning, and enhanced post-provision UX with next steps guidance.
Site Delete Command
app/Console/Site/SiteDeleteCommand.php
Added PlaybooksTrait and ServersTrait, changed option from \-\-site to \-\-domain, integrated server-side site removal via playbook execution, updated inventory deletion flow to depend on server removal status, and adjusted replay payload key accordingly.
Sites Trait
app/Traits/SitesTrait.php
Changed option key in selectSite() from 'site' to 'domain' to align with renamed command options.
Site Add Playbook
playbooks/site-add.sh
New Bash script for Ubuntu/Debian; creates site directories, demo index.php, Caddy vhost configuration, configures PHP-FPM unix socket routing, reloads services, and outputs YAML deployment details to DEPLOYER_OUTPUT_FILE.
Site Delete Playbook
playbooks/site-delete.sh
New Bash script for Ubuntu/Debian; removes Caddy vhost config, reloads Caddy, deletes site files, and writes success status to DEPLOYER_OUTPUT_FILE.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as site:add CLI
    participant Validator
    participant PhpSelector as PHP Selector
    participant SiteGatherer as Site Info
    participant Provisioner as Playbook Executor
    participant Server
    participant OutputHandler

    User->>CLI: Execute site:add
    CLI->>Validator: validateServerReady()
    Validator-->>CLI: Check for Caddy & PHP
    alt Server Not Ready
        CLI-->>User: Error: Services missing
    end
    
    CLI->>PhpSelector: selectPhpVersion()
    alt Multiple Versions
        PhpSelector->>User: Prompt for PHP version
        User-->>PhpSelector: Select version
    else Single Version
        PhpSelector-->>CLI: Auto-select
    end
    
    CLI->>SiteGatherer: gatherSiteInfo()
    SiteGatherer->>User: Prompt for domain, path, etc.
    User-->>SiteGatherer: Provide details
    SiteGatherer-->>CLI: Return SiteDTO
    
    CLI->>Provisioner: executePlaybook(site-add)
    Provisioner->>Server: site-add.sh with env vars
    Server->>Server: setup_site_directories()
    Server->>Server: setup_demo_page()
    Server->>Server: configure_caddy_vhost()
    Server->>Server: reload_services()
    Server->>OutputHandler: Write YAML to DEPLOYER_OUTPUT_FILE
    Provisioner-->>CLI: Parse YAML output
    
    CLI-->>User: Success message + next steps
Loading
sequenceDiagram
    participant User
    participant CLI as site:delete CLI
    participant SiteSelector
    participant ServerLocator
    participant Provisioner as Playbook Executor
    participant Server
    participant Inventory
    participant OutputHandler

    User->>CLI: Execute site:delete --domain=example.com
    CLI->>SiteSelector: Select site from inventory
    SiteSelector-->>CLI: Return SiteDTO
    
    CLI->>ServerLocator: Locate server
    alt Server Found
        ServerLocator-->>CLI: Server connection ready
        CLI->>Provisioner: executePlaybook(site-delete)
        Provisioner->>Server: site-delete.sh with env vars
        Server->>Server: remove_caddy_vhost()
        Server->>Server: reload_caddy()
        Server->>Server: remove_site_files()
        Server->>OutputHandler: Write success YAML
        Provisioner-->>CLI: Removed from server
    else Server Not Found/Unreachable
        ServerLocator-->>CLI: Server unavailable
        CLI->>User: Prompt: Remove from inventory anyway?
    end
    
    alt User Confirms
        CLI->>Inventory: Delete site from inventory
        Inventory-->>CLI: Deleted
        CLI-->>User: Success message
    else User Cancels
        CLI-->>User: Abort
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • PHP command logic: Both SiteAddCommand and SiteDeleteCommand introduce new control flows with playbook execution, server validation, and version selection. These require careful review to ensure proper error handling and state management.
  • Option renaming: The change from \-\-site to \-\-domain across multiple files (SiteDeleteCommand, SitesTrait) needs verification for consistency and no missed usages.
  • Bash script robustness: Both new playbooks (site-add.sh, site-delete.sh) handle multiple sequential operations with error propagation. Review should verify proper error handling, directory permissions, and service reload logic.
  • Integration points: The interaction between CLI commands, playbook execution, and YAML output parsing should be verified for correctness.

Possibly related PRs

Poem

🐰 With domain and PHP in hand,
We plant our sites across the land,
Playbooks dance and servers sing,
Site provisioning—oh what a thing!
Caddy vhosts and demo pages bloom,
No more inventory gloom!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat: site management add delete' is vague and generic. While it references site management, it doesn't clearly specify what was added or deleted, using non-descriptive terms that don't convey the specific changes made. Use a more specific title that clarifies the main changes, e.g., 'feat: add site provisioning and deletion workflows' or 'feat: implement site:add and site:delete commands with server integration'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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/site-management-add-delete

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: 1

🧹 Nitpick comments (5)
playbooks/site-delete.sh (1)

1-107: Site deletion playbook flow is solid; only minor cleanliness nit

The overall sequence (remove Caddy vhost → reload Caddy → remove site files → write YAML status) is sound, with good use of run_cmd and explicit error checks per step. Validation of required env vars and exporting DEPLOYER_PERMS before calling helpers also looks correct.

The only small nit: DEPLOYER_DISTRO is required but not used anywhere in this script. If there’s no distro-specific behavior expected here, you could drop that requirement to avoid confusion; if it will be used later, consider a brief comment to that effect.

app/Console/Site/SiteDeleteCommand.php (1)

101-190: Server-side removal flow is robust; only minor UX consideration

The added server-removal logic is well thought out:

  • Looks up the server from the site record and gracefully handles a missing server in inventory.
  • Verifies connectivity via serverInfo() before running the site-delete playbook.
  • Distinguishes between “removed from server” and “removed from inventory only” in messaging.
  • Uses a second confirmation (Remove site from inventory anyway?) when server-side removal didn’t happen, which is controlled by the same --yes option (no extra prompts in non-interactive runs).

Return codes (FAILURE when you don’t remove from inventory due to server issues, SUCCESS when deletion proceeds) are reasonable. The replay options, especially always setting 'force' => true, make replays non-interactive and safe for scripting.

If you want, you could add a short warning before returning Command::FAILURE when the user declines “inventory only” removal, to make it more obvious in logs why nothing was deleted, but that’s purely UX polish.

app/Console/Site/SiteAddCommand.php (3)

54-63: execute() flow is sound with good short‑circuiting; watch for partial‑failure consistency

The stepwise flow—select server → fetch/validate server info → check readiness → choose PHP version → gather site info → run site-add playbook → persist to inventory → show next steps/replay—reads clearly, and every operation that can return an int failure code is short‑circuited appropriately.

One thing to be aware of is consistency on partial failures: if the playbook succeeds but $this->sites->create($site) throws, the site will be provisioned on the server but missing from inventory. If that becomes a practical issue, consider either:

  • Ensuring the site-add playbook is idempotent so rerunning site:add is safe, or
  • Adding a compensating rollback (e.g. invoking site:delete/site-delete playbook) when inventory writes fail.

Not a blocker, but worth deciding on a convention for server vs. inventory consistency.

Also applies to: 66-103, 133-155, 170-193


198-231: Server readiness check is defensive; consider more specific messaging and potential reuse

The Caddy/PHP presence checks and early Command::FAILURE return are good guards before attempting provisioning. Two small improvements to consider:

  • The error message is quite generic; explicitly mentioning which component(s) are missing (Caddy, PHP, or both) would make troubleshooting easier.
  • If this logic ends up shared with other commands (e.g. delete/deploy flows), it might be worth moving into a shared trait/helper instead of keeping it as a single-use private method here; otherwise, inlining into execute() would align with the “no single-use methods” guideline.

233-297: PHP version selection is robust; minor polish possible for defaults and duplicates

Handling both structured (['version' => '8.2']) and legacy scalar version entries, plus only prompting when multiple versions exist, is nicely done. A couple of low-impact refinements you might consider:

  • Deduplicate $installedPhpVersions before sorting to avoid repeated entries in the select prompt:
    $installedPhpVersions = array_values(array_unique($installedPhpVersions));
    rsort($installedPhpVersions, SORT_NATURAL);
  • When deriving $defaultVersionStr, guard against a php['default'] value that isn’t actually in $installedPhpVersions, so the prompt’s default always corresponds to a real option:
    if ($defaultVersion !== null && in_array((string) $defaultVersion, $installedPhpVersions, true)) {
        $defaultVersionStr = (string) $defaultVersion;
    } else {
        $defaultVersionStr = $installedPhpVersions[0];
    }

These are purely quality-of-life tweaks; the current logic is functionally sound.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5576d91 and 1130b69.

📒 Files selected for processing (6)
  • app/Console/Server/ServerInstallCommand.php (1 hunks)
  • app/Console/Site/SiteAddCommand.php (7 hunks)
  • app/Console/Site/SiteDeleteCommand.php (3 hunks)
  • app/Traits/SitesTrait.php (1 hunks)
  • playbooks/site-add.sh (1 hunks)
  • playbooks/site-delete.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/Site/SiteAddCommand.php
  • app/Console/Server/ServerInstallCommand.php
  • app/Traits/SitesTrait.php
  • app/Console/Site/SiteDeleteCommand.php
🧬 Code graph analysis (4)
playbooks/site-delete.sh (1)
playbooks/helpers.sh (1)
  • run_cmd (18-24)
playbooks/site-add.sh (1)
playbooks/helpers.sh (1)
  • run_cmd (18-24)
app/Console/Site/SiteAddCommand.php (3)
app/Contracts/BaseCommand.php (2)
  • BaseCommand (30-244)
  • nay (186-190)
app/Services/IOService.php (3)
  • info (474-477)
  • writeln (463-469)
  • getOptionOrPrompt (84-137)
app/Traits/PlaybooksTrait.php (1)
  • executePlaybook (45-187)
app/Console/Site/SiteDeleteCommand.php (3)
app/Repositories/ServerRepository.php (1)
  • delete (119-133)
app/Repositories/SiteRepository.php (1)
  • delete (133-147)
app/Services/InventoryService.php (1)
  • delete (78-84)
🔇 Additional comments (7)
app/Traits/SitesTrait.php (1)

84-99: Domain option wiring in selectSite looks consistent

Using 'domain' as the key for getOptionOrPrompt() and then resolving via findByDomain() aligns with the new --domain CLI option and keeps the flow coherent across add/delete commands.

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

17-20: Command description tweak is safe

The updated AsCommand description is clearer and does not affect behavior; no further changes needed here.

app/Console/Site/SiteDeleteCommand.php (2)

7-25: Traits and command metadata are wired correctly

Adding PlaybooksTrait and ServersTrait alongside SitesTrait, and updating the AsCommand description to mention both server and inventory deletion, cleanly reflects the new behavior without altering the base execution pattern.


31-39: Domain-based option aligns with selection flow

Switching the primary option from --site to --domain and labelling it “Domain name” matches SitesTrait::selectSite() (which now looks up sites by domain) and keeps the CLI consistent with the add/delete playbook expectations.

app/Console/Site/SiteAddCommand.php (3)

9-26: Trait usage and command metadata integration look correct

Adding PlaybooksTrait here and updating AsCommand with a more descriptive description cleanly wires this command into the playbook-based provisioning flow; nothing problematic stands out.


40-42: New server and php-version options are correctly wired for non‑interactive use

Defining both as VALUE_REQUIRED matches how selectServer() and selectPhpVersion() consume them via IOService::getOptionOrPrompt, which keeps the command easily scriptable while still supporting interactive prompts.


303-309: gatherSiteInfo() naming and contract are clear and align with usage

Renaming to gatherSiteInfo() with a precise array shape docblock (domain, repo, branch) matches how execute() destructures the result and keeps the command’s main flow readable. The use of getValidatedOptionOrPrompt() for each field ensures both CLI options and interactive use are handled consistently.

Comment thread playbooks/site-add.sh
@loadinglucian
loadinglucian merged commit 8ccff91 into main Nov 17, 2025
5 checks passed
@loadinglucian
loadinglucian deleted the feat/site-management-add-delete branch November 17, 2025 13:24
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