Accurate deployment statuses (#168)

This commit is contained in:
Saeed Vaziry 2024-04-21 16:26:26 +02:00 committed by GitHub
parent e2dd9177f7
commit f0da1c6d8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 88 additions and 35 deletions

View File

@ -6,6 +6,7 @@
use App\Exceptions\DeploymentScriptIsEmptyException; use App\Exceptions\DeploymentScriptIsEmptyException;
use App\Exceptions\SourceControlIsNotConnected; use App\Exceptions\SourceControlIsNotConnected;
use App\Models\Deployment; use App\Models\Deployment;
use App\Models\ServerLog;
use App\Models\Site; use App\Models\Site;
class Deploy class Deploy
@ -37,10 +38,15 @@ public function run(Site $site): Deployment
$deployment->save(); $deployment->save();
dispatch(function () use ($site, $deployment) { dispatch(function () use ($site, $deployment) {
$log = $site->server->os()->runScript($site->path, $site->deploymentScript->content, $site->id); /** @var ServerLog $log */
$deployment->status = DeploymentStatus::FINISHED; $log = ServerLog::make($site->server, 'deploy-'.strtotime('now'))
->forSite($site);
$log->save();
$deployment->log_id = $log->id; $deployment->log_id = $log->id;
$deployment->save(); $deployment->save();
$site->server->os()->runScript($site->path, $site->deploymentScript->content, $log);
$deployment->status = DeploymentStatus::FINISHED;
$deployment->save();
})->catch(function () use ($deployment) { })->catch(function () use ($deployment) {
$deployment->status = DeploymentStatus::FAILED; $deployment->status = DeploymentStatus::FAILED;
$deployment->save(); $deployment->save();

View File

@ -3,6 +3,7 @@
namespace App\Facades; namespace App\Facades;
use App\Models\Server; use App\Models\Server;
use App\Models\ServerLog;
use App\Support\Testing\SSHFake; use App\Support\Testing\SSHFake;
use Illuminate\Support\Facades\Facade as FacadeAlias; use Illuminate\Support\Facades\Facade as FacadeAlias;
@ -10,7 +11,7 @@
* Class SSH * Class SSH
* *
* @method static init(Server $server, string $asUser = null) * @method static init(Server $server, string $asUser = null)
* @method static setLog(string $logType, int $siteId = null) * @method static setLog(?ServerLog $log)
* @method static connect() * @method static connect()
* @method static string exec(string $command, string $log = '', int $siteId = null, ?bool $stream = false) * @method static string exec(string $command, string $log = '', int $siteId = null, ?bool $stream = false)
* @method static string assertExecuted(array|string $commands) * @method static string assertExecuted(array|string $commands)

View File

@ -50,14 +50,9 @@ public function init(Server $server, ?string $asUser = null): self
return $this; return $this;
} }
public function setLog(string $logType, $siteId = null): self public function setLog(ServerLog $log): self
{ {
$this->log = $this->server->logs()->create([ $this->log = $log;
'site_id' => $siteId,
'name' => $this->server->id.'-'.strtotime('now').'-'.$logType.'.log',
'type' => $logType,
'disk' => config('core.logs_disk'),
]);
return $this; return $this;
} }
@ -98,10 +93,18 @@ public function connect(bool $sftp = false): void
*/ */
public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false): string public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false): string
{ {
if ($log) { if (! $this->log && $log) {
$this->setLog($log, $siteId); $this->log = $this->server->logs()->create([
} else { 'site_id' => $siteId,
$this->log = null; 'name' => $this->server->id.'-'.strtotime('now').'-'.$log.'.log',
'type' => $log,
'disk' => config('core.logs_disk'),
]);
$this->log = ServerLog::make($this->server, $log);
if ($siteId) {
$this->log->forSite($siteId);
}
$this->log->save();
} }
try { try {
@ -132,8 +135,8 @@ public function exec(string $command, string $log = '', ?int $siteId = null, ?bo
$this->log?->write($output); $this->log?->write($output);
if (Str::contains($output, 'VITO_SSH_ERROR')) { if ($this->connection->getExitStatus() !== 0 || Str::contains($output, 'VITO_SSH_ERROR')) {
throw new SSHCommandError('SSH command failed with an error'); throw new SSHCommandError('SSH command failed with an error', $this->connection->getExitStatus());
} }
return $output; return $output;

View File

@ -14,17 +14,17 @@ class HandleSSHErrors
public function handle(Request $request, Closure $next) public function handle(Request $request, Closure $next)
{ {
$res = $next($request); $res = $next($request);
if ($res instanceof Response && $res->exception) { // if ($res instanceof Response && $res->exception) {
if ($res->exception instanceof SSHConnectionError || $res->exception instanceof SSHCommandError) { // if ($res->exception instanceof SSHConnectionError || $res->exception instanceof SSHCommandError) {
Toast::error($res->exception->getMessage()); // Toast::error($res->exception->getMessage());
if ($request->hasHeader('HX-Request')) { // if ($request->hasHeader('HX-Request')) {
return htmx()->back(); // return htmx()->back();
} // }
return back(); // return back();
} // }
} // }
return $res; return $res;
} }

View File

@ -115,4 +115,27 @@ public static function log(Server $server, string $type, string $content, ?Site
$log->save(); $log->save();
$log->write($content); $log->write($content);
} }
public static function make(Server $server, string $type): ServerLog
{
return new static([
'server_id' => $server->id,
'name' => $server->id.'-'.strtotime('now').'-'.$type.'.log',
'type' => $type,
'disk' => config('core.logs_disk'),
]);
}
public function forSite(Site|int $site): ServerLog
{
if ($site instanceof Site) {
$site = $site->id;
}
if (is_int($site)) {
$this->site_id = $site;
}
return $this;
}
} }

View File

@ -3,6 +3,7 @@
namespace App\Models; namespace App\Models;
use App\Exceptions\SourceControlIsNotConnected; use App\Exceptions\SourceControlIsNotConnected;
use App\Exceptions\SSHError;
use App\SiteTypes\SiteType; use App\SiteTypes\SiteType;
use App\SSH\Services\Webserver\Webserver; use App\SSH\Services\Webserver\Webserver;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -271,6 +272,10 @@ public function hasFeature(string $feature): bool
public function getEnv(): string public function getEnv(): string
{ {
return $this->server->os()->readFile($this->path.'/.env'); try {
return $this->server->os()->readFile($this->path.'/.env');
} catch (SSHError) {
return '';
}
} }
} }

View File

@ -127,16 +127,18 @@ public function tail(string $path, int $lines): string
); );
} }
public function runScript(string $path, string $script, ?int $siteId = null): ServerLog public function runScript(string $path, string $script, ?ServerLog $serverLog): ServerLog
{ {
$ssh = $this->server->ssh(); $ssh = $this->server->ssh();
if ($serverLog) {
$ssh->setLog($serverLog);
}
$ssh->exec( $ssh->exec(
$this->getScript('run-script.sh', [ $this->getScript('run-script.sh', [
'path' => $path, 'path' => $path,
'script' => $script, 'script' => $script,
]), ]),
'run-script', 'run-script'
$siteId
); );
return $ssh->log; return $ssh->log;

View File

@ -5,6 +5,7 @@
use App\Models\Metric; use App\Models\Metric;
use App\SSH\HasScripts; use App\SSH\HasScripts;
use App\SSH\Services\AbstractService; use App\SSH\Services\AbstractService;
use Closure;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
@ -21,7 +22,12 @@ public function creationRules(array $input): array
{ {
return [ return [
'type' => [ 'type' => [
Rule::unique('services', 'type')->where('server_id', $this->service->server_id), function (string $attribute, mixed $value, Closure $fail) {
$monitoringExists = $this->service->server->monitoring();
if ($monitoringExists) {
$fail('You already have a monitoring service on the server.');
}
},
], ],
'version' => [ 'version' => [
'required', 'required',

View File

@ -36,10 +36,13 @@ public function connect(bool $sftp = false): void
public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false): string public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false): string
{ {
if ($log) { if (! $this->log && $log) {
$this->setLog($log, $siteId); $this->log = $this->server->logs()->create([
} else { 'site_id' => $siteId,
$this->log = null; 'name' => $this->server->id.'-'.strtotime('now').'-'.$log.'.log',
'type' => $log,
'disk' => config('core.logs_disk'),
]);
} }
$this->commands[] = $command; $this->commands[] = $command;

View File

@ -19,6 +19,10 @@ class="p-6"
{{ __("Install Vito Agent") }} {{ __("Install Vito Agent") }}
</h2> </h2>
@error("type")
<x-input-error class="mt-2" :messages="$message" />
@enderror
<div class="mt-6"> <div class="mt-6">
<x-alert-warning> <x-alert-warning>
Vito Agent is only works if you are running your Vito instance on a cloud not local! Consider Vito Agent is only works if you are running your Vito instance on a cloud not local! Consider

View File

@ -49,7 +49,7 @@ public function test_update_deployment_script()
public function test_deploy(): void public function test_deploy(): void
{ {
SSH::fake(); SSH::fake('fake output');
Http::fake([ Http::fake([
'github.com/*' => Http::response([ 'github.com/*' => Http::response([
'sha' => '123', 'sha' => '123',