feat: add Scheduler installation command (#1802)

This commit is contained in:
Phan An 2024-07-22 22:42:58 +02:00 committed by GitHub
parent e95ff45789
commit 7ae57e3d98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 145 additions and 26 deletions

View file

@ -7,15 +7,15 @@ use App\Exceptions\InstallationFailedException;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Contracts\Hashing\Hasher as Hash;
use Illuminate\Database\DatabaseManager as DB;
use Illuminate\Encryption\Encrypter; use Illuminate\Encryption\Encrypter;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Process;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Jackiedo\DotenvEditor\DotenvEditor; use Jackiedo\DotenvEditor\DotenvEditor;
use Psr\Log\LoggerInterface;
use Throwable; use Throwable;
class InitCommand extends Command class InitCommand extends Command
@ -32,12 +32,8 @@ class InitCommand extends Command
private bool $adminSeeded = false; private bool $adminSeeded = false;
public function __construct( public function __construct(private readonly DotenvEditor $dotenvEditor)
private readonly Hash $hash, {
private readonly DotenvEditor $dotenvEditor,
private readonly DB $db,
private readonly LoggerInterface $logger
) {
parent::__construct(); parent::__construct();
} }
@ -64,8 +60,9 @@ class InitCommand extends Command
$this->maybeSetMediaPath(); $this->maybeSetMediaPath();
$this->maybeCompileFrontEndAssets(); $this->maybeCompileFrontEndAssets();
$this->dotenvEditor->save(); $this->dotenvEditor->save();
$this->tryInstallingScheduler();
} catch (Throwable $e) { } catch (Throwable $e) {
$this->logger->error($e); Log::error($e);
$this->components->error("Oops! Koel installation or upgrade didn't finish successfully."); $this->components->error("Oops! Koel installation or upgrade didn't finish successfully.");
$this->components->error('Please check the error log at storage/logs/laravel.log and try again.'); $this->components->error('Please check the error log at storage/logs/laravel.log and try again.');
@ -92,7 +89,7 @@ class InitCommand extends Command
$this->info('Again, visit 📙 ' . config('koel.misc.docs_url') . ' for more tips and tweaks.'); $this->info('Again, visit 📙 ' . config('koel.misc.docs_url') . ' for more tips and tweaks.');
$this->info( $this->info(
"Feeling generous and want to support Koel's development? Check out " "Feeling generous and want to support Koels development? Check out "
. config('koel.misc.sponsor_github_url') . config('koel.misc.sponsor_github_url')
. ' 🤗' . ' 🤗'
); );
@ -202,7 +199,7 @@ class InitCommand extends Command
User::query()->create([ User::query()->create([
'name' => self::DEFAULT_ADMIN_NAME, 'name' => self::DEFAULT_ADMIN_NAME,
'email' => self::DEFAULT_ADMIN_EMAIL, 'email' => self::DEFAULT_ADMIN_EMAIL,
'password' => $this->hash->make(self::DEFAULT_ADMIN_PASSWORD), 'password' => Hash::make(self::DEFAULT_ADMIN_PASSWORD),
'is_admin' => true, 'is_admin' => true,
]); ]);
@ -241,12 +238,12 @@ class InitCommand extends Command
try { try {
// Make sure the config cache is cleared before another attempt. // Make sure the config cache is cleared before another attempt.
Artisan::call('config:clear', ['--quiet' => true]); Artisan::call('config:clear', ['--quiet' => true]);
$this->db->reconnect(); DB::reconnect();
$this->db->getDoctrineSchemaManager()->listTables(); DB::getDoctrineSchemaManager()->listTables();
break; break;
} catch (Throwable $e) { } catch (Throwable $e) {
$this->logger->error($e); Log::error($e);
// We only try to update credentials if running in interactive mode. // We only try to update credentials if running in interactive mode.
// Otherwise, we require admin intervention to fix them. // Otherwise, we require admin intervention to fix them.
@ -347,11 +344,26 @@ class InitCommand extends Command
return File::isDirectory($path) && File::isReadable($path); return File::isDirectory($path) && File::isReadable($path);
} }
/**
* Generate a random key for the application.
*/
private function generateRandomKey(): string private function generateRandomKey(): string
{ {
return 'base64:' . base64_encode(Encrypter::generateKey($this->laravel['config']['app.cipher'])); return 'base64:' . base64_encode(Encrypter::generateKey($this->laravel['config']['app.cipher']));
} }
private function tryInstallingScheduler(): void
{
if (PHP_OS_FAMILY === 'Windows' || PHP_OS_FAMILY === 'Unknown') {
return;
}
$this->components->info('Trying to install Koel scheduler…');
if (Artisan::call('koel:scheduler:install') !== self::SUCCESS) {
$this->components->warn(
'Failed to install scheduler. ' .
'Please install manually: https://docs.koel.dev/cli-commands#command-scheduling'
);
} else {
$this->components->info('Koel scheduler installed successfully.');
}
}
} }

View file

@ -0,0 +1,49 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use TiBeN\CrontabManager\CrontabAdapter;
use TiBeN\CrontabManager\CrontabJob;
use TiBeN\CrontabManager\CrontabRepository;
class InstallSchedulerCommand extends Command
{
protected $signature = 'koel:scheduler:install';
protected $description = 'Install the scheduler for Koel';
public function handle(): int
{
if (PHP_OS_FAMILY === 'Windows' || PHP_OS_FAMILY === 'Unknown') {
$this->components->error('This command is only available on Linux systems.');
return self::FAILURE;
}
$crontab = new CrontabRepository(new CrontabAdapter());
$this->components->info('Trying to install Koel scheduler…');
if (self::schedulerInstalled($crontab)) {
$this->components->info('Koel scheduler is already installed. Skipping…');
return self::SUCCESS;
}
$job = CrontabJob::createFromCrontabLine(
'* * * * * cd ' . base_path() . ' && php artisan schedule:run >> /dev/null 2>&1'
);
$crontab->addJob($job);
$crontab->persist();
$this->components->info('Koel scheduler installed successfully.');
return self::SUCCESS;
}
private static function schedulerInstalled(CrontabRepository $crontab): bool
{
return (bool) $crontab->findJobByRegex('/artisan schedule:run/');
}
}

View file

@ -44,7 +44,8 @@
"league/flysystem-sftp-v3": "^3.0", "league/flysystem-sftp-v3": "^3.0",
"saloonphp/xml-wrangler": "^1.2", "saloonphp/xml-wrangler": "^1.2",
"phanan/poddle": "^1.0", "phanan/poddle": "^1.0",
"spatie/laravel-ray": "^1.36" "spatie/laravel-ray": "^1.36",
"tiben/crontab-manager": "*"
}, },
"require-dev": { "require-dev": {
"mockery/mockery": "~1.0", "mockery/mockery": "~1.0",

49
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "306146627425d3042b82f1cd347237c8", "content-hash": "9da46e14c758c109ce78f30cbaa63a09",
"packages": [ "packages": [
{ {
"name": "algolia/algoliasearch-client-php", "name": "algolia/algoliasearch-client-php",
@ -9590,6 +9590,53 @@
], ],
"time": "2023-07-17T10:34:14+00:00" "time": "2023-07-17T10:34:14+00:00"
}, },
{
"name": "tiben/crontab-manager",
"version": "v1.4.0",
"source": {
"type": "git",
"url": "https://github.com/TiBeN/CrontabManager.git",
"reference": "79ade7bfc895c4594905a2554d7a82f6567aabf3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/TiBeN/CrontabManager/zipball/79ade7bfc895c4594905a2554d7a82f6567aabf3",
"reference": "79ade7bfc895c4594905a2554d7a82f6567aabf3",
"shasum": ""
},
"require": {
"php": ">= 5.3"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36|^5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"TiBeN\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Benjamin Legendre",
"email": "legendre.benjamin@gmail.com"
}
],
"description": "A library for managing linux cron jobs.",
"keywords": [
"crontab",
"scheduling"
],
"support": {
"issues": "https://github.com/TiBeN/CrontabManager/issues",
"source": "https://github.com/TiBeN/CrontabManager/tree/v1.4.0"
},
"time": "2023-05-02T16:54:54+00:00"
},
{ {
"name": "tijsverkoyen/css-to-inline-styles", "name": "tijsverkoyen/css-to-inline-styles",
"version": "v2.2.7", "version": "v2.2.7",

View file

@ -143,9 +143,13 @@ php artisan koel:sync [options] # Alias, deprecated
| `I`, `--ignore=` | The comma-separated tags to ignore (exclude) from scanning. Valid tags are `title`, `album`,`artist`, `albumartist`, `track`, `disc`, `year`, `genre`, `lyrics`, and `cover`. | | `I`, `--ignore=` | The comma-separated tags to ignore (exclude) from scanning. Valid tags are `title`, `album`,`artist`, `albumartist`, `track`, `disc`, `year`, `genre`, `lyrics`, and `cover`. |
| `F`, `--force` | Force re-scanning even unchanged files. | | `F`, `--force` | Force re-scanning even unchanged files. |
### `koel:scheduler:install`
Install the command scheduler. Refer to [Command Scheduling](#command-scheduling) for more information.
### `koel:search:import` ### `koel:search:import`
Import all searchable entities with Scout. See [Instant Search](./usage/search) for more information. Import all searchable entities with Scout. Refer to [Instant Search](./usage/search) for more information.
#### Usage #### Usage
@ -210,16 +214,22 @@ php artisan koel:tags:collect
## Command Scheduling ## Command Scheduling
Some of the commands, such as `koel:scan` and `koel:prune`, can be scheduled to run at regular intervals. Some of the commands, such as `koel:scan` and `koel:prune`, can be scheduled to run at regular intervals.
Koel uses Laravels built-in scheduler to manage this. Instead of setting up individual cron jobs, you can use Koels built-in scheduler to automatically handle the commands for you.
In order to set up the scheduler, you need to add the following cron entry into the crontab of the webserver user (for example, To set up the scheduler, run the `koel:scheduler:install` command as the web server user (e.g. `www-data` or `nginx`):
if it's `www-data`, run `sudo crontab -u www-data -e`):
```bash
php artisan koel:scheduler:install
```
Alternatively, you can manually add the following cron entry into the crontab of the webserver user (for example, if it's `www-data`, run `sudo crontab -u www-data -e`):
```bash ```bash
* * * * * cd /path-to-koel-installation && php artisan schedule:run >> /dev/null 2>&1 * * * * * cd /path-to-koel-installation && php artisan schedule:run >> /dev/null 2>&1
``` ```
This will run the scheduler every minute, which will then run any scheduled commands as needed. Either way, the scheduler will run every minute once installed, executing any scheduled commands as needed.
By default, `koel:scan`, `koel:prune`, and `koel:podcasts:sync` are set to run every day at midnight. By default, `koel:scan`, `koel:prune`, and `koel:podcasts:sync` are set to run every day at midnight.
Though you can still manually set up cron jobs for individual commands, the scheduler is the recommended approach to command scheduling in Koel, as it will automatically cover any commands that may be added in the future. Though you can still manually set up cron jobs for individual commands, the scheduler is the recommended approach to do command scheduling in Koel,
as it will automatically cover any commands that may be added in the future.