c0ntax/deployment-tasks-bundle
Install the Bundle
composer require c0ntax/deployment-tasks-bundle
Configure Gaufrette Storage
Add the required knp_gaufrette.yaml configuration (as per the README) to store task execution state. Ensure the var/memory directory exists:
mkdir -p var/memory
First Use Case: Run a One-Time Task
Define a task in a service (e.g., src/Command/DeployTaskCommand.php):
use c0ntax\DeploymentTasksBundle\Task\TaskInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DeployTaskCommand extends Command implements TaskInterface
{
public function isTask(): bool
{
return true; // Marks this as a task
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Your one-time logic (e.g., database migrations, cache clearing)
$output->writeln('Running one-time task...');
return Command::SUCCESS;
}
}
Register the command in services.yaml:
services:
App\Command\DeployTaskCommand:
tags: ['console.command']
Run the task via CLI:
php bin/console app:deploy-task
The task will execute only once (subsequent runs are skipped).
Deployment Hooks Use the bundle in CI/CD pipelines (e.g., GitHub Actions, GitLab CI) to run tasks once per deployment:
# .github/workflows/deploy.yml
jobs:
deploy:
steps:
- run: php bin/console app:deploy-task
Task Grouping
Group related tasks (e.g., PreDeployTask, PostDeployTask) by prefixing command names or using tags:
# config/services.yaml
services:
App\Command\PreDeployTask:
tags: ['console.command', 'deploy.pre']
Conditional Execution
Extend TaskInterface to add logic for environment-specific tasks:
public function isTask(): bool
{
return $this->getEnvironment() === 'production';
}
Artisan Command Wrapper Create a Laravel Artisan command to proxy Symfony commands:
// app/Console/Commands/RunDeploymentTasks.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
class RunDeploymentTasks extends Command
{
protected $signature = 'deploy:tasks';
protected $description = 'Run one-time deployment tasks';
public function handle()
{
$process = new Process(['php', 'bin/console', 'app:deploy-task']);
$process->run();
$this->output->writeln($process->getOutput());
}
}
Service Provider Integration
Register the bundle in config/app.php (Symfony bundles require manual registration in Laravel):
'providers' => [
// ...
c0ntax\DeploymentTasksBundle\c0ntaxDeploymentTasksBundle::class,
],
Task Storage Customization Override the default Gaufrette adapter in Laravel’s config:
// config/filesystems.php
'disks' => [
'tasks' => [
'driver' => 'local',
'root' => storage_path('framework/tasks'),
],
],
Gaufrette Dependency
knp-gaufrette-bundle, which may conflict with Laravel’s filesystem abstractions.Storage facade to mock the local adapter if needed:
// src/Service/TaskStorageAdapter.php
use League\Flysystem\Local\LocalFilesystemAdapter;
use Symfony\Component\DependencyInjection\ContainerInterface;
class TaskStorageAdapter
{
public function __construct(private ContainerInterface $container)
{
}
public function getAdapter(): LocalFilesystemAdapter
{
return new LocalFilesystemAdapter(
$this->container->getParameter('kernel.project_dir') . '/storage/framework/tasks'
);
}
}
Task Idempotency
isTask() logic is flawed (e.g., always returns true).public function isTask(): bool
{
$this->logger->debug('Checking task execution status...');
return $this->taskManager->wasExecuted($this->getTaskId());
}
Symfony vs. Laravel CLI
php bin/console c0ntax_deployment_tasks:run App\Command\DeployTaskCommand
Check Task Execution State
Inspect the var/memory directory for JSON files tracking executed tasks:
cat var/memory/*.json
Clear Task State (Dev Only)
Delete the var/memory directory to reset all tasks:
rm -rf var/memory/*
Log Task Execution Enable Symfony’s debug mode to log task lifecycle:
# config/packages/dev/debug.yaml
framework:
profiler: { only_exceptions: false }
Custom Task Storage
Implement a custom TaskStorageInterface to use Laravel’s cache or database:
// src/Service/DatabaseTaskStorage.php
use c0ntax\DeploymentTasksBundle\Task\TaskStorageInterface;
class DatabaseTaskStorage implements TaskStorageInterface
{
public function wasExecuted(string $taskId): bool
{
return \DB::table('deployed_tasks')->where('task_id', $taskId)->exists();
}
public function markExecuted(string $taskId): void
{
\DB::table('deployed_tasks')->insert(['task_id' => $taskId]);
}
}
Event-Driven Tasks
Trigger tasks via Laravel events (e.g., Deployed event):
// app/Providers/EventServiceProvider.php
use c0ntax\DeploymentTasksBundle\Task\TaskManagerInterface;
class EventServiceProvider extends ServiceProvider
{
public function boot(TaskManagerInterface $taskManager)
{
event('deployed', function () use ($taskManager) {
$taskManager->run('App\Command\PostDeployTask');
});
}
}
Parallel Task Execution
Use Symfony’s Process component to run tasks concurrently (advanced):
// src/Command/ParallelDeployCommand.php
use Symfony\Component\Process\Process;
class ParallelDeployCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$processes = [
new Process(['php', 'bin/console', 'app:task1']),
new Process(['php', 'bin/console', 'app:task2']),
];
foreach ($processes as $process) {
$process->start();
}
foreach ($processes as $process) {
$process->wait();
$output->writeln($process->getOutput());
}
return Command::SUCCESS;
}
}
How can I help you explore Laravel packages today?