Completed
Push — master ( 01982b...3c7394 )
by ANTHONIUS
11s
created

InitCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 3
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\Config\Config;
17
use Dotfiles\Core\Exceptions\InvalidOperationException;
18
use Dotfiles\Core\Util\CommandProcessor;
19
use Dotfiles\Core\Util\Filesystem;
20
use Dotfiles\Core\Util\Toolkit;
21
use Symfony\Component\Console\Input\InputArgument;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Input\InputOption;
24
use Symfony\Component\Console\Output\OutputInterface;
25
use Symfony\Component\Console\Question\Question;
26
use Symfony\Component\Finder\Finder;
27
28
class InitCommand extends Command
29
{
30
    /**
31
     * @var CommandProcessor
32
     */
33
    private $commandProcessor;
34
35
    /**
36
     * @var Config
37
     */
38
    private $config;
39
40
    /**
41
     * @var string
42
     */
43
    private $defaultHomeDir;
44
45
    /**
46
     * @var string
47
     */
48
    private $defaultRepoDir;
49
50
    /**
51
     * @var InputInterface
52
     */
53
    private $input;
54
55
    /**
56
     * @var OutputInterface
57
     */
58
    private $output;
59
60
    public function __construct(?string $name = null, CommandProcessor $processor, Config $config)
61
    {
62
        parent::__construct($name);
63
        $this->commandProcessor = $processor;
64
        $this->config = $config;
65
        $this->defaultHomeDir = $config->get('dotfiles.home_dir');
66
        $this->defaultRepoDir = $config->get('dotfiles.repo_dir');
67
    }
68
69
    protected function configure(): void
70
    {
71
        $this
72
            ->setName('init')
73
            ->setDescription('Initialize new Dotfiles project.')
74
            ->addArgument('repo-dir', InputArgument::OPTIONAL, 'Local repository directory')
75
            ->addOption('home-dir', 'hd', InputOption::VALUE_OPTIONAL, 'Home directory')
76
            ->addOption('machine', 'm', InputOption::VALUE_OPTIONAL, 'Machine name')
77
        ;
78
    }
79
80
    /**
81
     * @param InputInterface  $input
82
     * @param OutputInterface $output
83
     *
84
     * @return int|null|void
85
     */
86
    protected function execute(InputInterface $input, OutputInterface $output)
87
    {
88
        $this->input = $input;
89
        $this->output = $output;
90
91
        /* @var \Symfony\Component\Console\Helper\FormatterHelper $formatter */
92
        $formatter = $this->getHelper('formatter');
93
        $message = <<<'EOF'
94
95
Please initialize dotfiles project first to start using dotfiles
96
97
EOF;
98
99
        $block = $formatter->formatBlock(
100
            $message,
101
            'info'
102
        );
103
        $output->writeln($block);
104
        if (null === ($repoDir = $input->getArgument('repo-dir'))) {
105
            $repoDir = $this->doAskRepoDir();
106
        }
107
108
        if (null === ($machine = $input->getOption('machine'))) {
109
            $machine = $this->doAskMachineName();
110
        }
111
112
        if (null === ($homeDir = $input->getOption('home-dir'))) {
113
            $homeDir = $this->doAskHomeDir();
114
        }
115
116
        $this->initDotfilesDir($homeDir, $repoDir, $machine);
117
        $this->initRepoDir($repoDir);
118
    }
119
120
    private function doAskHomeDir()
121
    {
122
        $input = $this->input;
123
        $output = $this->output;
124
        $helper = $this->getHelper('question');
125
        $default = $this->defaultHomeDir;
126
        $question = new Question(sprintf('Please enter your home directory (<comment>%s</comment>):', $default), $default);
127
128
        return $helper->ask($input, $output, $question);
129
    }
130
131
    private function doAskMachineName()
132
    {
133
        $input = $this->input;
134
        $output = $this->output;
135
        $helper = $this->getHelper('question');
136
        $default = gethostname();
137
        $question = new Question(sprintf('Please enter your machine name (<comment>%s</comment>):', $default), $default);
138
139
        return $helper->ask($input, $output, $question);
140
    }
141
142
    private function doAskRepoDir()
143
    {
144
        $input = $this->input;
145
        $output = $this->output;
146
        $helper = $this->getHelper('question');
147
        $default = 'dev' === getenv('DOTFILES_ENV') ? sys_get_temp_dir().'/dotfiles/repo' : getcwd();
148
        $question = new Question("Please enter local repository dir (<comment>$default</comment>): ", $default);
149
        $question->setValidator(function ($answer) {
150
            if (null === $answer) {
151
                throw new InvalidOperationException('You have to define local repository directory');
152
            }
153
            $parent = dirname($answer);
154
            if (!is_dir($parent) || !is_writable($parent)) {
155
                throw new InvalidOperationException(
156
                    "Can not find parent directory, please ensure that $parent is exists and writable"
157
                );
158
            }
159
160
            return $answer;
161
        });
162
163
        $question->setMaxAttempts(3);
164
165
        return $helper->ask($input, $output, $question);
166
    }
167
168
    private function initDotFilesDir(string $homeDir, string $repoDir, string $machine): void
169
    {
170
        $dotfilesDir = $homeDir.DIRECTORY_SEPARATOR.'.dotfiles';
171
        Toolkit::ensureDir($dotfilesDir);
172
        $envFile = $dotfilesDir.DIRECTORY_SEPARATOR.'.env';
173
        $contents = <<<EOF
174
175
DOTFILES_MACHINE_NAME=$machine
176
DOTFILES_REPO_DIR=$repoDir
177
178
EOF;
179
180
        file_put_contents($envFile, $contents, LOCK_EX);
181
    }
182
183
    private function initRepoDir($repoDir): void
184
    {
185
        Toolkit::ensureDir($repoDir);
186
        $origin = __DIR__.'/../Resources/templates/repo';
187
188
        $finder = Finder::create()
189
            ->ignoreVCS(true)
190
            ->ignoreDotFiles(false)
191
            ->in($origin)
192
            ->files()
193
        ;
194
        $fs = new Filesystem();
195
        $fs->mirror($origin, $repoDir, $finder);
196
    }
197
}
198