[Galactica] V5 initial scope

This commit is contained in:
Andrés Montañez 2022-04-10 01:20:03 -03:00
parent d6432d732a
commit 9a46f89266
No known key found for this signature in database
GPG Key ID: 97E9F675F4C03DE2
75 changed files with 805 additions and 931 deletions

32
.github/workflows/linters.yml vendored Normal file
View 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

60
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,60 @@
name: PHPUnit
on:
push:
branches:
- master
- galactica
pull_request:
release:
types:
- created
jobs:
unit_tests_80:
name: Unit tests with PHP 8.0
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: Run tests
env:
XDEBUG_MODE: coverage
run: ./vendor/bin/phpunit --testsuite=unit
- name: Run Coveralls
env:
XDEBUG_MODE: coverage
run: ./vendor/bin/php-coveralls -v --coverage_clover build/logs/coverage.xml
unit_tests_81:
name: Unit tests with PHP 8.1
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.1
- name: Run tests
env:
XDEBUG_MODE: coverage
run: ./vendor/bin/phpunit --testsuite=unit
- name: Run Coveralls
env:
XDEBUG_MODE: coverage
run: ./vendor/bin/php-coveralls -v --coverage_clover build/logs/coverage.xml

2
.gitignore vendored
View File

@ -3,4 +3,4 @@
composer.lock
.mage.yml
.phpunit.result.cache
.phpcs-cache

View File

@ -1,8 +1,7 @@
language: php
php:
- '7.2'
- '7.4'
- '8.0'
- '8.1'
install:
- composer install

View File

@ -1,24 +1,8 @@
CHANGELOG for 4.X
CHANGELOG for 5.X
=================
* 4.1.1 (2021-02-20)
* Add `copyDirectory` option
* Bug fixes
* Improve testing and coverage
* 4.1.0 (2021-02-19)
* PHP 8 and Symfony 5 compatibility [PR#448]
* Timeout option for SSH [PR#436]
* Improve compatibility with Windows [PR#434] [PR#429]
* Improve config load [PR#422]
* Bug fixes [PR#448] [PR#424]
* Readme Update [PR#438]
* 4.0.0 (2018-04-02)
* v4 series release
* Refactored for Symfony 4 and PHP 7.1
* Symfony Pool Clear task added
* Symfony Pool Prune task added
* Symfony Assetic task removed
* 5.0.0 (2022-04-15)
* v5 series release
* Refactored for Symfony 6 and PHP 8
* Added strong types
* Removed task `composer/self-update`

View File

@ -7,7 +7,7 @@ Please read the following guidelines to make your and our work easier and cleane
1. Write clean code with no mess left
2. Contribute the docs when adding configurable new feature
3. Create your pull request from `nostromo` branch
3. Create your pull request from `galactica` branch
4. Ensure your code is fully covered by tests
----------
@ -28,39 +28,36 @@ In order to have the PRs prioritized name them with the following tags.
[FEATURE] Create new PermissionsTask
[HOTFIX] Exception not caught on deployment
```
All Pull Requests must be done to the `nostromo` branch, only exception are Hotfixes.
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 `nostromo` 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.
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-nostromo` to dependency version in your `composer.json` file:
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-nostromo"
"andres-montanez/magallanes": "dev-galactica"
}
}
```
## Organization and code quality
We use [PSR2](http://www.php-fig.org/psr/psr-2/) as PHP coding standard.
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. PHP-CodeSniffer
2. [PHP Mess Detector](https://phpmd.org/)
3. PHP Copy/Paste Detector
4. PHP Dead Code Detector
5. [PHP Coding Standards Fixer](http://cs.sensiolabs.org) with --level=psr2
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:
```
vendor/bin/phpunit --coverage-clover build/logs/coverage.xml
vendor/bin/coveralls -v --coverage_clover build/logs/coverage.xml
```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.

View File

@ -1,4 +1,4 @@
Copyright (c) 2011 - 2017 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

View File

@ -16,7 +16,7 @@ Simply add the following dependency to your projects composer.json file:
```json
"require-dev": {
"andres-montanez/magallanes": "^4.0"
"andres-montanez/magallanes": "^5.0"
}
```
Finally you can use **Magallanes** from the vendor's bin:
@ -26,4 +26,4 @@ vendor/bin/mage version
```
### Codename Discovery One
Each new mayor version of **Magallanes** will have a codename (like Ubuntu), version 3 was _Nostromo_, and in the current version it is **_Discovery One_**, in homage to the spaceship from the ground breaking film *2001: A Space Odyssey (1968)*.
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.

View File

@ -12,18 +12,20 @@
}
],
"require": {
"php": "^7.2 | ^8.0",
"monolog/monolog": "~1.11 | ^2.0",
"symfony/console": "^4.0 | ^5.0",
"symfony/filesystem": "^4.0 | ^5.0",
"symfony/event-dispatcher": "^4.0 | ^5.0",
"symfony/finder": "^4.0 | ^5.0",
"symfony/yaml": "^4.0 | ^5.0",
"symfony/process": "^4.0 | ^5.0"
"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": "^8.0",
"php-coveralls/php-coveralls": "~2.0"
"phpunit/phpunit": "^9.5",
"phpstan/phpstan": "^1.5",
"squizlabs/php_codesniffer": "^3.6",
"php-coveralls/php-coveralls": "^2.5"
},
"suggest": {
"ext-posix": "*"
@ -41,8 +43,8 @@
"bin": ["bin/mage"],
"extra": {
"branch-alias": {
"dev-master": "4.0.x-dev",
"dev-discovery-one": "4.x-dev"
"dev-master": "5.0.x-dev",
"dev-discovery-one": "5.x-dev"
}
}
}

View File

@ -1,7 +1,7 @@
version: '2'
services:
php7.4:
container_name: mage-php7.4
build: ./php7.4
php8.0:
container_name: mage-php8.0
build: ./php8.0
volumes:
- ../../:/home/magephp

View File

@ -1,11 +1,11 @@
FROM ubuntu:20.04
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 php7.4-cli php-zip php7.4-curl php7.4-xml php7.4-mbstring php7.4-xdebug
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

View File

@ -7,10 +7,8 @@ magephp:
host_path: /var/www/test
releases: 4
exclude:
- vendor
- app/cache
- app/log
- web/app_dev.php
- var/cache
- var/log
hosts:
- webserver1
- webserver2
@ -18,7 +16,7 @@ magephp:
pre-deploy:
- git/update
- composer/install
- composer/generate-autoload
- composer/dump-autoload
on-deploy:
- symfony/cache-warmup: { env: 'prod' }
- symfony/assets-install: { env: 'prod' }

14
phpcs.xml.dist Normal file
View 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
View File

@ -0,0 +1,4 @@
parameters:
level: 6
paths:
- src

View File

@ -1,37 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
>
<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>
<filter>
<whitelist>
<directory>./src/</directory>
<exclude>
<directory>./tests/</directory>
</exclude>
</whitelist>
</filter>
<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>

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -23,46 +24,30 @@ use Symfony\Component\Console\Command\Command;
*/
abstract class AbstractCommand extends Command
{
/**
* @var int
*/
protected $statusCode = 0;
/**
* @var Runtime Current Runtime instance
*/
protected $runtime;
protected int $statusCode = 0;
protected Runtime $runtime;
/**
* Set the Runtime configuration
*
* @param Runtime $runtime Runtime container
* @return AbstractCommand
*/
public function setRuntime(Runtime $runtime)
public function setRuntime(Runtime $runtime): self
{
$this->runtime = $runtime;
return $this;
}
/**
* Logs a message
*
* @param string $message
* @param string $level
*/
public function log($message, $level = LogLevel::DEBUG)
public function log(string $message, string $level = LogLevel::DEBUG): void
{
$this->runtime->log($message, $level);
}
/**
* Get the Human friendly Stage name
*
* @return string
*/
protected function getStageName()
protected function getStageName(): string
{
$utils = new Utils();
return $utils->getStageName($this->runtime->getStage());
@ -71,7 +56,7 @@ abstract class AbstractCommand extends Command
/**
* Requires the configuration to be loaded
*/
protected function requireConfig()
protected function requireConfig(): void
{
$app = $this->getApplication();
if ($app instanceof MageApplication) {

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -24,22 +25,17 @@ class DumpCommand extends AbstractCommand
/**
* Configure the Command
*/
protected function configure()
protected function configure(): void
{
$this
->setName('config:dump')
->setDescription('Dumps the Magallanes configuration')
;
->setDescription('Dumps the Magallanes configuration');
}
/**
* Execute the Command
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int|mixed
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->requireConfig();
@ -51,6 +47,6 @@ class DumpCommand extends AbstractCommand
$output->writeln('');
$output->writeln('Finished <fg=blue>Magallanes</>');
return 0;
return self::SUCCESS;
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -25,22 +26,17 @@ class EnvironmentsCommand extends AbstractCommand
/**
* Configure the Command
*/
protected function configure()
protected function configure(): void
{
$this
->setName('config:environments')
->setDescription('List all Magallanes configured Environments')
;
->setDescription('List all Magallanes configured Environments');
}
/**
* Execute the Command
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int|mixed
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->requireConfig();
@ -66,6 +62,6 @@ class EnvironmentsCommand extends AbstractCommand
$output->writeln('');
$output->writeln('Finished <fg=blue>Magallanes</>');
return 0;
return self::SUCCESS;
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -31,32 +32,30 @@ use Mage\Command\AbstractCommand;
*/
class DeployCommand extends AbstractCommand
{
/**
* @var TaskFactory
*/
protected $taskFactory;
protected TaskFactory $taskFactory;
/**
* Configure the Command
*/
protected function configure()
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(
'branch',
null,
InputOption::VALUE_REQUIRED,
'Force to switch to a branch other than the one defined',
false
);
}
/**
* Execute the Command
*
* @param InputInterface $input
* @param OutputInterface $output
* @return integer
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->requireConfig();
@ -103,17 +102,15 @@ class DeployCommand extends AbstractCommand
$output->writeln('Finished <fg=blue>Magallanes</>');
return $this->statusCode;
return intval($this->statusCode);
}
/**
* Run the Deployment Process
*
* @param OutputInterface $output
* @param StrategyInterface $strategy
* @throws RuntimeException
*/
protected function runDeployment(OutputInterface $output, StrategyInterface $strategy)
protected function runDeployment(OutputInterface $output, StrategyInterface $strategy): void
{
// Run "Pre Deploy" Tasks
$this->runtime->setStage(Runtime::PRE_DEPLOY);
@ -140,16 +137,20 @@ class DeployCommand extends AbstractCommand
}
}
protected function runOnHosts(OutputInterface $output, $tasks)
/**
* @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) {
if (count($hosts) === 0) {
$output->writeln(sprintf(' No hosts defined, skipping %s tasks', $this->getStageName()));
$output->writeln('');
return true;
return;
}
foreach ($hosts as $host) {
@ -165,21 +166,27 @@ class DeployCommand extends AbstractCommand
/**
* Runs all the tasks
*
* @param OutputInterface $output
* @param $tasks
* @return bool
* @param string[] $tasks
* @throws RuntimeException
*/
protected function runTasks(OutputInterface $output, $tasks)
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(
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()));
$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()));
}
@ -188,7 +195,6 @@ class DeployCommand extends AbstractCommand
$succeededTasks = 0;
foreach ($tasks as $taskName) {
/** @var AbstractTask $task */
$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()));
@ -196,25 +202,48 @@ class DeployCommand extends AbstractCommand
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()));
$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()));
$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()));
$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()));
$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->log(
sprintf(
'Task %s (%s) finished with FAIL, with Error "%s"',
$task->getDescription(),
$task->getName(),
$exception->getMessage()
)
);
$this->statusCode = 190;
}
}
@ -229,7 +258,15 @@ class DeployCommand extends AbstractCommand
$alertColor = 'green';
}
$output->writeln(sprintf(' Finished <fg=%s>%d/%d</> tasks for <fg=black;options=bold>%s</>.', $alertColor, $succeededTasks, $totalTasks, $this->getStageName()));
$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);
@ -238,8 +275,11 @@ class DeployCommand extends AbstractCommand
/**
* Exception for halting the the current process
*/
protected function getException()
protected function getException(): RuntimeException
{
return new RuntimeException(sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()), 50);
return new RuntimeException(
sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()),
50
);
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -25,31 +26,21 @@ use Mage\Command\AbstractCommand;
*/
class ListCommand extends AbstractCommand
{
/**
* @var int
*/
protected $statusCode = 0;
/**
* Configure the Command
*/
protected function configure()
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')
;
->addArgument('environment', InputArgument::REQUIRED, 'Name of the environment to deploy to');
}
/**
* Execute the Command
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int|mixed
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->requireConfig();
@ -102,7 +93,9 @@ class ListCommand extends AbstractCommand
}
if (count($releases) == 0) {
$output->writeln(sprintf(' No releases available on host <fg=black;options=bold>%s</>:', $host));
$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);
@ -110,7 +103,10 @@ class ListCommand extends AbstractCommand
/** @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);
throw new RuntimeException(
sprintf('Unable to retrieve current release from host "%s"', $host),
85
);
}
$currentReleaseId = explode('/', trim($process->getOutput()));
@ -121,7 +117,8 @@ class ListCommand extends AbstractCommand
foreach ($releases as $releaseId) {
$releaseDate = $utils->getReleaseDate($releaseId);
$output->write(sprintf(' Release ID: <fg=magenta>%s</> - Date: <fg=black;options=bold>%s</> [%s]',
$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)
@ -146,6 +143,6 @@ class ListCommand extends AbstractCommand
$output->writeln('Finished <fg=blue>Magallanes</>');
return $this->statusCode;
return intval($this->statusCode);
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -25,32 +26,22 @@ use Mage\Command\BuiltIn\DeployCommand;
*/
class RollbackCommand extends DeployCommand
{
/**
* @var int
*/
protected $statusCode = 0;
/**
* Configure the Command
*/
protected function configure()
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')
;
->addArgument('release', InputArgument::REQUIRED, 'The ID or the Index of the release to rollback to');
}
/**
* Execute the Command
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int|mixed
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->requireConfig();
@ -68,11 +59,14 @@ class RollbackCommand extends DeployCommand
}
$releaseToRollback = $input->getArgument('release');
if (($releaseId = $this->checkReleaseAvailability($releaseToRollback)) === false) {
throw new RuntimeException(sprintf('Release "%s" is not available on all hosts', $releaseToRollback), 72);
if ($this->checkReleaseAvailability($releaseToRollback) === false) {
throw new RuntimeException(
sprintf('Release "%s" is not available on all hosts', $releaseToRollback),
72
);
}
$this->runtime->setReleaseId($releaseId)->setRollback(true);
$this->runtime->setReleaseId($releaseToRollback)->setRollback(true);
$output->writeln(sprintf(' Environment: <fg=green>%s</>', $this->runtime->getEnvironment()));
$this->log(sprintf('Environment: %s', $this->runtime->getEnvironment()));
@ -95,16 +89,13 @@ class RollbackCommand extends DeployCommand
$output->writeln('Finished <fg=blue>Magallanes</>');
return $this->statusCode;
return intval($this->statusCode);
}
/**
* Check if the provided Release ID is available in all hosts
*
* @param string $releaseToRollback Release ID
* @return bool
*/
protected function checkReleaseAvailability($releaseToRollback)
protected function checkReleaseAvailability(string $releaseToRollback): bool
{
$hosts = $this->runtime->getEnvOption('hosts');
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
@ -132,7 +123,7 @@ class RollbackCommand extends DeployCommand
}
if ($availableInHosts === count($hosts)) {
return $releaseToRollback;
return (bool) $releaseToRollback;
}
return false;

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -25,25 +26,20 @@ class VersionCommand extends AbstractCommand
/**
* Configure the Command
*/
protected function configure()
protected function configure(): void
{
$this
->setName('version')
->setDescription('Get the version of Magallanes')
;
->setDescription('Get the version of Magallanes');
}
/**
* Executes the Command
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln(sprintf('Magallanes v%s [%s]', Mage::VERSION, Mage::CODENAME));
return 0;
return self::SUCCESS;
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -20,22 +21,19 @@ use Mage\Runtime\Runtime;
*/
class ReleasesStrategy implements StrategyInterface
{
/**
* @var Runtime
*/
protected $runtime;
protected Runtime $runtime;
public function getName()
public function getName(): string
{
return 'Releases';
}
public function setRuntime(Runtime $runtime)
public function setRuntime(Runtime $runtime): void
{
$this->runtime = $runtime;
}
public function getPreDeployTasks()
public function getPreDeployTasks(): array
{
$this->checkStage(Runtime::PRE_DEPLOY);
$tasks = $this->runtime->getTasks();
@ -51,7 +49,7 @@ class ReleasesStrategy implements StrategyInterface
return $tasks;
}
public function getOnDeployTasks()
public function getOnDeployTasks(): array
{
$this->checkStage(Runtime::ON_DEPLOY);
$tasks = $this->runtime->getTasks();
@ -67,7 +65,7 @@ class ReleasesStrategy implements StrategyInterface
return $tasks;
}
public function getOnReleaseTasks()
public function getOnReleaseTasks(): array
{
$this->checkStage(Runtime::ON_RELEASE);
$tasks = $this->runtime->getTasks();
@ -79,7 +77,7 @@ class ReleasesStrategy implements StrategyInterface
return $tasks;
}
public function getPostReleaseTasks()
public function getPostReleaseTasks(): array
{
$this->checkStage(Runtime::POST_RELEASE);
$tasks = $this->runtime->getTasks();
@ -91,7 +89,7 @@ class ReleasesStrategy implements StrategyInterface
return $tasks;
}
public function getPostDeployTasks()
public function getPostDeployTasks(): array
{
$this->checkStage(Runtime::POST_DEPLOY);
$tasks = $this->runtime->getTasks();
@ -110,13 +108,14 @@ class ReleasesStrategy implements StrategyInterface
/**
* Check the runtime stage is correct
*
* @param string $stage
* @throws RuntimeException
*/
private function checkStage($stage)
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));
throw new RuntimeException(
sprintf('Invalid stage, got "%s" but expected "%s"', $this->runtime->getStage(), $stage)
);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -20,22 +21,19 @@ use Mage\Runtime\Runtime;
*/
class RsyncStrategy implements StrategyInterface
{
/**
* @var Runtime
*/
protected $runtime;
protected Runtime $runtime;
public function getName()
public function getName(): string
{
return 'Rsync';
}
public function setRuntime(Runtime $runtime)
public function setRuntime(Runtime $runtime): void
{
$this->runtime = $runtime;
}
public function getPreDeployTasks()
public function getPreDeployTasks(): array
{
$this->checkStage(Runtime::PRE_DEPLOY);
$tasks = $this->runtime->getTasks();
@ -47,7 +45,7 @@ class RsyncStrategy implements StrategyInterface
return $tasks;
}
public function getOnDeployTasks()
public function getOnDeployTasks(): array
{
$this->checkStage(Runtime::ON_DEPLOY);
$tasks = $this->runtime->getTasks();
@ -59,17 +57,17 @@ class RsyncStrategy implements StrategyInterface
return $tasks;
}
public function getOnReleaseTasks()
public function getOnReleaseTasks(): array
{
return [];
}
public function getPostReleaseTasks()
public function getPostReleaseTasks(): array
{
return [];
}
public function getPostDeployTasks()
public function getPostDeployTasks(): array
{
$this->checkStage(Runtime::POST_DEPLOY);
$tasks = $this->runtime->getTasks();
@ -84,13 +82,14 @@ class RsyncStrategy implements StrategyInterface
/**
* Check the runtime stage is correct
*
* @param $stage
* @throws RuntimeException
*/
private function checkStage($stage)
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));
throw new RuntimeException(
sprintf('Invalid stage, got "%s" but expected "%s"', $this->runtime->getStage(), $stage)
);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,17 +20,32 @@ use Mage\Runtime\Runtime;
*/
interface StrategyInterface
{
public function getName();
public function getName(): string;
public function setRuntime(Runtime $runtime);
public function setRuntime(Runtime $runtime): void;
public function getPreDeployTasks();
/**
* @return string[]
*/
public function getPreDeployTasks(): array;
public function getOnDeployTasks();
/**
* @return string[]
*/
public function getOnDeployTasks(): array;
public function getOnReleaseTasks();
/**
* @return string[]
*/
public function getOnReleaseTasks(): array;
public function getPostReleaseTasks();
/**
* @return string[]
*/
public function getPostReleaseTasks(): array;
public function getPostDeployTasks();
/**
* @return string[]
*/
public function getPostDeployTasks(): array;
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -17,6 +18,6 @@ namespace Mage;
*/
class Mage
{
const VERSION = '4.1.1';
const CODENAME = 'Discovery One';
public const VERSION = '5.0.0';
public const CODENAME = 'Galactica';
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -22,7 +23,6 @@ use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Application;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Exception\ParseException;
use ReflectionClass;
use Mage\Runtime\Exception\RuntimeException;
/**
@ -32,13 +32,13 @@ use Mage\Runtime\Exception\RuntimeException;
*/
class MageApplication extends Application
{
protected $runtime;
protected $file;
protected Runtime $runtime;
protected string $file;
/**
* @param string $file The YAML file from which to read the configuration
*/
public function __construct($file)
public function __construct(string $file)
{
parent::__construct('Magallanes', Mage::VERSION);
@ -49,7 +49,9 @@ class MageApplication extends Application
$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()));
$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()));
});
@ -63,7 +65,7 @@ class MageApplication extends Application
*
* @throws RuntimeException
*/
public function configure()
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));
@ -78,15 +80,22 @@ class MageApplication extends Application
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'])) {
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));
} 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']));
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']);
@ -94,13 +103,15 @@ class MageApplication extends Application
return;
}
throw new RuntimeException(sprintf('The file "%s" does not have a valid Magallanes configuration.', $this->file));
throw new RuntimeException(
sprintf('The file "%s" does not have a valid Magallanes configuration.', $this->file)
);
}
/**
* Loads the BuiltIn Commands
*/
protected function loadBuiltInCommands()
protected function loadBuiltInCommands(): void
{
$finder = new Finder();
$finder->files()->in(__DIR__ . '/Command/BuiltIn')->name('*Command.php');
@ -109,7 +120,7 @@ class MageApplication extends Application
foreach ($finder as $file) {
$class = substr('\\Mage\\Command\\BuiltIn\\' . str_replace('/', '\\', $file->getRelativePathname()), 0, -4);
if (class_exists($class)) {
$reflex = new ReflectionClass($class);
$reflex = new \ReflectionClass($class);
if ($reflex->isInstantiable()) {
$command = new $class();
if ($command instanceof AbstractCommand) {
@ -123,20 +134,16 @@ class MageApplication extends Application
/**
* Gets the Runtime instance to use
*
* @return Runtime
*/
protected function instantiateRuntime()
protected function instantiateRuntime(): Runtime
{
return new Runtime();
}
/**
* Get the Runtime instance
*
* @return Runtime
*/
public function getRuntime()
public function getRuntime(): Runtime
{
return $this->runtime;
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -10,13 +11,11 @@
namespace Mage\Runtime\Exception;
use Exception;
/**
* An Error occurred while running
*
* @author Andrés Montañez <andresmontanez@gmail.com>
*/
class RuntimeException extends Exception
class RuntimeException extends \Exception
{
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -17,6 +18,7 @@ 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.
@ -25,63 +27,58 @@ use Mage\Runtime\Exception\RuntimeException;
*/
class Runtime
{
const PRE_DEPLOY = 'pre-deploy';
const ON_DEPLOY = 'on-deploy';
const POST_DEPLOY = 'post-deploy';
const ON_RELEASE = 'on-release';
const POST_RELEASE = 'post-release';
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 Magallanes configuration
* @var array<string, mixed> Magallanes configuration
*/
protected $configuration = [];
protected array $configuration = [];
/**
* @var string|null Environment being deployed
*/
protected $environment;
protected ?string $environment = null;
/**
* @var string Stage of Deployment
* @var string|null Stage of Deployment
*/
protected $stage;
protected ?string $stage = null;
/**
* @var string|null The host being deployed to
*/
protected $workingHost = null;
protected ?string $workingHost = null;
/**
* @var string|null The Release ID
*/
protected $releaseId = null;
protected ?string $releaseId = null;
/**
* @var array Hold a bag of variables for sharing information between tasks, if needed
* @var array<string, string> Hold a bag of variables for sharing information between tasks, if needed
*/
protected $vars = [];
/**
* @var LoggerInterface|null The logger instance
*/
protected $logger;
protected ?LoggerInterface $logger = null;
/**
* @var bool Indicates if a Rollback operation is in progress
*/
protected $rollback = false;
protected bool $rollback = false;
public function isWindows()
public function isWindows(): bool
{
return stripos(PHP_OS, 'WIN') === 0;
}
/**
* Generate the Release ID
*
* @return Runtime
*/
public function generateReleaseId()
public function generateReleaseId(): self
{
$this->setReleaseId(date('YmdHis'));
return $this;
@ -89,11 +86,8 @@ class Runtime
/**
* Sets the Release ID
*
* @param string $releaseId Release ID
* @return Runtime
*/
public function setReleaseId($releaseId)
public function setReleaseId(string $releaseId): self
{
$this->releaseId = $releaseId;
return $this;
@ -101,21 +95,16 @@ class Runtime
/**
* Retrieve the current Release ID
*
* @return null|string Release ID
*/
public function getReleaseId()
public function getReleaseId(): ?string
{
return $this->releaseId;
}
/**
* Sets the Runtime in Rollback mode On or Off
*
* @param bool $inRollback
* @return Runtime
*/
public function setRollback($inRollback)
public function setRollback(bool $inRollback): self
{
$this->rollback = $inRollback;
return $this;
@ -123,35 +112,25 @@ class Runtime
/**
* Indicates if Runtime is in rollback
*
* @return bool
*/
public function inRollback()
public function inRollback(): bool
{
return $this->rollback;
}
/**
* Sets a value in the Vars bag
*
* @param string $key Variable name
* @param string $value Variable value
* @return Runtime
*/
public function setVar($key, $value)
public function setVar(string $key, string $value): self
{
$this->vars[$key] = $value;
return $this;
}
/**
* Retrieve a value from the Vars bag
*
* @param string $key Variable name
* @param mixed $default Variable default value, returned if not found
* @return string
* Retrieve a value from the Vars bag, or a default (null) if not set
*/
public function getVar($key, $default = null)
public function getVar(string $key, mixed $default = null): ?string
{
if (array_key_exists($key, $this->vars)) {
return $this->vars[$key];
@ -162,11 +141,8 @@ class Runtime
/**
* Sets the Logger instance
*
* @param LoggerInterface $logger Logger instance
* @return Runtime
*/
public function setLogger(LoggerInterface $logger = null)
public function setLogger(?LoggerInterface $logger = null): self
{
$this->logger = $logger;
return $this;
@ -175,10 +151,9 @@ class Runtime
/**
* Sets the Magallanes Configuration to the Runtime
*
* @param array $configuration Configuration
* @return Runtime
* @param array<string, mixed> $configuration
*/
public function setConfiguration($configuration)
public function setConfiguration(array $configuration): self
{
$this->configuration = $configuration;
return $this;
@ -187,21 +162,17 @@ class Runtime
/**
* Retrieve the Configuration
*
* @return array
* @return array<string, mixed> $configuration
*/
public function getConfiguration()
public function getConfiguration(): array
{
return $this->configuration;
}
/**
* Retrieves the Configuration Option for a specific section in the configuration
*
* @param string $key Section name
* @param mixed $default Default value
* @return mixed
*/
public function getConfigOption($key, $default = null)
public function getConfigOption(string $key, mixed $default = null): mixed
{
if (array_key_exists($key, $this->configuration)) {
return $this->configuration[$key];
@ -212,14 +183,13 @@ class Runtime
/**
* Returns the Configuration Option for a specific section the current Environment
*
* @param string $key Section/Parameter name
* @param mixed $default Default value
* @return mixed
*/
public function getEnvOption($key, $default = null)
public function getEnvOption(string $key, mixed $default = null): mixed
{
if (!array_key_exists('environments', $this->configuration) || !is_array($this->configuration['environments'])) {
if (
!array_key_exists('environments', $this->configuration) ||
!is_array($this->configuration['environments'])
) {
return $default;
}
@ -238,12 +208,10 @@ class Runtime
* 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 $key
* @param array $defaultEnv
*
* @return array
* @param array<string, mixed> $defaultEnv
* @return array<string, mixed>
*/
public function getMergedOption($key, $defaultEnv = [])
public function getMergedOption(string $key, array $defaultEnv = []): array
{
$userGlobalOptions = $this->getConfigOption($key, $defaultEnv);
$userEnvOptions = $this->getEnvOption($key, $defaultEnv);
@ -256,12 +224,8 @@ class Runtime
/**
* Overwrites an Environment Configuration Option
*
* @param string $key
* @param mixed $value
* @return Runtime
*/
public function setEnvOption($key, $value)
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'])) {
@ -275,13 +239,14 @@ class Runtime
/**
* Sets the working Environment
*
* @param string $environment Environment name
* @return Runtime
* @throws RuntimeException
*/
public function setEnvironment($environment)
public function setEnvironment(string $environment): self
{
if (array_key_exists('environments', $this->configuration) && array_key_exists($environment, $this->configuration['environments'])) {
if (
array_key_exists('environments', $this->configuration) &&
array_key_exists($environment, $this->configuration['environments'])
) {
$this->environment = $environment;
return $this;
}
@ -291,21 +256,16 @@ class Runtime
/**
* Returns the current working Environment
*
* @return null|string
*/
public function getEnvironment()
public function getEnvironment(): ?string
{
return $this->environment;
}
/**
* Sets the working stage
*
* @param string $stage Stage code
* @return Runtime
*/
public function setStage($stage)
public function setStage(string $stage): self
{
$this->stage = $stage;
return $this;
@ -313,10 +273,8 @@ class Runtime
/**
* Retrieve the current working Stage
*
* @return string
*/
public function getStage()
public function getStage(): ?string
{
return $this->stage;
}
@ -324,11 +282,14 @@ class Runtime
/**
* Retrieve the defined Tasks for the current Environment and Stage
*
* @return array
* @return string[]
*/
public function getTasks()
public function getTasks(): array
{
if (!array_key_exists('environments', $this->configuration) || !is_array($this->configuration['environments'])) {
if (
!array_key_exists('environments', $this->configuration) ||
!is_array($this->configuration['environments'])
) {
return [];
}
@ -347,11 +308,8 @@ class Runtime
/**
* Sets the working Host
*
* @param string $host Host name
* @return Runtime
*/
public function setWorkingHost($host)
public function setWorkingHost(?string $host): self
{
$this->workingHost = $host;
return $this;
@ -359,21 +317,16 @@ class Runtime
/**
* Retrieve the working Host
*
* @return null|string
*/
public function getWorkingHost()
public function getWorkingHost(): ?string
{
return $this->workingHost;
}
/**
* Logs a Message into the Logger
*
* @param string $message Log message
* @param string $level Log Level
*/
public function log($message, $level = LogLevel::DEBUG)
public function log(string $message, string $level = LogLevel::DEBUG): void
{
if ($this->logger instanceof LoggerInterface) {
$this->logger->log($level, $message);
@ -382,12 +335,8 @@ class Runtime
/**
* Executes a command, it will be run Locally or Remotely based on the working Stage
*
* @param string $cmd Command to execute
* @param int $timeout Seconds to wait
* @return Process
*/
public function runCommand($cmd, $timeout = 120)
public function runCommand(string $cmd, int $timeout = 120): Process
{
switch ($this->getStage()) {
case self::ON_DEPLOY:
@ -401,12 +350,8 @@ class Runtime
/**
* Execute a command locally
*
* @param string $cmd Command to execute
* @param int $timeout Seconds to wait
* @return Process
*/
public function runLocalCommand($cmd, $timeout = 120)
public function runLocalCommand(string $cmd, int $timeout = 120): Process
{
$this->log($cmd, LogLevel::INFO);
@ -424,13 +369,8 @@ class Runtime
/**
* Executes a command remotely, if jail is true, it will run inside the Host Path and the Release (if available)
*
* @param string $cmd Command to execute
* @param bool $jail Jail the command
* @param int $timeout Seconds to wait
* @return Process
*/
public function runRemoteCommand($cmd, $jail, $timeout = 120)
public function runRemoteCommand(string $cmd, bool $jail, int $timeout = 120): Process
{
$user = $this->getEnvOption('user', $this->getCurrentUser());
$sudo = $this->getEnvOption('sudo', false);
@ -450,7 +390,14 @@ class Runtime
}
$cmdRemote = str_replace('"', '\"', $cmdDelegate);
$cmdLocal = sprintf('ssh -p %d %s %s@%s "%s"', $sshConfig['port'], $sshConfig['flags'], $user, $host, $cmdRemote);
$cmdLocal = sprintf(
'ssh -p %d %s %s@%s "%s"',
$sshConfig['port'],
$sshConfig['flags'],
$user,
$host,
$cmdRemote
);
return $this->runLocalCommand($cmdLocal, $timeout);
}
@ -458,11 +405,17 @@ class Runtime
/**
* Get the SSH configuration based on the environment
*
* @return array
* @return array<string, string>
*/
public function getSSHConfig()
public function getSSHConfig(): array
{
$sshConfig = $this->getEnvOption('ssh', ['port' => 22, 'flags' => '-q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no']);
$sshConfig = $this->getEnvOption(
'ssh',
[
'port' => 22,
'flags' => '-q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
]
);
if ($this->getHostPort() !== null) {
$sshConfig['port'] = $this->getHostPort();
@ -485,46 +438,38 @@ class Runtime
/**
* Get the current Host Port or default ssh port
*
* @return integer
*/
public function getHostPort()
public function getHostPort(): ?int
{
$info = explode(':', $this->getWorkingHost());
return isset($info[1]) ? $info[1] : null;
return isset($info[1]) ? intval($info[1]) : null;
}
/**
* Get the current Host Name
*
* @return string
*/
public function getHostName()
public function getHostName(): ?string
{
if (strpos($this->getWorkingHost(), ':') === false) {
return $this->getWorkingHost();
}
$info = explode(':', $this->getWorkingHost());
return $info[0];
return strval($info[0]);
}
/**
* Gets a Temporal File name
*
* @return string
*/
public function getTempFile()
public function getTempFile(): string
{
return tempnam(sys_get_temp_dir(), 'mage');
}
/**
* Get the current user
*
* @return string
*/
public function getCurrentUser()
public function getCurrentUser(): string
{
$userData = posix_getpwuid(posix_geteuid());
return $userData['name'];
@ -533,19 +478,17 @@ class Runtime
/**
* Shortcut for getting Branch information
*
* @return boolean|string
* @return bool|string
*/
public function getBranch()
public function getBranch(): mixed
{
return $this->getEnvOption('branch', false);
}
/**
* Guesses the Deploy Strategy to use
*
* @return StrategyInterface
*/
public function guessStrategy()
public function guessStrategy(): StrategyInterface
{
$strategy = new RsyncStrategy();

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,60 +20,41 @@ use Mage\Runtime\Runtime;
*/
abstract class AbstractTask
{
/**
* @var array Task custom options
*/
protected $options = [];
/** @var array<string, string|int|null> */
protected array $options = [];
/**
* @var Runtime
*/
protected $runtime;
protected Runtime $runtime;
/**
* Get the Name/Code of the Task
*
* @return string
*/
abstract public function getName();
abstract public function getName(): string;
/**
* Get a short Description of the Task
*
* @return string
*/
abstract public function getDescription();
abstract public function getDescription(): string;
/**
* Executes the Command
*
* @return bool
*/
abstract public function execute();
abstract public function execute(): bool;
/**
* Set additional Options for the Task
*
* @param array $options Options
* @return AbstractTask
* @param array<string, string|int|null> $options
*/
public function setOptions($options = [])
public function setOptions(array $options = []): self
{
if (!is_array($options)) {
$options = [];
}
$this->options = array_merge($this->getDefaults(), $options);
return $this;
}
/**
* Set the Runtime instance
*
* @param Runtime $runtime
* @return AbstractTask
*/
public function setRuntime(Runtime $runtime)
public function setRuntime(Runtime $runtime): self
{
$this->runtime = $runtime;
return $this;
@ -80,9 +62,10 @@ abstract class AbstractTask
/**
* Return Default options
* @return array
*
* @return array<string, string|int|null>
*/
public function getDefaults()
public function getDefaults(): array
{
return [];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,7 +20,10 @@ use Mage\Task\AbstractTask;
*/
abstract class AbstractComposerTask extends AbstractTask
{
protected function getOptions()
/**
* @return string[]
*/
protected function getOptions(): array
{
$options = array_merge(
['path' => 'composer'],
@ -31,7 +35,10 @@ abstract class AbstractComposerTask extends AbstractTask
return $options;
}
protected function getComposerOptions()
/**
* @return array<string, string|int>
*/
protected function getComposerOptions(): array
{
return [];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,17 +20,17 @@ use Symfony\Component\Process\Process;
*/
class DumpAutoloadTask extends AbstractComposerTask
{
public function getName()
public function getName(): string
{
return 'composer/dump-autoload';
}
public function getDescription()
public function getDescription(): string
{
return '[Composer] Dump Autoload';
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
$cmd = sprintf('%s dump-autoload %s', $options['path'], $options['flags']);
@ -40,7 +41,7 @@ class DumpAutoloadTask extends AbstractComposerTask
return $process->isSuccessful();
}
protected function getComposerOptions()
protected function getComposerOptions(): array
{
return ['flags' => '--optimize'];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,28 +20,28 @@ use Symfony\Component\Process\Process;
*/
class InstallTask extends AbstractComposerTask
{
public function getName()
public function getName(): string
{
return 'composer/install';
}
public function getDescription()
public function getDescription(): string
{
return '[Composer] Install';
}
public function execute()
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), $options['timeout']);
$process = $this->runtime->runCommand(trim($cmd), intval($options['timeout']));
return $process->isSuccessful();
}
protected function getComposerOptions()
protected function getComposerOptions(): array
{
return ['flags' => '--optimize-autoloader', 'timeout' => 120];
}

View File

@ -1,86 +0,0 @@
<?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\Exception\SkipException;
use Symfony\Component\Process\Process;
use DateTime;
/**
* Composer Task - Self update
*
* @author Yanick Witschi <https://github.com/Toflar>
*/
class SelfUpdateTask extends AbstractComposerTask
{
public function getName()
{
return 'composer/self-update';
}
public function getDescription()
{
return '[Composer] Self Update';
}
public function execute()
{
$options = $this->getOptions();
$cmdVersion = sprintf('%s --version', $options['path']);
/** @var Process $process */
$process = $this->runtime->runCommand(trim($cmdVersion));
if (!$process->isSuccessful()) {
return false;
}
$buildDate = $this->getBuildDate($process->getOutput());
if (!$buildDate instanceof DateTime) {
return false;
}
$compareDate = $this->getCompareDate();
if ($buildDate >= $compareDate) {
throw new SkipException();
}
$cmdUpdate = sprintf('%s self-update', $options['path']);
/** @var Process $process */
$process = $this->runtime->runCommand(trim($cmdUpdate));
return $process->isSuccessful();
}
protected function getBuildDate($output)
{
$buildDate = null;
$output = explode("\n", $output);
foreach ($output as $row) {
if (strpos($row, 'Composer version ') === 0) {
$buildDate = DateTime::createFromFormat('Y-m-d H:i:s', substr(trim($row), -19));
}
}
return $buildDate;
}
protected function getCompareDate()
{
$options = $this->getOptions();
$compareDate = new DateTime();
$compareDate->modify(sprintf('now -%d days', $options['days']));
return $compareDate;
}
protected function getComposerOptions()
{
return ['days' => 60];
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -20,17 +21,17 @@ use Mage\Task\AbstractTask;
*/
class CleanupTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'deploy/release/cleanup';
}
public function getDescription()
public function getDescription(): string
{
return '[Release] Cleaning up old Releases';
}
public function execute()
public function execute(): bool
{
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');
$currentReleaseId = $this->runtime->getReleaseId();

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -20,17 +21,17 @@ use Mage\Task\AbstractTask;
*/
class PrepareTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'deploy/release/prepare';
}
public function getDescription()
public function getDescription(): string
{
return '[Release] Preparing Release';
}
public function execute()
public function execute(): bool
{
$hostPath = rtrim($this->runtime->getEnvOption('host_path'), '/');

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -22,17 +23,17 @@ use Mage\Task\AbstractTask;
*/
class ReleaseTask extends AbstractTask implements ExecuteOnRollbackInterface
{
public function getName()
public function getName(): string
{
return 'deploy/release';
}
public function getDescription()
public function getDescription(): string
{
return '[Release] Creating Symlink';
}
public function execute()
public function execute(): bool
{
if (!$this->runtime->getEnvOption('releases', false)) {
throw new ErrorException('This task is only available with releases enabled', 40);
@ -44,7 +45,7 @@ class ReleaseTask extends AbstractTask implements ExecuteOnRollbackInterface
$cmdLinkRelease = sprintf('cd %s && ln -snf releases/%s current', $hostPath, $releaseId);
/** @var Process $process */
$process = $this->runtime->runRemoteCommand($cmdLinkRelease, false, null);
$process = $this->runtime->runRemoteCommand($cmdLinkRelease, false, 0);
return $process->isSuccessful();
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -21,17 +22,17 @@ use Mage\Task\AbstractTask;
*/
class RsyncTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'deploy/rsync';
}
public function getDescription()
public function getDescription(): string
{
return '[Deploy] Copying files with Rsync';
}
public function execute()
public function execute(): bool
{
$flags = $this->runtime->getEnvOption('rsync', '-avz');
$sshConfig = $this->runtime->getSSHConfig();
@ -46,14 +47,24 @@ class RsyncTask extends AbstractTask
$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);
$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, null);
$process = $this->runtime->runLocalCommand($cmdRsync, 0);
return $process->isSuccessful();
}
protected function getExcludes()
protected function getExcludes(): string
{
$excludes = $this->runtime->getMergedOption('exclude', []);
$excludes = array_merge(['.git'], array_filter($excludes));

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -21,17 +22,17 @@ use Mage\Task\AbstractTask;
*/
class CleanupTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'deploy/tar/cleanup';
}
public function getDescription()
public function getDescription(): string
{
return '[Deploy] Cleanup Tar file';
}
public function execute()
public function execute(): bool
{
if (!$this->runtime->getEnvOption('releases', false)) {
throw new ErrorException('This task is only available with releases enabled', 40);

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -21,17 +22,17 @@ use Mage\Task\AbstractTask;
*/
class CopyTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'deploy/tar/copy';
}
public function getDescription()
public function getDescription(): string
{
return '[Deploy] Copying files with Tar';
}
public function execute()
public function execute(): bool
{
if (!$this->runtime->getEnvOption('releases', false)) {
throw new ErrorException('This task is only available with releases enabled', 40);
@ -50,10 +51,19 @@ class CopyTask extends AbstractTask
$tarLocal = $this->runtime->getVar('tar_local');
$tarRemote = basename($tarLocal);
$cmdCopy = sprintf('scp -P %d %s %s %s@%s:%s/%s', $sshConfig['port'], $sshConfig['flags'], $tarLocal, $user, $host, $targetDir, $tarRemote);
$cmdCopy = sprintf(
'scp -P %d %s %s %s@%s:%s/%s',
$sshConfig['port'],
$sshConfig['flags'],
$tarLocal,
$user,
$host,
$targetDir,
$tarRemote
);
/** @var Process $process */
$process = $this->runtime->runLocalCommand($cmdCopy, $sshConfig['timeout']);
$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);

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -21,17 +22,17 @@ use Mage\Task\AbstractTask;
*/
class PrepareTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'deploy/tar/prepare';
}
public function getDescription()
public function getDescription(): string
{
return '[Deploy] Preparing Tar file';
}
public function execute()
public function execute(): bool
{
if (!$this->runtime->getEnvOption('releases', false)) {
throw new ErrorException('This task is only available with releases enabled', 40);
@ -42,7 +43,10 @@ class PrepareTask extends AbstractTask
$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');
$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)) {
@ -56,7 +60,7 @@ class PrepareTask extends AbstractTask
return $process->isSuccessful();
}
protected function getExcludes()
protected function getExcludes(): string
{
$excludes = $this->runtime->getMergedOption('exclude', []);
$excludes = array_merge(['.git'], array_filter($excludes));

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -24,7 +25,7 @@ class ExecTask extends AbstractTask
/**
* @return string
*/
public function getName()
public function getName(): string
{
return 'exec';
}
@ -32,7 +33,7 @@ class ExecTask extends AbstractTask
/**
* @return string
*/
public function getDescription()
public function getDescription(): string
{
$options = $this->getOptions();
@ -48,7 +49,7 @@ class ExecTask extends AbstractTask
*
* @throws ErrorException
*/
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
@ -57,14 +58,14 @@ class ExecTask extends AbstractTask
}
/** @var Process $process */
$process = $this->runtime->runCommand($options['cmd'], $options['timeout']);
$process = $this->runtime->runCommand(strval($options['cmd']), intval($options['timeout']));
return $process->isSuccessful();
}
/**
* @return array
* @return array<string, string|int>
*/
protected function getOptions()
protected function getOptions(): array
{
$options = array_merge(
['cmd' => '', 'desc' => '', 'timeout' => 120],

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -23,10 +24,10 @@ abstract class AbstractFileTask extends AbstractTask
/**
* Returns the Task options
*
* @return array
* @return array<string, string|int|null>
* @throws ErrorException
*/
protected function getOptions()
protected function getOptions(): array
{
$mandatory = $this->getParameters();
$defaults = array_keys($this->getDefaults());
@ -44,18 +45,16 @@ abstract class AbstractFileTask extends AbstractTask
/**
* Returns the mandatory parameters
*
* @return array
* @return string[]
*/
abstract protected function getParameters();
abstract protected function getParameters(): array;
/**
* Returns a file with the placeholders replaced
*
* @param string $file
* @return string
* @throws ErrorException
*/
protected function getFile($file)
protected function getFile(string $file): string
{
$mapping = [
'%environment%' => $this->runtime->getEnvironment(),
@ -73,7 +72,7 @@ abstract class AbstractFileTask extends AbstractTask
return str_replace(
array_keys($mapping),
array_values($mapping),
$options[$file]
strval($options[$file])
);
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -11,7 +12,6 @@
namespace Mage\Task\BuiltIn\FS;
use Symfony\Component\Process\Process;
use Exception;
/**
* File System Task - Copy a File
@ -20,21 +20,21 @@ use Exception;
*/
class ChangeModeTask extends AbstractFileTask
{
public function getName()
public function getName(): string
{
return 'fs/chmod';
}
public function getDescription()
public function getDescription(): string
{
try {
return sprintf('[FS] Change mode of "%s" to "%s"', $this->getFile('file'), $this->options['mode']);
} catch (Exception $exception) {
} catch (\Exception $exception) {
return '[FS] Chmod [missing parameters]';
}
}
public function execute()
public function execute(): bool
{
$file = $this->getFile('file');
$mode = $this->options['mode'];
@ -48,12 +48,12 @@ class ChangeModeTask extends AbstractFileTask
return $process->isSuccessful();
}
protected function getParameters()
protected function getParameters(): array
{
return ['file', 'mode', 'flags'];
}
public function getDefaults()
public function getDefaults(): array
{
return ['flags' => null];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -11,7 +12,6 @@
namespace Mage\Task\BuiltIn\FS;
use Symfony\Component\Process\Process;
use Exception;
/**
* File System Task - Copy a File
@ -20,21 +20,21 @@ use Exception;
*/
class CopyTask extends AbstractFileTask
{
public function getName()
public function getName(): string
{
return 'fs/copy';
}
public function getDescription()
public function getDescription(): string
{
try {
return sprintf('[FS] Copy "%s" to "%s"', $this->getFile('from'), $this->getFile('to'));
} catch (Exception $exception) {
} catch (\Exception $exception) {
return '[FS] Copy [missing parameters]';
}
}
public function execute()
public function execute(): bool
{
$copyFrom = $this->getFile('from');
$copyTo = $this->getFile('to');
@ -48,12 +48,12 @@ class CopyTask extends AbstractFileTask
return $process->isSuccessful();
}
protected function getParameters()
protected function getParameters(): array
{
return ['from', 'to', 'flags'];
}
public function getDefaults()
public function getDefaults(): array
{
return ['flags' => '-p'];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -11,7 +12,6 @@
namespace Mage\Task\BuiltIn\FS;
use Symfony\Component\Process\Process;
use Exception;
/**
* File System Task - Symlink a File
@ -20,21 +20,21 @@ use Exception;
*/
class LinkTask extends AbstractFileTask
{
public function getName()
public function getName(): string
{
return 'fs/link';
}
public function getDescription()
public function getDescription(): string
{
try {
return sprintf('[FS] Link "%s" to "%s"', $this->getFile('from'), $this->getFile('to'));
} catch (Exception $exception) {
} catch (\Exception $exception) {
return '[FS] Link [missing parameters]';
}
}
public function execute()
public function execute(): bool
{
$linkFrom = $this->getFile('from');
$linkTo = $this->getFile('to');
@ -48,12 +48,12 @@ class LinkTask extends AbstractFileTask
return $process->isSuccessful();
}
protected function getParameters()
protected function getParameters(): array
{
return ['from', 'to', 'flags'];
}
public function getDefaults()
public function getDefaults(): array
{
return ['flags' => '-snf'];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -20,12 +21,12 @@ use Exception;
*/
class MoveTask extends AbstractFileTask
{
public function getName()
public function getName(): string
{
return 'fs/move';
}
public function getDescription()
public function getDescription(): string
{
try {
return sprintf('[FS] Move "%s" to "%s"', $this->getFile('from'), $this->getFile('to'));
@ -34,7 +35,7 @@ class MoveTask extends AbstractFileTask
}
}
public function execute()
public function execute(): bool
{
$moveFrom = $this->getFile('from');
$moveTo = $this->getFile('to');
@ -48,12 +49,12 @@ class MoveTask extends AbstractFileTask
return $process->isSuccessful();
}
protected function getParameters()
protected function getParameters(): array
{
return ['from', 'to', 'flags'];
}
public function getDefaults()
public function getDefaults(): array
{
return ['flags' => null];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -11,7 +12,6 @@
namespace Mage\Task\BuiltIn\FS;
use Symfony\Component\Process\Process;
use Exception;
/**
* File System Task - Remove a File
@ -20,21 +20,21 @@ use Exception;
*/
class RemoveTask extends AbstractFileTask
{
public function getName()
public function getName(): string
{
return 'fs/remove';
}
public function getDescription()
public function getDescription(): string
{
try {
return sprintf('[FS] Remove "%s"', $this->getFile('file'));
} catch (Exception $exception) {
} catch (\Exception $exception) {
return '[FS] Remove [missing parameters]';
}
}
public function execute()
public function execute(): bool
{
$file = $this->getFile('file');
$flags = $this->options['flags'];
@ -47,12 +47,12 @@ class RemoveTask extends AbstractFileTask
return $process->isSuccessful();
}
protected function getParameters()
protected function getParameters(): array
{
return ['file', 'flags'];
}
public function getDefaults()
public function getDefaults(): array
{
return ['flags' => null];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -21,29 +22,30 @@ use Mage\Task\AbstractTask;
*/
class ChangeBranchTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'git/change-branch';
}
public function getDescription()
public function getDescription(): string
{
$options = $this->getOptions();
$branch = $options['branch'];
if ($this->runtime->getVar('git_revert_branch', false)) {
if ($this->runtime->getVar('git_revert_branch', null)) {
$branch = $this->runtime->getVar('git_revert_branch');
}
return sprintf('[Git] Change Branch (%s)', $branch);
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
$branch = $this->runtime->getVar('git_revert_branch', false);
/** @var string|bool */
$branch = $this->runtime->getVar('git_revert_branch', null);
if ($branch === false) {
if (!$branch) {
$cmdGetCurrent = sprintf('%s branch | grep "*"', $options['path']);
/** @var Process $process */
@ -68,7 +70,10 @@ class ChangeBranchTask extends AbstractTask
return $process->isSuccessful();
}
protected function getOptions()
/**
* @return array<string, string>
*/
protected function getOptions(): array
{
$branch = $this->runtime->getEnvOption('branch', 'master');
$options = array_merge(

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -20,28 +21,30 @@ use Mage\Task\AbstractTask;
*/
class UpdateTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'git/update';
}
public function getDescription()
public function getDescription(): string
{
return '[Git] Update';
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
$command = $options['path'] . ' pull';
/** @var Process $process */
$process = $this->runtime->runLocalCommand($command);
return $process->isSuccessful();
}
protected function getOptions()
/**
* @return array<string, string>
*/
protected function getOptions(): array
{
$branch = $this->runtime->getEnvOption('branch', 'master');
$options = array_merge(

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,7 +20,10 @@ use Mage\Task\AbstractTask;
*/
abstract class AbstractSymfonyTask extends AbstractTask
{
protected function getOptions()
/**
* @return array<string, string>
*/
protected function getOptions(): array
{
$options = array_merge(
['console' => 'bin/console', 'env' => 'dev', 'flags' => ''],
@ -31,7 +35,10 @@ abstract class AbstractSymfonyTask extends AbstractTask
return $options;
}
protected function getSymfonyOptions()
/**
* @return array<string, string|null>
*/
protected function getSymfonyOptions(): array
{
return [];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,28 +20,33 @@ use Symfony\Component\Process\Process;
*/
class AssetsInstallTask extends AbstractSymfonyTask
{
public function getName()
public function getName(): string
{
return 'symfony/assets-install';
}
public function getDescription()
public function getDescription(): string
{
return '[Symfony] Assets Install';
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
$command = sprintf('%s assets:install %s --env=%s %s', $options['console'], $options['target'], $options['env'], $options['flags']);
$command = sprintf(
'%s assets:install %s --env=%s %s',
$options['console'],
$options['target'],
$options['env'],
$options['flags']
);
/** @var Process $process */
$process = $this->runtime->runCommand(trim($command));
return $process->isSuccessful();
}
protected function getSymfonyOptions()
protected function getSymfonyOptions(): array
{
return ['target' => 'web', 'flags' => '--symlink --relative'];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,17 +20,17 @@ use Symfony\Component\Process\Process;
*/
class CacheClearTask extends AbstractSymfonyTask
{
public function getName()
public function getName(): string
{
return 'symfony/cache-clear';
}
public function getDescription()
public function getDescription(): string
{
return '[Symfony] Cache Clear';
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
$command = $options['console'] . ' cache:clear --env=' . $options['env'] . ' ' . $options['flags'];

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -20,17 +21,17 @@ use Symfony\Component\Process\Process;
*/
class CachePoolClearTask extends AbstractSymfonyTask
{
public function getName()
public function getName(): string
{
return 'symfony/cache-pool-clear';
}
public function getDescription()
public function getDescription(): string
{
return '[Symfony] Cache Pool Clear';
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
@ -38,7 +39,13 @@ class CachePoolClearTask extends AbstractSymfonyTask
throw new ErrorException('Parameter "pools" is not defined');
}
$command = $options['console'] . ' cache:pool:clear ' . $options['pools'] . ' --env=' . $options['env'] . ' ' . $options['flags'];
$command = sprintf(
'%s cache:pool:clear %s --env=%s %s',
$options['console'],
$options['pools'],
$options['env'],
$options['flags']
);
/** @var Process $process */
$process = $this->runtime->runCommand(trim($command));
@ -46,7 +53,7 @@ class CachePoolClearTask extends AbstractSymfonyTask
return $process->isSuccessful();
}
protected function getSymfonyOptions()
protected function getSymfonyOptions(): array
{
return ['pools' => null];
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,17 +20,17 @@ use Symfony\Component\Process\Process;
*/
class CachePoolPruneTask extends AbstractSymfonyTask
{
public function getName()
public function getName(): string
{
return 'symfony/cache-pool-prune';
}
public function getDescription()
public function getDescription(): string
{
return '[Symfony] Cache Pool Prune';
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
$command = $options['console'] . ' cache:pool:prune --env=' . $options['env'] . ' ' . $options['flags'];

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -19,17 +20,17 @@ use Symfony\Component\Process\Process;
*/
class CacheWarmupTask extends AbstractSymfonyTask
{
public function getName()
public function getName(): string
{
return 'symfony/cache-warmup';
}
public function getDescription()
public function getDescription(): string
{
return '[Symfony] Cache Warmup';
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
$command = $options['console'] . ' cache:warmup --env=' . $options['env'] . ' ' . $options['flags'];

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -10,16 +11,14 @@
namespace Mage\Task\Exception;
use Exception;
/**
* The Task Failed, and it has a Custom Message
*
* @author Andrés Montañez <andresmontanez@gmail.com>
*/
class ErrorException extends Exception
class ErrorException extends \Exception
{
public function getTrimmedMessage($maxLength = 60)
public function getTrimmedMessage(int $maxLength = 60): string
{
$message = $this->getMessage();

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -10,13 +11,11 @@
namespace Mage\Task\Exception;
use Exception;
/**
* The Task will be Skipped
*
* @author Andrés Montañez <andresmontanez@gmail.com>
*/
class SkipException extends Exception
class SkipException extends \Exception
{
}

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -23,20 +24,15 @@ use ReflectionClass;
*/
class TaskFactory
{
/**
* @var Runtime
*/
protected $runtime;
protected Runtime $runtime;
/**
* @var array Registered Tasks
* @var AbstractTask[] Registered Tasks
*/
protected $registeredTasks = [];
protected array $registeredTasks = [];
/**
* Constructor
*
* @param Runtime $runtime
*/
public function __construct(Runtime $runtime)
{
@ -47,10 +43,8 @@ class TaskFactory
/**
* Add a Task
*
* @param AbstractTask $task
*/
public function add(AbstractTask $task)
public function add(AbstractTask $task): void
{
$task->setRuntime($this->runtime);
$this->registeredTasks[$task->getName()] = $task;
@ -60,11 +54,10 @@ class TaskFactory
* Get a Task by it's registered Name/Code, or it can be a Class Name,
* in that case the class will be instantiated
*
* @param string $name Name/Code or Class of the Task
* @return AbstractTask
* @param string|mixed[] $name
* @throws RuntimeException
*/
public function get($name)
public function get(mixed $name): AbstractTask
{
$options = [];
if (is_array($name)) {
@ -96,14 +89,22 @@ class TaskFactory
/**
* Load BuiltIn Tasks
*/
protected function loadBuiltInTasks()
protected function loadBuiltInTasks(): void
{
$finder = new Finder();
$finder->files()->in(__DIR__ . '/BuiltIn')->name('*Task.php');
/** @var SplFileInfo $file */
foreach ($finder as $file) {
$taskClass = substr('\\Mage\\Task\\BuiltIn\\' . str_replace('/', '\\', $file->getRelativePathname()), 0, -4);
$taskClass = substr(
'\\Mage\\Task\\BuiltIn\\' . str_replace(
'/',
'\\',
$file->getRelativePathname()
),
0,
-4
);
if (class_exists($taskClass)) {
$reflex = new ReflectionClass($taskClass);
if ($reflex->isInstantiable()) {
@ -118,10 +119,11 @@ class TaskFactory
/**
* Load Custom Tasks
* @param array $tasksToLoad PreRegistered Tasks
*
* @param string[] $tasksToLoad
* @throws RuntimeException
*/
protected function loadCustomTasks($tasksToLoad)
protected function loadCustomTasks(array $tasksToLoad): void
{
foreach ($tasksToLoad as $taskClass) {
if (!class_exists($taskClass)) {
@ -135,7 +137,9 @@ class TaskFactory
$task = new $taskClass();
if (!$task instanceof AbstractTask) {
throw new RuntimeException(sprintf('Custom Task "%s" must inherit "Mage\\Task\\AbstractTask".', $taskClass));
throw new RuntimeException(
sprintf('Custom Task "%s" must inherit "Mage\\Task\\AbstractTask".', $taskClass)
);
}
// Add Task

View File

@ -1,4 +1,5 @@
<?php
/*
* This file is part of the Magallanes package.
*
@ -11,8 +12,6 @@
namespace Mage;
use Mage\Runtime\Runtime;
use DateTime;
use DateInterval;
/**
* Utility class for resolving trivial operations
@ -23,11 +22,8 @@ class Utils
{
/**
* Given a stage code it will resolve a human friendly name
*
* @param string $stage
* @return string
*/
public function getStageName($stage)
public function getStageName(string $stage): string
{
switch ($stage) {
case Runtime::PRE_DEPLOY:
@ -51,35 +47,36 @@ class Utils
/**
* Given a Release ID, convert it to a DateTime instance
*
* @param string $releaseId The Release ID
* @return DateTime
*/
public function getReleaseDate($releaseId)
public function getReleaseDate(string $releaseId): \DateTime
{
$formatted = sprintf('%d%d%d%d-%d%d-%d%d %d%d:%d%d:%d%d',
$releaseId[0], $releaseId[1], $releaseId[2], $releaseId[3],
$releaseId[4], $releaseId[5],
$releaseId[6], $releaseId[7],
$releaseId[8], $releaseId[9],
$releaseId[10], $releaseId[11],
$releaseId[12], $releaseId[13]
$formatted = sprintf(
'%d%d%d%d-%d%d-%d%d %d%d:%d%d:%d%d',
$releaseId[0],
$releaseId[1],
$releaseId[2],
$releaseId[3],
$releaseId[4],
$releaseId[5],
$releaseId[6],
$releaseId[7],
$releaseId[8],
$releaseId[9],
$releaseId[10],
$releaseId[11],
$releaseId[12],
$releaseId[13]
);
return new DateTime($formatted);
return new \DateTime($formatted);
}
/**
* Given a Date, calculate friendly how much time has passed
*
* @param DateTime $releaseDate
* @return string
*/
public function getTimeDiff(DateTime $releaseDate)
public function getTimeDiff(\DateTime $releaseDate): string
{
$now = new DateTime();
/** @var DateInterval $diff */
$now = new \DateTime();
$diff = $now->diff($releaseDate);
if ($diff->days > 7) {

View File

@ -530,6 +530,55 @@ class DeployCommandWithReleasesTest extends TestCase
$this->assertNotEquals(0, $tester->getStatusCode());
}
public function testDeploymentFailCleanupReleases()
{
$application = new MageApplicationMockup(__DIR__ . '/../../Resources/testhost.yml');
$application->getRuntime()->setReleaseId('20170101015120');
/** @var AbstractCommand $command */
$command = $application->find('deploy');
$this->assertTrue($command instanceof DeployCommand);
$application->getRuntime()->forceFail('ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "rm -rf /var/www/test/releases/20170101015110"');
$tester = new CommandTester($command);
$tester->execute(['command' => $command->getName(), 'environment' => 'test']);
$ranCommands = $application->getRuntime()->getRanCommands();
$testCase = array(
0 => 'git branch | grep "*"',
1 => 'git checkout test',
2 => 'git pull',
3 => 'composer install --optimize-autoloader',
4 => 'composer dump-autoload --optimize',
5 => 'tar cfzp /tmp/mageXYZ --exclude=".git" --exclude="./var/cache/*" --exclude="./var/log/*" --exclude="./web/app_dev.php" ./',
6 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "mkdir -p /var/www/test/releases/1234567890"',
7 => 'scp -P 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no /tmp/mageXYZ tester@testhost:/var/www/test/releases/1234567890/mageXYZ',
8 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "cd /var/www/test/releases/1234567890 && tar xfzop mageXYZ"',
9 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "rm /var/www/test/releases/1234567890/mageXYZ"',
10 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "cd /var/www/test/releases/1234567890 && bin/console cache:warmup --env=dev"',
11 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "cd /var/www/test/releases/1234567890 && bin/console assets:install web --env=dev --symlink --relative"',
12 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "cd /var/www/test/releases/1234567890 && bin/console cache:pool:prune --env=dev"',
13 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "cd /var/www/test && ln -snf releases/1234567890 current"',
14 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "ls -1 /var/www/test/releases"',
15 => 'ssh -p 22 -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tester@testhost "rm -rf /var/www/test/releases/20170101015110"',
);
// Check total of Executed Commands
$this->assertEquals(count($testCase), count($ranCommands));
// Check Generated Commands
foreach ($testCase as $index => $command) {
$this->assertEquals($command, $ranCommands[$index]);
}
$this->assertStringContainsString('Running [Release] Cleaning up old Releases ... FAIL', $tester->getDisplay());
$this->assertStringContainsString('Stage "Post Release" did not finished successfully, halting command.', $tester->getDisplay());
$this->assertNotEquals(0, $tester->getStatusCode());
}
public function testDeploymentFailMidway()
{
$application = new MageApplicationMockup(__DIR__ . '/../../Resources/testhost.yml');

View File

@ -20,7 +20,7 @@ class MageApplicationMockup extends MageApplication
*
* @return RuntimeMockup
*/
protected function instantiateRuntime()
protected function instantiateRuntime(): RuntimeMockup
{
return new RuntimeMockup();
}

View File

@ -12,6 +12,7 @@ namespace Mage\Tests;
use Mage\Tests\Runtime\RuntimeWindowsMockup;
use Mage\MageApplication;
use Mage\Runtime\Runtime;
class MageApplicationWindowsMockup extends MageApplication
{
@ -20,7 +21,7 @@ class MageApplicationWindowsMockup extends MageApplication
*
* @return RuntimeWindowsMockup
*/
protected function instantiateRuntime()
protected function instantiateRuntime(): Runtime
{
return new RuntimeWindowsMockup();
}

View File

@ -24,9 +24,10 @@ class ProcessMockup extends Process
$this->commandline = $commandline;
}
public function setTimeout($timeout)
public function setTimeout(?float $timeout): static
{
$this->timeout = $timeout;
return $this;
}
public function run(callable $callback = null, array $env = array()): int
@ -58,17 +59,17 @@ class ProcessMockup extends Process
return 0;
}
public function isSuccessful()
public function isSuccessful(): bool
{
return $this->success;
}
public function getErrorOutput()
public function getErrorOutput(): string
{
return '';
}
public function getOutput()
public function getOutput(): string
{
if ($this->commandline == 'git branch | grep "*"') {
return '* master';

View File

@ -31,10 +31,8 @@ class RuntimeMockup extends Runtime
/**
* Generate the Release ID
*
* @return Runtime
*/
public function generateReleaseId()
public function generateReleaseId(): Runtime
{
$this->setReleaseId('1234567890');
return $this;
@ -42,12 +40,8 @@ class RuntimeMockup extends Runtime
/**
* Execute a command locally
*
* @param string $cmd Command to execute
* @param int $timeout Seconds to wait
* @return Process
*/
public function runLocalCommand($cmd, $timeout = 120)
public function runLocalCommand(string $cmd, int $timeout = 120): Process
{
$this->ranCommands[] = $cmd;
$this->ranCommandTimeouts[$cmd] = $timeout;
@ -62,10 +56,8 @@ class RuntimeMockup extends Runtime
/**
* Gets a Temporal File name
*
* @return string
*/
public function getTempFile()
public function getTempFile(): string
{
return '/tmp/mageXYZ';
}
@ -76,13 +68,13 @@ class RuntimeMockup extends Runtime
* @param string $environment
* @return Runtime
*/
public function setInvalidEnvironment($environment)
public function setInvalidEnvironment($environment): Runtime
{
$this->environment = $environment;
return $this;
}
public function forceFail($cmd)
public function forceFail($cmd): void
{
$this->forceFail[] = $cmd;
}

View File

@ -12,7 +12,7 @@ namespace Mage\Tests\Runtime;
class RuntimeWindowsMockup extends RuntimeMockup
{
public function isWindows()
public function isWindows(): bool
{
return true;
}

View File

@ -16,14 +16,6 @@ use PHPUnit\Framework\TestCase;
class AbstractTaskTest extends TestCase
{
public function testNotArrayOptions()
{
$task = new TestCaseTask();
$task->setOptions('not an array');
$this->assertTrue(is_array($task->getOptions()));
}
public function testFailingTask()
{
$task = new TestCaseFailTask();

View File

@ -20,17 +20,17 @@ use Symfony\Component\Process\Process;
*/
class BasicComposerTask extends AbstractComposerTask
{
public function getName()
public function getName(): string
{
return 'composer/help';
}
public function getDescription()
public function getDescription(): string
{
return '[Composer] Help';
}
public function execute()
public function execute(): bool
{
$options = $this->getOptions();
$cmd = sprintf('%s help', $options['path']);

View File

@ -1,174 +0,0 @@
<?php
namespace Mage\Tests\Task\BuiltIn\Composer;
use Mage\Tests\Runtime\RuntimeMockup;
use Mage\Task\BuiltIn\Composer\SelfUpdateTask;
use Mage\Task\Exception\SkipException;
use Exception;
use PHPUnit\Framework\TestCase;
class SelfUpdateTaskTest extends TestCase
{
public function testSelfUpdateTask()
{
$runtime = new RuntimeMockup();
$runtime->setConfiguration(['environments' => ['test' => []]]);
$runtime->setEnvironment('test');
$task = new SelfUpdateTask();
$task->setOptions(['path' => 'composer']);
$task->setRuntime($runtime);
$this->assertEquals('[Composer] Self Update', $task->getDescription());
try {
$task->execute();
} catch (Exception $exception) {
$this->assertTrue($exception instanceof SkipException, 'Update should been skipped');
}
$ranCommands = $runtime->getRanCommands();
$testCase = array(
0 => 'composer --version',
);
// Check total of Executed Commands
$this->assertEquals(count($testCase), count($ranCommands));
// Check Generated Commands
foreach ($testCase as $index => $command) {
$this->assertEquals($command, $ranCommands[$index]);
}
}
public function testSelfUpdateAsRootTask()
{
$runtime = new RuntimeMockup();
$runtime->setConfiguration(['environments' => ['test' => []]]);
$runtime->setEnvironment('test');
$task = new SelfUpdateTask();
$task->setOptions(['path' => './composer']);
$task->setRuntime($runtime);
try {
$task->execute();
} catch (Exception $exception) {
$this->assertTrue($exception instanceof SkipException, 'Update should been skipped');
}
$ranCommands = $runtime->getRanCommands();
$testCase = array(
0 => './composer --version',
);
// Check total of Executed Commands
$this->assertEquals(count($testCase), count($ranCommands));
// Check Generated Commands
foreach ($testCase as $index => $command) {
$this->assertEquals($command, $ranCommands[$index]);
}
}
public function testSelfUpdateMustUpdateTask()
{
$runtime = new RuntimeMockup();
$runtime->setConfiguration(['environments' => ['test' => []]]);
$runtime->setEnvironment('test');
$task = new SelfUpdateTask();
$task->setOptions(['path' => 'composer.phar']);
$task->setRuntime($runtime);
try {
$result = $task->execute();
$this->assertTrue($result, 'Result should be successful');
} catch (Exception $exception) {
if ($exception instanceof SkipException) {
$this->assertTrue(false, 'Update should not have been skipped');
}
}
$ranCommands = $runtime->getRanCommands();
$testCase = array(
0 => 'composer.phar --version',
1 => 'composer.phar self-update',
);
// Check total of Executed Commands
$this->assertEquals(count($testCase), count($ranCommands));
// Check Generated Commands
foreach ($testCase as $index => $command) {
$this->assertEquals($command, $ranCommands[$index]);
}
}
public function testSelfUpdateWrongOutputTask()
{
$runtime = new RuntimeMockup();
$runtime->setConfiguration(['environments' => ['test' => []]]);
$runtime->setEnvironment('test');
$task = new SelfUpdateTask();
$task->setOptions(['path' => 'php composer']);
$task->setRuntime($runtime);
try {
$result = $task->execute();
$this->assertFalse($result, 'Result should be failure');
} catch (Exception $exception) {
if ($exception instanceof SkipException) {
$this->assertTrue(false, 'Update should not have been skipped');
}
}
$ranCommands = $runtime->getRanCommands();
$testCase = array(
0 => 'php composer --version',
);
// Check total of Executed Commands
$this->assertEquals(count($testCase), count($ranCommands));
// Check Generated Commands
foreach ($testCase as $index => $command) {
$this->assertEquals($command, $ranCommands[$index]);
}
}
public function testSelfUpdateFailExecTask()
{
$runtime = new RuntimeMockup();
$runtime->setConfiguration(['environments' => ['test' => []]]);
$runtime->setEnvironment('test');
$task = new SelfUpdateTask();
$task->setOptions(['path' => 'composer']);
$task->setRuntime($runtime);
$runtime->forceFail('composer --version');
try {
$result = $task->execute();
$this->assertFalse($result, 'Result should be failure');
} catch (Exception $exception) {
if ($exception instanceof SkipException) {
$this->assertTrue(false, 'Update should not have been skipped');
}
}
$ranCommands = $runtime->getRanCommands();
$testCase = array(
0 => 'composer --version',
);
// Check total of Executed Commands
$this->assertEquals(count($testCase), count($ranCommands));
// Check Generated Commands
foreach ($testCase as $index => $command) {
$this->assertEquals($command, $ranCommands[$index]);
}
}
}

View File

@ -23,7 +23,7 @@ abstract class NotInstantiableTask extends AbstractTask
/**
* @return string
*/
public function getName()
public function getName(): string
{
return 'custom-not-instantiable';
}
@ -31,7 +31,7 @@ abstract class NotInstantiableTask extends AbstractTask
/**
* @return string
*/
public function getDescription()
public function getDescription(): string
{
return '[Custom] Not Instantiable*';
}
@ -39,7 +39,7 @@ abstract class NotInstantiableTask extends AbstractTask
/**
* @return bool
*/
public function execute()
public function execute(): bool
{
/** @var Process $process */
$process = $this->runtime->runCommand('echo "custom-not-instantiable"');

View File

@ -23,7 +23,7 @@ class ValidTask extends AbstractTask
/**
* @return string
*/
public function getName()
public function getName(): string
{
return 'custom-valid';
}
@ -31,7 +31,7 @@ class ValidTask extends AbstractTask
/**
* @return string
*/
public function getDescription()
public function getDescription(): string
{
return '[Custom] Valid*';
}
@ -39,7 +39,7 @@ class ValidTask extends AbstractTask
/**
* @return bool
*/
public function execute()
public function execute(): bool
{
/** @var Process $process */
$process = $this->runtime->runCommand('echo "custom-valid"');

View File

@ -19,17 +19,17 @@ use Mage\Task\AbstractTask;
*/
class CustomTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'custom';
}
public function getDescription()
public function getDescription(): string
{
return '[Custom] Dummy Task';
}
public function execute()
public function execute(): bool
{
return true;
}

View File

@ -15,17 +15,17 @@ use Mage\Task\AbstractTask;
class TestCaseFailTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'test-fail';
}
public function getDescription()
public function getDescription(): string
{
return '[Test] This is a Test Task which Fails';
}
public function execute()
public function execute(): bool
{
throw new ErrorException('This is a text with a lot of characters');
}

View File

@ -14,17 +14,17 @@ use Mage\Task\AbstractTask;
class TestCaseTask extends AbstractTask
{
public function getName()
public function getName(): string
{
return 'test';
}
public function getDescription()
public function getDescription(): string
{
return '[Test] This is a Test Task';
}
public function execute()
public function execute(): bool
{
return true;
}