Completed
Pull Request — master (#1)
by ANTHONIUS
03:16
created

InitCommand::execute()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 8
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the dotfiles project.
7
 *
8
 *     (c) Anthonius Munthi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Dotfiles\Core\Command;
15
16
use Dotfiles\Core\Exceptions\InvalidOperationException;
17
use Dotfiles\Core\Util\CommandProcessor;
18
use Dotfiles\Core\Util\Filesystem;
19
use Dotfiles\Core\Util\Toolkit;
20
use Symfony\Component\Console\Input\InputArgument;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Input\InputOption;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use Symfony\Component\Console\Question\Question;
25
use Symfony\Component\Finder\Finder;
26
27
class InitCommand extends Command
28
{
29
    /**
30
     * @var CommandProcessor
31
     */
32
    private $commandProcessor;
33
34
    /**
35
     * @var InputInterface
36
     */
37
    private $input;
38
39
    /**
40
     * @var OutputInterface
41
     */
42
    private $output;
43
44
    public function __construct(?string $name = null, CommandProcessor $processor)
45
    {
46
        parent::__construct($name);
47
        $this->commandProcessor = $processor;
48
    }
49
50
    protected function configure(): void
51
    {
52
        $this
53
            ->setName('init')
54
            ->setDescription('Initialize new Dotfiles project.')
55
            ->addArgument('repo-dir', InputArgument::OPTIONAL, 'Local repository directory')
56
            ->addOption('home-dir', 'hd', InputOption::VALUE_OPTIONAL, 'Home directory')
57
            ->addOption('machine', 'm', InputOption::VALUE_OPTIONAL, 'Machine name')
58
        ;
59
    }
60
61
    /**
62
     * @param InputInterface  $input
63
     * @param OutputInterface $output
64
     *
65
     * @return int|null|void
66
     */
67
    protected function execute(InputInterface $input, OutputInterface $output)
68
    {
69
        $this->input = $input;
70
        $this->output = $output;
71
72
        if (null === ($repoDir = $input->getArgument('repo-dir'))) {
73
            $repoDir = $this->doAskRepoDir();
74
        }
75
76
        if (null === ($machine = $input->getOption('machine'))) {
77
            $machine = $this->doAskMachineName();
78
        }
79
80
        if (null === ($homeDir = $input->getOption('home-dir'))) {
81
            $homeDir = $this->doAskHomeDir();
82
        }
83
84
        $this->initDotfilesDir($homeDir, $repoDir, $machine);
85
        $this->initRepoDir($repoDir);
86
    }
87
88
    private function doAskHomeDir()
89
    {
90
        $input = $this->input;
91
        $output = $this->output;
92
        $helper = $this->getHelper('question');
93
        $question = new Question(sprintf('Please enter your home directory (default: <comment>%s</comment>):', getenv('HOME')), getenv('HOME'));
94
95
        return $helper->ask($input, $output, $question);
96
    }
97
98
    private function doAskMachineName()
99
    {
100
        $input = $this->input;
101
        $output = $this->output;
102
        $helper = $this->getHelper('question');
103
        $question = new Question(sprintf('Please enter your machine name (default: <comment>%s</comment>):', gethostname()), gethostname());
104
105
        return $helper->ask($input, $output, $question);
106
    }
107
108
    private function doAskRepoDir()
109
    {
110
        $input = $this->input;
111
        $output = $this->output;
112
        $helper = $this->getHelper('question');
113
        $question = new Question('Please enter local repository dir: ');
114
        $question->setValidator(function ($answer) {
115
            if (null === $answer) {
116
                throw new InvalidOperationException('You have to define local repository directory');
117
            }
118
            $parent = dirname($answer);
119
            if (!is_dir($parent) || !is_writable($parent)) {
120
                throw new InvalidOperationException(
121
                    "Can not find parent directory, please ensure that $parent is exists and writable"
122
                );
123
            }
124
125
            return $answer;
126
        });
127
128
        $question->setMaxAttempts(3);
129
130
        return $helper->ask($input, $output, $question);
131
    }
132
133
    private function initDotFilesDir(string $homeDir, $repoDir, $machine): void
134
    {
135
        $dotfilesDir = $homeDir.DIRECTORY_SEPARATOR.'.dotfiles';
136
        Toolkit::ensureDir($dotfilesDir);
137
        $templateDir = __DIR__.'/../Resources/templates/dotfiles';
138
        $finder = Finder::create()
139
            ->in($templateDir)
140
            ->ignoreDotFiles(false)
141
            ->files()
142
        ;
143
        $fs = new Filesystem();
144
        $fs->mirror($templateDir, $dotfilesDir, $finder);
145
146
        $envFile = $dotfilesDir.DIRECTORY_SEPARATOR.'.env';
147
        $contents = file_get_contents($envFile);
148
        $contents = strtr($contents, array(
149
            '{{machine_name}}' => $machine,
150
            '{{repo_dir}}' => $repoDir,
151
        ));
152
        file_put_contents($envFile, $contents, LOCK_EX);
153
    }
154
155
    private function initRepoDir($repoDir): void
156
    {
157
        Toolkit::ensureDir($repoDir);
158
        $fs = new Filesystem();
159
        $fs->mirror(__DIR__.'/../Resources/templates/repo', $repoDir);
160
    }
161
}
162