Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a POST /install/reset endpoint with soft and hard reset flows; streams docker-compose output to report per-container startup progress; reduces the global installer lock timeout and introduces throttled stale-lock cleanup; persists installer lock/ID to both sessionStorage and localStorage with backup fallbacks; relaxes SSL/emailCertificates validation and falls back to accountEmail where appropriate; adds per-row install counters and global action buttons in the installer UI; removes Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR fixes several installer state-management issues: it adds a Key changes to note:
Confidence Score: 3/5
Important Files Changed
|
🔄 PHP-Retry SummaryFlaky tests detected across commits: Commit
|
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testVectorsDBStats |
1 | 10.05s | Logs |
DocumentsDBConsoleClientTest::testTimeout |
1 | 123.77s | Logs |
LegacyConsoleClientTest::testListDocumentsWithCache |
1 | 470ms | Logs |
LegacyConsoleClientTest::testInvalidDocumentStructure |
1 | 240.85s | Logs |
LegacyCustomClientTest::testAttributeResponseModels |
1 | 242.36s | Logs |
DatabasesStringTypesTest::testListStringTypeAttributes |
1 | 240.38s | Logs |
DatabasesStringTypesTest::testGetCollectionWithStringTypeAttributes |
1 | 240.36s | Logs |
Commit 5ca30d3 - 2 flaky tests
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testVectorsDBStats |
1 | 10.01s | Logs |
ProxyCustomServerTest::testCreateSiteRule |
1 | 120.09s | Logs |
Commit d0978d8 - 5 flaky tests
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testVectorsDBStats |
1 | 10.01s | Logs |
TablesDBConsoleClientTest::testTimeout |
1 | 121.21s | Logs |
TablesDBCustomClientTest::testSelectQueries |
1 | 120.05s | Logs |
TablesDBTransactionsCustomServerTest::testBulkDeleteOperations |
1 | 120.81s | Logs |
TablesDBTransactionsCustomServerTest::testUpsertAutoIdThenUpdate |
1 | 240.41s | Logs |
Commit f016d4b - 2 flaky tests
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testVectorsDBStats |
1 | 10.10s | Logs |
DatabaseServerTest::testUpdateURLColumn |
1 | 240.39s | Logs |
Commit 4de9ec7 - 5 flaky tests
| Test | Retries | Total Time | Details |
|---|---|---|---|
DocumentsDBConsoleClientTest::testTimeout |
1 | 120.07s | Logs |
DocumentsDBCustomServerTest::testTimeout |
1 | 120.70s | Logs |
LegacyCustomClientTest::testCreateIndexes |
1 | 240.92s | Logs |
LegacyTransactionsConsoleClientTest::testDeleteDocumentDuringTransaction |
1 | 240.45s | Logs |
UsageTest::testVectorsDBStats |
1 | 10.12s | Logs |
Note: Flaky test results are tracked for the last 5 commits
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
app/views/install/installer/templates/steps/step-5.phtml (1)
55-62: Consider adding confirmation for the "Reset Everything" button.The hard reset button could have destructive consequences. Consider whether a confirmation dialog should be added before executing the hard reset to prevent accidental data loss.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/install/installer/templates/steps/step-5.phtml` around lines 55 - 62, The "Reset Everything" button (data attribute data-install-hard-reset) can trigger destructive actions without confirmation; modify the click handler that listens for [data-install-hard-reset] to present a confirmation dialog/modal before proceeding (e.g., browser confirm or site modal) and only call the existing hard-reset routine if the user explicitly confirms (optionally require typing a keyword or clicking a secondary confirm button); ensure the cancel path aborts without side effects and that any analytics/logging still reflect whether the action was confirmed.src/Appwrite/Platform/Tasks/Install.php (1)
752-753: Shell commands for system info are safe but may fail silently on non-Linux systems.The commands
nprocand reading from/proc/meminfoare Linux-specific. On other platforms (macOS during local dev, Windows WSL edge cases), these will return empty/null values, which is handled gracefully with the?: nullfallback and(int) round(...)conversion.Consider adding a brief inline comment noting this is Linux-specific for maintainability.
🤖 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 752 - 753, The code in Install.php that sets the 'cpus' and 'ram' values using shell_exec('nproc') and reading /proc/meminfo is Linux-specific and may return empty on non-Linux systems; add a brief inline comment immediately above the 'cpus' and 'ram' assignments (the lines that call nproc and grep MemTotal) stating that these commands are Linux-only and may return null/zero on other platforms (e.g., macOS/Windows), so the fallback behavior is intentional for portability and debugging.app/views/install/installer/js/modules/state.js (1)
141-149: Consider mirroring localStorage fallback back to sessionStorage for consistency.
getInstallLock()mirrors the localStorage value back to sessionStorage when falling back (line 71), butgetStoredInstallId()does not. This creates an inconsistency where the install lock will be synced but the install ID won't be if sessionStorage was cleared.♻️ Suggested fix for consistency
const getStoredInstallId = () => { try { const val = sessionStorage.getItem(INSTALL_ID_KEY); if (val) return val; } catch (error) {} try { - return localStorage.getItem(INSTALL_ID_LOCAL_KEY); + const val = localStorage.getItem(INSTALL_ID_LOCAL_KEY); + if (val) { + sessionStorage.setItem(INSTALL_ID_KEY, val); + return val; + } } catch (error) {} return null; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/install/installer/js/modules/state.js` around lines 141 - 149, getStoredInstallId currently reads INSTALL_ID_KEY from sessionStorage and falls back to INSTALL_ID_LOCAL_KEY in localStorage but does not mirror the localStorage value back into sessionStorage like getInstallLock does; update getStoredInstallId so that when it successfully retrieves the install id from localStorage (INSTALL_ID_LOCAL_KEY) it also writes that value into sessionStorage under INSTALL_ID_KEY, handling possible storage exceptions (try/catch) the same way as existing reads to maintain consistency with getInstallLock.src/Appwrite/Platform/Installer/Http/Installer/Complete.php (1)
111-123: IPv6 edge case handling could be clearer.When
$domaincontains more than 2 colons (e.g., a bare IPv6 address like2001:db8::1), the method returns the entire string lowercased rather than extracting the IP portion. This works forbuildCookieDomain()sincefilter_var(..., FILTER_VALIDATE_IP)will correctly identify it as an IP and return an empty cookie domain, but the method nameextractHostnameis slightly misleading in this case.Consider adding a brief inline comment to clarify the intent:
📝 Suggested clarification
$parts = explode(':', $domain); - return count($parts) <= 2 ? strtolower($parts[0]) : strtolower($domain); + // More than 2 colons indicates bare IPv6; return as-is for FILTER_VALIDATE_IP + return count($parts) <= 2 ? strtolower($parts[0]) : strtolower($domain);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Installer/Http/Installer/Complete.php` around lines 111 - 123, Update the extractHostname(string $domain) function to clarify IPv6 handling: add a concise inline comment near the colon-splitting logic (the branch that returns strtolower($domain) when count($parts) > 2) explaining that bare IPv6 addresses contain multiple colons and we intentionally return the full lowercased string so downstream checks (e.g., buildCookieDomain() using filter_var(..., FILTER_VALIDATE_IP)) can detect it as an IP; also add a short comment by the '[' bracket handling to note it covers scoped IPv6 literal syntax. Keep comments minimal and focused, referencing extractHostname and buildCookieDomain for clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/views/install/compose.phtml`:
- Around line 16-19: Restore consistency by re-adding 'postgresql' to the
$allowedDbServices array so $dbService isn't silently converted to 'mongodb'
(update the $allowedDbServices declaration and the $dbService fallback logic in
compose.phtml); alternatively, if PostgreSQL support should be removed, remove
it from the WhiteList validators in Install.php and Tasks/Install.php and from
the default/labels in context.js and ui.js so the UI, API and template remain
consistent—ensure the unique symbols to change are $allowedDbServices and
$dbService in the compose template, the WhiteList validators in Install.php and
Tasks/Install.php, and the defaults/label formatter in context.js and ui.js.
---
Nitpick comments:
In `@app/views/install/installer/js/modules/state.js`:
- Around line 141-149: getStoredInstallId currently reads INSTALL_ID_KEY from
sessionStorage and falls back to INSTALL_ID_LOCAL_KEY in localStorage but does
not mirror the localStorage value back into sessionStorage like getInstallLock
does; update getStoredInstallId so that when it successfully retrieves the
install id from localStorage (INSTALL_ID_LOCAL_KEY) it also writes that value
into sessionStorage under INSTALL_ID_KEY, handling possible storage exceptions
(try/catch) the same way as existing reads to maintain consistency with
getInstallLock.
In `@app/views/install/installer/templates/steps/step-5.phtml`:
- Around line 55-62: The "Reset Everything" button (data attribute
data-install-hard-reset) can trigger destructive actions without confirmation;
modify the click handler that listens for [data-install-hard-reset] to present a
confirmation dialog/modal before proceeding (e.g., browser confirm or site
modal) and only call the existing hard-reset routine if the user explicitly
confirms (optionally require typing a keyword or clicking a secondary confirm
button); ensure the cancel path aborts without side effects and that any
analytics/logging still reflect whether the action was confirmed.
In `@src/Appwrite/Platform/Installer/Http/Installer/Complete.php`:
- Around line 111-123: Update the extractHostname(string $domain) function to
clarify IPv6 handling: add a concise inline comment near the colon-splitting
logic (the branch that returns strtolower($domain) when count($parts) > 2)
explaining that bare IPv6 addresses contain multiple colons and we intentionally
return the full lowercased string so downstream checks (e.g.,
buildCookieDomain() using filter_var(..., FILTER_VALIDATE_IP)) can detect it as
an IP; also add a short comment by the '[' bracket handling to note it covers
scoped IPv6 literal syntax. Keep comments minimal and focused, referencing
extractHostname and buildCookieDomain for clarity.
In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 752-753: The code in Install.php that sets the 'cpus' and 'ram'
values using shell_exec('nproc') and reading /proc/meminfo is Linux-specific and
may return empty on non-Linux systems; add a brief inline comment immediately
above the 'cpus' and 'ram' assignments (the lines that call nproc and grep
MemTotal) stating that these commands are Linux-only and may return null/zero on
other platforms (e.g., macOS/Windows), so the fallback behavior is intentional
for portability and debugging.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 980f3387-45d7-4389-9f4d-3773051ec2bb
📒 Files selected for processing (16)
app/views/install/compose.phtmlapp/views/install/installer/css/styles.cssapp/views/install/installer/js/modules/progress.jsapp/views/install/installer/js/modules/state.jsapp/views/install/installer/js/modules/ui.jsapp/views/install/installer/js/steps.jsapp/views/install/installer/templates/steps/step-5.phtmldocker-compose.ymlsrc/Appwrite/Platform/Installer/Http/Installer/Complete.phpsrc/Appwrite/Platform/Installer/Http/Installer/Install.phpsrc/Appwrite/Platform/Installer/Http/Installer/Reset.phpsrc/Appwrite/Platform/Installer/Http/Installer/Status.phpsrc/Appwrite/Platform/Installer/Runtime/State.phpsrc/Appwrite/Platform/Installer/Services/Http.phpsrc/Appwrite/Platform/Tasks/Install.phptests/unit/Platform/Modules/Installer/ModuleTest.php
✨ Benchmark results
⚡ Benchmark Comparison
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/Appwrite/Platform/Tasks/Install.php (2)
661-676:⚠️ Potential issue | 🟠 MajorDo not substitute the session ID into
userId.Lines 674-676 intentionally leave
$userIdunset on the tolerated rerun/restricted path. Line 691 then falls back to$session['id'], which is a session resource, not an account resource, so the completion payload now carries an invalid user ID in exactly the scenario this change adds.Possible fix
- 'userId' => $userId ?? $session['id'], + 'userId' => $userId,If the client still needs the account ID here, resolve it explicitly after the session is created instead of reusing the session ID.
Also applies to: 681-695
🤖 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 661 - 676, The current change incorrectly allows a session ID to be reused as an account ID (the $userId variable) when makeApiCall('/v1/account', ...) fails with tolerated errors; instead of falling back to $session['id'], leave $userId null and, after creating the session (where $session is produced), explicitly resolve the real account ID via the appropriate API call (e.g., lookup account by session or call '/v1/account' GET) and assign that result to $userId; do not substitute $session['id'] for $userId and update code paths using $userId (including the completion payload) to use the resolved account ID.
739-757:⚠️ Potential issue | 🟠 MajorMove host-metadata probes inside error handling boundary.
Lines 756–757 call
shell_exec()before thetry/catchblock (line 761). On PHP 8.3+ withshell_execindisable_functions, this raises a fatal error that prevents payload construction, breaking the installation even though this method is explicitly designed to be non-blocking (per line 767). Move theshell_exec()calls inside thetryblock or guard them withfunction_exists('shell_exec')and null fallbacks to preserve the non-blocking contract.🤖 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 739 - 757, The payload construction currently calls shell_exec() for 'cpus' and 'ram' (inside Install::... around the $payload/data array) before the try/catch boundary, which can fatal on PHP 8.3+ when shell_exec is disabled; move those probes into the try block (the existing try/catch that sends the payload) or wrap each shell_exec call with a guard like function_exists('shell_exec') and provide null-safe fallbacks so failures do not abort payload creation—update references to the 'cpus' and 'ram' fields in the $payload/data array (and any related hostIp resolution using gethostbyname) to be computed safely inside the error-handling boundary.
🧹 Nitpick comments (2)
src/Appwrite/Platform/Tasks/Install.php (2)
982-983: Document the new callback and return contracts.The added
?callable $progressparameter and the shaped array returned fromexecWithContainerProgress()are not self-describing. Add PHPDoc for the callback signature and return keys so callers do not have to infer the contract from implementation details. As per coding guidelines, "Include comprehensive PHPDoc comments".Also applies to: 1053-1094
🤖 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 982 - 983, Add comprehensive PHPDoc to runDockerCompose to document the new ?callable $progress parameter and the shaped array returned by execWithContainerProgress(): specify the callable signature (e.g., function(string $container, string $status, int $completed, int $total): void or whatever fields you expect) and describe exact return array keys and value types produced by execWithContainerProgress() (e.g., ['container' => string, 'status' => string, 'progress' => int, ...]). Update the PHPDoc block above the runDockerCompose method and also add/adjust PHPDoc on execWithContainerProgress() (and any related helper methods around lines ~1053-1094) so callers can rely on the parameter and return contracts without reading implementation details.
1043-1050:countComposeServices()is countingcontainer_name, not services.This total stays correct only while every service emits exactly one
container_name:line. If a service ever omits that field, the progress counters will drift or stay at0/N. Counting the actual service entries would make this helper match its name and keep the installer counters stable.🤖 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 1043 - 1050, countComposeServices() currently counts "container_name:" occurrences, which breaks when services omit that field; change it to parse the compose YAML and return the number of top-level service entries instead. In the countComposeServices(string $composeFile) function, load and parse the compose file (e.g., using yaml_parse_file or Symfony\Component\Yaml\Yaml::parseFile) and if parsing succeeds, return count($parsed['services']) (with checks that 'services' exists and is an array); on any failure keep returning 0. This ensures the function counts actual service keys rather than container_name lines.
🤖 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 661-676: The current change incorrectly allows a session ID to be
reused as an account ID (the $userId variable) when makeApiCall('/v1/account',
...) fails with tolerated errors; instead of falling back to $session['id'],
leave $userId null and, after creating the session (where $session is produced),
explicitly resolve the real account ID via the appropriate API call (e.g.,
lookup account by session or call '/v1/account' GET) and assign that result to
$userId; do not substitute $session['id'] for $userId and update code paths
using $userId (including the completion payload) to use the resolved account ID.
- Around line 739-757: The payload construction currently calls shell_exec() for
'cpus' and 'ram' (inside Install::... around the $payload/data array) before the
try/catch boundary, which can fatal on PHP 8.3+ when shell_exec is disabled;
move those probes into the try block (the existing try/catch that sends the
payload) or wrap each shell_exec call with a guard like
function_exists('shell_exec') and provide null-safe fallbacks so failures do not
abort payload creation—update references to the 'cpus' and 'ram' fields in the
$payload/data array (and any related hostIp resolution using gethostbyname) to
be computed safely inside the error-handling boundary.
---
Nitpick comments:
In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 982-983: Add comprehensive PHPDoc to runDockerCompose to document
the new ?callable $progress parameter and the shaped array returned by
execWithContainerProgress(): specify the callable signature (e.g.,
function(string $container, string $status, int $completed, int $total): void or
whatever fields you expect) and describe exact return array keys and value types
produced by execWithContainerProgress() (e.g., ['container' => string, 'status'
=> string, 'progress' => int, ...]). Update the PHPDoc block above the
runDockerCompose method and also add/adjust PHPDoc on
execWithContainerProgress() (and any related helper methods around lines
~1053-1094) so callers can rely on the parameter and return contracts without
reading implementation details.
- Around line 1043-1050: countComposeServices() currently counts
"container_name:" occurrences, which breaks when services omit that field;
change it to parse the compose YAML and return the number of top-level service
entries instead. In the countComposeServices(string $composeFile) function, load
and parse the compose file (e.g., using yaml_parse_file or
Symfony\Component\Yaml\Yaml::parseFile) and if parsing succeeds, return
count($parsed['services']) (with checks that 'services' exists and is an array);
on any failure keep returning 0. This ensures the function counts actual service
keys rather than container_name lines.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7f794092-0def-4efa-a484-e3f681798350
📒 Files selected for processing (1)
src/Appwrite/Platform/Tasks/Install.php
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/views/install/installer/js/modules/progress.js`:
- Around line 996-1021: The catch block in performReset currently swallows
errors; update it to capture the exception (e) and log it for diagnostics and
user feedback: inside the catch of performReset, call console.error with a clear
message including installId and hard plus the error object, and also invoke
showToast? (if available) with status: 'error' and a short description from
e.message (fallback to a generic message) so failures during soft resets are
reported; keep the subsequent cleanup/redirect behavior intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: bcd89d22-5624-44cd-9adf-b1ee3ae22f04
📒 Files selected for processing (6)
app/views/install/compose.phtmlapp/views/install/installer/css/styles.cssapp/views/install/installer/js/modules/progress.jsapp/views/install/installer/templates/steps/step-5.phtmldocker-compose.ymlsrc/Appwrite/Platform/Tasks/Install.php
✅ Files skipped from review due to trivial changes (1)
- app/views/install/installer/css/styles.css
🚧 Files skipped from review as they are similar to previous changes (4)
- docker-compose.yml
- app/views/install/compose.phtml
- app/views/install/installer/templates/steps/step-5.phtml
- src/Appwrite/Platform/Tasks/Install.php
…to fix-installer-state # Conflicts: # src/Appwrite/Platform/Tasks/Install.php
There was a problem hiding this comment.
Actionable comments posted: 2
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)
206-210:⚠️ Potential issue | 🟠 MajorDon't reject the database you just auto-detected.
Lines 173-203 can now coerce
$databaseto the adapter from the existing install, but this guard still validates it against the default['mongodb', 'mariadb']. Existing PostgreSQL installs will still exit here, so the upgrade path stays blocked.💡 Suggested fix
$installerConfig = $this->readInstallerConfig(); $enabledDatabases = $installerConfig['enabledDatabases'] ?? ['mongodb', 'mariadb']; + if (isset($existingDatabase) && $existingDatabase !== null) { + $enabledDatabases[] = $existingDatabase; + $enabledDatabases = array_values(array_unique($enabledDatabases)); + } if (!in_array($database, $enabledDatabases, true)) { Console::error("Database '{$database}' is not available. Available options: " . implode(', ', $enabledDatabases)); Console::exit(1); }🤖 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 206 - 210, The guard that rejects $database uses a hard-coded default list instead of honoring the actual installer configuration: read the current installer config via readInstallerConfig(), derive $enabledDatabases from $installerConfig (not the literal ['mongodb','mariadb']) and then validate $database against that list so auto-detected adapters (e.g. 'postgresql') are allowed; update the check in Install.php that references $enabledDatabases and ensure the variable comes from $installerConfig['enabledDatabases'] ?? [] (or similar) before calling in_array on $database, then keep the existing Console::error/Console::exit behavior if validation truly fails.
♻️ Duplicate comments (1)
app/views/install/installer/js/modules/progress.js (1)
1010-1034:⚠️ Potential issue | 🟠 MajorDon't clear client state after a failed reset.
src/Appwrite/Platform/Installer/Http/Installer/Reset.phpLines 36-65 can still return a non-2xx on CSRF failure, and thrown fetch errors also land in the empty catch here. Both cases fall through toclearInstallLock()/clearInstallId()/cleanupInstallFlow()/redirect, so the browser behaves as if the reset succeeded while the server-side lock/progress can still exist.💡 Suggested fix
try { const res = await fetch('/install/reset', { method: 'POST', headers: withCsrfHeader({ 'Content-Type': 'application/json' }), body: JSON.stringify({ installId: installId || '', hard }) }); - if (hard && !res.ok) { + if (!res.ok) { const data = await res.json().catch(() => ({})); showToast?.({ status: 'error', title: 'Reset failed', - description: data?.message || 'Could not stop containers. Try running "docker compose down -v" manually.', + description: data?.message || (hard + ? 'Could not stop containers. Try running "docker compose down -v" manually.' + : 'Could not reset installer state. Refresh the page and try again.'), dismissible: true }); return; } - } catch (e) {} + } catch (error) { + console.error('Reset request failed:', { installId, hard, error }); + showToast?.({ + status: 'error', + title: 'Reset failed', + description: error?.message || 'Could not reach the installer server.', + dismissible: true + }); + return; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/views/install/installer/js/modules/progress.js` around lines 1010 - 1034, performReset currently clears client state and redirects even when the server returned a non-2xx or the fetch threw, causing the UI to assume success; update performReset to only call clearInstallLock(), clearInstallId(), cleanupInstallFlow(), and set window.location.href after a confirmed successful response (res.ok). Concretely: in the try block check if (!res.ok) for all cases (not only when hard) — parse error JSON (or fallback {}) and call showToast with the error message and return; in the catch block show an error toast describing the fetch/CSRF failure and return; only when the request succeeded (res.ok) proceed to run clearInstallLock, clearInstallId, cleanupInstallFlow, and redirect. Ensure you reference the performReset function and the clearInstallLock/clearInstallId/cleanupInstallFlow/window.location.href calls when making the change.
🧹 Nitpick comments (1)
src/Appwrite/Platform/Tasks/Install.php (1)
1069-1120: Add PHPDoc for the new compose-progress helpers.
countComposeServices()andexecWithContainerProgress()now hide non-obvious parsing and return-value contracts, but the helpers are undocumented. A short docblock here would make this path much easier to maintain.As per coding guidelines, "Include comprehensive PHPDoc comments".
🤖 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 1069 - 1120, Add comprehensive PHPDoc blocks to both countComposeServices() and execWithContainerProgress(): for countComposeServices(string $composeFile): int, document that it reads the compose file path, returns 0 on unreadable file or on preg_match_all failure, and that it counts lines matching the /^\s*container_name:/m pattern; for execWithContainerProgress(string $commandLine, int $totalServices, callable $progress, bool $isUpgrade): array, document the meaning and types of parameters (commandLine run via proc_open, totalServices used to cap progress, progress callable signature and expected constants InstallerServer::STEP_DOCKER_CONTAINERS and STATUS_IN_PROGRESS), describe returned array keys ('output' => array of stdout lines, 'exit' => process exit code), note that proc_open is used with stderr redirected, that progress exceptions are swallowed, and reference the timeout behavior via procCloseWithTimeout and self::PROC_CLOSE_TIMEOUT_SECONDS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 621-630: The currentStep variable is set after calling
waitForApiReady, so if waitForApiReady reports progress on STEP_ACCOUNT_SETUP or
times out the error is attributed to STEP_DOCKER_CONTAINERS; move the assignment
of $currentStep = InstallerServer::STEP_ACCOUNT_SETUP to before calling
waitForApiReady when !$isUpgrade so the progress/exception context matches;
update the block around waitForApiReady and createInitialAdminAccount
(references: waitForApiReady, $currentStep, InstallerServer::STEP_ACCOUNT_SETUP,
createInitialAdminAccount) so currentStep is set for fresh installs prior to the
readiness wait.
- Around line 1086-1118: The current fgets($pipes[1]) loop blocks indefinitely
if the child process stalls, preventing procCloseWithTimeout() from enforcing
the timeout; modify the read loop to set the stdout pipe non-blocking (call
stream_set_blocking($pipes[1], false)) and replace the blocking fgets loop with
a poll that uses stream_select() (or a loop that periodically calls
proc_get_status($process)) to check for available data and process liveness so
you can read available lines, update $output and call $progress
(InstallerServer::STEP_DOCKER_CONTAINERS) while still allowing
procCloseWithTimeout($process, self::PROC_CLOSE_TIMEOUT_SECONDS) to apply;
ensure you still fclose($pipes[1]) and preserve the existing logic that trims
lines and increments $started when checking for 'Container' +
'Started'/'Running'.
---
Outside diff comments:
In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 206-210: The guard that rejects $database uses a hard-coded
default list instead of honoring the actual installer configuration: read the
current installer config via readInstallerConfig(), derive $enabledDatabases
from $installerConfig (not the literal ['mongodb','mariadb']) and then validate
$database against that list so auto-detected adapters (e.g. 'postgresql') are
allowed; update the check in Install.php that references $enabledDatabases and
ensure the variable comes from $installerConfig['enabledDatabases'] ?? [] (or
similar) before calling in_array on $database, then keep the existing
Console::error/Console::exit behavior if validation truly fails.
---
Duplicate comments:
In `@app/views/install/installer/js/modules/progress.js`:
- Around line 1010-1034: performReset currently clears client state and
redirects even when the server returned a non-2xx or the fetch threw, causing
the UI to assume success; update performReset to only call clearInstallLock(),
clearInstallId(), cleanupInstallFlow(), and set window.location.href after a
confirmed successful response (res.ok). Concretely: in the try block check if
(!res.ok) for all cases (not only when hard) — parse error JSON (or fallback {})
and call showToast with the error message and return; in the catch block show an
error toast describing the fetch/CSRF failure and return; only when the request
succeeded (res.ok) proceed to run clearInstallLock, clearInstallId,
cleanupInstallFlow, and redirect. Ensure you reference the performReset function
and the clearInstallLock/clearInstallId/cleanupInstallFlow/window.location.href
calls when making the change.
---
Nitpick comments:
In `@src/Appwrite/Platform/Tasks/Install.php`:
- Around line 1069-1120: Add comprehensive PHPDoc blocks to both
countComposeServices() and execWithContainerProgress(): for
countComposeServices(string $composeFile): int, document that it reads the
compose file path, returns 0 on unreadable file or on preg_match_all failure,
and that it counts lines matching the /^\s*container_name:/m pattern; for
execWithContainerProgress(string $commandLine, int $totalServices, callable
$progress, bool $isUpgrade): array, document the meaning and types of parameters
(commandLine run via proc_open, totalServices used to cap progress, progress
callable signature and expected constants
InstallerServer::STEP_DOCKER_CONTAINERS and STATUS_IN_PROGRESS), describe
returned array keys ('output' => array of stdout lines, 'exit' => process exit
code), note that proc_open is used with stderr redirected, that progress
exceptions are swallowed, and reference the timeout behavior via
procCloseWithTimeout and self::PROC_CLOSE_TIMEOUT_SECONDS.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: dec9bbec-736e-49fd-8836-746f804eb8aa
📒 Files selected for processing (3)
app/views/install/installer/css/styles.cssapp/views/install/installer/js/modules/progress.jssrc/Appwrite/Platform/Tasks/Install.php
🚧 Files skipped from review as they are similar to previous changes (1)
- app/views/install/installer/css/styles.css
This comment has been minimized.
This comment has been minimized.
- Restore postgresql in compose.phtml allowedDbServices for consistency with WhiteList validators, JS defaults, and compose template sections - Log errors in performReset catch block instead of swallowing silently - Move $currentStep assignment before waitForApiReady so timeout errors are attributed to the correct step - Replace blocking fgets loop in execWithContainerProgress with non-blocking stream_select polling to prevent unbounded hangs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This reverts commit a659038.
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
Checklist