Passed
Push — master ( 444806...d96591 )
by Nicolaas
02:06
created

ComposerCompatibilityCheckerStep3::getTitle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 5
c 1
b 1
f 0
dl 0
loc 8
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
namespace Sunnysideup\UpgradeToSilverstripe4\Tasks\IndividualTasks;
4
5
use Sunnysideup\UpgradeToSilverstripe4\Api\FileSystemFixes;
6
use Sunnysideup\UpgradeToSilverstripe4\Tasks\Task;
7
8
//use either of the following to create the info.json file required
9
//your project will also require a composer.json.default file
10
//this file is used to reset the project to the default state before attempting to install each library
11
//composer info --format=json > info.json
12
//composer info --direct --format=json > info.json
13
14
class ComposerCompatibilityCheckerStep3 extends Task
15
{
16
    protected $taskStep = 's10';
17
18
    protected $infoFileFileName = 'composer-requirements-info.json';
19
20
    protected $resultsFileAsJSON = 'composer-requirements-info.upgraded.json';
21
22
    protected $lessUpgradeIsBetter = false;
23
24
    private $outputArray = [];
25
26
    private $firstTimeReset = true;
27
28
    private $webRootLocation = '';
29
30
    public function getTitle()
31
    {
32
        if ($this->mu()->getIsModuleUpgrade()) {
0 ignored issues
show
Bug introduced by
The method getIsModuleUpgrade() does not exist on Sunnysideup\UpgradeToSilverstripe4\ModuleUpgrader. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

32
        if ($this->mu()->/** @scrutinizer ignore-call */ getIsModuleUpgrade()) {
Loading history...
Bug introduced by
It seems like getIsModuleUpgrade() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

32
        if ($this->mu()->/** @scrutinizer ignore-call */ getIsModuleUpgrade()) {
Loading history...
33
            return 'For moduls upgrades, this is not being used right now.';
34
        }
35
        return 'Check what composer requirements packages are best to use, using the '
36
            . $this->getJsonFileLocation() . ' file and placing results in '
37
            . $this->getJsonFileLocationJSONResults();
38
    }
39
40
    public function getDescription()
41
    {
42
        return '
43
            THIS IS STILL UNDER CONSTRUCTION!
44
            ';
45
    }
46
47
    /**
48
     * @param array $params
49
     * @return string|null
50
     */
51
    public function runActualTask($params = [])
52
    {
53
        if ($this->mu()->getIsModuleUpgrade()) {
54
            return null;
55
        }
56
        $this->webRootLocation = $this->mu()->getWebRootDirLocation();
0 ignored issues
show
Bug introduced by
The method getWebRootDirLocation() does not exist on Sunnysideup\UpgradeToSilverstripe4\ModuleUpgrader. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

56
        $this->webRootLocation = $this->mu()->/** @scrutinizer ignore-call */ getWebRootDirLocation();
Loading history...
Bug introduced by
It seems like getWebRootDirLocation() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

56
        $this->webRootLocation = $this->mu()->/** @scrutinizer ignore-call */ getWebRootDirLocation();
Loading history...
57
58
        file_put_contents($this->getJsonFileLocationJSONResults(), '');
59
60
        $jsonFile = file_get_contents($this->getJsonFileLocation());
61
        $jsonData = json_decode($jsonFile, true);
62
63
        $libraries = $jsonData['installed'] ?? [];
64
65
        $this->resetProject();
66
        foreach ($libraries as $library) {
67
            $commit = '';
68
            $name = $library['name'];
69
            $version = $library['version'];
70
            if (strpos($version, 'dev-master') !== false) {
71
                $commit = $version;
72
                $version = str_replace(' ', '#', $version);
73
            }
74
            unset($libraryOutput);
75
            $libraryOutput = $this->mu()->execMe(
76
                $this->webRootLocation,
77
                'composer require ' . $name . " '" . $version . "' 2>&1 --no-interaction",
78
                'adding module'
79
            );
80
            $message = 'composer require ' . $name . ':' . $version . ' ... ';
81
            if (in_array('  [InvalidArgumentException]', $libraryOutput, true)) {
82
                $message .= "unsuccessful, could not find a matching version of package.\n";
83
                $this->mu()->colourPrint($message);
84
            } elseif (in_array('Installation failed, reverting ./composer.json to its original content.', $libraryOutput, true)) {
85
                $message .= "unsuccessful, searching for next best version.\n";
86
                $this->mu()->colourPrint($message);
87
                unset($show);
88
                $show = $this->mu()->execMe(
89
                    $this->webRootLocation,
90
                    'composer show -a ' . $name . ' --no-interaction 2>&1 ',
91
                    'show details of module'
92
                );
93
                $versionsString = $show[3];
94
                $versionsString = str_replace('versions : ', '', $versionsString);
95
                $currentVersionPos = strpos($versionsString, ', ' . $version);
96
                $versionsString = substr($versionsString, 0, $currentVersionPos);
97
                $newerVersions = explode(', ', $versionsString);
98
                if ($this->lessUpgradeIsBetter) {
99
                    $newerVersions = array_reverse($newerVersions);
100
                }
101
                $output = 0;
102
                $versionFound = false;
103
                foreach ($newerVersions as $newVersion) {
104
                    unset($output);
105
                    $output = $this->mu()->execMe(
106
                        $this->webRootLocation,
107
                        'composer require ' . $name . " '" . $newVersion . "' 2>&1 ",
108
                        'show details of module',
109
                        false
110
                    );
111
                    $message = 'composer require ' . $name . ':' . $newVersion . ' ...... ';
112
                    if (! in_array('Installation failed', $output, true)) {
113
                        $versionFound = true;
114
                        $message .= "successful!, it is the next best version.\n";
115
                        $this->mu()->colourPrint($message);
116
                        $this->addToOutputArray($name, $newVersion);
117
                        break;
118
                    }
119
                    $message .= "unsuccessful, searching for next best version.\n";
120
                    $this->mu()->colourPrint($message, false);
121
                }
122
123
                if (! $versionFound) {
124
                    $message = 'Could not find any compatible versions for:  ' . $name . "!'\n ";
125
                    $this->mu()->colourPrint($message);
126
                }
127
            } else {
128
                $message .= "successful!\n ";
129
                $this->mu()->colourPrint($message);
130
                $version = $commit ?: $version;
131
                $this->addToOutputArray($name, $version);
132
            }
133
        }
134
135
        file_put_contents(
136
            $this->getJsonFileLocationJSONResults(),
137
            json_encode($this->outputArray),
138
            FILE_APPEND | LOCK_EX
139
        );
140
141
        return null;
142
    }
143
144
    protected function resetProject()
145
    {
146
        $this->mu()->colourPrint('resetting project to composer.json.default', false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $colour of Sunnysideup\UpgradeToSil...Upgrader::colourPrint(). ( Ignorable by Annotation )

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

146
        $this->mu()->colourPrint('resetting project to composer.json.default', /** @scrutinizer ignore-type */ false);
Loading history...
Bug introduced by
It seems like colourPrint() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

146
        $this->mu()->/** @scrutinizer ignore-call */ colourPrint('resetting project to composer.json.default', false);
Loading history...
147
        if ($this->firstTimeReset === true) {
148
            $this->mu()->execMe(
0 ignored issues
show
Bug introduced by
It seems like execMe() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

148
            $this->mu()->/** @scrutinizer ignore-call */ execMe(
Loading history...
149
                $this->webRootLocation,
150
                'cp composer.json composer.json.temp.default',
151
                'make temporary copy of composer.json'
152
            );
153
        }
154
        $fixer = FileSystemFixes::inst($this->mu())
0 ignored issues
show
Unused Code introduced by
The assignment to $fixer is dead and can be removed.
Loading history...
155
            ->removeDirOrFile($this->webRootLocation . '/composer.json');
156
        $this->mu()->execMe(
157
            $this->webRootLocation,
158
            'cp composer.json.temp.default composer.json',
159
            'back to default composer file'
160
        );
161
        if ($this->firstTimeReset === false) {
162
            $this->mu()->execMe(
163
                $this->webRootLocation,
164
                'composer update',
165
                'run composer update'
166
            );
167
        }
168
        $this->firstTimeReset = false;
169
    }
170
171
    protected function addToOutputArray($name, $version)
172
    {
173
        $pos = strpos($name, '/') + 1;
174
        $array['folder'] = substr($name, $pos);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$array was never initialized. Although not strictly required by PHP, it is generally a good practice to add $array = array(); before regardless.
Loading history...
175
        $array['tag'] = $version;
176
        $array['repo'] = null;
177
        unset($strings);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $strings seems to be never defined.
Loading history...
178
        $strings = $this->mu()->execMe(
179
            $this->webRootLocation,
180
            'composer show -a ' . $name . ' 2>&1 ',
181
            'run composer update'
182
        );
183
        $source = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $source is dead and can be removed.
Loading history...
184
185
        foreach ($strings as $string) {
186
            if (strpos($string, 'source') !== false) {
187
                $source = $string;
188
                preg_match_all('#\bhttps?://[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/))#', $source, $match);
189
                if (! isset($match[0][0])) {
190
                    preg_match_all(
191
                        '#((git|ssh|http(s)?)|(git@[\w\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git)(/)?#',
192
                        $source,
193
                        $match
194
                    );
195
                }
196
                if (isset($match[0][0])) {
197
                    $array['repo'] = $match[0][0];
198
                }
199
                break;
200
            }
201
        }
202
203
        array_push($this->outputArray, $array);
204
    }
205
206
    protected function getJsonFileLocation(): string
207
    {
208
        return $this->mu()->getWebRootDirLocation() . '/' . $this->infoFileFileName;
209
    }
210
211
    protected function getJsonFileLocationJSONResults(): string
212
    {
213
        return $this->mu()->getWebRootDirLocation() . '/' . $this->resultsFileAsJSON;
214
    }
215
216
    protected function hasCommitAndPush()
217
    {
218
        return false;
219
    }
220
}
221