Passed
Pull Request — master (#287)
by Théo
02:12
created

GenerateDockerFile::guessPharPath()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 42
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 42
rs 8.5706
c 0
b 0
f 0
cc 7
nc 7
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Console\Command;
16
17
use Assert\Assertion;
18
use KevinGH\Box\DockerFileGenerator;
19
use Symfony\Component\Console\Input\InputArgument;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\StringInput;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Question\ConfirmationQuestion;
24
use Symfony\Component\Console\Style\SymfonyStyle;
25
use function file_exists;
26
use function getcwd;
27
use function KevinGH\Box\FileSystem\dump_file;
28
use function KevinGH\Box\FileSystem\make_path_relative;
29
use function KevinGH\Box\FileSystem\remove;
30
use function realpath;
31
use function sprintf;
32
33
/**
34
 * @private
35
 */
36
final class GenerateDockerFile extends ConfigurableCommand
37
{
38
    use CreateTemporaryPharFile;
39
40
    private const PHAR_ARG = 'phar';
41
    private const DOCKER_FILE_NAME = 'Dockerfile';
42
43
    /**
44
     * {@inheritdoc}
45
     */
46
    protected function configure(): void
47
    {
48
        parent::configure();
49
50
        $this->setName('docker');
51
        $this->setDescription('🐳  Generates a Dockerfile for the given PHAR');
52
        $this->addArgument(
53
            self::PHAR_ARG,
54
            InputArgument::OPTIONAL,
55
            'The PHAR file'
56
        );
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    protected function execute(InputInterface $input, OutputInterface $output): int
63
    {
64
        $io = new SymfonyStyle($input, $output);
65
66
        $pharPath = $input->getArgument(self::PHAR_ARG);
67
68
        if (null === $pharPath) {
69
            $pharPath = $this->guessPharPath($input, $output, $io);
70
        }
71
72
        if (null === $pharPath) {
73
            return 1;
74
        }
75
76
        Assertion::file($pharPath);
0 ignored issues
show
Bug introduced by
It seems like $pharPath can also be of type string[]; however, parameter $value of Assert\Assertion::file() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

76
        Assertion::file(/** @scrutinizer ignore-type */ $pharPath);
Loading history...
77
78
        $pharPath = false !== realpath($pharPath) ? realpath($pharPath) : $pharPath;
79
80
        $io->newLine();
81
        $io->writeln(
82
            sprintf(
83
                '🐳  Generating a Dockerfile for the PHAR "<comment>%s</comment>"',
84
                $pharPath
85
            )
86
        );
87
88
        $tmpPharPath = $this->createTemporaryPhar($pharPath);
89
90
        $requirementsPhar = 'phar://'.$tmpPharPath.'/.box/.requirements.php';
91
92
        try {
93
            if (false === file_exists($requirementsPhar)) {
94
                $io->error(
95
                    'Cannot retrieve the requirements for the PHAR. Make sure the PHAR has been built with Box and the '
96
                    .'requirement checker enabled.'
97
                );
98
99
                return 1;
100
            }
101
102
            $requirements = include $requirementsPhar;
103
104
            $dockerFileContents = DockerFileGenerator::createForRequirements(
105
                    $requirements,
106
                    make_path_relative($pharPath, getcwd())
107
                )
108
                ->generate()
109
            ;
110
111
            if (file_exists(self::DOCKER_FILE_NAME)) {
112
                $remove = $io->askQuestion(
113
                    new ConfirmationQuestion(
114
                        'A Docker file has already been found, are you sure you want to override it?',
115
                        true
116
                    )
117
                );
118
119
                if (false === $remove) {
120
                    $io->writeln('Skipped the docker file generation.');
121
122
                    return 0;
123
                }
124
            }
125
126
            dump_file(self::DOCKER_FILE_NAME, $dockerFileContents);
127
128
            $io->success('Done');
129
130
            $io->writeln(
131
                [
132
                    sprintf(
133
                        'You can now inspect your <comment>%s</comment> file or build your container with:',
134
                        self::DOCKER_FILE_NAME
135
                    ),
136
                    '$ <comment>docker build .</comment>',
137
                ]
138
            );
139
        } finally {
140
            if ($tmpPharPath !== $pharPath) {
141
                remove($tmpPharPath);
142
            }
143
        }
144
145
        return 0;
146
    }
147
148
    private function guessPharPath(InputInterface $input, OutputInterface $output, SymfonyStyle $io): ?string
149
    {
150
        $config = $this->getConfig($input, $output, true);
151
152
        if (file_exists($config->getOutputPath())) {
153
            return $config->getOutputPath();
154
        }
155
156
        $compile = $io->askQuestion(
157
            new ConfirmationQuestion(
158
                'The output PHAR could not be found, do you wish to generate it by running "<comment>box '
159
                .'compile</comment>"?',
160
                true
161
            )
162
        );
163
164
        if (false === $compile) {
165
            $io->error('Could not find the PHAR to generate the docker file for');
166
167
            return null;
168
        }
169
170
        $compileCommand = $this->getApplication()->find('compile');
171
172
        if ($output->isQuiet()) {
173
            $compileInput = '--quiet';
174
        } elseif ($output->isVerbose()) {
175
            $compileInput = '--verbose 1';
176
        } elseif ($output->isVeryVerbose()) {
177
            $compileInput = '--verbose 2';
178
        } elseif ($output->isDebug()) {
179
            $compileInput = '--verbose 3';
180
        } else {
181
            $compileInput = '';
182
        }
183
184
        $compileInput = new StringInput($compileInput);
185
        $compileInput->setInteractive(false);
186
187
        $compileCommand->run($compileInput, clone $output);
188
189
        return $config->getOutputPath();
190
    }
191
}
192