style updates

This commit is contained in:
Mark Gregory
2026-07-24 14:59:44 +01:00
parent 24ab771cd3
commit dd23205d0b
482 changed files with 7173 additions and 1435 deletions
@@ -1,6 +1,22 @@
<?php
namespace Modules\CloudDevOps\Console\Commands;
use Illuminate\Console\Command;
use Modules\CloudDevOps\Models\DeploymentHealthCheck;
use Modules\CloudDevOps\Services\HealthCheckRunner;
final class CloudDevOpsHealthCommand extends Command { protected $signature='cloud-devops:health'; protected $description='Run all active deployment health checks'; public function handle(HealthCheckRunner $runner): int { DeploymentHealthCheck::query()->each(fn($check)=>$runner->run($check)); $this->info('Health checks completed.'); return self::SUCCESS; } }
final class CloudDevOpsHealthCommand extends Command
{
protected $signature = 'cloud-devops:health';
protected $description = 'Run all active deployment health checks';
public function handle(HealthCheckRunner $runner): int
{
DeploymentHealthCheck::query()->each(fn ($check) => $runner->run($check));
$this->info('Health checks completed.');
return self::SUCCESS;
}
}
@@ -1,5 +1,22 @@
<?php
namespace Modules\CloudDevOps\Console\Commands;
use Illuminate\Console\Command;
use Modules\CloudDevOps\Models\DeploymentRelease;
final class CloudDevOpsPruneCommand extends Command { protected $signature='cloud-devops:prune {--days=}'; protected $description='Prune old deployment release history'; public function handle(): int { $days=(int)($this->option('days') ?: config('clouddevops.retention_days',90)); $count=DeploymentRelease::where('created_at','<',now()->subDays($days))->whereIn('status',['succeeded','failed','cancelled'])->delete(); $this->info("Pruned {$count} releases."); return self::SUCCESS; } }
final class CloudDevOpsPruneCommand extends Command
{
protected $signature = 'cloud-devops:prune {--days=}';
protected $description = 'Prune old deployment release history';
public function handle(): int
{
$days = (int) ($this->option('days') ?: config('clouddevops.retention_days', 90));
$count = DeploymentRelease::where('created_at', '<', now()->subDays($days))->whereIn('status', ['succeeded', 'failed', 'cancelled'])->delete();
$this->info("Pruned {$count} releases.");
return self::SUCCESS;
}
}
@@ -4,7 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
return new class extends Migration
{
public function up(): void
{
Schema::create('deployment_environments', function (Blueprint $table): void {
@@ -1,7 +1,21 @@
<?php
namespace Modules\CloudDevOps\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Modules\CloudDevOps\Models\DeploymentEnvironment;
use Modules\CloudDevOps\Models\DeploymentRelease;
final class CloudDevOpsApiController extends Controller { public function environments(): JsonResponse { return response()->json(['data'=>DeploymentEnvironment::withCount(['targets','pipelines','releases'])->get()]); } public function releases(): JsonResponse { return response()->json(['data'=>DeploymentRelease::with(['environment','steps'])->latest()->paginate()]); } }
final class CloudDevOpsApiController extends Controller
{
public function environments(): JsonResponse
{
return response()->json(['data' => DeploymentEnvironment::withCount(['targets', 'pipelines', 'releases'])->get()]);
}
public function releases(): JsonResponse
{
return response()->json(['data' => DeploymentRelease::with(['environment', 'steps'])->latest()->paginate()]);
}
}
@@ -26,22 +26,25 @@ final class CloudDevOpsController extends Controller
public function storeEnvironment(Request $request): RedirectResponse
{
$data = $request->validate(['name'=>'required|string|max:120','slug'=>'required|alpha_dash|max:120|unique:deployment_environments,slug','type'=>'required|in:development,staging,production']);
$data = $request->validate(['name' => 'required|string|max:120', 'slug' => 'required|alpha_dash|max:120|unique:deployment_environments,slug', 'type' => 'required|in:development,staging,production']);
DeploymentEnvironment::create($data);
return back()->with('status', 'Environment created.');
}
public function deploy(Request $request, DeploymentEnvironment $environment, DeploymentManager $manager): RedirectResponse
{
$data = $request->validate(['pipeline_id'=>'required|integer','version'=>'nullable|string|max:120','commit_hash'=>'nullable|string|max:120']);
$data = $request->validate(['pipeline_id' => 'required|integer', 'version' => 'nullable|string|max:120', 'commit_hash' => 'nullable|string|max:120']);
$pipeline = DeploymentPipeline::where('environment_id', $environment->id)->findOrFail($data['pipeline_id']);
$manager->createRelease($environment, $pipeline, $data + ['requested_by' => $request->user()?->id]);
return back()->with('status', 'Release created.');
}
public function runCheck(DeploymentHealthCheck $check, HealthCheckRunner $runner): RedirectResponse
{
$runner->run($check);
return back()->with('status', 'Health check completed.');
}
}
@@ -8,9 +8,29 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
final class DeploymentEnvironment extends Model
{
protected $guarded = [];
protected function casts(): array { return ['variables' => 'encrypted:array', 'is_protected' => 'boolean']; }
public function targets(): HasMany { return $this->hasMany(DeploymentTarget::class, 'environment_id'); }
public function pipelines(): HasMany { return $this->hasMany(DeploymentPipeline::class, 'environment_id'); }
public function releases(): HasMany { return $this->hasMany(DeploymentRelease::class, 'environment_id'); }
public function healthChecks(): HasMany { return $this->hasMany(DeploymentHealthCheck::class, 'environment_id'); }
protected function casts(): array
{
return ['variables' => 'encrypted:array', 'is_protected' => 'boolean'];
}
public function targets(): HasMany
{
return $this->hasMany(DeploymentTarget::class, 'environment_id');
}
public function pipelines(): HasMany
{
return $this->hasMany(DeploymentPipeline::class, 'environment_id');
}
public function releases(): HasMany
{
return $this->hasMany(DeploymentRelease::class, 'environment_id');
}
public function healthChecks(): HasMany
{
return $this->hasMany(DeploymentHealthCheck::class, 'environment_id');
}
}
@@ -1,4 +1,15 @@
<?php
namespace Modules\CloudDevOps\Models;
use Illuminate\Database\Eloquent\Model;
final class DeploymentHealthCheck extends Model { protected $guarded = []; protected function casts(): array { return ['checked_at' => 'datetime']; } }
final class DeploymentHealthCheck extends Model
{
protected $guarded = [];
protected function casts(): array
{
return ['checked_at' => 'datetime'];
}
}
@@ -1,4 +1,15 @@
<?php
namespace Modules\CloudDevOps\Models;
use Illuminate\Database\Eloquent\Model;
final class DeploymentPipeline extends Model { protected $guarded = []; protected function casts(): array { return ['steps' => 'array', 'requires_approval' => 'boolean', 'is_active' => 'boolean']; } }
final class DeploymentPipeline extends Model
{
protected $guarded = [];
protected function casts(): array
{
return ['steps' => 'array', 'requires_approval' => 'boolean', 'is_active' => 'boolean'];
}
}
@@ -1,6 +1,27 @@
<?php
namespace Modules\CloudDevOps\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
final class DeploymentRelease extends Model { protected $guarded = []; protected function casts(): array { return ['metadata' => 'array', 'started_at' => 'datetime', 'completed_at' => 'datetime']; } public function environment(): BelongsTo { return $this->belongsTo(DeploymentEnvironment::class, 'environment_id'); } public function steps(): HasMany { return $this->hasMany(DeploymentStep::class, 'release_id')->orderBy('position'); } }
final class DeploymentRelease extends Model
{
protected $guarded = [];
protected function casts(): array
{
return ['metadata' => 'array', 'started_at' => 'datetime', 'completed_at' => 'datetime'];
}
public function environment(): BelongsTo
{
return $this->belongsTo(DeploymentEnvironment::class, 'environment_id');
}
public function steps(): HasMany
{
return $this->hasMany(DeploymentStep::class, 'release_id')->orderBy('position');
}
}
@@ -1,4 +1,10 @@
<?php
namespace Modules\CloudDevOps\Models;
use Illuminate\Database\Eloquent\Model;
final class DeploymentRollback extends Model { protected $guarded = []; }
final class DeploymentRollback extends Model
{
protected $guarded = [];
}
@@ -1,4 +1,15 @@
<?php
namespace Modules\CloudDevOps\Models;
use Illuminate\Database\Eloquent\Model;
final class DeploymentSecret extends Model { protected $guarded = []; protected function casts(): array { return ['rotated_at' => 'datetime']; } }
final class DeploymentSecret extends Model
{
protected $guarded = [];
protected function casts(): array
{
return ['rotated_at' => 'datetime'];
}
}
+12 -1
View File
@@ -1,4 +1,15 @@
<?php
namespace Modules\CloudDevOps\Models;
use Illuminate\Database\Eloquent\Model;
final class DeploymentStep extends Model { protected $guarded = []; protected function casts(): array { return ['started_at' => 'datetime', 'completed_at' => 'datetime']; } }
final class DeploymentStep extends Model
{
protected $guarded = [];
protected function casts(): array
{
return ['started_at' => 'datetime', 'completed_at' => 'datetime'];
}
}
@@ -1,4 +1,15 @@
<?php
namespace Modules\CloudDevOps\Models;
use Illuminate\Database\Eloquent\Model;
final class DeploymentTarget extends Model { protected $guarded = []; protected function casts(): array { return ['configuration' => 'encrypted:array', 'is_active' => 'boolean']; } }
final class DeploymentTarget extends Model
{
protected $guarded = [];
protected function casts(): array
{
return ['configuration' => 'encrypted:array', 'is_active' => 'boolean'];
}
}
@@ -1,6 +1,25 @@
<?php
namespace Modules\CloudDevOps\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\CloudDevOps\Console\Commands\CloudDevOpsHealthCommand;
use Modules\CloudDevOps\Console\Commands\CloudDevOpsPruneCommand;
final class CloudDevOpsServiceProvider extends ServiceProvider { public function register(): void { $this->mergeConfigFrom(module_path('CloudDevOps','Config/config.php'),'clouddevops'); $this->app->register(RouteServiceProvider::class); } public function boot(): void { $this->loadViewsFrom(module_path('CloudDevOps','Resources/views'),'clouddevops'); $this->loadMigrationsFrom(module_path('CloudDevOps','Database/Migrations')); if ($this->app->runningInConsole()) { $this->commands([CloudDevOpsHealthCommand::class,CloudDevOpsPruneCommand::class]); } } }
final class CloudDevOpsServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->mergeConfigFrom(module_path('CloudDevOps', 'Config/config.php'), 'clouddevops');
$this->app->register(RouteServiceProvider::class);
}
public function boot(): void
{
$this->loadViewsFrom(module_path('CloudDevOps', 'Resources/views'), 'clouddevops');
$this->loadMigrationsFrom(module_path('CloudDevOps', 'Database/Migrations'));
if ($this->app->runningInConsole()) {
$this->commands([CloudDevOpsHealthCommand::class, CloudDevOpsPruneCommand::class]);
}
}
}
@@ -1,5 +1,15 @@
<?php
namespace Modules\CloudDevOps\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
final class RouteServiceProvider extends ServiceProvider { public function boot(): void { Route::middleware('web')->group(module_path('CloudDevOps','Routes/web.php')); Route::middleware('api')->prefix('api')->group(module_path('CloudDevOps','Routes/api.php')); } }
final class RouteServiceProvider extends ServiceProvider
{
public function boot(): void
{
Route::middleware('web')->group(module_path('CloudDevOps', 'Routes/web.php'));
Route::middleware('api')->prefix('api')->group(module_path('CloudDevOps', 'Routes/api.php'));
}
}
+6 -1
View File
@@ -1,4 +1,9 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\CloudDevOps\Http\Controllers\CloudDevOpsApiController;
Route::middleware(['auth:sanctum','permission:cloud-devops.view'])->prefix('v1/cloud-devops')->group(function (): void { Route::get('/environments',[CloudDevOpsApiController::class,'environments']); Route::get('/releases',[CloudDevOpsApiController::class,'releases']); });
Route::middleware(['auth:sanctum', 'permission:cloud-devops.view'])->prefix('v1/cloud-devops')->group(function (): void {
Route::get('/environments', [CloudDevOpsApiController::class, 'environments']);
Route::get('/releases', [CloudDevOpsApiController::class, 'releases']);
});
@@ -34,6 +34,7 @@ final class DeploymentManager
{
abort_unless(config('clouddevops.allow_execution'), 403, 'Deployment execution is disabled.');
$release->update(['status' => 'running', 'started_at' => now()]);
return $release->refresh();
}
}
@@ -1,5 +1,24 @@
<?php
namespace Modules\CloudDevOps\Services;
use Illuminate\Support\Facades\Http;
use Modules\CloudDevOps\Models\DeploymentHealthCheck;
final class HealthCheckRunner { public function run(DeploymentHealthCheck $check): DeploymentHealthCheck { $started = microtime(true); try { $response = Http::timeout(10)->get((string) $check->endpoint); $status = $response->status() === $check->expected_status ? 'healthy' : 'unhealthy'; $message = 'HTTP '.$response->status(); } catch (\Throwable $e) { $status = 'unhealthy'; $message = $e->getMessage(); } $check->update(['status'=>$status,'response_time_ms'=>(int)((microtime(true)-$started)*1000),'checked_at'=>now(),'message'=>$message]); return $check->refresh(); } }
final class HealthCheckRunner
{
public function run(DeploymentHealthCheck $check): DeploymentHealthCheck
{
$started = microtime(true);
try {
$response = Http::timeout(10)->get((string) $check->endpoint);
$status = $response->status() === $check->expected_status ? 'healthy' : 'unhealthy';
$message = 'HTTP '.$response->status();
} catch (\Throwable $e) {
$status = 'unhealthy';
$message = $e->getMessage();
} $check->update(['status' => $status, 'response_time_ms' => (int) ((microtime(true) - $started) * 1000), 'checked_at' => now(), 'message' => $message]);
return $check->refresh();
}
}
@@ -1,6 +1,18 @@
<?php
namespace Modules\CloudDevOps\Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\CloudDevOps\Models\DeploymentEnvironment;
use Tests\TestCase;
final class CloudDevOpsModuleTest extends TestCase { use RefreshDatabase; public function test_environment_can_be_created(): void { $environment=DeploymentEnvironment::create(['name'=>'Production','slug'=>'production','type'=>'production']); $this->assertDatabaseHas('deployment_environments',['id'=>$environment->id,'slug'=>'production']); } }
final class CloudDevOpsModuleTest extends TestCase
{
use RefreshDatabase;
public function test_environment_can_be_created(): void
{
$environment = DeploymentEnvironment::create(['name' => 'Production', 'slug' => 'production', 'type' => 'production']);
$this->assertDatabaseHas('deployment_environments', ['id' => $environment->id, 'slug' => 'production']);
}
}