soviann/deploy-tasks-bundle

One-time deploy task runner for Symfony applications

Maintainers

Package info

github.com/Soviann/deploy-tasks-bundle

Type:symfony-bundle

pkg:composer/soviann/deploy-tasks-bundle

Transparency log

Statistics

Installs: 13

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.4.0 2026-07-20 09:58 UTC

README

CI Coverage Latest Stable Version License

A Symfony bundle for running one-time deploy tasks — data migrations, cache warmups, seed scripts — via CLI. Each task is tracked so it executes exactly once across deployments.

Status: pre-1.0. Public API and configuration may change without a major-version bump until v1.0.0. Breaking changes bump the minor version and are documented in UPGRADE.md.

Requirements

  • PHP >= 8.2 (>= 8.4 for Symfony 8)
  • Symfony 6.4 LTS, 7.x or 8.x

Installation

composer require soviann/deploy-tasks-bundle

With Symfony Flex, the bundle is registered automatically. Without Flex, register it manually in config/bundles.php:

return [
    // ...
    Soviann\DeployTasksBundle\SoviannDeployTasksBundle::class => ['all' => true],
];

Flex recipe

The bundle's Flex recipe lives in symfony/recipes-contrib. When Contrib recipes are enabled, composer require soviann/deploy-tasks-bundle registers the bundle, publishes config/packages/soviann_deploy_tasks.yaml, installs the host runner (bin/deploy-tasks-host.sh), and adds the host-task .gitignore entries automatically. Flex asks once per project before running a Contrib recipe — answer yes, or enable them permanently:

composer config extra.symfony.allow-contrib true

The recipe is optional — without it, the bundle works with its default configuration, and deploytasks:host:install scaffolds the host runner on demand.

Alternative: dedicated recipe endpoint

Prefer not to enable Contrib recipes globally? The same recipe is also served from a dedicated endpoint:

composer config extra.symfony.endpoint --json '["https://api.github.com/repos/Soviann/flex-recipes/contents/index.json", "flex://defaults"]'

Quick Start

Creating a task

use Soviann\DeployTasksBundle\Attribute\AsDeployTask;
use Soviann\DeployTasksBundle\DeployTaskInterface;
use Soviann\DeployTasksBundle\TaskResult;
use Symfony\Component\Console\Output\OutputInterface;

#[AsDeployTask(id: 'task_20260412143000_seed_categories', priority: 10)]
final class SeedCategoriesTask implements DeployTaskInterface
{
    public function getDescription(): string
    {
        return 'Seeds the categories table with initial data.';
    }

    public function run(OutputInterface $output): TaskResult
    {
        // Your task logic here
        $output->writeln('Categories seeded.');

        return TaskResult::SUCCESS;
    }
}

Running tasks

Execute all pending tasks:

bin/console deploytasks:run

Check the status of all tasks:

bin/console deploytasks:status

Configuration

# config/packages/soviann_deploy_tasks.yaml
soviann_deploy_tasks:
    slow_task_threshold: 300     # seconds; warn when a task runs longer (nothing is killed)
    storage:
        type: filesystem         # filesystem | database | custom
        filesystem:
            path: '%kernel.project_dir%/var/deploy-tasks'
    lock:
        enabled: true

Full reference: docs/storage.md and docs/installation.md.

Storage Backends

Filesystem (default): stores execution records as files in var/deploy-tasks/. No additional dependencies required.

Database: stores execution records in a database table. Requires doctrine/dbal.

Custom: plug in any TaskStorageInterface implementation via storage.type: custom. See docs/storage.md.

Task Groups

Tasks can be assigned to one or more groups (e.g. predeploy, postdeploy) to split a deploy into named stages. Without --group, every command operates on every slot — the default (ungrouped) slot and every declared group; --group=<name> (repeatable) narrows to the tasks declaring the listed group(s), and a multi-group task records one row per matching slot.

#[AsDeployTask(id: 'task_...', groups: 'predeploy')]
#[AsDeployTask(id: 'task_...', groups: ['predeploy', 'postdeploy'])]

See docs/creating-tasks.md and docs/commands.md for details.

Host-scope tasks

Host tasks run outside the Symfony container — useful for operations that must execute on the host (Docker restarts, SSH-driven commands, infrastructure prep). The host runner (bin/deploy-tasks-host.sh) is installed automatically by the Flex recipe; without it, one command scaffolds everything:

bin/console deploytasks:host:install

This installs the runner script (executable), creates the configured host-task directory (default deploy/host-tasks/, with a .gitkeep), and adds a Flex-style .gitignore block for the runner's log, lock, and local-override files — each step idempotent. Re-run with --force to refresh the runner after a bundle update. See docs/host-tasks.md for generation, execution, .env cascade, and concurrency details.

Commands

Command Description Options
deploytasks:run Execute pending tasks --dry-run, --rerun-all, --id=<id>, --group=<name> (repeatable), --require-some
deploytasks:status List tasks with their execution state --no-state, --group=<name> (repeatable), --filter-status=<comma-list>
deploytasks:show <id> Show full metadata and every stored execution record for a single task
deploytasks:skip <id> Mark a task as skipped (interactive confirm) --group=<name> (repeatable)
deploytasks:reset <id> Clear the execution record for a task (interactive confirm) --no-interaction, --group=<name> (repeatable), --force
deploytasks:rollup Clear history and mark all tasks as executed --no-interaction, --group=<name> (repeatable), --force
deploytasks:generate Generate a blank deploy task (PHP class, runs inside the Symfony container) --dir, --namespace
deploytasks:create-schema Create the storage schema (storages implementing SchemaManageableInterface) --dump-sql
deploytasks:host:install Install the host runner, the configured host-task directory, and the .gitignore block (idempotent) --force
deploytasks:host:generate Generate a blank deploy task (bash script, runs on the host outside the container) --dir
deploytasks:host:skip <id> Mark a host-scope task as done in the completion log (interactive confirm)
deploytasks:host:reset <id> Remove a host-scope task's completion-log entry --no-interaction, --force
deploytasks:host:rollup Mark every pending host-scope task as done --no-interaction, --force
deploytasks:host:config Render (or write) the host runner env config matching soviann_deploy_tasks.host.* --write

Running shell commands

Tasks that shell out to external binaries can opt into the ProcessRunnerTrait, which wraps symfony/process to stream output and enforce a per-call timeout. See docs/creating-tasks.md for setup and behavior notes.

Documentation

Full index: docs/index.md.

Topic File
Installation, requirements, optional packages docs/installation.md
Creating tasks (attributes, env/group filtering, IDs) docs/creating-tasks.md
Console commands reference docs/commands.md
Storage backends (filesystem, database, custom) docs/storage.md
Host-scope tasks (host runner, .env cascade, concurrency) docs/host-tasks.md
Lifecycle events docs/events.md
Logging (PSR-3, Monolog channel) docs/logging.md
Testing (unit, functional, command tester) docs/testing.md
Security model, host runner hardening docs/security.md
Advanced (custom sorter, locking, slow-task threshold, transactions) docs/advanced.md
Troubleshooting / FAQ docs/troubleshooting.md

Project meta: CHANGELOG.md (release notes, Keep-a-Changelog format), UPGRADE.md (breaking-change migration notes), SECURITY.md (vulnerability disclosure), CONTRIBUTING.md (local dev setup and PR conventions).

Security

Failure logs from DBAL-backed storage are scrubbed of full exception objects to avoid leaking connection credentials into shared log sinks. See docs/logging.md and docs/security.md for the full trust model and hardening notes.

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT. See LICENSE for details.