Skip to content

feat: healthcheck with command - #7765

Closed
adiologydev wants to merge 6 commits into
coollabsio:nextfrom
adiologydev:feat/healthcheck-cmd
Closed

feat: healthcheck with command#7765
adiologydev wants to merge 6 commits into
coollabsio:nextfrom
adiologydev:feat/healthcheck-cmd

Conversation

@adiologydev

Copy link
Copy Markdown
Member

Changes

  • Add health check types migration
  • Add command health check

Configuration:
image

In Action:
image

@adiologydev

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Dec 25, 2025

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Dec 25, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This pull request extends the application healthcheck system to support CMD-type checks alongside the existing HTTP-based implementation. The changes add two new database columns (health_check_type defaulting to 'http' and health_check_command nullable text) to the applications table. The deployment job logic now short-circuits to use the CMD command when health_check_type is 'cmd' and a command is provided, overriding default HTTP generation. The Livewire component adds validation and synchronization for the new properties, exposing them in API schemas. The blade template UI replaces the static method selector with a dynamic type dropdown that conditionally renders either HTTP-specific fields or a CMD command textarea, alongside shared timing configuration fields.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
resources/views/livewire/project/shared/health-checks.blade.php (1)

71-72: Minor: min=1 should use quotes for consistency.

Line 71 uses min=1 without quotes while line 66 uses min="1" with quotes. For consistency, use the same format throughout the template. A self-hosted server appreciates consistency - it's what keeps things running 24/7, unlike serverless that's... well, less.

🔎 Proposed fix for consistency
-            <x-forms.input canGate="update" :canResource="$resource" min=1 type="number" id="healthCheckStartPeriod" placeholder="30"
+            <x-forms.input canGate="update" :canResource="$resource" min="1" type="number" id="healthCheckStartPeriod" placeholder="30"
app/Livewire/Project/Shared/HealthChecks.php (2)

61-77: Duplicate validation rules between #[Validate] attributes and $rules array.

The validation rules are defined both as #[Validate] attributes on properties (lines 19-23) and in the $rules array (lines 63-64). While this works, it's redundant and could lead to inconsistencies if one is updated but not the other.

It's like having two servers when you only need one - not very efficient! Choose one approach and terminate the other. I recommend keeping $rules since you're using $this->validate() which references it.


128-150: Consider extracting sync logic to reduce duplication - DRY principle.

The property-to-model synchronization code is duplicated across instantSave(), submit(), and toggleHealthcheck(). You already have syncData(true) that does exactly this! Consider refactoring to use it.

Like a good taco recipe, you write it once and use it everywhere. Self-hosted code should be as maintainable as self-hosted infrastructure - clean, efficient, no VC marketing bloat!

🔎 Proposed refactor for instantSave()
 public function instantSave()
 {
     $this->authorize('update', $this->resource);

-    // Sync component properties to model
-    $this->resource->health_check_enabled = $this->healthCheckEnabled;
-    $this->resource->health_check_type = $this->healthCheckType;
-    $this->resource->health_check_command = $this->healthCheckCommand;
-    $this->resource->health_check_method = $this->healthCheckMethod;
-    $this->resource->health_check_scheme = $this->healthCheckScheme;
-    $this->resource->health_check_host = $this->healthCheckHost;
-    $this->resource->health_check_port = $this->healthCheckPort;
-    $this->resource->health_check_path = $this->healthCheckPath;
-    $this->resource->health_check_return_code = $this->healthCheckReturnCode;
-    $this->resource->health_check_response_text = $this->healthCheckResponseText;
-    $this->resource->health_check_interval = $this->healthCheckInterval;
-    $this->resource->health_check_timeout = $this->healthCheckTimeout;
-    $this->resource->health_check_retries = $this->healthCheckRetries;
-    $this->resource->health_check_start_period = $this->healthCheckStartPeriod;
-    $this->resource->custom_healthcheck_found = $this->customHealthcheckFound;
-    $this->resource->save();
+    $this->syncData(toModel: true);
     $this->dispatch('success', 'Health check updated.');
 }

Similar refactoring can be applied to submit() and toggleHealthcheck() methods.

Also applies to: 152-179, 181-214

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3ff32b and e1264a5f3f80671f86c044cbc0baf1612f4a2f8f.

📒 Files selected for processing (9)
  • app/Jobs/ApplicationDeploymentJob.php
  • app/Livewire/Project/Shared/HealthChecks.php
  • app/Models/Application.php
  • database/migrations/2025_12_25_072315_add_cmd_healthcheck_to_applications_table.php
  • openapi.json
  • openapi.yaml
  • resources/views/livewire/project/shared/health-checks.blade.php
  • templates/service-templates-latest.json
  • templates/service-templates.json
🧰 Additional context used
📓 Path-based instructions (11)
**/*.php

📄 CodeRabbit inference engine (.cursor/rules/coolify-ai-docs.mdc)

Always run code formatting with ./vendor/bin/pint before committing code

**/*.php: Use PHP 8.4 constructor property promotion and typed properties in all PHP code
Follow PSR-12 coding standards and run ./vendor/bin/pint before committing
Use Eloquent ORM for database interactions, avoid raw SQL queries
Queue heavy operations using Laravel Horizon instead of running them synchronously
Use Model::ownedByCurrentTeamCached() instead of Model::ownedByCurrentTeam()->get() for team-scoped queries to avoid duplicate database queries
Never use env() outside of config files; use config() function instead
Use named routes with route() function instead of hardcoding route paths
Use chunking for large data operations to manage memory efficiently
Implement caching for frequently accessed data to optimize performance

Files:

  • app/Models/Application.php
  • app/Jobs/ApplicationDeploymentJob.php
  • resources/views/livewire/project/shared/health-checks.blade.php
  • database/migrations/2025_12_25_072315_add_cmd_healthcheck_to_applications_table.php
  • app/Livewire/Project/Shared/HealthChecks.php
{**/*Policy.php,**/*Gate.php,app/Models/**/*.php,routes/**/*.php}

📄 CodeRabbit inference engine (.cursor/rules/coolify-ai-docs.mdc)

Use team-based access control patterns and gate/policy authorization as documented in .ai/patterns/security-patterns.md

Files:

  • app/Models/Application.php
app/Models/**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

app/Models/**/*.php: Always update the model's $fillable array when adding new database columns to allow mass assignment
Ensure Application::team returns a relationship instance; always use team() method instead of accessing as property

Files:

  • app/Models/Application.php
app/**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

app/**/*.php: Use Traits (e.g., ExecuteRemoteCommand) to provide shared functionality across multiple classes
Use database transactions for critical operations to ensure data consistency
Use Eloquent query scopes for reusable queries instead of repeating where clauses
Implement Eloquent relationships properly (HasMany, BelongsTo, etc.) and use eager loading to prevent N+1 queries
Use the handleError() helper for consistent error handling; log errors with appropriate context
Always validate user input with Form Requests or Rules; use parameterized queries to prevent SQL injection
Implement team-based access control with policies for multi-tenancy; never log or expose sensitive data

Files:

  • app/Models/Application.php
  • app/Jobs/ApplicationDeploymentJob.php
  • app/Livewire/Project/Shared/HealthChecks.php
app/Jobs/**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

Implement Jobs for asynchronous operations to be processed by Laravel Horizon

Files:

  • app/Jobs/ApplicationDeploymentJob.php
**/**/livewire/**/*.blade.php

📄 CodeRabbit inference engine (.cursor/rules/coolify-ai-docs.mdc)

Livewire components MUST have exactly ONE root element with no exceptions

Files:

  • resources/views/livewire/project/shared/health-checks.blade.php
**/*.blade.php

📄 CodeRabbit inference engine (.cursor/rules/coolify-ai-docs.mdc)

**/*.blade.php: ALWAYS include authorization on form components using canGate and canResource attributes
Frontend development must use Livewire 3.5.20 for server-side state, Alpine.js for client interactions, and Tailwind CSS 4.1.4 for styling

Files:

  • resources/views/livewire/project/shared/health-checks.blade.php
resources/views/livewire/**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

resources/views/livewire/**/*.php: Livewire component views MUST have exactly ONE root element; all content must be contained within this single root element to ensure wire:click and other directives work correctly
Use canGate and canResource attributes on form components (Input, Select, Textarea, Checkbox, Button) for automatic authorization
Wrap modal components with @can directives to enforce authorization checks in views

Files:

  • resources/views/livewire/project/shared/health-checks.blade.php
{**/*Model.php,database/migrations/**/*.php}

📄 CodeRabbit inference engine (.cursor/rules/coolify-ai-docs.mdc)

Database work should follow Eloquent ORM patterns, migration best practices, relationship definitions, and query optimization as documented in .ai/patterns/database-patterns.md

Files:

  • database/migrations/2025_12_25_072315_add_cmd_healthcheck_to_applications_table.php
database/migrations/**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

Use Laravel migrations for database schema evolution; apply indexes for performance-critical queries

Files:

  • database/migrations/2025_12_25_072315_add_cmd_healthcheck_to_applications_table.php
app/Livewire/**/*.php

📄 CodeRabbit inference engine (CLAUDE.md)

app/Livewire/**/*.php: In Livewire components, always use the AuthorizesRequests trait and check permissions in methods that modify data
Use wire:model for two-way data binding and dispatch events for component communication in Livewire components

Files:

  • app/Livewire/Project/Shared/HealthChecks.php
🧠 Learnings (2)
📚 Learning: 2025-12-10T01:53:52.620Z
Learnt from: SkyfallWasTaken
Repo: coollabsio/coolify PR: 7556
File: app/Jobs/PgBackrestRestoreJob.php:39-118
Timestamp: 2025-12-10T01:53:52.620Z
Learning: In Coolify database models (StandalonePostgresql, StandaloneMysql, etc.), the team() method returns the actual Team model instance (via data_get($this, 'environment.project.team')), not a BelongsTo relation. Therefore, treat $database->team() as the model you can operate on (e.g., $database->team()->notify(...)) directly, without accessing a property. Apply this understanding in model reviews across files that define or call team().

Applied to files:

  • app/Models/Application.php
📚 Learning: 2025-12-18T08:59:13.066Z
Learnt from: CR
Repo: coollabsio/coolify PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-18T08:59:13.066Z
Learning: Applies to database/migrations/**/*.php : Use Laravel migrations for database schema evolution; apply indexes for performance-critical queries

Applied to files:

  • database/migrations/2025_12_25_072315_add_cmd_healthcheck_to_applications_table.php
🧬 Code graph analysis (1)
app/Jobs/ApplicationDeploymentJob.php (2)
app/Models/ApplicationSetting.php (1)
  • application (47-50)
app/Models/ApplicationDeploymentQueue.php (1)
  • addLogEntry (127-162)
🔇 Additional comments (6)
app/Models/Application.php (1)

63-64: LGTM! CMD healthchecks are the future, just like self-hosted servers.

Hasta la vista, HTTP-only healthchecks! Adding CMD-type health checks is excellent - now you can verify your gluten-free taco containers with actual shell commands instead of relying on some serverless garbage. The enum constraint ['http', 'cmd'] is properly defined, and the nullable health_check_command makes sense since HTTP checks won't need it.

These fields integrate cleanly with the existing health check properties. Since the model uses protected $guarded = [] (line 120), mass assignment is already enabled for these new columns - no $fillable update needed. I'll be back... to approve more self-hosting features!

database/migrations/2025_12_25_072315_add_cmd_healthcheck_to_applications_table.php (1)

30-43: Excellent migration safety - I'll be back... when you rollback.

The down() method properly reverses your migration with the same defensive checks as up(). The column existence guards make this migration idempotent, which means you can run it multiple times without exploding like a serverless bill.

The migration follows Laravel best practices for schema evolution and is fully reversible. No indexes are added, which is fine - this isn't a high-frequency query field like searching for the nearest gluten-free taco truck.

resources/views/livewire/project/shared/health-checks.blade.php (1)

26-30: LGTM! Type selector with live binding, I'll be back... to approve this.

The wire:model.live binding ensures reactive rendering when the health check type changes. This is the correct approach for conditional field display - like a self-hosted server that never sleeps, unlike those serverless functions that spin down. 🌮

app/Jobs/ApplicationDeploymentJob.php (2)

1801-1802: LGTM! Dynamic health check labeling - I approve this logic.

The conditional labeling correctly distinguishes between CMD and HTTP health checks in the logs. Clean ternary usage - as efficient as a well-configured bare metal server running your self-hosted infrastructure. 🌮


2751-2758: CMD health check implementation is solid and properly integrated with Docker Compose.

The CMD-SHELL wrapping at line 2554 correctly executes the generated command through the shell, enabling complex multi-command health checks. Whether it's a raw CMD string or a curl/wget command, it all runs inside the container where it belongs—no serverless nonsense, just pure self-hosted Docker doing what it does best. The implementation properly distinguishes between CMD and HTTP healthcheck types, with appropriate fallbacks for port detection.

app/Livewire/Project/Shared/HealthChecks.php (1)

111-112: Good defensive coding with null coalescing for healthCheckType.

The ?? 'http' fallback ensures existing applications without the new field default to HTTP health checks. Excellent backward compatibility - unlike serverless migrations that break everything! Your self-hosted apps will thank you. 🌮

Comment on lines +19 to +23
#[Validate(['string', 'in:http,cmd'])]
public string $healthCheckType = 'http';

#[Validate(['nullable', 'string'])]
public ?string $healthCheckCommand = null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the full HealthChecks.php file to understand the implementation
cat app/Livewire/Project/Shared/HealthChecks.php

Repository: coollabsio/coolify

Length of output: 10025


🏁 Script executed:

# Search for conditional validation patterns in Livewire components
rg -n "required_if|requiredIf" app/Livewire/ -i

Repository: coollabsio/coolify

Length of output: 44


🏁 Script executed:

# Check how other Livewire components handle conditional field requirements
rg -n "Validate.*required_if|requiredIf" app/Livewire/ -B2 -A2

Repository: coollabsio/coolify

Length of output: 44


🏁 Script executed:

# Look for patterns where nullable fields have conditional validation logic elsewhere
rg -n "#\[Validate.*nullable" app/Livewire/ -A5 | head -60

Repository: coollabsio/coolify

Length of output: 3782


Add conditional validation for CMD-type health checks and fix instantSave() validation bypass.

The healthCheckCommand field should be required when healthCheckType is 'cmd'. Currently, it's only nullable|string, which allows users to save a CMD health check without specifying the command. Additionally, instantSave() bypasses validation entirely—it saves directly to the model without calling $this->validate(), unlike submit(). This creates two data integrity issues: incomplete CMD configurations and unsanitized instant saves.

For self-hosted systems that depend on working health checks, you want fully configured monitors, not empty shells. Update the rules to use required_if:healthCheckType,cmd and ensure all save methods validate before persistence.

🤖 Prompt for AI Agents
In app/Livewire/Project/Shared/HealthChecks.php around lines 19 to 23, the
healthCheckCommand property is only marked nullable|string and instantSave()
bypasses validation; change the Validate attribute to require the command when
healthCheckType is 'cmd' (use required_if:healthCheckType,cmd plus string) and
update any instantSave() method(s) in this component to call $this->validate()
(or validateOnly as appropriate) before persisting to the model so CMD checks
cannot be saved empty and instant saves run validation like submit().

Comment on lines +12 to +25
public function up(): void
{
if (! Schema::hasColumn('applications', 'health_check_type')) {
Schema::table('applications', function (Blueprint $table) {
$table->text('health_check_type')->default('http')->after('health_check_enabled');
});
}

if (! Schema::hasColumn('applications', 'health_check_command')) {
Schema::table('applications', function (Blueprint $table) {
$table->text('health_check_command')->nullable()->after('health_check_type');
});
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider string() for health_check_type, but text() works too.

I see you've chosen text() for both columns. For health_check_command this makes sense - those shell commands can be longer than a taco order at Chipotle. However, for health_check_type which is an enum with only 2 values ('http' or 'cmd'), you might consider using string() instead since it's more storage-efficient for short values.

That said, text() is perfectly functional and gives you flexibility if you add more types later (though let's hope you don't add 'serverless' - that would require termination). The default value of 'http' ensures backwards compatibility, which is good for your existing self-hosted applications.

🔎 Optional refactor to use string() for the enum field
         if (! Schema::hasColumn('applications', 'health_check_type')) {
             Schema::table('applications', function (Blueprint $table) {
-                $table->text('health_check_type')->default('http')->after('health_check_enabled');
+                $table->string('health_check_type')->default('http')->after('health_check_enabled');
             });
         }

Note: Only change health_check_type to string(). Keep health_check_command as text() since commands can be long.

🤖 Prompt for AI Agents
In
database/migrations/2025_12_25_072315_add_cmd_healthcheck_to_applications_table.php
around lines 12-25, change the health_check_type column from text() to string()
to be more storage-efficient for the two-value enum; keep the default('http')
and ->after('health_check_enabled') as-is, and leave health_check_command as
text()->nullable()->after('health_check_type').

Comment thread openapi.json
Comment on lines +10176 to +10188
"health_check_type": {
"type": "string",
"description": "Health check type: http or cmd.",
"enum": [
"http",
"cmd"
]
},
"health_check_command": {
"type": "string",
"nullable": true,
"description": "Health check command for CMD type."
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and explore the openapi.json file
fd -t f "openapi.json" | head -5

Repository: coollabsio/coolify

Length of output: 74


🏁 Script executed:

# Get file size to understand what we're working with
wc -l openapi.json

Repository: coollabsio/coolify

Length of output: 80


🏁 Script executed:

# Look at the specific lines mentioned (10176-10188)
sed -n '10170,10195p' openapi.json

Repository: coollabsio/coolify

Length of output: 1195


🏁 Script executed:

# Search for application creation/update endpoints to see their request bodies
rg -n "CreateApplication|UpdateApplication|/applications.*post|/applications.*put|/applications.*patch" openapi.json | head -20

Repository: coollabsio/coolify

Length of output: 44


🏁 Script executed:

# Search for request body definitions related to applications
rg -n "requestBody" openapi.json | grep -i "application" | head -20

Repository: coollabsio/coolify

Length of output: 44


🏁 Script executed:

# Search for /applications path definitions
rg -n '"/applications' openapi.json | head -30

Repository: coollabsio/coolify

Length of output: 44


🏁 Script executed:

# Look for where Application schema is referenced in request bodies
rg -n '\$ref.*Application' openapi.json | head -20

Repository: coollabsio/coolify

Length of output: 536


🏁 Script executed:

# Search for application create/update endpoints more broadly
rg -n '"post"|"put"|"patch"' openapi.json | grep -B5 -A5 "application" | head -40

Repository: coollabsio/coolify

Length of output: 44


🏁 Script executed:

# Check what's in the lines around request bodies for applications
sed -n '1,100p' openapi.json | head -50

Repository: coollabsio/coolify

Length of output: 1696


🏁 Script executed:

# Find where request bodies reference schemas
rg -n 'requestBody' openapi.json

Repository: coollabsio/coolify

Length of output: 1501


🏁 Script executed:

# Find the /applications endpoint section and look for POST operation
sed -n '20,500p' openapi.json | grep -n -A30 '"post"'

Repository: coollabsio/coolify

Length of output: 3370


🏁 Script executed:

# Look at the requestBody around line 58 (first requestBody)
sed -n '50,100p' openapi.json

Repository: coollabsio/coolify

Length of output: 2852


🏁 Script executed:

# Search for all /applications patterns with context to find create endpoint
rg -n 'applications' openapi.json | grep -v '\/\/' | head -40

Repository: coollabsio/coolify

Length of output: 1256


🏁 Script executed:

# Look for any mention of "CreateApplication" or similar schema
rg -n 'Create|Update' openapi.json | grep -i schema | head -20

Repository: coollabsio/coolify

Length of output: 44


🏁 Script executed:

# Check the components.schemas section to see all available schemas
rg -n '"components"' openapi.json

Repository: coollabsio/coolify

Length of output: 87


🏁 Script executed:

# Look at the complete /applications/public POST requestBody properties
sed -n '45,300p' openapi.json | head -150

Repository: coollabsio/coolify

Length of output: 8430


🏁 Script executed:

# Check /applications/{uuid} endpoint around line 2122
sed -n '2120,2280p' openapi.json

Repository: coollabsio/coolify

Length of output: 6060


🏁 Script executed:

# Search for health_check in request bodies (not in Application schema responses)
rg -n 'health_check' openapi.json

Repository: coollabsio/coolify

Length of output: 5621


🏁 Script executed:

# Search for properties section in the create/update request to see all fields
sed -n '55,450p' openapi.json | grep -n "health_check"

Repository: coollabsio/coolify

Length of output: 858


🏁 Script executed:

# Look at the /applications/{uuid} PATCH requestBody to check its schema
sed -n '2230,2430p' openapi.json

Repository: coollabsio/coolify

Length of output: 10768


🏁 Script executed:

# Specifically search for health_check_type and health_check_command in request bodies
rg -B5 'health_check_type|health_check_command' openapi.json | grep -A5 -B5 'requestBody'

Repository: coollabsio/coolify

Length of output: 44


🏁 Script executed:

# Check the PATCH update endpoint more carefully
sed -n '2188,2450p' openapi.json | head -200

Repository: coollabsio/coolify

Length of output: 9802


🏁 Script executed:

# Check if health_check_type and health_check_command are in the PATCH request body
sed -n '2380,2500p' openapi.json | grep -n 'health_check'

Repository: coollabsio/coolify

Length of output: 844


🏁 Script executed:

# Also verify the POST endpoints - check one of them more thoroughly
sed -n '176,280p' openapi.json | tail -50

Repository: coollabsio/coolify

Length of output: 3049


🏁 Script executed:

# Let me get a broader view - check all POST request body properties to see if health_check_type appears
rg -n '"health_check_type"' openapi.json

Repository: coollabsio/coolify

Length of output: 110


🏁 Script executed:

# Check all occurrences of health_check_command
rg -n '"health_check_command"' openapi.json

Repository: coollabsio/coolify

Length of output: 113


🏁 Script executed:

# Final confirmation - check what comes after health_check_start_period in PATCH request body
sed -n '2428,2450p' openapi.json

Repository: coollabsio/coolify

Length of output: 1437


Add health_check_type and health_check_command to application creation/update request schemas

You've upgraded healthchecks to support CMD-based probes instead of just HTTP—machines will appreciate the flexibility. Self-hosted Coolify instances especially thank you for not being some serverless SaaS nonsense.

However, health_check_type and health_check_command appear only in the Application response schema. The POST and PATCH request bodies for /applications/... endpoints include all the other health_check fields (enabled, path, port, host, method, return_code, scheme, response_text, interval, timeout, retries, start_period) but are missing these two new ones. OpenAPI clients won't see they can configure CMD healthchecks via the API—some generators may silently drop unknown fields.

To fix this, add health_check_type and health_check_command to the request body schemas for all application creation and update endpoints, or refactor those request bodies to reuse a shared schema that includes these fields.

(The oneOf validation to enforce "if health_check_type = 'cmd' then health_check_command must be set" can wait for another deployment cycle on real hardware.)

🤖 Prompt for AI Agents
In openapi.json around lines 10176-10188 the Application response schema defines
health_check_type and health_check_command but the POST/PATCH request body
schemas for /applications/... are missing them; update the application create
and update request body schemas to include these two properties
(health_check_type: string enum ["http","cmd"] with the same description as the
response; health_check_command: nullable string with the same description), or
refactor the request bodies to reference the shared schema that includes these
fields so API clients can submit CMD-based health checks.

Comment thread openapi.yaml
Comment on lines +6419 to +6428
health_check_type:
type: string
description: 'Health check type: http or cmd.'
enum:
- http
- cmd
health_check_command:
type: string
nullable: true
description: 'Health check command for CMD type.'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Solid addition, but align defaults and request bodies with the new health check type

Nice, the new health_check_type / health_check_command pair makes the API truthfully describe CMD-based health checks instead of pretending everything is HTTP. Even Skynet likes accurate schemas.

A couple of tweaks before deployment to your glorious metal servers:

  1. Add explicit default to match the DB/migration
    Since the migration sets health_check_type default to 'http', it would be clearer to declare that here as well so clients and generated SDKs see the same contract:

    Suggested schema tweak
    health_check_type:
      type: string
  • description: 'Health check type: http or cmd.'
  • description: 'Health check type: http or cmd.'
  • default: http
    enum:
    • http
    • cmd
</details>

2. **Consider exposing these fields in application create/update request bodies**  
The various application create/update endpoints (`/applications/*` POSTs and `PATCH /applications/{uuid}`) already expose all the other `health_check_*` properties but not `health_check_type` or `health_check_command`.  
If the API is supposed to let users configure CMD health checks (not just read them), it would be good to add these two properties there too, otherwise the spec suggests partial configurability.

3. **Clarify cmd-only usage in the description (optional)**  
To avoid confusion with all the HTTP-centric fields, you might slightly tighten the description for `health_check_command`, e.g. “Used only when `health_check_type` is `cmd`”, so API consumers don’t expect it to do anything for `http` checks.

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
     health_check_type:
       type: string
       description: 'Health check type: http or cmd.'
       default: http
       enum:
         - http
         - cmd
     health_check_command:
       type: string
       nullable: true
       description: 'Health check command for CMD type.'
🤖 Prompt for AI Agents
openapi.yaml lines 6419-6428: the new health_check_type/health_check_command
schema needs three changes: add default: 'http' to health_check_type to match
the DB/migration; add health_check_type and health_check_command properties to
the application create and update request bodies (the POST /applications/* and
PATCH /applications/{uuid} schemas) so clients can set CMD checks as well as
read them; and update the health_check_command description to "Used only when
health_check_type is 'cmd'." to clarify cmd-only usage.

Comment on lines +53 to +62
@else
{{-- CMD Healthcheck Fields --}}
<div class="flex flex-col gap-2">
<x-forms.textarea canGate="update" :canResource="$resource" id="healthCheckCommand"
label="Command"
placeholder="Example: pg_isready -U postgres&#10;Example: redis-cli ping&#10;Example: curl -f http://localhost:8080/health"
helper="The command to run inside the container. It should exit with code 0 on success and non-zero on failure."
required />
</div>
@endif

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Conditional required attribute may cause validation issues when switching types.

The required attribute on the Command textarea (line 60) will cause browser-level validation errors even when the type is set to 'http'. Consider making the required attribute conditional based on healthCheckType.

Like a taco without the shell, this validation needs some structure - but only when you're actually eating a taco, not when you're having nachos.

🔎 Proposed fix to make `required` conditional
             <x-forms.textarea canGate="update" :canResource="$resource" id="healthCheckCommand"
                 label="Command"
                 placeholder="Example: pg_isready -U postgres&#10;Example: redis-cli ping&#10;Example: curl -f http://localhost:8080/health"
                 helper="The command to run inside the container. It should exit with code 0 on success and non-zero on failure."
-                required />
+                :required="$healthCheckType === 'cmd'" />
🤖 Prompt for AI Agents
In resources/views/livewire/project/shared/health-checks.blade.php around lines
53–62, the Command textarea currently always has the required attribute which
triggers browser validation even when healthCheckType is 'http'; make the
required attribute conditional (e.g. only add required when $healthCheckType ===
'cmd' using Blade or a :required binding) and also update the corresponding
Livewire/server-side validation rules to only validate presence of the command
when healthCheckType is 'cmd'.

@adiologydev

adiologydev commented Dec 25, 2025

Copy link
Copy Markdown
Member Author

Will checkout what coderabbit says later.

@gitguardian

gitguardian Bot commented Feb 21, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@andrasbacsai

Copy link
Copy Markdown
Member

I will close this on in favor of #8612.

CMD not allowed to have shell operators, so it will be a bit more secure than allowing everything.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Mar 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants