diff --git a/README.md b/README.md index 6150d0f..c79b917 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,19 @@ Commands can use `server` or `servers` interchangeably. - `--environment-name ` - Environment name (or use `--environment-uuid `) #### Application Previews +- `coolify app previews list ` - List preview deployments +- `coolify app previews get ` - Get a preview deployment +- `coolify app previews create ` - Open a preview deployment and queue its deployment (redeploys an existing preview) + - `--git-type ` - Git provider of the pull request: `github`, `gitlab`, `gitea`, or `bitbucket` (required unless the application uses a GitHub App or GitLab App source) + - `--commit ` - Commit SHA to deploy (required for Bitbucket) + - `--docker-tag ` - Docker image tag (Docker Image applications; required for a new preview) + - `--url ` - Pull request URL + - `--force` - Rebuild without cache + - `--no-deploy` - Only create the preview, do not queue a deployment +- `coolify app previews update ` - Replace the domains of a preview deployment + - `--domains ` - Domains (comma-separated) + - `--compose-domain =` - Docker Compose service domain (repeatable) + - `--force-domain-override` - Save the domains even when another resource uses them - `coolify app previews delete ` - Delete a preview deployment - `--force` - Skip confirmation prompt diff --git a/cmd/application/application.go b/cmd/application/application.go index a09e926..9ccef13 100644 --- a/cmd/application/application.go +++ b/cmd/application/application.go @@ -78,6 +78,10 @@ func NewAppCommand() *cobra.Command { Short: "Manage application preview deployments", Long: `Manage preview deployments created from pull requests. Requires the application UUID.`, } + previewsCmd.AddCommand(previews.NewListPreviewsCommand()) + previewsCmd.AddCommand(previews.NewGetPreviewCommand()) + previewsCmd.AddCommand(previews.NewCreatePreviewCommand()) + previewsCmd.AddCommand(previews.NewUpdatePreviewCommand()) previewsCmd.AddCommand(previews.NewDeletePreviewCommand()) cmd.AddCommand(previewsCmd) diff --git a/cmd/application/previews/create.go b/cmd/application/previews/create.go new file mode 100644 index 0000000..f0f99c5 --- /dev/null +++ b/cmd/application/previews/create.go @@ -0,0 +1,100 @@ +package previews + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/coollabsio/coolify-cli/internal/cli" + "github.com/coollabsio/coolify-cli/internal/models" + "github.com/coollabsio/coolify-cli/internal/output" + "github.com/coollabsio/coolify-cli/internal/service" +) + +func NewCreatePreviewCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Aliases: []string{"open", "deploy"}, + Short: "Open or redeploy a preview deployment", + Long: `Open a preview deployment for a pull request and queue its deployment. +When the preview already exists, it is deployed again. + +Git based applications need --git-type, unless they use a GitHub App or GitLab App source. +Bitbucket pull requests also need --commit. Docker Image applications need --docker-tag for a new preview.`, + Example: ` coolify app previews create 42 --git-type github + coolify app previews create 42 --git-type bitbucket --commit 1a2b3c4d + coolify app previews create 42 --docker-tag pr-42 + coolify app previews create 42 --git-type gitlab --no-deploy`, + Args: cli.ExactArgs(2, " "), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + prID, err := parsePullRequestID(args[1]) + if err != nil { + return err + } + + req := models.ApplicationPreviewCreateRequest{PullRequestID: prID} + for flag, target := range map[string]**string{ + "url": &req.PullRequestHTMLURL, + "git-type": &req.GitType, + "commit": &req.Commit, + "docker-tag": &req.DockerTag, + } { + if cmd.Flags().Changed(flag) { + value, _ := cmd.Flags().GetString(flag) + *target = &value + } + } + if cmd.Flags().Changed("force") { + force, _ := cmd.Flags().GetBool("force") + req.Force = &force + } + if noDeploy, _ := cmd.Flags().GetBool("no-deploy"); noDeploy { + instantDeploy := false + req.InstantDeploy = &instantDeploy + } + + client, err := cli.GetAPIClient(cmd) + if err != nil { + return fmt.Errorf("failed to get API client: %w", err) + } + + if err := cli.CheckMinimumVersion(ctx, client, minimumVersion); err != nil { + return err + } + + appSvc := service.NewApplicationService(client) + resp, err := appSvc.CreatePreview(ctx, args[0], req) + if err != nil { + return fmt.Errorf("failed to create preview deployment: %w", err) + } + + format, _ := cmd.Flags().GetString("format") + if format != output.FormatTable { + formatter, err := output.NewFormatter(format, output.Options{}) + if err != nil { + return err + } + return formatter.Format(resp) + } + + fmt.Println(resp.Message) + if resp.DeploymentUUID != nil && *resp.DeploymentUUID != "" { + fmt.Printf("Deployment UUID: %s\n", *resp.DeploymentUUID) + } + if resp.Preview.Domains != nil && *resp.Preview.Domains != "" { + fmt.Printf("Domains: %s\n", *resp.Preview.Domains) + } + return nil + }, + } + + cmd.Flags().String("url", "", "Pull request URL") + cmd.Flags().String("git-type", "", "Git provider of the pull request: github, gitlab, gitea, or bitbucket") + cmd.Flags().String("commit", "", "Commit SHA to deploy (required for Bitbucket)") + cmd.Flags().String("docker-tag", "", "Docker image tag (Docker Image applications only)") + cmd.Flags().Bool("force", false, "Rebuild without cache") + cmd.Flags().Bool("no-deploy", false, "Only create the preview, do not queue a deployment") + return cmd +} diff --git a/cmd/application/previews/get.go b/cmd/application/previews/get.go new file mode 100644 index 0000000..8e75492 --- /dev/null +++ b/cmd/application/previews/get.go @@ -0,0 +1,51 @@ +package previews + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/coollabsio/coolify-cli/internal/cli" + "github.com/coollabsio/coolify-cli/internal/output" + "github.com/coollabsio/coolify-cli/internal/service" +) + +func NewGetPreviewCommand() *cobra.Command { + return &cobra.Command{ + Use: "get ", + Short: "Get a preview deployment", + Long: `Get a preview deployment of an application by pull request ID.`, + Args: cli.ExactArgs(2, " "), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + prID, err := parsePullRequestID(args[1]) + if err != nil { + return err + } + + client, err := cli.GetAPIClient(cmd) + if err != nil { + return fmt.Errorf("failed to get API client: %w", err) + } + + if err := cli.CheckMinimumVersion(ctx, client, minimumVersion); err != nil { + return err + } + + appSvc := service.NewApplicationService(client) + preview, err := appSvc.GetPreview(ctx, args[0], prID) + if err != nil { + return fmt.Errorf("failed to get preview deployment: %w", err) + } + + format, _ := cmd.Flags().GetString("format") + formatter, err := output.NewFormatter(format, output.Options{}) + if err != nil { + return err + } + + return formatter.Format(preview) + }, + } +} diff --git a/cmd/application/previews/helpers.go b/cmd/application/previews/helpers.go new file mode 100644 index 0000000..8766acd --- /dev/null +++ b/cmd/application/previews/helpers.go @@ -0,0 +1,17 @@ +package previews + +import ( + "fmt" + "strconv" +) + +// minimumVersion is the first Coolify version with the preview list, get, and create endpoints. +const minimumVersion = "4.4.0" + +func parsePullRequestID(value string) (int, error) { + prID, err := strconv.Atoi(value) + if err != nil || prID <= 0 { + return 0, fmt.Errorf("invalid pr_id: must be a positive integer") + } + return prID, nil +} diff --git a/cmd/application/previews/list.go b/cmd/application/previews/list.go new file mode 100644 index 0000000..71b5213 --- /dev/null +++ b/cmd/application/previews/list.go @@ -0,0 +1,46 @@ +package previews + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/coollabsio/coolify-cli/internal/cli" + "github.com/coollabsio/coolify-cli/internal/output" + "github.com/coollabsio/coolify-cli/internal/service" +) + +func NewListPreviewsCommand() *cobra.Command { + return &cobra.Command{ + Use: "list ", + Short: "List preview deployments", + Long: `List the preview deployments of an application, newest pull request first.`, + Args: cli.ExactArgs(1, ""), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + client, err := cli.GetAPIClient(cmd) + if err != nil { + return fmt.Errorf("failed to get API client: %w", err) + } + + if err := cli.CheckMinimumVersion(ctx, client, minimumVersion); err != nil { + return err + } + + appSvc := service.NewApplicationService(client) + previews, err := appSvc.ListPreviews(ctx, args[0]) + if err != nil { + return fmt.Errorf("failed to list preview deployments: %w", err) + } + + format, _ := cmd.Flags().GetString("format") + formatter, err := output.NewFormatter(format, output.Options{}) + if err != nil { + return err + } + + return formatter.Format(previews) + }, + } +} diff --git a/cmd/application/previews/update.go b/cmd/application/previews/update.go new file mode 100644 index 0000000..19f69be --- /dev/null +++ b/cmd/application/previews/update.go @@ -0,0 +1,81 @@ +package previews + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/coollabsio/coolify-cli/cmd/application/appflags" + "github.com/coollabsio/coolify-cli/internal/cli" + "github.com/coollabsio/coolify-cli/internal/models" + "github.com/coollabsio/coolify-cli/internal/output" + "github.com/coollabsio/coolify-cli/internal/service" +) + +func NewUpdatePreviewCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "update ", + Short: "Update the domains of a preview deployment", + Long: `Replace the domains of a preview deployment. +Use --domains for regular applications and --compose-domain for Docker Compose applications. +Docker Compose domains are supplied as the complete service-to-domain mapping. +Redeploy the preview to apply the new domains.`, + Example: ` coolify app previews update 42 --domains https://pr-42.example.com + coolify app previews update 42 --compose-domain web=https://pr-42.example.com`, + Args: cli.ExactArgs(2, " "), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + prID, err := parsePullRequestID(args[1]) + if err != nil { + return err + } + + req := models.ApplicationPreviewDomainsUpdateRequest{} + if cmd.Flags().Changed("domains") { + domains, _ := cmd.Flags().GetString("domains") + req.Domains = &domains + } + composeChanged, err := appflags.ApplyComposeDomainsFlag(cmd, &req.DockerComposeDomains) + if err != nil { + return err + } + if req.Domains == nil && !composeChanged { + return fmt.Errorf("set --domains or --compose-domain") + } + if cmd.Flags().Changed("force-domain-override") { + force, _ := cmd.Flags().GetBool("force-domain-override") + req.ForceDomainOverride = &force + } + + client, err := cli.GetAPIClient(cmd) + if err != nil { + return fmt.Errorf("failed to get API client: %w", err) + } + + if err := cli.CheckMinimumVersion(ctx, client, "4.3.15"); err != nil { + return err + } + + appSvc := service.NewApplicationService(client) + resp, err := appSvc.UpdatePreviewDomains(ctx, args[0], prID, req) + if err != nil { + return fmt.Errorf("failed to update preview deployment: %w", err) + } + + format, _ := cmd.Flags().GetString("format") + formatter, err := output.NewFormatter(format, output.Options{}) + if err != nil { + return err + } + + return formatter.Format(resp) + }, + } + + cmd.Flags().String("domains", "", "Domains (comma-separated)") + appflags.BindComposeDomainsFlag(cmd) + cmd.Flags().Bool("force-domain-override", false, "Save the domains even when another resource uses them") + cmd.MarkFlagsMutuallyExclusive("domains", "compose-domain") + return cmd +} diff --git a/internal/models/application.go b/internal/models/application.go index b6959fd..5c81e01 100644 --- a/internal/models/application.go +++ b/internal/models/application.go @@ -248,6 +248,63 @@ type DockerComposeDomain struct { Domain string `json:"domain"` } +// ApplicationPreview represents a preview deployment of an application. +type ApplicationPreview struct { + UUID string `json:"uuid"` + PullRequestID int `json:"pull_request_id"` + Status string `json:"status"` + Domains *string `json:"domains"` + GitType *string `json:"git_type"` + DockerRegistryImageTag *string `json:"docker_registry_image_tag"` + PullRequestHTMLURL *string `json:"pull_request_html_url"` + DockerComposeDomains []PreviewComposeDomain `json:"docker_compose_domains" table:"-"` + DomainPortOverrides map[string]int `json:"domain_port_overrides" table:"-"` + LastOnlineAt *string `json:"last_online_at" table:"-"` + CreatedAt string `json:"created_at" table:"-"` + UpdatedAt string `json:"updated_at" table:"-"` +} + +// PreviewComposeDomain is the domain of one Docker Compose service in a preview deployment. +type PreviewComposeDomain struct { + Name string `json:"name"` + Domain *string `json:"domain"` + Redirect *string `json:"redirect,omitempty"` +} + +// ApplicationPreviewCreateRequest opens (or redeploys) a preview deployment. +type ApplicationPreviewCreateRequest struct { + PullRequestID int `json:"pull_request_id"` + PullRequestHTMLURL *string `json:"pull_request_html_url,omitempty"` + GitType *string `json:"git_type,omitempty"` + Commit *string `json:"commit,omitempty"` + DockerTag *string `json:"docker_tag,omitempty"` + Force *bool `json:"force,omitempty"` + InstantDeploy *bool `json:"instant_deploy,omitempty"` +} + +// ApplicationPreviewDeploymentResponse is returned when a preview deployment is opened. +type ApplicationPreviewDeploymentResponse struct { + Message string `json:"message"` + DeploymentUUID *string `json:"deployment_uuid"` + Preview ApplicationPreview `json:"preview"` +} + +// ApplicationPreviewDomainsUpdateRequest replaces the domains of a preview deployment. +type ApplicationPreviewDomainsUpdateRequest struct { + Domains *string `json:"domains,omitempty"` + DockerComposeDomains []DockerComposeDomain `json:"docker_compose_domains,omitempty"` + ForceDomainOverride *bool `json:"force_domain_override,omitempty"` +} + +// ApplicationPreviewDomainsUpdateResponse is returned after preview domains are replaced. +type ApplicationPreviewDomainsUpdateResponse struct { + UUID string `json:"uuid"` + PullRequestID int `json:"pull_request_id"` + Domains *string `json:"domains"` + DockerComposeDomains []PreviewComposeDomain `json:"docker_compose_domains" table:"-"` + DomainPortOverrides map[string]int `json:"domain_port_overrides" table:"-"` +} + // ApplicationLifecycleResponse represents the response from lifecycle operations type ApplicationLifecycleResponse struct { Message string `json:"message"` diff --git a/internal/service/application.go b/internal/service/application.go index 2795674..5693259 100644 --- a/internal/service/application.go +++ b/internal/service/application.go @@ -70,6 +70,46 @@ func (s *ApplicationService) DeletePreview(ctx context.Context, appUUID, prID st return nil } +// ListPreviews retrieves the preview deployments of an application +func (s *ApplicationService) ListPreviews(ctx context.Context, appUUID string) ([]models.ApplicationPreview, error) { + var previews []models.ApplicationPreview + err := s.client.Get(ctx, fmt.Sprintf("applications/%s/previews", url.PathEscape(appUUID)), &previews) + if err != nil { + return nil, fmt.Errorf("failed to list previews for application %s: %w", appUUID, err) + } + return previews, nil +} + +// GetPreview retrieves a preview deployment by pull request ID +func (s *ApplicationService) GetPreview(ctx context.Context, appUUID string, prID int) (*models.ApplicationPreview, error) { + var preview models.ApplicationPreview + err := s.client.Get(ctx, fmt.Sprintf("applications/%s/previews/%d", url.PathEscape(appUUID), prID), &preview) + if err != nil { + return nil, fmt.Errorf("failed to get preview %d for application %s: %w", prID, appUUID, err) + } + return &preview, nil +} + +// CreatePreview opens a preview deployment, or redeploys it when it already exists +func (s *ApplicationService) CreatePreview(ctx context.Context, appUUID string, req models.ApplicationPreviewCreateRequest) (*models.ApplicationPreviewDeploymentResponse, error) { + var resp models.ApplicationPreviewDeploymentResponse + err := s.client.Post(ctx, fmt.Sprintf("applications/%s/previews", url.PathEscape(appUUID)), req, &resp) + if err != nil { + return nil, fmt.Errorf("failed to create preview %d for application %s: %w", req.PullRequestID, appUUID, err) + } + return &resp, nil +} + +// UpdatePreviewDomains replaces the domains of a preview deployment +func (s *ApplicationService) UpdatePreviewDomains(ctx context.Context, appUUID string, prID int, req models.ApplicationPreviewDomainsUpdateRequest) (*models.ApplicationPreviewDomainsUpdateResponse, error) { + var resp models.ApplicationPreviewDomainsUpdateResponse + err := s.client.Patch(ctx, fmt.Sprintf("applications/%s/previews/%d", url.PathEscape(appUUID), prID), req, &resp) + if err != nil { + return nil, fmt.Errorf("failed to update preview %d for application %s: %w", prID, appUUID, err) + } + return &resp, nil +} + // Start starts an application (initiates deployment) func (s *ApplicationService) Start(ctx context.Context, uuid string, force bool, instantDeploy bool) (*models.ApplicationLifecycleResponse, error) { var resp models.ApplicationLifecycleResponse diff --git a/internal/service/application_test.go b/internal/service/application_test.go index 0ad7279..aa2db5f 100644 --- a/internal/service/application_test.go +++ b/internal/service/application_test.go @@ -450,6 +450,118 @@ func TestApplicationService_DeletePreview_ServerError(t *testing.T) { assert.Contains(t, err.Error(), "failed to delete preview") } +func TestApplicationService_ListPreviews(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/api/v1/applications/app-uuid-123/previews", r.URL.Path) + assert.Equal(t, "GET", r.Method) + + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`[{"uuid":"preview-1","pull_request_id":42,"status":"running:healthy","domains":"https://pr-42.example.com","git_type":"github","docker_compose_domains":null,"domain_port_overrides":{"https://pr-42.example.com":3000}}]`)) + })) + defer server.Close() + + svc := NewApplicationService(api.NewClient(server.URL, "test-token")) + + previews, err := svc.ListPreviews(context.Background(), "app-uuid-123") + require.NoError(t, err) + require.Len(t, previews, 1) + assert.Equal(t, 42, previews[0].PullRequestID) + assert.Equal(t, "https://pr-42.example.com", *previews[0].Domains) + assert.Equal(t, 3000, previews[0].DomainPortOverrides["https://pr-42.example.com"]) +} + +func TestApplicationService_GetPreview_NotFound(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/api/v1/applications/app-uuid-123/previews/7", r.URL.Path) + + w.WriteHeader(http.StatusNotFound) + _, _ = w.Write([]byte(`{"message":"Preview not found."}`)) + })) + defer server.Close() + + svc := NewApplicationService(api.NewClient(server.URL, "test-token")) + + _, err := svc.GetPreview(context.Background(), "app-uuid-123", 7) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to get preview 7") +} + +func TestApplicationService_CreatePreview(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/api/v1/applications/app-uuid-123/previews", r.URL.Path) + assert.Equal(t, "POST", r.Method) + + var body map[string]any + require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) + assert.Equal(t, map[string]any{ + "pull_request_id": float64(42), + "git_type": "bitbucket", + "commit": "1a2b3c4d", + "instant_deploy": false, + }, body) + + w.WriteHeader(http.StatusCreated) + _, _ = w.Write([]byte(`{"message":"Preview created.","deployment_uuid":null,"preview":{"uuid":"preview-1","pull_request_id":42,"git_type":"bitbucket"}}`)) + })) + defer server.Close() + + svc := NewApplicationService(api.NewClient(server.URL, "test-token")) + gitType := "bitbucket" + commit := "1a2b3c4d" + instantDeploy := false + + resp, err := svc.CreatePreview(context.Background(), "app-uuid-123", models.ApplicationPreviewCreateRequest{ + PullRequestID: 42, + GitType: &gitType, + Commit: &commit, + InstantDeploy: &instantDeploy, + }) + require.NoError(t, err) + assert.Equal(t, "Preview created.", resp.Message) + assert.Nil(t, resp.DeploymentUUID) + assert.Equal(t, "preview-1", resp.Preview.UUID) +} + +func TestApplicationService_CreatePreview_ValidationError(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusUnprocessableEntity) + _, _ = w.Write([]byte(`{"message":"Validation failed.","errors":{"git_type":["The git type field is required."]}}`)) + })) + defer server.Close() + + svc := NewApplicationService(api.NewClient(server.URL, "test-token")) + + _, err := svc.CreatePreview(context.Background(), "app-uuid-123", models.ApplicationPreviewCreateRequest{PullRequestID: 42}) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to create preview 42") +} + +func TestApplicationService_UpdatePreviewDomains(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/api/v1/applications/app-uuid-123/previews/42", r.URL.Path) + assert.Equal(t, "PATCH", r.Method) + + var body map[string]any + require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) + assert.Equal(t, map[string]any{ + "docker_compose_domains": []any{map[string]any{"name": "web", "domain": "https://pr-42.example.com"}}, + }, body) + + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"uuid":"preview-1","pull_request_id":42,"domains":"https://pr-42.example.com","docker_compose_domains":[{"name":"web","domain":"https://pr-42.example.com"}],"domain_port_overrides":null}`)) + })) + defer server.Close() + + svc := NewApplicationService(api.NewClient(server.URL, "test-token")) + + resp, err := svc.UpdatePreviewDomains(context.Background(), "app-uuid-123", 42, models.ApplicationPreviewDomainsUpdateRequest{ + DockerComposeDomains: []models.DockerComposeDomain{{Name: "web", Domain: "https://pr-42.example.com"}}, + }) + require.NoError(t, err) + require.Len(t, resp.DockerComposeDomains, 1) + assert.Equal(t, "web", resp.DockerComposeDomains[0].Name) +} + func TestApplicationService_Start(t *testing.T) { deploymentUUID := "deploy-uuid-123" server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/llms-full.txt b/llms-full.txt index f1e1b06..3787675 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -52,6 +52,7 @@ All commands support `--format` flag: Aliases are derived from the CLI command tree: - `coolify app destinations` | `coolify app destination` - `coolify app env` | `coolify app envs` | `coolify app environment` +- `coolify app previews create` | `coolify app previews open` | `coolify app previews deploy` - `coolify app previews` | `coolify app preview` - `coolify app start` | `coolify app deploy` - `coolify app storage` | `coolify app storages` @@ -1576,6 +1577,36 @@ Parameters: description: Target environment UUID (required) required: true +Command: coolify app previews create +Description: Open or redeploy a preview deployment +Parameters: + - name: --commit + type: string + description: Commit SHA to deploy (required for Bitbucket) + required: false + - name: --docker-tag + type: string + description: Docker image tag (Docker Image applications only) + required: false + - name: --force + type: boolean + description: Rebuild without cache + required: false + default: false + - name: --git-type + type: string + description: Git provider of the pull request: github, gitlab, gitea, or bitbucket + required: false + - name: --no-deploy + type: boolean + description: Only create the preview, do not queue a deployment + required: false + default: false + - name: --url + type: string + description: Pull request URL + required: false + Command: coolify app previews delete Description: Delete a preview deployment for an application. First argument is the application UUID, second is the pull request ID. Parameters: @@ -1585,6 +1616,31 @@ Parameters: required: false default: false +Command: coolify app previews get +Description: Get a preview deployment of an application by pull request ID. +Parameters: (None) + +Command: coolify app previews list +Description: List the preview deployments of an application, newest pull request first. +Parameters: (None) + +Command: coolify app previews update +Description: Update the domains of a preview deployment +Parameters: + - name: --compose-domain + type: stringArray + description: Docker Compose service domain in =[,] format (repeatable) + required: false + - name: --domains + type: string + description: Domains (comma-separated) + required: false + - name: --force-domain-override + type: boolean + description: Save the domains even when another resource uses them + required: false + default: false + Command: coolify app restart Description: Restart a running application. Parameters: (None)