Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ Commands can use `server` or `servers` interchangeably.
- `--git-branch <branch>` - Git branch
- `--git-repository <url>` - Git repository URL
- `--domains <domains>` - Domains (comma-separated)
- `--compose-domain <service>=<url>[,<url>]` - Docker Compose service domains (repeatable; update replaces the existing mapping)
- `--build-command <cmd>` - Build command
- `--start-command <cmd>` - Start command
- `--install-command <cmd>` - Install command
Expand All @@ -160,6 +161,7 @@ Commands can use `server` or `servers` interchangeably.
- `--show-timestamps` - Include timestamps in logs
- `coolify app move <uuid> --environment-uuid <uuid>` - Move an application to another environment
- `coolify app tag list|add|remove` - Manage application tags
- Git-backed application create variants (`public`, `github`, and `deploy-key`) and `app update` support repeatable `--compose-domain <service>=<url>[,<url>]` flags for Docker Compose routing. On update, the supplied entries replace the existing mapping.
- All application create variants support `--tag` and `--tags`; create and update expose the full application settings surface.

#### Application Environment Variables
Expand Down
42 changes: 42 additions & 0 deletions cmd/application/appflags/compose_domains.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package appflags

import (
"fmt"
"strings"

"github.com/spf13/cobra"

"github.com/coollabsio/coolify-cli/internal/models"
)

// BindComposeDomainsFlag registers service-specific domains for Docker Compose applications.
func BindComposeDomainsFlag(cmd *cobra.Command) {
cmd.Flags().StringArray("compose-domain", nil, "Docker Compose service domain in <service>=<url>[,<url>] format (repeatable)")
}

// ApplyComposeDomainsFlag copies explicitly supplied service domains to an API request.
func ApplyComposeDomainsFlag(cmd *cobra.Command, target *[]models.DockerComposeDomain) (bool, error) {
if !cmd.Flags().Changed("compose-domain") {
return false, nil
}

values, _ := cmd.Flags().GetStringArray("compose-domain")
domains := make([]models.DockerComposeDomain, 0, len(values))
for _, value := range values {
name, domain, found := strings.Cut(value, "=")
if !found {
return false, fmt.Errorf("invalid --compose-domain %q: expected <service>=<url>", value)
}
name = strings.TrimSpace(name)
if name == "" {
return false, fmt.Errorf("invalid --compose-domain %q: service name cannot be empty", value)
}
domains = append(domains, models.DockerComposeDomain{
Name: name,
Domain: strings.TrimSpace(domain),
})
}

*target = domains
return true, nil
}
79 changes: 79 additions & 0 deletions cmd/application/appflags/compose_domains_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package appflags

import (
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/coollabsio/coolify-cli/internal/models"
)

func TestApplyComposeDomainsFlag_PreservesCommaSeparatedURLsAndEquals(t *testing.T) {
cmd := &cobra.Command{Use: "test"}
BindComposeDomainsFlag(cmd)
require.NoError(t, cmd.Flags().Set("compose-domain", "litellm=https://litellm.example.com"))
require.NoError(t, cmd.Flags().Set("compose-domain", "admin=https://admin.example.com,https://admin2.example.com/login?next=/users&role=admin"))

var domains []models.DockerComposeDomain
changed, err := ApplyComposeDomainsFlag(cmd, &domains)

require.NoError(t, err)
assert.True(t, changed)
assert.Equal(t, []models.DockerComposeDomain{
{Name: "litellm", Domain: "https://litellm.example.com"},
{Name: "admin", Domain: "https://admin.example.com,https://admin2.example.com/login?next=/users&role=admin"},
}, domains)
}

func TestApplyComposeDomainsFlag_AllowsEmptyDomain(t *testing.T) {
cmd := &cobra.Command{Use: "test"}
BindComposeDomainsFlag(cmd)
require.NoError(t, cmd.Flags().Set("compose-domain", "admin="))

var domains []models.DockerComposeDomain
changed, err := ApplyComposeDomainsFlag(cmd, &domains)

require.NoError(t, err)
assert.True(t, changed)
assert.Equal(t, []models.DockerComposeDomain{{Name: "admin", Domain: ""}}, domains)
}

func TestApplyComposeDomainsFlag_RejectsMalformedValues(t *testing.T) {
tests := []struct {
name string
value string
want string
}{
{name: "missing separator", value: "litellm", want: "expected <service>=<url>"},
{name: "empty service", value: " =https://example.com", want: "service name cannot be empty"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := &cobra.Command{Use: "test"}
BindComposeDomainsFlag(cmd)
require.NoError(t, cmd.Flags().Set("compose-domain", tt.value))

var domains []models.DockerComposeDomain
changed, err := ApplyComposeDomainsFlag(cmd, &domains)

assert.False(t, changed)
require.Error(t, err)
assert.Contains(t, err.Error(), tt.want)
})
}
}

func TestApplyComposeDomainsFlag_OmitsUnchangedFlag(t *testing.T) {
cmd := &cobra.Command{Use: "test"}
BindComposeDomainsFlag(cmd)

var domains []models.DockerComposeDomain
changed, err := ApplyComposeDomainsFlag(cmd, &domains)

require.NoError(t, err)
assert.False(t, changed)
assert.Nil(t, domains)
}
17 changes: 17 additions & 0 deletions cmd/application/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestNewAppCommand_RegistersMoveAndTagCommands(t *testing.T) {
func TestNewApplicationCommands_ExposeParityFlags(t *testing.T) {
assert.NotNil(t, NewLogsCommand().Flags().Lookup("show-timestamps"))
assert.NotNil(t, NewMoveCommand().Flags().Lookup("environment-uuid"))
assert.NotNil(t, NewUpdateCommand().Flags().Lookup("compose-domain"))

for _, flag := range []string{
"disable-build-cache", "docker-images-to-keep", "include-source-commit-in-build",
Expand All @@ -41,3 +42,19 @@ func TestNewApplicationCommands_ExposeParityFlags(t *testing.T) {
assert.NotNil(t, NewUpdateCommand().Flags().Lookup(flag), "missing --%s", flag)
}
}

func TestUpdateCommand_RejectsDomainsWithComposeDomain(t *testing.T) {
cmd := NewUpdateCommand()
cmd.SilenceErrors = true
cmd.SilenceUsage = true
cmd.SetArgs([]string{
"app-uuid",
"--domains", "https://app.example.com",
"--compose-domain", "app=https://app.example.com",
})

err := cmd.Execute()

require.Error(t, err)
assert.Contains(t, err.Error(), "group [domains compose-domain]")
}
13 changes: 13 additions & 0 deletions cmd/application/create/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,16 @@ func TestCreateCommands_ExposeNewSettingsAndTagsFlags(t *testing.T) {
}
}
}

func TestGitCreateCommands_ExposeComposeDomainFlag(t *testing.T) {
commands := []*cobra.Command{
NewPublicCommand(), NewGitHubCommand(), NewDeployKeyCommand(),
}

for _, command := range commands {
assert.NotNil(t, command.Flags().Lookup("compose-domain"), "%s missing --compose-domain", command.Name())
}

assert.Nil(t, NewDockerfileCommand().Flags().Lookup("compose-domain"))
assert.Nil(t, NewDockerImageCommand().Flags().Lookup("compose-domain"))
}
15 changes: 12 additions & 3 deletions cmd/application/create/deploy_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ Examples:
--private-key-uuid <uuid> --git-repository "git@github.com:owner/repo.git" --git-branch main \
--build-pack nixpacks --ports-exposes 3000

coolify app create deploy-key --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--private-key-uuid <uuid> --git-repository "git@gitlab.com:owner/repo.git" --git-branch main \
--build-pack dockerfile --ports-exposes 8080 --instant-deploy`,
coolify app create deploy-key --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--private-key-uuid <uuid> --git-repository "git@gitlab.com:owner/repo.git" --git-branch main \
--build-pack dockerfile --ports-exposes 8080 --instant-deploy

coolify app create deploy-key --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--private-key-uuid <uuid> --git-repository "git@gitlab.com:owner/repo.git" --git-branch main \
--build-pack dockercompose --ports-exposes 4000 --compose-domain "api=https://api.example.com"`,
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()

Expand Down Expand Up @@ -81,6 +85,9 @@ Examples:
setOptionalStringFlag(cmd, "name", &req.Name)
setOptionalStringFlag(cmd, "description", &req.Description)
setOptionalStringFlag(cmd, "domains", &req.Domains)
if _, err := appflags.ApplyComposeDomainsFlag(cmd, &req.DockerComposeDomains); err != nil {
return err
}
setOptionalStringFlag(cmd, "git-commit-sha", &req.GitCommitSHA)
setOptionalStringFlag(cmd, "destination-uuid", &req.DestinationUUID)
setOptionalStringFlag(cmd, "build-command", &req.BuildCommand)
Expand Down Expand Up @@ -138,6 +145,7 @@ Examples:
cmd.Flags().String("name", "", "Application name")
cmd.Flags().String("description", "", "Application description")
cmd.Flags().String("domains", "", "Domain(s) for the application")
appflags.BindComposeDomainsFlag(cmd)
cmd.Flags().Bool("instant-deploy", false, "Deploy immediately after creation")
cmd.Flags().String("git-commit-sha", "", "Specific commit SHA to deploy")
cmd.Flags().String("destination-uuid", "", "Destination UUID if server has multiple destinations")
Expand All @@ -154,6 +162,7 @@ Examples:
cmd.Flags().String("dockerfile-target-build", "", "Dockerfile target build stage")
appflags.BindSettingsFlags(cmd)
appflags.BindTagsFlag(cmd)
cmd.MarkFlagsMutuallyExclusive("domains", "compose-domain")

return cmd
}
15 changes: 12 additions & 3 deletions cmd/application/create/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ Examples:
--github-app-uuid <uuid> --git-repository "owner/repo" --git-branch main \
--build-pack nixpacks --ports-exposes 3000

coolify app create github --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--github-app-uuid <uuid> --git-repository "owner/repo" --git-branch main \
--build-pack dockerfile --ports-exposes 8080 --instant-deploy`,
coolify app create github --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--github-app-uuid <uuid> --git-repository "owner/repo" --git-branch main \
--build-pack dockerfile --ports-exposes 8080 --instant-deploy

coolify app create github --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--github-app-uuid <uuid> --git-repository "owner/repo" --git-branch main \
--build-pack dockercompose --ports-exposes 4000 --compose-domain "api=https://api.example.com"`,
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()

Expand Down Expand Up @@ -82,6 +86,9 @@ Examples:
setOptionalStringFlag(cmd, "name", &req.Name)
setOptionalStringFlag(cmd, "description", &req.Description)
setOptionalStringFlag(cmd, "domains", &req.Domains)
if _, err := appflags.ApplyComposeDomainsFlag(cmd, &req.DockerComposeDomains); err != nil {
return err
}
setOptionalStringFlag(cmd, "git-commit-sha", &req.GitCommitSHA)
setOptionalStringFlag(cmd, "destination-uuid", &req.DestinationUUID)
setOptionalStringFlag(cmd, "build-command", &req.BuildCommand)
Expand Down Expand Up @@ -139,6 +146,7 @@ Examples:
cmd.Flags().String("name", "", "Application name")
cmd.Flags().String("description", "", "Application description")
cmd.Flags().String("domains", "", "Domain(s) for the application")
appflags.BindComposeDomainsFlag(cmd)
cmd.Flags().Bool("instant-deploy", false, "Deploy immediately after creation")
cmd.Flags().String("git-commit-sha", "", "Specific commit SHA to deploy")
cmd.Flags().String("destination-uuid", "", "Destination UUID if server has multiple destinations")
Expand All @@ -155,6 +163,7 @@ Examples:
cmd.Flags().String("dockerfile-target-build", "", "Dockerfile target build stage")
appflags.BindSettingsFlags(cmd)
appflags.BindTagsFlag(cmd)
cmd.MarkFlagsMutuallyExclusive("domains", "compose-domain")

return cmd
}
15 changes: 12 additions & 3 deletions cmd/application/create/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ Examples:
coolify app create public --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--git-repository "https://github.com/user/repo" --git-branch main --build-pack nixpacks --ports-exposes 3000

coolify app create public --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--git-repository "https://github.com/user/repo" --git-branch main --build-pack dockerfile --ports-exposes 8080 \
--instant-deploy --domains "myapp.example.com"`,
coolify app create public --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--git-repository "https://github.com/user/repo" --git-branch main --build-pack dockerfile --ports-exposes 8080 \
--instant-deploy --domains "myapp.example.com"

coolify app create public --server-uuid <uuid> --project-uuid <uuid> --environment-name production \
--git-repository "https://github.com/user/repo" --git-branch main --build-pack dockercompose --ports-exposes 4000 \
--compose-domain "api=https://api.example.com" --compose-domain "admin=https://admin.example.com,https://admin2.example.com"`,
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()

Expand Down Expand Up @@ -73,6 +77,9 @@ Examples:
setOptionalStringFlag(cmd, "name", &req.Name)
setOptionalStringFlag(cmd, "description", &req.Description)
setOptionalStringFlag(cmd, "domains", &req.Domains)
if _, err := appflags.ApplyComposeDomainsFlag(cmd, &req.DockerComposeDomains); err != nil {
return err
}
setOptionalStringFlag(cmd, "git-commit-sha", &req.GitCommitSHA)
setOptionalStringFlag(cmd, "destination-uuid", &req.DestinationUUID)
setOptionalStringFlag(cmd, "build-command", &req.BuildCommand)
Expand Down Expand Up @@ -129,6 +136,7 @@ Examples:
cmd.Flags().String("name", "", "Application name")
cmd.Flags().String("description", "", "Application description")
cmd.Flags().String("domains", "", "Domain(s) for the application")
appflags.BindComposeDomainsFlag(cmd)
cmd.Flags().Bool("instant-deploy", false, "Deploy immediately after creation")
cmd.Flags().String("git-commit-sha", "", "Specific commit SHA to deploy")
cmd.Flags().String("destination-uuid", "", "Destination UUID if server has multiple destinations")
Expand All @@ -145,6 +153,7 @@ Examples:
cmd.Flags().String("dockerfile-target-build", "", "Dockerfile target build stage")
appflags.BindSettingsFlags(cmd)
appflags.BindTagsFlag(cmd)
cmd.MarkFlagsMutuallyExclusive("domains", "compose-domain")

return cmd
}
Expand Down
17 changes: 15 additions & 2 deletions cmd/application/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ func NewUpdateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "update <uuid>",
Short: "Update application configuration",
Long: `Update configuration for a specific application. Only specified fields will be updated.`,
Args: cli.ExactArgs(1, "<uuid>"),
Long: `Update configuration for a specific application. Only specified fields will be updated.

Docker Compose domains are supplied as the complete service-to-domain mapping for the update.

Example:
coolify app update <uuid> --compose-domain "api=https://api.example.com" \
--compose-domain "admin=https://admin.example.com,https://admin2.example.com"`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
uuid := args[0]
Expand Down Expand Up @@ -55,6 +61,11 @@ func NewUpdateCommand() *cobra.Command {
req.Domains = &domains
hasUpdates = true
}
if changed, err := appflags.ApplyComposeDomainsFlag(cmd, &req.DockerComposeDomains); err != nil {
return err
} else if changed {
hasUpdates = true
}
if cmd.Flags().Changed("build-command") {
buildCmd, _ := cmd.Flags().GetString("build-command")
req.BuildCommand = &buildCmd
Expand Down Expand Up @@ -153,6 +164,7 @@ func NewUpdateCommand() *cobra.Command {
cmd.Flags().String("git-branch", "", "Git branch")
cmd.Flags().String("git-repository", "", "Git repository URL")
cmd.Flags().String("domains", "", "Domains (comma-separated)")
appflags.BindComposeDomainsFlag(cmd)
cmd.Flags().String("build-command", "", "Build command")
cmd.Flags().String("start-command", "", "Start command")
cmd.Flags().String("install-command", "", "Install command")
Expand All @@ -167,6 +179,7 @@ func NewUpdateCommand() *cobra.Command {
cmd.Flags().Bool("health-check-enabled", false, "Enable health check")
cmd.Flags().String("health-check-path", "", "Health check path")
appflags.BindSettingsFlags(cmd)
cmd.MarkFlagsMutuallyExclusive("domains", "compose-domain")

return cmd
}
1 change: 1 addition & 0 deletions cmd/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ coolify app move <uuid> --environment-uuid <uuid>
coolify app tag add <uuid> production
coolify app deployments list <app-uuid>
coolify app deployments logs <app-uuid> --follow
coolify app update <uuid> --compose-domain api=https://api.example.com --compose-domain admin=https://admin.example.com,https://admin2.example.com
` + "```" + `

### Environment Variables
Expand Down
Loading
Loading