PHP library for executing commands on multiple remote machines, via SSH

Related tags

Command Line shunt
Overview

#Shunt

Teaser

Build Status Dependencies Status Coverage Status Latest Stable Version Total Downloads

Inspired by Ruby's Capistrano, Shunt is PHP library for executing commands on multiple remote machines, via SSH. Specifically, this library was written to simplify and automate deployment of PHP applications to distributed environments.

Install

Via Composer

{
    "require": {
        "league/shunt": "~2.0"
    }
}

Requirement

  • PHP >= 5.3.3
  • libssh2
  • ssh2.so

Additional Features

  • Secure copy (SCP) support
  • Secure FTP (SFTP) support

Assumptions

Shunt has very firm ideas about how things ought to be done, and tries to force those ideas on you. Some of the assumptions behind these opinions are:

  • You are using SSH to access the remote servers.
  • You either have the same password to all target machines, or you have public keys in place to allow passwordless access to them.

Do not expect these assumptions to change.

Usage

In general, you'll use Shunt as follows:

  • Create a recipe file (Shuntfile).
  • Use the shunt script to execute your recipe.

From the root folder of your composer-based project, use the Shunt script as follows:

vendor/bin/shunt some_task some_host,other_host

By default, the script will look for a file called Shuntfile, which contain hosts information, credential and your tasks. Here the structure of Shuntfile :

 array(
		'staging' => 'staging.domain.com',
		'repro' => 'backup.domain.com',
		'production' => 'production.domain.com',
	),

	'auth' => array(
		'username' => 'shunt',
		'password' => 'hearmyroar',
		'pubkeyfile' => NULL,
		'privkeyfile' => NULL,
		'passphrase' => NULL,
	),

	'tasks' => array(
		'read_home_dir' => function($s) {
			$s->run('ls');
		},
		'print_php_info' => function($s) {
			$s->run('php -i');
		},
		'upload_foo_source' => function($s) {
			$s->sftp()->mkdir('source');
			$s->scp()->put('foo', 'source/foo');
		}
	),
);

The tasks collection indicates which tasks that available to execute. You can execute list command to see all the available tasks and available hosts. Based by above recipes, you could run :

vendor/bin/shunt read_home_dir .

Above command will execute ls on all remote machines defined in hosts parameter. You could tell Shunt to run the task on specific host(s) by appending the host nickname right after the task :

vendor/bin/shunt read_home_dir staging
vendor/bin/shunt print_php_info staging,production

As you may already notice, you could easily access SCP and SFTP instance by calling scp() or sftp() method within your task. Bellow table shows available APIs for both SCP and SFTP instances :

Type Method Signature Description
SCP put($localFile = '', $remoteFile = '') Send a file from local to remote path
SCP get($remoteFile = '', $localFile = '') Get a file from remote to local path
SFTP chmod($filename = '', $mode = 0644) Attempts to change the mode of the specified file to that given in mode.
SFTP lstat($path = '') Stats a symbolic link on the remote filesystem without following the link.
SFTP stat($path = '') Stats a file on the remote filesystem following any symbolic links.
SFTP mkdir($dirname = '', $mode = 0777, $recursive = false) Creates a directory on the remote file server with permissions set to mode.
SFTP rmdir($dirname = '') Removes a directory from the remote file server.
SFTP symlink($target = '',$link = '') Creates a symbolic link named link on the remote filesystem pointing to target.
SFTP readlink($link = '') Returns the target of a symbolic link.
SFTP realpath($filename = '') Translates filename into the effective real path on the remote filesystem.
SFTP rename($from = '', $to = '') Renames a file on the remote filesystem.
SFTP unlink($filename = '') Deletes a file on the remote filesystem.

Changelog

See the changelog file

Contributing

Please see CONTRIBUTING for details.

Support

Bugs and feature request are tracked on GitHub

License

Shunt is released under the MIT License. See the bundled LICENSE file for details.

Comments
  • Package naming

    Package naming

    I can see this being a helpful tool for lots of developers, but there's also grunt.js, which is super popular and dominates the name "grunt". I don't really see why the package is not named differently. Even the gruntfile is the same name, so I doubt this is a coincidence, right?

    My two cents would be to rename the package and respect the other tool's namespace, even if it is from another language.

    opened by frankdejonge 13
  • Deploying to clusters

    Deploying to clusters

    Can you really only define a single host per name? How are you expected to deploy to an entire live cluster without being able to define an array of target hosts?

    wontfix 
    opened by Bilge 5
  • Has this project been abandoned?

    Has this project been abandoned?

    @philsturgeon

    It's been nearly 3 years since the last commit on this project, and two issues I raised in December haven't had a response.

    Can you please clarify the status of this project, as I may need to find an alternative if there is no longer support for this library.

    Many thanks!

    opened by kabudu 2
  • Segmentation fault

    Segmentation fault

    Keep getting this error "[1] 73643 segmentation fault shunt print_php_info production" when trying to do a remote "ls" as shown in the README. Using SSH key to connect. Is there a log file or anything I can use to debug?

    opened by efarem 2
  • Install problem

    Install problem

    hello, i have this error :

     php composer.phar update
    Loading composer repositories with package information
    Updating dependencies (including require-dev)
    Your requirements could not be resolved to an installable set of packages.
    
      Problem 1
        - league/shunt v2.1.1 requires ext-ssh2 >=0.12 -> the requested PHP extension ssh2 is missing from your system.
        - league/shunt v2.1.0 requires ext-ssh2 >=0.12 -> the requested PHP extension ssh2 is missing from your system.
        - league/shunt v2.0.0 requires ext-ssh2 >=0.12 -> the requested PHP extension ssh2 is missing from your system.
        - Installation request for league/shunt ~2.0 -> satisfiable by league/shunt[v2.0.0, v2.1.0, v2.1.1].
    

    how to fix this please ?

    opened by ghost 2
  • Added ability to type hint a task

    Added ability to type hint a task

    Added ability to type hint a Grunt task so that syntax like:

    <?php
    
    use League\Grunt\Grunt;
    
    return [
        'hosts' => [
            'production' => 'foo.com'
        ],
        'tasks' => [
            'deploy' => function(Grunt $g) {
                $g->run('ls');
            }
        ],
    ];
    

    Can be used. This gives the ability to autocomplete methods from an IDE, as well as aid in using debuggers.

    opened by mrkrstphr 2
  • Help!

    Help!

    Excuse my ignorance, but still did not understand how to have the script to work, put I saved the Shuntfile file and how do I call the task? Would someone send me an example?

    I thank everyone's attention.

    opened by whera 1
  • Shunt doesn't run anything in parallel

    Shunt doesn't run anything in parallel

    According to the description, Shunt is a

    PHP library for executing commands in parallel on multiple remote machines, via SSH

    As far as I can tell, nothing is run in parallel. The commands are run one at a time (desirable, I would assume), and each machine is run one at a time (undesirable, IMHO). So nothing seems to be run in parallel.

    Example Shuntfile:

    <?php
    
    return [
        'hosts' => [
            'production' => 'staging.ussig.net',
            'production2' => 'dev.ussig.net',
        ],
        'auth' => [
            //...
        ],
        'tasks' => [
            'deploy' => function(\League\Shunt\Shunt $s) {
                $releasePath = '/var/www/deploytohere/releases/' . (new \DateTime())->format('YmdHis');
                $s->run('mkdir -p ' . $releasePath);
                $s->run('git clone repo_path ' . $releasePath);
                $s->run('cd ' . $releasePath . ' && php composer.phar install');
                echo "Finished:" . date('d M Y H:i:s') . "\n";
            }
        ],
    ];
    

    Output:

    $ vendor/bin/shunt deploy production,production2
    #1. Running "deploy" task on "production"
    Connecting...
    staging.ussig.net < mkdir -p /var/www/deploytohere/releases/20140306201644
    staging.ussig.net > [OK]
    staging.ussig.net < git clone [email protected]:mrkrstphr/sales.git /var/www/deploytohere/releases/20140306201644
    staging.ussig.net > Cloning into '/var/www/deploytohere/releases/20140306201644'...
                      > Checking connectivity... done
    staging.ussig.net < cd /var/www/deploytohere/releases/20140306201644 && php composer.phar install
    staging.ussig.net > Warning: This development build of composer is over 30 days old. It is recommended to update it by running "composer.phar self-update" to get the latest version.
                      > Loading composer repositories with package information
    [...snip...]
                      > Writing lock file
                      > Generating autoload files
    Finished:06 Mar 2014 20:17:16
    Done.
    
    #2. Running "deploy" task on "production2"
    Connecting...
    dev.ussig.net < mkdir -p /var/www/deploytohere/releases/20140306201716
    dev.ussig.net > [OK]
    dev.ussig.net < git clone [email protected]:mrkrstphr/sales.git /var/www/deploytohere/releases/20140306201716
    dev.ussig.net > Cloning into '/var/www/deploytohere/releases/20140306201716'...
                  > Checking connectivity... done
    dev.ussig.net < cd /var/www/deploytohere/releases/20140306201716 && php composer.phar install
    dev.ussig.net > Warning: This development build of composer is over 30 days old. It is recommended to update it by running "composer.phar self-update" to get the latest version.
                  > Loading composer repositories with package information
    [...snip...]
                      > Writing lock file
                      > Generating autoload files
    Finished:06 Mar 2014 20:19:43
    Done.
    
    opened by mrkrstphr 1
  • Make ssh2 extension optional

    Make ssh2 extension optional

    I'm having trouble installing the ssh2 extension, both on my work computer and at home. I feel this might be an issue for more users.

    Perhaps Shunt could use a pure PHP SSH2 implementation like phpseclib if the extension is not available. It also supports SSH2, SFTP and SCP.

    I've looked at the source code and it seems it is very implementation-dependent by using constants to dynamically call ssh2-specific functions, for example. Making Shunt SSH2 implementation-independent seems cumbersome.

    Any thoughts on whether this is a pursuable notion?

    opened by rjkip 1
  • Possible bug in recipe file auth configuration processing

    Possible bug in recipe file auth configuration processing

    I may have found a bug in the processing of the auth configuration of the Shuntfile recipe.

    With this config:

    <?php
    
    return [
        //hosts
    
        'auth' => [
              'username' => '<username>',
              'privkeyfile' => '/home/<username>/.ssh/id_rsa',
              'pubkeyfile'  => '/home/<username>/.ssh/id_rsa.pub',
         ],
       
         // tasks
    ];
    

    I got the following error when running my shunt task:

    SSH authorization failed. REASON : ssh2_auth_pubkey_file(): Authentication failed for <username> using public key: Invalid public key, too short
    

    To get it to work, I swapped the private / public key arguments like this:

    <?php
    
    return [
        //hosts
    
        'auth' => [
              'username' => '<username>',
              'privkeyfile' => '/home/<username>/.ssh/id_rsa.pub',
              'pubkeyfile'  => '/home/<username>/.ssh/id_rsa',
         ],
    
         // tasks
    ];
    

    It appears that Shunt is not interpreting the arguments correctly, as I don't believe the above configuration is correct.

    I'm using Shunt v2.1.1

    opened by kabudu 0
  • Pass custom arguments to shunt script

    Pass custom arguments to shunt script

    Is it possible to pass custom arguments to the shunt script?

    For example, consider a use-case where shunt is included as part of a CI pipeline, and the code deploy tag is determined dynamically. In essence doing something like the following:

    vendor/bin/shunt some_task production --tag=v1.0.0
    

    and later access the tag argument within a task like this:

    <?php
    
    return [
        //hosts
        //auth
        'tasks' => [
             'some_task' => function($s) {
                 $tag = $s->option('tag');
              }
         ]
    ];
    
    opened by kabudu 0
  • Add result handler to run command

    Add result handler to run command

    Hi,

    first i want to say, thanks for that awesome piece of code :+1:

    We've the requirement, that we want to execute a remote command by invoking the run() method and need to have access to the result.

    <?php
    
    return array(
    
        'hosts' => array(
            'jenkins' => '127.0.0.1',
        ),
    
        'auth' => array(
            'username' => 'root',
            'password' => NULL,
            'pubkeyfile' => '/home/user/.ssh/id_rsa.pub',
            'privkeyfile' => '/home/user/.ssh/id_rsa',
            'passphrase' => NULL,
        ),
    
        'tasks' => array(
            'download_test' => function($s) {
    
                $local = '';
                if (is_file('/tmp/test_encoded.php')) {
                    $local = md5_file('/tmp/test_encoded.php');
                }
    
                $remote = '';
                $resultHandler = function ($resultDio) use (&$remote) {
                    $result = explode('  ', $resultDio);
                    $remote = array_shift($result);
                };
    
                $s->run('md5sum test_encoded.php', false, $resultHandler);
    
                if ($local === $remote) {
                    error_log(sprintf('Hashes ARE equal %s/%s', $local, $remote));
                } else {
                    error_log(sprintf('Hashes are NOT equal %s/%s', $local, $remote));
                    $s->scp()->get('test_encoded.php', '/tmp/test_encoded.php');
                }
            }
        ),
    );
    

    The pull request added a third parameter that allows you to specify a closure that can be used to access the command's result. Actually i think there is no possibility to do this, right?

    opened by wagnert 0
  • cleaning the travis set up

    cleaning the travis set up

    After using your config as starting point to get the ssh2 extension available in travis, I've just realized that:

    • it's not required to track an empty file
    • you don't need to enable the already installed extension
    • the check step is redundant

    So, here you have a PR just to say thanks!

    opened by kpacha 0
  • Use SSH agent for authentication

    Use SSH agent for authentication

    This makes it possible to use agent authentication for hosts by specifying an agent key in the authentication section:

    'auth' => array(
        'username' => 'shunt',
        'agent' => true,
    )
    

    A username must be provided and the key must be registered with the SSH agent.

    opened by cschorn 3
  • Getting raw command output for processing

    Getting raw command output for processing

    First, thanks for the extraordinary package :)

    Secondly, inside my tasks, I would like to access the raw output from the SSH command that was run so I can make decisions. After a couple hours of trying to creatively inject my custom CapturedOutput outputter into the doRun() function, I realized that this outputter is not used for command output, since the Shunt object has its own outputter that it got during instantiation. It seems to me that getting the raw output would be useful for others as well, so why not do one of these:

    1. Save the raw output in the Shunt object, then provide access so the task can run() something, then grab the output.
    2. Add a third parameter to Shunt::run() called OutputInterface $command_output so we can pass in a custom Outputter to grab the raw output of the command.
    3. Add the raw output to the ArbitraryCommand itself, along with the return value and maybe some timing information so you can do ::getDuration() ::getStartTime() ::getEndTime(), etc, from the command.

    I think no. 1 would be the most convenient, what you do you think?

    Here's my very basic CapturedOutput class that I referenced:

        <?php
    
        namespace ScientiaMobile\UserManager\Output;
    
        use Symfony\Component\Console\Formatter\OutputFormatterInterface;
        use Symfony\Component\Console\Output\Output;
    
        class CapturedOutput extends Output {
    
                private $output;
    
                public function __construct($verbosity = self::VERBOSITY_NORMAL) {
                        $this->flushOutput();
                        parent::__construct($verbosity, false, null);
                }
    
                public function getOutput() {
                        return $this->output;
                }
    
                public function getOutputLines() {
                        return explode("\n", $this->output);
                }
    
                public function flushOutput() {
                        $this->output = '';
                }
    
                protected function doWrite($message, $newline) {
                        $this->output .= $message;
                        if ($newline) $this->output .= "\n";
                }
        }
    
    enhancement 
    opened by kamermans 4
Releases(v2.0.0)
Owner
The League of Extraordinary Packages
A group of developers who have banded together to build solid, well tested PHP packages using modern coding standards.
The League of Extraordinary Packages
An object-oriented option parser library for PHP, which supports type constraints, flag, multiple flag, multiple values, required value checking

GetOptionKit Code Quality Versions & Stats A powerful option parser toolkit for PHP, supporting type constraints, flag, multiple flag, multiple values

Yo-An Lin 140 Sep 28, 2022
Library for creating CLI commands or applications

Console Motivation: this library purpose is to provide a lighter and more robust API for console commands and/or applications to symfony/console. It c

Théo FIDRY 16 Dec 28, 2022
Simple but yet powerful library for running almost all artisan commands.

:artisan gui Simple but yet powerful library for running some artisan commands. Requirements Laravel 8.* php ^7.3 Installation Just install package: c

null 324 Dec 28, 2022
A simple object oriented interface to execute shell commands in PHP

php-shellcommand php-shellcommand provides a simple object oriented interface to execute shell commands. Installing Prerequisites Your php version mus

Michael Härtl 283 Dec 10, 2022
A developer-friendly wrapper around execution of shell commands.

ptlis/shell-command A developer-friendly wrapper around execution of shell commands. There were several goals that inspired the creation of this packa

brian ridley 18 Dec 31, 2022
Handle signals in artisan commands

Using this package you can easily handle signals like SIGINT, SIGTERM in your Laravel app.

Spatie 96 Dec 29, 2022
A package built for lumen that ports most of the make commands from laravel.

A package built for lumen that ports most of the make commands from laravel. For lumen v5.1, but will most likely work for 5.2 as well. I haven't tested. If you have requests, let me know, or do it yourself and make a pull request

Michael Bonds 22 Mar 8, 2022
Supercharge your Symfony console commands!

zenstruck/console-extra A modular set of features to reduce configuration boilerplate for your commands: /** * Creates a user in the database. * *

Kevin Bond 29 Nov 19, 2022
🤖 GitHub Action to run symfony console commands.

Symfony Console GitHub Action Usage You can use it as a Github Action like this: # .github/workflows/lint.yml name: "Lint" on: pull_request: push

Nucleos 3 Oct 20, 2022
Helper commands for Laravel Beyond Crud

About beyond-crud-helpers This package has many helper commands for the Laravel BEyond CRUD project by Spatie Installation composer require --dev tarr

null 4 Mar 8, 2022
A PocketMine-MP plugin which allows the users to edit no permission message of commands

CommandPermissionMessage A PocketMine-MP plugin which allows the users to edit no permission message of commands Have you ever got bored by the red me

cosmicnebula200 3 May 29, 2022
A Cli tool to save you time, and gives you the power to scaffold all of your models,controllers,commands

A Cli tool to save you time, and gives you the power to scaffold all of your models,controllers,commands... at once Installation You can install the p

Coderflex 16 Nov 11, 2022
Hentai Bash - This is the core of Hentai Terminal, responsible for the basic functions and commands

Hentai Bash - This is the core of Hentai Terminal, responsible for the basic functions and commands. It is mainly used for writing and executing commands.

Hentai Group 1 Jan 26, 2022
An Elegant CLI Library for PHP

Commando An Elegant PHP CLI Library Commando is a PHP command line interface library that beautifies and simplifies writing PHP scripts intended for c

Nate Good 793 Dec 25, 2022
A PHP library for command-line argument processing

GetOpt.PHP GetOpt.PHP is a library for command-line argument processing. It supports PHP version 5.4 and above. Releases For an overview of the releas

null 324 Dec 8, 2022
Generic PHP command line flags parse library

PHP Flag Generic PHP command line flags parse library Features Generic CLI options and arguments parser. Support set value data type(int,string,bool,a

PHP Toolkit 23 Nov 13, 2022
A PHP library for command-line argument processing

GetOpt.PHP GetOpt.PHP is a library for command-line argument processing. It supports PHP version 7.1 and above. Releases For an overview of the releas

null 324 Dec 8, 2022
BetterWPCLI - a small, zero-dependencies, PHP library that helps you build enterprise WordPress command-line applications.

BetterWPCLI - a small, zero-dependencies, PHP library that helps you build enterprise WordPress command-line applications.

Snicco 5 Oct 7, 2022