Completed
Push — master ( 3d2943...350704 )
by Basil
02:38
created

RepoController::actionUpdate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 0
1
<?php
2
3
namespace luya\dev;
4
5
use Curl\Curl;
6
use GitWrapper\GitWrapper;
7
use yii\console\widgets\Table;
8
use yii\console\Markdown;
9
use yii\helpers\Console;
10
11
/**
12
 * Dev Env cloning and updating.
13
 *
14
 * Provdes functions to clone and update the repos.
15
 *
16
 * Usage
17
 *
18
 * ```sh
19
 * ./vendor/bin/luyadev repo/init
20
 * ./vendor/bin/luyadev repo/update
21
 * ```
22
 *
23
 * Or clone a custom repo into the repos folder:
24
 *
25
 * ```sh
26
 * ./venodr/bin/luyadev repo/clone luya-module-news luyadev
27
 * ```
28
 *
29
 * @author Basil Suter <[email protected]>
30
 * @since 1.0.1
31
 */
32
class RepoController extends BaseDevCommand
33
{
34
    const CONFIG_VAR_USERNAME = 'username';
35
    
36
    const CONFIG_VAR_CLONETYPE = 'cloneType';
37
    
38
    /**
39
     * @var string Default action is actionInit();
40
     */
41
    public $defaultAction = 'init';
42
    
43
    /**
44
     * @var array The default repos from luyadev
45
     */
46
    public $repos = [
47
        'luya',
48
        'luya-module-admin',
49
        'luya-module-cms',
50
    ];
51
    
52
    public $text = <<<EOT
53
**CLONE REPOS**
54
55
We've detected that you don't have all module repos forked to your account. You can only push changes to the forked repos, all others are **READ ONLY**.
56
57
If you want to work on a specific repo, make sure that repo is forked to your Github account.
58
59
You can also skip this command, fork the repos and rerun this command again.
60
61
**FORK ME**
62
EOT;
63
    
64
    /**
65
     * Initilize the main repos.
66
     *
67
     * @return number
68
     */
69
    public function actionInit()
70
    {
71
        // username
72
        $username = $this->getConfig(self::CONFIG_VAR_USERNAME);
73
        if (!$username) {
74
            $username = $this->prompt('Whats your Github username?');
75
            $this->saveConfig(self::CONFIG_VAR_USERNAME, $username);
76
        }
77
        
78
        // clonetype
79
        $cloneType = $this->getConfig(self::CONFIG_VAR_CLONETYPE);
80
        if (!$cloneType) {
81
            $cloneType = $this->select('Are you connected via ssh or https?', ['ssh' => 'ssh', 'http' => 'http']);
82
            $this->saveConfig(self::CONFIG_VAR_CLONETYPE, $cloneType);
83
        }
84
        
85
        $summary = [];
86
        $itemWithoutFork = false;
87
        
88
        // generate summary overview
89
        foreach ($this->repos as $repo) {
90
            $newRepoHome = $this->getFilesystemRepoPath($repo);
91
            if (file_exists($newRepoHome . DIRECTORY_SEPARATOR . '.git')) {
92
                $summary[] = $this->summaryItem($repo, false, true);
93
            } elseif ($this->forkExists($username, $repo)) {
94
                $summary[] = $this->summaryItem($repo, true, false);
95
            } else {
96
                $itemWithoutFork = true;
97
                $summary[] = $this->summaryItem($repo, false, false);
98
            }
99
        }
100
        
101
        if ($itemWithoutFork) {
102
            Console::clearScreen();
103
            $this->outputInfo($this->markdown($this->text));
104
            foreach ($summary as $sum) {
105
                if (!$sum[2] && !$sum[1]) {
106
                    $this->outputInfo($this->markdown("**{$sum[0]}**: https://github.com/luyadev/{$sum[0]}/fork", true));
107
                }
108
            }
109
            echo (new Table())->setHeaders(['Repo', 'Already initialized', 'Fork exists'])->setRows($summary)->run();
110
            $this->outputError("Repos without fork detected. Those repos will be initialized as READ ONLY. It means you can not push any changes to them.");
111
            
112
            if (!$this->confirm("Continue?")) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->confirm('Continue?') of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
113
                return $this->outputError('Abort by User.');
114
            }
115
        }
116
        
117
        // foreach summary and clone
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
118
        foreach ($summary as $sum) {
119
            $repo = $sum[0];
120
            $hasFork = $sum[2];
121
            $exists = $sum[1];
122
            
123
            // continue already initialized repos.
124
            if ($exists) {
125
                continue;
126
            }
127
            
128
            $newRepoHome = $this->getFilesystemRepoPath($repo);
129
            
130
            if ($hasFork) {
131
                $cloneUrl = ($cloneType == 'ssh') ? "[email protected]:{$username}/{$repo}.git" : "https://github.com/{$username}/{$repo}.git";
132
            } else {
133
                $cloneUrl = ($cloneType == 'ssh') ? "[email protected]:luyadev/{$repo}.git" : "https://github.com/{$username}/{$repo}.git";
134
            }
135
            
136
            $this->cloneRepo($repo, $cloneUrl, $newRepoHome, 'luyadev');
137
        }
138
        
139
        return $this->outputSuccess("init complete.");
140
    }
141
    
142
    /**
143
     * Update all repos to master branch from upstream.
144
     */
145
    public function actionUpdate()
146
    {
147
        $wrapper = new GitWrapper();
148
        
149
        foreach ($this->repos as $repo) {
150
            $wrapper->git('checkout master', 'repos' . DIRECTORY_SEPARATOR . $repo);
151
            $this->outputInfo("{$repo}: checkout master ✔");
152
            
153
            $wrapper->git('fetch upstream', 'repos' . DIRECTORY_SEPARATOR . $repo);
154
            $this->outputInfo("{$repo}: fetch upstream ✔");
155
            
156
            $wrapper->git('rebase upstream/master master', 'repos' . DIRECTORY_SEPARATOR . $repo);
157
            $this->outputInfo("{$repo}: rebase master ✔");
158
        }
159
    }
160
    
161
    /**
162
     *
163
     * @param unknown $repo
164
     * @param unknown $vendor
165
     * @return unknown
166
     */
167
    public function actionClone($vendor = null, $repo = null)
168
    {
169
    	// if `vendor/repo` notation is provided
170
    	if ($vendor !== null && strpos($vendor, '/')) {
171
    		list($vendor, $repo) = explode("/", $vendor);	
172
    	}
173
    	
174
        if (empty($vendor)) {
175
            $vendor = $this->prompt("Enter the username/vendor for this repo (e.g. luyadev)");
176
        }
177
        
178
        if (empty($repo)) {
179
        	$repo = $this->prompt("Enter the name of the repo you like to clone (e.g. luya-module-news)");
180
        }
181
        
182
        return $this->cloneRepo($repo, $this->getCloneUrlBasedOnType($repo, $vendor), $this->getFilesystemRepoPath($repo), $vendor);
183
    }
184
    
185
    private $_gitWrapper;
186
    
187
    /**
188
     * @return \GitWrapper\GitWrapper
189
     */
190
    protected function getGitWrapper()
191
    {
192
        if ($this->_gitWrapper === null) {
193
            $this->_gitWrapper = new GitWrapper();
194
            $this->_gitWrapper->setTimeout(300);
195
        }
196
    
197
        return $this->_gitWrapper;
198
    }
199
    
200
    private function summaryItem($repo, $isFork, $exists)
201
    {
202
        return [$repo, $exists, $isFork];
203
    }
204
    
205
    private function getFilesystemRepoPath($repo)
206
    {
207
        return 'repos' . DIRECTORY_SEPARATOR . $repo;
208
    }
209
    
210
    private function forkExists($username, $repo)
211
    {
212
        return (new Curl())->get('https://api.github.com/repos/'.$username.'/'.$repo)->isSuccess();
213
    }
214
    
215
    private function markdown($text, $paragraph = false)
216
    {
217
        $parser = new Markdown();
218
    
219
        if ($paragraph) {
220
            return $parser->parseParagraph($text);
221
        }
222
    
223
        return $parser->parse($text);
224
    }
225
    
226
    /**
227
     * Return the url to clone based on config clone type (ssh/https).
228
     *
229
     * @param unknown $repo
230
     * @param unknown $username
231
     * @return string
232
     */
233
    private function getCloneUrlBasedOnType($repo, $username)
234
    {
235
        return ($this->getConfig(self::CONFIG_VAR_CLONETYPE) == 'ssh') ? "[email protected]:{$username}/{$repo}.git" : "https://github.com/{$username}/{$repo}.git";
236
    }
237
    
238
    /**
239
     * Clone a repo into the repos folder.
240
     *
241
     * @param string $repo
242
     * @param string $cloneUrl
243
     * @param string $newRepoHome
244
     * @param string $upstreamUsername The upstream vendor name of the repo if available.
245
     */
246
    private function cloneRepo($repo, $cloneUrl, $newRepoHome, $upstreamUsername)
247
    {
248
        $this->outputSuccess("{$repo}: cloning {$cloneUrl} ...");
249
        $this->getGitWrapper()->cloneRepository($cloneUrl, $newRepoHome);
250
        if (!empty($upstreamUsername)) {
251
            $this->getGitWrapper()->git('remote add upstream https://github.com/'.$upstreamUsername.'/'.$repo.'.git', $newRepoHome);
252
            $this->outputInfo("Configure upstream https://github.com/{$upstreamUsername}/{$repo}.git");
253
        }
254
        $this->outputSuccess("{$repo}: ✔ complete");
255
    }
256
}
257