├── .gitignore
├── README.md
├── app
├── bootstrap.php
└── config.php
├── bin
└── workflow
├── composer.json
├── composer.lock
├── phpunit.xml
├── src
├── Application.php
├── BufferWithTime.php
├── Command
│ ├── Build.php
│ ├── CommandInterface.php
│ ├── ComposerInstall.php
│ ├── ComposerRequire.php
│ ├── ComposerUpdate.php
│ ├── DatabaseDump.php
│ ├── Delete.php
│ ├── DockerAwareTrait.php
│ ├── Down.php
│ ├── Exec.php
│ ├── GenerateConfig.php
│ ├── Magento.php
│ ├── MagentoCompile.php
│ ├── MagentoConfigure.php
│ ├── MagentoFullInstall.php
│ ├── MagentoInstall.php
│ ├── MagentoModuleDisable.php
│ ├── MagentoModuleEnable.php
│ ├── MagentoSetupUpgrade.php
│ ├── NewProject.php
│ ├── NginxReload.php
│ ├── Php.php
│ ├── Pull.php
│ ├── Push.php
│ ├── Restart.php
│ ├── Sql.php
│ ├── Ssh.php
│ ├── Stop.php
│ ├── Sync.php
│ ├── Up.php
│ ├── VarnishDisable.php
│ ├── VarnishEnable.php
│ └── Watch.php
├── CommandLine.php
├── Config
│ ├── ConfigGeneratorFactory.php
│ ├── ConfigGeneratorInterface.php
│ ├── M1ConfigGenerator.php
│ └── M2ConfigGenerator.php
├── Files.php
├── Logger.php
├── LoggerInterface.php
├── NewProject
│ ├── Details.php
│ ├── DetailsGatherer.php
│ ├── Step
│ │ ├── AuthJson.php
│ │ ├── Capistrano.php
│ │ ├── CircleCI.php
│ │ ├── ComposerJson.php
│ │ ├── CreateProject.php
│ │ ├── Docker.php
│ │ ├── GitClean.php
│ │ ├── GitCommit.php
│ │ ├── GitInit.php
│ │ ├── PhpStorm.php
│ │ ├── PrTemplate.php
│ │ ├── Readme.php
│ │ └── StepInterface.php
│ ├── StepRunner.php
│ └── TemplateWriter.php
├── NullLogger.php
├── Platform.php
├── ProcessFailedException.php
├── Test
│ ├── Constraint
│ │ ├── FileExistsInContainer.php
│ │ └── FileUserAndGroupInContainer.php
│ └── WorkflowTest.php
├── WatchFactory.php
└── functions.php
├── templates
├── capistrano
│ ├── Capfile
│ ├── Gemfile
│ ├── deploy.rb
│ └── dev.rb
├── circle
│ └── circle.yml
├── composer
│ └── auth.json
├── config
│ ├── M1
│ │ ├── local.xml
│ │ └── local.xml.template
│ └── M2
│ │ └── env.php.template
├── docker
│ ├── .dockerignore
│ ├── certs
│ │ └── cert0.pem
│ ├── docker-compose.dev.yml
│ ├── docker-compose.prod.yml
│ ├── docker-compose.yml
│ ├── env
│ │ ├── local.env.dist
│ │ └── production.env.dist
│ ├── nginx
│ │ └── site.conf
│ └── php
│ │ ├── Dockerfile
│ │ ├── bin
│ │ ├── docker-configure
│ │ ├── magento-configure
│ │ └── magento-install
│ │ └── etc
│ │ ├── custom.template
│ │ ├── msmtprc.template
│ │ └── xdebug.template
├── git
│ └── .gitignore
├── phpstorm
│ └── .phpstorm.meta.php
├── pr
│ └── PULL_REQUEST_TEMPLATE.md
└── readme
│ └── README.md
└── test
├── ApplicationTest.php
├── Command
├── AbstractTestCommand.php
├── BuildTest.php
├── ComposerInstallTest.php
├── ComposerRequireTest.php
├── ComposerUpdateTest.php
├── DatabaseDumpTest.php
├── DeleteTest.php
├── DockerAwareTraitTest.php
├── ExecTest.php
├── GenerateConfigTest.php
├── MagentoCompileTest.php
├── MagentoConfigureTest.php
├── MagentoFullInstallTest.php
├── MagentoInstallTest.php
├── MagentoModuleDisableTest.php
├── MagentoModuleEnableTest.php
├── MagentoSetupUpgradeTest.php
├── MagentoTest.php
├── NginxReloadTest.php
├── PhpTest.php
├── PullTest.php
├── PushTest.php
├── RestartTest.php
├── SqlTest.php
├── SshTest.php
├── StopTest.php
├── SyncTest.php
├── UpTest.php
├── VarnishDisableTest.php
├── VarnishEnableTest.php
└── WatchTest.php
├── CommandLineTest.php
├── FilesTest.php
├── LoggerTest.php
├── Test
├── Constraint
│ ├── FileExistsInContainerTest.php
│ └── FileGroupAndOwnerInContainerTest.php
└── WorkflowTestTest.php
├── WatchFactoryTest.php
└── fixtures
├── broken-env
├── .docker
│ └── local.env
├── app.php.dockerfile
├── docker-compose.dev.yml
└── docker-compose.yml
├── config
├── env.default.php
├── env.production.php
├── env.queue.php
└── local.xml
├── invalid-env
├── .docker
│ └── local.env.dist
├── app.php.dockerfile
├── docker-compose.dev.yml
└── docker-compose.yml
├── missing-docker-files
└── some-file
├── test-env-files
└── some-file.php
├── test-env
├── app.php.dockerfile
└── docker-compose.yml
└── valid-env
├── .docker
└── local.env
├── app.php.dockerfile
├── docker-compose.dev.yml
├── docker-compose.prod.yml
├── docker-compose.yml
├── some-file.txt
├── some-folder
└── .gitkeep
└── some-import.sql
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | .idea
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
JH Development Workflow Tool
2 |
3 |
4 |
5 | Some Badges would be nice.
6 |
7 |
8 |
9 | ## Install
10 |
11 | Make sure you have `fswatch` installed. You can install via homebrew:
12 |
13 | ```
14 | brew install fswatch
15 | ```
16 |
17 | Run the following commands:
18 |
19 | ```
20 | composer global config repositories.workflow vcs git@github.com:wearejh/workflow
21 | composer global require wearejh/workflow:dev-master
22 | ```
23 |
24 | Notes:
25 |
26 | - Make sure your composer global bin directory `~/.composer/vendor/bin` is available in your `$PATH` environment variable.
27 | - Because packages installed globally with composer share dependencies, you may need to run `composer global update` if the
28 | previous command failed.
29 |
30 | ## Usage
31 |
32 | Before you create any new project, first update the tool, in-case of any fixes or new features.
33 |
34 | ```
35 | composer global update wearejh/workflow
36 | ```
37 |
38 | Then run `workflow` to see the list of available commands.
39 |
40 | Read the [wiki](https://github.com/WeareJH/workflow/wiki) for detailed information on each command
41 |
42 | ## Troubleshooting
43 |
44 | If you are experiencing very slow speeds (i.e. it's hanging for minutes inbetween commands), it may be due to a slow DNS lookup to localunixsocket.local. See relevant [GitHub issue](https://github.com/docker/compose/issues/3419#issuecomment-221793401)
45 | A quick fix is to add the following to your hosts file.
46 |
47 | `127.0.0.1 localunixsocket.local`
48 |
--------------------------------------------------------------------------------
/app/bootstrap.php:
--------------------------------------------------------------------------------
1 | addDefinitions(__DIR__ . '/config.php')
29 | ->build();
30 |
31 | exit($c->get(Application::class)->run($c->get(InputInterface::class), $c->get(OutputInterface::class)));
32 |
--------------------------------------------------------------------------------
/bin/workflow:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
2 |
3 |
4 |
5 | ./test
6 |
7 |
8 |
9 | ./src
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Application.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Application extends \Symfony\Component\Console\Application
16 | {
17 | private $fallbackCommand = 'magento';
18 |
19 | /**
20 | * @param string $commandName
21 | * @return void
22 | */
23 | public function setFallBackCommand(string $commandName)
24 | {
25 | $this->fallbackCommand = $commandName;
26 | }
27 |
28 | /**
29 | * @inheritdoc
30 | */
31 | public function run(InputInterface $input = null, OutputInterface $output = null)
32 | {
33 | try {
34 | $this->setCatchExceptions(false);
35 | return parent::run($input, $output);
36 | } catch (CommandNotFoundException $e) {
37 | $arguments = $_SERVER['argv'];
38 | array_splice($arguments, 1, 0, $this->fallbackCommand);
39 |
40 | $input = new ArgvInput($arguments);
41 |
42 | try {
43 | return parent::run($input);
44 | } catch (\Exception $e) {
45 | return $this->exception($e, $output);
46 | }
47 | } catch (\Exception $e) {
48 | return $this->exception($e, $output);
49 | }
50 | }
51 |
52 | /**
53 | * @param \Exception $e
54 | * @param OutputInterface|null $output
55 | * @return int|mixed
56 | */
57 | private function exception(\Exception $e, OutputInterface $output = null)
58 | {
59 | $output = $output ?? new ConsoleOutput;
60 |
61 | if ($output instanceof ConsoleOutputInterface) {
62 | $this->renderException($e, $output->getErrorOutput());
63 | } else {
64 | $this->renderException($e, $output);
65 | }
66 |
67 | $exitCode = $e->getCode();
68 | if (is_numeric($exitCode)) {
69 | $exitCode = (int) $exitCode;
70 | if (0 === $exitCode) {
71 | $exitCode = 1;
72 | }
73 | } else {
74 | $exitCode = 1;
75 | }
76 |
77 | return $exitCode;
78 | }
79 | }
--------------------------------------------------------------------------------
/src/BufferWithTime.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class BufferWithTime implements OperatorInterface
15 | {
16 | /**
17 | * @var int
18 | */
19 | private $milliSeconds;
20 |
21 | /**
22 | * @var AsyncSchedulerInterface
23 | */
24 | private $scheduler;
25 |
26 | /**
27 | * @var array
28 | */
29 | private $buffer = [];
30 |
31 | private $completed = false;
32 |
33 | public function __construct(int $milliSeconds, AsyncSchedulerInterface $scheduler)
34 | {
35 | $this->milliSeconds = $milliSeconds;
36 | $this->scheduler = $scheduler;
37 | }
38 |
39 | public function __invoke(ObservableInterface $observable, ObserverInterface $observer): DisposableInterface
40 | {
41 | $action = function () use ($observer, &$action) {
42 |
43 | if ($this->completed) {
44 | $observer->onCompleted();
45 | return;
46 | }
47 |
48 | $observer->onNext($this->buffer);
49 | $this->buffer = [];
50 | $this->scheduler->schedule($action, $this->milliSeconds);
51 | };
52 |
53 | $this->scheduler->schedule($action, $this->milliSeconds);
54 | return $observable->subscribe(
55 | function ($x) {
56 | $this->buffer[] = $x;
57 | },
58 | [$observer, 'onError'],
59 | function () {
60 | $this->completed = true;
61 | }
62 | );
63 | }
64 | }
--------------------------------------------------------------------------------
/src/Command/Build.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class Build extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | protected function configure()
30 | {
31 | $this
32 | ->setName('build')
33 | ->setDescription('Runs docker build to create an image ready for use')
34 | ->addOption('prod', 'p', InputOption::VALUE_NONE, 'Ommits development configurations')
35 | ->addOption('no-cache', null, InputOption::VALUE_NONE, 'Skip the build cache')
36 | ->addOption('service', 's', InputOption::VALUE_REQUIRED, 'Service to build');
37 | }
38 |
39 | public function execute(InputInterface $input, OutputInterface $output)
40 | {
41 | $service = $this->getServiceConfig('php');
42 |
43 | if (!isset($service['image'])) {
44 | throw new \RuntimeException('No image specified for PHP container');
45 | }
46 |
47 | $service = $input->getOption('service') ?: 'php';
48 | $args = $input->getOption('no-cache') ? '--no-cache ' : '';
49 |
50 | $composeFiles = $this->getComposeFileFlags($input->getOption('prod') ? true : false);
51 |
52 | $this->commandLine->run(
53 | rtrim(sprintf('docker-compose %s build %s%s', $composeFiles, $args, $service))
54 | );
55 |
56 | $output->writeln('Build complete!');
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Command/CommandInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface CommandInterface
12 | {
13 | public function execute(InputInterface $input, OutputInterface $output);
14 | }
15 |
--------------------------------------------------------------------------------
/src/Command/ComposerInstall.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class ComposerInstall extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | protected function configure()
30 | {
31 | $this
32 | ->setName('composer-install')
33 | ->setAliases(['ci'])
34 | ->setDescription('Runs composer install inside the container and pulls back required files to the host');
35 | }
36 |
37 | public function execute(InputInterface $input, OutputInterface $output)
38 | {
39 | $container = $this->phpContainerName();
40 | $flags = ['-o', '--ansi'];
41 |
42 | switch ($output->getVerbosity()) {
43 | case OutputInterface::VERBOSITY_VERBOSE:
44 | $flags[] = '-v';
45 | break;
46 | case OutputInterface::VERBOSITY_VERY_VERBOSE:
47 | $flags[] = '-vv';
48 | break;
49 | case OutputInterface::VERBOSITY_DEBUG:
50 | $flags[] = '-vvv';
51 | break;
52 | }
53 |
54 | $this->commandLine->run(
55 | sprintf(
56 | 'docker exec -u www-data -e COMPOSER_CACHE_DIR=.docker/composer-cache %s composer install %s',
57 | $container,
58 | implode(' ', $flags)
59 | )
60 | );
61 |
62 | $pullCommand = $this->getApplication()->find('pull');
63 | $pullArguments = new ArrayInput(['files' => ['vendor', '.docker/composer-cache']]);
64 |
65 | $pullCommand->run($pullArguments, $output);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Command/ComposerRequire.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class ComposerRequire extends Command implements CommandInterface
17 | {
18 | use DockerAwareTrait;
19 |
20 | /**
21 | * @var CommandLine
22 | */
23 | private $commandLine;
24 |
25 | public function __construct(CommandLine $commandLine)
26 | {
27 | parent::__construct();
28 | $this->commandLine = $commandLine;
29 | }
30 |
31 | protected function configure()
32 | {
33 | $this
34 | ->setName('composer-require')
35 | ->setAliases(['cr'])
36 | ->addArgument('package', InputArgument::REQUIRED)
37 | ->addOption('dev', 'd', InputOption::VALUE_NONE, 'Require as dev dependency')
38 | ->setDescription('Runs composer require inside the container and pulls back required files to the host');
39 | }
40 |
41 | public function execute(InputInterface $input, OutputInterface $output)
42 | {
43 | $container = $this->phpContainerName();
44 | $flags = ['--ansi'];
45 |
46 | switch ($output->getVerbosity()) {
47 | case OutputInterface::VERBOSITY_VERBOSE:
48 | $flags[] = '-v';
49 | break;
50 | case OutputInterface::VERBOSITY_VERY_VERBOSE:
51 | $flags[] = '-vv';
52 | break;
53 | case OutputInterface::VERBOSITY_DEBUG:
54 | $flags[] = '-vvv';
55 | break;
56 | }
57 |
58 | if ($input->getOption('dev')) {
59 | $flags[] = '--dev';
60 | }
61 |
62 | $command = sprintf(
63 | 'docker exec -u www-data -e COMPOSER_CACHE_DIR=.docker/composer-cache %s composer require %s %s',
64 | $container,
65 | $input->getArgument('package'),
66 | implode(' ', $flags)
67 | );
68 | $this->commandLine->run($command);
69 |
70 | $pullCommand = $this->getApplication()->find('pull');
71 | $pullFiles = ['.docker/composer-cache', 'vendor', 'composer.json', 'composer.lock'];
72 | $pullArguments = new ArrayInput(['files' => $pullFiles]);
73 |
74 | $pullCommand->run($pullArguments, $output);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Command/ComposerUpdate.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class ComposerUpdate extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | protected function configure()
30 | {
31 | $this
32 | ->setName('composer-update')
33 | ->setAliases(['cu'])
34 | ->setDescription('Runs composer update inside the container and pulls back required files to the host');
35 | }
36 |
37 | public function execute(InputInterface $input, OutputInterface $output)
38 | {
39 | $container = $this->phpContainerName();
40 | $flags = ['-o', '--ansi'];
41 |
42 | switch ($output->getVerbosity()) {
43 | case OutputInterface::VERBOSITY_VERBOSE:
44 | $flags[] = '-v';
45 | break;
46 | case OutputInterface::VERBOSITY_VERY_VERBOSE:
47 | $flags[] = '-vv';
48 | break;
49 | case OutputInterface::VERBOSITY_DEBUG:
50 | $flags[] = '-vvv';
51 | break;
52 | }
53 |
54 | $this->commandLine->run(
55 | sprintf(
56 | 'docker exec -u www-data -e COMPOSER_CACHE_DIR=.docker/composer-cache %s composer update %s',
57 | $container,
58 | implode(' ', $flags)
59 | )
60 | );
61 |
62 | $pullCommand = $this->getApplication()->find('pull');
63 | $pullArguments = new ArrayInput(['files' => ['.docker/composer-cache', 'vendor', 'composer.lock']]);
64 |
65 | $pullCommand->run($pullArguments, $output);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Command/DatabaseDump.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class DatabaseDump extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | public function configure()
30 | {
31 | $this
32 | ->setName('db-dump')
33 | ->setDescription('Dump the database to the host')
34 | ->addOption('database', 'd', InputOption::VALUE_REQUIRED, 'Optional database to dump');
35 | }
36 |
37 | public function execute(InputInterface $input, OutputInterface $output)
38 | {
39 | $container = $this->getContainerName('db');
40 |
41 | $this->dump($container, $input);
42 |
43 | $output->writeln('Database dump saved to ./dump.sql');
44 | }
45 |
46 | private function dump(string $container, InputInterface $input)
47 | {
48 | $details = $this->getDbDetails($input);
49 | $user = $details['user'];
50 | $pass = $details['pass'];
51 | $db = $details['db'];
52 |
53 | $command = sprintf('docker exec -i %s mysqldump -u%s -p%s %s > dump.sql', $container, $user, $pass, $db);
54 | $this->commandLine->runQuietly($command);
55 | }
56 |
57 | private function getDbDetails(InputInterface $input) : array
58 | {
59 | $envVars = $this->getDevEnvironmentVars();
60 | return [
61 | 'user' => 'root',
62 | 'pass' => $envVars['MYSQL_ROOT_PASSWORD'] ?? 'docker',
63 | 'db' => $input->getOption('database') ?? $envVars['MYSQL_DATABASE'] ?? 'docker'
64 | ];
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Command/Delete.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Delete extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var Files
21 | */
22 | private $files;
23 |
24 | public function __construct(Files $files)
25 | {
26 | parent::__construct();
27 | $this->files = $files;
28 | }
29 |
30 | public function configure()
31 | {
32 | $this
33 | ->setName('delete')
34 | ->setDescription('Delete files from the container')
35 | ->addArgument(
36 | 'files',
37 | InputArgument::REQUIRED | InputArgument::IS_ARRAY,
38 | 'Files to delete, relative to project root'
39 | );
40 | }
41 |
42 | public function execute(InputInterface $input, OutputInterface $output)
43 | {
44 | $this->files->delete($this->phpContainerName(), (array) $input->getArgument('files'));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Command/DockerAwareTrait.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | trait DockerAwareTrait
13 | {
14 | private function getDevEnvironmentVars(): array
15 | {
16 | $envFile = getcwd() . '/.docker/local.env';
17 |
18 | if (!file_exists($envFile)) {
19 | throw new \RuntimeException("Local env file doesn't exist, are you sure your configured correctly?");
20 | }
21 |
22 | return Parser::parse(file_get_contents($envFile));
23 | }
24 |
25 | private function phpContainerName(): string
26 | {
27 | return $this->getContainerName('php');
28 | }
29 |
30 | private function getContainerName(string $service): string
31 | {
32 | $serviceConfig = $this->getServiceConfig($service);
33 |
34 | if (!isset($serviceConfig['container_name'])) {
35 | throw new \RuntimeException(sprintf('Unable to get container name for service %s', $service));
36 | }
37 |
38 | return $serviceConfig['container_name'];
39 | }
40 |
41 | private function getServiceConfig(string $service) : array
42 | {
43 | $cwd = getcwd();
44 |
45 | $coreComposePath = $cwd . '/docker-compose.yml';
46 | $devComposePath = $cwd . '/docker-compose.dev.yml';
47 |
48 | if (!file_exists($coreComposePath)) {
49 | throw new \RuntimeException('Could not locate docker-compose.yml file. Are you in the right directory?');
50 | }
51 |
52 | try {
53 | $coreYaml = Yaml::parse(file_get_contents($coreComposePath));
54 | $devYaml = file_exists($devComposePath) ? Yaml::parse(file_get_contents($devComposePath)) : [];
55 | } catch (ParseException $e) {
56 | throw new \RuntimeException(sprintf("Unable to parse docker-compose file \n\n %s", $e->getMessage()));
57 | }
58 |
59 | $yaml = array_merge_recursive($coreYaml, $devYaml);
60 |
61 | if (!isset($yaml['services'][$service])) {
62 | throw new \RuntimeException(sprintf('Service "%s" doesn\'t exist in the compose files', $service));
63 | }
64 |
65 | return $yaml['services'][$service];
66 | }
67 |
68 | private function getComposeFileFlags(bool $prod = false)
69 | {
70 | $cwd = getcwd();
71 |
72 | $devComposePath = $cwd . '/docker-compose.dev.yml';
73 | $prodComposePath = $cwd . '/docker-compose.prod.yml';
74 |
75 | $composeFileFlags = '-f docker-compose.yml';
76 |
77 | if (!$prod && file_exists($devComposePath)) {
78 | $composeFileFlags .= ' -f docker-compose.dev.yml';
79 | }
80 |
81 | if ($prod && file_exists($prodComposePath)) {
82 | $composeFileFlags .= ' -f docker-compose.prod.yml';
83 | }
84 |
85 | return $composeFileFlags;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Command/Down.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class Down extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | public function configure()
30 | {
31 | $this
32 | ->setName('down')
33 | ->setDescription('Stop and remove containers, networks, images, and volumes')
34 | ->addOption('prod', 'p', InputOption::VALUE_OPTIONAL, 'Use when started with --prod / -p');
35 | }
36 |
37 | public function execute(InputInterface $input, OutputInterface $output)
38 | {
39 | $composeFiles = $this->getComposeFileFlags($input->getOption('prod') ? true : false);
40 |
41 | $this->commandLine->run(sprintf('docker-compose %s down', $composeFiles));
42 |
43 | $output->writeln('Containers stopped');
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Command/Exec.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Exec extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var CommandLine
21 | */
22 | private $commandLine;
23 |
24 | public function __construct(CommandLine $commandLine)
25 | {
26 | parent::__construct();
27 | $this->commandLine = $commandLine;
28 | }
29 |
30 | protected function configure()
31 | {
32 | $this
33 | ->setName('exec')
34 | ->setDescription('Run an arbitrary command on the app container')
35 | ->addArgument('command-line', InputArgument::REQUIRED, 'Command to execute')
36 | ->addOption(
37 | 'root',
38 | 'r',
39 | InputOption::VALUE_NONE,
40 | 'Exec as root user (must be passed before command e.g workflow exec -r ls -la)'
41 | )
42 | ->ignoreValidationErrors();
43 | }
44 |
45 | public function execute(InputInterface $input, OutputInterface $output)
46 | {
47 | $container = $this->phpContainerName();
48 | $slicePoint = 1 + (int) array_search($this->getName(), $_SERVER['argv'], true);
49 |
50 | $root = false;
51 | if ($_SERVER['argv'][$slicePoint] === '-r' || $_SERVER['argv'][$slicePoint] === '--root') {
52 | $root = true;
53 | $slicePoint++;
54 | }
55 |
56 | $args = array_slice($_SERVER['argv'], $slicePoint);
57 | $user = $root ? 'root' : 'www-data';
58 | $command = sprintf('docker exec -it -u %s %s %s', $user, $container, implode(' ', $args));
59 |
60 | $this->commandLine->runInteractively($command);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Command/GenerateConfig.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class GenerateConfig extends Command implements CommandInterface
17 | {
18 | /**
19 | * @var ConfigGeneratorFactory
20 | */
21 | private $configGeneratorFactory;
22 |
23 | public function __construct(ConfigGeneratorFactory $configGeneratorFactory)
24 | {
25 | parent::__construct();
26 | $this->configGeneratorFactory = $configGeneratorFactory;
27 | }
28 |
29 | public function configure()
30 | {
31 | $this
32 | ->setName('generate-config')
33 | ->setAliases(['gc'])
34 | ->setDescription('Generate the environment config for your instance, e.g. env.ph or local.xml')
35 | ->addOption('m1', null, InputOption::VALUE_NONE, 'Generate M1 local.xml instead of M2 env.php')
36 | ->addOption(
37 | 'root-dir',
38 | null,
39 | InputOption::VALUE_OPTIONAL,
40 | 'Root dir for write operations, default is CWD',
41 | getcwd()
42 | );
43 | }
44 |
45 | public function execute(InputInterface $input, OutputInterface $output)
46 | {
47 | $output = new SymfonyStyle($input, $output);
48 |
49 | $platform = $input->getOption('m1') ? Platform::M1() : Platform::M2();
50 | $rootDir = $input->getOption('root-dir');
51 |
52 | if (!file_exists($rootDir) || !is_dir($rootDir)) {
53 | throw new \RuntimeException(sprintf('Expected "%s" to be project root directory', $rootDir));
54 | }
55 |
56 | $this->configGeneratorFactory->create($platform)->generateEnvironmentConfig($rootDir, $output);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Command/Magento.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class Magento extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | protected function configure()
30 | {
31 | $this
32 | ->setName('magento')
33 | ->setAliases(['mage', 'm'])
34 | ->setDescription('Works as a proxy to the Magento bin inside the container')
35 | ->addArgument('cmd', InputArgument::OPTIONAL, 'Magento command you want to run')
36 | ->ignoreValidationErrors();
37 | }
38 |
39 | public function execute(InputInterface $input, OutputInterface $output)
40 | {
41 | $container = $this->phpContainerName();
42 | $slicePoint = 1 + (int) array_search($this->getName(), $_SERVER['argv'], true);
43 | $args = array_slice($_SERVER['argv'], $slicePoint);
44 | $command = sprintf('docker exec -u www-data %s bin/magento --ansi', $container);
45 |
46 | if (count($args) > 0) {
47 | $command .= sprintf(' %s', implode(' ', $args));
48 | }
49 |
50 | $this->commandLine->run($command);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Command/MagentoCompile.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class MagentoCompile extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | public function configure()
30 | {
31 | $this
32 | ->setName('magento-compile')
33 | ->setDescription('Runs the magento DI compile command and pulls back required files to the host');
34 | }
35 |
36 | public function execute(InputInterface $input, OutputInterface $output)
37 | {
38 | $container = $this->phpContainerName();
39 |
40 | $this->commandLine->run(sprintf('docker exec -u www-data %s bin/magento setup:di:compile --ansi', $container));
41 |
42 | $pullCommand = $this->getApplication()->find('pull');
43 | $pullArguments = new ArrayInput(['files' => ['var/di', 'var/generation']]);
44 |
45 | $pullCommand->run($pullArguments, $output);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Command/MagentoConfigure.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class MagentoConfigure extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var CommandLine
21 | */
22 | private $commandLine;
23 |
24 | public function __construct(CommandLine $commandLine)
25 | {
26 | parent::__construct();
27 | $this->commandLine = $commandLine;
28 | }
29 |
30 | public function configure()
31 | {
32 | $this
33 | ->setName('magento-configure')
34 | ->setAliases(['mc'])
35 | ->setDescription('Configures Magento ready for Docker use')
36 | ->addOption('prod', 'p', InputOption::VALUE_NONE, 'Ommits development configurations');
37 | }
38 |
39 | public function execute(InputInterface $input, OutputInterface $output)
40 | {
41 | $phpContainer = $this->phpContainerName();
42 | $mailContainer = $this->getContainerName('mail');
43 |
44 | $this->commandLine->run(
45 | sprintf(
46 | 'docker exec -u www-data %s magento-configure%s',
47 | $phpContainer,
48 | $input->getOption('prod') ? ' -p' : ''
49 | )
50 | );
51 |
52 | $pullCommand = $this->getApplication()->find('pull');
53 | $pullArguments = new ArrayInput(['files' => ['app/etc/env.php']]);
54 |
55 | $pullCommand->run($pullArguments, $output);
56 |
57 | if (!$input->getOption('prod')) {
58 | $this->configureMail($mailContainer, $output);
59 | }
60 |
61 | $output->writeln('Configuration complete!');
62 | }
63 |
64 | private function configureMail($mailContainer, $output)
65 | {
66 | $sql = "DELETE FROM core_config_data WHERE path LIKE 'system/smtp/%'; ";
67 | $sql .= "INSERT INTO core_config_data (scope, scope_id, path, value) ";
68 | $sql .= "VALUES ";
69 | $sql .= "('default', 0, 'system/smtp/host', '$mailContainer'), ";
70 | $sql .= "('default', 0, 'system/smtp/port', '1025');";
71 |
72 | $sqlCommand = $this->getApplication()->find('sql');
73 | $sqlArguments = new ArrayInput(['--sql' => $sql]);
74 |
75 | $sqlCommand->run($sqlArguments, $output);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Command/MagentoFullInstall.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class MagentoFullInstall extends Command implements CommandInterface
14 | {
15 | use DockerAwareTrait;
16 |
17 | public function configure()
18 | {
19 | $this
20 | ->setName('magento-full-install')
21 | ->setAliases(['mfi'])
22 | ->setDescription('Runs magento-install and magento-configure commands')
23 | ->addOption('prod', 'p', InputOption::VALUE_OPTIONAL, 'Ommits development configurations');
24 | }
25 |
26 | public function execute(InputInterface $input, OutputInterface $output)
27 | {
28 | $installCommand = $this->getApplication()->find('magento-install');
29 | $configureCommand = $this->getApplication()->find('magento-configure');
30 |
31 | $installCommand->run($input, $output);
32 | $configureCommand->run($input, $output);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Command/MagentoInstall.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class MagentoInstall extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | public function configure()
30 | {
31 | $this
32 | ->setName('magento-install')
33 | ->setAliases(['mi'])
34 | ->setDescription('Runs the magento install script');
35 | }
36 |
37 | public function execute(InputInterface $input, OutputInterface $output)
38 | {
39 | $this->commandLine->run(sprintf('docker exec -u www-data %s magento-install', $this->phpContainerName()));
40 |
41 | $pullCommand = $this->getApplication()->find('pull');
42 | $pullArguments = new ArrayInput(['files' => ['app/etc']]);
43 |
44 | $pullCommand->run($pullArguments, $output);
45 |
46 | $output->writeln('Install complete!');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Command/MagentoModuleDisable.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class MagentoModuleDisable extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var CommandLine
21 | */
22 | private $commandLine;
23 |
24 | public function __construct(CommandLine $commandLine)
25 | {
26 | parent::__construct();
27 | $this->commandLine = $commandLine;
28 | }
29 |
30 | public function configure()
31 | {
32 | $this
33 | ->setName('module:disable')
34 | ->setDescription('Disable Magento module and updates the config.php file')
35 | ->addArgument('module', InputArgument::REQUIRED, 'Module to disable');
36 | }
37 |
38 | public function execute(InputInterface $input, OutputInterface $output)
39 | {
40 | $container = $this->phpContainerName();
41 | $module = $input->getArgument('module');
42 |
43 | $this->commandLine->run(
44 | sprintf('docker exec -u www-data %s bin/magento module:disable %s --ansi', $container, $module)
45 | );
46 |
47 | $pullCommand = $this->getApplication()->find('pull');
48 | $pullArguments = new ArrayInput(['files' => ['app/etc/config.php']]);
49 |
50 | $pullCommand->run($pullArguments, $output);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Command/MagentoModuleEnable.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class MagentoModuleEnable extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var CommandLine
21 | */
22 | private $commandLine;
23 |
24 | public function __construct(CommandLine $commandLine)
25 | {
26 | parent::__construct();
27 | $this->commandLine = $commandLine;
28 | }
29 |
30 | public function configure()
31 | {
32 | $this
33 | ->setName('module:enable')
34 | ->setDescription('Enable Magento module and updates the config.php file')
35 | ->addArgument('module', InputArgument::REQUIRED, 'Module to enable');
36 | }
37 |
38 | public function execute(InputInterface $input, OutputInterface $output)
39 | {
40 | $container = $this->phpContainerName();
41 | $module = $input->getArgument('module');
42 |
43 | $this->commandLine->run(
44 | sprintf('docker exec -u www-data %s bin/magento module:enable %s --ansi', $container, $module)
45 | );
46 |
47 | $pullCommand = $this->getApplication()->find('pull');
48 | $pullArguments = new ArrayInput(['files' => ['app/etc/config.php']]);
49 |
50 | $pullCommand->run($pullArguments, $output);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Command/MagentoSetupUpgrade.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class MagentoSetupUpgrade extends Command implements CommandInterface
18 | {
19 | use DockerAwareTrait;
20 |
21 | /**
22 | * Option to skip deletion of generated/code directory
23 | */
24 | const INPUT_KEY_KEEP_GENERATED = 'keep-generated';
25 |
26 | /**
27 | * @var CommandLine
28 | */
29 | private $commandLine;
30 |
31 | public function __construct(CommandLine $commandLine)
32 | {
33 | parent::__construct();
34 | $this->commandLine = $commandLine;
35 | }
36 |
37 | public function configure()
38 | {
39 | $options = [
40 | new InputOption(
41 | self::INPUT_KEY_KEEP_GENERATED,
42 | null,
43 | InputOption::VALUE_NONE,
44 | 'Prevents generated files from being deleted. ' . PHP_EOL .
45 | 'We discourage using this option except when deploying to production. ' . PHP_EOL .
46 | 'Consult your system integrator or administrator for more information.'
47 | )
48 | ];
49 |
50 | $this
51 | ->setName('setup:upgrade')
52 | ->setDescription('Upgrades the Magento application and updates the config.php file')
53 | ->setDefinition($options);
54 | }
55 |
56 | public function execute(InputInterface $input, OutputInterface $output)
57 | {
58 | $container = $this->phpContainerName();
59 | $option = $input->getOption(self::INPUT_KEY_KEEP_GENERATED)
60 | ? '--' . self::INPUT_KEY_KEEP_GENERATED
61 | : ''
62 | ;
63 |
64 | $this->commandLine->run(
65 | sprintf('docker exec -u www-data %s bin/magento setup:upgrade %s --ansi', $container, $option)
66 | );
67 |
68 | $pullCommand = $this->getApplication()->find('pull');
69 | $pullArguments = new ArrayInput(['files' => ['app/etc/config.php']]);
70 |
71 | $pullCommand->run($pullArguments, $output);
72 | }
73 | }
74 |
75 |
--------------------------------------------------------------------------------
/src/Command/NewProject.php:
--------------------------------------------------------------------------------
1 | wearejh.com>
15 | * @author Michael Woodward
16 | */
17 | class NewProject extends Command implements CommandInterface
18 | {
19 | /**
20 | * @var StepRunner
21 | */
22 | private $stepRunner;
23 |
24 | /**
25 | * @var DetailsGatherer
26 | */
27 | private $detailsGatherer;
28 |
29 | public function __construct(DetailsGatherer $detailsGatherer, StepRunner $stepRunner)
30 | {
31 | parent::__construct();
32 |
33 | $this->detailsGatherer = $detailsGatherer;
34 | $this->stepRunner = $stepRunner;
35 | }
36 |
37 | protected function configure()
38 | {
39 | $this
40 | ->setName('new')
41 | ->setDescription('Create a new Magento 2 project');
42 | }
43 |
44 | public function execute(InputInterface $input, OutputInterface $output)
45 | {
46 | $output = new SymfonyStyle($input, $output);
47 |
48 | $details = $this->detailsGatherer->gatherDetails($output);
49 |
50 | try {
51 | $this->stepRunner->run($details, $output);
52 | } catch (ProcessFailedException $e) {
53 | throw $e;
54 | }
55 |
56 | $output->success(sprintf('%s Successfully Created', $details->getProjectName()));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Command/NginxReload.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class NginxReload extends Command implements CommandInterface
14 | {
15 | use DockerAwareTrait;
16 |
17 | /**
18 | * @var CommandLine
19 | */
20 | private $commandLine;
21 |
22 | public function __construct(CommandLine $commandLine)
23 | {
24 | parent::__construct();
25 | $this->commandLine = $commandLine;
26 | }
27 |
28 | public function configure()
29 | {
30 | $this
31 | ->setName('nginx-reload')
32 | ->setAliases(['nginx'])
33 | ->setDescription('Sends reload signal to NGINX in the container');
34 | }
35 |
36 | public function execute(InputInterface $input, OutputInterface $output)
37 | {
38 | $this->commandLine->run(sprintf('docker exec %s nginx -s "reload"', $this->getContainerName('nginx')));
39 |
40 | $output->writeln('Reload signal sent');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Command/Php.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class Php extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | protected function configure()
30 | {
31 | $this
32 | ->setName('php')
33 | ->setDescription('Run a php script on the app container')
34 | ->addArgument('php-file', InputOption::VALUE_REQUIRED, 'Path to PHP file');
35 | }
36 |
37 | public function execute(InputInterface $input, OutputInterface $output)
38 | {
39 | $this->commandLine->runInteractively(
40 | sprintf(
41 | 'docker exec -it -u www-data %s php %s',
42 | $this->phpContainerName(),
43 | $input->getArgument('php-file')
44 | )
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Command/Pull.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Pull extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var Files
21 | */
22 | private $files;
23 |
24 | public function __construct(Files $files)
25 | {
26 | parent::__construct();
27 | $this->files = $files;
28 | }
29 |
30 | public function configure()
31 | {
32 | $help = "Pull files from the docker environment to the host, Useful for pulling vendor etc\n\n";
33 | $help .= 'If the watch is running and you pull a file that is being watched it will ';
34 | $help .= "automatically be pushed back into the container\n";
35 | $help .= 'If this is not what you want (large dirs can cause issues here) stop the watch, ';
36 | $help .= 'pull then start the watch again afterwards';
37 |
38 | $this
39 | ->setName('pull')
40 | ->setDescription('Pull files from the docker environment to the host')
41 | ->setHelp($help)
42 | ->addArgument(
43 | 'files',
44 | InputArgument::REQUIRED | InputArgument::IS_ARRAY,
45 | 'Files to pull, relative to project root'
46 | )
47 | ->addOption('no-overwrite', 'o', InputOption::VALUE_NONE);
48 | }
49 |
50 | public function execute(InputInterface $input, OutputInterface $output)
51 | {
52 | $overwrite = !$input->getOption('no-overwrite');
53 | $container = $this->phpContainerName();
54 | $files = (array) $input->getArgument('files');
55 |
56 | foreach ($files as $file) {
57 |
58 | if (!$this->files->existsInContainer($container, $file)) {
59 | $output->writeln(sprintf('Looks like "%s" doesn\'t exist', $file));
60 | return;
61 | }
62 |
63 | if ($overwrite && is_dir($file)) {
64 | //we only remove if the file exists and is a directory
65 | //as the new directory we push may have a different set of files in it
66 | //for files we can just overwrite and save some cycles
67 | $this->files->deleteLocally([$file]);
68 | }
69 | }
70 |
71 | $this->files->download($container, $files);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Command/Push.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Push extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var Files
21 | */
22 | private $files;
23 |
24 | public function __construct(Files $files)
25 | {
26 | parent::__construct();
27 | $this->files = $files;
28 | }
29 |
30 | public function configure()
31 | {
32 | $this
33 | ->setName('push')
34 | ->setDescription('Push files from host to the container')
35 | ->addArgument(
36 | 'files',
37 | InputArgument::REQUIRED | InputArgument::IS_ARRAY,
38 | 'Files to push, relative to project root'
39 | )
40 | ->addOption('no-overwrite', 'o', InputOption::VALUE_NONE);
41 | }
42 |
43 | public function execute(InputInterface $input, OutputInterface $output)
44 | {
45 | $overwrite = !$input->getOption('no-overwrite');
46 | $container = $this->phpContainerName();
47 | $files = (array) $input->getArgument('files');
48 |
49 |
50 | foreach ($files as $file) {
51 | if (!file_exists($file)) {
52 | $output->writeln(sprintf('Looks like "%s" doesn\'t exist', $file));
53 | return;
54 | }
55 |
56 | if ($overwrite && is_dir($file) && $this->files->existsInContainer($container, $file)) {
57 | //we only remove on container first if it is a directory
58 | //as the new directory we push may have a different set of files in it
59 | //for files we can just overwrite and save some cycles
60 | $this->files->delete($container, [$file]);
61 | }
62 | }
63 |
64 | $this->files->upload($container, $files);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Command/Restart.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class Restart extends Command implements CommandInterface
14 | {
15 | public function configure()
16 | {
17 | $this
18 | ->setName('restart')
19 | ->setDescription('Restarts the containers')
20 | ->addOption('prod', 'p', InputOption::VALUE_OPTIONAL, 'Use when started with --prod / -p');
21 | }
22 |
23 | public function execute(InputInterface $input, OutputInterface $output)
24 | {
25 | $stopCommand = $this->getApplication()->find('stop');
26 | $upCommand = $this->getApplication()->find('up');
27 |
28 | $stopCommand->run($input, $output);
29 | $upCommand->run($input, $output);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Command/Ssh.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class Ssh extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | protected function configure()
30 | {
31 | $this
32 | ->setName('ssh')
33 | ->setDescription('Open up bash into the app container')
34 | ->addOption('root', 'r', InputOption::VALUE_NONE, 'Open as root user')
35 | ->addOption('container', 'c', InputOption::VALUE_REQUIRED, 'Container to SSH into');
36 | }
37 |
38 | public function execute(InputInterface $input, OutputInterface $output)
39 | {
40 | $container = $input->getOption('container')
41 | ? $this->getContainerName($input->getOption('container'))
42 | : $this->phpContainerName();
43 |
44 | $user = $input->getOption('root') ? 'root' : 'www-data';
45 |
46 | $width = $this->commandLine->runQuietly('tput cols');
47 | $height = $this->commandLine->runQuietly('tput lines');
48 |
49 | $command = <<commandLine->runInteractively($command);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Command/Stop.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class Stop extends Command implements CommandInterface
15 | {
16 | use DockerAwareTrait;
17 |
18 | /**
19 | * @var CommandLine
20 | */
21 | private $commandLine;
22 |
23 | public function __construct(CommandLine $commandLine)
24 | {
25 | parent::__construct();
26 | $this->commandLine = $commandLine;
27 | }
28 |
29 | public function configure()
30 | {
31 | $this
32 | ->setName('stop')
33 | ->setDescription('Stops the containers running')
34 | ->addOption('prod', 'p', InputOption::VALUE_OPTIONAL, 'Use when started with --prod / -p');
35 | }
36 |
37 | public function execute(InputInterface $input, OutputInterface $output)
38 | {
39 | $composeFiles = $this->getComposeFileFlags($input->getOption('prod') ? true : false);
40 |
41 | $this->commandLine->run(sprintf('docker-compose %s stop', $composeFiles));
42 |
43 | $output->writeln('Containers stopped');
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Command/Sync.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Sync extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var CommandLine
21 | */
22 | private $commandLine;
23 |
24 | public function __construct(CommandLine $commandLine)
25 | {
26 | parent::__construct();
27 | $this->commandLine = $commandLine;
28 | }
29 |
30 | public function configure()
31 | {
32 | $this
33 | ->setName('sync')
34 | ->setDescription('Syncs changes from the host filesystem to the relevant docker containers')
35 | ->addArgument('file', InputArgument::REQUIRED, 'The changed file path');
36 | }
37 |
38 | public function execute(InputInterface $input, OutputInterface $output)
39 | {
40 | $path = $input->getArgument('file');
41 | $containerPath = ltrim(str_replace(getcwd(), '', $path), '/');
42 | $container = $this->phpContainerName();
43 |
44 | if (file_exists($path)) {
45 | $pushCommand = $this->getApplication()->find('push');
46 | $pushArguments = new ArrayInput(['files' => [$path]]);
47 |
48 | $pushCommand->run($pushArguments, $output);
49 | return;
50 | }
51 |
52 | $this->commandLine->run(sprintf('docker exec %s rm -rf /var/www/%s', $container, $containerPath));
53 |
54 | $output->writeln(" x $containerPath > $container ");
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Command/Up.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Up extends Command implements CommandInterface
16 | {
17 | use DockerAwareTrait;
18 |
19 | /**
20 | * @var CommandLine
21 | */
22 | private $commandLine;
23 |
24 | public function __construct(CommandLine $commandLine)
25 | {
26 | parent::__construct();
27 | $this->commandLine = $commandLine;
28 | }
29 |
30 | public function configure()
31 | {
32 | $this
33 | ->setName('up')
34 | ->setAliases(['start'])
35 | ->setDescription('Uses docker-compose to start the containers')
36 | ->addOption('prod', 'p', InputOption::VALUE_OPTIONAL, 'Omits development configurations')
37 | ->addOption('no-build', null, InputOption::VALUE_NONE, 'Prevents running a full build');
38 | }
39 |
40 | public function execute(InputInterface $input, OutputInterface $output)
41 | {
42 | $buildArg = $input->getOption('no-build') ? '' : '--build';
43 |
44 | $composeFiles = $this->getComposeFileFlags($input->getOption('prod') ? true : false);
45 |
46 | $this->commandLine->run(rtrim(sprintf('docker-compose %s up -d %s', $composeFiles, $buildArg)));
47 |
48 | // Pull composer cache for future builds
49 | $pullCommand = $this->getApplication()->find('pull');
50 | $pullCommand->run(new ArrayInput(['files' => ['.docker/composer-cache']]), $output);
51 |
52 | $output->writeln('Containers started');
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Command/VarnishDisable.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class VarnishDisable extends Command implements CommandInterface
14 | {
15 | use DockerAwareTrait;
16 |
17 | /**
18 | * @var CommandLine
19 | */
20 | private $commandLine;
21 |
22 | public function __construct(CommandLine $commandLine)
23 | {
24 | parent::__construct();
25 | $this->commandLine = $commandLine;
26 | }
27 |
28 | public function configure()
29 | {
30 | $this
31 | ->setName('varnish-disable')
32 | ->setAliases(['vd'])
33 | ->setDescription('Switches the VCL to be a proxy');
34 | }
35 |
36 | public function execute(InputInterface $input, OutputInterface $output)
37 | {
38 | $this->commandLine->run('docker-compose exec -T varnish varnishadm vcl.use boot');
39 |
40 | $output->writeln('Varnish caching disabled');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Command/VarnishEnable.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class VarnishEnable extends Command implements CommandInterface
14 | {
15 | use DockerAwareTrait;
16 |
17 | /**
18 | * @var CommandLine
19 | */
20 | private $commandLine;
21 |
22 | public function __construct(CommandLine $commandLine)
23 | {
24 | parent::__construct();
25 | $this->commandLine = $commandLine;
26 | }
27 |
28 | public function configure()
29 | {
30 | $this
31 | ->setName('varnish-enable')
32 | ->setAliases(['ve'])
33 | ->setDescription('Switches the VCL to use caching');
34 | }
35 |
36 | public function execute(InputInterface $input, OutputInterface $output)
37 | {
38 | $this->commandLine->run('docker-compose exec -T varnish varnishadm vcl.use boot0');
39 |
40 | $output->writeln('Varnish caching enabled');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Command/Watch.php:
--------------------------------------------------------------------------------
1 |
17 | * @author Aydin Hassan
18 | */
19 | class Watch extends Command implements CommandInterface
20 | {
21 | use DockerAwareTrait;
22 |
23 | /**
24 | * @var WatchFactory
25 | */
26 | private $watchFactory;
27 |
28 | /**
29 | * @var Files
30 | */
31 | private $files;
32 |
33 | public function __construct(WatchFactory $watchFactory, Files $files)
34 | {
35 | parent::__construct();
36 | $this->watchFactory = $watchFactory;
37 | $this->files = $files;
38 | }
39 |
40 | public function configure()
41 | {
42 | $this
43 | ->setName('watch')
44 | ->setDescription('Keeps track of filesystem changes, piping the changes to the sync command')
45 | ->addArgument('watches', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Custom paths to watch')
46 | ->addOption('no-defaults');
47 | }
48 |
49 | public function execute(InputInterface $input, OutputInterface $output)
50 | {
51 | $watches = ['app/code', 'app/design','app/i18n', 'composer.json', 'phpcs.xml', 'phpunit.xml'];
52 | $excludes = ['".*__jb_.*$"', '".*swp$"', '".*swpx$"'];
53 |
54 | $watches = $input->getOption('no-defaults')
55 | ? $input->getArgument('watches')
56 | : array_merge($input->getArgument('watches'), $watches);
57 |
58 | if (!$watches) {
59 | throw new \InvalidArgumentException('You must watch at least something...');
60 | }
61 |
62 | $output->writeln('Watching for file changes...');
63 | $output->writeln('');
64 |
65 | $phpContainer = $this->phpContainerName();
66 | $fsWatch = $this->watchFactory->create($watches, $excludes);
67 | $fsWatch->lift(function () {
68 | return new BufferWithTime(500, Scheduler::getAsync());
69 | })->subscribe(function (array $watches) use ($phpContainer) {
70 | $files = collect($watches)
71 | ->reject(function (WatchEvent $event) {
72 | return $event->isDir();
73 | })
74 | ->map(function (WatchEvent $event) {
75 | return $event->getFile();
76 | });
77 |
78 | list($exists, $removed) = $files->partition(function ($item) {
79 | return file_exists($item);
80 | });
81 |
82 | if ($removed->isNotEmpty()) {
83 | $this->files->delete($phpContainer, $removed->values()->all());
84 | }
85 |
86 | if ($exists->isNotEmpty()) {
87 | $this->files->upload($phpContainer, $exists->values()->all());
88 | }
89 | });
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/CommandLine.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class CommandLine
16 | {
17 | /**
18 | * @var LoopInterface
19 | */
20 | private $eventLoop;
21 |
22 | /**
23 | * @var LoggerInterface
24 | */
25 | private $logger;
26 |
27 | /**
28 | * @var OutputInterface
29 | */
30 | private $output;
31 |
32 | public function __construct(LoopInterface $eventLoop, LoggerInterface $logger, OutputInterface $output)
33 | {
34 | $this->eventLoop = $eventLoop;
35 | $this->logger = $logger;
36 | $this->output = $output;
37 | }
38 |
39 | public function run(string $command) : string
40 | {
41 | $this->logCommand($command, 'normal');
42 |
43 | return $this->runProcess($this->newProcess($command), $this->onOutput());
44 | }
45 |
46 | public function runQuietly(string $command) : string
47 | {
48 | $this->logCommand($command, 'quiet');
49 |
50 | return $this->runProcess($this->newProcess($command));
51 | }
52 |
53 | public function runInteractively(string $command) : string
54 | {
55 | $this->logCommand($command, 'interactive');
56 |
57 | $process = $this->newProcess($command);
58 | $process->setTty(true);
59 |
60 | return $this->runProcess($process, $this->onOutput());
61 | }
62 |
63 | private function newProcess(string $command) : Process
64 | {
65 | $process = new Process($command);
66 | $process->setTimeout(null);
67 | return $process;
68 | }
69 |
70 | private function runProcess(Process $process, $onOutput = null) : string
71 | {
72 | $exitCode = $process->run($onOutput);
73 |
74 | if ($exitCode > 0) {
75 | throw new ProcessFailedException($process->getErrorOutput());
76 | }
77 |
78 | return $process->getOutput();
79 | }
80 |
81 | private function onOutput() : callable
82 | {
83 | return function ($type, $buffer) {
84 | $this->output->write($buffer);
85 | };
86 | }
87 |
88 | public function runAsync(string $command, callable $onComplete = null)
89 | {
90 | $this->logCommand($command, 'async');
91 |
92 | $errorSubject = new Subject;
93 | $errorSubject->subscribe(function (\Exception $e) {
94 | throw new ProcessFailedException($e->getMessage());
95 | });
96 |
97 | $process = new ProcessSubject($command, $errorSubject, null, [], [], $this->eventLoop);
98 | $process->subscribe(
99 | function ($buffer) {
100 | $this->output->write($buffer);
101 | },
102 | null,
103 | $onComplete
104 | );
105 | }
106 |
107 | private function logCommand(string $command, string $type)
108 | {
109 | $this->logger->logCommand($command, $type);
110 | }
111 |
112 | public function commandExists(string $executable) : bool
113 | {
114 | return (bool) (new ExecutableFinder)->find($executable, false);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/Config/ConfigGeneratorFactory.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class ConfigGeneratorFactory
11 | {
12 | /**
13 | * @var M1ConfigGenerator
14 | */
15 | private $m1ConfigGenerator;
16 |
17 | /**
18 | * @var M2ConfigGenerator
19 | */
20 | private $m2ConfigGenerator;
21 |
22 | public function __construct(M1ConfigGenerator $m1ConfigGenerator, M2ConfigGenerator $m2ConfigGenerator)
23 | {
24 | $this->m1ConfigGenerator = $m1ConfigGenerator;
25 | $this->m2ConfigGenerator = $m2ConfigGenerator;
26 | }
27 |
28 | public function create(Platform $platform) : ConfigGeneratorInterface
29 | {
30 | switch ($platform) {
31 | case Platform::M1():
32 | return $this->m1ConfigGenerator;
33 | break;
34 | case Platform::M2():
35 | default:
36 | return $this->m2ConfigGenerator;
37 | break;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Config/ConfigGeneratorInterface.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | interface ConfigGeneratorInterface
11 | {
12 | /**
13 | * @param string $rootDir Root directory for Platform installation
14 | * @param SymfonyStyle $output
15 | * @return void
16 | */
17 | public function generateEnvironmentConfig(string $rootDir, SymfonyStyle $output);
18 | }
19 |
--------------------------------------------------------------------------------
/src/Config/M1ConfigGenerator.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 | class M1ConfigGenerator implements ConfigGeneratorInterface
10 | {
11 | public function generateEnvironmentConfig(string $rootDir, SymfonyStyle $output)
12 | {
13 | $outputPath = file_exists($rootDir . '/htdocs')
14 | ? $rootDir . '/htdocs/app/etc/local.xml'
15 | : $rootDir . '/app/etc/local.xml';
16 |
17 | if (!file_exists(dirname($outputPath)) && !mkdir(dirname($outputPath), 0777, true)) {
18 | throw new \RuntimeException(sprintf('Unable to create path "%s"', dirname($outputPath)));
19 | }
20 |
21 | $config = file_get_contents(__DIR__ . '/../../templates/config/M1/local.xml.template');
22 | file_put_contents($outputPath, $config);
23 |
24 | $output->success('Fresh configuration written to ' . $outputPath);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Config/M2ConfigGenerator.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class M2ConfigGenerator implements ConfigGeneratorInterface
12 | {
13 | /**
14 | * @param string $rootDir
15 | * @param SymfonyStyle $output
16 | * @throws \RuntimeException When unable to output path
17 | */
18 | public function generateEnvironmentConfig(string $rootDir, SymfonyStyle $output)
19 | {
20 | $outputPath = $rootDir . '/app/etc/env.php';
21 |
22 | if (!file_exists(dirname($outputPath)) && !mkdir(dirname($outputPath), 0777, true)) {
23 | throw new \RuntimeException(sprintf('Unable to create path "%s"', dirname($outputPath)));
24 | }
25 |
26 | $standardConfig = file_get_contents(__DIR__ . '/../../templates/config/M2/env.php.template');
27 |
28 | $mode = $output->choice('Which Magento deployment mode ?', ['developer', 'production'], 'developer');
29 | $queues = $output->confirm('Do you require queue (e.g. RabbitMQ) configuration ?', false);
30 |
31 | $config = str_replace(
32 | ['{mage-mode}', '{use-rabbit}'],
33 | [$mode, $queues ? '' : '#'],
34 | $standardConfig
35 | );
36 |
37 | file_put_contents($outputPath, $config);
38 |
39 | $output->success('Fresh configuration written to ' . $outputPath);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Logger.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class Logger extends AbstractLogger implements LoggerInterface
12 | {
13 | /**
14 | * @var string
15 | */
16 | private $logFile;
17 |
18 | /**
19 | * @var OutputInterface
20 | */
21 | private $output;
22 |
23 | public function __construct(OutputInterface $output)
24 | {
25 | $this->logFile = getenv('HOME') . '/workflow.log';
26 | $this->init();
27 | $this->output = $output;
28 | }
29 |
30 | public function log($level, $message, array $context = [])
31 | {
32 | $dateTime = (new \DateTime)->format('d-m-y H:i:s');
33 | $line = sprintf("%s (%s) %s\n", $dateTime, strtoupper($level), $message);
34 | file_put_contents(
35 | $this->logFile,
36 | $line,
37 | FILE_APPEND | LOCK_EX
38 | );
39 | }
40 |
41 | public function logCommand(string $command, string $type)
42 | {
43 | $this->debug(sprintf('Executing command [%s]: "%s"', $type, $command));
44 |
45 | $this->output->writeln(
46 | sprintf(
47 | 'Executing [%s] %s',
48 | $type,
49 | $command
50 | )
51 | );
52 | }
53 |
54 | private function init()
55 | {
56 | @mkdir(dirname($this->logFile), 0777, true);
57 | }
58 |
59 | /**
60 | * For testing
61 | */
62 | public function setLogFile(string $logFile)
63 | {
64 | $this->logFile = $logFile;
65 | $this->init();
66 | }
67 | }
--------------------------------------------------------------------------------
/src/LoggerInterface.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | interface LoggerInterface extends \Psr\Log\LoggerInterface
9 | {
10 | public function logCommand(string $command, string $type);
11 | }
--------------------------------------------------------------------------------
/src/NewProject/Details.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class Details
9 | {
10 | private $repo;
11 | private $projectName;
12 | private $namespace;
13 | private $version;
14 | private $pubKey;
15 | private $privKey;
16 | private $accessToken;
17 | private $rabbitMQ;
18 |
19 | public function __construct(
20 | string $repo,
21 | string $projectName,
22 | string $namespace,
23 | string $version,
24 | string $pubKey,
25 | string $privKey,
26 | string $accessToken,
27 | bool $rabbitMQ
28 | ) {
29 | $this->repo = $repo;
30 | $this->projectName = $projectName;
31 | $this->namespace = $namespace;
32 | $this->version = $version;
33 | $this->pubKey = $pubKey;
34 | $this->privKey = $privKey;
35 | $this->accessToken = $accessToken;
36 | $this->rabbitMQ = $rabbitMQ;
37 | }
38 |
39 | public function getRepo() : string
40 | {
41 | return $this->repo;
42 | }
43 |
44 | public function getProjectName() : string
45 | {
46 | return $this->projectName;
47 | }
48 |
49 | public function getNamespace() : string
50 | {
51 | return $this->namespace;
52 | }
53 |
54 | public function getVersion() : string
55 | {
56 | return $this->version;
57 | }
58 |
59 | public function getPubKey() : string
60 | {
61 | return $this->pubKey;
62 | }
63 |
64 | public function getPrivKey() : string
65 | {
66 | return $this->privKey;
67 | }
68 |
69 | public function getProjectDomain() : string
70 | {
71 | return strtolower($this->projectName) . '.dev';
72 | }
73 |
74 | public function getAccessToken() : string
75 | {
76 | return $this->accessToken;
77 | }
78 |
79 | public function includeRabbitMQ() : bool
80 | {
81 | return $this->rabbitMQ;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/NewProject/DetailsGatherer.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class DetailsGatherer
11 | {
12 | private $versionMap = [
13 | 'EE' => 'enterprise',
14 | 'CE' => 'community',
15 | ];
16 |
17 | public function gatherDetails(OutputStyle $output)
18 | {
19 | $output->note('Gathering Details..');
20 |
21 | $repo = $output->ask('GitHub repository? Go create an empty repo first', null, function ($answer) {
22 | if (!preg_match('/^git@github\.com:[A-z]+\/[a-z0-9-]+.git$/', $answer)) {
23 | throw new \RuntimeException('GitHub url looks incorrect. Make sure it\'s the SSH url');
24 | }
25 | return $answer;
26 | });
27 |
28 | preg_match('/^git@github\.com:[A-z]+\/([a-z0-9-]+).git$/', $repo, $matches);
29 |
30 | $projectName = $matches[1];
31 |
32 | if (file_exists($projectName)) {
33 | throw new \RuntimeException(sprintf('folder %s exists already', $projectName));
34 | }
35 |
36 | $namespace = $output->ask('Project Namespace? Eg: Neom', null, function ($answer) {
37 | if (!preg_match('/^[A-Z][a-z]+$/', $answer)) {
38 | throw new \RuntimeException('Package name must be a valid PHP namespace. Eg: Neom');
39 | }
40 | return $answer;
41 | });
42 |
43 | $version = $this->versionMap[$output->choice('Magento edition?', array_keys($this->versionMap), 'CE')];
44 |
45 | $defaultPub = null;
46 | $defaultPriv = null;
47 | if (file_exists(getenv('HOME') . '/.composer/auth.json')) {
48 | $data = json_decode(file_get_contents(getenv('HOME') . '/.composer/auth.json'), true);
49 |
50 | if (isset($data['http-basic']['repo.magento.com'])) {
51 | $defaultPub = $data['http-basic']['repo.magento.com']['username'];
52 | $defaultPriv = $data['http-basic']['repo.magento.com']['password'];
53 | }
54 | }
55 |
56 | $pubKey = $output->ask('Public auth key?', $defaultPub);
57 | $privKey = $output->ask('Private auth key?', $defaultPriv);
58 |
59 | $accessToken = getenv('GITHUB_ACCESS_TOKEN') ?: $output->ask('GitHub Access Token?');
60 |
61 | $rabbitMq = $version === $this->versionMap['EE']
62 | ? $output->confirm('Include Rabbit MQ?')
63 | : false;
64 |
65 | return new Details(
66 | $repo,
67 | $projectName,
68 | $namespace,
69 | $version,
70 | $pubKey,
71 | $privKey,
72 | $accessToken,
73 | $rabbitMq
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/NewProject/Step/AuthJson.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class AuthJson implements StepInterface
13 | {
14 | /**
15 | * @var TemplateWriter
16 | */
17 | private $templateWriter;
18 |
19 | public function __construct(TemplateWriter $templateWriter)
20 | {
21 | $this->templateWriter = $templateWriter;
22 | }
23 |
24 | public function run(Details $details, OutputStyle $output)
25 | {
26 | $output->success(sprintf('Creating auth.json in %s with provided keys', $details->getProjectName()));
27 |
28 | $this->templateWriter->fillAndWriteTemplate(
29 | $details->getProjectName(),
30 | 'composer/auth.json',
31 | 'auth.json',
32 | [
33 | 'access-token' => $details->getAccessToken(),
34 | 'pubkey' => $details->getPubKey(),
35 | 'prikey' => $details->getPrivKey(),
36 | ]
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/NewProject/Step/Capistrano.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class Capistrano implements StepInterface
13 | {
14 | /**
15 | * @var TemplateWriter
16 | */
17 | private $templateWriter;
18 |
19 | public function __construct(TemplateWriter $templateWriter)
20 | {
21 | $this->templateWriter = $templateWriter;
22 | }
23 |
24 | public function run(Details $details, OutputStyle $output)
25 | {
26 | $output->success('Adding capistrano config');
27 |
28 | $this->templateWriter->copyTemplate($details->getProjectName(), 'capistrano/Capfile', 'Capfile');
29 | $this->templateWriter->copyTemplate($details->getProjectName(), 'capistrano/Gemfile', 'Gemfile');
30 |
31 | $this->templateWriter->fillAndWriteTemplate(
32 | $details->getProjectName(),
33 | 'capistrano/deploy.rb',
34 | 'cap/deploy.rb',
35 | [
36 | 'project-name' => $details->getProjectName()
37 | ]
38 | );
39 |
40 | $this->templateWriter->fillAndWriteTemplate(
41 | $details->getProjectName(),
42 | 'capistrano/dev.rb',
43 | 'cap/deploy/dev.rb',
44 | [
45 | 'project-name' => $details->getProjectName()
46 | ]
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/NewProject/Step/CircleCI.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class CircleCI implements StepInterface
13 | {
14 | /**
15 | * @var TemplateWriter
16 | */
17 | private $templateWriter;
18 |
19 | public function __construct(TemplateWriter $templateWriter)
20 | {
21 | $this->templateWriter = $templateWriter;
22 | }
23 |
24 | public function run(Details $details, OutputStyle $output)
25 | {
26 | $output->success('Adding Circle CI config');
27 | $this->templateWriter->copyTemplate($details->getProjectName(), 'circle/circle.yml', 'circle.yml');
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/NewProject/Step/ComposerJson.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class ComposerJson implements StepInterface
16 | {
17 | /**
18 | * @var CommandLine
19 | */
20 | private $commandLine;
21 |
22 | public function __construct(CommandLine $commandLine)
23 | {
24 | $this->commandLine = $commandLine;
25 | }
26 |
27 | public function run(Details $details, OutputStyle $output)
28 | {
29 | $output->success('Setting up composer.json including CS scripts');
30 |
31 | $data = json_decode(file_get_contents($details->getProjectName() . '/composer.json'), true);
32 | $csFormat = 'phpcs -s app/code/%s --standard=vendor/wearejh/php-coding-standards/Jh';
33 | $csFixFormat = 'phpcbf -s app/code/%s --standard=vendor/wearejh/php-coding-standards/Jh';
34 |
35 | $data['name'] = $details->getProjectName() . '-magento2';
36 | $data['description'] = 'eCommerce Platform for ' . $details->getProjectName();
37 | $data['repositories'][] = ['type' => 'vcs', 'url' => 'git@github.com:WeareJH/php-coding-standards.git'];
38 |
39 | $data['scripts'] = [
40 | 'test' => ['@cs', '@unit-tests'],
41 | 'cs' => sprintf($csFormat, $details->getNamespace()),
42 | 'cs-fix' => sprintf($csFixFormat, $details->getNamespace()),
43 | 'unit-tests' => 'phpunit',
44 | 'coverage' => 'phpunit --coverage-text',
45 | 'bootstrap' => 'composer install -o --prefer-dist --ignore-platform-reqs'
46 | ];
47 |
48 | $data['require']['php'] = '>=7.0.6 <7.1';
49 |
50 | $data['require-dev']['wearejh/php-coding-standards'] = 'dev-master';
51 | $data['require-dev']['wearejh/m2-module-symlink-assets'] = '^1.0';
52 | $data['require-dev']['squizlabs/php_codesniffer'] = '^3.0';
53 | $data['require-dev']['phpunit/phpunit'] = '^6.0';
54 |
55 | file_put_contents(
56 | $details->getProjectName() . '/composer.json',
57 | json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
58 | );
59 |
60 | $cwd = getcwd();
61 | chdir($details->getProjectName());
62 |
63 | $output->success('Updating composer lock file');
64 | try {
65 | $command = 'docker run --rm ';
66 | $command .= '-v %s:/root/build -v %s/.composer/cache:/root/.composer/cache ';
67 | $command .= 'wearejh/ci-build-env composer update --prefer-dist -qo';
68 |
69 | $this->commandLine->run(sprintf($command, getcwd(), getenv('HOME')));
70 | } catch (ProcessFailedException $e) {
71 | throw new \RuntimeException('Could not update composer lock file');
72 | }
73 |
74 | chdir($cwd);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/NewProject/Step/CreateProject.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class CreateProject implements StepInterface
16 | {
17 | /**
18 | * @var TemplateWriter
19 | */
20 | private $templateWriter;
21 |
22 | /**
23 | * @var CommandLine
24 | */
25 | private $commandLine;
26 |
27 | public function __construct(CommandLine $commandLine, TemplateWriter $templateWriter)
28 | {
29 | $this->templateWriter = $templateWriter;
30 | $this->commandLine = $commandLine;
31 | }
32 |
33 | public function run(Details $details, OutputStyle $output)
34 | {
35 | $output->success(sprintf('Running composer create-project into %s', $details->getProjectName()));
36 |
37 | $cmdFormat = 'docker run --rm ';
38 | $cmdFormat .= '-v %s:/root/build -v %s/.composer/cache:/root/.composer/cache ';
39 | $cmdFormat .= 'wearejh/ci-build-env ';
40 | $cmdFormat .= 'composer create-project -q --repository-url=https://%s:%s@repo.magento.com/ ';
41 | $cmdFormat .= 'magento/project-%s-edition %s --prefer-dist';
42 |
43 | $command = sprintf(
44 | $cmdFormat,
45 | getcwd(),
46 | getenv('HOME'),
47 | $details->getPubKey(),
48 | $details->getPrivKey(),
49 | $details->getVersion(),
50 | $details->getProjectName()
51 | );
52 |
53 | $this->commandLine->run($command);
54 |
55 | $filesToRemove = [
56 | '/ISSUE_TEMPLATE.md',
57 | '/nginx.conf.sample',
58 | '/php.ini.sample',
59 | '/package.json.sample',
60 | '/LICENSE.txt',
61 | '/LICENSE_AFL.txt',
62 | '/LICENSE_EE.txt',
63 | '/README_EE.txt',
64 | '/Gruntfile.js.sample',
65 | '/CHANGELOG.md',
66 | '/CONTRIBUTING.md',
67 | '/CHANGELOG.md',
68 | '/.travis.yml',
69 | '/.php_cs',
70 | '/.htaccess.sample',
71 | '/.htaccess',
72 | ];
73 |
74 | foreach ($filesToRemove as $file) {
75 | $file = $details->getProjectName() . $file;
76 |
77 | if (file_exists($file)) {
78 | unlink($file);
79 | }
80 | }
81 |
82 | $this->templateWriter->copyTemplate($details->getProjectName(), 'git/.gitignore', '.gitignore');
83 |
84 | $output->success(sprintf('Creating code directory app/code/%s', $details->getNamespace()));
85 |
86 | mkdir($details->getProjectName() . '/app/code/' . $details->getNamespace(), 0777, true);
87 | touch($details->getProjectName() . '/app/code/' . $details->getNamespace() . '/.gitkeep');
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/NewProject/Step/GitClean.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class GitClean implements StepInterface
13 | {
14 | /**
15 | * @var CommandLine
16 | */
17 | private $commandLine;
18 |
19 | public function __construct(CommandLine $commandLine)
20 | {
21 | $this->commandLine = $commandLine;
22 | }
23 |
24 | public function run(Details $details, OutputStyle $output)
25 | {
26 | $output->success('Cleaning your host');
27 |
28 | $cwd = getcwd();
29 | chdir($details->getProjectName());
30 |
31 | $this->commandLine->runQuietly('git clean -dfX');
32 |
33 | chdir($cwd);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/NewProject/Step/GitCommit.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class GitCommit implements StepInterface
15 | {
16 | /**
17 | * @var CommandLine
18 | */
19 | private $commandLine;
20 |
21 | public function __construct(CommandLine $commandLine)
22 | {
23 | $this->commandLine = $commandLine;
24 | }
25 |
26 | public function run(Details $details, OutputStyle $output)
27 | {
28 | $output->success('Commit config and pushing to repo');
29 |
30 | $cwd = getcwd();
31 | chdir($details->getProjectName());
32 |
33 | $command = 'git add .';
34 | $command .= ' && git commit -m "Project Config"';
35 | $command .= ' && git push origin master';
36 |
37 | $this->commandLine->runQuietly($command);
38 |
39 | chdir($cwd);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/NewProject/Step/GitInit.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class GitInit implements StepInterface
15 | {
16 | /**
17 | * @var CommandLine
18 | */
19 | private $commandLine;
20 |
21 | public function __construct(CommandLine $commandLine)
22 | {
23 | $this->commandLine = $commandLine;
24 | }
25 |
26 | public function run(Details $details, OutputStyle $output)
27 | {
28 | $output->success('Initialising git repo');
29 |
30 | $cwd = getcwd();
31 | chdir($details->getProjectName());
32 |
33 | $command = 'git init';
34 | $command .= ' && git remote add origin ' . $details->getRepo();
35 | $command .= ' && git add .';
36 | $command .= ' && git commit -m "Add magento"';
37 |
38 | $this->commandLine->run($command);
39 |
40 | chdir($cwd);
41 |
42 | $output->success('Initialised repo');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/NewProject/Step/PhpStorm.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class PhpStorm implements StepInterface
13 | {
14 | /**
15 | * @var TemplateWriter
16 | */
17 | private $templateWriter;
18 |
19 | public function __construct(TemplateWriter $templateWriter)
20 | {
21 | $this->templateWriter = $templateWriter;
22 | }
23 |
24 | public function run(Details $details, OutputStyle $output)
25 | {
26 | $output->success('Adding PHPStorm meta');
27 |
28 | $this->templateWriter->copyTemplate(
29 | $details->getProjectName(),
30 | 'phpstorm/.phpstorm.meta.php',
31 | '.phpstorm.meta.php'
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/NewProject/Step/PrTemplate.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class PrTemplate implements StepInterface
13 | {
14 | /**
15 | * @var TemplateWriter
16 | */
17 | private $templateWriter;
18 |
19 | public function __construct(TemplateWriter $templateWriter)
20 | {
21 | $this->templateWriter = $templateWriter;
22 | }
23 |
24 | public function run(Details $details, OutputStyle $output)
25 | {
26 | $output->success('Adding PR template');
27 |
28 | $this->templateWriter->copyTemplate(
29 | $details->getProjectName(),
30 | 'pr/PULL_REQUEST_TEMPLATE.md',
31 | 'PULL_REQUEST_TEMPLATE.md'
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/NewProject/Step/Readme.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class Readme implements StepInterface
13 | {
14 | /**
15 | * @var TemplateWriter
16 | */
17 | private $templateWriter;
18 |
19 | public function __construct(TemplateWriter $templateWriter)
20 | {
21 | $this->templateWriter = $templateWriter;
22 | }
23 |
24 | public function run(Details $details, OutputStyle $output)
25 | {
26 | $output->success('Adding Readme');
27 |
28 | $this->templateWriter->fillAndWriteTemplate(
29 | $details->getProjectName(),
30 | 'readme/README.md',
31 | 'README.md',
32 | [
33 | 'project-name' => $details->getProjectName(),
34 | 'project-namespace' => $details->getNamespace(),
35 | 'project-domain' => $details->getProjectDomain(),
36 | 'project-repo' => $details->getRepo()
37 | ]
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/NewProject/Step/StepInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface StepInterface
12 | {
13 | public function run(Details $details, OutputStyle $output);
14 | }
15 |
--------------------------------------------------------------------------------
/src/NewProject/StepRunner.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class StepRunner
12 | {
13 | /**
14 | * @var StepInterface[]
15 | */
16 | private $steps;
17 |
18 | public function __construct(array $steps)
19 | {
20 | $this->steps = array_filter($steps, function ($step) {
21 | return in_array(StepInterface::class, class_implements($step), true);
22 | });
23 | }
24 |
25 | public function run(Details $details, OutputStyle $output)
26 | {
27 | foreach ($this->steps as $step) {
28 | $step->run($details, $output);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/NewProject/TemplateWriter.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class TemplateWriter
9 | {
10 | const TEMPLATE_DIR = __DIR__ . '/../../templates/';
11 |
12 | public function copyTemplate(string $projectName, string $templatePath, string $projectDestPath)
13 | {
14 | copy(static::TEMPLATE_DIR . $templatePath, $projectName . '/' .$projectDestPath);
15 | }
16 |
17 | public function fillAndWriteTemplate(
18 | string $projectName,
19 | string $templateFile,
20 | string $outFile,
21 | array $searchAndReplace
22 | ) {
23 | $contents = file_get_contents(static::TEMPLATE_DIR . $templateFile);
24 | $outFilePath = $projectName . '/' . $outFile;
25 |
26 | foreach ($searchAndReplace as $search => $replace) {
27 | $contents = str_replace('{' . $search . '}', $replace, $contents);
28 | }
29 |
30 | $this->writeContents($outFilePath, $contents);
31 | }
32 |
33 | public function regexFillAndWriteTemplate(
34 | string $projectName,
35 | string $templateFile,
36 | string $outFile,
37 | array $regextAndReplace
38 | ) {
39 | $contents = file_get_contents(static::TEMPLATE_DIR . $templateFile);
40 | $outFilePath = $projectName . '/' . $outFile;
41 |
42 | foreach ($regextAndReplace as $regex => $replace) {
43 | $contents = preg_replace($regex, $replace, $contents);
44 | }
45 |
46 | $this->writeContents($outFilePath, $contents);
47 | }
48 |
49 | private function writeContents(string $path, string $contents)
50 | {
51 | if (!file_exists(dirname($path))) {
52 | mkdir(dirname($path), 0777, true);
53 | }
54 |
55 | file_put_contents($path, $contents);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/NullLogger.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class NullLogger extends \Psr\Log\NullLogger implements LoggerInterface
9 | {
10 |
11 | public function logCommand(string $command, string $type)
12 | {
13 | //noop
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Platform.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Platform extends Enum
11 | {
12 | const M1 = 'm1';
13 | const M2 = 'm2';
14 | }
15 |
--------------------------------------------------------------------------------
/src/ProcessFailedException.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class ProcessFailedException extends \RuntimeException
9 | {
10 |
11 | }
--------------------------------------------------------------------------------
/src/Test/Constraint/FileExistsInContainer.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class FileExistsInContainer extends Constraint
11 | {
12 | /**
13 | * @var string
14 | */
15 | private $container;
16 |
17 | public function __construct(string $container)
18 | {
19 | parent::__construct();
20 | $this->container = $container;
21 | }
22 |
23 | public function matches($other)
24 | {
25 | if (!is_string($other)) {
26 | return false;
27 | }
28 |
29 | try {
30 | $this->exec(sprintf('docker exec %s test -e %s', $this->container, $other));
31 | } catch (\RuntimeException $e) {
32 | return false;
33 | }
34 |
35 | return true;
36 | }
37 |
38 | private function exec(string $command)
39 | {
40 | exec($command, $output, $exitCode);
41 |
42 | if ($exitCode > 0) {
43 | throw new \RuntimeException('Command failed with exit code: ' . $exitCode);
44 | }
45 | }
46 |
47 | /**
48 | * Returns a string representation of the object.
49 | *
50 | * @return string
51 | */
52 | public function toString()
53 | {
54 | return 'exists in container ' . $this->container;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Test/Constraint/FileUserAndGroupInContainer.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class FileUserAndGroupInContainer extends Constraint
11 | {
12 | /**
13 | * @var string
14 | */
15 | private $container;
16 |
17 | /**
18 | * @var string
19 | */
20 | private $group;
21 | /**
22 | * @var string
23 | */
24 | private $user;
25 |
26 | public function __construct(string $container, string $group, string $user)
27 | {
28 | parent::__construct();
29 | $this->container = $container;
30 | $this->group = $group;
31 | $this->user = $user;
32 | }
33 |
34 | public function matches($other)
35 | {
36 | if (!is_string($other)) {
37 | return false;
38 | }
39 |
40 | try {
41 | return sprintf('%s:%s', $this->group, $this->user) ===
42 | $this->exec(sprintf('docker exec %s stat -c "%%G:%%U" %s', $this->container, $other));
43 | } catch (\RuntimeException $e) {
44 | return false;
45 | }
46 | }
47 |
48 | private function exec(string $command) : string
49 | {
50 | exec($command, $output, $exitCode);
51 |
52 | if ($exitCode > 0) {
53 | throw new \RuntimeException('Command failed with exit code: ' . $exitCode);
54 | }
55 |
56 | return trim(implode("\n", $output));
57 | }
58 |
59 | /**
60 | * Returns a string representation of the object.
61 | *
62 | * @return string
63 | */
64 | public function toString()
65 | {
66 | return 'has correct group and user in container ' . $this->container;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Test/WorkflowTest.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class WorkflowTest extends TestCase
14 | {
15 |
16 | public static function assertFileExistsInContainer(string $filePath, string $container, string $message = '')
17 | {
18 | self::assertThat($filePath, new FileExistsInContainer($container), $message);
19 | }
20 |
21 | public static function assertFileNotExistsInContainer(string $filePath, string $container, string $message = '')
22 | {
23 | self::assertThat($filePath, new LogicalNot(new FileExistsInContainer($container)), $message);
24 | }
25 |
26 | public static function assertFileUserAndGroupInContainer(string $filePath, string $group, string $user, string $container, string $message = '')
27 | {
28 | self::assertThat($filePath, new FileUserAndGroupInContainer($container, $group, $user));
29 | }
30 |
31 | protected function copyFileInToContainer(string $source, string $destination)
32 | {
33 | $this->exec(
34 | sprintf(
35 | 'docker exec m2-php mkdir -p %s',
36 | dirname($destination)
37 | )
38 | );
39 |
40 | $this->exec(
41 | sprintf(
42 | 'docker cp %s m2-php:%s',
43 | $source,
44 | $destination
45 | )
46 | );
47 |
48 | self::assertFileExistsInContainer($destination, 'm2-php');
49 | }
50 |
51 | protected function exec(string $command)
52 | {
53 | exec($command, $output, $exitCode);
54 |
55 | if ($exitCode > 0) {
56 | throw new \RuntimeException('Command failed with exit code: ' . $exitCode);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/WatchFactory.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class WatchFactory
13 | {
14 | /**
15 | * @var LoopInterface
16 | */
17 | private $loop;
18 |
19 | public function __construct(LoopInterface $loop)
20 | {
21 | $this->loop = $loop;
22 | }
23 |
24 | public function create(array $watches, array $excludes = []) : Observable
25 | {
26 | return new FsWatch(
27 | implode(' ', $watches),
28 | sprintf('-e %s -l 0.5', implode(' -e ', $excludes)),
29 | $this->loop
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/functions.php:
--------------------------------------------------------------------------------
1 | 3.4'
4 | gem 'capistrano-composer'
--------------------------------------------------------------------------------
/templates/capistrano/dev.rb:
--------------------------------------------------------------------------------
1 | set :stage, :development
2 | set :keep_releases, 1
3 | set :deploy_to, '/microcloud/domains/dev2jh/domains/dev.wearejh.info/___{project-name}'
4 | set :branch, 'develop'
5 |
6 | role :web, %w{www-data@dh1.c309.sonassihosting.com:3022}
--------------------------------------------------------------------------------
/templates/circle/circle.yml:
--------------------------------------------------------------------------------
1 | general:
2 | branches:
3 | only:
4 | - develop
5 | dependencies:
6 | pre:
7 | - gem install bundler
8 | - bundle install
9 | - sudo apt-get update; sudo apt-get install nmap
10 | cache_directories:
11 | - ~/.composer/cache
12 | machine:
13 | php:
14 | version: 7.0.4
15 | ruby:
16 | version: 2.1.2
17 | test:
18 | override:
19 | - composer test
20 | deployment:
21 | develop:
22 | branch: develop
23 | commands:
24 | - for x in $PORT1 $PORT2 $PORT3; do nmap -Pn --host_timeout 201 --max-retries 0 -p $x 149.86.99.250; done
25 | - cap dev deploy
--------------------------------------------------------------------------------
/templates/composer/auth.json:
--------------------------------------------------------------------------------
1 | {
2 | "github-oauth": {
3 | "github.com": "{access-token}"
4 | },
5 | "http-basic": {
6 | "repo.magento.com": {
7 | "username": "{pubkey}",
8 | "password": "{prikey}"
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/templates/config/M1/local.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeareJH/workflow/bf4d11820ab8eccf1b8f7a7d168419d58131c5d6/templates/config/M1/local.xml
--------------------------------------------------------------------------------
/templates/config/M1/local.xml.template:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | false
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 1
25 |
26 |
27 |
28 |
29 | Mage_Cache_Backend_Redis
30 |
31 | redis
32 | 6379
33 |
34 | 0
35 |
36 | 0
37 | 1
38 | 10
39 | 0
40 | 1
41 | 1
42 | 20480
43 | gzip
44 |
45 |
46 |
47 | db
48 |
49 | redis
50 | 6379
51 | 2.5
52 |
53 | 1
54 | 2048
55 | gzip
56 | 0
57 | 15
58 | 5
59 | 30
60 | 7200
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/templates/config/M2/env.php.template:
--------------------------------------------------------------------------------
1 | [
5 | 'frontName' => 'admin',
6 | ],
7 | 'db' => [
8 | 'connection' => [
9 | 'indexer' => [
10 | 'host' => 'db',
11 | 'dbname' => 'docker',
12 | 'username' => 'docker',
13 | 'password' => 'docker',
14 | 'model' => 'mysql4',
15 | 'engine' => 'innodb',
16 | 'initStatements' => 'SET NAMES utf8;',
17 | 'active' => '1',
18 | 'persistent' => NULL,
19 | ],
20 | 'default' => [
21 | 'host' => 'db',
22 | 'dbname' => 'docker',
23 | 'username' => 'docker',
24 | 'password' => 'docker',
25 | 'model' => 'mysql4',
26 | 'engine' => 'innodb',
27 | 'initStatements' => 'SET NAMES utf8;',
28 | 'active' => '1',
29 | ],
30 | ],
31 | 'table_prefix' => '',
32 | ],
33 | 'resource' => [
34 | 'default_setup' => [
35 | 'connection' => 'default',
36 | ],
37 | ],
38 | 'x-frame-options' => 'SAMEORIGIN',
39 | 'MAGE_MODE' => '{mage-mode}',
40 | 'session' => [
41 | 'save' => 'redis',
42 | 'redis' => [
43 | 'host' => 'redis',
44 | 'port' => '6379',
45 | 'password' => '',
46 | 'timeout' => '2.5',
47 | 'persistent_identifier' => '',
48 | 'database' => '0',
49 | 'compression_threshold' => '2048',
50 | 'compression_library' => 'gzip',
51 | 'log_level' => '1',
52 | 'max_concurrency' => '6',
53 | 'break_after_frontend' => '5',
54 | 'break_after_adminhtml' => '30',
55 | 'first_lifetime' => '600',
56 | 'bot_first_lifetime' => '60',
57 | 'bot_lifetime' => '7200',
58 | 'disable_locking' => '0',
59 | 'min_lifetime' => '60',
60 | 'max_lifetime' => '2592000',
61 | ],
62 | ],
63 | 'cache_types' => [
64 | 'config' => 1,
65 | 'layout' => 1,
66 | 'block_html' => 1,
67 | 'collections' => 1,
68 | 'reflection' => 1,
69 | 'db_ddl' => 1,
70 | 'eav' => 1,
71 | 'customer_notification' => 1,
72 | 'config_integration' => 1,
73 | 'config_integration_api' => 1,
74 | 'target_rule' => 1,
75 | 'full_page' => 1,
76 | 'amasty_shopby' => 1,
77 | 'translate' => 1,
78 | 'config_webservice' => 1,
79 | ],
80 | 'install' => [
81 | 'date' => 'Mon, 05 Mar 2018 11:35:35 +0000',
82 | ],
83 | 'cache' => [
84 | 'frontend' => [
85 | 'default' => [
86 | 'backend' => 'Cm_Cache_Backend_Redis',
87 | 'backend_options' => [
88 | 'server' => 'redis',
89 | 'port' => '6379',
90 | 'database' => '1',
91 | ],
92 | ],
93 | ],
94 | ],
95 | {use-rabbit} 'queue' => [
96 | {use-rabbit} 'amqp' => [
97 | {use-rabbit} 'host' => 'rabbitmq',
98 | {use-rabbit} 'port' => '5672',
99 | {use-rabbit} 'user' => getenv('RABBITMQ_DEFAULT_USER'),
100 | {use-rabbit} 'password' => getenv('RABBITMQ_DEFAULT_PASS'),
101 | {use-rabbit} 'virtualhost' => '/',
102 | {use-rabbit} 'ssl' => '',
103 | {use-rabbit} ],
104 | {use-rabbit} ],
105 | ];
106 |
--------------------------------------------------------------------------------
/templates/docker/.dockerignore:
--------------------------------------------------------------------------------
1 | # Ignore .docker to prevent it being copied with COPY ./
2 | .docker
3 |
4 | # Unignore these to allow docker build to copy them in
5 | !.docker/php
6 | !.docker/nginx
7 | !.docker/certs
8 | !.docker/composer-cache
9 |
10 | docker-compose*.yml
11 | .dockerignore
12 |
13 | # Ignore some files we don't need in the containers
14 | pub
15 | dev
16 | phpserver
17 | setup
18 | var
19 | LICENSE*.txt
20 | COPYING.txt
21 | .user.ini
22 | .travis.yml
23 | .php_cs
24 | .htaccess
25 | *.sample
26 | *.md
27 | generated
28 |
29 | # Only take custom code from host
30 | app
31 | !app/code
32 | !app/design
33 |
34 | Gemfile
35 | cap
36 | Capfile
37 | circle.yml
38 | .phpstorm.meta.php
39 |
40 | .git
41 | .gitignore
42 | .idea
43 | vendor
44 |
45 |
--------------------------------------------------------------------------------
/templates/docker/certs/cert0.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEpAIBAAKCAQEA2wa+9FzdlSLoiMlHh7h6bTVtN2FC70QGKVGCr7cw2CDHnsnD
3 | TEC0HeDNMRZ9byXU0uRBDAeixSMJK+qhHGoVwqVv63XdH6Vk4M/Nc3fta/rWXNVm
4 | wBJM3k+uA6vQ17CTXmq5N5SOi5Yp2CY77dZ1V6BtihOz/8WeZIXR8P+cQYbxHh4Z
5 | yNLoqISfdmrar71/D6KvoiD7kQSIqsW8w8/ZIBrF7fe7GCA/xefV6N3PSxwbNQEF
6 | BfF+SKs6blfiWl9rmYrZKR8pKc2GvryAR1o+QAH2dnzDsiYU//IMP4+efl4/CNH+
7 | CnX8S7WCvIlWntrHJ+4J6Rs+CAg+Qj/orgJc5QIDAQABAoIBACiONDd9Jo5h21rQ
8 | ok0QLKMiHGn/uWwiVV27OQ6eRg4O68eMJnxtEqzhnjzzpCA7ig/OsfivRUVpel5G
9 | YLSpNARJq9KWjW45qtcxwyIZV74BwUWJQjBYcyFK8ba+TrpReMgnzMns2QQhtvfO
10 | BJTCfBHQo9fIVDEM/4oveTM9sUo0gZj1LngZwYU7KhwsoK4xGB/c01VtORXN0Cxv
11 | 5xxIRbw88EntSJi8nzr6Jb6ceo6ugqkRUpr3M4nycgzCej0P2w45VYT5cuuYnLCJ
12 | vKh60jtB4+/x6/VIwB85f3E4TisxZ/FurNVTbL/ckpxf3fEdGJ0h8yX8wjxRyyML
13 | SR+v/vUCgYEA9jJTQVx9Y1KaaZVcmXOakGEwTXyazEkG7UjwJ8akY9xEkD+85SHX
14 | xAXsV0u/GUXp8Mzee/ekfq5//c111WWJzwJVb8keEcyHm4rhnuP4K3dIBRlc7oWg
15 | 0iFes4pKLJf+3cYCgMwcE3BWHdU8oxKChZKBiysE15o67CevvE1lbkMCgYEA4790
16 | Br56JsiZcqDjHdbxlQY6dChBQNUgA9gjmsPtoBRARO+Su1alI0K6Ortd7qN6U+6/
17 | tR6MfJ0vmpCLJ/118FcRjce75gp7Lh2skhALNMShkqVsXv1gi6wZ0ci0vT6Kr+kI
18 | sixkn/FuqF6hLyZt3T8LfV9jtlbDu8buATyUGbcCgYEA42b8i6TYlINZ6ShzDbJA
19 | FBgRO6FaglL5uPbkDHloomx0UCDvY11tJLyr3r4yVy/CtA8nea32HKUlx9Kdgmx1
20 | a+Yl8Ej+I0aeA0e2usKrGcrllQAmXJLFRxJXnNKhTKtgWIxrB3iAflwGzyuFBMM2
21 | GBaI3Xjw0gy9XCAULIP4qm0CgYEAyJRaZIInZLbxZiJKRIKEu8IDgz/c0HOjwZ7/
22 | JJQAWSbcv5nbugCCaj6fc5CHFuCFoRw5XROtmSZ6wX8h/7NbxrN4M01AsEZ03FWo
23 | Ie/dXrj6sAPfIP24pOqKxtckTzOgw3LShNFSQgdJdSH6hWMsCVo4DVAQqotZ0axO
24 | +2nV6ZsCgYBjiaowVqf/PsnDdGYciezCxVJ2GwHsO47QaNzjLfyVkd9ueNefrIlg
25 | CLE5BBB4BK45ZWuO7Dk0F92ytUZ8Pi2/48fIRn+C+j9oRlmHKLgHiGhnoWLPXDYS
26 | z48hmO/g56d0472FXC5OqBqRVsu8MPXPyxdDoMJIGQL5F5dbfk+yfg==
27 | -----END RSA PRIVATE KEY-----
28 | -----BEGIN CERTIFICATE-----
29 | MIIDWzCCAkOgAwIBAgIJAJc1JICrjnAPMA0GCSqGSIb3DQEBBQUAMCcxCjAIBgNV
30 | BAMUASoxDDAKBgNVBAoTA0pILjELMAkGA1UEBhMCVUswHhcNMTcxMDI1MTczOTQ0
31 | WhcNMjAxMDA5MTczOTQ0WjAnMQowCAYDVQQDFAEqMQwwCgYDVQQKEwNKSC4xCzAJ
32 | BgNVBAYTAlVLMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2wa+9Fzd
33 | lSLoiMlHh7h6bTVtN2FC70QGKVGCr7cw2CDHnsnDTEC0HeDNMRZ9byXU0uRBDAei
34 | xSMJK+qhHGoVwqVv63XdH6Vk4M/Nc3fta/rWXNVmwBJM3k+uA6vQ17CTXmq5N5SO
35 | i5Yp2CY77dZ1V6BtihOz/8WeZIXR8P+cQYbxHh4ZyNLoqISfdmrar71/D6KvoiD7
36 | kQSIqsW8w8/ZIBrF7fe7GCA/xefV6N3PSxwbNQEFBfF+SKs6blfiWl9rmYrZKR8p
37 | Kc2GvryAR1o+QAH2dnzDsiYU//IMP4+efl4/CNH+CnX8S7WCvIlWntrHJ+4J6Rs+
38 | CAg+Qj/orgJc5QIDAQABo4GJMIGGMB0GA1UdDgQWBBQu2vjcFNPDjsGiIedr0euY
39 | Rw9UxzBXBgNVHSMEUDBOgBQu2vjcFNPDjsGiIedr0euYRw9Ux6ErpCkwJzEKMAgG
40 | A1UEAxQBKjEMMAoGA1UEChMDSkguMQswCQYDVQQGEwJVS4IJAJc1JICrjnAPMAwG
41 | A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAMs7PIfwyOS6iOaDMi8TB8Tj
42 | AYVzndQh10+kaZbmfqSwW5P/WKmj+jIgv7E5BixA3olmMqReZTpLa58AT+Afj+3D
43 | hN4WPC9AXtp3eE3/rYFMzMuV4qpKa1+J9BPvFUX7ezMkABSxpzH1aCu39wvRgLTd
44 | aDenrWOR8FrGQS6q5qfRUEA/73NZDCfjKv9hqNaxp5+5SZpD23U26jbD+iFnv52I
45 | 4iIwlMc3eCpnOx9ZvaScbtYtvinkg2/xBpOQxanasw/48vI+NnGvfp/sjzSVnPo/
46 | jlV2yR8UWuw9nMdz7oIvK2DLDfoIM76qg75CgzZ0D9rdlSwvhfr/Z3NYQRK+M48=
47 | -----END CERTIFICATE-----
48 |
--------------------------------------------------------------------------------
/templates/docker/docker-compose.dev.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | nginx:
5 | volumes:
6 | - .docker/certs:/etc/letsencrypt
7 | env_file:
8 | - ./.docker/local.env
9 |
10 | php:
11 | env_file:
12 | - .docker/local.env
13 | volumes:
14 | - .docker/composer-cache:/var/www/.docker/composer-cache
15 |
16 | db:
17 | env_file:
18 | - ./.docker/local.env
19 | volumes:
20 | - .docker/db/:/docker-entrypoint-initdb.d/
21 |
22 | {use-rabbit} rabbitmq:
23 | {use-rabbit} env_file:
24 | {use-rabbit} - ./.docker/local.env
25 |
26 | mail:
27 | container_name: {project-name}-mail
28 | image: mailhog/mailhog
29 | ports:
30 | - 1025
31 | - 8025:8025
32 |
33 | blackfire:
34 | container_name: {project-name}-blackfire
35 | image: blackfire/blackfire
36 | env_file:
37 | - .docker/local.env
38 |
--------------------------------------------------------------------------------
/templates/docker/docker-compose.prod.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | volumes:
4 | app-var:
5 |
6 | services:
7 | nginx:
8 | volumes:
9 | - /etc/letsencrypt:/etc/letsencrypt
10 | env_file:
11 | - .docker/production.env
12 |
13 | php:
14 | env_file:
15 | - .docker/production.env
16 | volumes:
17 | - app-var:/var/www/var
18 |
19 | db:
20 | env_file:
21 | - .docker/production.env
22 |
23 | {use-rabbit} rabbitmq:
24 | {use-rabbit} env_file:
25 | {use-rabbit} - ./.docker/production.env
26 |
27 | # varnish:
28 | # image: million12/varnish
29 | # ports:
30 | # - "80:80"
31 | # env_file:
32 | # - ./.docker/production.env
--------------------------------------------------------------------------------
/templates/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | volumes:
4 | db-data:
5 | app-env:
6 |
7 | services:
8 | haproxy:
9 | image: dockercloud/haproxy:latest
10 | links:
11 | - varnish
12 | environment:
13 | - CERT_FOLDER=/certs
14 | volumes:
15 | - /var/run/docker.sock:/var/run/docker.sock
16 | - .docker/certs:/certs
17 | ports:
18 | - "80:80"
19 | - "443:443"
20 |
21 | varnish:
22 | container_name: {project-name}-varnish
23 | image: wearejh/magento-varnish:latest
24 | environment:
25 | - FORCE_SSL=yes
26 | depends_on:
27 | - nginx
28 |
29 | nginx:
30 | container_name: {project-name}
31 | image: nginx:stable-alpine
32 | working_dir: /var/www
33 | volumes:
34 | - .docker/nginx/sites:/etc/nginx/conf.d
35 | volumes_from:
36 | - php
37 | depends_on:
38 | - php
39 |
40 | php:
41 | container_name: {project-name}-php
42 | image: wearejh/{project-name}
43 | build:
44 | context: .
45 | dockerfile: .docker/php/Dockerfile
46 | volumes:
47 | - app-env:/var/www/app/etc
48 | - ~/.composer/auth.json:/root/.composer/auth.json
49 | depends_on:
50 | - db
51 | ports:
52 | - 9000
53 |
54 | db:
55 | container_name: {project-name}-db
56 | image: mysql:5.6
57 | volumes:
58 | - db-data:/var/lib/mysql
59 | ports:
60 | - "3306:3306"
61 | restart: unless-stopped
62 |
63 | redis:
64 | container_name: {project-name}-redis
65 | image: redis:3-alpine
66 | ports:
67 | - "6379:6379"
68 |
69 | # elasticsearch:
70 | # container_name: {project-name}-elasticsearch
71 | # image: elasticsearch
72 | # ports:
73 | # - "9200:9200"
74 | # - "9300:9300"
75 |
76 | {use-rabbit} rabbitmq:
77 | {use-rabbit} container_name: {project-name}-rabbitmq
78 | {use-rabbit} image: rabbitmq:3.6.1-management
79 | {use-rabbit} ports:
80 | {use-rabbit} - "15672:15672"
81 | {use-rabbit} - "5672:5672"
82 |
--------------------------------------------------------------------------------
/templates/docker/env/local.env.dist:
--------------------------------------------------------------------------------
1 | # Copy to local.env and store on local machine
2 |
3 | # Host Config
4 | MAGE_ROOT_DIR=/var/www
5 | MAGE_HOST=https://mage.dev
6 | MAGE_ADMIN_USER=admin
7 | MAGE_ADMIN_PASS=password123
8 | MAGE_ADMIN_FIRSTNAME=Joe
9 | MAGE_ADMIN_LASTNAME=Bloggs
10 | MAGE_ADMIN_EMAIL=magento@wearejh.com
11 | MAGE_BACKEND_FRONTNAME=admin
12 | HTTPS=on
13 |
14 | # MySQL Details
15 | MYSQL_ROOT_PASSWORD=docker
16 | MYSQL_DATABASE=docker
17 | MYSQL_USER=docker
18 | MYSQL_PASSWORD=docker
19 |
20 | # PHP
21 | PHP_MEMORY_LIMIT=2G
22 |
23 | # RabbitMQ
24 | RABBITMQ_DEFAULT_USER=user
25 | RABBITMQ_DEFAULT_PASS=password
26 |
27 | ## Mail config
28 | MAIL_HOST=mail
29 | MAIL_PORT=1025
30 |
31 | ## Xdebug config
32 | XDEBUG_IDE_KEY=PHPSTORM
33 | XDEBUG_CONFIG=remote_host=docker.for.mac.host.internal
34 | XDEBUG_ENABLE=true
35 | PHP_IDE_CONFIG=serverName={project-domain}
36 |
37 | # Blackfire
38 | BLACKFIRE_CLIENT_ID=
39 | BLACKFIRE_CLIENT_TOKEN=
40 | BLACKFIRE_SERVER_ID=
41 | BLACKFIRE_SERVER_TOKEN=
42 |
--------------------------------------------------------------------------------
/templates/docker/env/production.env.dist:
--------------------------------------------------------------------------------
1 | # Copy to production.env and store on production machine
2 | # Never commit your production details into the repo
3 |
4 | # Host Config
5 | MAGE_HOST=https://mage.dev
6 | MAGE_ADMIN_USER=admin
7 | MAGE_ADMIN_PASS=password123
8 | MAGE_ADMIN_FIRSTNAME=Joe
9 | MAGE_ADMIN_LASTNAME=Bloggs
10 | MAGE_ADMIN_EMAIL=magento@wearejh.com
11 | MAGE_BACKEND_FRONTNAME=admin
12 | HTTPS=on
13 |
14 | # MySQL Details
15 | MYSQL_ROOT_PASSWORD=docker
16 | MYSQL_DATABASE=docker
17 | MYSQL_USER=docker
18 | MYSQL_PASSWORD=docker
19 |
20 | # PHP
21 | PHP_MEMORY_LIMIT=2G
22 |
23 | # RabbitMQ
24 | RABBITMQ_DEFAULT_USER=user
25 | RABBITMQ_DEFAULT_PASS=password
26 |
27 | ## Xdebug config
28 | XDEBUG_IDE_KEY=PHPSTORM
29 | XDEBUG_CONFIG=remote_host=10.254.254.254
30 | XDEBUG_ENABLE=false
--------------------------------------------------------------------------------
/templates/docker/php/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.0-fpm
2 | MAINTAINER Michael Woodward
3 |
4 | ARG BUILD_ENV=dev
5 | ENV PROD_ENV=prod
6 |
7 | RUN apt-get update \
8 | && apt-get install -y \
9 | cron \
10 | libfreetype6-dev \
11 | libicu-dev \
12 | libjpeg62-turbo-dev \
13 | libmcrypt-dev \
14 | libpng12-dev \
15 | libxslt1-dev \
16 | gettext \
17 | msmtp \
18 | git \
19 | vim
20 |
21 | RUN docker-php-ext-configure \
22 | gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/
23 |
24 | RUN docker-php-ext-install \
25 | gd \
26 | intl \
27 | mbstring \
28 | mcrypt \
29 | pdo_mysql \
30 | xsl \
31 | zip \
32 | soap \
33 | bcmath \
34 | mysqli \
35 | opcache \
36 | pcntl
37 |
38 | # Xdebug
39 | RUN [ "$BUILD_ENV" != "$PROD_ENV" ] && pecl install -o -f xdebug-2.5.0; true
40 |
41 | # Blackfire
42 | RUN [ "$BUILD_ENV" != "$PROD_ENV" ] \
43 | && version=$(php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;") \
44 | && curl -A "Docker" -o /tmp/blackfire-probe.tar.gz -D - -L -s https://blackfire.io/api/v1/releases/probe/php/linux/amd64/$version \
45 | && tar zxpf /tmp/blackfire-probe.tar.gz -C /tmp \
46 | && mv /tmp/blackfire-*.so $(php -r "echo ini_get('extension_dir');")/blackfire.so \
47 | && printf "extension=blackfire.so\nblackfire.agent_socket=tcp://blackfire:8707\n" > $PHP_INI_DIR/conf.d/blackfire.ini; \
48 | true
49 |
50 | # Configuration files
51 | COPY .docker/php/etc/custom.template .docker/php/etc/xdebug.template /usr/local/etc/php/conf.d/
52 | COPY .docker/php/etc/msmtprc.template /etc/msmtprc.template
53 |
54 | # Copy in Entrypoint file & Magento installation script
55 | COPY .docker/php/bin/docker-configure .docker/php/bin/magento-install .docker/php/bin/magento-configure /usr/local/bin/
56 | RUN chmod +x /usr/local/bin/docker-configure /usr/local/bin/magento-install /usr/local/bin/magento-configure
57 |
58 | # Composer
59 | RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
60 |
61 | WORKDIR /var/www
62 |
63 | RUN [ ! -d pub ] && mkdir pub
64 | RUN [ ! -d var ] && mkdir var
65 | RUN [ ! -d app/etc ] && mkdir -p app/etc
66 |
67 | COPY composer.json composer.lock auth.json ./
68 | COPY .docker/composer-cache .docker/composer-cache
69 |
70 | RUN chsh -s /bin/bash www-data \
71 | && chown -R www-data:www-data ./
72 |
73 | RUN [ "$BUILD_ENV" = "$PROD_ENV" ] \
74 | && su - www-data -c "COMPOSER_CACHE_DIR=.docker/composer-cache composer install --no-dev --no-interaction --prefer-dist -o" \
75 | || su - www-data -c "COMPOSER_CACHE_DIR=.docker/composer-cache composer install --no-interaction --prefer-dist -o"
76 |
77 | COPY app app
78 | COPY .data-migration .data-migration
79 |
80 | RUN rm -rf \
81 | html \
82 | dev \
83 | phpserver \
84 | LICENSE*.txt \
85 | COPYING.txt \
86 | .user.ini \
87 | .travis.yml \
88 | .php_cs \
89 | .htaccess* \
90 | *.sample \
91 | .phpstorm.meta.php \
92 | *.md
93 |
94 | RUN find . -user root | xargs chown www-data:www-data \
95 | && chmod +x bin/magento
96 |
97 | VOLUME ["/var/www"]
98 | ENTRYPOINT ["/usr/local/bin/docker-configure"]
99 | CMD ["php-fpm"]
100 |
--------------------------------------------------------------------------------
/templates/docker/php/bin/docker-configure:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | envsubst < "/usr/local/etc/php/conf.d/xdebug.template" > "/usr/local/etc/php/conf.d/xdebug.ini"
4 | envsubst < "/usr/local/etc/php/conf.d/custom.template" > "/usr/local/etc/php/conf.d/custom.ini"
5 | envsubst < "/etc/msmtprc.template" > "/etc/msmtprc"
6 |
7 | rm /usr/local/etc/php/conf.d/xdebug.template
8 | rm /usr/local/etc/php/conf.d/custom.template
9 | rm /etc/msmtprc.template
10 |
11 | [ "$XDEBUG_ENABLE" = "true" ] && \
12 | docker-php-ext-enable xdebug && \
13 | echo "Xdebug is enabled"
14 |
15 | exec "$@"
16 |
--------------------------------------------------------------------------------
/templates/docker/php/bin/magento-configure:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | 'redis',
11 | 'redis' =>
12 | [
13 | 'host' => 'redis',
14 | 'port' => '6379',
15 | 'password' => '',
16 | 'timeout' => '2.5',
17 | 'persistent_identifier' => '',
18 | 'database' => '0',
19 | 'compression_threshold' => '2048',
20 | 'compression_library' => 'gzip',
21 | 'log_level' => '1',
22 | 'max_concurrency' => '6',
23 | 'break_after_frontend' => '5',
24 | 'break_after_adminhtml' => '30',
25 | 'first_lifetime' => '600',
26 | 'bot_first_lifetime' => '60',
27 | 'bot_lifetime' => '7200',
28 | 'disable_locking' => '0',
29 | 'min_lifetime' => '60',
30 | 'max_lifetime' => '2592000'
31 | ]
32 | ];
33 |
34 | $config['cache'] = [
35 | 'frontend' =>
36 | [
37 | 'default' =>
38 | [
39 | 'backend' => 'Cm_Cache_Backend_Redis',
40 | 'backend_options' =>
41 | [
42 | 'server' => 'redis',
43 | 'port' => '6379',
44 | 'database' => '1'
45 | ],
46 | ]
47 | ]
48 | ];
49 |
50 | $config['MAGE_MODE'] = $isProduction ? 'production' : 'developer';
51 |
52 | $contents = <<exec($sql);
76 |
--------------------------------------------------------------------------------
/templates/docker/php/bin/magento-install:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd /var/www
4 |
5 | echo "Installing Magento..."
6 |
7 | bin/magento setup:install \
8 | --db-host=db \
9 | --db-name=$MYSQL_DATABASE \
10 | --db-user=$MYSQL_USER \
11 | --db-password=$MYSQL_PASSWORD \
12 | --base-url=$MAGE_HOST \
13 | --base-url-secure=$MAGE_HOST \
14 | --admin-firstname="$MAGE_ADMIN_FIRSTNAME" \
15 | --admin-lastname="$MAGE_ADMIN_LASTNAME" \
16 | --admin-email=$MAGE_ADMIN_EMAIL \
17 | --admin-user=$MAGE_ADMIN_USER \
18 | --admin-password=$MAGE_ADMIN_PASS \
19 | --backend-frontname=$MAGE_BACKEND_FRONTNAME \
20 | --use-secure=1 \
21 | --use-secure-admin=1 \ ##RABBIT
22 | --amqp-host=rabbitmq \
23 | --amqp-port=5672 \
24 | --amqp-user=$RABBITMQ_DEFAULT_USER \
25 | --amqp-password=$RABBITMQ_DEFAULT_PASS \
26 | --amqp-virtualhost=/ \ ##RABBIT
27 | --cleanup-database -vvv \
28 | || { exit 1; }
29 |
30 | bin/magento index:reindex && \
31 | bin/magento dev:source-theme:deploy --area="adminhtml" --theme="Magento/backend" css/styles-old css/styles && \
32 | bin/magento dev:source-theme:deploy --theme="Magento/blank" css/styles-m css/styles-l css/email css/email-inline && \
33 | bin/magento dev:source-theme:deploy && \
34 | bin/magento setup:static-content:deploy
35 |
36 | echo "Installation complete ᕦ( ̿ ̿ - ̿ ̿ )つ"
37 |
--------------------------------------------------------------------------------
/templates/docker/php/etc/custom.template:
--------------------------------------------------------------------------------
1 | memory_limit = ${PHP_MEMORY_LIMIT};
2 | session.auto_start=off;
3 | suhosin.session.cryptua=off;
4 | sendmail_path=/usr/bin/msmtp -t;
5 | opcache.enable=1
6 | opcache.enable_cli=1
7 | opcache.save_comments=1
8 | opcache.max_accelerated_files=65406
9 | opcache.memory_consumption=256
10 | upload_max_filesize = 20M
11 | post_max_size = 20M
--------------------------------------------------------------------------------
/templates/docker/php/etc/msmtprc.template:
--------------------------------------------------------------------------------
1 | account default
2 | host ${MAIL_HOST}
3 | port ${MAIL_PORT}
4 | auto_from on
--------------------------------------------------------------------------------
/templates/docker/php/etc/xdebug.template:
--------------------------------------------------------------------------------
1 | xdebug.remote_enable = 1
2 | xdebug.remote_port = 9000
3 | xdebug.scream = 0
4 | xdebug.show_local_vars = 1
5 | xdebug.idekey = ${XDEBUG_IDE_KEY}
6 | xdebug.var_display_max_data = -1
7 |
--------------------------------------------------------------------------------
/templates/git/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | vendor
4 | .docker/*.env
5 | .docker/composer-cache/*
6 |
7 | /.idea
8 | /.gitattributes
9 | /app/design/*/Magento
10 | /app/etc
11 | /app/i18n/magento
12 | /app/*.*
13 |
14 | /bin
15 | /update
16 | /dev
17 | /lib
18 | /pub
19 | /setup
20 | /var
21 | /phpserver
22 | /generated
23 |
24 | /*.*
25 |
26 | !/.data-migration
27 | !/.dockerignore
28 | !/docker-compose.*
29 | !/.docker/
30 | !/composer.json
31 | !/composer.lock
32 | !/README.md
33 | !/.gitignore
34 | !/circle.yml
35 | !/auth.json
36 | !*/**/.gitkeep
37 | !/.phpstorm.meta.php
38 | !PULL_REQUEST_TEMPLATE.md
39 |
--------------------------------------------------------------------------------
/templates/phpstorm/.phpstorm.meta.php:
--------------------------------------------------------------------------------
1 | [
6 | "" == "@",
7 | ],
8 | \Magento\Framework\ObjectManagerInterface::get('') => [
9 | "" == "@",
10 | ],
11 | ];
12 | }
--------------------------------------------------------------------------------
/templates/pr/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | - **Ready for Merge?** : Yes/No
2 | - **Ticket** : [#244](https://app.activecollab.com/ticket/path)
3 |
4 | ### PR Dependencies
5 |
6 | - List of other PRs that need to be merged prior to this
7 | - Omit section if no dependants
8 |
9 |
10 | ### Feature/Issue Description
11 |
12 | This description should be a small TLDR of the ticket, the reviewer may wish to view the ticket if they need more details other than this.
13 |
14 | ### Code Explanation
15 |
16 | This section should provide details on why you have made the changes and if you feel necessary a justifcation of your approach.
17 |
18 | ### Issues / Caveats
19 |
20 | Use this to warn of any issues you think may arise from this, caveats etc and any issues you came across during the implementation that may help the reviewer better understand or help in some way.
21 |
22 | ### TODOs
23 |
24 | - [x] Checklist of all todos
25 | - [ ] Use if still in WIP
26 | - [ ] Anything with a TODO list should not be merged!
27 |
28 | ---
29 |
30 | 
--------------------------------------------------------------------------------
/templates/readme/README.md:
--------------------------------------------------------------------------------
1 | {project-namespace}
2 |
3 |
4 |
5 | Some Badges would be nice.
6 |
7 |
8 |
9 | ## Initial Setup
10 |
11 | Ensure you have [Docker ](https://docs.docker.com/docker-for-mac/) and the [Workflow tool](https://github.com/WeareJH/workflow) installed and it's pre-requisites.
12 |
13 | If you want to supply a database seed, put the SQL file in `.docker/db` before starting.
14 |
15 | ```sh
16 | $ git clone {project-reop} && cd {project-name}
17 | $ cp .docker/local.env.dist .docker/local.env
18 | $ workflow start
19 | ```
20 |
21 | Your recommended to run two terminal windows as the start command will start a file watcher copying in files to the container on change which you should keep running.
22 |
23 | In a new terminal you can now finish off your setup dependant on your installation type.
24 |
25 | ### Supplied Database Seed
26 |
27 | If you supplied a database seed you simply need to run the configure command.
28 |
29 | ```sh
30 | $ workflow mc
31 | ```
32 |
33 | ### Fresh Magento Database
34 |
35 | If you want a fresh database and haven't supplied a database seed you need to run a full install.
36 |
37 | ```sh
38 | $ workflow mfi
39 | ```
40 |
41 | After the initial setup you will be able to use the `workflow` too to start, stop development as and when you wish, dynamic data will stay persistant until you remove it through docker.
42 |
43 | *Note: You can configure the `.docker/local.env` file to meet your requirements but likely not required*
44 |
45 | ## Mailhog
46 |
47 | Mailhog is accessible on `http://{project-domain}:8025/`
48 |
49 | ## Tests
50 |
51 | ```bash
52 | composer test
53 | ```
--------------------------------------------------------------------------------
/test/ApplicationTest.php:
--------------------------------------------------------------------------------
1 | setAutoExit(false);
24 | $app->add(new class extends Command {
25 | protected function configure()
26 | {
27 | $this->setName('some-command');
28 | }
29 | });
30 |
31 | $fallback = new Magento(new CommandLine(new StreamSelectLoop, new NullLogger, new ConsoleOutput));
32 | $fallback->setCode(function (InputInterface $input) {
33 | static::assertTrue($input->hasArgument('cmd'));
34 | static::assertEquals($input->getArgument('cmd'), 'some-command-that-does-not-exist');
35 | });
36 |
37 | $app->add($fallback);
38 |
39 | $_SERVER['argv'] = ['workflow', 'some-command-that-does-not-exist', 'arg1'];
40 |
41 | $exitCode = $app->run();
42 |
43 | static::assertEquals(0, $exitCode);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/Command/AbstractTestCommand.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | class AbstractTestCommand extends TestCase
21 | {
22 | /**
23 | * @var Prophet
24 | */
25 | protected $prophet;
26 |
27 | /**
28 | * @var ObjectProphecy|InputInterface
29 | */
30 | protected $input;
31 |
32 | /**
33 | * @var ObjectProphecy|OutputInterface
34 | */
35 | protected $output;
36 |
37 | /**
38 | * @var ObjectProphecy|CommandLine
39 | */
40 | protected $commandLine;
41 |
42 | public function setUp()
43 | {
44 | $this->prophet = new Prophet();
45 |
46 | $this->input = $this->prophesize(ArgvInput::class);
47 | $this->output = $this->prophesize(Output::class);
48 |
49 | $this->commandLine = $this->prophesize(CommandLine::class);
50 | }
51 |
52 | protected function useInvalidEnvironment()
53 | {
54 | chdir(__DIR__ . '/../fixtures/invalid-env');
55 | }
56 |
57 | protected function useValidEnvironment()
58 | {
59 | chdir(__DIR__ . '/../fixtures/valid-env');
60 | }
61 |
62 | protected function useBrokenEnvironment()
63 | {
64 | chdir(__DIR__ . '/../fixtures/broken-env');
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/test/Command/DatabaseDumpTest.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class DatabaseDumpTest extends AbstractTestCommand
12 | {
13 | /**
14 | * @var DatabaseDump
15 | */
16 | private $command;
17 |
18 | public function setUp()
19 | {
20 | parent::setUp();
21 | $this->command = new DatabaseDump($this->commandLine->reveal());
22 | }
23 |
24 | public function tearDown()
25 | {
26 | $this->prophet->checkPredictions();
27 | }
28 |
29 | public function testCommandIsConfigured()
30 | {
31 | static::assertEquals('db-dump', $this->command->getName());
32 | static::assertEquals([], $this->command->getAliases());
33 | static::assertEquals('Dump the database to the host', $this->command->getDescription());
34 | }
35 |
36 | public function testHasDatabaseOptionAndValueIsRequired()
37 | {
38 | $definition = $this->command->getDefinition();
39 |
40 | static::assertTrue($definition->hasOption('database'));
41 | static::assertTrue($definition->getOption('database')->isValueRequired());
42 | }
43 |
44 | public function testDump()
45 | {
46 | $this->useValidEnvironment();
47 |
48 | $this->input->getOption('database')->willReturn(null);
49 |
50 | $this->commandLine->runQuietly('docker exec -i m2-db mysqldump -uroot -pdocker docker > dump.sql')
51 | ->shouldBeCalled();
52 |
53 | $this->output->writeln('Database dump saved to ./dump.sql')->shouldBeCalled();
54 |
55 | $this->command->execute($this->input->reveal(), $this->output->reveal());
56 | }
57 |
58 | public function testDumpWithCustomDatabase()
59 | {
60 | $this->useValidEnvironment();
61 |
62 | $this->input->getOption('database')->willReturn('custom_db');
63 |
64 | $this->commandLine->runQuietly('docker exec -i m2-db mysqldump -uroot -pdocker custom_db > dump.sql')
65 | ->shouldBeCalled();
66 | $this->output->writeln('Database dump saved to ./dump.sql')->shouldBeCalled();
67 |
68 | $this->command->execute($this->input->reveal(), $this->output->reveal());
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/test/Command/DeleteTest.php:
--------------------------------------------------------------------------------
1 | files = $this->prophesize(Files::class);
25 | $this->command = new Delete($this->files->reveal());
26 | }
27 |
28 | public function tearDown()
29 | {
30 | $this->prophet->checkPredictions();
31 | }
32 |
33 | public function testCommandIsConfigured()
34 | {
35 | static::assertEquals('delete', $this->command->getName());
36 | static::assertEquals([], $this->command->getAliases());
37 | static::assertEquals('Delete files from the container', $this->command->getDescription());
38 | static::assertArrayHasKey('files', $this->command->getDefinition()->getArguments());
39 | }
40 |
41 | public function testFilesArgumentIsRequiredAndArray()
42 | {
43 | $args = $this->command->getDefinition()->getArguments();
44 | /** @var InputArgument $fileArg */
45 | $fileArg = array_shift($args);
46 |
47 | static::assertTrue($fileArg->isRequired());
48 | static::assertTrue($fileArg->isArray());
49 | }
50 |
51 | public function testDeleteCommandRelativePath()
52 | {
53 | $this->useValidEnvironment();
54 |
55 | $this->input->getArgument('files')->shouldBeCalled()->willReturn(['some-file.txt']);
56 |
57 | $this->files->delete('m2-php', ['some-file.txt'])->shouldBeCalled();
58 |
59 | $this->command->execute($this->input->reveal(), $this->output->reveal());
60 | }
61 |
62 | public function testDeleteCommandAbsolutePath()
63 | {
64 | $this->useValidEnvironment();
65 |
66 | $filePath = realpath('some-file.txt');
67 | $this->input->getArgument('files')->shouldBeCalled()->willReturn([$filePath]);
68 |
69 | $this->files->delete('m2-php', [$filePath])->shouldBeCalled();
70 |
71 | $this->command->execute($this->input->reveal(), $this->output->reveal());
72 | }
73 |
74 | public function testExceptionThrownIfContainerNameNotFound()
75 | {
76 | $this->useInvalidEnvironment();
77 | $this->expectException(\RuntimeException::class);
78 |
79 | $this->command->execute($this->input->reveal(), $this->output->reveal());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/test/Command/DockerAwareTraitTest.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class DockerAwareTraitTest extends AbstractTestCommand
11 | {
12 | private $implementation;
13 |
14 | public function setUp()
15 | {
16 | parent::setUp();
17 |
18 | $this->implementation = new class()
19 | {
20 | use DockerAwareTrait;
21 |
22 | public function getDevEnvironmentVarsTest()
23 | {
24 | return $this->getDevEnvironmentVars();
25 | }
26 |
27 | public function containerNameTest(string $name)
28 | {
29 | $this->getContainerName($name);
30 | }
31 | };
32 | }
33 |
34 | public function testExceptionIsThrownIfServiceDoesntExist()
35 | {
36 | $this->useValidEnvironment();
37 | $this->expectException(\RuntimeException::class);
38 | $this->implementation->containerNameTest('non-existant-service');
39 | }
40 |
41 | public function testExceptionIsThrownWhenComposeFileUnParsable()
42 | {
43 | $this->useBrokenEnvironment();
44 | $this->expectException(\RuntimeException::class);
45 | $this->implementation->containerNameTest('php');
46 | }
47 |
48 | public function testExceptionIsThrownIfLocalEnvFileDoesntExist()
49 | {
50 | $this->useInvalidEnvironment();
51 | $this->expectException(\RuntimeException::class);
52 | $this->implementation->getDevEnvironmentVarsTest();
53 | }
54 |
55 | public function testExceptionIsThrownIfLDockerFileDoesntExist()
56 | {
57 | chdir(__DIR__ . '/../fixtures/missing-docker-files');
58 | $this->expectException(\RuntimeException::class);
59 |
60 | $message = 'Could not locate docker-compose.yml file. Are you in the right directory?';
61 |
62 | $this->expectExceptionMessage($message);
63 | $this->implementation->containerNameTest('php');
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/test/Command/MagentoCompileTest.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class MagentoCompileTest extends AbstractTestCommand
17 | {
18 | /**
19 | * @var ComposerUpdate
20 | */
21 | private $command;
22 |
23 | /**
24 | * @var ObjectProphecy|Application
25 | */
26 | private $application;
27 |
28 | /**
29 | * @var ObjectProphecy|Pull
30 | */
31 | private $pullCommand;
32 |
33 |
34 | public function setUp()
35 | {
36 | parent::setUp();
37 |
38 | $this->command = new MagentoCompile($this->commandLine->reveal());
39 | $this->application = $this->prophesize(Application::class);
40 | $this->pullCommand = $this->prophesize(Pull::class);
41 |
42 | $this->application->getHelperSet()->willReturn(new HelperSet);
43 | $this->application->find('pull')->willReturn($this->pullCommand->reveal());
44 |
45 | $this->command->setApplication($this->application->reveal());
46 | }
47 |
48 | public function tearDown()
49 | {
50 | $this->prophet->checkPredictions();
51 | }
52 |
53 | public function testCommandIsConfigured()
54 | {
55 | $description = 'Runs the magento DI compile command and pulls back required files to the host';
56 |
57 | static::assertEquals('magento-compile', $this->command->getName());
58 | static::assertEquals($description, $this->command->getDescription());
59 | }
60 |
61 | public function testComposerInstallCommand()
62 | {
63 | $this->useValidEnvironment();
64 |
65 | $this->commandLine->run('docker exec -u www-data m2-php bin/magento setup:di:compile --ansi')->shouldBeCalled();
66 |
67 | $expectedInput = new ArrayInput(['files' => ['var/di', 'var/generation']]);
68 | $this->pullCommand->run($expectedInput, $this->output)->shouldBeCalled();
69 |
70 | $this->command->execute($this->input->reveal(), $this->output->reveal());
71 | }
72 |
73 | public function testExceptionThrownIfContainerNameNotFound()
74 | {
75 | $this->useInvalidEnvironment();
76 | $this->expectException(\RuntimeException::class);
77 |
78 | $this->command->execute($this->input->reveal(), $this->output->reveal());
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/test/Command/MagentoFullInstallTest.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class MagentoFullInstallTest extends AbstractTestCommand
16 | {
17 | /**
18 | * @var MagentoFullInstall
19 | */
20 | private $command;
21 |
22 | /**
23 | * @var ObjectProphecy|Application
24 | */
25 | private $application;
26 |
27 | /**
28 | * @var ObjectProphecy|MagentoInstall
29 | */
30 | private $installCommand;
31 |
32 | /**
33 | * @var ObjectProphecy|MagentoConfigure
34 | */
35 | private $configureCommand;
36 |
37 |
38 | public function setUp()
39 | {
40 | parent::setUp();
41 |
42 | $this->command = new MagentoFullInstall();
43 | $this->application = $this->prophesize(Application::class);
44 | $this->installCommand = $this->prophesize(MagentoInstall::class);
45 | $this->configureCommand = $this->prophesize(MagentoConfigure::class);
46 |
47 | $this->application->getHelperSet()->willReturn(new HelperSet);
48 | $this->application->find('magento-install')->willReturn($this->installCommand->reveal());
49 | $this->application->find('magento-configure')->willReturn($this->configureCommand->reveal());
50 |
51 | $this->command->setApplication($this->application->reveal());
52 | }
53 |
54 | public function tearDown()
55 | {
56 | $this->prophet->checkPredictions();
57 | }
58 |
59 | public function testCommandIsConfigured()
60 | {
61 | static::assertEquals('magento-full-install', $this->command->getName());
62 | static::assertEquals(['mfi'], $this->command->getAliases());
63 | static::assertEquals('Runs magento-install and magento-configure commands', $this->command->getDescription());
64 | static::assertArrayHasKey('prod', $this->command->getDefinition()->getOptions());
65 | }
66 |
67 | public function testCommandRunsBothSubCommands()
68 | {
69 | $this->application->find('magento-install')->shouldBeCalled();
70 | $this->application->find('magento-configure')->shouldBeCalled();
71 |
72 | $this->installCommand->run($this->input, $this->output)->shouldBeCalled();
73 | $this->configureCommand->run($this->input, $this->output)->shouldBeCalled();
74 |
75 | $this->command->execute($this->input->reveal(), $this->output->reveal());
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/test/Command/MagentoInstallTest.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class MagentoInstallTest extends AbstractTestCommand
16 | {
17 | /**
18 | * @var MagentoInstall
19 | */
20 | private $command;
21 |
22 | /**
23 | * @var ObjectProphecy|Application
24 | */
25 | private $application;
26 |
27 | /**
28 | * @var ObjectProphecy|Pull
29 | */
30 | private $pullCommand;
31 |
32 |
33 | public function setUp()
34 | {
35 | parent::setUp();
36 |
37 | $this->command = new MagentoInstall($this->commandLine->reveal());
38 | $this->application = $this->prophesize(Application::class);
39 | $this->pullCommand = $this->prophesize(Pull::class);
40 |
41 | $this->application->getHelperSet()->willReturn(new HelperSet);
42 | $this->application->find('pull')->willReturn($this->pullCommand->reveal());
43 |
44 | $this->command->setApplication($this->application->reveal());
45 | }
46 |
47 | public function tearDown()
48 | {
49 | $this->prophet->checkPredictions();
50 | }
51 |
52 | public function testCommandIsConfigured()
53 | {
54 | static::assertEquals('magento-install', $this->command->getName());
55 | static::assertEquals(['mi'], $this->command->getAliases());
56 | static::assertEquals('Runs the magento install script', $this->command->getDescription());
57 | }
58 |
59 | public function testMagentoInstallCommand()
60 | {
61 | $this->useValidEnvironment();
62 |
63 | $this->commandLine->run('docker exec -u www-data m2-php magento-install')->shouldBeCalled();
64 |
65 | $expectedInput = new ArrayInput(['files' => ['app/etc']]);
66 | $this->pullCommand->run($expectedInput, $this->output)->shouldBeCalled();
67 | $this->output->writeln('Install complete!')->shouldBeCalled();
68 |
69 | $this->command->execute($this->input->reveal(), $this->output->reveal());
70 | }
71 |
72 | public function testExceptionThrownIfContainerNameNotFound()
73 | {
74 | $this->useInvalidEnvironment();
75 | $this->expectException(\RuntimeException::class);
76 |
77 | $this->command->execute($this->input->reveal(), $this->output->reveal());
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/test/Command/MagentoModuleDisableTest.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class MagentoModuleDisableTest extends AbstractTestCommand
16 | {
17 | /**
18 | * @var ComposerUpdate
19 | */
20 | private $command;
21 |
22 | /**
23 | * @var ObjectProphecy|Application
24 | */
25 | private $application;
26 |
27 | /**
28 | * @var ObjectProphecy|Pull
29 | */
30 | private $pullCommand;
31 |
32 |
33 | public function setUp()
34 | {
35 | parent::setUp();
36 |
37 | $this->command = new MagentoModuleDisable($this->commandLine->reveal());
38 | $this->application = $this->prophesize(Application::class);
39 | $this->pullCommand = $this->prophesize(Pull::class);
40 |
41 | $this->application->getHelperSet()->willReturn(new HelperSet);
42 | $this->application->find('pull')->willReturn($this->pullCommand->reveal());
43 |
44 | $this->command->setApplication($this->application->reveal());
45 | }
46 |
47 | public function tearDown()
48 | {
49 | $this->prophet->checkPredictions();
50 | }
51 |
52 | public function testCommandIsConfigured()
53 | {
54 | $description = 'Disable Magento module and updates the config.php file';
55 |
56 | static::assertEquals('module:disable', $this->command->getName());
57 | static::assertEquals($description, $this->command->getDescription());
58 | }
59 |
60 | public function testModuleEnableCommand()
61 | {
62 | $this->useValidEnvironment();
63 | $this->input->getArgument('module')->shouldBeCalled()->willReturn('Jh_Brands');
64 |
65 | $cmd = 'docker exec -u www-data m2-php bin/magento module:disable Jh_Brands --ansi';
66 | $this->commandLine->run($cmd);
67 |
68 | $expectedInput = new ArrayInput(['files' => ['app/etc/config.php']]);
69 | $this->pullCommand->run($expectedInput, $this->output)->shouldBeCalled();
70 |
71 | $this->command->execute($this->input->reveal(), $this->output->reveal());
72 | }
73 |
74 | public function testExceptionThrownIfContainerNameNotFound()
75 | {
76 | $this->useInvalidEnvironment();
77 | $this->expectException(\RuntimeException::class);
78 |
79 | $this->command->execute($this->input->reveal(), $this->output->reveal());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/test/Command/MagentoModuleEnableTest.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class MagentoModuleEnableTest extends AbstractTestCommand
16 | {
17 | /**
18 | * @var ComposerUpdate
19 | */
20 | private $command;
21 |
22 | /**
23 | * @var ObjectProphecy|Application
24 | */
25 | private $application;
26 |
27 | /**
28 | * @var ObjectProphecy|Pull
29 | */
30 | private $pullCommand;
31 |
32 |
33 | public function setUp()
34 | {
35 | parent::setUp();
36 |
37 | $this->command = new MagentoModuleEnable($this->commandLine->reveal());
38 | $this->application = $this->prophesize(Application::class);
39 | $this->pullCommand = $this->prophesize(Pull::class);
40 |
41 | $this->application->getHelperSet()->willReturn(new HelperSet);
42 | $this->application->find('pull')->willReturn($this->pullCommand->reveal());
43 |
44 | $this->command->setApplication($this->application->reveal());
45 | }
46 |
47 | public function tearDown()
48 | {
49 | $this->prophet->checkPredictions();
50 | }
51 |
52 | public function testCommandIsConfigured()
53 | {
54 | $description = 'Enable Magento module and updates the config.php file';
55 |
56 | static::assertEquals('module:enable', $this->command->getName());
57 | static::assertEquals($description, $this->command->getDescription());
58 | }
59 |
60 | public function testModuleEnableCommand()
61 | {
62 | $this->useValidEnvironment();
63 | $this->input->getArgument('module')->shouldBeCalled()->willReturn('Jh_Brands');
64 |
65 | $cmd = 'docker exec -u www-data m2-php bin/magento module:enable Jh_Brands --ansi';
66 | $this->commandLine->run($cmd)->shouldBeCalled();
67 |
68 | $expectedInput = new ArrayInput(['files' => ['app/etc/config.php']]);
69 | $this->pullCommand->run($expectedInput, $this->output)->shouldBeCalled();
70 |
71 | $this->command->execute($this->input->reveal(), $this->output->reveal());
72 | }
73 |
74 | public function testExceptionThrownIfContainerNameNotFound()
75 | {
76 | $this->useInvalidEnvironment();
77 | $this->expectException(\RuntimeException::class);
78 |
79 | $this->command->execute($this->input->reveal(), $this->output->reveal());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/test/Command/MagentoTest.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class MagentoTest extends AbstractTestCommand
11 | {
12 | /**
13 | * @var Magento
14 | */
15 | private $command;
16 |
17 | public function setUp()
18 | {
19 | parent::setUp();
20 | $this->command = new Magento($this->commandLine->reveal());
21 | }
22 |
23 | public function tearDown()
24 | {
25 | $this->prophet->checkPredictions();
26 | }
27 |
28 | public function testCommandIsConfigured()
29 | {
30 | $description = 'Works as a proxy to the Magento bin inside the container';
31 |
32 | static::assertEquals('magento', $this->command->getName());
33 | static::assertEquals(['mage', 'm'], $this->command->getAliases());
34 | static::assertEquals($description, $this->command->getDescription());
35 | static::assertArrayHasKey('cmd', $this->command->getDefinition()->getArguments());
36 | }
37 |
38 | /**
39 | * @dataProvider magentoCommandProvider
40 | */
41 | public function testCommandWorksAsAProxy($args)
42 | {
43 | $this->useValidEnvironment();
44 |
45 | // We have to use $_SERVER['argv'] here
46 | $_SERVER['argv'] = array_merge(['workflow', 'magento'], $args);
47 |
48 | $this->commandLine
49 | ->run(sprintf('docker exec -u www-data m2-php bin/magento --ansi %s', implode(' ', $args)))
50 | ->shouldBeCalled();
51 |
52 | $this->command->execute($this->input->reveal(), $this->output->reveal());
53 | }
54 |
55 | public function magentoCommandProvider() : array
56 | {
57 | return [
58 | [['cache:flush', 'config']],
59 | [['setup:static-content:deploy', '--theme="Luma/default"']],
60 | [['module:status']],
61 | [['module:disable', 'Magento_Weee']]
62 | ];
63 | }
64 |
65 | public function testCanRunCommandWithoutArgs()
66 | {
67 | $this->useValidEnvironment();
68 |
69 | // We have to use $_SERVER['argv'] here
70 | $_SERVER['argv'] = ['workflow', 'magento'];
71 |
72 | $this->commandLine->run('docker exec -u www-data m2-php bin/magento --ansi')->shouldBeCalled();
73 |
74 | $this->command->execute($this->input->reveal(), $this->output->reveal());
75 | }
76 |
77 | public function testExceptionThrownIfContainerNameNotFound()
78 | {
79 | $this->useInvalidEnvironment();
80 | $this->expectException(\RuntimeException::class);
81 |
82 | $this->command->execute($this->input->reveal(), $this->output->reveal());
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/test/Command/NginxReloadTest.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class NginxReloadTest extends AbstractTestCommand
11 | {
12 | /**
13 | * @var NginxReload
14 | */
15 | private $command;
16 |
17 | public function setUp()
18 | {
19 | parent::setUp();
20 | $this->command = new NginxReload($this->commandLine->reveal());
21 | }
22 |
23 | public function tearDown()
24 | {
25 | $this->prophet->checkPredictions();
26 | }
27 |
28 | public function testCommandIsConfigured()
29 | {
30 | static::assertEquals('nginx-reload', $this->command->getName());
31 | static::assertEquals(['nginx'], $this->command->getAliases());
32 | static::assertEquals('Sends reload signal to NGINX in the container', $this->command->getDescription());
33 | }
34 |
35 | public function testNginxReloadCommand()
36 | {
37 | $this->useValidEnvironment();
38 |
39 | $this->commandLine->run('docker exec m2 nginx -s "reload"')->shouldBeCalled();
40 | $this->output->writeln('Reload signal sent')->shouldBeCalled();
41 |
42 | $this->command->execute($this->input->reveal(), $this->output->reveal());
43 | }
44 |
45 | public function testExceptionThrownIfContainerNameNotFound()
46 | {
47 | $this->useInvalidEnvironment();
48 | $this->expectException(\RuntimeException::class);
49 |
50 | $this->command->execute($this->input->reveal(), $this->output->reveal());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/test/Command/PhpTest.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class PhpTest extends AbstractTestCommand
11 | {
12 | /**
13 | * @var Ssh
14 | */
15 | private $command;
16 |
17 | public function setUp()
18 | {
19 | parent::setUp();
20 | $this->command = new Php($this->commandLine->reveal());
21 | }
22 |
23 | public function tearDown()
24 | {
25 | $this->prophet->checkPredictions();
26 | }
27 |
28 | public function testCommandIsConfigured()
29 | {
30 | static::assertEquals('php', $this->command->getName());
31 | static::assertEmpty($this->command->getAliases());
32 | static::assertEquals('Run a php script on the app container', $this->command->getDescription());
33 | static::assertArrayHasKey('php-file', $this->command->getDefinition()->getArguments());
34 | }
35 |
36 | public function testPhpCommand()
37 | {
38 | $this->useValidEnvironment();
39 | $this->input->getArgument('php-file')->willReturn('my-file.php');
40 |
41 | $this->commandLine->runInteractively('docker exec -it -u www-data m2-php php my-file.php')->shouldBeCalled();
42 |
43 | $this->command->execute($this->input->reveal(), $this->output->reveal());
44 | }
45 |
46 | public function testExceptionThrownIfComposeFileMissingImageTag()
47 | {
48 | $this->useInvalidEnvironment();
49 | $this->expectException(\RuntimeException::class);
50 |
51 | $this->command->execute($this->input->reveal(), $this->output->reveal());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/test/Command/RestartTest.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class RestartTest extends AbstractTestCommand
17 | {
18 | /**
19 | * @var Restart
20 | */
21 | private $command;
22 |
23 | /**
24 | * @var ObjectProphecy|Application
25 | */
26 | private $application;
27 |
28 | /**
29 | * @var ObjectProphecy|Up
30 | */
31 | private $upCommand;
32 |
33 | /**
34 | * @var ObjectProphecy|Stop
35 | */
36 | private $stopCommand;
37 |
38 |
39 | public function setUp()
40 | {
41 | parent::setUp();
42 |
43 | $this->command = new Restart();
44 | $this->application = $this->prophesize(Application::class);
45 | $this->upCommand = $this->prophesize(Up::class);
46 | $this->stopCommand = $this->prophesize(Stop::class);
47 |
48 | $this->application->getHelperSet()->willReturn(new HelperSet);
49 | $this->application->find('up')->willReturn($this->upCommand->reveal());
50 | $this->application->find('stop')->willReturn($this->stopCommand->reveal());
51 |
52 | $this->command->setApplication($this->application->reveal());
53 | }
54 |
55 | public function tearDown()
56 | {
57 | $this->prophet->checkPredictions();
58 | }
59 |
60 | public function testCommandIsConfigured()
61 | {
62 | static::assertEquals('restart', $this->command->getName());
63 | static::assertEquals([], $this->command->getAliases());
64 | static::assertEquals('Restarts the containers', $this->command->getDescription());
65 | static::assertArrayHasKey('prod', $this->command->getDefinition()->getOptions());
66 | }
67 |
68 | public function testCommandRunsAllSubCommands()
69 | {
70 | $this->application->find('stop')->shouldBeCalled();
71 | $this->application->find('up')->shouldBeCalled();
72 |
73 | $this->stopCommand->run($this->input, $this->output)->shouldBeCalled();
74 | $this->upCommand->run($this->input, $this->output)->shouldBeCalled();
75 |
76 | $this->command->execute($this->input->reveal(), $this->output->reveal());
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/test/Command/StopTest.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class StopTest extends AbstractTestCommand
11 | {
12 | /**
13 | * @var Stop
14 | */
15 | private $command;
16 |
17 | public function setUp()
18 | {
19 | parent::setUp();
20 | $this->command = new Stop($this->commandLine->reveal());
21 | }
22 |
23 | public function tearDown()
24 | {
25 | $this->prophet->checkPredictions();
26 | }
27 |
28 | public function testCommandIsConfigured()
29 | {
30 | static::assertEquals('stop', $this->command->getName());
31 | static::assertEquals([], $this->command->getAliases());
32 | static::assertEquals('Stops the containers running', $this->command->getDescription());
33 | static::assertArrayHasKey('prod', $this->command->getDefinition()->getOptions());
34 | }
35 |
36 | public function testStopsDevelopmentMode()
37 | {
38 | $this->useValidEnvironment();
39 |
40 | $this->commandLine
41 | ->run('docker-compose -f docker-compose.yml -f docker-compose.dev.yml stop')
42 | ->shouldBeCalled();
43 |
44 | $this->output->writeln('Containers stopped')->shouldBeCalled();
45 |
46 | $this->command->execute($this->input->reveal(), $this->output->reveal());
47 | }
48 |
49 | public function testStopsProductionMode()
50 | {
51 | $this->useValidEnvironment();
52 |
53 | $this->input->getOption('prod')->willReturn(true);
54 |
55 | $this->commandLine
56 | ->run('docker-compose -f docker-compose.yml -f docker-compose.prod.yml stop')
57 | ->shouldBeCalled();
58 |
59 | $this->output->writeln('Containers stopped')->shouldBeCalled();
60 |
61 | $this->command->execute($this->input->reveal(), $this->output->reveal());
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/test/Command/SyncTest.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class SyncTest extends AbstractTestCommand
19 | {
20 | /**
21 | * @var Sync
22 | */
23 | private $command;
24 |
25 | /**
26 | * @var ObjectProphecy|Push
27 | */
28 | private $pushCommand;
29 |
30 | /**
31 | * @var ObjectProphecy|Application
32 | */
33 | private $application;
34 |
35 | public function setUp()
36 | {
37 | parent::setUp();
38 | $this->command = new Sync($this->commandLine->reveal());
39 | $this->application = $this->prophesize(Application::class);
40 | $this->pushCommand = $this->prophesize(Push::class);
41 |
42 | $this->application->getHelperSet()->willReturn(new HelperSet);
43 | $this->application->find('push')->willReturn($this->pushCommand->reveal());
44 |
45 | $this->command->setApplication($this->application->reveal());
46 | }
47 |
48 | public function tearDown()
49 | {
50 | $this->prophet->checkPredictions();
51 | }
52 |
53 | public function testCommandIsConfigured()
54 | {
55 | $description = 'Syncs changes from the host filesystem to the relevant docker containers';
56 |
57 | static::assertEquals('sync', $this->command->getName());
58 | static::assertEquals([], $this->command->getAliases());
59 | static::assertEquals($description, $this->command->getDescription());
60 | }
61 |
62 | public function testSyncWillAddAFileWhenItExists()
63 | {
64 | $this->useValidEnvironment();
65 |
66 | $this->input->getArgument('file')->willReturn('some-file.txt');
67 |
68 | $expectedInput = new ArrayInput(['files' => ['some-file.txt']]);
69 | $this->pushCommand->run($expectedInput, $this->output)->shouldBeCalled();
70 |
71 | $this->command->execute($this->input->reveal(), $this->output->reveal());
72 | }
73 |
74 | public function testSyncWillDeleteAFileWhenItDoesntExist()
75 | {
76 | $this->useValidEnvironment();
77 |
78 | $this->input->getArgument('file')->willReturn('some-deleted-file.txt');
79 |
80 | $this->commandLine->run('docker exec m2-php rm -rf /var/www/some-deleted-file.txt')->shouldBeCalled();
81 | $this->output->writeln(' x some-deleted-file.txt > m2-php ')->shouldBeCalled();
82 |
83 | $this->command->execute($this->input->reveal(), $this->output->reveal());
84 | }
85 |
86 | public function testExceptionThrownIfComposeFileMissingImageTag()
87 | {
88 | $this->useInvalidEnvironment();
89 | $this->expectException(\RuntimeException::class);
90 |
91 | $this->command->execute($this->input->reveal(), $this->output->reveal());
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/test/Command/VarnishDisableTest.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class VarnishDisableTest extends AbstractTestCommand
12 | {
13 | /**
14 | * @var NginxReload
15 | */
16 | private $command;
17 |
18 | public function setUp()
19 | {
20 | parent::setUp();
21 | $this->command = new VarnishDisable($this->commandLine->reveal());
22 | }
23 |
24 | public function tearDown()
25 | {
26 | $this->prophet->checkPredictions();
27 | }
28 |
29 | public function testCommandIsConfigured()
30 | {
31 | static::assertEquals('varnish-disable', $this->command->getName());
32 | static::assertEquals(['vd'], $this->command->getAliases());
33 | static::assertEquals('Switches the VCL to be a proxy', $this->command->getDescription());
34 | }
35 |
36 | public function testVarnishEnableCommand()
37 | {
38 | $this->useValidEnvironment();
39 |
40 | $this->commandLine->run('docker-compose exec -T varnish varnishadm vcl.use boot')->shouldBeCalled();
41 | $this->output->writeln('Varnish caching disabled')->shouldBeCalled();
42 |
43 | $this->command->execute($this->input->reveal(), $this->output->reveal());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/Command/VarnishEnableTest.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class VarnishEnableTest extends AbstractTestCommand
12 | {
13 | /**
14 | * @var NginxReload
15 | */
16 | private $command;
17 |
18 | public function setUp()
19 | {
20 | parent::setUp();
21 | $this->command = new VarnishEnable($this->commandLine->reveal());
22 | }
23 |
24 | public function tearDown()
25 | {
26 | $this->prophet->checkPredictions();
27 | }
28 |
29 | public function testCommandIsConfigured()
30 | {
31 | static::assertEquals('varnish-enable', $this->command->getName());
32 | static::assertEquals(['ve'], $this->command->getAliases());
33 | static::assertEquals('Switches the VCL to use caching', $this->command->getDescription());
34 | }
35 |
36 | public function testVarnishEnableCommand()
37 | {
38 | $this->useValidEnvironment();
39 |
40 | $this->commandLine->run('docker-compose exec -T varnish varnishadm vcl.use boot0')->shouldBeCalled();
41 | $this->output->writeln('Varnish caching enabled')->shouldBeCalled();
42 |
43 | $this->command->execute($this->input->reveal(), $this->output->reveal());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/CommandLineTest.php:
--------------------------------------------------------------------------------
1 | loop = new StreamSelectLoop;
33 | $this->output = new BufferedOutput;
34 | $this->commandLine = new CommandLine($this->loop, new NullLogger, $this->output);
35 | }
36 |
37 | /**
38 | * @expectedException \Jh\Workflow\ProcessFailedException
39 | */
40 | public function testRunThrowsExceptionIfCommandFails()
41 | {
42 | $this->commandLine->run('exit 1');
43 | }
44 |
45 | public function testRunOutputsAndReturnsCommandOutput()
46 | {
47 | $out = $this->commandLine->run('echo "yes"');
48 |
49 | self::assertEquals("yes\n", $out);
50 | self::assertEquals("yes\n", $this->output->fetch());
51 | }
52 |
53 | public function testRunQuietlyDoesNotOutputButReturnsOutput()
54 | {
55 | $out = $this->commandLine->runQuietly('echo "yes"');
56 |
57 | self::assertEquals("yes\n", $out);
58 | self::assertEquals('', $this->output->fetch());
59 | }
60 |
61 | public function testRunAsyncOutputsAndExecutesCallBackAfterCommand()
62 | {
63 | $ran = false;
64 | $this->commandLine->runAsync('echo "yes"', function () use (&$ran) {
65 | $ran = true;
66 | });
67 |
68 | $this->loop->run();
69 |
70 | self::assertEquals("yes\n", $this->output->fetch());
71 | self::assertTrue($ran);
72 | }
73 |
74 | public function testRunLogsCommand()
75 | {
76 | $logger = $this->prophesize(Logger::class);
77 | $this->commandLine = new CommandLine($this->loop, $logger->reveal(), $this->output);
78 |
79 | $this->commandLine->run('echo "yes"');
80 |
81 | $logger->logCommand('echo "yes"', 'normal')->shouldHaveBeenCalled();
82 | }
83 |
84 | public function testRunQuietlyLogsCommand()
85 | {
86 | $logger = $this->prophesize(Logger::class);
87 | $this->commandLine = new CommandLine($this->loop, $logger->reveal(), $this->output);
88 |
89 | $this->commandLine->runQuietly('echo "yes"');
90 |
91 | $logger->logCommand('echo "yes"', 'quiet')->shouldHaveBeenCalled();
92 | }
93 |
94 | public function testRunAsyncLogsCommand()
95 | {
96 | $logger = $this->prophesize(Logger::class);
97 | $this->commandLine = new CommandLine($this->loop, $logger->reveal(), $this->output);
98 |
99 | $this->commandLine->runAsync('echo "yes"');
100 |
101 | $logger->logCommand('echo "yes"', 'async')->shouldHaveBeenCalled();
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/test/LoggerTest.php:
--------------------------------------------------------------------------------
1 | log = sprintf('%s/%s/workflow.log', sys_get_temp_dir(), $this->getName());
16 | }
17 | public function testLogger()
18 | {
19 | $logger = new Logger(new BufferedOutput);
20 | $logger->setLogFile($this->log);
21 |
22 | $logger->debug('DEBUG MESSAGE');
23 |
24 | $expected = "/\d{2}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \(DEBUG\) DEBUG MESSAGE\\n/";
25 |
26 | self::assertRegExp($expected, file_get_contents($this->log));
27 | }
28 |
29 | public function testLogCommandLogsAndPrints()
30 | {
31 | $logger = new Logger($output = new BufferedOutput);
32 | $logger->setLogFile($this->log);
33 |
34 | $logger->logCommand('echo "yes"', 'async');
35 |
36 | $expected = "/\d{2}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \(DEBUG\) Executing command \[async\]: \"echo \"yes\"\"\n/";
37 |
38 | self::assertRegExp($expected, file_get_contents($this->log));
39 | self::assertEquals("Executing [async] echo \"yes\"\n", $output->fetch());
40 | }
41 |
42 | public function tearDown()
43 | {
44 | unlink($this->log);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/Test/Constraint/FileExistsInContainerTest.php:
--------------------------------------------------------------------------------
1 |
11 | * @runTestsInSeparateProcesses Because you cannot mock global functions which already used
12 | */
13 | class FileExistsInContainerTest extends TestCase
14 | {
15 | use PHPMock;
16 |
17 | public function testMatchesWhenFileExists()
18 | {
19 | $exec = $this->getFunctionMock('Jh\Workflow\Test\Constraint', 'exec');
20 | $exec->expects($this->once())->willReturnCallback(
21 | function ($command, &$output, &$exitCode) {
22 | self::assertEquals('docker exec m2-php test -e some-file.txt', $command);
23 | $exitCode = 0;
24 | }
25 | );
26 |
27 | self::assertTrue((new FileExistsInContainer('m2-php'))->matches('some-file.txt'));
28 | }
29 |
30 | public function testMatchesWhenFileDoesNotExist()
31 | {
32 | $exec = $this->getFunctionMock('Jh\Workflow\Test\Constraint', 'exec');
33 | $exec->expects($this->once())->willReturnCallback(
34 | function ($command, &$output, &$exitCode) {
35 | self::assertEquals('docker exec m2-php test -e some-file.txt', $command);
36 | $exitCode = 1;
37 | }
38 | );
39 |
40 | self::assertFalse((new FileExistsInContainer('m2-php'))->matches('some-file.txt'));
41 | }
42 |
43 | public function testToString()
44 | {
45 | self::assertEquals('exists in container m2-php', (new FileExistsInContainer('m2-php'))->toString());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/test/Test/Constraint/FileGroupAndOwnerInContainerTest.php:
--------------------------------------------------------------------------------
1 |
11 | * @runTestsInSeparateProcesses Because you cannot mock global functions which already used
12 | */
13 | class FileGroupAndOwnerInContainerTest extends TestCase
14 | {
15 | use PHPMock;
16 |
17 | public function testMatchesWhenFileUserAndGroupCorrect()
18 | {
19 | $exec = $this->getFunctionMock('Jh\Workflow\Test\Constraint', 'exec');
20 | $exec->expects($this->once())->willReturnCallback(
21 | function ($command, &$output, &$exitCode) {
22 | self::assertEquals('docker exec m2-php stat -c "%G:%U" some-file.txt', $command);
23 | $exitCode = 0;
24 | $output = ['www-data:www-data'];
25 | }
26 | );
27 |
28 | self::assertTrue(
29 | (new FileUserAndGroupInContainer('m2-php', 'www-data', 'www-data'))->matches('some-file.txt')
30 | );
31 | }
32 |
33 | public function testMatchesWhenFileUserAndGroupIncorrect()
34 | {
35 | $exec = $this->getFunctionMock('Jh\Workflow\Test\Constraint', 'exec');
36 | $exec->expects($this->once())->willReturnCallback(
37 | function ($command, &$output, &$exitCode) {
38 | self::assertEquals('docker exec m2-php stat -c "%G:%U" some-file.txt', $command);
39 | $exitCode = 0;
40 | $output = ['root:root'];
41 | }
42 | );
43 |
44 | self::assertFalse(
45 | (new FileUserAndGroupInContainer('m2-php', 'www-data', 'www-data'))->matches('some-file.txt')
46 | );
47 | }
48 |
49 | public function testMatchesWhenFileDoesNotExist()
50 | {
51 | $exec = $this->getFunctionMock('Jh\Workflow\Test\Constraint', 'exec');
52 | $exec->expects($this->once())->willReturnCallback(
53 | function ($command, &$output, &$exitCode) {
54 | self::assertEquals('docker exec m2-php stat -c "%G:%U" some-file.txt', $command);
55 | $exitCode = 1;
56 | }
57 | );
58 |
59 | self::assertFalse(
60 | (new FileUserAndGroupInContainer('m2-php', 'www-data', 'www-data'))->matches('some-file.txt')
61 | );
62 | }
63 |
64 | public function testToString()
65 | {
66 | self::assertEquals(
67 | 'has correct group and user in container m2-php',
68 | (new FileUserAndGroupInContainer('m2-php', 'www-data', 'www-data'))->toString()
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/test/WatchFactoryTest.php:
--------------------------------------------------------------------------------
1 | create(['app']);
15 |
16 | self::assertInstanceOf(FsWatch::class, $watcher);
17 | }
18 |
19 | public function testWatchFactoryWithExcludes()
20 | {
21 | $watcher = (new WatchFactory(new StreamSelectLoop))->create(['app'], ['exclude-me']);
22 |
23 | self::assertInstanceOf(FsWatch::class, $watcher);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/fixtures/broken-env/.docker/local.env:
--------------------------------------------------------------------------------
1 | # Copy to local.env and store on local machine
2 |
3 | # Host Config
4 | MAGE_ROOT_DIR=/var/www
5 | MAGE_HOST=https://m2.dev
6 | MAGE_ADMIN_USER=admin
7 | MAGE_ADMIN_PASS=password123
8 | MAGE_ADMIN_FIRSTNAME=Joe
9 | MAGE_ADMIN_LASTNAME=Bloggs
10 | MAGE_ADMIN_EMAIL=magento@wearejh.com
11 | MAGE_BACKEND_FRONTNAME=admin
12 | HTTPS=on
13 |
14 | # MySQL Details
15 | MYSQL_ROOT_PASSWORD=docker
16 | MYSQL_DATABASE=docker
17 | MYSQL_USER=docker
18 | MYSQL_PASSWORD=docker
19 |
20 | # PHP
21 | PHP_MEMORY_LIMIT=2G
22 |
23 | # RabbitMQ
24 | RABBITMQ_DEFAULT_USER=user
25 | RABBITMQ_DEFAULT_PASS=password
26 |
27 | ## Mail config
28 | MAIL_HOST=mail
29 | MAIL_PORT=1025
30 |
31 | ## Xdebug config
32 | XDEBUG_IDE_KEY=PHPSTORM
33 | XDEBUG_CONFIG=remote_host=10.254.254.254
34 | XDEBUG_ENABLE=true
35 |
36 | # Blackfire
37 | BLACKFIRE_CLIENT_ID=
38 | BLACKFIRE_CLIENT_TOKEN=
39 | BLACKFIRE_SERVER_ID=
40 | BLACKFIRE_SERVER_TOKEN=
--------------------------------------------------------------------------------
/test/fixtures/broken-env/app.php.dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.0-fpm
2 | MAINTAINER Michael Woodward
3 |
4 | ARG BUILD_ENV=dev
5 | ENV PROD_ENV=prod
6 |
7 | CMD ["php-fpm"]
--------------------------------------------------------------------------------
/test/fixtures/broken-env/docker-compose.dev.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | nginx:
5 | volumes:
6 | - .docker/certs:/etc/letsencrypt
7 | env_file:
8 | - ./.docker/local.env
9 |
10 | php:
11 | env_file:
12 | - .docker/local.env
13 | volumes:
14 | - .docker/composer-cache:/var/www/.docker/composer-cache
15 |
16 | db:
17 | env_file:
18 | - ./.docker/local.env
19 | volumes:
20 | - .docker/db/:/docker-entrypoint-initdb.d/
21 |
22 | # rabbitmq:
23 | # env_file:
24 | # - ./.docker/local.env
25 |
26 | mail:
27 | container_name: m2-mail
28 | image: schickling/mailcatcher
29 | ports:
30 | - 1025
31 | - 1080:1080
32 |
33 | blackfire:
34 | container_name: m2-blackfire
35 | image: blackfire/blackfire
36 | env_file:
37 | - .docker/local.env
38 |
--------------------------------------------------------------------------------
/test/fixtures/broken-env/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | volumes:
4 | db-data:
5 | app-pub:
6 | app-var:
7 | app-env:
8 |
9 | services:
10 | nginx:
11 | container_name: m2
12 | image: nginx:stable-alpine
13 | volumes:
14 | - app-pub:/var/www/pub
15 | - .docker/nginx/sites:/etc/nginx/conf.d
16 | working_dir: /var/www
17 | ports:
18 | - "80:80"
19 | - "443:443"
20 |
21 | php:
22 | container_name: m2-php
23 | image: wearejh/m2
24 | build:
25 | context: ./
26 | dockerfile: app.php.dockerfile
27 | volumes:
28 | - app-pub:/var/www/pub
29 | - app-env:/var/www/app/etc
30 | - ~/.composer/auth.json:/root/.composer/auth.json
31 | working_dir: /var/www
32 | ports:
33 | - 9000
34 |
35 | db:
36 | container_name: m2-db
37 | image: mysql:5.6
38 | volumes:
39 | - db-data:/var/lib/mysql
40 | ports:
41 | - "3306:3306"
42 | restart: unless-stopped
43 |
44 | redis:
45 | container_name: m2-redis
46 | image: redis:3-alpine
47 | ports:
48 | - "6379:6379"
49 |
50 | # elasticsearch:
51 | # image: elasticsearch
52 | # ports:
53 | # - "9200:9200"
54 | # - "9300:9300"
55 |
56 | # rabbitmq:
57 | # image: rabbitmq:3.6.1-management
58 | # ports:
59 | # - "15672:15672"
60 | # - "5672:5672"
61 |
--------------------------------------------------------------------------------
/test/fixtures/config/env.default.php:
--------------------------------------------------------------------------------
1 | [
5 | 'frontName' => 'admin',
6 | ],
7 | 'db' => [
8 | 'connection' => [
9 | 'indexer' => [
10 | 'host' => 'db',
11 | 'dbname' => 'docker',
12 | 'username' => 'docker',
13 | 'password' => 'docker',
14 | 'model' => 'mysql4',
15 | 'engine' => 'innodb',
16 | 'initStatements' => 'SET NAMES utf8;',
17 | 'active' => '1',
18 | 'persistent' => NULL,
19 | ],
20 | 'default' => [
21 | 'host' => 'db',
22 | 'dbname' => 'docker',
23 | 'username' => 'docker',
24 | 'password' => 'docker',
25 | 'model' => 'mysql4',
26 | 'engine' => 'innodb',
27 | 'initStatements' => 'SET NAMES utf8;',
28 | 'active' => '1',
29 | ],
30 | ],
31 | 'table_prefix' => '',
32 | ],
33 | 'resource' => [
34 | 'default_setup' => [
35 | 'connection' => 'default',
36 | ],
37 | ],
38 | 'x-frame-options' => 'SAMEORIGIN',
39 | 'MAGE_MODE' => 'developer',
40 | 'session' => [
41 | 'save' => 'redis',
42 | 'redis' => [
43 | 'host' => 'redis',
44 | 'port' => '6379',
45 | 'password' => '',
46 | 'timeout' => '2.5',
47 | 'persistent_identifier' => '',
48 | 'database' => '0',
49 | 'compression_threshold' => '2048',
50 | 'compression_library' => 'gzip',
51 | 'log_level' => '1',
52 | 'max_concurrency' => '6',
53 | 'break_after_frontend' => '5',
54 | 'break_after_adminhtml' => '30',
55 | 'first_lifetime' => '600',
56 | 'bot_first_lifetime' => '60',
57 | 'bot_lifetime' => '7200',
58 | 'disable_locking' => '0',
59 | 'min_lifetime' => '60',
60 | 'max_lifetime' => '2592000',
61 | ],
62 | ],
63 | 'cache_types' => [
64 | 'config' => 1,
65 | 'layout' => 1,
66 | 'block_html' => 1,
67 | 'collections' => 1,
68 | 'reflection' => 1,
69 | 'db_ddl' => 1,
70 | 'eav' => 1,
71 | 'customer_notification' => 1,
72 | 'config_integration' => 1,
73 | 'config_integration_api' => 1,
74 | 'target_rule' => 1,
75 | 'full_page' => 1,
76 | 'amasty_shopby' => 1,
77 | 'translate' => 1,
78 | 'config_webservice' => 1,
79 | ],
80 | 'install' => [
81 | 'date' => 'Mon, 05 Mar 2018 11:35:35 +0000',
82 | ],
83 | 'cache' => [
84 | 'frontend' => [
85 | 'default' => [
86 | 'backend' => 'Cm_Cache_Backend_Redis',
87 | 'backend_options' => [
88 | 'server' => 'redis',
89 | 'port' => '6379',
90 | 'database' => '1',
91 | ],
92 | ],
93 | ],
94 | ],
95 | # 'queue' => [
96 | # 'amqp' => [
97 | # 'host' => 'rabbitmq',
98 | # 'port' => '5672',
99 | # 'user' => getenv('RABBITMQ_DEFAULT_USER'),
100 | # 'password' => getenv('RABBITMQ_DEFAULT_PASS'),
101 | # 'virtualhost' => '/',
102 | # 'ssl' => '',
103 | # ],
104 | # ],
105 | ];
106 |
--------------------------------------------------------------------------------
/test/fixtures/config/env.production.php:
--------------------------------------------------------------------------------
1 | [
5 | 'frontName' => 'admin',
6 | ],
7 | 'db' => [
8 | 'connection' => [
9 | 'indexer' => [
10 | 'host' => 'db',
11 | 'dbname' => 'docker',
12 | 'username' => 'docker',
13 | 'password' => 'docker',
14 | 'model' => 'mysql4',
15 | 'engine' => 'innodb',
16 | 'initStatements' => 'SET NAMES utf8;',
17 | 'active' => '1',
18 | 'persistent' => NULL,
19 | ],
20 | 'default' => [
21 | 'host' => 'db',
22 | 'dbname' => 'docker',
23 | 'username' => 'docker',
24 | 'password' => 'docker',
25 | 'model' => 'mysql4',
26 | 'engine' => 'innodb',
27 | 'initStatements' => 'SET NAMES utf8;',
28 | 'active' => '1',
29 | ],
30 | ],
31 | 'table_prefix' => '',
32 | ],
33 | 'resource' => [
34 | 'default_setup' => [
35 | 'connection' => 'default',
36 | ],
37 | ],
38 | 'x-frame-options' => 'SAMEORIGIN',
39 | 'MAGE_MODE' => 'production',
40 | 'session' => [
41 | 'save' => 'redis',
42 | 'redis' => [
43 | 'host' => 'redis',
44 | 'port' => '6379',
45 | 'password' => '',
46 | 'timeout' => '2.5',
47 | 'persistent_identifier' => '',
48 | 'database' => '0',
49 | 'compression_threshold' => '2048',
50 | 'compression_library' => 'gzip',
51 | 'log_level' => '1',
52 | 'max_concurrency' => '6',
53 | 'break_after_frontend' => '5',
54 | 'break_after_adminhtml' => '30',
55 | 'first_lifetime' => '600',
56 | 'bot_first_lifetime' => '60',
57 | 'bot_lifetime' => '7200',
58 | 'disable_locking' => '0',
59 | 'min_lifetime' => '60',
60 | 'max_lifetime' => '2592000',
61 | ],
62 | ],
63 | 'cache_types' => [
64 | 'config' => 1,
65 | 'layout' => 1,
66 | 'block_html' => 1,
67 | 'collections' => 1,
68 | 'reflection' => 1,
69 | 'db_ddl' => 1,
70 | 'eav' => 1,
71 | 'customer_notification' => 1,
72 | 'config_integration' => 1,
73 | 'config_integration_api' => 1,
74 | 'target_rule' => 1,
75 | 'full_page' => 1,
76 | 'amasty_shopby' => 1,
77 | 'translate' => 1,
78 | 'config_webservice' => 1,
79 | ],
80 | 'install' => [
81 | 'date' => 'Mon, 05 Mar 2018 11:35:35 +0000',
82 | ],
83 | 'cache' => [
84 | 'frontend' => [
85 | 'default' => [
86 | 'backend' => 'Cm_Cache_Backend_Redis',
87 | 'backend_options' => [
88 | 'server' => 'redis',
89 | 'port' => '6379',
90 | 'database' => '1',
91 | ],
92 | ],
93 | ],
94 | ],
95 | # 'queue' => [
96 | # 'amqp' => [
97 | # 'host' => 'rabbitmq',
98 | # 'port' => '5672',
99 | # 'user' => getenv('RABBITMQ_DEFAULT_USER'),
100 | # 'password' => getenv('RABBITMQ_DEFAULT_PASS'),
101 | # 'virtualhost' => '/',
102 | # 'ssl' => '',
103 | # ],
104 | # ],
105 | ];
106 |
--------------------------------------------------------------------------------
/test/fixtures/config/env.queue.php:
--------------------------------------------------------------------------------
1 | [
5 | 'frontName' => 'admin',
6 | ],
7 | 'db' => [
8 | 'connection' => [
9 | 'indexer' => [
10 | 'host' => 'db',
11 | 'dbname' => 'docker',
12 | 'username' => 'docker',
13 | 'password' => 'docker',
14 | 'model' => 'mysql4',
15 | 'engine' => 'innodb',
16 | 'initStatements' => 'SET NAMES utf8;',
17 | 'active' => '1',
18 | 'persistent' => NULL,
19 | ],
20 | 'default' => [
21 | 'host' => 'db',
22 | 'dbname' => 'docker',
23 | 'username' => 'docker',
24 | 'password' => 'docker',
25 | 'model' => 'mysql4',
26 | 'engine' => 'innodb',
27 | 'initStatements' => 'SET NAMES utf8;',
28 | 'active' => '1',
29 | ],
30 | ],
31 | 'table_prefix' => '',
32 | ],
33 | 'resource' => [
34 | 'default_setup' => [
35 | 'connection' => 'default',
36 | ],
37 | ],
38 | 'x-frame-options' => 'SAMEORIGIN',
39 | 'MAGE_MODE' => 'developer',
40 | 'session' => [
41 | 'save' => 'redis',
42 | 'redis' => [
43 | 'host' => 'redis',
44 | 'port' => '6379',
45 | 'password' => '',
46 | 'timeout' => '2.5',
47 | 'persistent_identifier' => '',
48 | 'database' => '0',
49 | 'compression_threshold' => '2048',
50 | 'compression_library' => 'gzip',
51 | 'log_level' => '1',
52 | 'max_concurrency' => '6',
53 | 'break_after_frontend' => '5',
54 | 'break_after_adminhtml' => '30',
55 | 'first_lifetime' => '600',
56 | 'bot_first_lifetime' => '60',
57 | 'bot_lifetime' => '7200',
58 | 'disable_locking' => '0',
59 | 'min_lifetime' => '60',
60 | 'max_lifetime' => '2592000',
61 | ],
62 | ],
63 | 'cache_types' => [
64 | 'config' => 1,
65 | 'layout' => 1,
66 | 'block_html' => 1,
67 | 'collections' => 1,
68 | 'reflection' => 1,
69 | 'db_ddl' => 1,
70 | 'eav' => 1,
71 | 'customer_notification' => 1,
72 | 'config_integration' => 1,
73 | 'config_integration_api' => 1,
74 | 'target_rule' => 1,
75 | 'full_page' => 1,
76 | 'amasty_shopby' => 1,
77 | 'translate' => 1,
78 | 'config_webservice' => 1,
79 | ],
80 | 'install' => [
81 | 'date' => 'Mon, 05 Mar 2018 11:35:35 +0000',
82 | ],
83 | 'cache' => [
84 | 'frontend' => [
85 | 'default' => [
86 | 'backend' => 'Cm_Cache_Backend_Redis',
87 | 'backend_options' => [
88 | 'server' => 'redis',
89 | 'port' => '6379',
90 | 'database' => '1',
91 | ],
92 | ],
93 | ],
94 | ],
95 | 'queue' => [
96 | 'amqp' => [
97 | 'host' => 'rabbitmq',
98 | 'port' => '5672',
99 | 'user' => getenv('RABBITMQ_DEFAULT_USER'),
100 | 'password' => getenv('RABBITMQ_DEFAULT_PASS'),
101 | 'virtualhost' => '/',
102 | 'ssl' => '',
103 | ],
104 | ],
105 | ];
106 |
--------------------------------------------------------------------------------
/test/fixtures/config/local.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | false
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 1
25 |
26 |
27 |
28 |
29 | Mage_Cache_Backend_Redis
30 |
31 | redis
32 | 6379
33 |
34 | 0
35 |
36 | 0
37 | 1
38 | 10
39 | 0
40 | 1
41 | 1
42 | 20480
43 | gzip
44 |
45 |
46 |
47 | db
48 |
49 | redis
50 | 6379
51 | 2.5
52 |
53 | 1
54 | 2048
55 | gzip
56 | 0
57 | 15
58 | 5
59 | 30
60 | 7200
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/test/fixtures/invalid-env/.docker/local.env.dist:
--------------------------------------------------------------------------------
1 | # Copy to local.env and store on local machine
2 |
3 | sds dfsahdf ukasdf asdf
4 |
5 | # Host Config
6 | MAGE_ROOT_DIR=/var/www
7 | MAGE_HOST=https://m2.dev
8 | MAGE_ADMIN_USER=admin
9 | MAGE_ADMIN_PASS=password123
10 | MAGE_ADMIN_FIRSTNAME=Joe
11 | MAGE_ADMIN_LASTNAME=Bloggs
12 | MAGE_ADMIN_EMAIL=magento@wearejh.com
13 | MAGE_BACKEND_FRONTNAME=admin
14 | HTTPS=on
15 |
16 | # MySQL Details
17 | MYSQL_ROOT_PASSWORD=docker
18 | MYSQL_DATABASE=docker
19 | MYSQL_USER=docker
20 | MYSQL_PASSWORD=docker
21 |
22 | # PHP
23 | PHP_MEMORY_LIMIT=2G
24 |
25 | # RabbitMQ
26 | RABBITMQ_DEFAULT_USER=user
27 | RABBITMQ_DEFAULT_PASS=password
28 |
29 | ## Mail config
30 | MAIL_HOST=mail
31 | MAIL_PORT=1025
32 |
33 | ## Xdebug config
34 | XDEBUG_IDE_KEY=PHPSTORM
35 | XDEBUG_CONFIG=remote_host=10.254.254.254
36 | XDEBUG_ENABLE=true
37 |
38 | # Blackfire
39 | BLACKFIRE_CLIENT_ID=
40 | BLACKFIRE_CLIENT_TOKEN=
41 | BLACKFIRE_SERVER_ID=
42 | BLACKFIRE_SERVER_TOKEN=
--------------------------------------------------------------------------------
/test/fixtures/invalid-env/app.php.dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.0-fpm
2 | MAINTAINER Michael Woodward
3 |
4 | ARG BUILD_ENV=dev
5 | ENV PROD_ENV=prod
6 |
7 | CMD ["php-fpm"]
--------------------------------------------------------------------------------
/test/fixtures/invalid-env/docker-compose.dev.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | nginx:
5 | volumes:
6 | - .docker/certs:/etc/letsencrypt
7 | env_file:
8 | - ./.docker/local.env
9 |
10 | php:
11 | env_file:
12 | - .docker/local.env
13 | volumes:
14 | - .docker/composer-cache:/var/www/.docker/composer-cache
15 |
16 | db:
17 | env_file:
18 | - ./.docker/local.env
19 | volumes:
20 | - .docker/db/:/docker-entrypoint-initdb.d/
21 |
22 | # rabbitmq:
23 | # env_file:
24 | # - ./.docker/local.env
25 |
26 | mail:
27 | container_name: m2-mail
28 | image: schickling/mailcatcher
29 | ports:
30 | - 1025
31 | - 1080:1080
32 |
33 | blackfire:
34 | container_name: m2-blackfire
35 | image: blackfire/blackfire
36 | env_file:
37 | - .docker/local.env
38 |
--------------------------------------------------------------------------------
/test/fixtures/invalid-env/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | volumes:
4 | db-data:
5 | app-pub:
6 | app-var:
7 | app-env:
8 |
9 | services:
10 | nginx:
11 | image: nginx:stable-alpine
12 | volumes:
13 | - app-pub:/var/www/pub
14 | - .docker/nginx/sites:/etc/nginx/conf.d
15 | working_dir: /var/www
16 | ports:
17 | - "80:80"
18 | - "443:443"
19 |
20 | php:
21 | build:
22 | context: ./
23 | dockerfile: app.php.dockerfile
24 | volumes:
25 | - app-pub:/var/www/pub
26 | - app-env:/var/www/app/etc
27 | - ~/.composer/auth.json:/root/.composer/auth.json
28 | working_dir: /var/www
29 | ports:
30 | - 9000
31 |
32 | db:
33 | container_name: m2-db
34 | image: mysql:5.6
35 | volumes:
36 | - db-data:/var/lib/mysql
37 | ports:
38 | - "3306:3306"
39 | restart: unless-stopped
40 |
41 | redis:
42 | container_name: m2-redis
43 | image: redis:3-alpine
44 | ports:
45 | - "6379:6379"
46 |
47 | # elasticsearch:
48 | # image: elasticsearch
49 | # ports:
50 | # - "9200:9200"
51 | # - "9300:9300"
52 |
53 | # rabbitmq:
54 | # image: rabbitmq:3.6.1-management
55 | # ports:
56 | # - "15672:15672"
57 | # - "5672:5672"
58 |
--------------------------------------------------------------------------------
/test/fixtures/missing-docker-files/some-file:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeareJH/workflow/bf4d11820ab8eccf1b8f7a7d168419d58131c5d6/test/fixtures/missing-docker-files/some-file
--------------------------------------------------------------------------------
/test/fixtures/test-env-files/some-file.php:
--------------------------------------------------------------------------------
1 | some-file.php
--------------------------------------------------------------------------------
/test/fixtures/test-env/app.php.dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.0-fpm
2 | MAINTAINER Michael Woodward
3 |
4 | WORKDIR /var/www
5 | RUN chown www-data:www-data /var/www
6 |
7 | CMD ["php-fpm"]
8 |
9 |
--------------------------------------------------------------------------------
/test/fixtures/test-env/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | php:
5 | container_name: m2-php
6 | image: workflow-test
7 | build:
8 | context: ./
9 | dockerfile: app.php.dockerfile
10 | working_dir: /var/www
11 | ports:
12 | - 9000
13 |
--------------------------------------------------------------------------------
/test/fixtures/valid-env/.docker/local.env:
--------------------------------------------------------------------------------
1 | # Copy to local.env and store on local machine
2 |
3 | # Host Config
4 | MAGE_ROOT_DIR=/var/www
5 | MAGE_HOST=https://m2.dev
6 | MAGE_ADMIN_USER=admin
7 | MAGE_ADMIN_PASS=password123
8 | MAGE_ADMIN_FIRSTNAME=Joe
9 | MAGE_ADMIN_LASTNAME=Bloggs
10 | MAGE_ADMIN_EMAIL=magento@wearejh.com
11 | MAGE_BACKEND_FRONTNAME=admin
12 | HTTPS=on
13 |
14 | # MySQL Details
15 | MYSQL_ROOT_PASSWORD=docker
16 | MYSQL_DATABASE=docker
17 | MYSQL_USER=docker
18 | MYSQL_PASSWORD=docker
19 |
20 | # PHP
21 | PHP_MEMORY_LIMIT=2G
22 |
23 | # RabbitMQ
24 | RABBITMQ_DEFAULT_USER=user
25 | RABBITMQ_DEFAULT_PASS=password
26 |
27 | ## Mail config
28 | MAIL_HOST=mail
29 | MAIL_PORT=1025
30 |
31 | ## Xdebug config
32 | XDEBUG_IDE_KEY=PHPSTORM
33 | XDEBUG_CONFIG=remote_host=10.254.254.254
34 | XDEBUG_ENABLE=true
35 |
36 | # Blackfire
37 | BLACKFIRE_CLIENT_ID=
38 | BLACKFIRE_CLIENT_TOKEN=
39 | BLACKFIRE_SERVER_ID=
40 | BLACKFIRE_SERVER_TOKEN=
--------------------------------------------------------------------------------
/test/fixtures/valid-env/app.php.dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.0-fpm
2 | MAINTAINER Michael Woodward
3 |
4 | ARG BUILD_ENV=dev
5 | ENV PROD_ENV=prod
6 |
7 | CMD ["php-fpm"]
--------------------------------------------------------------------------------
/test/fixtures/valid-env/docker-compose.dev.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | nginx:
5 | volumes:
6 | - .docker/certs:/etc/letsencrypt
7 | env_file:
8 | - ./.docker/local.env
9 |
10 | php:
11 | env_file:
12 | - .docker/local.env
13 | volumes:
14 | - .docker/composer-cache:/var/www/.docker/composer-cache
15 |
16 | db:
17 | env_file:
18 | - ./.docker/local.env
19 | volumes:
20 | - .docker/db/:/docker-entrypoint-initdb.d/
21 |
22 | # rabbitmq:
23 | # env_file:
24 | # - ./.docker/local.env
25 |
26 | mail:
27 | container_name: m2-mail
28 | image: schickling/mailcatcher
29 | ports:
30 | - 1025
31 | - 1080:1080
32 |
33 | blackfire:
34 | container_name: m2-blackfire
35 | image: blackfire/blackfire
36 | env_file:
37 | - .docker/local.env
38 |
--------------------------------------------------------------------------------
/test/fixtures/valid-env/docker-compose.prod.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | volumes:
4 | app-var:
5 |
6 | services:
7 | nginx:
8 | env_file:
9 | - .docker/production.env
10 |
11 | php:
12 | env_file:
13 | - .docker/production.env
14 | volumes:
15 | - app-var:/var/www/var
16 |
17 | db:
18 | env_file:
19 | - .docker/production.env
20 |
21 | # rabbitmq:
22 | # env_file:
23 | # - ./.docker/production.env
--------------------------------------------------------------------------------
/test/fixtures/valid-env/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | volumes:
4 | db-data:
5 | app-pub:
6 | app-var:
7 | app-env:
8 |
9 | services:
10 | haproxy:
11 | image: dockercloud/haproxy:latest
12 | links:
13 | - varnish
14 | environment:
15 | - CERT_FOLDER=/certs
16 | volumes:
17 | - /var/run/docker.sock:/var/run/docker.sock
18 | - .docker/certs:/certs
19 | ports:
20 | - "80:80"
21 | - "443:443"
22 |
23 | varnish:
24 | container_name: m2-varnish
25 | image: wearejh/magento-varnish:latest
26 | environment:
27 | - FORCE_SSL=yes
28 | depends_on:
29 | - nginx
30 |
31 | nginx:
32 | container_name: m2
33 | image: nginx:stable-alpine
34 | working_dir: /var/www
35 | volumes:
36 | - .docker/nginx/sites:/etc/nginx/conf.d
37 | volumes_from:
38 | - php
39 | depends_on:
40 | - php
41 |
42 | php:
43 | container_name: m2-php
44 | image: wearejh/m2
45 | build:
46 | context: ./
47 | dockerfile: app.php.dockerfile
48 | volumes:
49 | - app-env:/var/www/app/etc
50 | - ~/.composer/auth.json:/root/.composer/auth.json
51 | working_dir: /var/www
52 | ports:
53 | - 9000
54 |
55 | db:
56 | container_name: m2-db
57 | image: mysql:5.6
58 | volumes:
59 | - db-data:/var/lib/mysql
60 | ports:
61 | - "3306:3306"
62 | restart: unless-stopped
63 |
64 | redis:
65 | container_name: m2-redis
66 | image: redis:3-alpine
67 | ports:
68 | - "6379:6379"
69 |
70 | # elasticsearch:
71 | # image: elasticsearch
72 | # ports:
73 | # - "9200:9200"
74 | # - "9300:9300"
75 |
76 | # rabbitmq:
77 | # image: rabbitmq:3.6.1-management
78 | # ports:
79 | # - "15672:15672"
80 | # - "5672:5672"
81 |
--------------------------------------------------------------------------------
/test/fixtures/valid-env/some-file.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeareJH/workflow/bf4d11820ab8eccf1b8f7a7d168419d58131c5d6/test/fixtures/valid-env/some-file.txt
--------------------------------------------------------------------------------
/test/fixtures/valid-env/some-folder/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeareJH/workflow/bf4d11820ab8eccf1b8f7a7d168419d58131c5d6/test/fixtures/valid-env/some-folder/.gitkeep
--------------------------------------------------------------------------------
/test/fixtures/valid-env/some-import.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WeareJH/workflow/bf4d11820ab8eccf1b8f7a7d168419d58131c5d6/test/fixtures/valid-env/some-import.sql
--------------------------------------------------------------------------------