Skip to content

Fix installer#11689

Merged
abnegate merged 13 commits into1.9.xfrom
fix-installer
Mar 31, 2026
Merged

Fix installer#11689
abnegate merged 13 commits into1.9.xfrom
fix-installer

Conversation

@abnegate
Copy link
Copy Markdown
Member

What does this PR do?

(Provide a description of what this PR does and why it's needed.)

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Screenshots may also be helpful.)

Related PRs and Issues

  • (Related PR or issue)

Checklist

  • Have you read the Contributing Guidelines on issues?
  • If the PR includes a change to an API's metadata (desc, label, params, etc.), does it also include updated API specs and example docs?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 673797f3-bf55-43d2-9fe7-6bdca18e6807

📥 Commits

Reviewing files that changed from the base of the PR and between fe6f79c and 4e32497.

📒 Files selected for processing (6)
  • tests/e2e/Services/GraphQL/Legacy/AbuseTest.php
  • tests/e2e/Services/GraphQL/Legacy/AuthTest.php
  • tests/e2e/Services/GraphQL/Legacy/DatabaseClientTest.php
  • tests/e2e/Services/GraphQL/TablesDB/AbuseTest.php
  • tests/e2e/Services/GraphQL/TablesDB/AuthTest.php
  • tests/e2e/Services/GraphQL/TablesDB/DatabaseClientTest.php

📝 Walkthrough

Walkthrough

Replaced AGENTS.md with new Appwrite-focused project/module and HTTP action documentation. Installer flow: Install.php centralizes completion via an $onComplete callback and a $responseSent guard; Tasks/Install.php adds ?callable $onComplete = null, tightens web-installer launch when explicit CLI params exist, adds hasExplicitCliParams(), runs analytics in a coroutine when available, changes host/IP resolution and analytics payload field, and adds timeouts to the Growth API client. Server.php enables Swoole coroutines with Runtime::enableCoroutine(SWOOLE_HOOK_ALL). Installer templates/CSS were minorly adjusted. Multiple e2e tests increased polling timeouts and intervals.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description contains only the default contribution template with no actual description of changes, test plan, or explanation of why the PR was created. Replace the template with a meaningful description explaining the installer fixes, the reasoning behind timeout adjustments, test verification steps, and related issues.
Docstring Coverage ⚠️ Warning Docstring coverage is 36.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fix installer' is vague and doesn't specify which installer issue is being addressed or what the fix entails. Provide a more descriptive title that clarifies the specific installer problem being fixed, such as 'Fix installer completion handling and polling timeouts' or similar.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-installer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
src/Appwrite/Platform/Tasks/Install.php (1)

777-791: ⚠️ Potential issue | 🔴 Critical

Remove the unsupported ip field from the Growth API payload or update the API.

The Growth API analytics endpoint does not accept an ip field in the payload. Sending this field will either fail silently or cause errors. Either remove the field from the payload or coordinate an API update to support it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Appwrite/Platform/Tasks/Install.php` around lines 777 - 791, The Growth
API payload in src/Appwrite/Platform/Tasks/Install.php currently includes an
unsupported 'ip' field built from $hostIp inside the $payload array; remove the
'ip' => (...) entry from the $payload construction (or alternatively coordinate
updating the Growth API) so the sent payload no longer contains the 'ip' key —
update the code that builds $payload in the Install task where $hostIp is
computed and referenced to omit the 'ip' element.
🧹 Nitpick comments (2)
AGENTS.md (1)

44-52: Add language specifier to fenced code block.

The code block showing module structure should have a language identifier for consistency and to satisfy markdown linting rules. Since this is a directory structure, use text or plaintext.

📝 Suggested fix
-```
+```text
 Module.php           -- registers all services for the module
 Services/Http.php    -- registers HTTP endpoints
 Services/Workers.php -- registers background workers
 Services/Tasks.php   -- registers CLI tasks
 Http/{Service}/      -- endpoint actions (Create.php, Get.php, Update.php, Delete.php, XList.php)
 Workers/             -- worker implementations
 Tasks/               -- CLI task implementations
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 44 - 52, The fenced code block that lists the module
structure lacks a language specifier; update the opening triple-backtick to
include a plaintext identifier (e.g., ```text) so the block becomes a labeled
plaintext code fence containing the listed lines (Module.php, Services/Http.php,
Services/Workers.php, Services/Tasks.php, Http/{Service}/, Workers/, Tasks/).
src/Appwrite/Platform/Tasks/Install.php (1)

639-646: Swallowing all exceptions from $onComplete may hide legitimate errors.

While silently catching \Throwable prevents completion callbacks from breaking the installation flow, it also suppresses any diagnostic information. Consider logging the exception for debugging purposes.

📝 Suggested improvement
                if ($onComplete) {
                    try {
                        $onComplete();
-                   } catch (\Throwable) {
+                   } catch (\Throwable $e) {
+                       // Log for debugging but don't fail installation
+                       error_log('onComplete callback failed: ' . $e->getMessage());
                    }
                }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Appwrite/Platform/Tasks/Install.php` around lines 639 - 646, The current
catch block around $onComplete() swallows all throwables; change it to catch
(\Throwable $e) and log the exception before continuing so errors aren't
hidden—use the existing logger (e.g. $this->logger or the available logging
instance) to call ->error(...) including $e->getMessage() and
$e->getTraceAsString() (or pass $e as context) inside the catch, then continue
without rethrowing to preserve the current flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 777-791: The Growth API payload in
src/Appwrite/Platform/Tasks/Install.php currently includes an unsupported 'ip'
field built from $hostIp inside the $payload array; remove the 'ip' => (...)
entry from the $payload construction (or alternatively coordinate updating the
Growth API) so the sent payload no longer contains the 'ip' key — update the
code that builds $payload in the Install task where $hostIp is computed and
referenced to omit the 'ip' element.

---

Nitpick comments:
In `@AGENTS.md`:
- Around line 44-52: The fenced code block that lists the module structure lacks
a language specifier; update the opening triple-backtick to include a plaintext
identifier (e.g., ```text) so the block becomes a labeled plaintext code fence
containing the listed lines (Module.php, Services/Http.php,
Services/Workers.php, Services/Tasks.php, Http/{Service}/, Workers/, Tasks/).

In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 639-646: The current catch block around $onComplete() swallows all
throwables; change it to catch (\Throwable $e) and log the exception before
continuing so errors aren't hidden—use the existing logger (e.g. $this->logger
or the available logging instance) to call ->error(...) including
$e->getMessage() and $e->getTraceAsString() (or pass $e as context) inside the
catch, then continue without rethrowing to preserve the current flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ebb3edb3-46d3-4644-a4db-7e43d804557c

📥 Commits

Reviewing files that changed from the base of the PR and between ea032c2 and 100cbf5.

📒 Files selected for processing (4)
  • AGENTS.md
  • src/Appwrite/Platform/Installer/Http/Installer/Install.php
  • src/Appwrite/Platform/Installer/Server.php
  • src/Appwrite/Platform/Tasks/Install.php

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 31, 2026

🔄 PHP-Retry Summary

Flaky tests detected across commits:

Commit 100cbf5 - 2 flaky tests
Test Retries Total Time Details
RealtimeConsoleClientTest::testCreateDeployment 1 2.11s Logs
UsageTest::testVectorsDBStats 1 10.11s Logs
Commit 8b7459f - 2 flaky tests
Test Retries Total Time Details
UsageTest::testVectorsDBStats 1 10.53s Logs
AuthTest::testInvalidAuth 1 0ms Logs
Commit fe6f79c - 3 flaky tests
Test Retries Total Time Details
UsageTest::testVectorsDBStats 1 10.09s Logs
LegacyCustomClientTest::testAttributeResponseModels 1 242.90s Logs
LegacyCustomServerTest::testCreatedBefore 1 240.38s Logs
Commit 4e32497 - 21 flaky tests
Test Retries Total Time Details
UsageTest::testVectorsDBStats 1 10.34s Logs
LegacyConsoleClientTest::testListDocumentsWithCache 1 1.91s Logs
LegacyCustomClientTest::testTimeout 1 125.55s Logs
LegacyCustomServerTest::testListAttributes 1 240.71s Logs
LegacyCustomServerTest::testCreateDocument 1 17ms Logs
LegacyCustomServerTest::testListDocuments 1 17ms Logs
LegacyCustomServerTest::testListDocumentsWithCache 1 14ms Logs
LegacyCustomServerTest::testListDocumentsCacheBustedByAttributeChange 1 12ms Logs
LegacyCustomServerTest::testGetDocument 1 13ms Logs
LegacyCustomServerTest::testGetDocumentWithQueries 1 14ms Logs
LegacyCustomServerTest::testQueryBySequenceType 1 14ms Logs
LegacyCustomServerTest::testListDocumentsAfterPagination 1 14ms Logs
LegacyCustomServerTest::testListDocumentsBeforePagination 1 13ms Logs
LegacyCustomServerTest::testListDocumentsLimitAndOffset 1 13ms Logs
LegacyCustomServerTest::testDocumentsListQueries 1 14ms Logs
LegacyCustomServerTest::testUpdateDocument 1 13ms Logs
LegacyCustomServerTest::testDeleteDocument 1 15ms Logs
LegacyCustomServerTest::testDefaultPermissions 1 12ms Logs
LegacyCustomServerTest::testPersistentCreatedAt 1 15ms Logs
LegacyCustomServerTest::testNotSearch 1 6ms Logs
LegacyTransactionsCustomClientTest::testPartialFailureRollback 1 240.40s Logs
Commit 7e49cf0 - 2 flaky tests
Test Retries Total Time Details
UsageTest::testVectorsDBStats 1 10.09s Logs
RealtimeConsoleClientTest::testCreateDeployment 1 2.12s Logs

Note: Flaky test results are tracked for the last 5 commits

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 31, 2026

Greptile Summary

This PR fixes several bugs in the web installer flow, primarily around coroutine support and SSE stream timing. The core change moves completion signaling (SSE done event / JSON response) into an idempotent $onComplete callback that is invoked before the analytics tracking call, so the browser can redirect immediately rather than waiting for the HTTP analytics request to complete. Tracking is then offloaded to a go() coroutine when running inside a Swoole request context (detected via Coroutine::getCid() !== -1), which requires the newly added Runtime::enableCoroutine(SWOOLE_HOOK_ALL) call in the server bootstrap. Additional fixes include: properly treating an existing installation as an upgrade in the web installer, adding HTTP client timeouts for the analytics endpoint, suppressing DNS resolution warnings with @gethostbyname, and several UI polish items.

Key changes:

  • Server.php: Runtime::enableCoroutine(SWOOLE_HOOK_ALL) is now called before the Swoole server starts, enabling hook-based coroutines for all native PHP blocking calls inside the request handlers.
  • Http/Installer/Install.php: The $onComplete closure is guarded by a $responseSent flag to prevent duplicate SSE/JSON responses; it is both passed to performInstallation() (for early signalling, pre-tracking) and called again as a safety net after the call returns (handles the $noStart path where $onComplete is not called internally).
  • Tasks/Install.php: $isUpgrade || $existingInstallation corrects the web installer mode for re-runs against an existing deployment; 5-second connect/read timeouts are added to the analytics HTTP client, guarding against DNS hangs.
  • UI: The "Secret API key" row is hidden during upgrades in step-4.phtml, the progress label now reads "Appwrite" instead of "your app", and a CSS min-height: 0 rule eliminates excess whitespace in the upgrade step layout.

Confidence Score: 5/5

Safe to merge — no new P0/P1 issues introduced; all meaningful concerns were raised in prior review threads.

All changed files are in the installer path only (no production API surface affected). The $responseSent guard makes the double-$onComplete invocation pattern safe. Coroutine enablement is placed correctly before the server starts. The remaining open concerns (hasExplicitCliParams breadth, ip field rename) were flagged in prior threads and are pre-existing decisions by the author. No new correctness or security issues were found.

No files require special attention.

Important Files Changed

Filename Overview
src/Appwrite/Platform/Tasks/Install.php Calls $onComplete before analytics tracking so the SSE stream closes promptly; uses Coroutine::getCid() !== -1 to run tracking asynchronously in a go() coroutine when inside Swoole; adds HTTP client timeouts; fixes upgrade detection for the web installer.
src/Appwrite/Platform/Installer/Http/Installer/Install.php Introduces an idempotent $onComplete callback (guarded by a $responseSent flag) that is both passed into performInstallation() (called pre-tracking) and called again as a safety net after the call returns; the guard correctly prevents double-sends in both paths.
src/Appwrite/Platform/Installer/Server.php Adds Runtime::enableCoroutine(SWOOLE_HOOK_ALL) before the Swoole server starts, enabling coroutine hooks for all native PHP functions (sleep, sockets, etc.) required for the new go() tracking coroutine to work correctly.
app/views/install/installer/templates/steps/step-4.phtml Hides the "Secret API key" review row during upgrades; $isUpgrade is safely guarded by the ?? false default at the top of the file.
app/views/install/installer/templates/steps/step-5.phtml Minor text update: "your app" → "Appwrite" for both install and upgrade states; no logic changes.
app/views/install/installer/css/styles.css Adds min-height: 0 to .installer-step elements inside an upgrade page, fixing excess vertical whitespace in the upgrade flow.
AGENTS.md Full rewrite to a denser, table-driven format; adds cross-repo context, module structure details, and stack information; no functional code changes.

Reviews (2): Last reviewed commit: "Revert "(fix): increase GraphQL schema p..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/Appwrite/Platform/Tasks/Install.php (1)

775-789: Minor: gethostbyname() never returns false.

The function returns the unmodified hostname on failure rather than false. The check $hostIp !== $domain is sufficient.

🔧 Simplified check
 $hostIp = `@gethostbyname`($domain);
 ...
-'hostIp' => ($hostIp !== false && $hostIp !== $domain) ? $hostIp : null,
+'hostIp' => ($hostIp !== $domain) ? $hostIp : null,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Appwrite/Platform/Tasks/Install.php` around lines 775 - 789, The check
for DNS resolution uses gethostbyname($domain) which never returns false, so
update the condition that sets 'hostIp' in the payload (in Install.php where
$hostIp is assigned and used) to only test whether $hostIp differs from $domain;
remove the redundant !== false check and set hostIp to null when $hostIp ===
$domain, i.e. replace the current ($hostIp !== false && $hostIp !== $domain)
logic with a single ($hostIp !== $domain) check so unresolved hostnames yield
null.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 775-789: The check for DNS resolution uses gethostbyname($domain)
which never returns false, so update the condition that sets 'hostIp' in the
payload (in Install.php where $hostIp is assigned and used) to only test whether
$hostIp differs from $domain; remove the redundant !== false check and set
hostIp to null when $hostIp === $domain, i.e. replace the current ($hostIp !==
false && $hostIp !== $domain) logic with a single ($hostIp !== $domain) check so
unresolved hostnames yield null.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8bfedd10-96c4-43d4-8666-30706e9feb66

📥 Commits

Reviewing files that changed from the base of the PR and between 100cbf5 and 8b7459f.

📒 Files selected for processing (1)
  • src/Appwrite/Platform/Tasks/Install.php

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 31, 2026

✨ Benchmark results

  • Requests per second: 1,787
  • Requests with 200 status code: 321,771
  • P99 latency: 0.097952854

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 1,787 1,195
200 321,771 215,193
P99 0.097952854 0.192065999

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/Appwrite/Platform/Tasks/Install.php (2)

775-789: The !== false check is unnecessary for gethostbyname().

PHP's gethostbyname() returns the original hostname string on failure, never false. The check $hostIp !== false is always true.

♻️ Simplified condition
         $hostIp = `@gethostbyname`($domain);
 
         $payload = [
             ...
-            'ip' => ($hostIp !== false && $hostIp !== $domain) ? $hostIp : null,
+            'ip' => ($hostIp !== $domain) ? $hostIp : null,
             ...
         ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Appwrite/Platform/Tasks/Install.php` around lines 775 - 789, The ip
assignment logic uses gethostbyname($domain) and checks "$hostIp !== false", but
gethostbyname never returns false (it returns the original hostname on failure),
so update the condition that builds the payload 'ip' field in Install::
(variable $hostIp and the payload array) to only check that $hostIp is not equal
to the original $domain (or is a valid IP) — remove the redundant !== false
check and use a single clear test like ($hostIp !== $domain) or PHP's
filter_var($hostIp, FILTER_VALIDATE_IP) to set the 'ip' value or null.

639-656: Silently swallowing exceptions in $onComplete may hide lock-release failures.

Per the context, $onComplete contains critical logic including $state->updateGlobalLock(). If the lock update fails, the exception is caught and ignored, potentially leaving the installer in a locked state for subsequent runs.

Consider logging the exception or at least preserving it for debugging:

💡 Proposed enhancement to log swallowed exceptions
                 if ($onComplete) {
                     try {
                         $onComplete();
                     } catch (\Throwable) {
+                        // Lock update or response finalization failed - log for debugging
+                        // Consider: Console::warning('onComplete callback failed: ' . $e->getMessage());
                     }
                 }

The coroutine-wrapped tracking logic is correct — go() is only called when inside a Swoole context (Coroutine::getCid() !== -1), otherwise tracking runs synchronously.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Appwrite/Platform/Tasks/Install.php` around lines 639 - 656, The current
try/catch around invoking the $onComplete callback swallows all exceptions
(which may include failures from $state->updateGlobalLock()), so update the
catch to record the error instead of ignoring it: catch the Throwable as $e and
log it (e.g. via $this->logger->error(...) or a suitable logger/error_log) with
context indicating it occurred in the $onComplete callback and include
$e->getMessage() and stack trace; alternatively rethrow or store the exception
for higher-level handling—locate the invocation of $onComplete in Install.php
and update that try/catch accordingly (leave the coroutine handling and
trackSelfHostedInstall calls unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 775-789: The ip assignment logic uses gethostbyname($domain) and
checks "$hostIp !== false", but gethostbyname never returns false (it returns
the original hostname on failure), so update the condition that builds the
payload 'ip' field in Install:: (variable $hostIp and the payload array) to only
check that $hostIp is not equal to the original $domain (or is a valid IP) —
remove the redundant !== false check and use a single clear test like ($hostIp
!== $domain) or PHP's filter_var($hostIp, FILTER_VALIDATE_IP) to set the 'ip'
value or null.
- Around line 639-656: The current try/catch around invoking the $onComplete
callback swallows all exceptions (which may include failures from
$state->updateGlobalLock()), so update the catch to record the error instead of
ignoring it: catch the Throwable as $e and log it (e.g. via
$this->logger->error(...) or a suitable logger/error_log) with context
indicating it occurred in the $onComplete callback and include $e->getMessage()
and stack trace; alternatively rethrow or store the exception for higher-level
handling—locate the invocation of $onComplete in Install.php and update that
try/catch accordingly (leave the coroutine handling and trackSelfHostedInstall
calls unchanged).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5ed736f4-c240-4241-90cf-b14bcd81f1ee

📥 Commits

Reviewing files that changed from the base of the PR and between 8b7459f and fe6f79c.

📒 Files selected for processing (4)
  • app/views/install/installer/css/styles.css
  • app/views/install/installer/templates/steps/step-4.phtml
  • app/views/install/installer/templates/steps/step-5.phtml
  • src/Appwrite/Platform/Tasks/Install.php
✅ Files skipped from review due to trivial changes (3)
  • app/views/install/installer/templates/steps/step-5.phtml
  • app/views/install/installer/css/styles.css
  • app/views/install/installer/templates/steps/step-4.phtml

@abnegate abnegate merged commit ec20fb5 into 1.9.x Mar 31, 2026
38 of 42 checks passed
@abnegate abnegate deleted the fix-installer branch March 31, 2026 03:51
@blacksmith-sh
Copy link
Copy Markdown

blacksmith-sh bot commented Mar 31, 2026

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
› Tests\E2E\Services\TablesDB\TablesDBCustomClientTest/testDecrementAttribute View Logs

Fix in Cursor

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