Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions system/Commands/Housekeeping/ClearLogs.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected function interact(array &$arguments, array &$options): void
return;
}

if (CLI::prompt('Are you sure you want to delete the logs?', ['n', 'y']) === 'n') {
if (CLI::prompt(sprintf('Delete all log files in %s?', clean_path(WRITEPATH . 'logs')), ['n', 'y']) === 'n') {
return;
}

Expand All @@ -49,24 +49,26 @@ protected function interact(array &$arguments, array &$options): void
protected function execute(array $arguments, array $options): int
{
if ($options['force'] === false) {
CLI::error('Deleting logs aborted.');
if ($this->isInteractive()) {
CLI::write('Log deletion cancelled.', 'yellow');

if (! $this->isInteractive()) {
CLI::error('If you want, use the "--force" option to force delete all log files.');
return EXIT_SUCCESS;
}

CLI::error('Log deletion aborted: pass --force to delete log files in non-interactive mode.');

return EXIT_ERROR;
}

helper('filesystem');

if (! delete_files(WRITEPATH . 'logs', htdocs: true)) {
CLI::error('Error in deleting the logs files.');
CLI::error(sprintf('Failed to delete log files in %s.', clean_path(WRITEPATH . 'logs')));

return EXIT_ERROR;
}

CLI::write('Logs cleared.', 'green');
CLI::write(sprintf('Log files in %s cleared.', clean_path(WRITEPATH . 'logs')), 'green');

return EXIT_SUCCESS;
}
Expand Down
59 changes: 35 additions & 24 deletions tests/system/Commands/Housekeeping/ClearLogsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,13 @@ public function testClearLogsUsingForce(): void

$this->assertFileDoesNotExist(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . 'index.html');
$this->assertSame("\nLogs cleared.\n", preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()));
$this->assertSame(
sprintf("\nLog files in %s cleared.\n", clean_path(WRITEPATH . 'logs')),
preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()),
);
}

public function testClearLogsAbortsClearWithoutForce(): void
public function testClearLogsCancelsWithoutForce(): void
{
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");

Expand All @@ -96,16 +99,19 @@ public function testClearLogsAbortsClearWithoutForce(): void

$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
$this->assertSame(
<<<'EOT'
Are you sure you want to delete the logs? [n, y]: n
Deleting logs aborted.

EOT,
sprintf(
<<<'EOT'
Delete all log files in %s? [n, y]: n
Log deletion cancelled.

EOT,
clean_path(WRITEPATH . 'logs'),
),
preg_replace('/\e\[[^m]+m/', '', $io->getOutput()),
);
}

public function testClearLogsAbortsClearWithoutForceWithDefaultAnswer(): void
public function testClearLogsCancelsWithoutForceWithDefaultAnswer(): void
{
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");

Expand All @@ -119,17 +125,20 @@ public function testClearLogsAbortsClearWithoutForceWithDefaultAnswer(): void

$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
$this->assertSame(
<<<EOT
Are you sure you want to delete the logs? [n, y]:{$space}
Deleting logs aborted.

EOT,
sprintf(
<<<EOT
Delete all log files in %s? [n, y]:{$space}
Log deletion cancelled.

EOT,
clean_path(WRITEPATH . 'logs'),
),
preg_replace('/\e\[[^m]+m/', '', $io->getOutput()),
);
}

#[DataProvider('provideClearLogsAbortsNonInteractivelyAndHintsAboutForceFlag')]
public function testClearLogsAbortsNonInteractivelyAndHintsAboutForceFlag(string $flag): void
#[DataProvider('provideClearLogsAbortsNonInteractively')]
public function testClearLogsAbortsNonInteractively(string $flag): void
{
$this->assertFileExists(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");

Expand All @@ -139,8 +148,7 @@ public function testClearLogsAbortsNonInteractivelyAndHintsAboutForceFlag(string
$this->assertSame(
<<<'EOT'

Deleting logs aborted.
If you want, use the "--force" option to force delete all log files.
Log deletion aborted: pass --force to delete log files in non-interactive mode.

EOT,
preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()),
Expand All @@ -150,7 +158,7 @@ public function testClearLogsAbortsNonInteractivelyAndHintsAboutForceFlag(string
/**
* @return iterable<string, array{string}>
*/
public static function provideClearLogsAbortsNonInteractivelyAndHintsAboutForceFlag(): iterable
public static function provideClearLogsAbortsNonInteractively(): iterable
{
yield 'long form' => ['--no-interaction'];

Expand All @@ -169,11 +177,14 @@ public function testClearLogsWithoutForceButWithConfirmation(): void

$this->assertFileDoesNotExist(WRITEPATH . 'logs' . DIRECTORY_SEPARATOR . "log-{$this->date}.log");
$this->assertSame(
<<<'EOT'
Are you sure you want to delete the logs? [n, y]: y
Logs cleared.

EOT,
sprintf(
<<<'EOT'
Delete all log files in %1$s? [n, y]: y
Log files in %1$s cleared.

EOT,
clean_path(WRITEPATH . 'logs'),
),
preg_replace('/\e\[[^m]+m/', '', $io->getOutput()),
);
}
Expand All @@ -194,7 +205,7 @@ public function testClearLogsFailsOnChmodFailure(): void

$this->assertFileExists($path);
$this->assertSame(
"\nError in deleting the logs files.\n",
sprintf("\nFailed to delete log files in %s.\n", clean_path(WRITEPATH . 'logs')),
preg_replace('/\e\[[^m]+m/', '', $this->getStreamFilterBuffer()),
);
}
Expand Down
1 change: 1 addition & 0 deletions user_guide_src/source/changelogs/v4.8.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Behavior Changes
- **Commands:** The ``filter:check`` command now requires the HTTP method argument to be uppercase (e.g., ``spark filter:check GET /`` instead of ``spark filter:check get /``).
- **Commands:** Several built-in commands have been migrated from ``BaseCommand`` to the modern ``AbstractCommand`` style. Applications that extend a built-in command to override
behaviour may need to re-implement against the modern API (``configure()`` + ``execute()`` and the ``#[Command]`` attribute) once the class it extends is migrated, or, preferably, compose instead of extending. Invocations on the command line are unaffected.
- **Commands:** The ``logs:clear`` command now returns ``EXIT_SUCCESS`` (previously ``EXIT_ERROR``) when the user declines the interactive confirmation prompt, since user-initiated cancellation is not a failure. Output messages have also been reworded to distinguish cancellation (interactive ``n``) from abort (non-interactive without ``--force``), and the resolved log directory path is now included in the prompt, success, and failure messages.
- **Database:** The Postgre driver's ``$db->error()['code']`` previously always returned ``''``. It now returns the 5-character SQLSTATE string for query and transaction failures (e.g., ``'42P01'``), or ``'08006'`` for connection-level failures. Code that relied on ``$db->error()['code'] === ''`` will need updating.
- **Filters:** HTTP method matching for method-based filters is now case-sensitive. The keys in ``Config\Filters::$methods`` must exactly match the request method
(e.g., ``GET``, ``POST``). Lowercase method names (e.g., ``post``) will no longer match.
Expand Down
Loading