feat: add custom health check commands per app
All checks were successful
Check / check (pull_request) Successful in 1m53s

Add configurable health check commands per app via a new
'healthcheck_command' field. When set, the command is passed
to Docker as a CMD-SHELL health check on the container.
When empty, the image's default health check is used.

Changes:
- Add migration 007 for healthcheck_command column on apps table
- Add HealthcheckCommand field to App model with full CRUD support
- Add buildHealthcheck() to docker client for CMD-SHELL config
- Pass health check command through CreateContainerOptions
- Add health check command input to app create/edit UI forms
- Extract optionalNullString helper to reduce handler complexity
- Update README features list

closes #81
This commit is contained in:
user
2026-03-17 02:11:08 -07:00
parent fd110e69db
commit e2522f2017
12 changed files with 342 additions and 92 deletions

View File

@@ -704,6 +704,72 @@ func TestAppGetWebhookEvents(t *testing.T) {
assert.Len(t, events, 1)
}
// App HealthcheckCommand Tests.
func TestAppHealthcheckCommand(t *testing.T) {
t.Parallel()
t.Run("saves and loads healthcheck command", func(t *testing.T) {
t.Parallel()
testDB, cleanup := setupTestDB(t)
defer cleanup()
app := createTestApp(t, testDB)
app.HealthcheckCommand = sql.NullString{
String: "curl -f http://localhost:8080/healthz || exit 1",
Valid: true,
}
err := app.Save(context.Background())
require.NoError(t, err)
found, err := models.FindApp(context.Background(), testDB, app.ID)
require.NoError(t, err)
require.NotNil(t, found)
assert.True(t, found.HealthcheckCommand.Valid)
assert.Equal(t, "curl -f http://localhost:8080/healthz || exit 1", found.HealthcheckCommand.String)
})
t.Run("null when not set", func(t *testing.T) {
t.Parallel()
testDB, cleanup := setupTestDB(t)
defer cleanup()
app := createTestApp(t, testDB)
found, err := models.FindApp(context.Background(), testDB, app.ID)
require.NoError(t, err)
require.NotNil(t, found)
assert.False(t, found.HealthcheckCommand.Valid)
})
t.Run("can be cleared", func(t *testing.T) {
t.Parallel()
testDB, cleanup := setupTestDB(t)
defer cleanup()
app := createTestApp(t, testDB)
app.HealthcheckCommand = sql.NullString{String: "true", Valid: true}
err := app.Save(context.Background())
require.NoError(t, err)
// Clear it
app.HealthcheckCommand = sql.NullString{}
err = app.Save(context.Background())
require.NoError(t, err)
found, err := models.FindApp(context.Background(), testDB, app.ID)
require.NoError(t, err)
require.NotNil(t, found)
assert.False(t, found.HealthcheckCommand.Valid)
})
}
// Cascade Delete Tests.
//nolint:funlen // Test function with many assertions - acceptable for integration tests