Issues (21)

src/Controller/ProjectController.php (3 issues)

Labels
Severity
1
<?php
2
3
namespace App\Controller;
4
5
use App\Controller\Traits\ProjectTrait;
6
use App\Facades\Log;
7
use App\Facades\Provider;
8
use App\Facades\Router;
9
use App\Facades\Security;
10
use App\Facades\Session;
11
use App\Facades\View;
12
use App\Model\Deployment;
13
use App\Model\Event;
14
use App\Model\Project;
15
use App\Queue\DeployJob;
16
use App\Queue\ReactivateJob;
17
use Exception;
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Http\Message\ServerRequestInterface;
20
use Ronanchilvers\Foundation\Facade\Queue;
21
use Ronanchilvers\Foundation\Queue\Exception\FailedDispatchException;
22
use Ronanchilvers\Orm\Orm;
23
use Ronanchilvers\Utility\Str;
24
use RuntimeException;
25
26
/**
27
 * Controller for the index
28
 *
29
 * @author Ronan Chilvers <[email protected]>
30
 */
31
class ProjectController
32
{
33
    use ProjectTrait;
34
35
    /**
36
     * Index action
37
     *
38
     * @author Ronan Chilvers <[email protected]>
39
     */
40
    public function index(
41
        ServerRequestInterface $request,
42
        ResponseInterface $response
43
    ) {
44
        $user = Security::user();
45
        $all = Orm::finder(Project::class)->all();
46
47
        $userFavourites = $user->preference('favourites', []);
48
        $favourites = $projects = [];
49
        foreach ($all as $project) {
50
            if (in_array($project->id, $userFavourites)) {
51
                $favourites[] = $project;
52
                continue;
53
            }
54
            $projects[] = $project;
55
        }
56
57
        return View::render(
58
            $response,
59
            'project/index.html.twig',
60
            [
61
                'favourites' => $favourites,
62
                'projects'   => $projects
63
            ]
64
        );
65
    }
66
67
    /**
68
     * View a project dashboard
69
     *
70
     * @author Ronan Chilvers <[email protected]>
71
     */
72
    public function view(
73
        ServerRequestInterface $request,
74
        ResponseInterface $response,
75
        $args
76
    ) {
77
        if (!$project = $this->projectFromArgs($args)) {
78
            return $response->withRedirect(
0 ignored issues
show
The method withRedirect() does not exist on Psr\Http\Message\ResponseInterface. It seems like you code against a sub-type of Psr\Http\Message\ResponseInterface such as Slim\Http\Response. ( Ignorable by Annotation )

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

78
            return $response->/** @scrutinizer ignore-call */ withRedirect(
Loading history...
79
                Router::pathFor('project.index')
80
            );
81
        }
82
        $finder = Orm::finder(Deployment::class);
83
        $deployments = $finder->forProject($project);
84
85
        $selected_number = $request->getQueryParam(
0 ignored issues
show
The method getQueryParam() does not exist on Psr\Http\Message\ServerRequestInterface. Did you maybe mean getQueryParams()? ( Ignorable by Annotation )

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

85
        /** @scrutinizer ignore-call */ 
86
        $selected_number = $request->getQueryParam(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
86
            'deployment',
87
            (0 < count($deployments)) ? $deployments[0]->number : false
88
        );
89
        $selectedDeployment = (0 < count($deployments)) ? $deployments[0] : false;
90
        foreach ($deployments as $deployment) {
91
            if ($deployment->number == $selected_number) {
92
                $selectedDeployment = $deployment;
93
                break;
94
            }
95
        }
96
        $events = [];
97
        if ($selectedDeployment) {
98
            $events = Orm::finder(Event::class)->arrayForDeploymentId(
99
                $selectedDeployment->id
100
            );
101
            // $events = $selectedDeployment->events;
102
        }
103
104
        return View::render(
105
            $response,
106
            'project/view.html.twig',
107
            [
108
                'project'             => $project,
109
                'deployments'         => $deployments,
110
                'selected_deployment' => $selectedDeployment,
111
                'events'              => $events,
112
            ]
113
        );
114
    }
115
116
    /**
117
     * Add a project
118
     *
119
     * @author Ronan Chilvers <[email protected]>
120
     */
121
    public function add(
122
        ServerRequestInterface $request,
123
        ResponseInterface $response
124
    ) {
125
        $project = new Project;
126
        if ('POST' == $request->getMethod()) {
127
            $data = $request->getParsedBody()['project'];
128
            $project->fromArray($data);
129
            if ($project->saveWithValidation()) {
130
                Session::flash([
131
                    'heading' => 'Project added'
132
                ], 'success');
133
                return $response->withRedirect(
134
                    Router::pathFor('project.view', ['key' => $project->key])
135
                );
136
            }
137
            Log::debug('Project add failed', [
138
                'errors' => $project->getErrors()
139
            ]);
140
        }
141
142
        return View::render(
143
            $response,
144
            'project/add.html.twig',
145
            [
146
                'project' => $project,
147
                'providers' => Provider::getOptions(),
148
            ]
149
        );
150
    }
151
152
    /**
153
     * Edit action
154
     *
155
     * @author Ronan Chilvers <[email protected]>
156
     */
157
    public function edit(
158
        ServerRequestInterface $request,
159
        ResponseInterface $response,
160
        $args
161
    ) {
162
        if (!$project = $this->projectFromArgs($args)) {
163
            return $response->withRedirect(
164
                Router::pathFor('project.index')
165
            );
166
        }
167
        if ('POST' == $request->getMethod()) {
168
            $data = $request->getParsedBody()['project'];
169
            $project->fromArray($data);
170
            if ($project->saveWithValidation()) {
171
                Session::flash([
172
                    'heading' => 'Project saved'
173
                ]);
174
                return $response->withRedirect(
175
                    Router::pathFor('project.edit', [
176
                        'key' => $project->key
177
                    ])
178
                );
179
            }
180
        }
181
182
        return View::render(
183
            $response,
184
            'project/edit.html.twig',
185
            [
186
                'project' => $project,
187
                'providers' => Provider::getOptions(),
188
            ]
189
        );
190
    }
191
192
    /**
193
     * Prepare a deployment
194
     *
195
     * @author Ronan Chilvers <[email protected]>
196
     */
197
    public function prepareDeploy(
198
        ServerRequestInterface $request,
199
        ResponseInterface $response,
200
        $args
201
    ) {
202
        if (!$project = $this->projectFromArgs($args)) {
203
            return $response->withRedirect(
204
                Router::pathFor('project.index')
205
            );
206
        }
207
        $provider = Provider::forProject($project);
208
        $tagsBranches = $provider->getTagsAndBranches($project->repository);
209
210
        return View::render(
211
            $response,
212
            'project/prepare-deploy.html.twig',
213
            [
214
                'project'       => $project,
215
                'tags_branches' => $tagsBranches,
216
            ]
217
        );
218
    }
219
220
    /**
221
     * Trigger a deploy for a project
222
     *
223
     * @author Ronan Chilvers <[email protected]>
224
     */
225
    public function deploy(
226
        ServerRequestInterface $request,
227
        ResponseInterface $response,
228
        $args
229
    ) {
230
        try {
231
            if (!$project = $this->projectFromArgs($args)) {
232
                return $response->withRedirect(
233
                    Router::pathFor('project.index')
234
                );
235
            }
236
            if (!$project->isDeployable()) {
237
                throw new RuntimeException('Project is not deployable at the moment');
238
            }
239
            $input  = $request->getParsedBodyParam('project', []);
0 ignored issues
show
The method getParsedBodyParam() does not exist on Psr\Http\Message\ServerRequestInterface. Did you maybe mean getParsedBody()? ( Ignorable by Annotation )

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

239
            /** @scrutinizer ignore-call */ 
240
            $input  = $request->getParsedBodyParam('project', []);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
240
            $branch = (!isset($input['branch']) || empty($input['branch'])) ? $project->branch : $input['branch'];
241
            $provider = Provider::forProject(
242
                $project
243
            );
244
            $finder = Orm::finder(Event::class);
245
            Orm::transaction(function() use ($project, $provider, $branch, $response, $finder) {
246
                try {
247
                    $deployment = Orm::finder(Deployment::class)->nextForProject(
248
                        $project
249
                    );
250
                    $deployment->source = Security::email();
251
                    if (!$deployment->save()) {
252
                        Log::debug('Unable to create new deployment object', [
253
                            'project' => $project->toArray(),
254
                        ]);
255
                        throw new RuntimeException('Unable to create new deployment');
256
                    }
257
                    $finder->event(
258
                        'info',
259
                        $deployment,
260
                        'Initialise',
261
                        sprintf("Querying %s for head commit data", $provider->getLabel())
262
                    );
263
                    $head = $provider->getHeadInfo($project->repository, $branch);
264
                    $finder->event(
265
                        'info',
266
                        $deployment,
267
                        'Initialise',
268
                        "Commit data : " . json_encode($head, JSON_PRETTY_PRINT)
269
                    );
270
                    Log::debug('Updating deployment commit information', $head);
271
                    $deployment->branch    = $branch;
272
                    $deployment->sha       = $head['sha'];
273
                    $deployment->author    = $head['author'];
274
                    $deployment->committer = $head['committer'];
275
                    $deployment->message   = $head['message'];
276
                    if (!$deployment->save()) {
277
                        return $response->withRedirect(
278
                            Router::pathFor('project.view', [
279
                                'key' => $project->key
280
                            ])
281
                        );
282
                    }
283
                    if (!$project->markDeploying()) {
284
                        throw new RuntimeException('Unable to mark project as deploying');
285
                    }
286
                    Queue::dispatch(
287
                        new DeployJob($deployment)
288
                    );
289
                } catch (Exception $ex) {
290
                    if (isset($deployment) && $deployment instanceof Deployment) {
291
                        $finder->event(
292
                            'error',
293
                            $deployment,
294
                            'Initialise',
295
                            $ex->getMessage()
296
                        );
297
                    }
298
                    throw $ex;
299
                }
300
            });
301
302
            Session::flash([
303
                'heading' => 'Deploy queued successfully'
304
            ]);
305
        } catch (Exception $ex) {
306
            $message = [$ex->getMessage()];
307
            if ($previous = $ex->getPrevious()) {
308
                $message[] = $previous->getMessage();
309
            }
310
            $message = implode(' - ', $message);
311
            Session::flash(
312
                [
313
                    'heading' => 'Failed to initialise new deployment',
314
                    'content' => get_class($ex) . ' : ' . $message,
315
                ],
316
                'error'
317
            );
318
            Log::error('Failed to initialise new deployment', [
319
                'exception' => $ex,
320
            ]);
321
        }
322
323
        return $response->withRedirect(
324
            Router::pathFor('project.view', [
325
                'key' => $project->key
326
            ])
327
        );
328
    }
329
330
    /**
331
     * Trigger a deploy for a project
332
     *
333
     * @author Ronan Chilvers <[email protected]>
334
     */
335
    public function redeploy(
336
        ServerRequestInterface $request,
337
        ResponseInterface $response,
338
        $args
339
    ) {
340
        try {
341
            if (!$project = $this->projectFromArgs($args)) {
342
                return $response->withRedirect(
343
                    Router::pathFor('project.index')
344
                );
345
            }
346
            Orm::transaction(function() use ($project, $args) {
347
                $dummy = Orm::finder(Deployment::class)->nextForProject(
348
                    $project
349
                );
350
                $original = Orm::finder(Deployment::class)->one(
351
                    $args['deployment']
352
                );
353
                if (!$original instanceof Deployment) {
354
                    throw new RuntimeException('Invalid attempt to re-deploy non-existant deployment');
355
                }
356
                $deployment           = clone $original;
357
                $deployment->original = $original;
358
                $deployment->number   = $dummy->number;
359
                if (!$deployment->save()) {
360
                    Log::debug('Unable to create deployment object', [
361
                        'project' => $project->toArray(),
362
                    ]);
363
                    throw new RuntimeException('Unable to create new deployment');
364
                }
365
                if (!$project->markDeploying()) {
366
                    throw new RuntimeException('Unable to mark project as deploying');
367
                }
368
                Queue::dispatch(
369
                    new ReactivateJob($original, $deployment)
370
                );
371
            });
372
            Session::flash([
373
                'heading' => 'Re-deploy queued successfully'
374
            ]);
375
        } catch (Exception $ex) {
376
            Session::flash(
377
                [
378
                    'heading' => 'Failed to initialise re-deployment',
379
                    'content' => $ex->getMessage(),
380
                ],
381
                'error'
382
            );
383
            Log::error('Failed to initialise re-deployment', [
384
                'exception' => $ex,
385
            ]);
386
        }
387
388
        return $response->withRedirect(
389
            Router::pathFor('project.view', [
390
                'key' => $project->key
391
            ])
392
        );
393
    }
394
}
395