From 48f0ff86ba1f792d6f631ece7ae6cadb59141eb3 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 24 Jul 2023 15:21:34 -0700 Subject: [PATCH 1/6] Make the install path a class property This makes it so the property can be used by subclasses. --- src/Appwrite/Platform/Tasks/Install.php | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index 1d384715cb..5b7042e14d 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -16,6 +16,8 @@ use Utopia\Platform\Action; class Install extends Action { + protected string $path = '/usr/src/code/appwrite'; + public static function getName(): string { return 'install'; @@ -36,7 +38,6 @@ class Install extends Action public function action(string $httpPort, string $httpsPort, string $organization, string $image, string $interactive): void { $config = Config::getParam('variables'); - $path = '/usr/src/code/appwrite'; $defaultHTTPPort = '80'; $defaultHTTPSPort = '443'; $vars = []; @@ -56,19 +57,19 @@ class Install extends Action Console::success('Starting Appwrite installation...'); // Create directory with write permissions - if (!\file_exists(\dirname($path))) { - if (!@\mkdir(\dirname($path), 0755, true)) { - Console::error('Can\'t create directory ' . \dirname($path)); + if (!\file_exists(\dirname($this->path))) { + if (!@\mkdir(\dirname($this->path), 0755, true)) { + Console::error('Can\'t create directory ' . \dirname($this->path)); Console::exit(1); } } - $data = @file_get_contents($path . '/docker-compose.yml'); + $data = @file_get_contents($this->path . '/docker-compose.yml'); if ($data !== false) { $time = \time(); Console::info('Compose file found, creating backup: docker-compose.yml.' . $time . '.backup'); - file_put_contents($path . '/docker-compose.yml.' . $time . '.backup', $data); + file_put_contents($this->path . '/docker-compose.yml.' . $time . '.backup', $data); $compose = new Compose($data); $appwrite = $compose->getService('appwrite'); $oldVersion = ($appwrite) ? $appwrite->getImageVersion() : null; @@ -102,11 +103,11 @@ class Install extends Action } } - $data = @file_get_contents($path . '/.env'); + $data = @file_get_contents($this->path . '/.env'); if ($data !== false) { // Fetch all env vars from previous .env file Console::info('Env file found, creating backup: .env.' . $time . '.backup'); - file_put_contents($path . '/.env.' . $time . '.backup', $data); + file_put_contents($this->path . '/.env.' . $time . '.backup', $data); $env = new Env($data); foreach ($env->list() as $key => $value) { @@ -196,14 +197,14 @@ class Install extends Action $templateForEnv->setParam('vars', $input); - if (!file_put_contents($path . '/docker-compose.yml', $templateForCompose->render(false))) { + if (!file_put_contents($this->path . '/docker-compose.yml', $templateForCompose->render(false))) { $message = 'Failed to save Docker Compose file'; $this->sendEvent($analytics, $message); Console::error($message); Console::exit(1); } - if (!file_put_contents($path . '/.env', $templateForEnv->render(false))) { + if (!file_put_contents($this->path . '/.env', $templateForEnv->render(false))) { $message = 'Failed to save environment variables file'; $this->sendEvent($analytics, $message); Console::error($message); @@ -220,9 +221,9 @@ class Install extends Action } } - Console::log("Running \"docker compose up -d --remove-orphans --renew-anon-volumes\""); + Console::log("Running \"docker compose -f $this->path/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\""); - $exit = Console::execute("${env} docker compose --project-directory ${path} up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr); + $exit = Console::execute("$env docker compose -f $this->path/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr); if ($exit !== 0) { $message = 'Failed to install Appwrite dockers'; From 75f7459c40721a94b78a3b05e9c3d1df45445100 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 24 Jul 2023 15:34:04 -0700 Subject: [PATCH 2/6] Create a dedicated upgrade task Before, we used the same command for both installation and upgrades. This lead to problems because developers would try to upgrade in the wrong folder and end up creating a new installation. This new upgrade command validates the existence of an existing installation before proceeding with the upgrade to ensure no new installation is created when upgrading. --- bin/upgrade | 3 ++ src/Appwrite/Platform/Services/Tasks.php | 2 ++ src/Appwrite/Platform/Tasks/Upgrade.php | 42 ++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100755 bin/upgrade create mode 100644 src/Appwrite/Platform/Tasks/Upgrade.php diff --git a/bin/upgrade b/bin/upgrade new file mode 100755 index 0000000000..ce32b9ca30 --- /dev/null +++ b/bin/upgrade @@ -0,0 +1,3 @@ +#!/bin/sh + +php /usr/src/code/app/cli.php upgrade $@ \ No newline at end of file diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index 10c573da42..bc8d1bbc72 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -22,6 +22,7 @@ use Appwrite\Platform\Tasks\VolumeSync; use Appwrite\Platform\Tasks\CalcUsersStats; use Appwrite\Platform\Tasks\CalcTierStats; use Appwrite\Platform\Tasks\PatchDeleteProjectCollections; +use Appwrite\Platform\Tasks\Upgrade; class Tasks extends Service { @@ -36,6 +37,7 @@ class Tasks extends Service ->addAction(Hamster::getName(), new Hamster()) ->addAction(Doctor::getName(), new Doctor()) ->addAction(Install::getName(), new Install()) + ->addAction(Upgrade::getName(), new Upgrade()) ->addAction(Maintenance::getName(), new Maintenance()) ->addAction(PatchCreateMissingSchedules::getName(), new PatchCreateMissingSchedules()) ->addAction(ClearCardCache::getName(), new ClearCardCache()) diff --git a/src/Appwrite/Platform/Tasks/Upgrade.php b/src/Appwrite/Platform/Tasks/Upgrade.php new file mode 100644 index 0000000000..e3f0458394 --- /dev/null +++ b/src/Appwrite/Platform/Tasks/Upgrade.php @@ -0,0 +1,42 @@ +desc('Upgrade Appwrite') + ->param('httpPort', '', new Text(4), 'Server HTTP port', true) + ->param('httpsPort', '', new Text(4), 'Server HTTPS port', true) + ->param('organization', 'appwrite', new Text(0), 'Docker Registry organization', true) + ->param('image', 'appwrite', new Text(0), 'Main appwrite docker image', true) + ->param('interactive', 'Y', new Text(1), 'Run an interactive session', true) + ->callback(fn ($httpPort, $httpsPort, $organization, $image, $interactive) => $this->action($httpPort, $httpsPort, $organization, $image, $interactive)); + } + + public function action(string $httpPort, string $httpsPort, string $organization, string $image, string $interactive): void + { + // Check for previous installation + $data = @file_get_contents($this->path . '/docker-compose.yml'); + if (empty($data)) { + Console::error('Appwrite installation not found.'); + Console::log('The command was not run in the parent folder of your appwrite installation.'); + Console::log('Please navigate to the parent directory of the Appwrite installation and try again.'); + Console::log(' parent_directory <= you run the command in this directory'); + Console::log(' └── appwrite'); + Console::log(' └── docker-compose.yml'); + Console::exit(1); + } + parent::action($httpPort, $httpsPort, $organization, $image, $interactive); + } +} From 7d56c6f37e94eb291999334cbe39409b8cfe3d8f Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Tue, 25 Jul 2023 13:50:43 -0700 Subject: [PATCH 3/6] Prompt developer to confirm installing over existing install --- src/Appwrite/Platform/Tasks/Install.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index 5b7042e14d..4fe42ab732 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -67,6 +67,15 @@ class Install extends Action $data = @file_get_contents($this->path . '/docker-compose.yml'); if ($data !== false) { + if ($interactive == 'Y' && Console::isInteractive()) { + $answer = Console::confirm('Previous installation found, do you want to overwrite it? (Y/n)'); + + if ($answer !== 'Y') { + Console::info('No action taken.'); + return; + } + } + $time = \time(); Console::info('Compose file found, creating backup: docker-compose.yml.' . $time . '.backup'); file_put_contents($this->path . '/docker-compose.yml.' . $time . '.backup', $data); From 2f2edd41d593291cfeb3727177bf0917748eefdb Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 30 Aug 2023 10:17:37 -0700 Subject: [PATCH 4/6] Update install.php not specify docker-compose.yml file By not specifying a docker-compose.yml file, docker compose automatically uses the docker-compose.yml file in the project directory and a docker-compose.override.yml file if one is present. --- src/Appwrite/Platform/Tasks/Install.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index 4fe42ab732..aaf72d5f20 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -230,9 +230,9 @@ class Install extends Action } } - Console::log("Running \"docker compose -f $this->path/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\""); + Console::log("Running \"docker compose up -d --remove-orphans --renew-anon-volumes\""); - $exit = Console::execute("$env docker compose -f $this->path/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr); + $exit = Console::execute("$env docker compose --project-directory $this->path up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr); if ($exit !== 0) { $message = 'Failed to install Appwrite dockers'; From 2d0d0365198a0d2fb12cfbb24500631cd43eb8f2 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 30 Aug 2023 12:31:25 -0700 Subject: [PATCH 5/6] Ensure upgrade executable has execute permissions --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index e9e2c547df..23bd3f188f 100755 --- a/Dockerfile +++ b/Dockerfile @@ -167,6 +167,7 @@ RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/maintenance && \ chmod +x /usr/local/bin/usage && \ chmod +x /usr/local/bin/install && \ + chmod +x /usr/local/bin/upgrade && \ chmod +x /usr/local/bin/migrate && \ chmod +x /usr/local/bin/realtime && \ chmod +x /usr/local/bin/schedule && \ From 917f2f1d741adf962a842c4886d239344251bd7f Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 30 Aug 2023 12:32:10 -0700 Subject: [PATCH 6/6] Update installation confirmation text Let the user know a backup will be created. --- src/Appwrite/Platform/Tasks/Install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index aaf72d5f20..22355d0269 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -68,7 +68,7 @@ class Install extends Action if ($data !== false) { if ($interactive == 'Y' && Console::isInteractive()) { - $answer = Console::confirm('Previous installation found, do you want to overwrite it? (Y/n)'); + $answer = Console::confirm('Previous installation found, do you want to overwrite it (a backup will be created before overwriting)? (Y/n)'); if ($answer !== 'Y') { Console::info('No action taken.');