Merge remote-tracking branch 'origin/v4.x' into coolify-init-wireguard-mesh

This commit is contained in:
Andras Bacsai
2026-04-20 13:36:31 +02:00
6 changed files with 151 additions and 1 deletions
+11
View File
@@ -5,6 +5,7 @@ import (
"github.com/coollabsio/coolify-cli/cmd/application/create"
"github.com/coollabsio/coolify-cli/cmd/application/env"
"github.com/coollabsio/coolify-cli/cmd/application/previews"
"github.com/coollabsio/coolify-cli/cmd/application/storage"
)
@@ -57,5 +58,15 @@ func NewAppCommand() *cobra.Command {
storageCmd.AddCommand(storage.NewDeleteCommand())
cmd.AddCommand(storageCmd)
// Add previews subcommand with its children
previewsCmd := &cobra.Command{
Use: "previews",
Aliases: []string{"preview"},
Short: "Manage application preview deployments",
Long: `Manage preview deployments created from pull requests. Requires the application UUID.`,
}
previewsCmd.AddCommand(previews.NewDeletePreviewCommand())
cmd.AddCommand(previewsCmd)
return cmd
}
+72
View File
@@ -0,0 +1,72 @@
package previews
import (
"fmt"
"strconv"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
)
func NewDeletePreviewCommand() *cobra.Command {
deletePreviewCmd := &cobra.Command{
Use: "delete <app_uuid> <pr_id>",
Short: "Delete a preview deployment",
Long: `Delete a preview deployment for an application. First argument is the application UUID, second is the pull request ID.`,
Args: cli.ExactArgs(2, "<app_uuid> <pr_id>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
appUUID := args[0]
prID := args[1]
prIDInt, err := strconv.Atoi(prID)
if err != nil {
return fmt.Errorf("invalid pr_id: must be an integer")
}
if prIDInt <= 0 {
return fmt.Errorf("invalid pr_id: must be a positive integer")
}
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.0.0-beta.474"); err != nil {
return err
}
force, _ := cmd.Flags().GetBool("force")
// Prompt for confirmation unless --force is used
if !force {
var response string
fmt.Printf("Are you sure you want to delete the preview deployment for PR %s? (yes/no): ", prID)
_, err := fmt.Scanln(&response)
if err != nil {
return fmt.Errorf("failed to read confirmation: %w", err)
}
if response != "yes" && response != "y" {
fmt.Println("Delete cancelled.")
return nil
}
}
appSvc := service.NewApplicationService(client)
err = appSvc.DeletePreview(ctx, appUUID, prID)
if err != nil {
return fmt.Errorf("failed to delete preview deployment: %w", err)
}
fmt.Printf("Preview deployment for PR %s deleted successfully.\n", prID)
return nil
},
}
deletePreviewCmd.Flags().Bool("force", false, "Skip confirmation prompt")
return deletePreviewCmd
}
+9
View File
@@ -59,6 +59,15 @@ func (s *ApplicationService) Delete(ctx context.Context, uuid string) error {
return nil
}
// DeletePreview deletes a preview deployment for an application
func (s *ApplicationService) DeletePreview(ctx context.Context, appUUID, prID string) error {
err := s.client.Delete(ctx, fmt.Sprintf("applications/%s/previews/%s", appUUID, prID))
if err != nil {
return fmt.Errorf("failed to delete preview %s for application %s: %w", prID, appUUID, err)
}
return 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
+48
View File
@@ -402,6 +402,54 @@ func TestApplicationService_Delete_Error(t *testing.T) {
assert.Contains(t, err.Error(), "failed to delete application")
}
func TestApplicationService_DeletePreview_Success(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, "DELETE", r.Method)
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"))
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"message":"Preview deletion request queued."}`))
}))
defer server.Close()
client := api.NewClient(server.URL, "test-token")
svc := NewApplicationService(client)
err := svc.DeletePreview(context.Background(), "app-uuid-123", "42")
require.NoError(t, err)
}
func TestApplicationService_DeletePreview_NotFound(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"message":"Preview not found."}`))
}))
defer server.Close()
client := api.NewClient(server.URL, "test-token")
svc := NewApplicationService(client)
err := svc.DeletePreview(context.Background(), "app-uuid-123", "999")
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to delete preview")
}
func TestApplicationService_DeletePreview_ServerError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(`{"message":"internal server error"}`))
}))
defer server.Close()
client := api.NewClient(server.URL, "test-token")
svc := NewApplicationService(client)
err := svc.DeletePreview(context.Background(), "app-uuid-123", "42")
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to delete preview")
}
func TestApplicationService_Start(t *testing.T) {
deploymentUUID := "deploy-uuid-123"
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+1 -1
View File
@@ -15,7 +15,7 @@ import (
// Version variables injected by GoReleaser at build time via ldflags
var (
version = "v1.6.1"
version = "v1.6.2"
)
// GitHubAPIURL is the URL for fetching CLI version tags (exported for testing)
+10
View File
@@ -51,6 +51,7 @@ All commands support `--format` flag:
Aliases are derived from the CLI command tree:
- `coolify app env` | `coolify app envs` | `coolify app environment`
- `coolify app previews` | `coolify app preview`
- `coolify app start` | `coolify app deploy`
- `coolify app storage` | `coolify app storages`
- `coolify app` | `coolify apps` | `coolify application` | `coolify applications`
@@ -851,6 +852,15 @@ Parameters:
required: false
default: 100
Command: coolify app previews delete <app_uuid> <pr_id>
Description: Delete a preview deployment for an application. First argument is the application UUID, second is the pull request ID.
Parameters:
- name: --force
type: boolean
description: Skip confirmation prompt
required: false
default: false
Command: coolify app restart <uuid>
Description: Restart a running application.
Parameters: (None)