Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[11.x] Prevent destructive commands from running #51376

Merged
merged 9 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/Illuminate/Console/Preventable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Illuminate\Console;

trait Preventable
{
protected static $preventFromRunning = false;

/**
* Whether to prevent command from running.
*
* @param bool $prevent
* @return bool
*/
public static function preventFromRunning($prevent = true)
{
static::$preventFromRunning = $prevent;
}

/**
* Determine if the command is prevented from
* running and display a warning if so.
*
* @return bool
*/
protected function preventedFromRunning()
{
if (! static::$preventFromRunning) {
return false;
}

$this->components->warn('Command has been prevented from running in this environment.');

return true;
}
}
7 changes: 6 additions & 1 deletion src/Illuminate/Database/Console/Migrations/FreshCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
use Illuminate\Console\Preventable;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Events\DatabaseRefreshed;
use Illuminate\Database\Migrations\Migrator;
Expand All @@ -13,7 +14,7 @@
#[AsCommand(name: 'migrate:fresh')]
class FreshCommand extends Command
{
use ConfirmableTrait;
use ConfirmableTrait, Preventable;

/**
* The console command name.
Expand Down Expand Up @@ -56,6 +57,10 @@ public function __construct(Migrator $migrator)
*/
public function handle()
{
if ($this->preventedFromRunning()) {
return 1;
}

if (! $this->confirmToProceed()) {
return 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
use Illuminate\Console\Preventable;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Events\DatabaseRefreshed;
use Symfony\Component\Console\Attribute\AsCommand;
Expand All @@ -12,7 +13,7 @@
#[AsCommand(name: 'migrate:refresh')]
class RefreshCommand extends Command
{
use ConfirmableTrait;
use ConfirmableTrait, Preventable;

/**
* The console command name.
Expand All @@ -35,6 +36,10 @@ class RefreshCommand extends Command
*/
public function handle()
{
if ($this->preventedFromRunning()) {
return 1;
}

if (! $this->confirmToProceed()) {
return 1;
}
Expand Down
7 changes: 6 additions & 1 deletion src/Illuminate/Database/Console/Migrations/ResetCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
namespace Illuminate\Database\Console\Migrations;

use Illuminate\Console\ConfirmableTrait;
use Illuminate\Console\Preventable;
use Illuminate\Database\Migrations\Migrator;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;

#[AsCommand(name: 'migrate:reset')]
class ResetCommand extends BaseCommand
{
use ConfirmableTrait;
use ConfirmableTrait, Preventable;

/**
* The console command name.
Expand Down Expand Up @@ -53,6 +54,10 @@ public function __construct(Migrator $migrator)
*/
public function handle()
{
if ($this->preventedFromRunning()) {
return 1;
}

if (! $this->confirmToProceed()) {
return 1;
}
Expand Down
7 changes: 6 additions & 1 deletion src/Illuminate/Database/Console/WipeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
use Illuminate\Console\Preventable;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;

#[AsCommand(name: 'db:wipe')]
class WipeCommand extends Command
{
use ConfirmableTrait;
use ConfirmableTrait, Preventable;

/**
* The console command name.
Expand All @@ -33,6 +34,10 @@ class WipeCommand extends Command
*/
public function handle()
{
if ($this->preventedFromRunning()) {
return 1;
}

if (! $this->confirmToProceed()) {
return 1;
}
Expand Down
22 changes: 22 additions & 0 deletions tests/Database/DatabaseMigrationRefreshCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class DatabaseMigrationRefreshCommandTest extends TestCase
{
protected function tearDown(): void
{
RefreshCommand::preventFromRunning(false);
m::close();
}

Expand Down Expand Up @@ -72,6 +73,27 @@ public function testRefreshCommandCallsCommandsWithStep()
$this->runCommand($command, ['--step' => 2]);
}

public function testRefreshCommandExitsWhenPrevented()
{
$command = new RefreshCommand;

$app = new ApplicationDatabaseRefreshStub(['path.database' => __DIR__]);
$dispatcher = $app->instance(Dispatcher::class, $events = m::mock());
$console = m::mock(ConsoleApplication::class)->makePartial();
$console->__construct();
$command->setLaravel($app);
$command->setApplication($console);

RefreshCommand::preventFromRunning();

$code = $this->runCommand($command);

$this->assertSame(1, $code);

$console->shouldNotHaveBeenCalled();
$dispatcher->shouldNotReceive('dispatch');
}

protected function runCommand($command, $input = [])
{
return $command->run(new ArrayInput($input), new NullOutput);
Expand Down
18 changes: 18 additions & 0 deletions tests/Database/DatabaseMigrationResetCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class DatabaseMigrationResetCommandTest extends TestCase
{
protected function tearDown(): void
{
ResetCommand::preventFromRunning(false);
m::close();
}

Expand Down Expand Up @@ -52,6 +53,23 @@ public function testResetCommandCanBePretended()
$this->runCommand($command, ['--pretend' => true, '--database' => 'foo']);
}

public function testRefreshCommandExitsWhenPrevented()
{
$command = new ResetCommand($migrator = m::mock(Migrator::class));

$app = new ApplicationDatabaseResetStub(['path.database' => __DIR__]);
$app->useDatabasePath(__DIR__);
$command->setLaravel($app);

ResetCommand::preventFromRunning();

$code = $this->runCommand($command);

$this->assertSame(1, $code);

$migrator->shouldNotHaveBeenCalled();
}

protected function runCommand($command, $input = [])
{
return $command->run(new ArrayInput($input), new NullOutput);
Expand Down