 86f6cd5fd6
			
		
	
	86f6cd5fd6
	
	
	
		
			
			- Introduced multiple new markdown files covering API and routing, application architecture, deployment architecture, database patterns, frontend patterns, and security practices. - Established guidelines for development workflows, testing strategies, and continuous improvement of rules. - Enhanced project overview and technology stack documentation to provide clarity on Coolify's features and architecture.
		
			
				
	
	
		
			654 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			654 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ---
 | |
| description: 
 | |
| globs: 
 | |
| alwaysApply: false
 | |
| ---
 | |
| # Coolify Development Workflow
 | |
| 
 | |
| ## Development Environment Setup
 | |
| 
 | |
| ### Prerequisites
 | |
| - **PHP 8.4+** - Latest PHP version for modern features
 | |
| - **Node.js 18+** - For frontend asset compilation
 | |
| - **Docker & Docker Compose** - Container orchestration
 | |
| - **PostgreSQL 15** - Primary database
 | |
| - **Redis 7** - Caching and queues
 | |
| 
 | |
| ### Local Development Setup
 | |
| 
 | |
| #### Using Docker (Recommended)
 | |
| ```bash
 | |
| # Clone the repository
 | |
| git clone https://github.com/coollabsio/coolify.git
 | |
| cd coolify
 | |
| 
 | |
| # Copy environment configuration
 | |
| cp .env.example .env
 | |
| 
 | |
| # Start development environment
 | |
| docker-compose -f docker-compose.dev.yml up -d
 | |
| 
 | |
| # Install PHP dependencies
 | |
| docker-compose exec app composer install
 | |
| 
 | |
| # Install Node.js dependencies
 | |
| docker-compose exec app npm install
 | |
| 
 | |
| # Generate application key
 | |
| docker-compose exec app php artisan key:generate
 | |
| 
 | |
| # Run database migrations
 | |
| docker-compose exec app php artisan migrate
 | |
| 
 | |
| # Seed development data
 | |
| docker-compose exec app php artisan db:seed
 | |
| ```
 | |
| 
 | |
| #### Native Development
 | |
| ```bash
 | |
| # Install PHP dependencies
 | |
| composer install
 | |
| 
 | |
| # Install Node.js dependencies
 | |
| npm install
 | |
| 
 | |
| # Setup environment
 | |
| cp .env.example .env
 | |
| php artisan key:generate
 | |
| 
 | |
| # Setup database
 | |
| createdb coolify_dev
 | |
| php artisan migrate
 | |
| php artisan db:seed
 | |
| 
 | |
| # Start development servers
 | |
| php artisan serve &
 | |
| npm run dev &
 | |
| php artisan queue:work &
 | |
| ```
 | |
| 
 | |
| ## Development Tools & Configuration
 | |
| 
 | |
| ### Code Quality Tools
 | |
| - **[Laravel Pint](mdc:pint.json)** - PHP code style fixer
 | |
| - **[Rector](mdc:rector.php)** - PHP automated refactoring (989B, 35 lines)
 | |
| - **PHPStan** - Static analysis for type safety
 | |
| - **ESLint** - JavaScript code quality
 | |
| 
 | |
| ### Development Configuration Files
 | |
| - **[docker-compose.dev.yml](mdc:docker-compose.dev.yml)** - Development Docker setup (3.4KB, 126 lines)
 | |
| - **[vite.config.js](mdc:vite.config.js)** - Frontend build configuration (1.0KB, 42 lines)
 | |
| - **[.editorconfig](mdc:.editorconfig)** - Code formatting standards (258B, 19 lines)
 | |
| 
 | |
| ### Git Configuration
 | |
| - **[.gitignore](mdc:.gitignore)** - Version control exclusions (522B, 40 lines)
 | |
| - **[.gitattributes](mdc:.gitattributes)** - Git file handling (185B, 11 lines)
 | |
| 
 | |
| ## Development Workflow Process
 | |
| 
 | |
| ### 1. Feature Development
 | |
| ```bash
 | |
| # Create feature branch
 | |
| git checkout -b feature/new-deployment-strategy
 | |
| 
 | |
| # Make changes following coding standards
 | |
| # Run code quality checks
 | |
| ./vendor/bin/pint
 | |
| ./vendor/bin/rector process --dry-run
 | |
| ./vendor/bin/phpstan analyse
 | |
| 
 | |
| # Run tests
 | |
| ./vendor/bin/pest
 | |
| ./vendor/bin/pest --coverage
 | |
| 
 | |
| # Commit changes
 | |
| git add .
 | |
| git commit -m "feat: implement blue-green deployment strategy"
 | |
| ```
 | |
| 
 | |
| ### 2. Code Review Process
 | |
| ```bash
 | |
| # Push feature branch
 | |
| git push origin feature/new-deployment-strategy
 | |
| 
 | |
| # Create pull request with:
 | |
| # - Clear description of changes
 | |
| # - Screenshots for UI changes
 | |
| # - Test coverage information
 | |
| # - Breaking change documentation
 | |
| ```
 | |
| 
 | |
| ### 3. Testing Requirements
 | |
| - **Unit tests** for new models and services
 | |
| - **Feature tests** for API endpoints
 | |
| - **Browser tests** for UI changes
 | |
| - **Integration tests** for deployment workflows
 | |
| 
 | |
| ## Coding Standards & Conventions
 | |
| 
 | |
| ### PHP Coding Standards
 | |
| ```php
 | |
| // Follow PSR-12 coding standards
 | |
| class ApplicationDeploymentService
 | |
| {
 | |
|     public function __construct(
 | |
|         private readonly DockerService $dockerService,
 | |
|         private readonly ConfigurationGenerator $configGenerator
 | |
|     ) {}
 | |
|     
 | |
|     public function deploy(Application $application): ApplicationDeploymentQueue
 | |
|     {
 | |
|         return DB::transaction(function () use ($application) {
 | |
|             $deployment = $application->deployments()->create([
 | |
|                 'status' => 'queued',
 | |
|                 'commit_sha' => $application->getLatestCommitSha(),
 | |
|             ]);
 | |
|             
 | |
|             DeployApplicationJob::dispatch($deployment);
 | |
|             
 | |
|             return $deployment;
 | |
|         });
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Laravel Best Practices
 | |
| ```php
 | |
| // Use Laravel conventions
 | |
| class Application extends Model
 | |
| {
 | |
|     // Mass assignment protection
 | |
|     protected $fillable = [
 | |
|         'name', 'git_repository', 'git_branch', 'fqdn'
 | |
|     ];
 | |
|     
 | |
|     // Type casting
 | |
|     protected $casts = [
 | |
|         'environment_variables' => 'array',
 | |
|         'build_pack' => BuildPack::class,
 | |
|         'created_at' => 'datetime',
 | |
|     ];
 | |
|     
 | |
|     // Relationships
 | |
|     public function server(): BelongsTo
 | |
|     {
 | |
|         return $this->belongsTo(Server::class);
 | |
|     }
 | |
|     
 | |
|     public function deployments(): HasMany
 | |
|     {
 | |
|         return $this->hasMany(ApplicationDeploymentQueue::class);
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Frontend Standards
 | |
| ```javascript
 | |
| // Alpine.js component structure
 | |
| document.addEventListener('alpine:init', () => {
 | |
|     Alpine.data('deploymentMonitor', () => ({
 | |
|         status: 'idle',
 | |
|         logs: [],
 | |
|         
 | |
|         init() {
 | |
|             this.connectWebSocket();
 | |
|         },
 | |
|         
 | |
|         connectWebSocket() {
 | |
|             Echo.private(`application.${this.applicationId}`)
 | |
|                 .listen('DeploymentStarted', (e) => {
 | |
|                     this.status = 'deploying';
 | |
|                 })
 | |
|                 .listen('DeploymentCompleted', (e) => {
 | |
|                     this.status = 'completed';
 | |
|                 });
 | |
|         }
 | |
|     }));
 | |
| });
 | |
| ```
 | |
| 
 | |
| ### CSS/Tailwind Standards
 | |
| ```html
 | |
| <!-- Use semantic class names and consistent spacing -->
 | |
| <div class="bg-white dark:bg-gray-900 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
 | |
|     <div class="p-6">
 | |
|         <h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
 | |
|             Application Status
 | |
|         </h3>
 | |
|         <div class="space-y-3">
 | |
|             <!-- Content with consistent spacing -->
 | |
|         </div>
 | |
|     </div>
 | |
| </div>
 | |
| ```
 | |
| 
 | |
| ## Database Development
 | |
| 
 | |
| ### Migration Best Practices
 | |
| ```php
 | |
| // Create descriptive migration files
 | |
| class CreateApplicationDeploymentQueuesTable extends Migration
 | |
| {
 | |
|     public function up(): void
 | |
|     {
 | |
|         Schema::create('application_deployment_queues', function (Blueprint $table) {
 | |
|             $table->id();
 | |
|             $table->foreignId('application_id')->constrained()->cascadeOnDelete();
 | |
|             $table->string('status')->default('queued');
 | |
|             $table->string('commit_sha')->nullable();
 | |
|             $table->text('build_logs')->nullable();
 | |
|             $table->text('deployment_logs')->nullable();
 | |
|             $table->timestamp('started_at')->nullable();
 | |
|             $table->timestamp('finished_at')->nullable();
 | |
|             $table->timestamps();
 | |
|             
 | |
|             $table->index(['application_id', 'status']);
 | |
|             $table->index('created_at');
 | |
|         });
 | |
|     }
 | |
|     
 | |
|     public function down(): void
 | |
|     {
 | |
|         Schema::dropIfExists('application_deployment_queues');
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Model Factory Development
 | |
| ```php
 | |
| // Create comprehensive factories for testing
 | |
| class ApplicationFactory extends Factory
 | |
| {
 | |
|     protected $model = Application::class;
 | |
|     
 | |
|     public function definition(): array
 | |
|     {
 | |
|         return [
 | |
|             'name' => $this->faker->words(2, true),
 | |
|             'fqdn' => $this->faker->domainName,
 | |
|             'git_repository' => 'https://github.com/' . $this->faker->userName . '/' . $this->faker->word . '.git',
 | |
|             'git_branch' => 'main',
 | |
|             'build_pack' => BuildPack::NIXPACKS,
 | |
|             'server_id' => Server::factory(),
 | |
|             'environment_id' => Environment::factory(),
 | |
|         ];
 | |
|     }
 | |
|     
 | |
|     public function withCustomDomain(): static
 | |
|     {
 | |
|         return $this->state(fn (array $attributes) => [
 | |
|             'fqdn' => $this->faker->domainName,
 | |
|         ]);
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## API Development
 | |
| 
 | |
| ### Controller Standards
 | |
| ```php
 | |
| class ApplicationController extends Controller
 | |
| {
 | |
|     public function __construct()
 | |
|     {
 | |
|         $this->middleware('auth:sanctum');
 | |
|         $this->middleware('team.access');
 | |
|     }
 | |
|     
 | |
|     public function index(Request $request): AnonymousResourceCollection
 | |
|     {
 | |
|         $applications = $request->user()
 | |
|             ->currentTeam
 | |
|             ->applications()
 | |
|             ->with(['server', 'environment', 'latestDeployment'])
 | |
|             ->paginate();
 | |
|             
 | |
|         return ApplicationResource::collection($applications);
 | |
|     }
 | |
|     
 | |
|     public function store(StoreApplicationRequest $request): ApplicationResource
 | |
|     {
 | |
|         $application = $request->user()
 | |
|             ->currentTeam
 | |
|             ->applications()
 | |
|             ->create($request->validated());
 | |
|             
 | |
|         return new ApplicationResource($application);
 | |
|     }
 | |
|     
 | |
|     public function deploy(Application $application): JsonResponse
 | |
|     {
 | |
|         $this->authorize('deploy', $application);
 | |
|         
 | |
|         $deployment = app(ApplicationDeploymentService::class)
 | |
|             ->deploy($application);
 | |
|             
 | |
|         return response()->json([
 | |
|             'message' => 'Deployment started successfully',
 | |
|             'deployment_id' => $deployment->id,
 | |
|         ]);
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### API Resource Development
 | |
| ```php
 | |
| class ApplicationResource extends JsonResource
 | |
| {
 | |
|     public function toArray($request): array
 | |
|     {
 | |
|         return [
 | |
|             'id' => $this->id,
 | |
|             'name' => $this->name,
 | |
|             'fqdn' => $this->fqdn,
 | |
|             'status' => $this->status,
 | |
|             'git_repository' => $this->git_repository,
 | |
|             'git_branch' => $this->git_branch,
 | |
|             'build_pack' => $this->build_pack,
 | |
|             'created_at' => $this->created_at,
 | |
|             'updated_at' => $this->updated_at,
 | |
|             
 | |
|             // Conditional relationships
 | |
|             'server' => new ServerResource($this->whenLoaded('server')),
 | |
|             'environment' => new EnvironmentResource($this->whenLoaded('environment')),
 | |
|             'latest_deployment' => new DeploymentResource($this->whenLoaded('latestDeployment')),
 | |
|             
 | |
|             // Computed attributes
 | |
|             'deployment_url' => $this->getDeploymentUrl(),
 | |
|             'can_deploy' => $this->canDeploy(),
 | |
|         ];
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Livewire Component Development
 | |
| 
 | |
| ### Component Structure
 | |
| ```php
 | |
| class ApplicationShow extends Component
 | |
| {
 | |
|     public Application $application;
 | |
|     public bool $showLogs = false;
 | |
|     
 | |
|     protected $listeners = [
 | |
|         'deployment.started' => 'refreshDeploymentStatus',
 | |
|         'deployment.completed' => 'refreshDeploymentStatus',
 | |
|     ];
 | |
|     
 | |
|     public function mount(Application $application): void
 | |
|     {
 | |
|         $this->authorize('view', $application);
 | |
|         $this->application = $application;
 | |
|     }
 | |
|     
 | |
|     public function deploy(): void
 | |
|     {
 | |
|         $this->authorize('deploy', $this->application);
 | |
|         
 | |
|         try {
 | |
|             app(ApplicationDeploymentService::class)->deploy($this->application);
 | |
|             
 | |
|             $this->dispatch('deployment.started', [
 | |
|                 'application_id' => $this->application->id
 | |
|             ]);
 | |
|             
 | |
|             session()->flash('success', 'Deployment started successfully');
 | |
|         } catch (Exception $e) {
 | |
|             session()->flash('error', 'Failed to start deployment: ' . $e->getMessage());
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public function refreshDeploymentStatus(): void
 | |
|     {
 | |
|         $this->application->refresh();
 | |
|     }
 | |
|     
 | |
|     public function render(): View
 | |
|     {
 | |
|         return view('livewire.application.show', [
 | |
|             'deployments' => $this->application
 | |
|                 ->deployments()
 | |
|                 ->latest()
 | |
|                 ->limit(10)
 | |
|                 ->get()
 | |
|         ]);
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Queue Job Development
 | |
| 
 | |
| ### Job Structure
 | |
| ```php
 | |
| class DeployApplicationJob implements ShouldQueue
 | |
| {
 | |
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | |
|     
 | |
|     public int $tries = 3;
 | |
|     public int $maxExceptions = 1;
 | |
|     
 | |
|     public function __construct(
 | |
|         public ApplicationDeploymentQueue $deployment
 | |
|     ) {}
 | |
|     
 | |
|     public function handle(
 | |
|         DockerService $dockerService,
 | |
|         ConfigurationGenerator $configGenerator
 | |
|     ): void {
 | |
|         $this->deployment->update(['status' => 'running', 'started_at' => now()]);
 | |
|         
 | |
|         try {
 | |
|             // Generate configuration
 | |
|             $config = $configGenerator->generateDockerCompose($this->deployment->application);
 | |
|             
 | |
|             // Build and deploy
 | |
|             $imageTag = $dockerService->buildImage($this->deployment->application);
 | |
|             $dockerService->deployContainer($this->deployment->application, $imageTag);
 | |
|             
 | |
|             $this->deployment->update([
 | |
|                 'status' => 'success',
 | |
|                 'finished_at' => now()
 | |
|             ]);
 | |
|             
 | |
|             // Broadcast success
 | |
|             broadcast(new DeploymentCompleted($this->deployment));
 | |
|             
 | |
|         } catch (Exception $e) {
 | |
|             $this->deployment->update([
 | |
|                 'status' => 'failed',
 | |
|                 'error_message' => $e->getMessage(),
 | |
|                 'finished_at' => now()
 | |
|             ]);
 | |
|             
 | |
|             broadcast(new DeploymentFailed($this->deployment));
 | |
|             
 | |
|             throw $e;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public function backoff(): array
 | |
|     {
 | |
|         return [1, 5, 10];
 | |
|     }
 | |
|     
 | |
|     public function failed(Throwable $exception): void
 | |
|     {
 | |
|         $this->deployment->update([
 | |
|             'status' => 'failed',
 | |
|             'error_message' => $exception->getMessage(),
 | |
|             'finished_at' => now()
 | |
|         ]);
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Testing Development
 | |
| 
 | |
| ### Test Structure
 | |
| ```php
 | |
| // Feature test example
 | |
| test('user can deploy application via API', function () {
 | |
|     $user = User::factory()->create();
 | |
|     $application = Application::factory()->create([
 | |
|         'team_id' => $user->currentTeam->id
 | |
|     ]);
 | |
|     
 | |
|     // Mock external services
 | |
|     $this->mock(DockerService::class, function ($mock) {
 | |
|         $mock->shouldReceive('buildImage')->andReturn('app:latest');
 | |
|         $mock->shouldReceive('deployContainer')->andReturn(true);
 | |
|     });
 | |
|     
 | |
|     $response = $this->actingAs($user)
 | |
|         ->postJson("/api/v1/applications/{$application->id}/deploy");
 | |
|     
 | |
|     $response->assertStatus(200)
 | |
|         ->assertJson([
 | |
|             'message' => 'Deployment started successfully'
 | |
|         ]);
 | |
|     
 | |
|     expect($application->deployments()->count())->toBe(1);
 | |
|     expect($application->deployments()->first()->status)->toBe('queued');
 | |
| });
 | |
| ```
 | |
| 
 | |
| ## Documentation Standards
 | |
| 
 | |
| ### Code Documentation
 | |
| ```php
 | |
| /**
 | |
|  * Deploy an application to the specified server.
 | |
|  * 
 | |
|  * This method creates a new deployment queue entry and dispatches
 | |
|  * a background job to handle the actual deployment process.
 | |
|  * 
 | |
|  * @param Application $application The application to deploy
 | |
|  * @param array $options Additional deployment options
 | |
|  * @return ApplicationDeploymentQueue The created deployment queue entry
 | |
|  * 
 | |
|  * @throws DeploymentException When deployment cannot be started
 | |
|  * @throws ServerConnectionException When server is unreachable
 | |
|  */
 | |
| public function deploy(Application $application, array $options = []): ApplicationDeploymentQueue
 | |
| {
 | |
|     // Implementation
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### API Documentation
 | |
| ```php
 | |
| /**
 | |
|  * @OA\Post(
 | |
|  *     path="/api/v1/applications/{application}/deploy",
 | |
|  *     summary="Deploy an application",
 | |
|  *     description="Triggers a new deployment for the specified application",
 | |
|  *     operationId="deployApplication",
 | |
|  *     tags={"Applications"},
 | |
|  *     security={{"bearerAuth":{}}},
 | |
|  *     @OA\Parameter(
 | |
|  *         name="application",
 | |
|  *         in="path",
 | |
|  *         required=true,
 | |
|  *         @OA\Schema(type="integer"),
 | |
|  *         description="Application ID"
 | |
|  *     ),
 | |
|  *     @OA\Response(
 | |
|  *         response=200,
 | |
|  *         description="Deployment started successfully",
 | |
|  *         @OA\JsonContent(
 | |
|  *             @OA\Property(property="message", type="string"),
 | |
|  *             @OA\Property(property="deployment_id", type="integer")
 | |
|  *         )
 | |
|  *     )
 | |
|  * )
 | |
|  */
 | |
| ```
 | |
| 
 | |
| ## Performance Optimization
 | |
| 
 | |
| ### Database Optimization
 | |
| ```php
 | |
| // Use eager loading to prevent N+1 queries
 | |
| $applications = Application::with([
 | |
|     'server:id,name,ip',
 | |
|     'environment:id,name',
 | |
|     'latestDeployment:id,application_id,status,created_at'
 | |
| ])->get();
 | |
| 
 | |
| // Use database transactions for consistency
 | |
| DB::transaction(function () use ($application) {
 | |
|     $deployment = $application->deployments()->create(['status' => 'queued']);
 | |
|     $application->update(['last_deployment_at' => now()]);
 | |
|     DeployApplicationJob::dispatch($deployment);
 | |
| });
 | |
| ```
 | |
| 
 | |
| ### Caching Strategies
 | |
| ```php
 | |
| // Cache expensive operations
 | |
| public function getServerMetrics(Server $server): array
 | |
| {
 | |
|     return Cache::remember(
 | |
|         "server.{$server->id}.metrics",
 | |
|         now()->addMinutes(5),
 | |
|         fn () => $this->fetchServerMetrics($server)
 | |
|     );
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Deployment & Release Process
 | |
| 
 | |
| ### Version Management
 | |
| - **[versions.json](mdc:versions.json)** - Version tracking (355B, 19 lines)
 | |
| - **[CHANGELOG.md](mdc:CHANGELOG.md)** - Release notes (187KB, 7411 lines)
 | |
| - **[cliff.toml](mdc:cliff.toml)** - Changelog generation (3.2KB, 85 lines)
 | |
| 
 | |
| ### Release Workflow
 | |
| ```bash
 | |
| # Create release branch
 | |
| git checkout -b release/v4.1.0
 | |
| 
 | |
| # Update version numbers
 | |
| # Update CHANGELOG.md
 | |
| # Run full test suite
 | |
| ./vendor/bin/pest
 | |
| npm run test
 | |
| 
 | |
| # Create release commit
 | |
| git commit -m "chore: release v4.1.0"
 | |
| 
 | |
| # Create and push tag
 | |
| git tag v4.1.0
 | |
| git push origin v4.1.0
 | |
| 
 | |
| # Merge to main
 | |
| git checkout main
 | |
| git merge release/v4.1.0
 | |
| ```
 | |
| 
 | |
| ## Contributing Guidelines
 | |
| 
 | |
| ### Pull Request Process
 | |
| 1. **Fork** the repository
 | |
| 2. **Create** feature branch from `main`
 | |
| 3. **Implement** changes with tests
 | |
| 4. **Run** code quality checks
 | |
| 5. **Submit** pull request with clear description
 | |
| 6. **Address** review feedback
 | |
| 7. **Merge** after approval
 | |
| 
 | |
| ### Code Review Checklist
 | |
| - [ ] Code follows project standards
 | |
| - [ ] Tests cover new functionality
 | |
| - [ ] Documentation is updated
 | |
| - [ ] No breaking changes without migration
 | |
| - [ ] Performance impact considered
 | |
| - [ ] Security implications reviewed
 | |
| 
 | |
| ### Issue Reporting
 | |
| - Use issue templates
 | |
| - Provide reproduction steps
 | |
| - Include environment details
 | |
| - Add relevant logs/screenshots
 | |
| - Label appropriately
 |