Skip to content

feat(site): add site:https command for enabling HTTPS - #97

Merged
loadinglucian merged 5 commits into
mainfrom
feat/site-https-support
Nov 20, 2025
Merged

feat(site): add site:https command for enabling HTTPS#97
loadinglucian merged 5 commits into
mainfrom
feat/site-https-support

Conversation

@loadinglucian

@loadinglucian loadinglucian commented Nov 20, 2025

Copy link
Copy Markdown
Owner
  • Add SiteHttpsCommand class with domain selection and playbook execution
  • Add site-https.sh playbook that configures Caddy for automatic HTTPS
  • Support different WWW redirect modes (redirect-to-root, redirect-to-www)
  • Display HTTPS-enabled URL after successful configuration

Summary by CodeRabbit

  • New Features

    • Added --www-mode option when adding sites and a new site:https command to enable HTTPS and show the accessible HTTPS URL.
    • Server info now detects and displays per-domain PHP version, WWW mode and HTTPS status.
  • Refactor

    • Site provisioning now generates Caddy vhosts dynamically based on selected www mode; playbooks updated to support www-mode flows.
  • Documentation

    • Next-steps output and DNS guidance updated to show per-domain URLs and record recommendations.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add SiteHttpsCommand class with domain selection and playbook execution
- Add site-https.sh playbook that configures Caddy for automatic HTTPS
- Support different WWW redirect modes (redirect-to-root, redirect-to-www)
- Display HTTPS-enabled URL after successful configuration
@coderabbitai

coderabbitai Bot commented Nov 20, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds configurable WWW redirect handling across site provisioning and HTTPS enablement, a new site:https command, server-site detection in server-info, dynamic Caddy vhost generation for site-add/site-https, and trait/UI updates to propagate and display per-site wwwMode and PHP/HTTPS info.

Changes

Cohort / File(s) Summary
HTTPS Command
app/Console/Site/SiteHttpsCommand.php
New Symfony command site:https to enable HTTPS for an existing site; validates input, loads site/server/site-config, invokes playbooks/site-https.sh with DEPLOYER_* env vars (including DEPLOYER_WWW_MODE), and prints the HTTPS URL.
Site Addition CLI
app/Console/Site/SiteAddCommand.php
Added --www-mode option; gatherSiteInfo now collects/returns wwwMode; wwwMode propagated into provisioning payload, DEPLOYER_WWW_MODE, command replay payload, and next-steps/DNS output.
Command Registration
app/SymfonyApp.php
Registers the new SiteHttpsCommand in the application commands list.
Server display & helpers
app/Traits/ServersTrait.php
Added getSiteConfig(array $info, string $domain): ?array; extended displayServerInfo() to render a "Sites Config" section showing per-domain PHP version, www_mode, and HTTPS status.
Site helpers
app/Traits/SitesTrait.php
Added normalizeDomain(string $domain) and updated validateSiteDomain() to normalize domains (lowercase/trim/strip leading www.) before validation.
Server-info playbook
playbooks/server-info.sh
Added get_sites_config() to detect Caddy site configs (domain, PHP socket/version, www redirect mode, HTTPS flag) and emit a sites_config block into DEPLOYER_OUTPUT_FILE (YAML-like).
Site-add playbook
playbooks/site-add.sh
Switched vhost generation to use DEPLOYER_WWW_MODE; builds dynamic Caddy config (cases: redirect-to-root, redirect-to-www) instead of a single static vhost block.
Site-https playbook
playbooks/site-https.sh
New playbook to update a site's Caddy config for HTTPS: validates env, updates vhost per www_mode, reloads services, and emits YAML-like success output (https_enabled: true).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant CLI_Add as SiteAddCommand
    participant CLI_HTTPS as SiteHttpsCommand
    participant Traits
    participant Playbook_Add as playbooks/site-add.sh
    participant Playbook_HTTPS as playbooks/site-https.sh
    participant Caddy

    User->>CLI_Add: run site:add (optionally --www-mode)
    CLI_Add->>Traits: gatherSiteInfo (returns wwwMode)
    CLI_Add->>Playbook_Add: execute with DEPLOYER_WWW_MODE
    Playbook_Add->>Caddy: write/update vhost file (mode-specific)
    Playbook_Add-->>CLI_Add: exit status & output
    CLI_Add->>User: show site URL and DNS guidance (based on wwwMode)

    User->>CLI_HTTPS: run site:https --domain example.com
    CLI_HTTPS->>Traits: select site, load server/site config (wwwMode)
    CLI_HTTPS->>Playbook_HTTPS: execute with DEPLOYER_WWW_MODE
    Playbook_HTTPS->>Caddy: update vhost file and reload services
    Playbook_HTTPS-->>CLI_HTTPS: success output (https_enabled:true)
    CLI_HTTPS->>User: display HTTPS URL (considering wwwMode)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • playbooks/server-info.sh: parsing of Caddy configs and robustness for varied file formats.
    • playbooks/site-add.sh and playbooks/site-https.sh: correctness of generated Caddy blocks for both redirect modes and error handling when writing/reloading.
    • SiteAddCommand: gatherSiteInfo changes and replay payload compatibility.
    • ServersTrait display changes: handling of missing/malformed sites_config entries.

Possibly related PRs

  • bigpixelrocket/deployer-php#77 — Overlaps ServersTrait/displayServerInfo and server-info playbook output changes; likely touches the same site-config emission and display logic.
  • bigpixelrocket/deployer-php#60 — Related edits to SiteAddCommand and playbook/site-add flow; relevant to the new --www-mode option and propagation.
  • bigpixelrocket/deployer-php#90 — Prior work on SiteAddCommand and site-add playbook refactors; closely related to the dynamic Caddy configuration updates.

Poem

🐇 I hopped through configs, gentle and spry,

I set the WWW where redirects lie.
Caddy now sings with secure TLS,
Domains bloom tidy, no more mess.
A rabbity cheer — safe sites, oh my!

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 PR title clearly and concisely summarizes the main feature being added: a new site:https command for enabling HTTPS, which directly matches the primary changes in the changeset.
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-https-support

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/Traits/SitesTrait.php (1)

137-158: Normalize domain consistently when resolving sites (selectSite vs validation)

You now normalize domains in validateSiteDomain() / gatherSiteInfo() via normalizeDomain(), but selectSite() still passes the raw --domain option straight into $this->sites->findByDomain($domain). That means site:https --domain=www.Example.com can fail to match an existing example.com entry that was normalized at creation time.

Consider normalizing the domain in selectSite() before lookup, e.g.:

$domain = $this->normalizeDomain($domain);
$site = $this->sites->findByDomain($domain);

to keep domain handling consistent across creation, validation, and selection.

Also applies to: 160-172

🧹 Nitpick comments (3)
app/Traits/ServersTrait.php (1)

73-102: Consider reusing getSiteConfig() in displayServerInfo()

getSiteConfig() encapsulates the parsing/normalization for php_version, www_mode, and https_enabled from sites_config, but displayServerInfo() re-implements similar extraction logic when building the “Sites Config” lines.

Not a correctness issue, but you could simplify and centralize behavior by using getSiteConfig() inside the loop, e.g.:

foreach ($info['sites_config'] as $domain => $_) {
    $config = $this->getSiteConfig($info, (string) $domain);
    if ($config === null) {
        continue;
    }

    $sitesItems[] = sprintf(
        '%s: PHP %s, %s, %s',
        $domain,
        $config['php_version'],
        $config['www_mode'],
        $config['https_enabled'] ? '<fg=green>HTTPS</>' : '<fg=yellow>HTTP</>'
    );
}

This keeps the parsing rules in one place and reduces duplication.

Also applies to: 425-449

playbooks/server-info.sh (1)

379-432: Sites configuration detection is coherent; consider updating header docs

The new get_sites_config() implementation and the sites_config YAML emission align with how site-add and site-https write Caddy configs (comments and http:// vs https:// usage), so HTTPS status and WWW mode should be detected reliably.

You might also:

  • Update the header “Returns YAML with:” section to mention the new sites_config block for completeness.
  • (Optional) quote php_version / www_mode / https_enabled in the YAML to avoid them being parsed as non-strings in some YAML consumers, though your PHP side already normalizes these safely.

Also applies to: 595-621

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

41-43: WWW mode plumbing from CLI to playbooks is correct; consider validating CLI values

The new --www-mode option is correctly:

  • Captured in gatherSiteInfo() (with a sensible interactive default and description).
  • Returned as wwwMode and wired into:
    • DEPLOYER_WWW_MODE for the site-add playbook.
    • The initial HTTP URL shown in “Next steps”.
    • The replay payload for site:add.

This end-to-end wiring looks solid.

You might optionally add validation for CLI-provided --www-mode (similar to how you validate php-version) so that typos are caught before hitting the remote playbook, rather than only via the “Invalid WWW mode” error from Bash.

Also applies to: 105-111, 130-141, 168-181, 187-195, 304-407

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 350388d and b367631.

📒 Files selected for processing (8)
  • app/Console/Site/SiteAddCommand.php (8 hunks)
  • app/Console/Site/SiteHttpsCommand.php (1 hunks)
  • app/SymfonyApp.php (2 hunks)
  • app/Traits/ServersTrait.php (2 hunks)
  • app/Traits/SitesTrait.php (2 hunks)
  • playbooks/server-info.sh (2 hunks)
  • playbooks/site-add.sh (5 hunks)
  • playbooks/site-https.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/Traits/SitesTrait.php
  • app/Traits/ServersTrait.php
  • app/Console/Site/SiteHttpsCommand.php
  • app/SymfonyApp.php
  • app/Console/Site/SiteAddCommand.php
🧠 Learnings (8)
📚 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-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 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 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 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 : 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 should not invoke other commands (no proxy commands)

Applied to files:

  • app/SymfonyApp.php
🧬 Code graph analysis (3)
app/Traits/SitesTrait.php (2)
app/Repositories/SiteRepository.php (1)
  • delete (133-147)
app/Console/Server/ServerDeleteCommand.php (1)
  • site (93-93)
app/Traits/ServersTrait.php (2)
app/Services/IOService.php (2)
  • displayDeets (543-566)
  • writeln (463-469)
app/Console/Server/ServerListCommand.php (1)
  • AsCommand (15-62)
playbooks/site-https.sh (2)
playbooks/helpers.sh (1)
  • run_cmd (18-24)
playbooks/site-delete.sh (1)
  • reload_caddy (59-67)
🔇 Additional comments (3)
playbooks/site-add.sh (1)

34-35: WWW mode wiring and Caddy HTTP config look consistent with HTTPS flow

The new DEPLOYER_WWW_MODE requirement and the dynamic Caddy config generation for redirect-to-root / redirect-to-www are consistent with the site-https playbook and server-info.sh’s sites_config detection (comments and http:// patterns line up). I don’t see functional issues here; behavior should be predictable from initial HTTP provisioning through later HTTPS enablement.

Also applies to: 141-231

app/SymfonyApp.php (1)

22-23: Command registration for site:https is correct

Importing SiteHttpsCommand and adding it to the $commands array cleanly wires the new HTTPS flow into the app and is consistent with existing command registration.

Also applies to: 165-171

playbooks/site-https.sh (1)

25-36: HTTPS Caddy update logic is coherent with provisioning and detection

The new site-https playbook correctly:

  • Validates all required DEPLOYER_* env vars (including DEPLOYER_WWW_MODE).
  • Rewrites the vhost to HTTPS-friendly Caddy config using the same site_block_config shape as site-add, but with Auto HTTPS listeners and HTTPS redirects that line up with server-info.sh’s https_enabled heuristics.
  • Reloads Caddy and surfaces failures clearly, then emits a simple YAML result.

This should behave idempotently and integrate cleanly with the existing provisioning flow.

Also applies to: 47-124, 130-155

Comment thread app/Console/Site/SiteHttpsCommand.php
Prevent the site-https command from proceeding with playbook execution
when PHP version cannot be detected from server configuration, avoiding
confusing failures during playbook execution with invalid phpunknown-fpm.sock paths.

@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/Console/Site/SiteHttpsCommand.php (1)

116-126: Optional: make site config access more defensive

$config['php_version'] and $config['www_mode'] are used assuming both keys are always present. You already guard the 'unknown' PHP version case, but if the structure of $config ever changes (e.g., missing www_mode), this would raise notices and break the command.

You could optionally harden this by providing defaults or validating keys up front, e.g. defaulting www_mode to 'redirect-to-root' if missing, and failing fast if php_version is absent entirely.

Also applies to: 140-142

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b367631 and 8cbbad3.

📒 Files selected for processing (1)
  • app/Console/Site/SiteHttpsCommand.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/Site/SiteHttpsCommand.php
🔇 Additional comments (2)
app/Console/Site/SiteHttpsCommand.php (2)

42-151: HTTPS execution flow looks solid

The end-to-end flow in execute() (site/server selection, server info, site config lookup, php_version === 'unknown' guard, playbook execution, and HTTPS URL display honoring www_mode) is coherent and consistent. No blocking issues from my side here.


35-36: Confirm --domain option is actually used for site selection

The command declares a required domain option, but execute() never reads it directly. This is fine if selectSite() (or shared command infrastructure) internally consults the domain option; otherwise the option would be effectively unused.

Please double‑check that selectSite() honors the domain option for filtering/preselection; if not, consider wiring it through explicitly.

Use getSiteConfig() method instead of duplicating extraction logic
when building Sites Config display lines. This reduces code duplication
and ensures consistent parsing behavior.
- Extract WWW mode options into reusable array
- Switch to getValidatedOptionOrPrompt for proper validation
- Add validation logic to ensure only valid WWW modes are accepted
- Add quotes around php_version, www_mode, and https_enabled values
- Add documentation comment for sites_config field in output

@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 (3)
app/Traits/ServersTrait.php (2)

73-102: Robust site config extraction; consider intent around single-use helper and HTTPS unknowns

The helper cleanly normalizes per-site config (php_version, www_mode, https_enabled) and guards against malformed sites_config entries, which makes downstream rendering code much safer.

Two small points to consider:

  • Given the current codebase, getSiteConfig() is only called from displayServerInfo(). Per the PHP guidelines about avoiding single‑use helpers/method‑call overhead, you could inline this logic into the Sites Config section to reduce indirection. That said, if you expect other commands/traits to consume site config soon (e.g., site:https), keeping this helper is reasonable for reuse and consistency.
  • https_enabled falls back to false when missing, which renders as HTTP. If the playbook might ever omit this key for “unknown” rather than “explicitly not HTTPS”, you may want a tri‑state (true/false/unknown) or a dedicated sentinel so the UI can distinguish “definitely HTTP” from “no data yet”.

425-446: Sites Config display is clear; consider domain normalization for consistency

The new “Sites Config” section is straightforward and user‑friendly:

  • Skips invalid entries via getSiteConfig(), so it won’t explode on bad data.
  • Sensible fallbacks (? for unknown PHP / WWW mode, color‑coded HTTPS vs HTTP) make the output readable.

Two minor refinements to consider:

  • If site domains are stored in a normalized form elsewhere (e.g., via normalizeDomain() in SitesTrait), you might want to normalize $domain before display so this view always matches how domains are shown in other commands.
  • If you anticipate a lot of sites per server, you might later want to sort array_keys($info['sites_config']) to produce a stable, alphabetical listing.
app/Console/Site/SiteAddCommand.php (1)

308-308: Domain normalization and WWW mode selection are solid; consider centralizing allowed modes

The updated gatherSiteInfo() flow looks good:

  • Domain is validated first and then normalized via normalizeDomain(), which prevents issues like www.www.example.com when building display URLs or Caddy config.
  • www-mode is obtained via getValidatedOptionOrPrompt, with a clear prompt, helpful labels (Redirect www to non-www / Redirect non-www to www), and strict validation against the allowed keys. Invalid CLI values cleanly cause the method to return null, which the caller already treats as a failure.
  • The return type and payload now correctly include wwwMode, keeping the structure explicit.

One suggestion:

  • The $wwwModes array (and the corresponding set of valid keys) will almost certainly be needed in SiteHttpsCommand and possibly in other flows. You might consider centralizing these allowed modes (e.g., a small enum, constant, or a helper in SitesTrait) so you don’t have to keep multiple definitions in sync across commands.

Also applies to: 328-357, 410-416

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cbbad3 and ab18577.

📒 Files selected for processing (3)
  • app/Console/Site/SiteAddCommand.php (8 hunks)
  • app/Traits/ServersTrait.php (2 hunks)
  • playbooks/server-info.sh (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • playbooks/server-info.sh
🧰 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/Traits/ServersTrait.php
🧬 Code graph analysis (2)
app/Console/Site/SiteAddCommand.php (2)
app/Services/IOService.php (3)
  • writeln (463-469)
  • getValidatedOptionOrPrompt (164-187)
  • promptSelect (301-317)
app/Traits/SitesTrait.php (1)
  • normalizeDomain (163-172)
app/Traits/ServersTrait.php (1)
app/Services/IOService.php (3)
  • info (474-477)
  • displayDeets (543-566)
  • writeln (463-469)
🔇 Additional comments (2)
app/Console/Site/SiteAddCommand.php (2)

41-42: CLI options for PHP version and WWW mode are consistent and well-scoped

Defining --php-version and --www-mode here aligns the CLI surface with how selectPhpVersion() and the new wwwMode handling work later in the command. Names match the internal option keys used in prompts and replay, so this will behave predictably for both interactive and scripted usage.


105-111: End‑to‑end wwwMode propagation and UX look coherent

The new wwwMode wiring is cohesive:

  • It’s captured from gatherSiteInfo() and destructured alongside other site info.
  • It’s passed to the playbook as DEPLOYER_WWW_MODE, so provisioning can generate the correct Caddy config.
  • The “Next steps” URL reflects the chosen mode (http://www.{domain} vs http://{domain}), and the DNS guidance now clearly calls out both root and www records.
  • Command replay includes 'www-mode' => $wwwMode, which keeps automation reproducible.

Behaviorally this matches the intent of “redirect-to-root” vs “redirect-to-www” without surprising double‑www cases (thanks to earlier domain normalization).

Also applies to: 139-140, 168-181, 193-194

@loadinglucian
loadinglucian merged commit 2718b55 into main Nov 20, 2025
5 checks passed
@loadinglucian
loadinglucian deleted the feat/site-https-support branch November 20, 2025 14:22
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