{"id":36983022,"url":"https://github.com/aes3xs/tasker","last_synced_at":"2026-01-13T22:54:55.435Z","repository":{"id":56941518,"uuid":"82079400","full_name":"aes3xs/tasker","owner":"aes3xs","description":"Task automation tool","archived":false,"fork":false,"pushed_at":"2018-02-25T12:49:27.000Z","size":309,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-13T22:54:37.157Z","etag":null,"topics":["deploy","deployment","php","tool"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aes3xs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-15T16:09:00.000Z","updated_at":"2018-01-18T08:47:15.000Z","dependencies_parsed_at":"2022-08-21T07:50:23.554Z","dependency_job_id":null,"html_url":"https://github.com/aes3xs/tasker","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/aes3xs/tasker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aes3xs%2Ftasker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aes3xs%2Ftasker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aes3xs%2Ftasker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aes3xs%2Ftasker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aes3xs","download_url":"https://codeload.github.com/aes3xs/tasker/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aes3xs%2Ftasker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28405138,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T21:51:37.118Z","status":"ssl_error","status_checked_at":"2026-01-13T21:45:14.585Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deploy","deployment","php","tool"],"created_at":"2026-01-13T22:54:54.806Z","updated_at":"2026-01-13T22:54:55.430Z","avatar_url":"https://github.com/aes3xs.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"Tasker\n======\nTasker is an automation tool to write small and simple deploy scripts.  \nIt's suitable for one-server projects without CI integrations.  \nIf you want more, please take a look at Jenkins, TeamCity and other tools. It's not pretent to replace them. \nTasker inspired by Deployer project, you can also take a look for it. Tasker is designed to be more flexible (overridable) and more object-oriented, but less featured.  \n\nInstallation\n------------\nInstall from composer  \n```composer require aes3xs/tasker```\n\nHow to deploy\n-------------\nA good way to go will be splitting deployment into local and remote parts. \n   \nLocal part prepares code, warms up file caches, downloads vendors and other operations, which affects everything *inside* project dir.  \nNo external dependencies, services or files (except vendor managers and php itself).  \nNo database requests, no migrations.  \nThis script can be executed everythere, on build server or local machines. It creates ready-to-deploy module which can be copied and run on production as is.  \nIt's pretty useful to have such script in project and it should't have dependencies, such as composer. Because it's responsible to run it, if you have bare broject from repo.  \nSome of these steps sometimes defined in composer.json scripts section. But I prefer to have standalone file.  \n\nHere is an example (./bin/deploy):\n ```bash\n#!/bin/bash  \n  \nset -e\nset -o pipefail\n  \nSYMFONY_ENV=${SYMFONY_ENV:=\"dev\"}\nSYMFONY_DEBUG=${SYMFONY_DEBUG:=\"1\"}\nfor i in \"$@\"; do case $i in -e=*|--env=*) SYMFONY_ENV=\"${i#*=}\"; shift;; --no-debug) SYMFONY_DEBUG=\"0\"; shift;; *);; esac done\n  \nROOT=\"$(dirname \"$(dirname \"$(readlink -fm \"$0\")\")\")\"\nPHP=$(which \"php\") || { echo \"PHP is not found\" ; exit 1; }\nYARN=$(which \"yarn\") || { echo \"Yarn is not found\" ; exit 1; }\nCOMPOSER=$(which \"composer\") || { echo \"Composer is not found\" ; exit 1; }\nif [ \"$SYMFONY_DEBUG\" == \"0\" ]; then NO_DEBUG=\"--no-debug\"; fi\nCONSOLE=\"${ROOT}/bin/console --quiet --env=${SYMFONY_ENV} ${NO_DEBUG}\"\n  \necho \"SYMFONY_ENV   = ${SYMFONY_ENV}\"\necho \"SYMFONY_DEBUG = ${SYMFONY_DEBUG}\"\necho \"PROJECT_ROOT  = ${ROOT}\"\necho \"PHP           = ${PHP}\"\necho \"YARN          = ${YARN}\"\necho \"COMPOSER      = ${COMPOSER}\"\necho \"CONSOLE       = ${CONSOLE}\"\necho \"\"\n  \n_exec () {\n    echo -e \"\\033[1m[$(date +%T)] \u003e\\033[0m\" $1\n    eval $1\n}\n  \n# Vendors\n_exec \"cd ${ROOT} \u0026\u0026 ${COMPOSER} install --prefer-dist --no-progress --no-interaction --optimize-autoloader\"\n_exec \"cd ${ROOT} \u0026\u0026 ${YARN} install --prod --non-interactive\"\n  \n# Cache\n_exec \"rm -rf ${ROOT}/var/cache/${SYMFONY_ENV}\"\n_exec \"chmod 0775 ${ROOT}/var/cache\"\n_exec \"${CONSOLE} cache:warmup\"\n  \n# Assets\nPATHS=(\n\"${ROOT}/web/js\"\n\"${ROOT}/web/css\"\n\"${ROOT}/web/bundles\"\n)\n_exec \"rm -rf ${PATHS[*]}\"\n_exec \"${CONSOLE} assets:install ${ROOT}/web --symlink --relative\"\n_exec \"${CONSOLE} assetic:dump ${ROOT}/web\"\n  \n# Writable\n_exec \"if [ ! -w ${ROOT}/var/cache ]; then { echo 'Is not writable' ; exit 1; }; fi\"\n_exec \"if [ ! -w ${ROOT}/var/logs ]; then { echo 'Is not writable' ; exit 1; }; fi\"\n_exec \"if [ ! -w ${ROOT}/var/spool ]; then { echo 'Is not writable' ; exit 1; }; fi\"\n\n```\n\nYou can also write this with Tasker, but I'd suggest to keep separate shell script.  \n\nRemote part of deployment process works with external services, such as nginx, php-fpm, databases.  \nAlso you must have access to project repository to clone it.  \n\nThere are few important things.  \n\nFirst, executing commands from another user.  \nFor security reasons, each person on server must have it's own credentials.  \nBut project itself configured to work from one user, for example, `www-data`.  \nSo you must add something like `sudo -u USER` to each call, and it's already implemented.  \n\nSecond, authentication to clone repo on server.  \nGitLab has login/pass option or public key, for example.  \nSo better way to use key to SSH, and same key to authenticate to GitLab, or another system.  \nThis can be done with SSH forwarding authentication.  \n\nDeploy script example\n\n```php\n#!/usr/bin/env php\n\u003c?php\n  \nrequire_once \"./vendor/autoload.php\";  \n  \nuse Aes3xs\\Tasker\\Connection\\Connection;\nuse Aes3xs\\Tasker\\Service\\Git;\nuse Aes3xs\\Tasker\\Service\\Releaser;\nuse Aes3xs\\Tasker\\Service\\Shell;\nuse Aes3xs\\Tasker\\Service\\Symfony;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputOption;\n  \nclass TestRecipe extends \\Aes3xs\\Tasker\\AbstractRecipe\n{\n    public $repository = 'REPOSITORY_PATH';\n    public $deployPath = '/var/www/PROJECT';\n    public $runUser = 'www-data';\n    public $branch = 'master';\n\n    protected function configure()\n    {\n        $this\n            -\u003eaddArgument('server', InputArgument::REQUIRED)\n            -\u003eaddOption('branch', 'b', InputOption::VALUE_OPTIONAL);\n    }\n\n    public function execute($server, Connection $connection)\n    {\n        if (!in_array($server, ['prod', 'dev'])) {\n            throw new \\RuntimeException('Server available values: prod, dev');\n        }\n\n        $connection\n            -\u003egetParameters()\n            -\u003esetHost(null)\n            -\u003esetForwarding(true); // Configure to deploy on different servers\n\n        $this-\u003ebranch = 'master'; // Set branch if needed\n\n        try {\n            $this-\u003erunActions(array_filter($this-\u003egetAvailableActions(), function ($actionName) {\n                return !in_array($actionName, ['execute', 'failNotify']);\n            }), 'Deploy');\n        } catch (\\Exception $e) {\n            $this-\u003erunActions(['failNotify'], 'Failback');\n        }\n    }\n    \n    public function switchUser(Shell $shell, $runUser)\n    {\n        $shell-\u003esetUser($runUser);\n\n        return $runUser;\n    }\n    \n    public function createRelease(Releaser $releaser, Git $git, Shell $shell, $deployPath, $repository, $branch)\n    {\n        $releaser-\u003esetDeployPath($deployPath);\n        $releaser-\u003eprepare();\n\n        if ($releaser-\u003eisLocked() \u0026\u0026 $this-\u003easkConfirmationQuestion('Deploy is locked. Unlock?')) {\n            $releaser-\u003eunlock();\n        }\n        $releaser-\u003elock();\n\n        $releaser-\u003ecreate();\n        $releasePath = $releaser-\u003egetReleasePath();\n\n        $releases = $releaser-\u003egetReleaseList();\n        $reference = $releases ? $releaser-\u003egetReleasePathByName(reset($releases)) : null;\n\n        $shell-\u003esetCwd($releasePath);\n\n        $git-\u003ecloneAt($repository, $releasePath, $branch, $reference); // Uses SSH forwarding if presented\n\n        $releaser-\u003eupdateReleaseShares(['var/logs', 'var/spool', 'var/sessions'], ['app/config/parameters.yml']);\n\n        $shell-\u003echmod('./bin', 0777);\n        $shell-\u003eexec('./bin/deploy --env=prod --no-debug');\n    }\n    \n    public function migrate(Symfony $symfony)\n    {\n        $symfony-\u003erunCommand('doctrine:migrations:migrate', [], ['allow-no-migration']);\n    }\n\n    public function release(Releaser $releaser, Git $git, Shell $shell, $server, $branch)\n    {\n        $releaser-\u003erelease();\n        $releaser-\u003eunlock();\n\n        $last_commits = $git-\u003elog($releaser-\u003egetReleasePath(), 3);\n\n        $this-\u003einfo(\u003c\u003c\u003cEOL\nServer: {{ server }}\nReleased: {{ releaser.getReleaseName() }}\nBranch: {{ server }}\nLast commits:\n$last_commits\nEOL\n        );\n\n        $releaser-\u003ecleanup(5);\n\n        $shell-\u003esetUser(null);\n\n        $shell-\u003eexec(\"sudo service nginx reload\");\n        $shell-\u003eexec(\"sudo service php-fpm reload\");\n    }\n\n    public function shutdownRoutine(Shell $shell)\n    {\n        $shell-\u003esetUser(null);\n    }\n\n    public function failNotify()\n    {\n        $this-\u003eerror(\u003c\u003c\u003cEOL\nServer: {{ server }}\nRelease: {{ releaser.getReleaseName() }}\nBranch: {{ server }}\nFALURE\nEOL\n        );\n    }\n}\n\n\\TestRecipe::run();\n\n```\n\nOverview\n--------\n\nTasker is intended to create php binaries with a set of actions.  \nActions can be executed on local or remote server.  \nThey are located in \"recipe\" class.  \nRecipe is a self-executable console command.  \n\n```php\n#!/usr/bin/env php\n\u003c?php  \n  \nrequire_once \"./vendor/autoload.php\";\n  \nclass TestRecipe extends \\Aes3xs\\Tasker\\AbstractRecipe\n{\n    public function execute()\n    {\n        // Do smth\n    }\n}\n  \n\\TestRecipe::run();\n\n```\nMake your file executable with first line`#!/usr/bin/env php`   \nAdd permissions `chmod a+x ./testRecipe`   \nThen add autoload from composer `require_once \"./vendor/autoload.php\";`, make sure path is correct.  \nDefine your own recipe class, extend it from `\\Aes3xs\\Tasker\\AbstractRecipe`  \nAdd `execute` (default) or other method, which will be executed first.  \nCall static `::run()` or `::run('yourMethodName')`  \n  \nActions\n-------\nInside actions you can actually do your work.  \nActions are public non-static methods.  \nMagic methods (__*) or started with get[A-Z] (getSmth, for example) cannot be executed.  \nActions are called with `runActions(['prepare', 'release'])` or `runAction('release'')`  \nYou cat get list of all available actions in recipe with `getAvailableActions()`  \nIf you want to skip action during it execution, call `skipAction('only in production')`, like PhpUnit's `$this-\u003emarkTestSkipped()`  \n\n```php\n#!/usr/bin/env php\n\u003c?php  \n  \nrequire_once \"./vendor/autoload.php\";\n  \nclass TestRecipe extends \\Aes3xs\\Tasker\\AbstractRecipe\n{\n    public function execute()\n    {\n        $this-\u003erunActions($this-\u003egetAvailableActions(), 'deployment');\n    }\n    \n    public function prepare()\n    {\n    }\n    \n    public function deploy($env)\n    {\n        if ('prod' === $env) {\n            $this-\u003erunAction('restart');\n        }\n    }\n    \n    public function restart()\n    {\n    }\n    \n    public function notify($env)\n    {\n        if ('prod' !== $env) {\n             $this-\u003eskipAction('Production only');\n        }\n    }\n}\n  \n\\TestRecipe::run();\n\n```\n  \nCommand\n-------\nRecipe is a command with arguments and options.  \nIt is based on Symfony Console component.  \nSo you have `configure()` to define what input will be available.  \nAnd same methods `addArgument()` and `addOption()`\nYou can use all Symfony default options:\n  - --help to show info about command\n  - -v, -vv, -vvv to make output more verbose\n  - -q to disable output, except errors\n  - -n to disable user input (non-interactive mode)\n\n```php\n\u003c?php\n  \nuse Symfony\\Component\\Console\\Input\\InputArgument;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\n  \nclass TestRecipe extends \\Aes3xs\\Tasker\\AbstractRecipe\n{\n    protected function configure()\n    {\n        $this\n            -\u003eaddArgument('argument', InputArgument::REQUIRED)\n            -\u003eaddOption('option', null, InputOption::VALUE_REQUIRED);\n    }\n  \n    public function doSmthAction(InputInterface $input, $argument, $option)\n    {\n        $argumentValue = $input-\u003egetArgument('argument');\n        $optionValue = $input-\u003egetArgument('option');\n        $argumentValue = $argument; // same\n        $optionValue = $option; // same\n    }\n    \n    public function doSmth2Action(OutputInterface $output)\n    {\n        $output-\u003ewriteln('Hello');\n    }\n}\n```\nYou can access defined inputs by it's names.  \n  \nThere are few helper methods for user interations:  \nTo get true/false result `$this-\u003easkConfirmationQuestion()`  \nTo get choice from array of variants `$this-\u003easkChoiceQuestion()` (it results value, not key)  \nTo get string input `$this-\u003easkQuestion()`  \n  \n```php\n\u003c?php\n  \nclass TestRecipe extends \\Aes3xs\\Tasker\\AbstractRecipe\n{\n    public function askSmthAction()\n    {\n        $result = $this-\u003easkConfirmationQuestion('Are you sure?', false);\n        \n        $result = $this-\u003easkChoiceQuestion('Pick a color', ['red', 'green', 'blue'], 'green');\n        \n        $result = $this-\u003easkQuestion('Enter your name', 'anonymous');\n    }\n}\n```\nYou can implement your own questions or override these.  \n\nAutowired resources\n-------------------\nResources are arguments to actions.  \nYou can use resource name or class name to wire existing resource to action call.  \nClass and name can point to different resources, so result may be unpredictable. Please avoid these situations.  \n  \nResources are (in decreasing priority order):  \n  - get[A-Z] methods in recipe, some sort or dymanic properties\n  - public non-static recipe properties\n  - input arguments\n  - input options\n  - container services (internal)\n  - container parameters (internal)\n  \nIf there are resources with same names or class names, first occurence will be used.  \nSnake_case and camelCase treat same.  \n  \nUsable container services:\n  - input (Symfony\\Component\\Console\\Input\\InputInterface)\n  - output (Symfony\\Component\\Console\\Output\\OutputInterface)\n  - style (Symfony\\Component\\Console\\Style\\SymfonyStyle)\n  - connection (Aes3xs\\Tasker\\Connection\\Connection)\n  - logger (Monolog\\Logger)\n  - runner (Aes3xs\\Tasker\\Runner\\Runner)\n\nAnd some helpers:\n  - shell (Aes3xs\\Tasker\\Service\\Shell)\n  - composer (Aes3xs\\Tasker\\Service\\Composer)\n  - git (Aes3xs\\Tasker\\Service\\Git)\n  - releaser (Aes3xs\\Tasker\\Service\\Releaser)\n  - symfony (Aes3xs\\Tasker\\Service\\Symfony)\n\n```php\n\u003c?php\n\nclass TestRecipe extends \\Aes3xs\\Tasker\\AbstractRecipe\n{\n    /**\n     * Can be obtained by:\n     * dynamicRecipeResource\n     * dynamic_recipe_resource\n     */\n    public function getDynamicRecipeResource($dependency)\n    {\n        return $dependency * 10;\n    }\n    \n    /**\n     * Can be obtained by:\n     * SomeClass\n     * recipePropertyObject\n     * recipe_property_object\n     */\n    public $recipePropertyObject; // Contains \\SomeClass\n    \n    /**\n     * Can be obtained by:\n     * recipeProperty\n     * recipe_property\n     */\n    public $recipeProperty;\n    \n    /**\n     * Can be obtained by:\n     * recipePropertyCallback\n     * recipe_property_callback\n     */\n    public $recipePropertyCallback;\n    \n    public function setupRecipeCallbackProperty()\n    {\n        // Callback arguments resolving same way as actions\n        $this-\u003erecipePropertyCallback = function ($dependency) {\n            return $dependency * 10;\n        };\n    }\n    \n    protected function configure()\n    {\n        /**\n         * Can be obtained by:\n         * inputArgument\n         * input_argument\n         */\n        $this-\u003eaddArgument('input_argument');\n        \n        /**\n         * Can be obtained by:\n         * inputOption\n         * input_option\n         */\n        $this-\u003eaddOption('input_option');\n    }\n    \n    /**\n     * Can be obtained by class or name\n     */\n    public function containerServicesAction(\n        \\Symfony\\Component\\Console\\Input\\InputInterface $input,\n        \\Symfony\\Component\\Console\\Output\\OutputInterface $output,\n        \\Symfony\\Component\\Console\\Style\\SymfonyStyle $style,\n        \\Aes3xs\\Tasker\\Connection\\Connection $connection,\n        \\Monolog\\Logger $logger,\n        \\Aes3xs\\Tasker\\Runner\\Runner $runner,\n        \\Aes3xs\\Tasker\\Service\\Shell $shell,\n        \\Aes3xs\\Tasker\\Service\\Composer $composer,\n        \\Aes3xs\\Tasker\\Service\\Git $git,\n        \\Aes3xs\\Tasker\\Service\\Releaser $releaser,\n        \\Aes3xs\\Tasker\\Service\\Symfony $symfony\n    ) {\n        // Do smth\n    }\n}\n```\n\nConnection\n----------\n\nSimply set up connection parameters before using it.  \nIt initializes automatically on first call. Reuse or reconnect is not provided for now.  \n  \nLocal connection is pretty straightforward. To use it leave host null.  \n\nRemote connection supplied with three authentication types:  \n  - login/password, use `setPassword('password')`\n  - public key (optionally passphrase), use `setPublicKey($path or key itself)`\n  - agent forwarding, use `setForwarding(true)`\n  \nRemote connection based on PhpSecLib.  \n\n\u003e Ssh extension was implemented, but disabled in case of broken forwarding.  \n\u003e When you are using ssh forwarding manually, there is environment variable $SSH_AUTH_SOCK, which contains path to agent socket.So you can continue using forwarding to connect to another server.  \n\u003e With php ssh extension this variable is missing. It's really necessary feature, so I switched back to PhpSecLib for now.   \n\n```php\n\u003c?php\n  \nclass TestRecipe extends \\Aes3xs\\Tasker\\AbstractRecipe\n{\n    protected function connect(\\Aes3xs\\Tasker\\Connection\\Connection $connection)\n    {\n        // By default connection is local\n        // Local means host is null, 127.0.0.1 or localhost\n        \n        // Remote connection\n        $connection\n            -\u003egetParameters()\n            -\u003esetHost('192.168.1.1') // Default port 22\n            -\u003esetLogin('root')\n            -\u003esetPassword('password');\n        \n        $connection-\u003eexec('echo hello');\n    }\n}\n```\n\nServices\n--------\n\n### Shell\n\nShell is built on top of connection. So if you're already connected to remote (or local) server, you can also use Shell.  \nIt contains most usable shell commands:  \n  - exec\n  - ln\n  - chmod\n  - chown\n  - rm\n  - mkdir\n  - touch\n  - readlink\n  - realpath\n  - dirname\n  - ls\n  - which\n  \nHelper methods\n  - exists()\n  - isFile()\n  - isDir()\n  - isLink()\n  - isWritable()\n  - isReadable()\n  - write()\n  - read()\n  - copy()\n  - copyPaths()\n  - linkPaths()\n  - checkWritable()\n  - checkReadable()\n  \nIf you want to run all commands as another user, configure it with setUser(). So all commands will be prepended with `sudo -EHu USER bash -c \"COMMAND\"`. SSH agent forwarding will be also available.  \n  \nIf you want to run all commands from specific directory, configure it with setCwd().  \n  \nThese options take effect only on Shell service itself, not Connection.  \nOther services (Releaser, Git, Composer, Symfony) also use Shell inside, so take it into account.  \n\n### Releaser\n\nReleaser manages releases. It prepares directory structure to store your releases and links them during deploy or rollback.  \nFirst call setDeployPath() to point to root directory where all related stuff will be located.  \n```\n/var/www/project \u003c- deploy_path\n    │ \n    ├─ releases\n    │   ├─ 20180101221100 (YmdHis format)\n    │   ├─ 20180101221101\n    │   ├─ 20180101221102\n    │   ├─ 20180101221103\n    │   ├─ 20180101221104 (Symfony example) \u003c- current_path\n    │   │   ├─ app\n    │   │   │   └─ config\n    │   │   │       └─ ~parameters.yml (symlink in shared)\n    │   │   ├─ var\n    │   │   │   └─ ~logs (symlink in shared)\n    │   │   └─ release.lock (exists only in completed releases)\n    │   │\n    │   └─ 20180101221105 (deploy in progress...) \u003c- release_path\n    │     \n    ├─ ~current (symlink to 20180101221104 for example)\n    │\n    ├─ shared\n    │   ├─ app\n    │   │   └─ config\n    │   │       └─ parameters.yml\n    │   └─ var\n    │       └─ logs\n    │           └─ ...\n    │           \n    └─ deploy.lock\n```\n\nThen call prepare() to build directory structure.  \nUse lock(), unlock() and isLocked() to control deploy.lock file, use it to prevent simultaneous deploys.  \nCall create() to create new release, release() to symlink as current and add release.lock file, link() to symlink specific existing release, rollback() to symlink previous, cleanup() to delete unused releases. Get all available releases with getReleaseList(), it shows only completed releases, broken and unexpected directories and files are ignored.    \nUse updateReleaseShares() to update shared files and directories. Shares are same in all releases and they are stored separately.  \ngetCurrentReleasePath() to get path to currently linked release.  \ngetCurrentReleaseName() to get name of current release (dirname obviously).  \n\n### Git\n\nPreferred way to use private repositories is agent forwarding. But you can also setKeyPath() to use your public key.  \nMethods:\n  - checkout()\n  - cloneAt()\n  - log()\n  - fetch()\n  - getBranches()\n  - getCurrentBranch()\n\n### Composer\nMethods:\n  - install()\n  - update()\n  - download() to download phar archive\n\n### Symfony\nFirst set path to console setConsolePath(), usually release_path/bin/console\n  - setEnv()\n  - setDebug()\n  - setInteractive()\n  - runCommand()\n\nPass arguments and options to runCommand(). Options is an associative array, key is option name (if value is null, option treats as a flag).  \n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faes3xs%2Ftasker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faes3xs%2Ftasker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faes3xs%2Ftasker/lists"}