mirror of
https://github.com/andres-montanez/Magallanes
synced 2024-05-10 17:56:43 +02:00
Compare commits
No commits in common. "0.9.14" and "master" have entirely different histories.
32
.github/workflows/linters.yml
vendored
Normal file
32
.github/workflows/linters.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
name: Linters
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- galactica
|
||||
pull_request:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
linters:
|
||||
name: Linters
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Composer install
|
||||
uses: php-actions/composer@v5
|
||||
with:
|
||||
command: install
|
||||
args: --ignore-platform-reqs --no-scripts
|
||||
version: 2
|
||||
php_version: 8.0
|
||||
- name: PHPStan
|
||||
run: ./vendor/bin/phpstan analyse
|
||||
- name: PHP Code Sniffer
|
||||
run: ./vendor/bin/phpcs
|
46
.github/workflows/tests.yml
vendored
Normal file
46
.github/workflows/tests.yml
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
name: PHPUnit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- galactica
|
||||
pull_request:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
unit_tests:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- php: 8.0
|
||||
coveralls: true
|
||||
- php: 8.1
|
||||
coveralls: false
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Composer install
|
||||
uses: php-actions/composer@v5
|
||||
with:
|
||||
command: install
|
||||
args: --ignore-platform-reqs --no-scripts
|
||||
version: 2
|
||||
php_version: ${{ matrix.php }}
|
||||
- name: Run tests
|
||||
env:
|
||||
XDEBUG_MODE: coverage
|
||||
run: |
|
||||
mkdir .logs
|
||||
./vendor/bin/phpunit --coverage-clover build/logs/coverage.xml
|
||||
- name: Run Coveralls
|
||||
if: ${{ matrix.coveralls }}
|
||||
env:
|
||||
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./vendor/bin/php-coveralls -v --coverage_clover build/logs/coverage.xml
|
18
.gitignore
vendored
18
.gitignore
vendored
|
@ -1,11 +1,7 @@
|
|||
.settings
|
||||
.settings/*
|
||||
.project
|
||||
.buildpath
|
||||
|
||||
# OS generated files # // GitHub Recommendation
|
||||
######################
|
||||
.DS_Store*
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
/vendor/
|
||||
/build
|
||||
composer.lock
|
||||
.mage.yml
|
||||
.phpunit.result.cache
|
||||
.phpcs-cache
|
||||
.logs
|
||||
|
|
14
.travis.yml
Normal file
14
.travis.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
language: php
|
||||
php:
|
||||
- '8.0'
|
||||
- '8.1'
|
||||
|
||||
install:
|
||||
- composer install
|
||||
|
||||
script:
|
||||
- mkdir -p build/logs
|
||||
- vendor/bin/phpunit --coverage-clover build/logs/coverage.xml
|
||||
|
||||
after_script:
|
||||
- vendor/bin/coveralls -v --coverage_clover build/logs/coverage.xml
|
15
CHANGELOG.md
Normal file
15
CHANGELOG.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
CHANGELOG for 5.X
|
||||
=================
|
||||
|
||||
* 5.0.0 (2022-04-15)
|
||||
* v5 series release.
|
||||
* Refactored for Symfony 6 and PHP 8.
|
||||
* Added strong types.
|
||||
* Removed task `composer/self-update`.
|
||||
* Allow `exec` task to interpolate `%environment%` and `%release%`.
|
||||
* Added new `sleep` task to day execution [PR#414].
|
||||
* Added new `symlink` option to define the name of symbolic link on the Release [PR#425].
|
||||
* Improved Windows compatibility [PR#427].
|
||||
* Added new `log_limit` option to limit how many logs are kept [Issue#403].
|
||||
* Add new deploy option `--tag` to specify deploying a specific tag [Issue#192] [Issue#315].
|
||||
* Added new `scp_flags` option for the `scp` command when SSH flags are incompatible with [Issue#439].
|
67
CONTRIBUTING.md
Normal file
67
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
Contributor Guidelines for Magallanes
|
||||
=====================================
|
||||
Welcome to Magallanes! We are much appreciated you've decided to contribute to this project!
|
||||
Please read the following guidelines to make your and our work easier and cleaner.
|
||||
|
||||
**TL;DR**
|
||||
|
||||
1. Write clean code with no mess left
|
||||
2. Contribute the docs when adding configurable new feature
|
||||
3. Create your pull request from `galactica` branch
|
||||
4. Ensure your code is fully covered by tests
|
||||
|
||||
----------
|
||||
|
||||
# Reporting Issues
|
||||
If you have a problem or you've noticed a bug, please feel free to open a new issue. However follow the rules below:
|
||||
* First, make sure that similar/the same issue doesn't already exists
|
||||
* If you've already found the solution of the problem you are about to report, please feel free to open a new Pull Request. Then follow the rules below in **Developing Magallanes** section.
|
||||
* If you are able to, include some test cases or steps to reproduce the bug for us to examine the problem and find a solution.
|
||||
|
||||
## Opening Pull Requests
|
||||
Pull Request is a very powerful tool, so let's be measured in its usage. Always commit code which has at least 95% of coverage, and if it's a new feature always provide concrete tests.
|
||||
In order to have the PRs prioritized name them with the following tags.
|
||||
|
||||
```
|
||||
[#66] Add new CONTRIBUTING document
|
||||
[FIX] Set correct permissions on deploy stage
|
||||
[FEATURE] Create new PermissionsTask
|
||||
[HOTFIX] Exception not caught on deployment
|
||||
```
|
||||
All Pull Requests must be done to the `galactica` branch, only exception are Hotfixes.
|
||||
Remember of square brackets when adding issue number. If you'd forget adding them, your whole message will be a comment!
|
||||
|
||||
# Developing Magallanes
|
||||
## Branches
|
||||
The flow is pretty simple.
|
||||
In most common cases we work on the `galactica` branch. It's the branch with the main development for the current major version. All Pull Requests must merge with that branch. The `master` branch is used to move the validated code and generate the releases in an orderly fashion, also we could use it for hotfixes.
|
||||
|
||||
If you want to use developing branch in your code, simple pass `dev-galactica` to dependency version in your `composer.json` file:
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"andres-montanez/magallanes": "dev-galactica"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Organization and code quality
|
||||
We use [PSR-12](http://www.php-fig.org/psr/psr-12/) as PHP coding standard.
|
||||
|
||||
### Tools you can use to ensure your code quality
|
||||
|
||||
1. PHPStan `./vendor/bin/phpstan analyse`
|
||||
2. PHP Code Sniffer `./vendor/bin/phpcs`
|
||||
|
||||
## Testing and quality
|
||||
We use PHPUnit to test our code. Most of the project is covered with tests, so if you want your code to be merged push it with proper testing and coverage (at least 95%). To execute the tests with code coverage report:
|
||||
```bash
|
||||
./vendor/bin/phpunit --coverage-clover build/logs/coverage.xml --coverage-text
|
||||
./vendor/bin/php-coveralls -v --coverage_clover build/logs/coverage.xml
|
||||
```
|
||||
|
||||
Tests structure follow almost the same structure as production code with `Test` suffix in class and file name. Follow the tests already made as guidelines.
|
||||
|
||||
# Last Words
|
||||
Thank you for using Magallanes, and special thanks for making it better. When adding features always have in mind the main goal *Deploy code from A to B, and run some tasks*, and think if the feature is aiming at that.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2011 - 2013 Andrés Montañez
|
||||
Copyright (c) 2011 - 2022 Andrés Montañez
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
@ -20,4 +20,4 @@ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
|||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
class Mage_Autoload
|
||||
{
|
||||
public static function autoload($className)
|
||||
{
|
||||
$baseDir = dirname(dirname(__FILE__));
|
||||
$classFile = $baseDir . '/' . str_replace('_', '/', $className . '.php');
|
||||
require_once $classFile;
|
||||
}
|
||||
|
||||
public static function isLoadable($className)
|
||||
{
|
||||
$baseDir = dirname(dirname(__FILE__));
|
||||
$classFile = $baseDir . '/' . str_replace('_', '/', $className . '.php');
|
||||
return (file_exists($classFile) && is_readable($classFile));
|
||||
}
|
||||
|
||||
public static function loadUserTask($taskName)
|
||||
{
|
||||
$classFile = '.mage/tasks/' . ucfirst($taskName) . '.php';
|
||||
require_once $classFile;
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Add
|
||||
extends Mage_Command_CommandAbstract
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$subCommand = $this->getConfig()->getArgument(1);
|
||||
|
||||
try {
|
||||
switch ($subCommand) {
|
||||
case 'environment':
|
||||
$this->_environment();
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Mage_Console::output('<red>' . $e->getMessage() . '</red>', 1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
private function _environment()
|
||||
{
|
||||
$withReleases = $this->getConfig()->getParameter('enableReleases', false);
|
||||
$environmentName = strtolower($this->getConfig()->getParameter('name'));
|
||||
|
||||
if ($environmentName == '') {
|
||||
throw new Exception('You must specify a name for the environment.');
|
||||
}
|
||||
|
||||
$environmentConfigFile = '.mage/config/environment/' . $environmentName . '.yml';
|
||||
|
||||
if (file_exists($environmentConfigFile)) {
|
||||
throw new Exception('The environment already exists.');
|
||||
}
|
||||
|
||||
Mage_Console::output('Adding new environment: <dark_gray>' . $environmentName . '</dark_gray>');
|
||||
|
||||
$releasesConfig = 'releases:' . PHP_EOL
|
||||
. ' enabled: true' . PHP_EOL
|
||||
. ' max: 10' . PHP_EOL
|
||||
. ' symlink: current' . PHP_EOL
|
||||
. ' directory: releases' . PHP_EOL;
|
||||
|
||||
$baseConfig = '#' . $environmentName . PHP_EOL
|
||||
. 'deployment:' . PHP_EOL
|
||||
. ' user: dummy' . PHP_EOL
|
||||
. ' from: ./' . PHP_EOL
|
||||
. ' to: /var/www/vhosts/example.com/www' . PHP_EOL
|
||||
. ' excludes:' . PHP_EOL
|
||||
. ($withReleases ? $releasesConfig : '')
|
||||
. 'hosts:' . PHP_EOL
|
||||
. 'tasks:' . PHP_EOL
|
||||
. ' pre-deploy:' . PHP_EOL
|
||||
. ' on-deploy:' . PHP_EOL
|
||||
. ' - deployment/rsync' . PHP_EOL
|
||||
. ($withReleases ? (' post-release:' . PHP_EOL) : '')
|
||||
. ' post-deploy:' . PHP_EOL;
|
||||
|
||||
$result = file_put_contents($environmentConfigFile, $baseConfig);
|
||||
|
||||
if ($result) {
|
||||
Mage_Console::output('<light_green>Success!!</light_green> Environment config file for <dark_gray>' . $environmentName . '</dark_gray> created successfully at <blue>' . $environmentConfigFile . '</blue>');
|
||||
Mage_Console::output('<dark_gray>So please! Review and adjust its configuration.</dark_gray>', 2, 2);
|
||||
} else {
|
||||
Mage_Console::output('<light_red>Error!!</light_red> Unable to create config file for environment called <dark_gray>' . $environmentName . '</dark_gray>', 1, 2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Class Mage_Command_BuiltIn_Compile
|
||||
*
|
||||
* @author Ismael Ambrosi<ismaambrosi@gmail.com>
|
||||
*/
|
||||
class Mage_Command_BuiltIn_Compile
|
||||
extends Mage_Command_CommandAbstract
|
||||
{
|
||||
/**
|
||||
* @see Mage_Compile::compile()
|
||||
*/
|
||||
public function run ()
|
||||
{
|
||||
Mage_Console::output('Compiling <dark_gray>Magallanes</dark_gray>... ', 1, 0);
|
||||
|
||||
$compiler = new Mage_Compiler();
|
||||
$compiler->compile();
|
||||
|
||||
Mage_Console::output('Mage compiled successfully');
|
||||
}
|
||||
}
|
|
@ -1,276 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Deploy
|
||||
extends Mage_Command_CommandAbstract
|
||||
implements Mage_Command_RequiresEnvironment
|
||||
{
|
||||
private $_startTime = null;
|
||||
private $_startTimeHosts = null;
|
||||
private $_endTimeHosts = null;
|
||||
private $_hostsCount = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->getConfig()->setReleaseId(date('YmdHis'));
|
||||
$failedTasks = 0;
|
||||
|
||||
$this->_startTime = time();
|
||||
|
||||
$lockFile = '.mage/' . $this->getConfig()->getEnvironment() . '.lock';
|
||||
if (file_exists($lockFile)) {
|
||||
Mage_Console::output('<red>This environment is locked!</red>', 1, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Run Pre-Deployment Tasks
|
||||
$this->_runNonDeploymentTasks('pre-deploy', $this->getConfig(), 'Pre-Deployment');
|
||||
|
||||
// Run Tasks for Deployment
|
||||
$hosts = $this->getConfig()->getHosts();
|
||||
$this->_hostsCount = count($hosts);
|
||||
|
||||
if ($this->_hostsCount == 0) {
|
||||
Mage_Console::output('<light_purple>Warning!</light_purple> <dark_gray>No hosts defined, skipping deployment tasks.</dark_gray>', 1, 3);
|
||||
|
||||
} else {
|
||||
$this->_startTimeHosts = time();
|
||||
foreach ($hosts as $host) {
|
||||
$this->getConfig()->setHost($host);
|
||||
$tasks = 0;
|
||||
$completedTasks = 0;
|
||||
|
||||
Mage_Console::output('Deploying to <dark_gray>' . $this->getConfig()->getHost() . '</dark_gray>');
|
||||
|
||||
$tasksToRun = $this->getConfig()->getTasks();
|
||||
array_unshift($tasksToRun, 'deployment/rsync');
|
||||
|
||||
if (count($tasksToRun) == 0) {
|
||||
Mage_Console::output('<light_purple>Warning!</light_purple> <dark_gray>No </dark_gray><light_cyan>Deployment</light_cyan> <dark_gray>tasks defined.</dark_gray>', 2);
|
||||
Mage_Console::output('Deployment to <dark_gray>' . $host . '</dark_gray> skipped!', 1, 3);
|
||||
|
||||
} else {
|
||||
foreach ($tasksToRun as $taskData) {
|
||||
$tasks++;
|
||||
$task = Mage_Task_Factory::get($taskData, $this->getConfig(), false, 'deploy');
|
||||
|
||||
if ($this->_runTask($task)) {
|
||||
$completedTasks++;
|
||||
} else {
|
||||
$failedTasks++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($completedTasks == $tasks) {
|
||||
$tasksColor = 'green';
|
||||
} else {
|
||||
$tasksColor = 'red';
|
||||
}
|
||||
|
||||
Mage_Console::output('Deployment to <dark_gray>' . $this->getConfig()->getHost() . '</dark_gray> completed: <' . $tasksColor . '>' . $completedTasks . '/' . $tasks . '</' . $tasksColor . '> tasks done.', 1, 3);
|
||||
}
|
||||
}
|
||||
$this->_endTimeHosts = time();
|
||||
|
||||
if ($failedTasks > 0) {
|
||||
Mage_Console::output('A total of <dark_gray>' . $failedTasks . '</dark_gray> deployment tasks failed: <red>ABORTING</red>', 1, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Releasing
|
||||
if ($this->getConfig()->release('enabled', false) == true) {
|
||||
// Execute the Releases
|
||||
Mage_Console::output('Starting the <dark_gray>Releaseing</dark_gray>');
|
||||
foreach ($hosts as $host) {
|
||||
$this->getConfig()->setHost($host);
|
||||
$task = Mage_Task_Factory::get('deployment/release', $this->getConfig(), false, 'deploy');
|
||||
|
||||
if ($this->_runTask($task, 'Releasing on host <purple>' . $host . '</purple> ... ')) {
|
||||
$completedTasks++;
|
||||
}
|
||||
}
|
||||
Mage_Console::output('Finished the <dark_gray>Releaseing</dark_gray>', 1, 3);
|
||||
|
||||
// Execute the Post-Release Tasks
|
||||
foreach ($hosts as $host) {
|
||||
$this->getConfig()->setHost($host);
|
||||
$tasksToRun = $this->getConfig()->getTasks('post-release');
|
||||
$tasks = count($tasksToRun);
|
||||
$completedTasks = 0;
|
||||
|
||||
if (count($tasksToRun) > 0) {
|
||||
Mage_Console::output('Starting <dark_gray>Post-Release</dark_gray> tasks for <dark_gray>' . $host . '</dark_gray>:');
|
||||
|
||||
foreach ($tasksToRun as $task) {
|
||||
$task = Mage_Task_Factory::get($task, $this->getConfig(), false, 'post-release');
|
||||
|
||||
if ($this->_runTask($task)) {
|
||||
$completedTasks++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($completedTasks == $tasks) {
|
||||
$tasksColor = 'green';
|
||||
} else {
|
||||
$tasksColor = 'red';
|
||||
}
|
||||
Mage_Console::output('Finished <dark_gray>Post-Release</dark_gray> tasks for <dark_gray>' . $host . '</dark_gray>: <' . $tasksColor . '>' . $completedTasks . '/' . $tasks . '</' . $tasksColor . '> tasks done.', 1, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run Post-Deployment Tasks
|
||||
$this->_runNonDeploymentTasks('post-deploy', $this->getConfig(), 'Post-Deployment');
|
||||
|
||||
// Time Information Hosts
|
||||
if ($this->_hostsCount > 0) {
|
||||
$timeTextHost = $this->_transcurredTime($this->_endTimeHosts - $this->_startTimeHosts);
|
||||
Mage_Console::output('Time for deployment: <dark_gray>' . $timeTextHost . '</dark_gray>.');
|
||||
|
||||
$timeTextPerHost = $this->_transcurredTime(round(($this->_endTimeHosts - $this->_startTimeHosts) / $this->_hostsCount));
|
||||
Mage_Console::output('Average time per host: <dark_gray>' . $timeTextPerHost . '</dark_gray>.');
|
||||
}
|
||||
|
||||
// Time Information General
|
||||
$timeText = $this->_transcurredTime(time() - $this->_startTime);
|
||||
Mage_Console::output('Total time: <dark_gray>' . $timeText . '</dark_gray>.', 1, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute Pre and Post Deployment Tasks
|
||||
*
|
||||
* @param string $stage
|
||||
* @param Mage_Config $config
|
||||
* @param string $title
|
||||
*/
|
||||
private function _runNonDeploymentTasks($stage, Mage_Config $config, $title)
|
||||
{
|
||||
$tasksToRun = $config->getTasks($stage);
|
||||
|
||||
// PreDeployment Hook
|
||||
if ($stage == 'pre-deploy') {
|
||||
// Look for Remote Source
|
||||
if (is_array($this->_config->deployment('source', null))) {
|
||||
array_unshift($tasksToRun, 'scm/clone');
|
||||
}
|
||||
|
||||
// Change Branch
|
||||
if ($this->getConfig()->deployment('scm', false)) {
|
||||
array_unshift($tasksToRun, 'scm/change-branch');
|
||||
}
|
||||
}
|
||||
|
||||
// PostDeployment Hook
|
||||
if ($stage == 'post-deploy') {
|
||||
// Change Branch Back
|
||||
if ($this->getConfig()->deployment('scm', false)) {
|
||||
array_unshift($tasksToRun, 'scm/change-branch-back');
|
||||
}
|
||||
|
||||
// Remove Remote Source
|
||||
if (is_array($this->_config->deployment('source', null))) {
|
||||
array_push($tasksToRun, 'scm/remove-clone');
|
||||
}
|
||||
}
|
||||
|
||||
if (count($tasksToRun) == 0) {
|
||||
Mage_Console::output('<dark_gray>No </dark_gray><light_cyan>' . $title . '</light_cyan> <dark_gray>tasks defined.</dark_gray>', 1, 3);
|
||||
|
||||
} else {
|
||||
Mage_Console::output('Starting <dark_gray>' . $title . '</dark_gray> tasks:');
|
||||
|
||||
$tasks = 0;
|
||||
$completedTasks = 0;
|
||||
|
||||
foreach ($tasksToRun as $taskData) {
|
||||
$tasks++;
|
||||
$task = Mage_Task_Factory::get($taskData, $config, false, $stage);
|
||||
|
||||
if ($this->_runTask($task)) {
|
||||
$completedTasks++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($completedTasks == $tasks) {
|
||||
$tasksColor = 'green';
|
||||
} else {
|
||||
$tasksColor = 'red';
|
||||
}
|
||||
|
||||
Mage_Console::output('Finished <dark_gray>' . $title . '</dark_gray> tasks: <' . $tasksColor . '>' . $completedTasks . '/' . $tasks . '</' . $tasksColor . '> tasks done.', 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
private function _runTask($task, $title = null)
|
||||
{
|
||||
$task->init();
|
||||
|
||||
if ($title == null) {
|
||||
$title = 'Running <purple>' . $task->getName() . '</purple> ... ';
|
||||
}
|
||||
Mage_Console::output($title, 2, 0);
|
||||
|
||||
$runTask = true;
|
||||
if (($task instanceOf Mage_Task_Releases_SkipOnOverride) && $this->getConfig()->getParameter('overrideRelease', false)) {
|
||||
$runTask == false;
|
||||
}
|
||||
|
||||
$result = false;
|
||||
if ($runTask == true) {
|
||||
try {
|
||||
$result = $task->run();
|
||||
|
||||
if ($result == true) {
|
||||
Mage_Console::output('<green>OK</green>', 0);
|
||||
$result = true;
|
||||
|
||||
} else {
|
||||
Mage_Console::output('<red>FAIL</red>', 0);
|
||||
$result = false;
|
||||
}
|
||||
} catch (Mage_Task_SkipException $e) {
|
||||
Mage_Console::output('<yellow>SKIPPED</yellow>', 0);
|
||||
$result = true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
Mage_Console::output('<red>FAIL</red>', 0);
|
||||
$result = false;
|
||||
}
|
||||
} else {
|
||||
Mage_Console::output('<yellow>SKIPPED</yellow>', 0);
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Humanize Transcurred time
|
||||
* @param integer $time
|
||||
* @return string
|
||||
*/
|
||||
private function _transcurredTime($time)
|
||||
{
|
||||
$hours = floor($time / 3600);
|
||||
$minutes = floor(($time - ($hours * 3600)) / 60);
|
||||
$seconds = $time - ($minutes * 60) - ($hours * 3600);
|
||||
$timeText = array();
|
||||
|
||||
if ($hours > 0) {
|
||||
$timeText[] = $hours . ' hours';
|
||||
}
|
||||
|
||||
if ($minutes > 0) {
|
||||
$timeText[] = $minutes . ' minutes';
|
||||
}
|
||||
|
||||
if ($seconds > 0) {
|
||||
$timeText[] = $seconds . ' seconds';
|
||||
}
|
||||
|
||||
return implode(' ', $timeText);
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Init
|
||||
extends Mage_Command_CommandAbstract
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$configDir = '.mage';
|
||||
|
||||
Mage_Console::output('Initiating managing process for application with <dark_gray>Magallanes</dark_gray>');
|
||||
|
||||
// Check if there is already a config dir
|
||||
if (file_exists($configDir)) {
|
||||
Mage_Console::output('<light_red>Error!!</light_red> Already exists <dark_gray>.mage</dark_gray> directory.', 1, 2);
|
||||
} else {
|
||||
$results = array();
|
||||
$results[] = mkdir($configDir);
|
||||
$results[] = mkdir($configDir . '/logs');
|
||||
$results[] = mkdir($configDir . '/tasks');
|
||||
$results[] = mkdir($configDir . '/config');
|
||||
$results[] = mkdir($configDir . '/config/environment');
|
||||
$results[] = file_put_contents($configDir . '/config/general.yml', '#global settings' . PHP_EOL . PHP_EOL);
|
||||
$results[] = file_put_contents($configDir . '/config/scm.yml', '#scm settings' . PHP_EOL . PHP_EOL);
|
||||
|
||||
if (!in_array(false, $results)) {
|
||||
Mage_Console::output('<light_green>Success!!</light_green> The configuration for <dark_gray>Magallanes</dark_gray> has been generated at <blue>.mage</blue> directory.');
|
||||
Mage_Console::output('<dark_gray>Please!! Review and adjust the configuration.</dark_gray>', 2, 2);
|
||||
} else {
|
||||
Mage_Console::output('<light_red>Error!!</light_red> Unable to generate the configuration.', 1, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Install
|
||||
extends Mage_Command_CommandAbstract
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
Mage_Console::output('Installing <dark_gray>Magallanes</dark_gray>... ', 1, 0);
|
||||
|
||||
// Vars
|
||||
$installDir = $this->getConfig()->getParameter('installDir', '/opt/magallanes');
|
||||
$systemWide = $this->getConfig()->getParameter('systemWide', false);
|
||||
|
||||
// Clean vars
|
||||
$baseDir = realpath(dirname($installDir));
|
||||
$installDir = basename($installDir);
|
||||
|
||||
// Check if install dir is available
|
||||
if (!is_dir($baseDir) || !is_writable($baseDir)) {
|
||||
Mage_Console::output('<red>Failure: install directory is invalid.</red>', 0, 2);
|
||||
|
||||
// Chck if it is a system wide install the user is root
|
||||
} else if ($systemWide && (getenv('LOGNAME') != 'root')) {
|
||||
Mage_Console::output('<red>Failure: you have to be root to perform a system wide install.</red>', 0, 2);
|
||||
|
||||
} else {
|
||||
$destinationDir = $baseDir . '/' . $installDir;
|
||||
if (!is_dir($destinationDir)) {
|
||||
mkdir($destinationDir);
|
||||
}
|
||||
|
||||
// Copy
|
||||
$this->_recursiveCopy('./', $destinationDir . '/' . MAGALLANES_VERSION);
|
||||
|
||||
// Check if there is already a symlink
|
||||
if (file_exists($destinationDir . '/' . 'latest')
|
||||
&& is_link($destinationDir . '/' . 'latest')) {
|
||||
unlink($destinationDir . '/' . 'latest');
|
||||
}
|
||||
|
||||
// Create "latest" symlink
|
||||
symlink(
|
||||
$destinationDir . '/' . MAGALLANES_VERSION,
|
||||
$destinationDir . '/' . 'latest'
|
||||
);
|
||||
chmod($destinationDir . '/' . MAGALLANES_VERSION . '/bin/mage', 0755);
|
||||
|
||||
if ($systemWide) {
|
||||
if (!file_exists('/usr/bin/mage')) {
|
||||
symlink($destinationDir . '/latest/bin/mage', '/usr/bin/mage');
|
||||
}
|
||||
}
|
||||
|
||||
Mage_Console::output('<light_green>Success!</light_green>', 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
private function _recursiveCopy($from, $to)
|
||||
{
|
||||
if (is_dir($from)) {
|
||||
mkdir($to);
|
||||
$files = scandir($from);
|
||||
|
||||
if (count($files) > 0) {
|
||||
foreach ($files as $file) {
|
||||
if (strpos($file, '.') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir($from . DIRECTORY_SEPARATOR . $file)) {
|
||||
$this->_recursiveCopy(
|
||||
$from . DIRECTORY_SEPARATOR . $file,
|
||||
$to . DIRECTORY_SEPARATOR . $file
|
||||
);
|
||||
|
||||
} else {
|
||||
copy(
|
||||
$from . DIRECTORY_SEPARATOR . $file,
|
||||
$to . DIRECTORY_SEPARATOR . $file
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
} elseif (is_file($from)) {
|
||||
return copy($from, $to);
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Lock
|
||||
extends Mage_Command_CommandAbstract
|
||||
implements Mage_Command_RequiresEnvironment
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$lockFile = '.mage/' . $this->getConfig()->getEnvironment() . '.lock';
|
||||
file_put_contents($lockFile, 'Locked environment at date: ' . date('Y-m-d H:i:s'));
|
||||
|
||||
Mage_Console::output('Locked deployment to <light_purple>' . $this->getConfig()->getEnvironment() . '</light_purple> environment', 1, 2);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Releases
|
||||
extends Mage_Command_CommandAbstract
|
||||
implements Mage_Command_RequiresEnvironment
|
||||
{
|
||||
private $_release = null;
|
||||
|
||||
public function run()
|
||||
{
|
||||
$subcommand = $this->getConfig()->getArgument(1);
|
||||
$lockFile = '.mage/' . $this->getConfig()->getEnvironment() . '.lock';
|
||||
if (file_exists($lockFile) && ($subcommand == 'rollback')) {
|
||||
Mage_Console::output('<red>This environment is locked!</red>', 0, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Run Tasks for Deployment
|
||||
$hosts = $this->getConfig()->getHosts();
|
||||
|
||||
if (count($hosts) == 0) {
|
||||
Mage_Console::output('<light_purple>Warning!</light_purple> <dark_gray>No hosts defined, unable to get releases.</dark_gray>', 1, 3);
|
||||
|
||||
} else {
|
||||
foreach ($hosts as $host) {
|
||||
$this->getConfig()->setHost($host);
|
||||
|
||||
switch ($subcommand) {
|
||||
case 'list':
|
||||
$task = Mage_Task_Factory::get('releases/list', $this->getConfig());
|
||||
$task->init();
|
||||
$result = $task->run();
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
$releaseId = $this->getConfig()->getParameter('release', '');
|
||||
$task = Mage_Task_Factory::get('releases/rollback', $this->getConfig());
|
||||
$task->init();
|
||||
$task->setRelease($releaseId);
|
||||
$result = $task->run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Unlock
|
||||
extends Mage_Command_CommandAbstract
|
||||
implements Mage_Command_RequiresEnvironment
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$lockFile = '.mage/' . $this->getConfig()->getEnvironment() . '.lock';
|
||||
if (file_exists($lockFile)) {
|
||||
@unlink($lockFile);
|
||||
}
|
||||
|
||||
Mage_Console::output('Unlocked deployment to <light_purple>' . $this->getConfig()->getEnvironment() . '</light_purple> environment', 1, 2);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Update
|
||||
extends Mage_Command_CommandAbstract
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$task = Mage_Task_Factory::get('scm/update', $this->getConfig());
|
||||
$task->init();
|
||||
|
||||
Mage_Console::output('Updating application via ' . $task->getName() . ' ... ', 1, 0);
|
||||
$result = $task->run();
|
||||
|
||||
if ($result == true) {
|
||||
Mage_Console::output('<green>OK</green>' . PHP_EOL, 0);
|
||||
} else {
|
||||
Mage_Console::output('<red>FAIL</red>' . PHP_EOL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Upgrade
|
||||
extends Mage_Command_CommandAbstract
|
||||
{
|
||||
const DOWNLOAD = 'https://github.com/andres-montanez/Magallanes/tarball/stable';
|
||||
|
||||
public function run ()
|
||||
{
|
||||
Mage_Console::output('Upgrading <dark_gray>Magallanes</dark_gray> ... ', 1, 0);
|
||||
|
||||
$user = '';
|
||||
// Check if user is root
|
||||
Mage_Console::executeCommand('whoami', $user);
|
||||
if ($user != 'root') {
|
||||
Mage_Console::output('<red>FAIL</red>', 0, 1);
|
||||
Mage_Console::output('You need to be the <dark_gray>root</dark_gray> user to perform the upgrade.', 2);
|
||||
|
||||
} else {
|
||||
// Download Package
|
||||
$tarball = file_get_contents(self::DOWNLOAD);
|
||||
$tarballFile = tempnam('/tmp', 'magallanes_download');
|
||||
rename($tarballFile, $tarballFile . '.tar.gz');
|
||||
$tarballFile .= '.tar.gz';
|
||||
file_put_contents($tarballFile, $tarball);
|
||||
|
||||
// Unpackage
|
||||
if (file_exists('/tmp/__magallanesDownload')) {
|
||||
Mage_Console::executeCommand('rm -rf /tmp/__magallanesDownload');
|
||||
}
|
||||
Mage_Console::executeCommand('mkdir /tmp/__magallanesDownload');
|
||||
Mage_Console::executeCommand('cd /tmp/__magallanesDownload && tar xfz ' . $tarballFile);
|
||||
Mage_Console::executeCommand('rm -f ' . $tarballFile);
|
||||
|
||||
// Find Package
|
||||
$tarballDir = opendir('/tmp/__magallanesDownload');
|
||||
while (($file = readdir($tarballDir)) == true) {
|
||||
if ($file == '.' || $file == '..') {
|
||||
continue;
|
||||
} else {
|
||||
$packageDir = $file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get Version
|
||||
$version = false;
|
||||
if (file_exists('/tmp/__magallanesDownload/' . $packageDir . '/bin/mage')) {
|
||||
list(, $version) = file('/tmp/__magallanesDownload/' . $packageDir . '/bin/mage');
|
||||
$version = trim(str_replace('#VERSION:', '', $version));
|
||||
}
|
||||
|
||||
if ($version != false) {
|
||||
$versionCompare = version_compare(MAGALLANES_VERSION, $version);
|
||||
if ($versionCompare == 0) {
|
||||
Mage_Console::output('<yellow>SKIP</yellow>', 0, 1);
|
||||
Mage_Console::output('Your current version is up to date.', 2);
|
||||
|
||||
} else if ($versionCompare > 0) {
|
||||
Mage_Console::output('<yellow>SKIP</yellow>', 0, 1);
|
||||
Mage_Console::output('Your current version is newer.', 2);
|
||||
|
||||
} else {
|
||||
$this->_recursiveCopy('/tmp/__magallanesDownload/' . $packageDir, '/opt/magallanes-' . $version);
|
||||
unlink('/opt/magallanes');
|
||||
symlink('/opt/magallanes-' . $version, '/opt/magallanes');
|
||||
chmod('/opt/magallanes/bin/mage', 0755);
|
||||
|
||||
Mage_Console::output('<green>OK</green>', 0, 1);
|
||||
}
|
||||
|
||||
} else {
|
||||
Mage_Console::output('<red>FAIL</red>', 0, 1);
|
||||
Mage_Console::output('Corrupted download.', 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function _recursiveCopy ($from, $to)
|
||||
{
|
||||
if (is_dir($from)) {
|
||||
mkdir($to);
|
||||
$files = scandir($from);
|
||||
|
||||
if (count($files) > 0) {
|
||||
foreach ($files as $file) {
|
||||
if (strpos($file, '.') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir($from . DIRECTORY_SEPARATOR . $file)) {
|
||||
$this->_recursiveCopy(
|
||||
$from . DIRECTORY_SEPARATOR . $file,
|
||||
$to . DIRECTORY_SEPARATOR . $file
|
||||
);
|
||||
|
||||
} else {
|
||||
copy(
|
||||
$from . DIRECTORY_SEPARATOR . $file,
|
||||
$to . DIRECTORY_SEPARATOR . $file
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
} elseif (is_file($from)) {
|
||||
return copy($from, $to);
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_BuiltIn_Version
|
||||
extends Mage_Command_CommandAbstract
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
Mage_Console::output('Running <blue>Magallanes</blue> version <dark_gray>' . MAGALLANES_VERSION .'</dark_gray>', 0, 2);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
abstract class Mage_Command_CommandAbstract
|
||||
{
|
||||
protected $_config = null;
|
||||
|
||||
public abstract function run();
|
||||
|
||||
public function setConfig(Mage_Config $config)
|
||||
{
|
||||
$this->_config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Mage_Config
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->_config;
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
class Mage_Command_Factory
|
||||
{
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string $commandName
|
||||
* @param Mage_Config $config
|
||||
* @return Mage_Command_CommandAbstract
|
||||
*/
|
||||
public static function get($commandName, Mage_Config $config)
|
||||
{
|
||||
$instance = null;
|
||||
$commandName = ucwords(str_replace('-', ' ', $commandName));
|
||||
$commandName = str_replace(' ', '', $commandName);
|
||||
|
||||
// if (strpos($commandName, '/') === false) {
|
||||
// Mage_Autoload::loadUserTask($taskName);
|
||||
// $className = 'Task_' . ucfirst($taskName);
|
||||
// $instance = new $className($taskConfig, $inRollback, $stage);
|
||||
|
||||
// } else {
|
||||
$commandName = str_replace(' ', '_', ucwords(str_replace('/', ' ', $commandName)));
|
||||
$className = 'Mage_Command_BuiltIn_' . $commandName;
|
||||
if (Mage_Autoload::isLoadable($className)) {
|
||||
$instance = new $className;
|
||||
$instance->setConfig($config);
|
||||
} else {
|
||||
throw new Exception('Command not found.');
|
||||
}
|
||||
|
||||
// }
|
||||
|
||||
assert($instance instanceOf Mage_Command_CommandAbstract);
|
||||
return $instance;
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
interface Mage_Command_RequiresEnvironment
|
||||
{
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class Mage_Compiler
|
||||
*
|
||||
* Compiles the library into a .phar file
|
||||
*
|
||||
* @author Ismael Ambrosi<ismaambrosi@gmail.com>
|
||||
*/
|
||||
class Mage_Compiler
|
||||
{
|
||||
|
||||
/**
|
||||
* Compiles the library
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public function compile($file = 'mage.phar')
|
||||
{
|
||||
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
$phar = new Phar($file, 0, 'mage.phar');
|
||||
$phar->setSignatureAlgorithm(Phar::SHA1);
|
||||
|
||||
$phar->startBuffering();
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__), RecursiveIteratorIterator::CHILD_FIRST);
|
||||
/** @var $path SplFileInfo */
|
||||
foreach ($iterator as $path) {
|
||||
if ($path->isFile()) {
|
||||
$phar->addFromString(str_replace(dirname(__DIR__).'/', '', $path->getPathname()), file_get_contents($path));
|
||||
}
|
||||
}
|
||||
|
||||
$phar->addFromString('mage', str_replace(
|
||||
'$baseDir = dirname(dirname(__FILE__));',
|
||||
'$baseDir = __DIR__;',
|
||||
file_get_contents(__DIR__.'/../bin/mage.php')
|
||||
));
|
||||
|
||||
$phar->setStub("#!/usr/bin/env php\n<?php Phar::mapPhar('mage.phar'); require 'phar://mage.phar/mage'; __HALT_COMPILER();");
|
||||
|
||||
$phar->stopBuffering();
|
||||
|
||||
unset($phar);
|
||||
|
||||
chmod($file, 0755);
|
||||
}
|
||||
}
|
414
Mage/Config.php
414
Mage/Config.php
|
@ -1,414 +0,0 @@
|
|||
<?php
|
||||
class Mage_Config
|
||||
{
|
||||
private $_arguments = array();
|
||||
private $_parameters = array();
|
||||
private $_environment = false;
|
||||
private $_host = null;
|
||||
private $_releaseId = null;
|
||||
private $_config = array(
|
||||
'general' => array(),
|
||||
'scm' => array(),
|
||||
'environment' => array(),
|
||||
);
|
||||
|
||||
/**
|
||||
* Load the Configuration and parses the Arguments
|
||||
*
|
||||
* @param array $arguments
|
||||
*/
|
||||
public function load($arguments)
|
||||
{
|
||||
$this->_parse($arguments);
|
||||
$this->_loadGeneral();
|
||||
$this->_loadSCM();
|
||||
$this->_loadEnvironment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the invocation argument based on a position
|
||||
* 0 = Invoked Command Name
|
||||
*
|
||||
* @param integer $position
|
||||
* @return mixed
|
||||
*/
|
||||
public function getArgument($position = 0)
|
||||
{
|
||||
if (isset($this->_arguments[$position])) {
|
||||
return $this->_arguments[$position];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the invocation arguments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->_arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the a parameter
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParameter($name, $default = null, $extraParameters = array())
|
||||
{
|
||||
if (isset($this->_parameters[$name])) {
|
||||
return $this->_parameters[$name];
|
||||
} else if (isset($extraParameters[$name])) {
|
||||
return $extraParameters[$name];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the invocation arguments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->_parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Current environment
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEnvironment()
|
||||
{
|
||||
return $this->_environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the configuration
|
||||
*/
|
||||
public function reload()
|
||||
{
|
||||
$this->_loadGeneral();
|
||||
$this->_loadSCM();
|
||||
$this->_loadEnvironment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Tasks to execute
|
||||
*
|
||||
* @param string $stage
|
||||
* @return array
|
||||
*/
|
||||
public function getTasks($stage = 'on-deploy')
|
||||
{
|
||||
$tasks = array();
|
||||
$config = $this->_getEnvironmentOption('tasks', array());
|
||||
if (isset($config[$stage])) {
|
||||
$tasksData = ($config[$stage] ? (array) $config[$stage] : array());
|
||||
foreach ($tasksData as $taskName => $taskData) {
|
||||
if (is_array($taskData)) {
|
||||
;
|
||||
$tasks[] = array(
|
||||
'name' => key($taskData),
|
||||
'parameters' => current($taskData),
|
||||
);
|
||||
} else {
|
||||
$tasks[] = $taskData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Hosts to deploy
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHosts()
|
||||
{
|
||||
$hosts = array();
|
||||
|
||||
if (isset($this->_config['environment']['hosts'])) {
|
||||
if (is_array($this->_config['environment']['hosts'])) {
|
||||
$hosts = (array) $this->_config['environment']['hosts'];
|
||||
} else if (is_string($this->_config['environment']['hosts']) && file_exists($this->_config['environment']['hosts']) && is_readable($this->_config['environment']['hosts'])) {
|
||||
$fileContent = fopen($this->_config['environment']['hosts'], 'r');
|
||||
while (($host = fgets($fileContent)) == true) {
|
||||
$host = trim($host);
|
||||
if ($host != '') {
|
||||
$hosts[] = $host;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $hosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current host
|
||||
*
|
||||
* @param string $host
|
||||
* @return Mage_Config
|
||||
*/
|
||||
public function setHost($host)
|
||||
{
|
||||
$this->_host = $host;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current host name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHostName()
|
||||
{
|
||||
$info = explode(':', $this->_host);
|
||||
return $info[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Host Port
|
||||
*
|
||||
* @return unknown
|
||||
*/
|
||||
public function getHostPort()
|
||||
{
|
||||
$info = explode(':', $this->_host);
|
||||
$info[] = $this->deployment('port', '22');
|
||||
return $info[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Host
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->_host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets General Configuration
|
||||
*
|
||||
* @param string $option
|
||||
* @param string $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function general($option, $default = false)
|
||||
{
|
||||
$config = $this->_config['general'];
|
||||
if (isset($config[$option])) {
|
||||
if (is_array($default) && ($config[$option] == '')) {
|
||||
return $default;
|
||||
} else {
|
||||
return $config[$option];
|
||||
}
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets SCM Configuration
|
||||
*
|
||||
* @param string $option
|
||||
* @param string $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function scm($option, $default = false)
|
||||
{
|
||||
$config = $this->_config['scm'];
|
||||
if (isset($config[$option])) {
|
||||
if (is_array($default) && ($config[$option] == '')) {
|
||||
return $default;
|
||||
} else {
|
||||
return $config[$option];
|
||||
}
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get deployment configuration
|
||||
*
|
||||
* @param string $option
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function deployment($option, $default = false)
|
||||
{
|
||||
$config = $this->_getEnvironmentOption('deployment', array());
|
||||
if (isset($config[$option])) {
|
||||
if (is_array($default) && ($config[$option] == '')) {
|
||||
return $default;
|
||||
} else {
|
||||
return $config[$option];
|
||||
}
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Releaseing Options
|
||||
*
|
||||
* @param string $option
|
||||
* @param string $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function release($option, $default = false)
|
||||
{
|
||||
$config = $this->_getEnvironmentOption('releases', array());
|
||||
if (isset($config[$option])) {
|
||||
if (is_array($default) && ($config[$option] == '')) {
|
||||
return $default;
|
||||
} else {
|
||||
return $config[$option];
|
||||
}
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set From Deployment Path
|
||||
*
|
||||
* @param string $from
|
||||
* @return Mage_Config
|
||||
*/
|
||||
public function setFrom($from)
|
||||
{
|
||||
$this->_config['environment']['deployment']['from'] = $from;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Current Release ID
|
||||
*
|
||||
* @param integer $id
|
||||
* @return Mage_Config
|
||||
*/
|
||||
public function setReleaseId($id)
|
||||
{
|
||||
$this->_releaseId = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Current Release ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getReleaseId()
|
||||
{
|
||||
return $this->_releaseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the Command Line options
|
||||
* @return boolean
|
||||
*/
|
||||
private function _parse($arguments)
|
||||
{
|
||||
foreach ($arguments as $argument) {
|
||||
if (preg_match('/to:[\w]+/i', $argument)) {
|
||||
$this->_environment = str_replace('to:', '', $argument);
|
||||
|
||||
} else if (preg_match('/--[\w]+/i', $argument)) {
|
||||
$optionValue = explode('=', substr($argument, 2));
|
||||
if (count($optionValue) == 1) {
|
||||
$this->_parameters[$optionValue[0]] = true;
|
||||
} else if (count($optionValue) == 2) {
|
||||
if (strtolower($optionValue[1]) == 'true') {
|
||||
$this->_parameters[$optionValue[0]] = true;
|
||||
} else if (strtolower($optionValue[1]) == 'false') {
|
||||
$this->_parameters[$optionValue[0]] = false;
|
||||
} else {
|
||||
$this->_parameters[$optionValue[0]] = $optionValue[1];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->_arguments[] = $argument;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the General Configuration
|
||||
*/
|
||||
private function _loadGeneral()
|
||||
{
|
||||
if (file_exists('.mage/config/general.yml')) {
|
||||
$this->_config['general'] = spyc_load_file('.mage/config/general.yml');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the SCM Configuration
|
||||
*/
|
||||
private function _loadSCM()
|
||||
{
|
||||
if (file_exists('.mage/config/scm.yml')) {
|
||||
$this->_config['scm'] = spyc_load_file('.mage/config/scm.yml');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Environment configuration
|
||||
*
|
||||
* @throws Exception
|
||||
* @return boolean
|
||||
*/
|
||||
private function _loadEnvironment()
|
||||
{
|
||||
$environment = $this->getEnvironment();
|
||||
if (($environment != false) && file_exists('.mage/config/environment/' . $environment . '.yml')) {
|
||||
$this->_config['environment'] = spyc_load_file('.mage/config/environment/' . $environment . '.yml');
|
||||
|
||||
// Create temporal directory for clone
|
||||
if (isset($this->_config['environment']['deployment']['source']) && is_array($this->_config['environment']['deployment']['source'])) {
|
||||
if (trim($this->_config['environment']['deployment']['source']['temporal']) == '') {
|
||||
$this->_config['environment']['deployment']['source']['temporal'] = '/tmp';
|
||||
}
|
||||
$newTemporal = rtrim($this->_config['environment']['deployment']['source']['temporal'], '/')
|
||||
. '/' . md5(microtime()) . '/';
|
||||
$this->_config['environment']['deployment']['source']['temporal'] = $newTemporal;
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (($environment != '') && !file_exists('.mage/config/environment/' . $environment . '.yml')) {
|
||||
throw new Exception('Environment does not exists.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Environment root option
|
||||
*
|
||||
* @param string $option
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
private function _getEnvironmentOption($option, $default = array())
|
||||
{
|
||||
$config = $this->_config['environment'];
|
||||
if (isset($config[$option])) {
|
||||
return $config[$option];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
135
Mage/Console.php
135
Mage/Console.php
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
class Mage_Console
|
||||
{
|
||||
private static $_log = null;
|
||||
private static $_logEnabled = true;
|
||||
private static $_screenBuffer = '';
|
||||
private static $_commandsOutput = '';
|
||||
|
||||
/**
|
||||
* Runns a Magallanes Command
|
||||
* @throws Exception
|
||||
*/
|
||||
public function run($arguments)
|
||||
{
|
||||
$configError = false;
|
||||
try {
|
||||
// Load Config
|
||||
$config = new Mage_Config;
|
||||
$config->load($arguments);
|
||||
$configLoadedOk = true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$configError = $e->getMessage();
|
||||
}
|
||||
|
||||
// Command Option
|
||||
$commandName = $config->getArgument(0);
|
||||
|
||||
// Logging
|
||||
$showGrettings = true;
|
||||
if (in_array($commandName, array('install', 'upgrade', 'version'))) {
|
||||
self::$_logEnabled = false;
|
||||
$showGrettings = false;
|
||||
} else {
|
||||
self::$_logEnabled = $config->general('logging', false);
|
||||
}
|
||||
|
||||
// Grettings
|
||||
if ($showGrettings) {
|
||||
Mage_Console::output('Starting <blue>Magallanes</blue>', 0, 2);
|
||||
}
|
||||
|
||||
// Run Command
|
||||
if ($configError !== false) {
|
||||
Mage_Console::output('<red>' . $configError . '</red>', 1, 2);
|
||||
|
||||
} else {
|
||||
try {
|
||||
$command = Mage_Command_Factory::get($commandName, $config);
|
||||
|
||||
if ($command instanceOf Mage_Command_RequiresEnvironment) {
|
||||
if ($config->getEnvironment() == false) {
|
||||
throw new Exception('You must specify an environment for this command.');
|
||||
}
|
||||
}
|
||||
$command->run();
|
||||
|
||||
} catch (Exception $e) {
|
||||
Mage_Console::output('<red>' . $e->getMessage() . '</red>', 1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if ($showGrettings) {
|
||||
Mage_Console::output('Finished <blue>Magallanes</blue>', 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a message to the user screen
|
||||
*
|
||||
* @param string $message
|
||||
* @param integer $tabs
|
||||
* @param integer $newLine
|
||||
*/
|
||||
public static function output($message, $tabs = 1, $newLine = 1)
|
||||
{
|
||||
self::log(strip_tags($message));
|
||||
|
||||
self::$_screenBuffer .= str_repeat("\t", $tabs)
|
||||
. strip_tags($message)
|
||||
. str_repeat(PHP_EOL, $newLine);
|
||||
|
||||
$output = str_repeat("\t", $tabs)
|
||||
. Mage_Console_Colors::color($message)
|
||||
. str_repeat(PHP_EOL, $newLine);
|
||||
|
||||
echo $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Command on the Shell
|
||||
*
|
||||
* @param string $command
|
||||
* @param string $output
|
||||
* @return boolean
|
||||
*/
|
||||
public static function executeCommand($command, &$output = null)
|
||||
{
|
||||
self::log('---------------------------------');
|
||||
self::log('---- Executing: $ ' . $command);
|
||||
|
||||
$return = 1;
|
||||
$log = array();
|
||||
exec($command . ' 2>&1', $log, $return);
|
||||
$log = implode(PHP_EOL, $log);
|
||||
|
||||
if (!$return) {
|
||||
$output = trim($log);
|
||||
}
|
||||
self::$_commandsOutput .= PHP_EOL . trim($log) . PHP_EOL;
|
||||
|
||||
self::log($log);
|
||||
self::log('---------------------------------');
|
||||
|
||||
return !$return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message to the logfile.
|
||||
*
|
||||
* @param string $message
|
||||
* @param boolean $continuation
|
||||
*/
|
||||
public static function log($message, $continuation = false)
|
||||
{
|
||||
if (self::$_logEnabled) {
|
||||
if (self::$_log == null) {
|
||||
self::$_log = fopen('.mage/logs/log-' . date('Ymd-His') . '.log', 'w');
|
||||
}
|
||||
|
||||
$message = date('Y-m-d H:i:s -- ') . $message;
|
||||
fwrite(self::$_log, $message . PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
class Mage_Console_Colors
|
||||
{
|
||||
private static $foreground_colors = array(
|
||||
'black' => '0;30',
|
||||
'dark_gray' => '1;30',
|
||||
'blue' => '0;34',
|
||||
'light_blue' => '1;34',
|
||||
'green' => '0;32',
|
||||
'light_green' => '1;32',
|
||||
'cyan' => '0;36',
|
||||
'light_cyan' => '1;36',
|
||||
'red' => '0;31',
|
||||
'light_red' => '1;31',
|
||||
'purple' => '0;35',
|
||||
'light_purple' => '1;35',
|
||||
'brown' => '0;33',
|
||||
'yellow' => '1;33',
|
||||
'light_gray' => '0;37',
|
||||
'white' => '1;37'
|
||||
|
||||
);
|
||||
|
||||
// Returns colored string
|
||||
public static function color($string)
|
||||
{
|
||||
foreach (self::$foreground_colors as $key => $code) {
|
||||
$replaceFrom = array(
|
||||
'<' . $key . '>',
|
||||
'</' . $key . '>'
|
||||
);
|
||||
$replaceTo = array(
|
||||
"\033[" . $code . 'm',
|
||||
"\033[0m"
|
||||
);
|
||||
|
||||
$string = str_replace($replaceFrom, $replaceTo, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Deployment_Release
|
||||
extends Mage_Task_TaskAbstract
|
||||
implements Mage_Task_Releases_BuiltIn, Mage_Task_Releases_SkipOnOverride
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Releasing [built-in]';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
if ($this->getConfig()->release('enabled', false) == true) {
|
||||
$releasesDirectory = $this->getConfig()->release('directory', 'releases');
|
||||
$symlink = $this->getConfig()->release('symlink', 'current');
|
||||
|
||||
if (substr($symlink, 0, 1) == '/') {
|
||||
$releasesDirectory = rtrim($this->getConfig()->deployment('to'), '/') . '/' . $releasesDirectory;
|
||||
}
|
||||
|
||||
$currentCopy = $releasesDirectory . '/' . $this->getConfig()->getReleaseId();
|
||||
|
||||
// Fetch the user and group from base directory
|
||||
$userGroup = '33:33';
|
||||
$resultFetch = $this->_runRemoteCommand('ls -ld . | awk \'{print \$3":"\$4}\'', $userGroup);
|
||||
|
||||
// Remove symlink if exists; create new symlink and change owners
|
||||
$command = 'rm -f ' . $symlink
|
||||
. ' ; '
|
||||
. 'ln -sf ' . $currentCopy . ' ' . $symlink
|
||||
. ' && '
|
||||
. 'chown -h ' . $userGroup . ' ' . $symlink
|
||||
. ' && '
|
||||
. 'chown -R ' . $userGroup . ' ' . $currentCopy;
|
||||
$result = $this->_runRemoteCommand($command);
|
||||
|
||||
return $result;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Deployment_Rsync
|
||||
extends Mage_Task_TaskAbstract
|
||||
implements Mage_Task_Releases_BuiltIn
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
if ($this->getConfig()->release('enabled', false) == true) {
|
||||
if ($this->getConfig()->getParameter('overrideRelease', false) == true) {
|
||||
return 'Rsync (with Releases override) [built-in]';
|
||||
} else {
|
||||
return 'Rsync (with Releases) [built-in]';
|
||||
}
|
||||
} else {
|
||||
return 'Rsync [built-in]';
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$overrideRelease = $this->getConfig()->getParameter('overrideRelease', false);
|
||||
|
||||
if ($overrideRelease == true) {
|
||||
$releaseToOverride = false;
|
||||
$resultFetch = $this->_runRemoteCommand('ls -ld current | cut -d"/" -f2', $releaseToOverride);
|
||||
if (is_numeric($releaseToOverride)) {
|
||||
$this->getConfig()->setReleaseId($releaseToOverride);
|
||||
}
|
||||
}
|
||||
|
||||
$excludes = array(
|
||||
'.git',
|
||||
'.svn',
|
||||
'.mage',
|
||||
'.gitignore'
|
||||
);
|
||||
|
||||
// Look for User Excludes
|
||||
$userExcludes = $this->getConfig()->deployment('excludes', array());
|
||||
|
||||
// If we are working with releases
|
||||
$deployToDirectory = $this->getConfig()->deployment('to');
|
||||
if ($this->getConfig()->release('enabled', false) == true) {
|
||||
$releasesDirectory = $this->getConfig()->release('directory', 'releases');
|
||||
|
||||
$deployToDirectory = rtrim($this->getConfig()->deployment('to'), '/')
|
||||
. '/' . $releasesDirectory
|
||||
. '/' . $this->getConfig()->getReleaseId();
|
||||
$this->_runRemoteCommand('mkdir -p ' . $releasesDirectory . '/' . $this->getConfig()->getReleaseId());
|
||||
}
|
||||
|
||||
$command = 'rsync -avz '
|
||||
. '--rsh="ssh -p' . $this->getConfig()->getHostPort() . '" '
|
||||
. $this->_excludes(array_merge($excludes, $userExcludes)) . ' '
|
||||
. $this->getConfig()->deployment('from') . ' '
|
||||
. $this->getConfig()->deployment('user') . '@' . $this->getConfig()->getHostName() . ':' . $deployToDirectory;
|
||||
|
||||
$result = $this->_runLocalCommand($command);
|
||||
|
||||
// Count Releases
|
||||
if ($this->getConfig()->release('enabled', false) == true) {
|
||||
$releasesDirectory = $this->getConfig()->release('directory', 'releases');
|
||||
$symlink = $this->getConfig()->release('symlink', 'current');
|
||||
|
||||
if (substr($symlink, 0, 1) == '/') {
|
||||
$releasesDirectory = rtrim($this->getConfig()->deployment('to'), '/') . '/' . $releasesDirectory;
|
||||
}
|
||||
|
||||
$maxReleases = $this->getConfig()->release('max', false);
|
||||
if (($maxReleases !== false) && ($maxReleases > 0)) {
|
||||
$releasesList = '';
|
||||
$countReleasesFetch = $this->_runRemoteCommand('ls -1 ' . $releasesDirectory, $releasesList);
|
||||
$releasesList = trim($releasesList);
|
||||
|
||||
if ($releasesList != '') {
|
||||
$releasesList = explode(PHP_EOL, $releasesList);
|
||||
if (count($releasesList) > $maxReleases) {
|
||||
$releasesToDelete = array_diff($releasesList, array($this->getConfig()->getReleaseId()));
|
||||
sort($releasesToDelete);
|
||||
$releasesToDeleteCount = count($releasesToDelete) - $maxReleases;
|
||||
$releasesToDelete = array_slice($releasesToDelete, 0, $releasesToDeleteCount + 1);
|
||||
|
||||
foreach ($releasesToDelete as $releaseIdToDelete) {
|
||||
$directoryToDelete = $releasesDirectory . '/' . $releaseIdToDelete;
|
||||
if ($directoryToDelete != '/') {
|
||||
$command = 'rm -rf ' . $directoryToDelete;
|
||||
$result = $result && $this->_runRemoteCommand($command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function _excludes(Array $excludes)
|
||||
{
|
||||
$excludesRsync = '';
|
||||
foreach ($excludes as $exclude) {
|
||||
$excludesRsync .= ' --exclude ' . $exclude . ' ';
|
||||
}
|
||||
|
||||
$excludesRsync = trim($excludesRsync);
|
||||
return $excludesRsync;
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Releases_List
|
||||
extends Mage_Task_TaskAbstract
|
||||
implements Mage_Task_Releases_BuiltIn
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Listing releases [built-in]';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
if ($this->getConfig()->release('enabled', false) == true) {
|
||||
$releasesDirectory = $this->getConfig()->release('directory', 'releases');
|
||||
$symlink = $this->getConfig()->release('symlink', 'current');
|
||||
|
||||
Mage_Console::output('Releases available on <dark_gray>' . $this->getConfig()->getHost() . '</dark_gray>');
|
||||
|
||||
// Get Releases
|
||||
$output = '';
|
||||
$result = $this->_runRemoteCommand('ls -1 ' . $releasesDirectory, $output);
|
||||
$releases = ($output == '') ? array() : explode(PHP_EOL, $output);
|
||||
|
||||
// Get Current
|
||||
$result = $this->_runRemoteCommand('ls -l ' . $symlink, $output);
|
||||
$currentRelease = explode('/', $output);
|
||||
$currentRelease = trim(array_pop($currentRelease));
|
||||
|
||||
if (count($releases) == 0) {
|
||||
Mage_Console::output('<dark_gray>No releases available</dark_gray> ... ', 2);
|
||||
} else {
|
||||
rsort($releases);
|
||||
$releases = array_slice($releases, 0, 10);
|
||||
|
||||
foreach ($releases as $releaseIndex => $release) {
|
||||
$release = trim($release);
|
||||
$releaseIndex = str_pad($releaseIndex * -1, 2, ' ', STR_PAD_LEFT);
|
||||
$releaseDate = $release[0] . $release[1] . $release[2] .$release[3]
|
||||
. '-'
|
||||
. $release[4] . $release[5]
|
||||
. '-'
|
||||
. $release[6] . $release[7]
|
||||
. ' '
|
||||
. $release[8] . $release[9]
|
||||
. ':'
|
||||
. $release[10] . $release[11]
|
||||
. ':'
|
||||
. $release[12] . $release[13];
|
||||
|
||||
$isCurrent = '';
|
||||
if ($currentRelease == $release) {
|
||||
$isCurrent = ' <- current';
|
||||
}
|
||||
|
||||
$dateDiff = $this->_dateDiff($releaseDate);
|
||||
|
||||
Mage_Console::output(
|
||||
'Release: <purple>' . $release . '</purple> '
|
||||
. '- Date: <dark_gray>' . $releaseDate . '</dark_gray> '
|
||||
. '- Index: <dark_gray>' . $releaseIndex . '</dark_gray>' . $dateDiff . $isCurrent, 2);
|
||||
}
|
||||
}
|
||||
|
||||
Mage_Console::output('');
|
||||
return $result;
|
||||
|
||||
} else {
|
||||
Mage_Console::output('');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function _dateDiff($releaseDate)
|
||||
{
|
||||
$textDiff = '';
|
||||
$releaseDate = new DateTime($releaseDate);
|
||||
$now = new DateTime();
|
||||
$diff = $now->diff($releaseDate);
|
||||
|
||||
if ($diff->format('%a') <= 7) {
|
||||
if ($diff->format('%d') == 7) {
|
||||
$textDiff = ' [a week ago] ';
|
||||
|
||||
} else if ($diff->format('%d') > 0 && $diff->format('%d') < 7) {
|
||||
$days = $diff->format('%d');
|
||||
if ($days <= 1) {
|
||||
$textDiff = ' [one day ago] ';
|
||||
} else {
|
||||
$textDiff = ' [' . $days . ' days ago] ';
|
||||
}
|
||||
|
||||
} else if ($diff->format('%d') == 0 && $diff->format('%h') > 0) {
|
||||
$hours = $diff->format('%h');
|
||||
if ($hours <= 1) {
|
||||
$textDiff = ' [one hour ago] ';
|
||||
} else {
|
||||
$textDiff = ' [' . $hours . ' hours ago] ';
|
||||
}
|
||||
|
||||
} else if ($diff->format('%d') == 0 && $diff->format('%h') == 0) {
|
||||
$minutes = $diff->format('%i');
|
||||
if ($minutes <= 1) {
|
||||
$textDiff = ' [one minute ago] ';
|
||||
} else {
|
||||
$textDiff = ' [' . $minutes . ' minutes ago] ';
|
||||
}
|
||||
|
||||
} else if ($diff->format('%d') == 0 && $diff->format('%h') == 0 && $diff->format('%i') == 0) {
|
||||
$seconds = $diff->format('%s');
|
||||
if ($seconds < 10) {
|
||||
$textDiff = ' [just now!] ';
|
||||
} else {
|
||||
$textDiff = ' [' . $seconds . ' seconds ago] ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $textDiff;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Releases_Rollback
|
||||
extends Mage_Task_TaskAbstract
|
||||
implements Mage_Task_Releases_BuiltIn
|
||||
{
|
||||
private $_release = null;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'Rollback release [built-in]';
|
||||
}
|
||||
|
||||
public function setRelease($releaseId)
|
||||
{
|
||||
$this->_release = $releaseId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRelease()
|
||||
{
|
||||
return $this->_release;
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
if ($this->getConfig()->release('enabled', false) == true) {
|
||||
$releasesDirectory = $this->getConfig()->release('directory', 'releases');
|
||||
$symlink = $this->getConfig()->release('symlink', 'current');
|
||||
|
||||
$output = '';
|
||||
$result = $this->_runRemoteCommand('ls -1 ' . $releasesDirectory, $output);
|
||||
$releases = ($output == '') ? array() : explode(PHP_EOL, $output);
|
||||
|
||||
if (count($releases) == 0) {
|
||||
Mage_Console::output('Release are not available for <dark_gray>' . $this->getConfig()->getHost() . '</dark_gray> ... <red>FAIL</red>');
|
||||
|
||||
} else {
|
||||
rsort($releases);
|
||||
|
||||
$releaseIsAvailable = false;
|
||||
if ($this->getRelease() == '') {
|
||||
$releaseId = $releases[0];
|
||||
$releaseIsAvailable = true;
|
||||
|
||||
} else if ($this->getRelease() <= 0) {
|
||||
$index = $this->getRelease() * -1;
|
||||
if (isset($releases[$index])) {
|
||||
$releaseId = $releases[$index];
|
||||
$releaseIsAvailable = true;
|
||||
}
|
||||
} else {
|
||||
if (in_array($this->getRelease(), $releases)) {
|
||||
$releaseId = $this->getRelease();
|
||||
$releaseIsAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$releaseIsAvailable) {
|
||||
Mage_Console::output('Release <dark_gray>' . $this->getRelease() . '</dark_gray> is invalid or unavailable for <dark_gray>' . $this->getConfig()->getHost() . '</dark_gray> ... <red>FAIL</red>');
|
||||
|
||||
} else {
|
||||
Mage_Console::output('Rollback release on <dark_gray>' . $this->getConfig()->getHost() . '</dark_gray>');
|
||||
$rollbackTo = $releasesDirectory . '/' . $releaseId;
|
||||
|
||||
// Tasks
|
||||
$tasks = 1;
|
||||
$completedTasks = 0;
|
||||
$tasksToRun = $this->getConfig()->getTasks();
|
||||
$this->getConfig()->setReleaseId($releaseId);
|
||||
|
||||
if (count($tasksToRun) == 0) {
|
||||
Mage_Console::output('<light_purple>Warning!</light_purple> <dark_gray>No </dark_gray><light_cyan>Deployment</light_cyan> <dark_gray>tasks defined.</dark_gray>', 2);
|
||||
Mage_Console::output('Deployment to <dark_gray>' . $this->getConfig()->getHost() . '</dark_gray> skipped!', 1, 3);
|
||||
|
||||
} else {
|
||||
foreach ($tasksToRun as $taskData) {
|
||||
$task = Mage_Task_Factory::get($taskData, $this->getConfig(), true, 'deploy');
|
||||
$task->init();
|
||||
Mage_Console::output('Running <purple>' . $task->getName() . '</purple> ... ', 2, false);
|
||||
|
||||
if ($task instanceOf Mage_Task_Releases_RollbackAware) {
|
||||
$tasks++;
|
||||
$result = $task->run();
|
||||
|
||||
if ($result == true) {
|
||||
Mage_Console::output('<green>OK</green>', 0);
|
||||
$completedTasks++;
|
||||
} else {
|
||||
Mage_Console::output('<red>FAIL</red>', 0);
|
||||
}
|
||||
} else {
|
||||
Mage_Console::output('<yellow>SKIPPED</yellow>', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Changing Release
|
||||
Mage_Console::output('Running <purple>Rollback Release [id=' . $releaseId . ']</purple> ... ', 2, false);
|
||||
|
||||
$userGroup = '';
|
||||
$resultFetch = $this->_runRemoteCommand('ls -ld ' . $rollbackTo . ' | awk \'{print \$3":"\$4}\'', $userGroup);
|
||||
$command = 'rm -f ' . $symlink
|
||||
. ' && '
|
||||
. 'ln -sf ' . $rollbackTo . ' ' . $symlink
|
||||
. ' && '
|
||||
. 'chown -h ' . $userGroup . ' ' . $symlink;
|
||||
$result = $this->_runRemoteCommand($command);
|
||||
|
||||
if ($result) {
|
||||
Mage_Console::output('<green>OK</green>', 0);
|
||||
$completedTasks++;
|
||||
} else {
|
||||
Mage_Console::output('<red>FAIL</red>', 0);
|
||||
}
|
||||
|
||||
if ($completedTasks == $tasks) {
|
||||
$tasksColor = 'green';
|
||||
} else {
|
||||
$tasksColor = 'red';
|
||||
}
|
||||
|
||||
Mage_Console::output('Release rollback on <dark_gray>' . $this->getConfig()->getHost() . '</dark_gray> compted: <' . $tasksColor . '>' . $completedTasks . '/' . $tasks . '</' . $tasksColor . '> tasks done.', 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Scm_ChangeBranch
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
private $_name = 'SCM Changing branch [built-in]';
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
switch ($this->getConfig()->scm('type')) {
|
||||
case 'git':
|
||||
$this->_name = 'SCM Changing branch (GIT) [built-in]';
|
||||
break;
|
||||
|
||||
case 'svn':
|
||||
$this->_name = 'SCM Changing branch (Subversion) [built-in]';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
switch ($this->getConfig()->scm('type')) {
|
||||
case 'git':
|
||||
$command = 'git branch | grep \'*\' | cut -d\' \' -f 2';
|
||||
$currentBranch = 'master';
|
||||
$result = $this->_runLocalCommand($command, $currentBranch);
|
||||
|
||||
$scmData = $this->getConfig()->deployment('scm', false);
|
||||
if ($result && is_array($scmData) && isset($scmData['branch'])) {
|
||||
$branch = $this->getParameter('branch', $scmData['branch']);
|
||||
$command = 'git checkout ' . $branch;
|
||||
$result = $this->_runLocalCommand($command);
|
||||
|
||||
$oldBranchFile = '.mage/' . $this->getConfig()->getEnvironment() . '.oldBranch';
|
||||
file_put_contents($oldBranchFile, $currentBranch);
|
||||
|
||||
} else {
|
||||
throw new Mage_Task_SkipException;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$this->getConfig()->reload();
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Scm_ChangeBranchBack
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
private $_name = 'SCM Changing branch Back [built-in]';
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
switch ($this->getConfig()->scm('type')) {
|
||||
case 'git':
|
||||
$this->_name = 'SCM Changing branch Back (GIT) [built-in]';
|
||||
break;
|
||||
|
||||
case 'svn':
|
||||
$this->_name = 'SCM Changing branch Back (Subversion) [built-in]';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
switch ($this->getConfig()->scm('type')) {
|
||||
case 'git':
|
||||
$oldBranchFile = '.mage/' . $this->getConfig()->getEnvironment() . '.oldBranch';
|
||||
$currentBranch = trim(file_get_contents($oldBranchFile));
|
||||
|
||||
$command = 'git checkout ' . $currentBranch;
|
||||
$result = $this->_runLocalCommand($command);
|
||||
@unlink($oldBranchFile);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$this->getConfig()->reload();
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Scm_Clone
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
private $_name = 'SCM Clone [built-in]';
|
||||
private $_source = null;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->_source = $this->getConfig()->deployment('source');
|
||||
switch ($this->_source['type']) {
|
||||
case 'git':
|
||||
$this->_name = 'SCM Clone (GIT) [built-in]';
|
||||
break;
|
||||
|
||||
case 'svn':
|
||||
$this->_name = 'SCM Clone (Subversion) [built-in]';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->_runLocalCommand('mkdir -p ' . $this->_source['temporal']);
|
||||
switch ($this->_source['type']) {
|
||||
case 'git':
|
||||
// Clone Repo
|
||||
$command = 'cd ' . $this->_source['temporal'] . ' ; '
|
||||
. 'git clone ' . $this->_source['repository'] . ' . ';
|
||||
$result = $this->_runLocalCommand($command);
|
||||
|
||||
// Checkout Branch
|
||||
$command = 'cd ' . $this->_source['temporal'] . ' ; '
|
||||
. 'git checkout ' . $this->_source['from'];
|
||||
$result = $result && $this->_runLocalCommand($command);
|
||||
|
||||
$this->getConfig()->setFrom($this->_source['temporal']);
|
||||
break;
|
||||
|
||||
case 'svn':
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Scm_RemoveClone
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
private $_name = 'SCM Remove Clone [built-in]';
|
||||
private $_source = null;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->_source = $this->getConfig()->deployment('source');
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
return $this->_runLocalCommand('rm -rf ' . $this->_source['temporal']);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Scm_Update
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
private $_name = 'SCM Update [built-in]';
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
switch ($this->getConfig()->scm('type')) {
|
||||
case 'git':
|
||||
$this->_name = 'SCM Update (GIT) [built-in]';
|
||||
break;
|
||||
|
||||
case 'svn':
|
||||
$this->_name = 'SCM Update (Subversion) [built-in]';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
switch ($this->getConfig()->scm('type')) {
|
||||
case 'git':
|
||||
$command = 'git pull';
|
||||
break;
|
||||
|
||||
case 'svn':
|
||||
$command = 'svn update';
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$result = $this->_runLocalCommand($command);
|
||||
$this->getConfig()->reload();
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Symfony1_ClearCache
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Symfony v1 - Clear Cache [built-in]';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$command = 'symfony cc';
|
||||
$result = $this->_runLocalCommand($command);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Symfony1_Migrate
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Symfony v1 - Run Migrations [built-in]';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$command = 'symfony doctrine:migrate';
|
||||
$result = $this->_runLocalCommand($command);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Symfony2_CacheClear
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Symfony v2 - Cache Clear [built-in]';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$command = 'app/console cache:clear';
|
||||
$result = $this->_runLocalCommand($command);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Symfony2_CacheWarmup
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Symfony v2 - Cache Warmup [built-in]';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$command = 'app/console cache:warmup';
|
||||
$result = $this->_runLocalCommand($command);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_BuiltIn_Symfony2_Migrate
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Symfony v2 - Run Migrations [built-in]';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$command = 'app/console doctrine:migrations:migrate';
|
||||
$result = $this->_runLocalCommand($command);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_Factory
|
||||
{
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string|array $taskData
|
||||
* @param boolean $inRollback
|
||||
* @return Mage_Task_TaskAbstract
|
||||
*/
|
||||
public static function get($taskData, Mage_Config $taskConfig, $inRollback = false, $stage = null)
|
||||
{
|
||||
if (is_array($taskData)) {
|
||||
$taskName = $taskData['name'];
|
||||
$taskParameters = $taskData['parameters'];
|
||||
} else {
|
||||
$taskName = $taskData;
|
||||
$taskParameters = array();
|
||||
}
|
||||
|
||||
$instance = null;
|
||||
$taskName = ucwords(str_replace('-', ' ', $taskName));
|
||||
$taskName = str_replace(' ', '', $taskName);
|
||||
|
||||
if (strpos($taskName, '/') === false) {
|
||||
Mage_Autoload::loadUserTask($taskName);
|
||||
$className = 'Task_' . ucfirst($taskName);
|
||||
$instance = new $className($taskConfig, $inRollback, $stage, $taskParameters);
|
||||
|
||||
} else {
|
||||
$taskName = str_replace(' ', '_', ucwords(str_replace('/', ' ', $taskName)));
|
||||
$className = 'Mage_Task_BuiltIn_' . $taskName;
|
||||
$instance = new $className($taskConfig, $inRollback, $stage, $taskParameters);
|
||||
}
|
||||
|
||||
assert($instance instanceOf Mage_Task_TaskAbstract);
|
||||
return $instance;
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
interface Mage_Task_Releases_BuiltIn
|
||||
{
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
interface Mage_Task_Releases_RollbackAware
|
||||
{
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
interface Mage_Task_Releases_SkipOnOverride
|
||||
{
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
class Mage_Task_SkipException
|
||||
extends Exception
|
||||
{
|
||||
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
<?php
|
||||
abstract class Mage_Task_TaskAbstract
|
||||
{
|
||||
protected $_config = null;
|
||||
protected $_inRollback = false;
|
||||
protected $_stage = null;
|
||||
protected $_parameters = array();
|
||||
|
||||
public abstract function getName();
|
||||
|
||||
public abstract function run();
|
||||
|
||||
public final function __construct(Mage_Config $config, $inRollback = false, $stage = null, $parameters = array())
|
||||
{
|
||||
$this->_config = $config;
|
||||
$this->_inRollback = $inRollback;
|
||||
$this->_stage = $stage;
|
||||
$this->_parameters = $parameters;
|
||||
}
|
||||
|
||||
public function inRollback()
|
||||
{
|
||||
return $this->_inRollback;
|
||||
}
|
||||
|
||||
public function getStage()
|
||||
{
|
||||
return $this->_stage;
|
||||
}
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->_config;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the a parameter
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParameter($name, $default = null)
|
||||
{
|
||||
return $this->getConfig()->getParameter($name, $default, $this->_parameters);
|
||||
}
|
||||
|
||||
protected final function _runLocalCommand($command, &$output = null)
|
||||
{
|
||||
return Mage_Console::executeCommand($command, $output);
|
||||
}
|
||||
|
||||
protected final function _runRemoteCommand($command, &$output = null)
|
||||
{
|
||||
if ($this->_config->release('enabled', false) == true) {
|
||||
if ($this instanceOf Mage_Task_Releases_BuiltIn) {
|
||||
$releasesDirectory = '';
|
||||
|
||||
} else {
|
||||
$releasesDirectory = '/'
|
||||
. $this->_config->release('directory', 'releases')
|
||||
. '/'
|
||||
. $this->_config->getReleaseId();
|
||||
}
|
||||
|
||||
} else {
|
||||
$releasesDirectory = '';
|
||||
}
|
||||
|
||||
$localCommand = 'ssh -p ' . $this->_config->getHostPort() . ' '
|
||||
. '-q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no '
|
||||
. $this->_config->deployment('user') . '@' . $this->_config->getHostName() . ' '
|
||||
. '"cd ' . rtrim($this->_config->deployment('to'), '/') . $releasesDirectory . ' && '
|
||||
. str_replace('"', '\"', $command) . '"';
|
||||
|
||||
return $this->_runLocalCommand($localCommand, $output);
|
||||
}
|
||||
}
|
1046
Mage/spyc.php
1046
Mage/spyc.php
File diff suppressed because it is too large
Load diff
64
README.md
64
README.md
|
@ -1,47 +1,29 @@
|
|||
# Magallanes #
|
||||
# Magallanes
|
||||
![Linters](https://github.com/andres-montanez/Magallanes/actions/workflows/linters.yml/badge.svg?)
|
||||
![Tests](https://github.com/andres-montanez/Magallanes/actions/workflows/tests.yml/badge.svg?)
|
||||
[![Coverage Status](https://img.shields.io/coveralls/andres-montanez/Magallanes/master.svg)](https://coveralls.io/github/andres-montanez/Magallanes?branch=master)
|
||||
[![Code Quality](https://img.shields.io/scrutinizer/g/andres-montanez/Magallanes.svg)](https://scrutinizer-ci.com/g/andres-montanez/Magallanes/)
|
||||
[![Latest Stable Version](https://img.shields.io/packagist/v/andres-montanez/magallanes.svg?label=stable)](https://packagist.org/packages/andres-montanez/magallanes)
|
||||
[![Pre Release](https://img.shields.io/packagist/vpre/andres-montanez/magallanes.svg?label=dev)](https://packagist.org/packages/andres-montanez/magallanes)
|
||||
[![Total Downloads](https://img.shields.io/packagist/dt/andres-montanez/magallanes.svg)](https://packagist.org/packages/andres-montanez/magallanes)
|
||||
[![License](https://img.shields.io/packagist/l/andres-montanez/magallanes.svg)](https://packagist.org/packages/andres-montanez/magallanes)
|
||||
|
||||
### What's Magallanes? ###
|
||||
Magallanes is a deployment tool for PHP applications; it's quite simple to use and manage.
|
||||
It will get your application to a safe harbor.
|
||||
### What's Magallanes?
|
||||
**Magallanes** is a deployment tool for made with PHP for PHP applications; it's quite simple to use and manage. For more information and documentation please visit [magephp.com](https://www.magephp.com/)
|
||||
|
||||
### Installing
|
||||
Simply add the following dependency to your project’s composer.json file:
|
||||
|
||||
### So, What can it do? ###
|
||||
You can instruct Magallanes to deploy your code to all the servers you want (via rsync over ssh),
|
||||
and run tasks for that freshly deployed code.
|
||||
|
||||
|
||||
### Can you give me some examples/ideas? ###
|
||||
**Sure!**
|
||||
Suppose you have a checkout of your app and you have to deploy it to four servers;
|
||||
and after each deploy you have to run some boring tasks, like fixing file permissions, creating symlinks, etc.
|
||||
You can define all this on Magallanes and with *just one command* you can do all this at once!
|
||||
|
||||
Like this:
|
||||
```json
|
||||
"require-dev": {
|
||||
"andres-montanez/magallanes": "^5.0"
|
||||
}
|
||||
```
|
||||
$ mage deploy to:production
|
||||
Finally you can use **Magallanes** from the vendor's bin:
|
||||
|
||||
```bash
|
||||
vendor/bin/mage version
|
||||
```
|
||||
|
||||
|
||||
### What's this sorcery?! ###
|
||||
Easy boy. It's not sorcery, just some *technomagick*!
|
||||
|
||||
In Magallanes you define environments like *testing*, *staging*, or *production* like on the example above.
|
||||
Then, on that environment, you can configure a setup specifing to which hosts you want to deploy and what tasks to run (*after*, *on*, and *before* deploying).
|
||||
And you are done!
|
||||
|
||||
|
||||
### And what spells... tasks! What tasks has it built in? ###
|
||||
Just a few, for now...
|
||||
|
||||
- **deployment/rsync** - This task is for deploying your code to the remote servers.
|
||||
- **scm/update** - This task is for updating (git/svn) your base working copy.
|
||||
|
||||
But that's not all! You can create your own tasks, and execute commands on your working copy and in your deployed code!
|
||||
Also we are working on creating more built in tasks.
|
||||
|
||||
|
||||
### This is awesome! Where can I learn more? ###
|
||||
You can read the whole source code (naaah!); or checkout the documentation wiki at: http://magallanes.zenreworks.com/wiki
|
||||
|
||||
|
||||
Enjoy your magic trip with **Magallanes** to the land of the easily deployable apps!!
|
||||
### Codename Galactica
|
||||
Each new mayor version of **Magallanes** will have a codename (like Ubuntu), version 3 was _Nostromo_, version 4 was _Discovery One_, and in the current version it is **_Galactica_**, in homage to the space battleship from the TV series Battlestar Galactica, both the '70s and the mind blowing revision of 2005.
|
||||
|
|
27
bin/mage
27
bin/mage
|
@ -2,18 +2,19 @@
|
|||
<?php
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
$baseDir = dirname(dirname(__FILE__));
|
||||
if (file_exists(__DIR__ . '/../../../autoload.php')) {
|
||||
require __DIR__ . '/../../../autoload.php';
|
||||
} elseif (file_exists(__DIR__ . '/../vendor/autoload.php')) {
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
}
|
||||
|
||||
define('MAGALLANES_VERSION', '0.9.14');
|
||||
use Mage\MageApplication;
|
||||
|
||||
// Preload
|
||||
require_once $baseDir . '/Mage/spyc.php';
|
||||
require_once $baseDir . '/Mage/Autoload.php';
|
||||
spl_autoload_register(array('Mage_Autoload', 'autoload'));
|
||||
|
||||
// Clean arguments
|
||||
array_shift($argv);
|
||||
|
||||
// Run Magallanes
|
||||
$console = new Mage_Console;
|
||||
$console->run($argv);
|
||||
try {
|
||||
$file = sprintf('%s/.mage.yml', getcwd());
|
||||
$mage = new MageApplication($file);
|
||||
$mage->run();
|
||||
} catch (Exception $exception) {
|
||||
printf('Error: %s' . PHP_EOL, $exception->getMessage());
|
||||
exit(9);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,50 @@
|
|||
{
|
||||
"name": "andres-montanez/magallanes",
|
||||
"description": "A Deployment Tool for PHP Applications",
|
||||
"homepage": "http://magephp.com",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": "deployment",
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
"name": "andres-montanez/magallanes",
|
||||
"description": "The Deployment Tool for PHP Applications",
|
||||
"homepage": "https://magephp.com",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": ["deployment"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andrés Montañez",
|
||||
"email": "andresmontanez@gmail.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"monolog/monolog": "^2.5",
|
||||
"symfony/console": "^6.0",
|
||||
"symfony/filesystem": "^6.0",
|
||||
"symfony/event-dispatcher": "^6.0",
|
||||
"symfony/finder": "^6.0",
|
||||
"symfony/yaml": "^6.0",
|
||||
"symfony/process": "^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"phpstan/phpstan": "^1.5",
|
||||
"squizlabs/php_codesniffer": "^3.6",
|
||||
"php-coveralls/php-coveralls": "^2.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-posix": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mage\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Mage\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"bin": ["bin/mage"],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0.x-dev",
|
||||
"dev-galactica": "5.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
### List of Commands ###
|
||||
|
||||
# Installs Magallanes on the system
|
||||
sudo mage install
|
||||
|
||||
# Displays Magallanes version
|
||||
mage version
|
||||
|
||||
# Creats a Magallanes instance configuration
|
||||
mage init
|
||||
|
||||
# Creates a compiled version of Magallanes using phar
|
||||
mage compile
|
||||
|
||||
# Upgrades Magallanes itself
|
||||
mage upgrade
|
||||
|
||||
# Add a new Environment configuration
|
||||
mage add environment --name=production
|
||||
|
||||
# Add a new Environment configuration with releases enabled
|
||||
mage add environment --name=production --enableReleases
|
||||
|
||||
# Performs a SCM Update, if configured
|
||||
mage update
|
||||
|
||||
# Deploys Application to Production environment
|
||||
mage deploy to:production
|
||||
|
||||
# Deploys Application to Production environment, overriding the current release
|
||||
mage deploy to:production --overrideRelease
|
||||
|
||||
# Locks deployment to Production environment
|
||||
mage lock to:production
|
||||
|
||||
# Unlocks deployment to Production environment
|
||||
mage unlock to:production
|
||||
|
||||
# Lists all Releases on the Production environment
|
||||
mage releases list to:production
|
||||
|
||||
# Rollback to the last Release on the Production environment
|
||||
mage releases rollback to:production
|
||||
mage releases rollback --release=0 to:production
|
||||
|
||||
# Rollback to the first, second, or thrith Release before the current Release on the Production environment
|
||||
mage releases rollback --release=-1 to:production
|
||||
mage releases rollback --release=-2 to:production
|
||||
mage releases rollback --release=-3 to:production
|
||||
|
||||
# Rollback to a specific Release on the Production environment
|
||||
# mage releases rollback --release=20120101172148 to:production
|
||||
|
||||
|
||||
### List of UPCOMING Commands ###
|
||||
# mage config add host s05.example.com to:[production]
|
||||
# mage config git git://github.com/andres-montanez/Zend-Framework-Twig-example-app.git
|
||||
# mage config svn svn://example.com/repo
|
||||
# mage task:deployment/rsync to:production
|
7
docs/dockers/docker-compose.yml
Normal file
7
docs/dockers/docker-compose.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
version: '2'
|
||||
services:
|
||||
php8.0:
|
||||
container_name: mage-php8.0
|
||||
build: ./php8.0
|
||||
volumes:
|
||||
- ../../:/home/magephp
|
12
docs/dockers/php8.0/Dockerfile
Normal file
12
docs/dockers/php8.0/Dockerfile
Normal file
|
@ -0,0 +1,12 @@
|
|||
FROM ubuntu:21.10
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
TZ=UTC
|
||||
|
||||
RUN apt-get update && apt-get upgrade -y
|
||||
RUN apt-get install -y vim curl git unzip
|
||||
RUN apt-get install -y php8.0-cli php8.0-zip php8.0-curl php8.0-xml php8.0-mbstring php8.0-xdebug
|
||||
|
||||
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer
|
||||
|
||||
WORKDIR /home/magephp
|
29
docs/example-config.yml
Normal file
29
docs/example-config.yml
Normal file
|
@ -0,0 +1,29 @@
|
|||
magephp:
|
||||
environments:
|
||||
production:
|
||||
user: app
|
||||
branch: test
|
||||
from: ./
|
||||
host_path: /var/www/test
|
||||
releases: 4
|
||||
exclude:
|
||||
- var/cache
|
||||
- var/log
|
||||
hosts:
|
||||
- webserver1
|
||||
- webserver2
|
||||
- webserver3
|
||||
pre-deploy:
|
||||
- git/update
|
||||
- composer/install
|
||||
- composer/dump-autoload
|
||||
on-deploy:
|
||||
- symfony/cache-warmup: { env: 'prod' }
|
||||
- symfony/assets-install: { env: 'prod' }
|
||||
- symfony/assetic-dump: { env: 'prod' }
|
||||
on-release:
|
||||
post-release:
|
||||
post-deploy:
|
||||
- magic
|
||||
custom_tasks:
|
||||
- App\Deployment\MagicTask
|
|
@ -1,28 +0,0 @@
|
|||
#production
|
||||
deployment:
|
||||
user: root
|
||||
# from: ./
|
||||
source:
|
||||
type: git
|
||||
repository: git://github.com/andres-montanez/Zend-Framework-Twig-example-app.git
|
||||
from: production
|
||||
temporal: /tmp/myAppClone
|
||||
to: /var/www/vhosts/example.com/www
|
||||
excludes:
|
||||
- application/data/cache/twig/*
|
||||
releases:
|
||||
enabled: true
|
||||
max: 5
|
||||
symlink: current
|
||||
directory: releases
|
||||
hosts:
|
||||
- s01.example.com:22
|
||||
- s02.example.com
|
||||
tasks:
|
||||
pre-deploy:
|
||||
- scm/update
|
||||
on-deploy:
|
||||
- privileges
|
||||
- sampleTask
|
||||
- sampleTaskRollbackAware
|
||||
#post-deploy:
|
|
@ -1,30 +0,0 @@
|
|||
#staging
|
||||
deployment:
|
||||
user: root
|
||||
from: ./
|
||||
to: /var/www/
|
||||
scm:
|
||||
branch: master
|
||||
releases:
|
||||
enabled: true
|
||||
max: 5
|
||||
symlink: current
|
||||
directory: releases
|
||||
hosts:
|
||||
# - localhost
|
||||
# - dbserver
|
||||
tasks:
|
||||
pre-deploy:
|
||||
- sampleTask
|
||||
- scm/update
|
||||
on-deploy:
|
||||
- privileges
|
||||
- sampleTask
|
||||
- sampleTaskRollbackAware
|
||||
- taskWithParameters:
|
||||
booleanOption: true
|
||||
- taskWithParameters
|
||||
post-release:
|
||||
# - sampleTask
|
||||
post-deploy:
|
||||
- sampleTask
|
|
@ -1,11 +0,0 @@
|
|||
#general settings
|
||||
logging: true
|
||||
mail:
|
||||
enabled: true
|
||||
from: andresmontanez@gmail.com
|
||||
password: xxxxxx
|
||||
smtp: smtp.gmail.com
|
||||
to:
|
||||
- andresmontanez@gmail.com
|
||||
- andres.montanez@zenreworks.com
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#scm settings
|
||||
type: git
|
||||
uri: git://github.com/andres-montanez/Zend-Framework-Twig-example-app.git
|
1
docs/example-config/.mage/logs/.gitignore
vendored
1
docs/example-config/.mage/logs/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
log-*
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
class Task_Permissions
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Fixing file permissions';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$command = 'chmod 755 . -R';
|
||||
$result = $this->_runRemoteCommand($command);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
class Task_Privileges
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Fixing file privileges';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$command = 'chown 33:33 . -R';
|
||||
$result = $this->_runRemoteCommand($command);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
class Task_SampleTask
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'A Sample Task';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
class Task_SampleTaskRollbackAware
|
||||
extends Mage_Task_TaskAbstract
|
||||
implements Mage_Task_Releases_RollbackAware
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
if ($this->inRollback()) {
|
||||
return 'A Sample Task aware of rollbacks [in rollback]';
|
||||
} else {
|
||||
return 'A Sample Task aware of rollbacks [not in rollback]';
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
class Task_TaskWithParameters
|
||||
extends Mage_Task_TaskAbstract
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
$booleanOption = $this->getParameter('booleanOption', false);
|
||||
if ($booleanOption) {
|
||||
return 'A Sample Task With Parameters [booleanOption=true]';
|
||||
} else {
|
||||
return 'A Sample Task With Parameters [booleanOption=false]';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
//throw new Mage_Task_SkipException;
|
||||
//return false;
|
||||
return true;
|
||||
}
|
||||
}
|
14
phpcs.xml.dist
Normal file
14
phpcs.xml.dist
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
|
||||
|
||||
<arg name="basepath" value="."/>
|
||||
<arg name="cache" value=".phpcs-cache"/>
|
||||
<arg name="colors"/>
|
||||
<arg name="extensions" value="php"/>
|
||||
|
||||
<rule ref="PSR12"/>
|
||||
|
||||
<file>src/</file>
|
||||
</ruleset>
|
4
phpstan.neon
Normal file
4
phpstan.neon
Normal file
|
@ -0,0 +1,4 @@
|
|||
parameters:
|
||||
level: 6
|
||||
paths:
|
||||
- src
|
28
phpunit.xml.dist
Normal file
28
phpunit.xml.dist
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="vendor/autoload.php">
|
||||
<coverage>
|
||||
<include>
|
||||
<directory>./src/</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory>./tests/</directory>
|
||||
</exclude>
|
||||
</coverage>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1"/>
|
||||
<ini name="intl.default_locale" value="en"/>
|
||||
<ini name="intl.error_level" value="0"/>
|
||||
<ini name="memory_limit" value="-1"/>
|
||||
</php>
|
||||
<testsuites>
|
||||
<testsuite name="Magallanes Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>benchmark</group>
|
||||
<group>intl-data</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
</phpunit>
|
66
src/Command/AbstractCommand.php
Normal file
66
src/Command/AbstractCommand.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Command;
|
||||
|
||||
use Mage\MageApplication;
|
||||
use Mage\Utils;
|
||||
use Mage\Runtime\Runtime;
|
||||
use Psr\Log\LogLevel;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
/**
|
||||
* Abstract base class for Magallanes Commands
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
abstract class AbstractCommand extends Command
|
||||
{
|
||||
protected int $statusCode = 0;
|
||||
protected Runtime $runtime;
|
||||
|
||||
/**
|
||||
* Set the Runtime configuration
|
||||
*/
|
||||
public function setRuntime(Runtime $runtime): self
|
||||
{
|
||||
$this->runtime = $runtime;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message
|
||||
*/
|
||||
public function log(string $message, string $level = LogLevel::DEBUG): void
|
||||
{
|
||||
$this->runtime->log($message, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Human friendly Stage name
|
||||
*/
|
||||
protected function getStageName(): string
|
||||
{
|
||||
$utils = new Utils();
|
||||
return $utils->getStageName($this->runtime->getStage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires the configuration to be loaded
|
||||
*/
|
||||
protected function requireConfig(): void
|
||||
{
|
||||
$app = $this->getApplication();
|
||||
if ($app instanceof MageApplication) {
|
||||
$app->configure();
|
||||
}
|
||||
}
|
||||
}
|
52
src/Command/BuiltIn/Config/DumpCommand.php
Normal file
52
src/Command/BuiltIn/Config/DumpCommand.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Command\BuiltIn\Config;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Mage\Command\AbstractCommand;
|
||||
|
||||
/**
|
||||
* Command for Dumping the Configuration
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class DumpCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* Configure the Command
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('config:dump')
|
||||
->setDescription('Dumps the Magallanes configuration');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Command
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->requireConfig();
|
||||
|
||||
$output->writeln('Starting <fg=blue>Magallanes</>');
|
||||
$output->writeln('');
|
||||
|
||||
$output->writeln(sprintf('<comment>%s</comment>', var_export($this->runtime->getConfiguration(), true)));
|
||||
|
||||
$output->writeln('');
|
||||
$output->writeln('Finished <fg=blue>Magallanes</>');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
67
src/Command/BuiltIn/Config/EnvironmentsCommand.php
Normal file
67
src/Command/BuiltIn/Config/EnvironmentsCommand.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Command\BuiltIn\Config;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Mage\Command\AbstractCommand;
|
||||
|
||||
/**
|
||||
* Command for listing all the Environments
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class EnvironmentsCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* Configure the Command
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('config:environments')
|
||||
->setDescription('List all Magallanes configured Environments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Command
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->requireConfig();
|
||||
|
||||
$output->writeln('Starting <fg=blue>Magallanes</>');
|
||||
$output->writeln('');
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setHeaders(['Environment', 'User', 'Branch', 'Hosts']);
|
||||
|
||||
$configuration = $this->runtime->getConfigOption('environments');
|
||||
foreach ($configuration as $environment => $config) {
|
||||
$row = [$environment];
|
||||
|
||||
$row[] = (isset($config['user']) ? $config['user'] : '-');
|
||||
$row[] = (isset($config['branch']) ? $config['branch'] : '-');
|
||||
$row[] = (isset($config['hosts']) ? implode(PHP_EOL, $config['hosts']) : '-');
|
||||
|
||||
$table->addRow($row);
|
||||
}
|
||||
|
||||
$table->render();
|
||||
|
||||
$output->writeln('');
|
||||
$output->writeln('Finished <fg=blue>Magallanes</>');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
303
src/Command/BuiltIn/DeployCommand.php
Normal file
303
src/Command/BuiltIn/DeployCommand.php
Normal file
|
@ -0,0 +1,303 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Command\BuiltIn;
|
||||
|
||||
use Mage\Deploy\Strategy\StrategyInterface;
|
||||
use Mage\Runtime\Exception\RuntimeException;
|
||||
use Mage\Runtime\Runtime;
|
||||
use Mage\Task\ExecuteOnRollbackInterface;
|
||||
use Mage\Task\AbstractTask;
|
||||
use Mage\Task\Exception\ErrorException;
|
||||
use Mage\Task\Exception\SkipException;
|
||||
use Mage\Task\TaskFactory;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Mage\Command\AbstractCommand;
|
||||
|
||||
/**
|
||||
* The Deployment Command
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class DeployCommand extends AbstractCommand
|
||||
{
|
||||
protected TaskFactory $taskFactory;
|
||||
|
||||
/**
|
||||
* Configure the Command
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('deploy')
|
||||
->setDescription('Deploy code to hosts')
|
||||
->addArgument('environment', InputArgument::REQUIRED, 'Name of the environment to deploy to.')
|
||||
->addOption(
|
||||
'branch',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Force to switch to a branch other than the one defined.',
|
||||
false
|
||||
)
|
||||
->addOption(
|
||||
'tag',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Deploys a specific tag.',
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Command
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->requireConfig();
|
||||
|
||||
$output->writeln('Starting <fg=blue>Magallanes</>');
|
||||
$output->writeln('');
|
||||
|
||||
try {
|
||||
$this->runtime->setEnvironment($input->getArgument('environment'));
|
||||
|
||||
$strategy = $this->runtime->guessStrategy();
|
||||
$this->taskFactory = new TaskFactory($this->runtime);
|
||||
|
||||
$output->writeln(sprintf(' Environment: <fg=green>%s</>', $this->runtime->getEnvironment()));
|
||||
$this->log(sprintf('Environment: %s', $this->runtime->getEnvironment()));
|
||||
|
||||
if ($this->runtime->getEnvOption('releases', false)) {
|
||||
$this->runtime->generateReleaseId();
|
||||
$output->writeln(sprintf(' Release ID: <fg=green>%s</>', $this->runtime->getReleaseId()));
|
||||
$this->log(sprintf('Release ID: %s', $this->runtime->getReleaseId()));
|
||||
}
|
||||
|
||||
if ($this->runtime->getConfigOption('log_file', false)) {
|
||||
$output->writeln(sprintf(' Logfile: <fg=green>%s</>', $this->runtime->getConfigOption('log_file')));
|
||||
}
|
||||
|
||||
$output->writeln(sprintf(' Strategy: <fg=green>%s</>', $strategy->getName()));
|
||||
|
||||
if (($input->getOption('branch') !== false) && ($input->getOption('tag') !== false)) {
|
||||
throw new RuntimeException('Branch and Tag options are mutually exclusive.');
|
||||
}
|
||||
|
||||
if ($input->getOption('branch') !== false) {
|
||||
$this->runtime->setEnvOption('branch', $input->getOption('branch'));
|
||||
}
|
||||
|
||||
if ($input->getOption('tag') !== false) {
|
||||
$this->runtime->setEnvOption('branch', false);
|
||||
$this->runtime->setEnvOption('tag', $input->getOption('tag'));
|
||||
$output->writeln(sprintf(' Tag: <fg=green>%s</>', $this->runtime->getEnvOption('tag')));
|
||||
}
|
||||
|
||||
if ($this->runtime->getEnvOption('branch', false)) {
|
||||
$output->writeln(sprintf(' Branch: <fg=green>%s</>', $this->runtime->getEnvOption('branch')));
|
||||
}
|
||||
|
||||
|
||||
$output->writeln('');
|
||||
$this->runDeployment($output, $strategy);
|
||||
} catch (RuntimeException $exception) {
|
||||
$output->writeln('');
|
||||
$output->writeln(sprintf('<error>%s</error>', $exception->getMessage()));
|
||||
$output->writeln('');
|
||||
$this->statusCode = 7;
|
||||
}
|
||||
|
||||
$output->writeln('Finished <fg=blue>Magallanes</>');
|
||||
|
||||
return intval($this->statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Deployment Process
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function runDeployment(OutputInterface $output, StrategyInterface $strategy): void
|
||||
{
|
||||
// Run "Pre Deploy" Tasks
|
||||
$this->runtime->setStage(Runtime::PRE_DEPLOY);
|
||||
if (!$this->runTasks($output, $strategy->getPreDeployTasks())) {
|
||||
throw $this->getException();
|
||||
}
|
||||
|
||||
// Run "On Deploy" Tasks
|
||||
$this->runtime->setStage(Runtime::ON_DEPLOY);
|
||||
$this->runOnHosts($output, $strategy->getOnDeployTasks());
|
||||
|
||||
// Run "On Release" Tasks
|
||||
$this->runtime->setStage(Runtime::ON_RELEASE);
|
||||
$this->runOnHosts($output, $strategy->getOnReleaseTasks());
|
||||
|
||||
// Run "Post Release" Tasks
|
||||
$this->runtime->setStage(Runtime::POST_RELEASE);
|
||||
$this->runOnHosts($output, $strategy->getPostReleaseTasks());
|
||||
|
||||
// Run "Post Deploy" Tasks
|
||||
$this->runtime->setStage(Runtime::POST_DEPLOY);
|
||||
if (!$this->runTasks($output, $strategy->getPostDeployTasks())) {
|
||||
throw $this->getException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $tasks
|
||||
*/
|
||||
protected function runOnHosts(OutputInterface $output, array $tasks): void
|
||||
{
|
||||
$hosts = $this->runtime->getEnvOption('hosts');
|
||||
if (!is_array($hosts) && !$hosts instanceof \Countable) {
|
||||
$hosts = [];
|
||||
}
|
||||
|
||||
if (count($hosts) === 0) {
|
||||
$output->writeln(sprintf(' No hosts defined, skipping %s tasks', $this->getStageName()));
|
||||
$output->writeln('');
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($hosts as $host) {
|
||||
$this->runtime->setWorkingHost($host);
|
||||
if (!$this->runTasks($output, $tasks)) {
|
||||
$this->runtime->setWorkingHost(null);
|
||||
throw $this->getException();
|
||||
}
|
||||
$this->runtime->setWorkingHost(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all the tasks
|
||||
*
|
||||
* @param string[] $tasks
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function runTasks(OutputInterface $output, array $tasks): bool
|
||||
{
|
||||
if (count($tasks) == 0) {
|
||||
$output->writeln(
|
||||
sprintf(' No tasks defined for <fg=black;options=bold>%s</> stage', $this->getStageName())
|
||||
);
|
||||
$output->writeln('');
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->runtime->getHostName() !== null) {
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
' Starting <fg=black;options=bold>%s</> tasks on host <fg=black;options=bold>%s</>:',
|
||||
$this->getStageName(),
|
||||
$this->runtime->getHostName()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$output->writeln(sprintf(' Starting <fg=black;options=bold>%s</> tasks:', $this->getStageName()));
|
||||
}
|
||||
|
||||
$totalTasks = count($tasks);
|
||||
$succeededTasks = 0;
|
||||
|
||||
foreach ($tasks as $taskName) {
|
||||
$task = $this->taskFactory->get($taskName);
|
||||
$output->write(sprintf(' Running <fg=magenta>%s</> ... ', $task->getDescription()));
|
||||
$this->log(sprintf('Running task %s (%s)', $task->getDescription(), $task->getName()));
|
||||
|
||||
if ($this->runtime->inRollback() && !$task instanceof ExecuteOnRollbackInterface) {
|
||||
$succeededTasks++;
|
||||
$output->writeln('<fg=yellow>SKIPPED</>');
|
||||
$this->log(
|
||||
sprintf(
|
||||
'Task %s (%s) finished with SKIPPED, it was in a Rollback',
|
||||
$task->getDescription(),
|
||||
$task->getName()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
if ($task->execute()) {
|
||||
$succeededTasks++;
|
||||
$output->writeln('<fg=green>OK</>');
|
||||
$this->log(
|
||||
sprintf('Task %s (%s) finished with OK', $task->getDescription(), $task->getName())
|
||||
);
|
||||
} else {
|
||||
$output->writeln('<fg=red>FAIL</>');
|
||||
$this->statusCode = 180;
|
||||
$this->log(
|
||||
sprintf('Task %s (%s) finished with FAIL', $task->getDescription(), $task->getName())
|
||||
);
|
||||
}
|
||||
} catch (SkipException $exception) {
|
||||
$succeededTasks++;
|
||||
$output->writeln('<fg=yellow>SKIPPED</>');
|
||||
$this->log(
|
||||
sprintf(
|
||||
'Task %s (%s) finished with SKIPPED, thrown SkipException',
|
||||
$task->getDescription(),
|
||||
$task->getName()
|
||||
)
|
||||
);
|
||||
} catch (ErrorException $exception) {
|
||||
$output->writeln(sprintf('<fg=red>ERROR</> [%s]', $exception->getTrimmedMessage()));
|
||||
$this->log(
|
||||
sprintf(
|
||||
'Task %s (%s) finished with FAIL, with Error "%s"',
|
||||
$task->getDescription(),
|
||||
$task->getName(),
|
||||
$exception->getMessage()
|
||||
)
|
||||
);
|
||||
$this->statusCode = 190;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->statusCode !== 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$alertColor = 'red';
|
||||
if ($succeededTasks == $totalTasks) {
|
||||
$alertColor = 'green';
|
||||
}
|
||||
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
' Finished <fg=%s>%d/%d</> tasks for <fg=black;options=bold>%s</>.',
|
||||
$alertColor,
|
||||
$succeededTasks,
|
||||
$totalTasks,
|
||||
$this->getStageName()
|
||||
)
|
||||
);
|
||||
$output->writeln('');
|
||||
|
||||
return ($succeededTasks == $totalTasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception for halting the the current process
|
||||
*/
|
||||
protected function getException(): RuntimeException
|
||||
{
|
||||
return new RuntimeException(
|
||||
sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()),
|
||||
50
|
||||
);
|
||||
}
|
||||
}
|
148
src/Command/BuiltIn/Releases/ListCommand.php
Normal file
148
src/Command/BuiltIn/Releases/ListCommand.php
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Command\BuiltIn\Releases;
|
||||
|
||||
use Mage\Utils;
|
||||
use Mage\Runtime\Exception\RuntimeException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Mage\Command\AbstractCommand;
|
||||
|
||||
/**
|
||||
* Command for Listing all Releases
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class ListCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* Configure the Command
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('releases:list')
|
||||
->setDescription('List the releases on an environment')
|
||||
->addArgument('environment', InputArgument::REQUIRED, 'Name of the environment to deploy to');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Command
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->requireConfig();
|
||||
|
||||
$utils = new Utils();
|
||||
$output->writeln('Starting <fg=blue>Magallanes</>');
|
||||
$output->writeln('');
|
||||
|
||||
try {
|
||||
$this->runtime->setEnvironment($input->getArgument('environment'));
|
||||
|
||||
if (!$this->runtime->getEnvOption('releases', false)) {
|
||||
throw new RuntimeException('Releases are not enabled', 70);
|
||||
}
|
||||
|
||||
$output->writeln(sprintf(' Environment: <fg=green>%s</>', $this->runtime->getEnvironment()));
|
||||
$this->log(sprintf('Environment: %s', $this->runtime->getEnvironment()));
|
||||
|
||||
if ($this->runtime->getConfigOption('log_file', false)) {
|
||||
$output->writeln(sprintf(' Logfile: <fg=green>%s</>', $this->runtime->getConfigOption('log_file')));
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
|
||||
$hosts = $this->runtime->getEnvOption('hosts');
|
||||
if (!is_array($hosts) && !$hosts instanceof \Countable) {
|
||||
$hosts = [];
|
||||
}
|
||||
if (count($hosts) == 0) {
|
||||
$output->writeln('No hosts defined');
|
||||
$output->writeln('');
|
||||
} else {
|
||||
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
|
||||
|
||||
foreach ($hosts as $host) {
|
||||
$this->runtime->setWorkingHost($host);
|
||||
|
||||
// Get List of Releases
|
||||
$cmdListReleases = sprintf('ls -1 %s/releases', $hostPath);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runRemoteCommand($cmdListReleases, false);
|
||||
if (!$process->isSuccessful()) {
|
||||
throw new RuntimeException(sprintf('Unable to retrieve releases from host "%s"', $host), 80);
|
||||
}
|
||||
|
||||
$releases = [];
|
||||
if (trim($process->getOutput()) != '') {
|
||||
$releases = explode("\n", trim($process->getOutput()));
|
||||
rsort($releases);
|
||||
}
|
||||
|
||||
if (count($releases) == 0) {
|
||||
$output->writeln(
|
||||
sprintf(' No releases available on host <fg=black;options=bold>%s</>:', $host)
|
||||
);
|
||||
} else {
|
||||
// Get Current Release
|
||||
$cmdCurrentRelease = sprintf('readlink -f %s/current', $hostPath);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runRemoteCommand($cmdCurrentRelease, false);
|
||||
if (!$process->isSuccessful()) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Unable to retrieve current release from host "%s"', $host),
|
||||
85
|
||||
);
|
||||
}
|
||||
|
||||
$currentReleaseId = explode('/', trim($process->getOutput()));
|
||||
$currentReleaseId = $currentReleaseId[count($currentReleaseId) - 1];
|
||||
|
||||
$output->writeln(sprintf(' Releases on host <fg=black;options=bold>%s</>:', $host));
|
||||
|
||||
foreach ($releases as $releaseId) {
|
||||
$releaseDate = $utils->getReleaseDate($releaseId);
|
||||
|
||||
$output->write(sprintf(
|
||||
' Release ID: <fg=magenta>%s</> - Date: <fg=black;options=bold>%s</> [%s]',
|
||||
$releaseId,
|
||||
$releaseDate->format('Y-m-d H:i:s'),
|
||||
$utils->getTimeDiff($releaseDate)
|
||||
));
|
||||
|
||||
if ($releaseId == $currentReleaseId) {
|
||||
$output->writeln(' <fg=red;options=bold>[current]</>');
|
||||
} else {
|
||||
$output->writeln('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->runtime->setWorkingHost(null);
|
||||
$output->writeln('');
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException $exception) {
|
||||
$output->writeln(sprintf('<error>%s</error>', $exception->getMessage()));
|
||||
$this->statusCode = $exception->getCode();
|
||||
}
|
||||
|
||||
$output->writeln('Finished <fg=blue>Magallanes</>');
|
||||
|
||||
return intval($this->statusCode);
|
||||
}
|
||||
}
|
131
src/Command/BuiltIn/Releases/RollbackCommand.php
Normal file
131
src/Command/BuiltIn/Releases/RollbackCommand.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Command\BuiltIn\Releases;
|
||||
|
||||
use Mage\Task\TaskFactory;
|
||||
use Mage\Runtime\Exception\RuntimeException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Mage\Command\BuiltIn\DeployCommand;
|
||||
|
||||
/**
|
||||
* Command for Rolling Back a Releases
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class RollbackCommand extends DeployCommand
|
||||
{
|
||||
/**
|
||||
* Configure the Command
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('releases:rollback')
|
||||
->setDescription('Rollback to a release on an environment')
|
||||
->addArgument('environment', InputArgument::REQUIRED, 'Name of the environment to deploy to')
|
||||
->addArgument('release', InputArgument::REQUIRED, 'The ID or the Index of the release to rollback to');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Command
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->requireConfig();
|
||||
|
||||
$output->writeln('Starting <fg=blue>Magallanes</>');
|
||||
$output->writeln('');
|
||||
|
||||
try {
|
||||
$this->runtime->setEnvironment($input->getArgument('environment'));
|
||||
|
||||
$strategy = $this->runtime->guessStrategy();
|
||||
$this->taskFactory = new TaskFactory($this->runtime);
|
||||
|
||||
if (!$this->runtime->getEnvOption('releases', false)) {
|
||||
throw new RuntimeException('Releases are not enabled', 70);
|
||||
}
|
||||
|
||||
$releaseToRollback = $input->getArgument('release');
|
||||
if ($this->checkReleaseAvailability($releaseToRollback) === false) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Release "%s" is not available on all hosts', $releaseToRollback),
|
||||
72
|
||||
);
|
||||
}
|
||||
|
||||
$this->runtime->setReleaseId($releaseToRollback)->setRollback(true);
|
||||
|
||||
$output->writeln(sprintf(' Environment: <fg=green>%s</>', $this->runtime->getEnvironment()));
|
||||
$this->log(sprintf('Environment: %s', $this->runtime->getEnvironment()));
|
||||
|
||||
$output->writeln(sprintf(' Rollback to Release Id: <fg=green>%s</>', $this->runtime->getReleaseId()));
|
||||
$this->log(sprintf('Release ID: %s', $this->runtime->getReleaseId()));
|
||||
|
||||
if ($this->runtime->getConfigOption('log_file', false)) {
|
||||
$output->writeln(sprintf(' Logfile: <fg=green>%s</>', $this->runtime->getConfigOption('log_file')));
|
||||
}
|
||||
|
||||
$output->writeln(sprintf(' Strategy: <fg=green>%s</>', $strategy->getName()));
|
||||
|
||||
$output->writeln('');
|
||||
$this->runDeployment($output, $strategy);
|
||||
} catch (RuntimeException $exception) {
|
||||
$output->writeln(sprintf('<error>%s</error>', $exception->getMessage()));
|
||||
$this->statusCode = $exception->getCode();
|
||||
}
|
||||
|
||||
$output->writeln('Finished <fg=blue>Magallanes</>');
|
||||
|
||||
return intval($this->statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the provided Release ID is available in all hosts
|
||||
*/
|
||||
protected function checkReleaseAvailability(string $releaseToRollback): bool
|
||||
{
|
||||
$hosts = $this->runtime->getEnvOption('hosts');
|
||||
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
|
||||
|
||||
$availableInHosts = 0;
|
||||
foreach ($hosts as $host) {
|
||||
$releases = [];
|
||||
$this->runtime->setWorkingHost($host);
|
||||
|
||||
// Get List of Releases
|
||||
$cmdListReleases = sprintf('ls -1 %s/releases', $hostPath);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runRemoteCommand($cmdListReleases, false);
|
||||
if ($process->isSuccessful()) {
|
||||
$releases = explode("\n", trim($process->getOutput()));
|
||||
rsort($releases);
|
||||
}
|
||||
|
||||
if (in_array($releaseToRollback, $releases)) {
|
||||
$availableInHosts++;
|
||||
}
|
||||
|
||||
$this->runtime->setWorkingHost(null);
|
||||
}
|
||||
|
||||
if ($availableInHosts === count($hosts)) {
|
||||
return (bool) $releaseToRollback;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
45
src/Command/BuiltIn/VersionCommand.php
Normal file
45
src/Command/BuiltIn/VersionCommand.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Command\BuiltIn;
|
||||
|
||||
use Mage\Mage;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Mage\Command\AbstractCommand;
|
||||
|
||||
/**
|
||||
* Version Command, return the current version of Magallanes
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class VersionCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* Configure the Command
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setName('version')
|
||||
->setDescription('Get the version of Magallanes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the Command
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln(sprintf('Magallanes v%s [%s]', Mage::VERSION, Mage::CODENAME));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
129
src/Deploy/Strategy/ReleasesStrategy.php
Normal file
129
src/Deploy/Strategy/ReleasesStrategy.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Deploy\Strategy;
|
||||
|
||||
use Mage\Runtime\Exception\RuntimeException;
|
||||
use Mage\Runtime\Runtime;
|
||||
|
||||
/**
|
||||
* Strategy for Deployment with Releases, using Tar and SCP
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class ReleasesStrategy implements StrategyInterface
|
||||
{
|
||||
protected Runtime $runtime;
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Releases';
|
||||
}
|
||||
|
||||
public function setRuntime(Runtime $runtime): void
|
||||
{
|
||||
$this->runtime = $runtime;
|
||||
}
|
||||
|
||||
public function getPreDeployTasks(): array
|
||||
{
|
||||
$this->checkStage(Runtime::PRE_DEPLOY);
|
||||
$tasks = $this->runtime->getTasks();
|
||||
|
||||
if (
|
||||
($this->runtime->getBranch() || $this->runtime->getTag()) &&
|
||||
!$this->runtime->inRollback() &&
|
||||
!in_array('git/change-branch', $tasks)
|
||||
) {
|
||||
array_unshift($tasks, 'git/change-branch');
|
||||
}
|
||||
|
||||
if (!$this->runtime->inRollback() && !in_array('deploy/tar/prepare', $tasks)) {
|
||||
array_push($tasks, 'deploy/tar/prepare');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
public function getOnDeployTasks(): array
|
||||
{
|
||||
$this->checkStage(Runtime::ON_DEPLOY);
|
||||
$tasks = $this->runtime->getTasks();
|
||||
|
||||
if (!$this->runtime->inRollback() && !in_array('deploy/tar/copy', $tasks)) {
|
||||
array_unshift($tasks, 'deploy/tar/copy');
|
||||
}
|
||||
|
||||
if (!$this->runtime->inRollback() && !in_array('deploy/release/prepare', $tasks)) {
|
||||
array_unshift($tasks, 'deploy/release/prepare');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
public function getOnReleaseTasks(): array
|
||||
{
|
||||
$this->checkStage(Runtime::ON_RELEASE);
|
||||
$tasks = $this->runtime->getTasks();
|
||||
|
||||
if (!in_array('deploy/release', $tasks)) {
|
||||
array_unshift($tasks, 'deploy/release');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
public function getPostReleaseTasks(): array
|
||||
{
|
||||
$this->checkStage(Runtime::POST_RELEASE);
|
||||
$tasks = $this->runtime->getTasks();
|
||||
|
||||
if (!in_array('deploy/release/cleanup', $tasks)) {
|
||||
array_unshift($tasks, 'deploy/release/cleanup');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
public function getPostDeployTasks(): array
|
||||
{
|
||||
$this->checkStage(Runtime::POST_DEPLOY);
|
||||
$tasks = $this->runtime->getTasks();
|
||||
|
||||
if (
|
||||
($this->runtime->getBranch() || $this->runtime->getTag()) &&
|
||||
!$this->runtime->inRollback() &&
|
||||
!in_array('git/change-branch', $tasks)
|
||||
) {
|
||||
array_unshift($tasks, 'git/change-branch');
|
||||
}
|
||||
|
||||
if (!$this->runtime->inRollback() && !in_array('deploy/tar/cleanup', $tasks)) {
|
||||
array_unshift($tasks, 'deploy/tar/cleanup');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the runtime stage is correct
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function checkStage(string $stage): void
|
||||
{
|
||||
if ($this->runtime->getStage() !== $stage) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Invalid stage, got "%s" but expected "%s"', $this->runtime->getStage(), $stage)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
104
src/Deploy/Strategy/RsyncStrategy.php
Normal file
104
src/Deploy/Strategy/RsyncStrategy.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Deploy\Strategy;
|
||||
|
||||
use Mage\Runtime\Exception\RuntimeException;
|
||||
use Mage\Runtime\Runtime;
|
||||
|
||||
/**
|
||||
* Strategy for Deployment with Rsync
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class RsyncStrategy implements StrategyInterface
|
||||
{
|
||||
protected Runtime $runtime;
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Rsync';
|
||||
}
|
||||
|
||||
public function setRuntime(Runtime $runtime): void
|
||||
{
|
||||
$this->runtime = $runtime;
|
||||
}
|
||||
|
||||
public function getPreDeployTasks(): array
|
||||
{
|
||||
$this->checkStage(Runtime::PRE_DEPLOY);
|
||||
$tasks = $this->runtime->getTasks();
|
||||
|
||||
if (
|
||||
($this->runtime->getBranch() || $this->runtime->getTag()) &&
|
||||
!$this->runtime->inRollback() &&
|
||||
!in_array('git/change-branch', $tasks)
|
||||
) {
|
||||
array_unshift($tasks, 'git/change-branch');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
public function getOnDeployTasks(): array
|
||||
{
|
||||
$this->checkStage(Runtime::ON_DEPLOY);
|
||||
$tasks = $this->runtime->getTasks();
|
||||
|
||||
if (!$this->runtime->inRollback() && !in_array('deploy/rsync', $tasks)) {
|
||||
array_unshift($tasks, 'deploy/rsync');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
public function getOnReleaseTasks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getPostReleaseTasks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getPostDeployTasks(): array
|
||||
{
|
||||
$this->checkStage(Runtime::POST_DEPLOY);
|
||||
$tasks = $this->runtime->getTasks();
|
||||
|
||||
if (
|
||||
($this->runtime->getBranch() ||
|
||||
$this->runtime->getTag()) &&
|
||||
!$this->runtime->inRollback() &&
|
||||
!in_array('git/change-branch', $tasks)
|
||||
) {
|
||||
array_push($tasks, 'git/change-branch');
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the runtime stage is correct
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function checkStage(string $stage): void
|
||||
{
|
||||
if ($this->runtime->getStage() !== $stage) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Invalid stage, got "%s" but expected "%s"', $this->runtime->getStage(), $stage)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
51
src/Deploy/Strategy/StrategyInterface.php
Normal file
51
src/Deploy/Strategy/StrategyInterface.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Deploy\Strategy;
|
||||
|
||||
use Mage\Runtime\Runtime;
|
||||
|
||||
/**
|
||||
* Interface for Deploy Strategies
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
interface StrategyInterface
|
||||
{
|
||||
public function getName(): string;
|
||||
|
||||
public function setRuntime(Runtime $runtime): void;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPreDeployTasks(): array;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getOnDeployTasks(): array;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getOnReleaseTasks(): array;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPostReleaseTasks(): array;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPostDeployTasks(): array;
|
||||
}
|
23
src/Mage.php
Normal file
23
src/Mage.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage;
|
||||
|
||||
/**
|
||||
* Mallaganes base class
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class Mage
|
||||
{
|
||||
public const VERSION = '5.0.0';
|
||||
public const CODENAME = 'Galactica';
|
||||
}
|
172
src/MageApplication.php
Normal file
172
src/MageApplication.php
Normal file
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage;
|
||||
|
||||
use Mage\Command\AbstractCommand;
|
||||
use Mage\Runtime\Runtime;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Console\Event\ConsoleErrorEvent;
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Yaml\Parser;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Mage\Runtime\Exception\RuntimeException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* The Console Application for launching the Mage command in a standalone instance
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class MageApplication extends Application
|
||||
{
|
||||
protected Runtime $runtime;
|
||||
protected string $file;
|
||||
|
||||
/**
|
||||
* @param string $file The YAML file from which to read the configuration
|
||||
*/
|
||||
public function __construct(string $file)
|
||||
{
|
||||
parent::__construct('Magallanes', Mage::VERSION);
|
||||
|
||||
$this->file = $file;
|
||||
$dispatcher = new EventDispatcher();
|
||||
$this->setDispatcher($dispatcher);
|
||||
|
||||
$dispatcher->addListener(ConsoleEvents::ERROR, function (ConsoleErrorEvent $event) {
|
||||
$output = $event->getOutput();
|
||||
$command = $event->getCommand();
|
||||
$output->writeln(
|
||||
sprintf('Oops, exception thrown while running command <info>%s</info>', $command->getName())
|
||||
);
|
||||
$exitCode = $event->getExitCode();
|
||||
$event->setError(new \LogicException('Caught exception', $exitCode, $event->getError()));
|
||||
});
|
||||
|
||||
$this->runtime = $this->instantiateRuntime();
|
||||
$this->loadBuiltInCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the Magallanes Application
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function configure(): void
|
||||
{
|
||||
if (!file_exists($this->file) || !is_readable($this->file)) {
|
||||
throw new RuntimeException(sprintf('The file "%s" does not exists or is not readable.', $this->file));
|
||||
}
|
||||
|
||||
try {
|
||||
$parser = new Parser();
|
||||
$config = $parser->parse(file_get_contents($this->file));
|
||||
} catch (ParseException $exception) {
|
||||
throw new RuntimeException(sprintf('Error parsing the file "%s".', $this->file));
|
||||
}
|
||||
|
||||
if (array_key_exists('magephp', $config) && is_array($config['magephp'])) {
|
||||
$logger = null;
|
||||
if (
|
||||
array_key_exists('log_dir', $config['magephp']) &&
|
||||
file_exists($config['magephp']['log_dir']) && is_dir($config['magephp']['log_dir'])
|
||||
) {
|
||||
$logfile = sprintf('%s/%s.log', $config['magephp']['log_dir'], date('Ymd_His'));
|
||||
$config['magephp']['log_file'] = $logfile;
|
||||
|
||||
$logger = new Logger('magephp');
|
||||
$logger->pushHandler(new StreamHandler($logfile));
|
||||
|
||||
$logLimit = isset($config['magephp']['log_limit']) ? intval($config['magephp']['log_limit']) : 30;
|
||||
$this->clearOldLogs($config['magephp']['log_dir'], $logLimit);
|
||||
} elseif (array_key_exists('log_dir', $config['magephp']) && !is_dir($config['magephp']['log_dir'])) {
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
'The configured log_dir "%s" does not exists or is not a directory.',
|
||||
$config['magephp']['log_dir']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->runtime->setConfiguration($config['magephp']);
|
||||
$this->runtime->setLogger($logger);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
sprintf('The file "%s" does not have a valid Magallanes configuration.', $this->file)
|
||||
);
|
||||
}
|
||||
|
||||
protected function clearOldLogs(string $logDir, int $logLimit): void
|
||||
{
|
||||
$filesystem = new Filesystem();
|
||||
$finder = new Finder();
|
||||
|
||||
$finder
|
||||
->files()
|
||||
->followLinks()
|
||||
->in($logDir)
|
||||
->name('*.log')
|
||||
->sortByModifiedTime()
|
||||
->reverseSorting();
|
||||
|
||||
$logs = iterator_to_array($finder);
|
||||
$logsToRemove = array_slice($logs, $logLimit - 1);
|
||||
$filesystem->remove($logsToRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the BuiltIn Commands
|
||||
*/
|
||||
protected function loadBuiltInCommands(): void
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder->files()->in(__DIR__ . '/Command/BuiltIn')->name('*Command.php');
|
||||
|
||||
/** @var SplFileInfo $file */
|
||||
foreach ($finder as $file) {
|
||||
$class = substr('\\Mage\\Command\\BuiltIn\\' . str_replace('/', '\\', $file->getRelativePathname()), 0, -4);
|
||||
if (class_exists($class)) {
|
||||
$reflex = new \ReflectionClass($class);
|
||||
if ($reflex->isInstantiable()) {
|
||||
$command = new $class();
|
||||
if ($command instanceof AbstractCommand) {
|
||||
$command->setRuntime($this->runtime);
|
||||
$this->add($command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Runtime instance to use
|
||||
*/
|
||||
protected function instantiateRuntime(): Runtime
|
||||
{
|
||||
return new Runtime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Runtime instance
|
||||
*/
|
||||
public function getRuntime(): Runtime
|
||||
{
|
||||
return $this->runtime;
|
||||
}
|
||||
}
|
21
src/Runtime/Exception/RuntimeException.php
Normal file
21
src/Runtime/Exception/RuntimeException.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Runtime\Exception;
|
||||
|
||||
/**
|
||||
* An Error occurred while running
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class RuntimeException extends \Exception
|
||||
{
|
||||
}
|
522
src/Runtime/Runtime.php
Normal file
522
src/Runtime/Runtime.php
Normal file
|
@ -0,0 +1,522 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Runtime;
|
||||
|
||||
use Mage\Deploy\Strategy\ReleasesStrategy;
|
||||
use Mage\Deploy\Strategy\RsyncStrategy;
|
||||
use Mage\Deploy\Strategy\StrategyInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Runtime\Exception\RuntimeException;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Runtime is a container of all run in time configuration, stages of progress, hosts being deployed, etc.
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class Runtime
|
||||
{
|
||||
public const PRE_DEPLOY = 'pre-deploy';
|
||||
public const ON_DEPLOY = 'on-deploy';
|
||||
public const POST_DEPLOY = 'post-deploy';
|
||||
public const ON_RELEASE = 'on-release';
|
||||
public const POST_RELEASE = 'post-release';
|
||||
|
||||
/**
|
||||
* @var array<string, mixed> Magallanes configuration
|
||||
*/
|
||||
protected array $configuration = [];
|
||||
|
||||
/**
|
||||
* @var string|null Environment being deployed
|
||||
*/
|
||||
protected ?string $environment = null;
|
||||
|
||||
/**
|
||||
* @var string|null Stage of Deployment
|
||||
*/
|
||||
protected ?string $stage = null;
|
||||
|
||||
/**
|
||||
* @var string|null The host being deployed to
|
||||
*/
|
||||
protected ?string $workingHost = null;
|
||||
|
||||
/**
|
||||
* @var string|null The Release ID
|
||||
*/
|
||||
protected ?string $releaseId = null;
|
||||
|
||||
/**
|
||||
* @var array<string, string> Hold a bag of variables for sharing information between tasks, if needed
|
||||
*/
|
||||
protected $vars = [];
|
||||
|
||||
protected ?LoggerInterface $logger = null;
|
||||
|
||||
/**
|
||||
* @var bool Indicates if a Rollback operation is in progress
|
||||
*/
|
||||
protected bool $rollback = false;
|
||||
|
||||
public function isWindows(): bool
|
||||
{
|
||||
return stripos(PHP_OS, 'WIN') === 0;
|
||||
}
|
||||
|
||||
public function hasPosix(): bool
|
||||
{
|
||||
return function_exists('posix_getpwuid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the Release ID
|
||||
*/
|
||||
public function generateReleaseId(): self
|
||||
{
|
||||
$this->setReleaseId(date('YmdHis'));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Release ID
|
||||
*/
|
||||
public function setReleaseId(string $releaseId): self
|
||||
{
|
||||
$this->releaseId = $releaseId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current Release ID
|
||||
*/
|
||||
public function getReleaseId(): ?string
|
||||
{
|
||||
return $this->releaseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Runtime in Rollback mode On or Off
|
||||
*/
|
||||
public function setRollback(bool $inRollback): self
|
||||
{
|
||||
$this->rollback = $inRollback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if Runtime is in rollback
|
||||
*/
|
||||
public function inRollback(): bool
|
||||
{
|
||||
return $this->rollback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the Vars bag
|
||||
*/
|
||||
public function setVar(string $key, string $value): self
|
||||
{
|
||||
$this->vars[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a value from the Vars bag, or a default (null) if not set
|
||||
*/
|
||||
public function getVar(string $key, mixed $default = null): ?string
|
||||
{
|
||||
if (array_key_exists($key, $this->vars)) {
|
||||
return $this->vars[$key];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Logger instance
|
||||
*/
|
||||
public function setLogger(?LoggerInterface $logger = null): self
|
||||
{
|
||||
$this->logger = $logger;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Magallanes Configuration to the Runtime
|
||||
*
|
||||
* @param array<string, mixed> $configuration
|
||||
*/
|
||||
public function setConfiguration(array $configuration): self
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Configuration
|
||||
*
|
||||
* @return array<string, mixed> $configuration
|
||||
*/
|
||||
public function getConfiguration(): array
|
||||
{
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Configuration Option for a specific section in the configuration
|
||||
*/
|
||||
public function getConfigOption(string $key, mixed $default = null): mixed
|
||||
{
|
||||
if (array_key_exists($key, $this->configuration)) {
|
||||
return $this->configuration[$key];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Configuration Option for a specific section the current Environment
|
||||
*/
|
||||
public function getEnvOption(string $key, mixed $default = null): mixed
|
||||
{
|
||||
if (
|
||||
!array_key_exists('environments', $this->configuration) ||
|
||||
!is_array($this->configuration['environments'])
|
||||
) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (!array_key_exists($this->environment, $this->configuration['environments'])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (array_key_exists($key, $this->configuration['environments'][$this->environment])) {
|
||||
return $this->configuration['environments'][$this->environment][$key];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to get the the configuration option for a specific environment and merge it with
|
||||
* the global one (environment specific overrides the global one if present).
|
||||
*
|
||||
* @param array<string, mixed> $defaultEnv
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getMergedOption(string $key, array $defaultEnv = []): array
|
||||
{
|
||||
$userGlobalOptions = $this->getConfigOption($key, $defaultEnv);
|
||||
$userEnvOptions = $this->getEnvOption($key, $defaultEnv);
|
||||
|
||||
return array_merge(
|
||||
(is_array($userGlobalOptions) ? $userGlobalOptions : []),
|
||||
(is_array($userEnvOptions) ? $userEnvOptions : [])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites an Environment Configuration Option
|
||||
*/
|
||||
public function setEnvOption(string $key, mixed $value): self
|
||||
{
|
||||
if (array_key_exists('environments', $this->configuration) && is_array($this->configuration['environments'])) {
|
||||
if (array_key_exists($this->environment, $this->configuration['environments'])) {
|
||||
$this->configuration['environments'][$this->environment][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the working Environment
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function setEnvironment(string $environment): self
|
||||
{
|
||||
if (
|
||||
array_key_exists('environments', $this->configuration) &&
|
||||
array_key_exists($environment, $this->configuration['environments'])
|
||||
) {
|
||||
$this->environment = $environment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('The environment "%s" does not exists.', $environment), 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current working Environment
|
||||
*/
|
||||
public function getEnvironment(): ?string
|
||||
{
|
||||
return $this->environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the working stage
|
||||
*/
|
||||
public function setStage(string $stage): self
|
||||
{
|
||||
$this->stage = $stage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current working Stage
|
||||
*/
|
||||
public function getStage(): ?string
|
||||
{
|
||||
return $this->stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the defined Tasks for the current Environment and Stage
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getTasks(): array
|
||||
{
|
||||
if (
|
||||
!array_key_exists('environments', $this->configuration) ||
|
||||
!is_array($this->configuration['environments'])
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!array_key_exists($this->environment, $this->configuration['environments'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (array_key_exists($this->stage, $this->configuration['environments'][$this->environment])) {
|
||||
if (is_array($this->configuration['environments'][$this->environment][$this->stage])) {
|
||||
return $this->configuration['environments'][$this->environment][$this->stage];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the working Host
|
||||
*/
|
||||
public function setWorkingHost(?string $host): self
|
||||
{
|
||||
$this->workingHost = $host;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the working Host
|
||||
*/
|
||||
public function getWorkingHost(): ?string
|
||||
{
|
||||
return $this->workingHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a Message into the Logger
|
||||
*/
|
||||
public function log(string $message, string $level = LogLevel::DEBUG): void
|
||||
{
|
||||
if ($this->logger instanceof LoggerInterface) {
|
||||
$this->logger->log($level, $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command, it will be run Locally or Remotely based on the working Stage
|
||||
*/
|
||||
public function runCommand(string $cmd, int $timeout = 120): Process
|
||||
{
|
||||
switch ($this->getStage()) {
|
||||
case self::ON_DEPLOY:
|
||||
case self::ON_RELEASE:
|
||||
case self::POST_RELEASE:
|
||||
return $this->runRemoteCommand($cmd, true, $timeout);
|
||||
default:
|
||||
return $this->runLocalCommand($cmd, $timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command locally
|
||||
*/
|
||||
public function runLocalCommand(string $cmd, int $timeout = 120): Process
|
||||
{
|
||||
$this->log($cmd, LogLevel::INFO);
|
||||
|
||||
$process = Process::fromShellCommandline($cmd);
|
||||
$process->setTimeout($timeout);
|
||||
$process->run();
|
||||
|
||||
$this->log($process->getOutput(), LogLevel::DEBUG);
|
||||
if (!$process->isSuccessful()) {
|
||||
$this->log($process->getErrorOutput(), LogLevel::ERROR);
|
||||
}
|
||||
|
||||
return $process;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command remotely, if jail is true, it will run inside the Host Path and the Release (if available)
|
||||
*/
|
||||
public function runRemoteCommand(string $cmd, bool $jail, int $timeout = 120): Process
|
||||
{
|
||||
$user = $this->getEnvOption('user', $this->getCurrentUser());
|
||||
$sudo = $this->getEnvOption('sudo', false);
|
||||
$host = $this->getHostName();
|
||||
$sshConfig = $this->getSSHConfig();
|
||||
|
||||
$cmdDelegate = $cmd;
|
||||
if ($sudo === true) {
|
||||
$cmdDelegate = sprintf('sudo %s', $cmd);
|
||||
}
|
||||
|
||||
$hostPath = rtrim($this->getEnvOption('host_path'), '/');
|
||||
if ($jail && $this->getReleaseId() !== null) {
|
||||
$cmdDelegate = sprintf('cd %s/releases/%s && %s', $hostPath, $this->getReleaseId(), $cmdDelegate);
|
||||
} elseif ($jail) {
|
||||
$cmdDelegate = sprintf('cd %s && %s', $hostPath, $cmdDelegate);
|
||||
}
|
||||
|
||||
$cmdRemote = str_replace('"', '\"', $cmdDelegate);
|
||||
$cmdLocal = sprintf(
|
||||
'ssh -p %d %s %s@%s "%s"',
|
||||
$sshConfig['port'],
|
||||
$sshConfig['flags'],
|
||||
$user,
|
||||
$host,
|
||||
$cmdRemote
|
||||
);
|
||||
|
||||
return $this->runLocalCommand($cmdLocal, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SSH configuration based on the environment
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getSSHConfig(): array
|
||||
{
|
||||
$sshConfig = $this->getEnvOption(
|
||||
'ssh',
|
||||
[
|
||||
'port' => 22,
|
||||
'flags' => '-q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
|
||||
]
|
||||
);
|
||||
|
||||
if ($this->getHostPort() !== null) {
|
||||
$sshConfig['port'] = $this->getHostPort();
|
||||
}
|
||||
|
||||
if (!array_key_exists('port', $sshConfig)) {
|
||||
$sshConfig['port'] = '22';
|
||||
}
|
||||
|
||||
if (!array_key_exists('flags', $sshConfig)) {
|
||||
$sshConfig['flags'] = '-q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no';
|
||||
}
|
||||
|
||||
if (!array_key_exists('timeout', $sshConfig)) {
|
||||
$sshConfig['timeout'] = 300;
|
||||
}
|
||||
|
||||
return $sshConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Host Port or default ssh port
|
||||
*/
|
||||
public function getHostPort(): ?int
|
||||
{
|
||||
$info = explode(':', strval($this->getWorkingHost()));
|
||||
return isset($info[1]) ? intval($info[1]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Host Name
|
||||
*/
|
||||
public function getHostName(): ?string
|
||||
{
|
||||
if (strpos(strval($this->getWorkingHost()), ':') === false) {
|
||||
return $this->getWorkingHost();
|
||||
}
|
||||
|
||||
$info = explode(':', $this->getWorkingHost());
|
||||
return strval($info[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Temporal File name
|
||||
*/
|
||||
public function getTempFile(): string
|
||||
{
|
||||
return tempnam(sys_get_temp_dir(), 'mage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current user
|
||||
*/
|
||||
public function getCurrentUser(): string
|
||||
{
|
||||
if ($this->hasPosix()) {
|
||||
$userData = posix_getpwuid(posix_geteuid());
|
||||
return $userData['name'];
|
||||
}
|
||||
|
||||
// Windows fallback
|
||||
return strval(getenv('USERNAME'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for getting Branch information
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getBranch(): mixed
|
||||
{
|
||||
return $this->getEnvOption('branch', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for getting Tag information
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getTag(): mixed
|
||||
{
|
||||
return $this->getEnvOption('tag', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses the Deploy Strategy to use
|
||||
*/
|
||||
public function guessStrategy(): StrategyInterface
|
||||
{
|
||||
$strategy = new RsyncStrategy();
|
||||
|
||||
if ($this->getEnvOption('releases', false)) {
|
||||
$strategy = new ReleasesStrategy();
|
||||
}
|
||||
|
||||
$strategy->setRuntime($this);
|
||||
return $strategy;
|
||||
}
|
||||
}
|
72
src/Task/AbstractTask.php
Normal file
72
src/Task/AbstractTask.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task;
|
||||
|
||||
use Mage\Runtime\Runtime;
|
||||
|
||||
/**
|
||||
* Abstract base class for Magallanes Tasks
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
abstract class AbstractTask
|
||||
{
|
||||
/** @var array<string, string|int|null> */
|
||||
protected array $options = [];
|
||||
|
||||
protected Runtime $runtime;
|
||||
|
||||
/**
|
||||
* Get the Name/Code of the Task
|
||||
*/
|
||||
abstract public function getName(): string;
|
||||
|
||||
/**
|
||||
* Get a short Description of the Task
|
||||
*/
|
||||
abstract public function getDescription(): string;
|
||||
|
||||
/**
|
||||
* Executes the Command
|
||||
*/
|
||||
abstract public function execute(): bool;
|
||||
|
||||
/**
|
||||
* Set additional Options for the Task
|
||||
*
|
||||
* @param array<string, string|int|null> $options
|
||||
*/
|
||||
public function setOptions(array $options = []): self
|
||||
{
|
||||
$this->options = array_merge($this->getDefaults(), $options);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Runtime instance
|
||||
*/
|
||||
public function setRuntime(Runtime $runtime): self
|
||||
{
|
||||
$this->runtime = $runtime;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Default options
|
||||
*
|
||||
* @return array<string, string|int|null>
|
||||
*/
|
||||
public function getDefaults(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
45
src/Task/BuiltIn/Composer/AbstractComposerTask.php
Normal file
45
src/Task/BuiltIn/Composer/AbstractComposerTask.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Composer;
|
||||
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Abstract Composer Task
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
abstract class AbstractComposerTask extends AbstractTask
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getOptions(): array
|
||||
{
|
||||
$options = array_merge(
|
||||
['path' => 'composer'],
|
||||
$this->getComposerOptions(),
|
||||
$this->runtime->getMergedOption('composer'),
|
||||
$this->options
|
||||
);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string|int>
|
||||
*/
|
||||
protected function getComposerOptions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
48
src/Task/BuiltIn/Composer/DumpAutoloadTask.php
Normal file
48
src/Task/BuiltIn/Composer/DumpAutoloadTask.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Composer;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Composer Task - Generate Autoload
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class DumpAutoloadTask extends AbstractComposerTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'composer/dump-autoload';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Composer] Dump Autoload';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
$cmd = sprintf('%s dump-autoload %s', $options['path'], $options['flags']);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runCommand(trim($cmd));
|
||||
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getComposerOptions(): array
|
||||
{
|
||||
return ['flags' => '--optimize'];
|
||||
}
|
||||
}
|
48
src/Task/BuiltIn/Composer/InstallTask.php
Normal file
48
src/Task/BuiltIn/Composer/InstallTask.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Composer;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Composer Task - Install Vendors
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class InstallTask extends AbstractComposerTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'composer/install';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Composer] Install';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
$cmd = sprintf('%s install %s', $options['path'], $options['flags']);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runCommand(trim($cmd), intval($options['timeout']));
|
||||
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getComposerOptions(): array
|
||||
{
|
||||
return ['flags' => '--optimize-autoloader', 'timeout' => 120];
|
||||
}
|
||||
}
|
68
src/Task/BuiltIn/Deploy/Release/CleanupTask.php
Normal file
68
src/Task/BuiltIn/Deploy/Release/CleanupTask.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Deploy\Release;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Release Task - Cleanup old releases
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class CleanupTask extends AbstractTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'deploy/release/cleanup';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Release] Cleaning up old Releases';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
|
||||
$currentReleaseId = $this->runtime->getReleaseId();
|
||||
$maxReleases = $this->runtime->getEnvOption('releases');
|
||||
|
||||
$cmdListReleases = sprintf('ls -1 %s/releases', $hostPath);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runRemoteCommand($cmdListReleases, false);
|
||||
if ($process->isSuccessful()) {
|
||||
$releases = $process->getOutput();
|
||||
$releases = explode("\n", trim($releases));
|
||||
|
||||
if (count($releases) > $maxReleases) {
|
||||
sort($releases);
|
||||
$releasesToDelete = array_slice($releases, 0, count($releases) - $maxReleases);
|
||||
foreach ($releasesToDelete as $releaseId) {
|
||||
if ($releaseId !== $currentReleaseId) {
|
||||
$cmdDeleteRelease = sprintf('rm -rf %s/releases/%s', $hostPath, $releaseId);
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runRemoteCommand($cmdDeleteRelease, false);
|
||||
if (!$process->isSuccessful()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
44
src/Task/BuiltIn/Deploy/Release/PrepareTask.php
Normal file
44
src/Task/BuiltIn/Deploy/Release/PrepareTask.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Deploy\Release;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Release Task - Create the Release Directory
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class PrepareTask extends AbstractTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'deploy/release/prepare';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Release] Preparing Release';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
|
||||
|
||||
$cmdMakeDir = sprintf('mkdir -p %s/releases/%s', $hostPath, $this->runtime->getReleaseId());
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runRemoteCommand($cmdMakeDir, false);
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
}
|
53
src/Task/BuiltIn/Deploy/ReleaseTask.php
Normal file
53
src/Task/BuiltIn/Deploy/ReleaseTask.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Deploy;
|
||||
|
||||
use Mage\Task\Exception\ErrorException;
|
||||
use Mage\Task\ExecuteOnRollbackInterface;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Release Task - Create the Symlink
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class ReleaseTask extends AbstractTask implements ExecuteOnRollbackInterface
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'deploy/release';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Release] Creating Symlink';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
if (!$this->runtime->getEnvOption('releases', false)) {
|
||||
throw new ErrorException('This task is only available with releases enabled', 40);
|
||||
}
|
||||
|
||||
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
|
||||
$releaseId = $this->runtime->getReleaseId();
|
||||
|
||||
$symlink = $this->runtime->getEnvOption('symlink', 'current');
|
||||
|
||||
$cmdLinkRelease = sprintf('cd %s && ln -snf releases/%s %s', $hostPath, $releaseId, $symlink);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runRemoteCommand($cmdLinkRelease, false, 0);
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
}
|
78
src/Task/BuiltIn/Deploy/RsyncTask.php
Normal file
78
src/Task/BuiltIn/Deploy/RsyncTask.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Deploy;
|
||||
|
||||
use Mage\Task\Exception\ErrorException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Rsync Task - Copy files with Rsync
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class RsyncTask extends AbstractTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'deploy/rsync';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Deploy] Copying files with Rsync';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$flags = $this->runtime->getEnvOption('rsync', '-avz');
|
||||
$sshConfig = $this->runtime->getSSHConfig();
|
||||
$user = $this->runtime->getEnvOption('user', $this->runtime->getCurrentUser());
|
||||
$host = $this->runtime->getHostName();
|
||||
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
|
||||
$targetDir = rtrim($hostPath, '/');
|
||||
|
||||
if ($this->runtime->getEnvOption('releases', false)) {
|
||||
throw new ErrorException('Can\'t be used with Releases, use "deploy/tar/copy"');
|
||||
}
|
||||
|
||||
$excludes = $this->getExcludes();
|
||||
$from = $this->runtime->getEnvOption('from', './');
|
||||
$cmdRsync = sprintf(
|
||||
'rsync -e "ssh -p %d %s" %s %s %s %s@%s:%s',
|
||||
$sshConfig['port'],
|
||||
$sshConfig['flags'],
|
||||
$flags,
|
||||
$excludes,
|
||||
$from,
|
||||
$user,
|
||||
$host,
|
||||
$targetDir
|
||||
);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runLocalCommand($cmdRsync, 0);
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getExcludes(): string
|
||||
{
|
||||
$excludes = $this->runtime->getMergedOption('exclude', []);
|
||||
$excludes = array_merge(['.git'], array_filter($excludes));
|
||||
|
||||
foreach ($excludes as &$exclude) {
|
||||
$exclude = '--exclude=' . $exclude;
|
||||
}
|
||||
|
||||
return implode(' ', $excludes);
|
||||
}
|
||||
}
|
49
src/Task/BuiltIn/Deploy/Tar/CleanupTask.php
Normal file
49
src/Task/BuiltIn/Deploy/Tar/CleanupTask.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Deploy\Tar;
|
||||
|
||||
use Mage\Task\Exception\ErrorException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Tar Task - Delete temporal Tar
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class CleanupTask extends AbstractTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'deploy/tar/cleanup';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Deploy] Cleanup Tar file';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
if (!$this->runtime->getEnvOption('releases', false)) {
|
||||
throw new ErrorException('This task is only available with releases enabled', 40);
|
||||
}
|
||||
|
||||
$tarLocal = $this->runtime->getVar('tar_local');
|
||||
|
||||
$cmdDeleteTar = sprintf('rm %s', $tarLocal);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runLocalCommand($cmdDeleteTar);
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
}
|
79
src/Task/BuiltIn/Deploy/Tar/CopyTask.php
Normal file
79
src/Task/BuiltIn/Deploy/Tar/CopyTask.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Deploy\Tar;
|
||||
|
||||
use Mage\Task\Exception\ErrorException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Tar Task - Copy Tar
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class CopyTask extends AbstractTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'deploy/tar/copy';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Deploy] Copying files with Tar';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
if (!$this->runtime->getEnvOption('releases', false)) {
|
||||
throw new ErrorException('This task is only available with releases enabled', 40);
|
||||
}
|
||||
|
||||
$user = $this->runtime->getEnvOption('user', $this->runtime->getCurrentUser());
|
||||
$host = $this->runtime->getHostName();
|
||||
$sshConfig = $sshConfig = $this->runtime->getSSHConfig();
|
||||
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
|
||||
$currentReleaseId = $this->runtime->getReleaseId();
|
||||
|
||||
$tarPath = $this->runtime->getEnvOption('tar_extract_path', 'tar');
|
||||
$flags = $this->runtime->getEnvOption('tar_extract', 'xfzop');
|
||||
$targetDir = sprintf('%s/releases/%s', $hostPath, $currentReleaseId);
|
||||
|
||||
$tarLocal = $this->runtime->getVar('tar_local');
|
||||
$tarRemote = basename($tarLocal);
|
||||
|
||||
$cmdCopy = sprintf(
|
||||
'scp -P %d %s %s %s@%s:%s/%s',
|
||||
$sshConfig['port'],
|
||||
isset($sshConfig['scp_flags']) ? $sshConfig['scp_flags'] : $sshConfig['flags'],
|
||||
$tarLocal,
|
||||
$user,
|
||||
$host,
|
||||
$targetDir,
|
||||
$tarRemote
|
||||
);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runLocalCommand($cmdCopy, intval($sshConfig['timeout']));
|
||||
if ($process->isSuccessful()) {
|
||||
$cmdUnTar = sprintf('cd %s && %s %s %s', $targetDir, $tarPath, $flags, $tarRemote);
|
||||
$process = $this->runtime->runRemoteCommand($cmdUnTar, false, 600);
|
||||
if ($process->isSuccessful()) {
|
||||
$cmdDelete = sprintf('rm %s/%s', $targetDir, $tarRemote);
|
||||
$process = $this->runtime->runRemoteCommand($cmdDelete, false);
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
74
src/Task/BuiltIn/Deploy/Tar/PrepareTask.php
Normal file
74
src/Task/BuiltIn/Deploy/Tar/PrepareTask.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Deploy\Tar;
|
||||
|
||||
use Mage\Task\Exception\ErrorException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Tar Task - Create temporal Tar
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class PrepareTask extends AbstractTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'deploy/tar/prepare';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '[Deploy] Preparing Tar file';
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
if (!$this->runtime->getEnvOption('releases', false)) {
|
||||
throw new ErrorException('This task is only available with releases enabled', 40);
|
||||
}
|
||||
|
||||
$tarLocal = $this->runtime->getTempFile();
|
||||
$this->runtime->setVar('tar_local', $tarLocal);
|
||||
|
||||
$excludes = $this->getExcludes();
|
||||
$tarPath = $this->runtime->getEnvOption('tar_create_path', 'tar');
|
||||
$flags = $this->runtime->getEnvOption(
|
||||
'tar_create',
|
||||
$this->runtime->isWindows() ? '--force-local -c -z -p -f' : 'cfzp'
|
||||
);
|
||||
$from = $this->runtime->getEnvOption('from', './');
|
||||
|
||||
if ($this->runtime->getEnvOption('copyDirectory', false)) {
|
||||
$from = sprintf('-C %s ./', $from);
|
||||
}
|
||||
|
||||
$cmdTar = sprintf('%s %s %s %s %s', $tarPath, $flags, $tarLocal, $excludes, $from);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runLocalCommand($cmdTar, 300);
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getExcludes(): string
|
||||
{
|
||||
$excludes = $this->runtime->getMergedOption('exclude', []);
|
||||
$excludes = array_merge(['.git'], array_filter($excludes));
|
||||
|
||||
foreach ($excludes as &$exclude) {
|
||||
$exclude = '--exclude="' . $exclude . '"';
|
||||
}
|
||||
|
||||
return implode(' ', $excludes);
|
||||
}
|
||||
}
|
91
src/Task/BuiltIn/ExecTask.php
Normal file
91
src/Task/BuiltIn/ExecTask.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn;
|
||||
|
||||
use Mage\Task\Exception\ErrorException;
|
||||
use Mage\Task\AbstractTask;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Exec task. Allows you to execute arbitrary commands.
|
||||
*
|
||||
* @author Yanick Witschi <https://github.com/Toflar>
|
||||
*/
|
||||
class ExecTask extends AbstractTask
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'exec';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
|
||||
if ($options['desc']) {
|
||||
return '[Exec] ' . $options['desc'];
|
||||
}
|
||||
|
||||
return '[Exec] Custom command';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @throws ErrorException
|
||||
*/
|
||||
public function execute(): bool
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
|
||||
if (!$options['cmd']) {
|
||||
throw new ErrorException('Parameter "cmd" is not defined');
|
||||
}
|
||||
|
||||
$mapping = [
|
||||
'%environment%' => $this->runtime->getEnvironment(),
|
||||
];
|
||||
|
||||
if ($this->runtime->getReleaseId() !== null) {
|
||||
$mapping['%release%'] = $this->runtime->getReleaseId();
|
||||
}
|
||||
|
||||
$cmd = str_replace(
|
||||
array_keys($mapping),
|
||||
array_values($mapping),
|
||||
strval($options['cmd'])
|
||||
);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runCommand($cmd, intval($options['timeout']));
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string|int>
|
||||
*/
|
||||
protected function getOptions(): array
|
||||
{
|
||||
$options = array_merge(
|
||||
['cmd' => '', 'desc' => '', 'timeout' => 120],
|
||||
$this->options
|
||||
);
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
78
src/Task/BuiltIn/FS/AbstractFileTask.php
Normal file
78
src/Task/BuiltIn/FS/AbstractFileTask.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\FS;
|
||||
|
||||
use Mage\Task\Exception\ErrorException;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* File System Task - Abstract Base class for File Operations
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
abstract class AbstractFileTask extends AbstractTask
|
||||
{
|
||||
/**
|
||||
* Returns the Task options
|
||||
*
|
||||
* @return array<string, string|int|null>
|
||||
* @throws ErrorException
|
||||
*/
|
||||
protected function getOptions(): array
|
||||
{
|
||||
$mandatory = $this->getParameters();
|
||||
$defaults = array_keys($this->getDefaults());
|
||||
$missing = array_diff($mandatory, $defaults);
|
||||
|
||||
foreach ($missing as $parameter) {
|
||||
if (!array_key_exists($parameter, $this->options)) {
|
||||
throw new ErrorException(sprintf('Parameter "%s" is not defined', $parameter));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mandatory parameters
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
abstract protected function getParameters(): array;
|
||||
|
||||
/**
|
||||
* Returns a file with the placeholders replaced
|
||||
*
|
||||
* @throws ErrorException
|
||||
*/
|
||||
protected function getFile(string $file): string
|
||||
{
|
||||
$mapping = [
|
||||
'%environment%' => $this->runtime->getEnvironment(),
|
||||
];
|
||||
|
||||
if ($this->runtime->getHostName() !== null) {
|
||||
$mapping['%host%'] = $this->runtime->getHostName();
|
||||
}
|
||||
|
||||
if ($this->runtime->getReleaseId() !== null) {
|
||||
$mapping['%release%'] = $this->runtime->getReleaseId();
|
||||
}
|
||||
|
||||
$options = $this->getOptions();
|
||||
return str_replace(
|
||||
array_keys($mapping),
|
||||
array_values($mapping),
|
||||
strval($options[$file])
|
||||
);
|
||||
}
|
||||
}
|
60
src/Task/BuiltIn/FS/ChangeModeTask.php
Normal file
60
src/Task/BuiltIn/FS/ChangeModeTask.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\FS;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* File System Task - Copy a File
|
||||
*
|
||||
* @author Marian Bäuerle
|
||||
*/
|
||||
class ChangeModeTask extends AbstractFileTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'fs/chmod';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
try {
|
||||
return sprintf('[FS] Change mode of "%s" to "%s"', $this->getFile('file'), $this->options['mode']);
|
||||
} catch (\Exception $exception) {
|
||||
return '[FS] Chmod [missing parameters]';
|
||||
}
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$file = $this->getFile('file');
|
||||
$mode = $this->options['mode'];
|
||||
$flags = $this->options['flags'];
|
||||
|
||||
$cmd = sprintf('chmod %s %s "%s"', $flags, $mode, $file);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runCommand($cmd);
|
||||
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getParameters(): array
|
||||
{
|
||||
return ['file', 'mode', 'flags'];
|
||||
}
|
||||
|
||||
public function getDefaults(): array
|
||||
{
|
||||
return ['flags' => null];
|
||||
}
|
||||
}
|
60
src/Task/BuiltIn/FS/CopyTask.php
Normal file
60
src/Task/BuiltIn/FS/CopyTask.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\FS;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* File System Task - Copy a File
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class CopyTask extends AbstractFileTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'fs/copy';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
try {
|
||||
return sprintf('[FS] Copy "%s" to "%s"', $this->getFile('from'), $this->getFile('to'));
|
||||
} catch (\Exception $exception) {
|
||||
return '[FS] Copy [missing parameters]';
|
||||
}
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$copyFrom = $this->getFile('from');
|
||||
$copyTo = $this->getFile('to');
|
||||
$flags = $this->options['flags'];
|
||||
|
||||
$cmd = sprintf('cp %s "%s" "%s"', $flags, $copyFrom, $copyTo);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runCommand($cmd);
|
||||
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getParameters(): array
|
||||
{
|
||||
return ['from', 'to', 'flags'];
|
||||
}
|
||||
|
||||
public function getDefaults(): array
|
||||
{
|
||||
return ['flags' => '-p'];
|
||||
}
|
||||
}
|
60
src/Task/BuiltIn/FS/LinkTask.php
Normal file
60
src/Task/BuiltIn/FS/LinkTask.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\FS;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* File System Task - Symlink a File
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class LinkTask extends AbstractFileTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'fs/link';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
try {
|
||||
return sprintf('[FS] Link "%s" to "%s"', $this->getFile('from'), $this->getFile('to'));
|
||||
} catch (\Exception $exception) {
|
||||
return '[FS] Link [missing parameters]';
|
||||
}
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$linkFrom = $this->getFile('from');
|
||||
$linkTo = $this->getFile('to');
|
||||
$flags = $this->options['flags'];
|
||||
|
||||
$cmd = sprintf('ln %s "%s" "%s"', $flags, $linkFrom, $linkTo);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runCommand($cmd);
|
||||
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getParameters(): array
|
||||
{
|
||||
return ['from', 'to', 'flags'];
|
||||
}
|
||||
|
||||
public function getDefaults(): array
|
||||
{
|
||||
return ['flags' => '-snf'];
|
||||
}
|
||||
}
|
61
src/Task/BuiltIn/FS/MoveTask.php
Normal file
61
src/Task/BuiltIn/FS/MoveTask.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\FS;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* File System Task - Move a File
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class MoveTask extends AbstractFileTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'fs/move';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
try {
|
||||
return sprintf('[FS] Move "%s" to "%s"', $this->getFile('from'), $this->getFile('to'));
|
||||
} catch (Exception $exception) {
|
||||
return '[FS] Move [missing parameters]';
|
||||
}
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$moveFrom = $this->getFile('from');
|
||||
$moveTo = $this->getFile('to');
|
||||
$flags = $this->options['flags'];
|
||||
|
||||
$cmd = sprintf('mv %s "%s" "%s"', $flags, $moveFrom, $moveTo);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runCommand($cmd);
|
||||
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getParameters(): array
|
||||
{
|
||||
return ['from', 'to', 'flags'];
|
||||
}
|
||||
|
||||
public function getDefaults(): array
|
||||
{
|
||||
return ['flags' => null];
|
||||
}
|
||||
}
|
59
src/Task/BuiltIn/FS/RemoveTask.php
Normal file
59
src/Task/BuiltIn/FS/RemoveTask.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\FS;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* File System Task - Remove a File
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class RemoveTask extends AbstractFileTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'fs/remove';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
try {
|
||||
return sprintf('[FS] Remove "%s"', $this->getFile('file'));
|
||||
} catch (\Exception $exception) {
|
||||
return '[FS] Remove [missing parameters]';
|
||||
}
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$file = $this->getFile('file');
|
||||
$flags = $this->options['flags'];
|
||||
|
||||
$cmd = sprintf('rm %s "%s"', $flags, $file);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runCommand($cmd);
|
||||
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
protected function getParameters(): array
|
||||
{
|
||||
return ['file', 'flags'];
|
||||
}
|
||||
|
||||
public function getDefaults(): array
|
||||
{
|
||||
return ['flags' => null];
|
||||
}
|
||||
}
|
92
src/Task/BuiltIn/Git/ChangeBranchTask.php
Normal file
92
src/Task/BuiltIn/Git/ChangeBranchTask.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Magallanes package.
|
||||
*
|
||||
* (c) Andrés Montañez <andres@andresmontanez.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mage\Task\BuiltIn\Git;
|
||||
|
||||
use Mage\Task\Exception\SkipException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Mage\Task\AbstractTask;
|
||||
|
||||
/**
|
||||
* Git Task - Checkout Branch
|
||||
*
|
||||
* @author Andrés Montañez <andresmontanez@gmail.com>
|
||||
*/
|
||||
class ChangeBranchTask extends AbstractTask
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'git/change-branch';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
$tag = $options['tag'];
|
||||
$branch = $options['branch'];
|
||||
|
||||
if ($this->runtime->getVar('git_revert_branch', null)) {
|
||||
$branch = $this->runtime->getVar('git_revert_branch');
|
||||
}
|
||||
|
||||
if ($tag) {
|
||||
return sprintf('[Git] Checkout Tag (%s)', $tag);
|
||||
}
|
||||
|
||||
return sprintf('[Git] Change Branch (%s)', $branch);
|
||||
}
|
||||
|
||||
public function execute(): bool
|
||||
{
|
||||
$options = $this->getOptions();
|
||||
/** @var string|bool */
|
||||
$branch = $this->runtime->getVar('git_revert_branch', null);
|
||||
|
||||
if (!$branch) {
|
||||
$cmdGetCurrent = sprintf('%s branch | grep "*"', $options['path']);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runLocalCommand($cmdGetCurrent);
|
||||
if (!$process->isSuccessful()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentBranch = str_replace('* ', '', trim($process->getOutput()));
|
||||
if ($currentBranch === $options['branch']) {
|
||||
throw new SkipException();
|
||||
}
|
||||
|
||||
$branch = $options['tag'] ? $options['tag'] : $options['branch'];
|
||||
$this->runtime->setVar('git_revert_branch', $currentBranch);
|
||||
}
|
||||
|
||||
$cmdChange = sprintf('%s checkout %s', $options['path'], $branch);
|
||||
|
||||
/** @var Process $process */
|
||||
$process = $this->runtime->runLocalCommand($cmdChange);
|
||||
return $process->isSuccessful();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function getOptions(): array
|
||||
{
|
||||
$tag = $this->runtime->getEnvOption('tag', false);
|
||||
$branch = $this->runtime->getEnvOption('branch', 'master');
|
||||
$options = array_merge(
|
||||
['path' => 'git', 'branch' => $branch, 'tag' => $tag],
|
||||
$this->options
|
||||
);
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue