add auto-deployment (#71)

add update source-control to site-settings
This commit is contained in:
Saeed Vaziry
2023-10-29 22:20:15 +01:00
committed by GitHub
parent 700cc5f44c
commit 1bf3c94358
33 changed files with 384 additions and 126 deletions

View File

@ -0,0 +1,35 @@
<?php
namespace App\Actions\Site;
use App\Models\Site;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
class UpdateSourceControl
{
/**
* @throws ValidationException
*/
public function update(Site $site, array $input): void
{
$this->validate($input);
$site->source_control_id = $input['source_control'];
$site->save();
}
/**
* @throws ValidationException
*/
protected function validate(array $input): void
{
Validator::make($input, [
'source_control' => [
'required',
Rule::exists('source_controls', 'id'),
],
])->validate();
}
}

View File

@ -11,6 +11,4 @@ final class SourceControl extends Enum
const GITLAB = 'gitlab';
const BITBUCKET = 'bitbucket';
const CUSTOM = 'custom';
}

View File

@ -7,7 +7,7 @@
class SourceControlIsNotConnected extends Exception
{
public function __construct(protected SourceControl|string $sourceControl, string $message = null)
public function __construct(protected SourceControl|string|null $sourceControl, string $message = null)
{
parent::__construct($message ?? 'Source control is not connected');
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers;
use App\Exceptions\SourceControlIsNotConnected;
use App\Models\GitHook;
use Illuminate\Http\Request;
use Throwable;
class GitHookController extends Controller
{
public function __invoke(Request $request)
{
if (! $request->input('secret')) {
abort(404);
}
/** @var GitHook $gitHook */
$gitHook = GitHook::query()
->where('secret', $request->input('secret'))
->firstOrFail();
foreach ($gitHook->actions as $action) {
if ($action == 'deploy') {
try {
$gitHook->site->deploy();
} catch (SourceControlIsNotConnected) {
// TODO: send notification
} catch (Throwable $e) {
Log::error('git-hook-exception', (array) $e);
}
}
}
return response()->json([
'success' => true,
]);
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Http\Livewire\Application;
use App\Exceptions\SourceControlIsNotConnected;
use App\Models\Site;
use App\Traits\HasToast;
use App\Traits\RefreshComponentOnBroadcast;
use Illuminate\Contracts\View\View;
use Livewire\Component;
use Throwable;
class AutoDeployment extends Component
{
use RefreshComponentOnBroadcast;
use HasToast;
public Site $site;
/**
* @throws Throwable
*/
public function enable(): void
{
if (! $this->site->auto_deployment) {
try {
$this->site->enableAutoDeployment();
$this->site->refresh();
$this->toast()->success(__('Auto deployment has been enabled.'));
} catch (SourceControlIsNotConnected) {
$this->toast()->error(__('Source control is not connected. Check site\'s settings.'));
}
}
}
/**
* @throws Throwable
*/
public function disable(): void
{
if ($this->site->auto_deployment) {
try {
$this->site->disableAutoDeployment();
$this->site->refresh();
$this->toast()->success(__('Auto deployment has been disabled.'));
} catch (SourceControlIsNotConnected) {
$this->toast()->error(__('Source control is not connected. Check site\'s settings.'));
}
}
}
public function render(): View
{
return view('livewire.application.auto-deployment');
}
}

View File

@ -28,6 +28,7 @@ public function save(): void
session()->flash('status', 'script-updated');
$this->emitTo(Deploy::class, '$refresh');
$this->emitTo(AutoDeployment::class, '$refresh');
}
public function render(): View

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Livewire\Sites;
use App\Actions\Site\UpdateSourceControl;
use App\Models\Site;
use Illuminate\Contracts\View\View;
use Livewire\Component;
class UpdateSourceControlProvider extends Component
{
public Site $site;
public ?int $source_control = null;
public function update(): void
{
app(UpdateSourceControl::class)->update($this->site, $this->all());
session()->flash('status', 'source-control-updated');
}
public function render(): View
{
if (! $this->source_control) {
$this->source_control = $this->site->source_control_id;
}
return view('livewire.sites.update-source-control-provider');
}
}

View File

@ -2,9 +2,9 @@
namespace App\Models;
use App\Exceptions\FailedToDeployGitHook;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\DB;
use Throwable;
@ -22,6 +22,8 @@
*/
class GitHook extends AbstractModel
{
use HasFactory;
protected $fillable = [
'site_id',
'source_control_id',
@ -55,9 +57,6 @@ public function scopeHasEvent(Builder $query, string $event): Builder
return $query->where('events', 'like', "%\"{$event}\"%");
}
/**
* @throws FailedToDeployGitHook
*/
public function deployHook(): void
{
$this->update(

View File

@ -6,7 +6,6 @@
use App\Enums\DeploymentStatus;
use App\Enums\SiteStatus;
use App\Enums\SslStatus;
use App\Exceptions\FailedToDeployGitHook;
use App\Exceptions\SourceControlIsNotConnected;
use App\Jobs\Site\ChangePHPVersion;
use App\Jobs\Site\Deploy;
@ -19,7 +18,7 @@
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Str;
use Throwable;
/**
@ -341,22 +340,16 @@ public function getWebDirectoryPathAttribute(): string
/**
* @throws SourceControlIsNotConnected
* @throws ValidationException
* @throws FailedToDeployGitHook
* @throws Throwable
*/
public function enableAutoDeployment(): void
{
if ($this->gitHook) {
throw ValidationException::withMessages([
'auto_deployment' => __('Auto deployment already enabled'),
])->errorBag('auto_deployment');
return;
}
if (! $this->sourceControl()) {
throw ValidationException::withMessages([
'auto_deployment' => __('Your application does not use any source controls'),
])->errorBag('auto_deployment');
throw new SourceControlIsNotConnected($this->source_control);
}
try {
@ -364,7 +357,7 @@ public function enableAutoDeployment(): void
$gitHook = new GitHook([
'site_id' => $this->id,
'source_control_id' => $this->sourceControl()->id,
'secret' => generate_uid(),
'secret' => Str::uuid()->toString(),
'actions' => ['deploy'],
'events' => ['push'],
]);

View File

@ -46,7 +46,7 @@ public function deployHook(string $repo, array $events, string $secret): array
{
$response = Http::withToken($this->sourceControl->access_token)->post($this->apiUrl."/repositories/$repo/hooks", [
'description' => 'deploy',
'url' => url('/git-hooks?secret='.$secret),
'url' => url('/api/git-hooks?secret='.$secret),
'events' => [
'repo:'.implode(',', $events),
],

View File

@ -1,41 +0,0 @@
<?php
namespace App\SourceControlProviders;
class Custom extends AbstractSourceControlProvider
{
public function connect(): bool
{
return true;
}
public function getRepo(string $repo = null): string
{
return '';
}
public function fullRepoUrl(string $repo, string $key): string
{
return $repo;
}
public function deployHook(string $repo, array $events, string $secret): array
{
return [];
}
public function destroyHook(string $repo, string $hookId): void
{
// TODO: Implement destroyHook() method.
}
public function getLastCommit(string $repo, string $branch): ?array
{
return null;
}
public function deployKey(string $title, string $repo, string $key): void
{
// TODO: Implement deployKey() method.
}
}

View File

@ -59,7 +59,7 @@ public function deployHook(string $repo, array $events, string $secret): array
'name' => 'web',
'events' => $events,
'config' => [
'url' => url('/git-hooks?secret='.$secret),
'url' => url('/api/git-hooks?secret='.$secret),
'content_type' => 'json',
],
'active' => true,

View File

@ -53,7 +53,7 @@ public function deployHook(string $repo, array $events, string $secret): array
$this->getApiUrl().'/projects/'.$repository.'/hooks',
[
'description' => 'deploy',
'url' => url('/git-hooks?secret='.$secret),
'url' => url('/api/git-hooks?secret='.$secret),
'push_events' => in_array('push', $events),
'issues_events' => false,
'job_events' => false,