Completed
Push — master ( d32259...77ced4 )
by Gordon
08:40 queued 06:34
created

TaskBase::installPackages()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 15
rs 9.9666
cc 3
nc 2
nop 0
1
<?php declare(strict_types = 1);
2
3
namespace Suilven\PHPTravisEnhancer\Abstraction;
4
5
use League\CLImate\CLImate;
6
use Suilven\PHPTravisEnhancer\Helper\TravisYMLHelper;
7
use Suilven\PHPTravisEnhancer\IFace\Task;
8
use Suilven\PHPTravisEnhancer\Terminal\TerminalHelper;
9
10
abstract class TaskBase implements Task
11
{
12
13
    use TerminalHelper;
14
15
    /** @var \League\CLImate\CLImate */
16
    private $climate;
17
18
    /** @var array<string> The names of the commands, e.g. phpstan, phpcs */
19
    private static $codeCheckCommands = [];
20
21
    abstract public function getFlag(): string;
22
23
24
    /**
25
     * TaskBase constructor.
26
     */
27
    public function __construct()
28
    {
29
        $this->climate = new CLImate();
30
    }
31
32
33
    /**
34
     * Update Travis file to incorporate for the relevant task
35
     *
36
     * @todo What is the correct annotation to prevent this error?
37
     * @psalm-suppress PossiblyInvalidArrayOffset
38
     * @psalm-suppress PossiblyNullOperand - nulls are checked for
39
     * @param string $travisFile An injectable filename (for testing), leave blank for default of .travis.yml
40
     * @return int 0 if task successful, error code if not
41
     */
42
    public function run(string $travisFile = '.travis.yml'): int
43
    {
44
        $this->climate->border();
45
        $this->climate->info('Adding ' . $this->getCommand());
46
        $this->climate->border();
47
48
        // install composer packages
49
        $retVal = $this->installPackages();
50
        if ($retVal !== 0) {
51
            $this->climate->error('Packages could not be installed, not altering Travis or adding config files');
52
53
            return $retVal;
54
        }
55
56
        $this->addScriptsToComposerJSON();
57
58
59
        // any alterations post composer failures should be successful
60
        $helper = new TravisYMLHelper($travisFile);
61
62
        /** @var array<string, string|array> $yamlAsArray */
63
        $yamlAsArray = $helper->loadTravis();
64
65
        $helper->ensurePathExistsInYaml($yamlAsArray, 'matrix/include');
66
67
        $foundExisting = $helper->checkForExistingInEnv($yamlAsArray, $this->getFlag());
68
        if (!$foundExisting) {
69
            // add a matrix entry
70
            $yamlAsArray['matrix']['include'][] = [
71
                'php' => 7.4,
72
                'env' => $this->getFlag() . '=1',
73
            ];
74
75
            $this->taskReport('Added env option: ' . $this->getFlag() . '=1');
76
77
            /** @var string $prefix the bash prefix to check for the flag being set */
78
            $prefix = 'if [[ $' . $this->getFlag() .' ]]; then ';
79
80
            $beforeScript = $this->getTravisBeforeScript();
81
            if (isset($beforeScript)) {
82
                // install jdscpd, node tool, for duplication detection
83
                $helper->ensurePathExistsInYaml($yamlAsArray, 'before_script');
84
                $yamlAsArray['before_script'][] = $prefix . $beforeScript . '  ;fi';
85
                $this->taskReport('Added before script: ' . $beforeScript);
86
            }
87
88
            $script = $this->getTravisScript();
89
            if (isset($script)) {
90
                // run jscpd on src and tests dir
91
                $helper->ensurePathExistsInYaml($yamlAsArray, 'script');
92
                $yamlAsArray['script'][] = $prefix . $script . ' ; fi';
93
                $this->taskReport('Added script: ' . $script);
94
            }
95
        }
96
97
        $helper->saveTravis($yamlAsArray);
98
99
        $this->copyFiles();
100
101
        $this->climate->br(4);
102
103
        // signal success
104
        return 1;
105
    }
106
107
108
    /**
109
     * Copy and files required for this task to their appropriate place in the hierarchy of the main project
110
     */
111
    private function copyFiles(): void
112
    {
113
        $fileTransferArray = $this->filesToCopy();
114
        foreach ($fileTransferArray as $srcFile => $destFile) {
115
            $destFile = \str_replace('SRC_DIR', 'src', $destFile);
116
            $destFile = \str_replace('TESTS_DIR', 'tests', $destFile);
117
118
            $absoluteSrcFile = __DIR__ . '/../../' . $srcFile;
119
            $absoluteDestFile = \getcwd() . '/' . $destFile;
120
121
            \error_log('SRC FILE: ' . $absoluteSrcFile);
122
            \error_log('DEST FILE: ' . $absoluteDestFile);
123
124
            // @todo Replace YOUR_PROJECT with composer project name
125
            $contents = \file_get_contents($absoluteSrcFile);
126
            \file_put_contents($absoluteDestFile, $contents);
127
128
            $this->taskReport('Copied ' . $absoluteSrcFile . ' to ' . $absoluteDestFile);
129
        }
130
    }
131
132
133
    /**
134
     * Install the composer packages for this task
135
     *
136
     * @return int 0 for no error, otherwise an error code
137
     */
138
    private function installPackages(): int
139
    {
140
        $retVal = 0;
141
142
        $packages = $this->getComposerPackages();
143
        if (\sizeof($packages) > 0) {
144
            foreach ($packages as $package) {
145
                $cmd = 'composer --verbose --profile require --dev ' . $package;
146
                $output = [];
147
                \exec($cmd, $output, $retVal);
148
                $this->taskReport('Installing ' . $package, $retVal);
149
            }
150
        }
151
152
        return $retVal;
153
    }
154
155
156
    private function addScriptsToComposerJSON(): void
157
    {
158
        $composerFile = \getcwd() . '/composer.json';
159
        $contents = \file_get_contents($composerFile);
160
        $json = \json_decode($contents, true);
161
        if (!isset($json['scripts'])) {
162
            $json['scripts'] = [];
163
        }
164
        $scriptsInJSON = $json['scripts'];
165
        $existingScriptNames = \array_keys($scriptsInJSON);
166
        \error_log(\print_r($existingScriptNames, true));
167
168
        $this->climate->info('COmposer file: ' . $composerFile);
169
        /** @var array<string,string> $scripts */
170
        $scripts = $this->getComposerScripts();
171
172
        $this->climate->border();
173
        \error_log(\print_r($scriptsInJSON, true));
174
        $this->climate->border();
175
176
        foreach ($scripts as $scriptName => $bashCode) {
177
            $this->climate->out('Adding script ' . $scriptName . ' --> ' . $bashCode);
178
            if (!\in_array($scriptName, $existingScriptNames, true)) {
179
                $scriptsInJSON[$scriptName] = $bashCode;
180
                ;
181
            } else {
182
                $this->climate->error('Script ' . $scriptName . ' already exists');
183
            }
184
            if (!$this->isCodeCheck() || $scriptName === 'fixcs') {
185
                continue;
186
            }
187
188
            self::$codeCheckCommands[] = 'composer ' . $scriptName;
189
        }
190
191
        $scriptsInJSON['checkCode'] = \implode(' && ', self::$codeCheckCommands);
192
193
        $json['scripts'] = $scriptsInJSON;
194
        $contents = \json_encode($json, \JSON_PRETTY_PRINT);
195
        \file_put_contents($composerFile, $contents);
196
    }
197
}
198