serversinc/ssh-runner

Actions-based SSH Runner

Maintainers

Package info

github.com/serversinc/ssh-runner

pkg:composer/serversinc/ssh-runner

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.0.1 2026-05-07 19:19 UTC

This package is auto-updated.

Last update: 2026-05-07 19:29:00 UTC


README

Latest Version on Packagist Total Downloads GitHub Actions

A pipeline-based SSH runner for Laravel that executes commands on remote servers with support for action composition, failure strategies, automatic rollback, and execution logging.

This package provides a fluent API for building SSH command pipelines using the Spatie SSH library under the hood.

Installation

You can install the package via Composer:

composer require serversinc/ssh-runner

Configuration

Publish the configuration file:

php artisan vendor:publish --provider="Serversinc\SshRunner\SshRunnerServiceProvider" --tag="ssh-runner-config"

Publish the migrations:

php artisan vendor:publish --provider="Serversinc\SshRunner\SshRunnerServiceProvider" --tag="ssh-runner-migrations"

Run the migrations to create the logging tables:

php artisan migrate

Basic Usage

1. Implement the SshServer Interface

Your server model must implement the SshServer contract:

use Serversinc\SshRunner\Contracts\SshServer;

class Server extends Model implements SshServer
{
    public function getSshHost(): string
    {
        return $this->ip_address;
    }

    public function getSshPort(): int
    {
        return $this->ssh_port ?? 22;
    }

    public function getSshUser(): string
    {
        return $this->ssh_user;
    }

    public function getSshKeyPath(): ?string
    {
        return $this->ssh_key_path;
    }

    public function getSshKeyContents(): ?string
    {
        return $this->ssh_key_contents;
    }
}

2. Create an Action

Actions are reusable, testable units of work:

use Serversinc\SshRunner\Actions\BaseSshAction;
use Serversinc\SshRunner\Contracts\SshServer;
use Serversinc\SshRunner\Results\ActionResult;
use Spatie\Ssh\Ssh;

class InstallPackage extends BaseSshAction
{
    public function __construct(private string $packageName)
    {
    }

    public function handle(SshServer $server, Ssh $ssh): ActionResult
    {
        return $this->run($ssh, ["apt-get install -y {$this->packageName}"]);
    }

    public function undo(SshServer $server, Ssh $ssh): void
    {
        // Called automatically on rollback
        $ssh->execute(["apt-get remove -y {$this->packageName}"]);
    }
}

3. Execute a Pipeline

There are several ways to execute pipelines:

Using the Facade (Recommended)

use SshRunner;
use Serversinc\SshRunner\Enums\FailureStrategy;

$result = SshRunner::pipeline($server)
    ->run(new UpdatePackageList)
    ->run(new InstallPackage('nginx'))
    ->run(new InstallPackage('nginx'))
    ->run(new RestartService('nginx'))
    ->execute();

if ($result->success) {
    echo "Pipeline completed in {$result->duration()} seconds";
} else {
    foreach ($result->failedActions() as $action) {
        echo "Failed: {$action->action}\n";
        echo "Error: {$action->errorOutput}\n";
    }
}

Using SshConnection

use Serversinc\SshRunner\SshConnection;

$connection = SshConnection::for($server);

$result = $connection->pipeline()
    ->run(new UpdatePackageList)
    ->run(new InstallPackage('nginx'))
    ->execute();

Using the Factory Class

use Serversinc\SshRunner\SshRunner;

$result = SshRunner::pipeline($server)
    ->run(new UpdatePackageList)
    ->run(new InstallPackage('nginx'))
    ->execute();

Failure Strategies

Control what happens when an action fails:

use Serversinc\SshRunner\Enums\FailureStrategy;

// STOP (default) - Stop execution on first failure
$pipeline->onFailure(FailureStrategy::STOP);

// CONTINUE - Keep executing remaining actions
$pipeline->onFailure(FailureStrategy::CONTINUE);

// ROLLBACK - Undo completed actions in reverse order
$pipeline->onFailure(FailureStrategy::ROLLBACK)
    ->run(new CreateDatabase)
    ->run(new CreateUser) // If this fails, CreateDatabase->undo() is called
    ->execute();

Execution Logging

All pipeline runs are automatically logged to the database:

use Serversinc\SshRunner\Models\SshPipelineLog;

// Get all runs for a server
$runs = SshPipelineLog::where('server_id', $server->id)->get();

// Check if a specific run failed
$run = SshPipelineLog::find(1);
if ($run->failed()) {
    foreach ($run->actionLogs as $log) {
        echo "{$log->action}: {$log->exit_code}\n";
    }
}

Single Action Execution

Execute a single action without the pipeline:

// Using the Facade
$result = SshRunner::run($server, new UpdatePackageList);

// Or using SshConnection
$connection = SshConnection::for($server);
$result = $connection->execute(new UpdatePackageList);

if ($result->success) {
    echo $result->output;
} else {
    echo $result->errorOutput;
}

Advanced Usage

Creating a Connection for Reuse

use Serversinc\SshRunner\SshConnection;
use Serversinc\SshRunner\SshPipeline;

$connection = SshConnection::for($server);

// Execute multiple pipelines on the same connection
$result1 = $connection->pipeline()
    ->run(new Action1())
    ->execute();

$result2 = $connection->pipeline()
    ->run(new Action2())
    ->execute();

Using SshRunner Factory Methods

use Serversinc\SshRunner\SshRunner;

// Create a connection
$connection = SshRunner::connect($server);

// Create a pipeline directly
$pipeline = SshRunner::pipeline($server);

// Execute a single action
$result = SshRunner::run($server, new SomeAction());

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security-related issues, please use the issue tracker and mark it as a security concern.

Credits

License

The MIT License (MIT). Please see License File for more information.