---
description: Development setup, coding standards, contribution guidelines, and best practices
globs: **/*.php, composer.json, package.json, *.md, .env.example
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
```
## 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