Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Feature/refactor git and related config #283

Open
wants to merge 11 commits into
base: 3.8-beta
Choose a base branch
from
5 changes: 3 additions & 2 deletions docs/app-scaffold.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,10 @@ hosts:
port: {{ 1024 + random(20000) }}
type: dev
rootFolder: /var/www/{{ webRoot }}
gitRootFolder: /var/www
git:
rootFolder: /var/www
branch: develop
backupFolder: /var/www/backups
branch: develop
database:
name: {{ projectFolder|replace({'-': '_'}) }}_db
user: root
Expand Down
8 changes: 4 additions & 4 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -582,16 +582,16 @@ phab npm run build:css --config hostA
phab npm run lint --config hostB
```

This will run an npm command on the given configuration. Make sure, that your host config has `npm` as a need a `npmRootFolder` points to the folder containing package.json.
This will run an npm command on the given configuration. Make sure, that your host config has `npm` as a need a `npm.rootFolder` points to the folder containing package.json.

## yarn

```bash
phab npm yarn build:css --config hostA
phab npm yarn lint --config hostB
phab yarn build:css --config hostA
phab yarn lint --config hostB
```

This will run a yarn command on the given configuration. Make sure, that your host config has `yarn` as a need a `yarnRootFolder` points to the folder containing package.json.
This will run a yarn command on the given configuration. Make sure, that your host config has `yarn` as a need a `yarn.rootFolder` points to the folder containing package.json.

##variable:pull

Expand Down
44 changes: 30 additions & 14 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ hosts:
id: 01-example
label: An example category
rootFolder: /var/www/public
gitRootFolder: /var/www
git:
rootFolder: /var/www
siteFolder: /sites/default
filesFolder: /sites/default/files
backupFolder: /var/www/backups
Expand Down Expand Up @@ -201,14 +202,14 @@ This will print all host configuration for the host `staging`.

### Configuration for the git-method

* `gitRootFolder` the folder, where the git-repository is located. Defaults to `rootFolder`
* `branch` the name of the branch to use for deployments, they get usually checked out and pulled from origin.
* `ignoreSubmodules` default is false, set to false, if you don't want to update a projects' submodule on deploy.
* `gitOptions` a keyed list of options to apply to a git command. Currently only pull is supported. If your git-version does not support `--rebase` you can disable it via an empty array: `pull: []`
* `git.rootFolder` the folder, where the git-repository is located. Defaults to `rootFolder`
* `git.branch` the name of the branch to use for deployments, they get usually checked out and pulled from origin.
* `git.ignoreSubmodules` default is false, set to false, if you don't want to update a projects' submodule on deploy.
* `git.options` a keyed list of options to apply to a git command. Currently only pull is supported. If your git-version does not support `--rebase` you can disable it via an empty array: `pull: []`

### Configuration for the composer-method

* `composerRootFolder` the folder where the composer.json for the project is stored, defaults to `gitRootFolder`.
* `composer.rootFolder` the folder where the composer.json for the project is stored, defaults to `git.rootFolder`.

### Configuration for the drush-method

Expand Down Expand Up @@ -258,7 +259,7 @@ This will print all host configuration for the host `staging`.

### Configuration of the laravel-method

* `laravelRootFolder` folder where the package.json is located, defaults to the (git-) root-folder.
* `laravel.rootFolder` folder where the package.json is located, defaults to the (git-) root-folder.
* `artisanTasks`:
* `reset`: (array) A list of aritsan tasks to execute for the `reset`-command. Default is
- `config:cache`
Expand All @@ -271,15 +272,29 @@ This will print all host configuration for the host `staging`.

### Configuration of the yarn-method

* `yarnRootFolder` folder where the package.json is located.
* `yarnBuildCommand` build-command for yarn to execute when running the install- or reset-task.
* `yarnRunContext` in which context should the command be executed. Defaults to `host`, alternative is `dockerHost`, which means, that the yarn command is not executed in the context of the host, but instead of the dockerHost. Suitable if you replace the yarn executable by a docker exec method.
```yaml
yarn:
buildCommand: ...
context: ...
rootFolder: ...
```

* `yarn.rootFolder` folder where the package.json is located.
* `yarn.buildCommand` build-command for yarn to execute when running the install- or reset-task.
* `yarn.context` in which context should the command be executed. Defaults to `host`, alternative is `dockerHost`, which means, that the yarn command is not executed in the context of the host, but instead of the dockerHost. Suitable if you replace the yarn executable by a docker exec method. You can also use `docker-image to execute yarn in a dedicated docker image. More infos can be found at script execution contexts.

### Configuration of the npm-method

* `npmRootFolder` folder where the package.json is located.
* `npmBuildCommand` build-command for npm to execute when running the install- or reset-task.
* `npmRunContext` in which context should the command be executed. Defaults to `host`, alternative is `dockerHost`, which means, that the npm command is not executed in the context of the host, but instead of the dockerHost. Suitable if you replace the npm executable by a docker exec method.
```yaml
npm:
buildCommand: ...
context: ...
rootFolder: ...
```

* `npm.rootFolder` folder where the package.json is located.
* `npm.buildCommand` build-command for npm to execute when running the install- or reset-task.
* `npm.context` in which context should the command be executed. Defaults to `host`, alternative is `dockerHost`, which means, that the npm command is not executed in the context of the host, but instead of the dockerHost. Suitable if you replace the npm executable by a docker exec method. You can also use `docker-image` to execute npm in a dedicated docker image. More infos can be found at script execution contexts.

### Configuration of the artifacts--ftp-method

Expand Down Expand Up @@ -327,6 +342,7 @@ This will print all host configuration for the host `staging`.
* `notifyOn`: a list of all tasks where to send a message to a Mattermost channel. Have a look at the global Mattermost-configuration-example below.

### Configuration of the k8s-method

The Kubernetes integration is documented [here](kubernetes.md).

## dockerHosts
Expand Down Expand Up @@ -540,7 +556,7 @@ webhooks:
url: <url>
method: get
payload:
branch: "%host.branch%"
branch: "%host.git.branch%"
token: "%settings.token%"
someOtherValue: "%arguments.foo%"
```
Expand Down
6 changes: 4 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ Here's an example blueprint:
blueprint:
inheritsFrom: http://some.host/data.yaml
configName: '%project-slug%-%slug.with-hyphens.without-feature%.some.host.tld'
branch: '%identifier%'
git:
branch: '%identifier%'
database:
name: '%slug.without-feature%_mysql'
docker:
Expand All @@ -98,7 +99,8 @@ And the output of `phab --blueprint=feature/XY-123-my_Branch-name --config=<conf

```yaml
phbackend-xy-123-my-branch-name.some.host.tld:
branch: feature/XY-123-my_Branch-name
git:
branch: feature/XY-123-my_Branch-name
configName: phbackend-xy-123-my-branch-name.some.host.tld
database:
name: xy123mybranchname_mysql
Expand Down
2 changes: 1 addition & 1 deletion src/Artifact/Actions/ActionBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function validateConfig($host_config, array $action_config, ValidationErr
$service = new ValidationService(
$action_config,
$errors,
sprintf('host-config.%s.%s.actions', $host_config['configName'], ArtifactsBaseMethod::PREFS_KEY)
sprintf('host-config.%s.%s', $host_config['configName'], ArtifactsBaseMethod::ACTIONS_KEY)
);
$service->hasKeys([
'action' => 'Every action needs the type of action to perform',
Expand Down
6 changes: 6 additions & 0 deletions src/Command/BaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,16 @@ private function handleSetOption($option_value)
$key = $key_combined;
}
if ($what == 'host') {
if (is_null($this->hostConfig->getProperty($key, null))) {
throw new \InvalidArgumentException('Can only set existing values!');
}
$this->hostConfig->setProperty($key, $value);
} elseif ($what == 'docker') {
$docker_config = $this->getDockerConfig();
if ($docker_config) {
if (is_null($docker_config->getProperty($key, null))) {
throw new \InvalidArgumentException('Can only set existing values!');
}
$docker_config->setProperty($key, $value);
} else {
throw new \InvalidArgumentException('Can\'t set value for unavailable docker-config');
Expand Down
7 changes: 4 additions & 3 deletions src/Command/DeployCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Phabalicious\Exception\MissingDockerHostConfigException;
use Phabalicious\Exception\ShellProviderNotFoundException;
use Phabalicious\Exception\TaskNotFoundInMethodException;
use Phabalicious\Method\GitMethod;
use Phabalicious\Method\TaskContext;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -67,13 +68,13 @@ protected function execute(InputInterface $input, OutputInterface $output)
// Override branch in config.
$branch = $input->getArgument('branch');
if (!empty($branch)) {
$this->getHostConfig()['branch'] = $branch;
$this->getHostConfig()->setProperty(GitMethod::BRANCH_KEY, $branch);
}

if ($this->getHostConfig()->get('branch')) {
if ($this->getHostConfig()->getProperty(GitMethod::BRANCH_KEY)) {
$context->io()->comment(sprintf(
'Deploying branch `%s` with config `%s` ...',
$this->getHostConfig()['branch'],
$this->getHostConfig()->getProperty(GitMethod::BRANCH_KEY),
$this->getHostConfig()->getConfigName()
));
}
Expand Down
12 changes: 12 additions & 0 deletions src/Configuration/ConfigurationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,9 @@ private function validateHostConfig($config_name, Node $data)
// Validate data against used methods.

foreach ($used_methods as $method) {
if (!empty($deprecation_mapping = $method->getDeprecationMapping())) {
$this->mapDeprecatedConfig($data, $deprecation_mapping);
}
$method->validateConfig($data, $validation_errors);
}

Expand Down Expand Up @@ -1125,4 +1128,13 @@ public function getData(): Node
{
return $this->settings;
}

private function mapDeprecatedConfig(Node $data, array $mapping)
{
foreach ($mapping as $deprecated => $key) {
if ($data->has($deprecated)) {
$data->setProperty($key, $data[$deprecated]);
}
}
}
}
14 changes: 12 additions & 2 deletions src/Configuration/Storage/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,19 @@ public function setProperty(string $dotted_key, $new_value)
{
$node = $this->find($dotted_key);
if (!$node) {
throw new \InvalidArgumentException(sprintf("Could not find key %s in data!", $dotted_key));
$node = $this;
$keys = explode(".", $dotted_key);
$last_key = array_pop($keys);
foreach ($keys as $key) {
if (!$node->has($key)) {
$node[$key] = [];
}
$node = $node->get($key);
}
$node[$last_key] = $new_value;
} else {
$node->setValue($new_value);
}
$node->setValue($new_value);
}

public function push($value)
Expand Down
61 changes: 30 additions & 31 deletions src/Method/ArtifactsBaseMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@

abstract class ArtifactsBaseMethod extends BaseMethod
{
const PREFS_KEY = 'artifact';

const USE_LOCAL_REPOSITORY_KEY = 'artifact.useLocalRepository';
const ACTIONS_KEY = 'artifact.actions';

public function __construct(LoggerInterface $logger)
{
Expand All @@ -45,36 +46,29 @@ public function __construct(LoggerInterface $logger)
public function getDefaultConfig(ConfigurationService $configuration_service, Node $host_config): Node
{
$parent = parent::getDefaultConfig($configuration_service, $host_config); // TODO: Change the autogenerated stub
$return = [];
$return[self::PREFS_KEY]['useLocalRepository'] = false;
$return = new Node([], $this->getName() . ' method defaults');
$return->setProperty(self::USE_LOCAL_REPOSITORY_KEY, false);

return $parent->merge(new Node($return, $this->getName() . ' method defaults'));
return $parent->merge($return);
}

public function validateConfig(Node $config, ValidationErrorBagInterface $errors)
{
parent::validateConfig($config, $errors); // TODO: Change the autogenerated stub

$service = new ValidationService($config, $errors, 'Host-config ' . $config['configName']);
$service->isArray(self::PREFS_KEY, 'Please provide an artifact configuration');
if (!empty($config[self::PREFS_KEY])) {
$service = new ValidationService($config[self::PREFS_KEY], $errors, sprintf(
'host-config.%s.%s',
$config['configName'],
self::PREFS_KEY
));
$service->hasKeys([
'actions' => 'An artifact needs a list of actions',
]);
if (isset($config[self::PREFS_KEY]['actions'])) {
foreach ($config[self::PREFS_KEY]['actions'] as $action_config) {
if (!isset($action_config['action'])) {
$errors->addError('unknown', 'action needs a name');
} else {
$action = ActionFactory::get($this->getName(), $action_config['action']);
$action->validateConfig($config, $action_config, $errors);
}
}
$service->isArray('artifact', 'Please provide an artifact configuration');

$service->hasKeys([
self::ACTIONS_KEY => 'An artifact needs a list of actions',
]);

foreach ($config->getProperty(self::ACTIONS_KEY, []) as $action_config) {
if (!isset($action_config['action'])) {
$errors->addError('unknown', 'action needs a name');
} else {
$action = ActionFactory::get($this->getName(), $action_config['action']);
$action->validateConfig($config, $action_config, $errors);
}
}
}
Expand All @@ -84,10 +78,10 @@ protected function prepareDirectoriesAndStages(
TaskContextInterface $context,
array $stages,
bool $create_target_dir
) {
): array {
$hash = md5(rand(0, PHP_INT_MAX) . '-' . time());
if ($use_local_repository = $host_config[self::PREFS_KEY]['useLocalRepository']) {
$install_dir = $host_config['gitRootFolder'];
if ($use_local_repository = $host_config->getProperty(self::USE_LOCAL_REPOSITORY_KEY)) {
$install_dir = $host_config->getProperty('git.rootFolder');
$stages = array_diff($stages, ['installCode']);
} else {
$install_dir = $host_config['tmpFolder'] . '/' . $host_config->getConfigName() . '-' . $hash;
Expand Down Expand Up @@ -152,7 +146,7 @@ protected function buildArtifact(
$keys = array_unique($keys);

foreach ($keys as $key) {
if ($host_config->get($key, 'ignore')[0] == '.') {
if ($host_config->getProperty($key, 'ignore')[0] == '.') {
$dir = $install_dir . '/' . $host_config[$key];
} else {
$dir = $install_dir;
Expand Down Expand Up @@ -210,8 +204,10 @@ protected function runStageSteps(HostConfig $host_config, TaskContextInterface $
/**
* @param HostConfig $host_config
* @param TaskContextInterface $context
* @throws MethodNotFoundException
* @throws MissingScriptCallbackImplementation
*
* @throws \Phabalicious\Exception\MethodNotFoundException
* @throws \Phabalicious\Exception\MissingScriptCallbackImplementation
* @throws \Phabalicious\Exception\UnknownReplacementPatternException
*/
protected function runDeployScript(HostConfig $host_config, TaskContextInterface $context)
{
Expand All @@ -228,10 +224,13 @@ protected function runDeployScript(HostConfig $host_config, TaskContextInterface
}


/**
* @param \Phabalicious\Configuration\HostConfig $host_config
* @param \Phabalicious\Method\TaskContextInterface $context
*/
public function runActions(HostConfig $host_config, TaskContextInterface $context)
{

$actions = $host_config[self::PREFS_KEY]['actions'];
$actions = $host_config->getProperty(self::ACTIONS_KEY, []);
ksort($actions);

foreach ($actions as $step => $action_config) {
Expand Down
Loading