forked from mirror/coolify-cli
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 780b3674c7 | |||
| 77adbfaebc | |||
| 9215fd537e | |||
| 26c0925854 | |||
| 1f1b187ed2 | |||
| 4af598c213 | |||
| 6ca3b700ce | |||
| 8cf0b71ebf |
Vendored
+6
-1
@@ -31,6 +31,7 @@ func NewCreateEnvCommand() *cobra.Command {
|
||||
isPreview, _ := cmd.Flags().GetBool("preview")
|
||||
isLiteral, _ := cmd.Flags().GetBool("is-literal")
|
||||
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
|
||||
isRuntime, _ := cmd.Flags().GetBool("runtime")
|
||||
|
||||
if key == "" {
|
||||
return fmt.Errorf("--key is required")
|
||||
@@ -56,6 +57,9 @@ func NewCreateEnvCommand() *cobra.Command {
|
||||
if cmd.Flags().Changed("is-multiline") {
|
||||
req.IsMultiline = &isMultiline
|
||||
}
|
||||
if cmd.Flags().Changed("runtime") {
|
||||
req.IsRuntime = &isRuntime
|
||||
}
|
||||
|
||||
appSvc := service.NewApplicationService(client)
|
||||
env, err := appSvc.CreateEnv(ctx, appUUID, req)
|
||||
@@ -71,9 +75,10 @@ func NewCreateEnvCommand() *cobra.Command {
|
||||
|
||||
cmd.Flags().String("key", "", "Environment variable key (required)")
|
||||
cmd.Flags().String("value", "", "Environment variable value (required)")
|
||||
cmd.Flags().Bool("build-time", false, "Available at build time")
|
||||
cmd.Flags().Bool("build-time", true, "Available at build time (default: true)")
|
||||
cmd.Flags().Bool("preview", false, "Available in preview deployments")
|
||||
cmd.Flags().Bool("is-literal", false, "Treat value as literal (don't interpolate variables)")
|
||||
cmd.Flags().Bool("is-multiline", false, "Value is multiline")
|
||||
cmd.Flags().Bool("runtime", true, "Available at runtime (default: true)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
Vendored
+5
-1
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func NewGetEnvCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
cmd := &cobra.Command{
|
||||
Use: "get <app_uuid> <env_uuid_or_key>",
|
||||
Short: "Get environment variable details",
|
||||
Long: `Get detailed information about a specific environment variable by UUID or key name.`,
|
||||
@@ -27,6 +27,8 @@ func NewGetEnvCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
appSvc := service.NewApplicationService(client)
|
||||
|
||||
// First try to get by the identifier directly
|
||||
env, err := appSvc.GetEnv(ctx, appUUID, envUUIDOrKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get environment variable: %w", err)
|
||||
@@ -53,4 +55,6 @@ func NewGetEnvCommand() *cobra.Command {
|
||||
return formatter.Format(env)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
Vendored
+32
-2
@@ -2,19 +2,21 @@ package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"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 NewListEnvCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
cmd := &cobra.Command{
|
||||
Use: "list <app_uuid>",
|
||||
Short: "List all environment variables for an application",
|
||||
Long: `List all environment variables for a specific application.`,
|
||||
Long: `List all environment variables for a specific application. By default, only non-preview environment variables are shown. Use --preview to show preview environment variables instead, or --all to show all variables (non-preview first, then preview).`,
|
||||
Args: cli.ExactArgs(1, "<uuid>"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
@@ -31,6 +33,29 @@ func NewListEnvCommand() *cobra.Command {
|
||||
return fmt.Errorf("failed to list environment variables: %w", err)
|
||||
}
|
||||
|
||||
// Filter by preview/all flags
|
||||
showAll, _ := cmd.Flags().GetBool("all")
|
||||
showPreview, _ := cmd.Flags().GetBool("preview")
|
||||
|
||||
if showAll {
|
||||
// Sort: non-preview first, then preview
|
||||
sort.SliceStable(envs, func(i, j int) bool {
|
||||
if envs[i].IsPreview != envs[j].IsPreview {
|
||||
return !envs[i].IsPreview // non-preview (false) comes before preview (true)
|
||||
}
|
||||
return false // maintain original order within groups
|
||||
})
|
||||
} else {
|
||||
// Filter by preview flag
|
||||
var filtered []models.EnvironmentVariable
|
||||
for _, env := range envs {
|
||||
if env.IsPreview == showPreview {
|
||||
filtered = append(filtered, env)
|
||||
}
|
||||
}
|
||||
envs = filtered
|
||||
}
|
||||
|
||||
format, _ := cmd.Flags().GetString("format")
|
||||
showSensitive, _ := cmd.Flags().GetBool("show-sensitive")
|
||||
|
||||
@@ -54,4 +79,9 @@ func NewListEnvCommand() *cobra.Command {
|
||||
return formatter.Format(envs)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Bool("preview", false, "Show preview environment variables instead of regular ones")
|
||||
cmd.Flags().Bool("all", false, "Show all environment variables (non-preview first, then preview)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
Vendored
+6
-1
@@ -40,6 +40,7 @@ Example: coolify app env sync abc123 --file .env.production`,
|
||||
isBuildTime, _ := cmd.Flags().GetBool("build-time")
|
||||
isPreview, _ := cmd.Flags().GetBool("preview")
|
||||
isLiteral, _ := cmd.Flags().GetBool("is-literal")
|
||||
isRuntime, _ := cmd.Flags().GetBool("runtime")
|
||||
|
||||
// Parse the .env file
|
||||
envVars, err := parser.ParseEnvFile(filePath)
|
||||
@@ -87,6 +88,9 @@ Example: coolify app env sync abc123 --file .env.production`,
|
||||
if cmd.Flags().Changed("is-literal") {
|
||||
req.IsLiteral = &isLiteral
|
||||
}
|
||||
if cmd.Flags().Changed("runtime") {
|
||||
req.IsRuntime = &isRuntime
|
||||
}
|
||||
|
||||
// Auto-detect multiline values
|
||||
if strings.Contains(envVar.Value, "\n") {
|
||||
@@ -147,8 +151,9 @@ Example: coolify app env sync abc123 --file .env.production`,
|
||||
}
|
||||
|
||||
syncEnvCmd.Flags().StringP("file", "f", "", "Path to .env file (required)")
|
||||
syncEnvCmd.Flags().Bool("build-time", false, "Make all variables available at build time")
|
||||
syncEnvCmd.Flags().Bool("build-time", true, "Make all variables available at build time (default: true)")
|
||||
syncEnvCmd.Flags().Bool("preview", false, "Make all variables available in preview deployments")
|
||||
syncEnvCmd.Flags().Bool("is-literal", false, "Treat all values as literal (don't interpolate variables)")
|
||||
syncEnvCmd.Flags().Bool("runtime", true, "Make all variables available at runtime (default: true)")
|
||||
return syncEnvCmd
|
||||
}
|
||||
|
||||
Vendored
+7
-2
@@ -54,8 +54,12 @@ func NewUpdateEnvCommand() *cobra.Command {
|
||||
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
|
||||
req.IsMultiline = &isMultiline
|
||||
}
|
||||
if cmd.Flags().Changed("runtime") {
|
||||
isRuntime, _ := cmd.Flags().GetBool("runtime")
|
||||
req.IsRuntime = &isRuntime
|
||||
}
|
||||
|
||||
if req.Key == nil && req.Value == nil && req.IsBuildTime == nil && req.IsPreview == nil && req.IsLiteral == nil && req.IsMultiline == nil {
|
||||
if req.Key == nil && req.Value == nil && req.IsBuildTime == nil && req.IsPreview == nil && req.IsLiteral == nil && req.IsMultiline == nil && req.IsRuntime == nil {
|
||||
return fmt.Errorf("at least one field must be provided to update")
|
||||
}
|
||||
|
||||
@@ -72,9 +76,10 @@ func NewUpdateEnvCommand() *cobra.Command {
|
||||
|
||||
cmd.Flags().String("key", "", "New environment variable key")
|
||||
cmd.Flags().String("value", "", "New environment variable value")
|
||||
cmd.Flags().Bool("build-time", false, "Available at build time")
|
||||
cmd.Flags().Bool("build-time", true, "Available at build time (default: true)")
|
||||
cmd.Flags().Bool("preview", false, "Available in preview deployments")
|
||||
cmd.Flags().Bool("is-literal", false, "Treat value as literal")
|
||||
cmd.Flags().Bool("is-multiline", false, "Value is multiline")
|
||||
cmd.Flags().Bool("runtime", true, "Available at runtime (default: true)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
+2
-21
@@ -6,7 +6,6 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
compareVersion "github.com/hashicorp/go-version"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
@@ -144,24 +143,6 @@ func initConfig() {
|
||||
// They are loaded on-demand by getAPIClient() based on --instance or default instance
|
||||
// This allows --instance flag to work correctly
|
||||
|
||||
// Check for updates
|
||||
latestVersionStr, err := version.CheckLatestVersionOfCli(Debug)
|
||||
if err != nil {
|
||||
if Debug {
|
||||
log.Println("Failed to check for updates:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Compare versions properly using semantic versioning
|
||||
if latestVersionStr != "" {
|
||||
latestVersion, err := compareVersion.NewVersion(latestVersionStr)
|
||||
if err == nil {
|
||||
currentVersion, err := compareVersion.NewVersion(version.GetVersion())
|
||||
if err == nil && latestVersion.GreaterThan(currentVersion) {
|
||||
if Debug {
|
||||
log.Printf("New version of Coolify CLI is available: %s\n", latestVersionStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for updates (errors are handled silently inside the function)
|
||||
_, _ = version.CheckLatestVersionOfCli(Debug)
|
||||
}
|
||||
|
||||
Vendored
+7
-7
@@ -28,9 +28,9 @@ func NewCreateCommand() *cobra.Command {
|
||||
key, _ := cmd.Flags().GetString("key")
|
||||
value, _ := cmd.Flags().GetString("value")
|
||||
isBuildTime, _ := cmd.Flags().GetBool("build-time")
|
||||
isPreview, _ := cmd.Flags().GetBool("preview")
|
||||
isLiteral, _ := cmd.Flags().GetBool("is-literal")
|
||||
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
|
||||
isRuntime, _ := cmd.Flags().GetBool("runtime")
|
||||
|
||||
if key == "" {
|
||||
return fmt.Errorf("--key is required")
|
||||
@@ -39,7 +39,7 @@ func NewCreateCommand() *cobra.Command {
|
||||
return fmt.Errorf("--value is required")
|
||||
}
|
||||
|
||||
req := &models.EnvironmentVariableCreateRequest{
|
||||
req := &models.ServiceEnvironmentVariableCreateRequest{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
@@ -48,15 +48,15 @@ func NewCreateCommand() *cobra.Command {
|
||||
if cmd.Flags().Changed("build-time") {
|
||||
req.IsBuildTime = &isBuildTime
|
||||
}
|
||||
if cmd.Flags().Changed("preview") {
|
||||
req.IsPreview = &isPreview
|
||||
}
|
||||
if cmd.Flags().Changed("is-literal") {
|
||||
req.IsLiteral = &isLiteral
|
||||
}
|
||||
if cmd.Flags().Changed("is-multiline") {
|
||||
req.IsMultiline = &isMultiline
|
||||
}
|
||||
if cmd.Flags().Changed("runtime") {
|
||||
req.IsRuntime = &isRuntime
|
||||
}
|
||||
|
||||
serviceSvc := service.NewService(client)
|
||||
env, err := serviceSvc.CreateEnv(ctx, uuid, req)
|
||||
@@ -71,10 +71,10 @@ func NewCreateCommand() *cobra.Command {
|
||||
|
||||
cmd.Flags().String("key", "", "Environment variable key (required)")
|
||||
cmd.Flags().String("value", "", "Environment variable value (required)")
|
||||
cmd.Flags().Bool("build-time", false, "Available at build time")
|
||||
cmd.Flags().Bool("preview", false, "Available in preview deployments")
|
||||
cmd.Flags().Bool("build-time", true, "Available at build time (default: true)")
|
||||
cmd.Flags().Bool("is-literal", false, "Treat value as literal (don't interpolate variables)")
|
||||
cmd.Flags().Bool("is-multiline", false, "Value is multiline")
|
||||
cmd.Flags().Bool("runtime", true, "Available at runtime (default: true)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
Vendored
+11
-11
@@ -38,8 +38,8 @@ Example: coolify service env sync abc123 --file .env.production`,
|
||||
}
|
||||
|
||||
isBuildTime, _ := cmd.Flags().GetBool("build-time")
|
||||
isPreview, _ := cmd.Flags().GetBool("preview")
|
||||
isLiteral, _ := cmd.Flags().GetBool("is-literal")
|
||||
isRuntime, _ := cmd.Flags().GetBool("runtime")
|
||||
|
||||
// Parse the .env file
|
||||
envVars, err := parser.ParseEnvFile(filePath)
|
||||
@@ -62,17 +62,17 @@ Example: coolify service env sync abc123 --file .env.production`,
|
||||
}
|
||||
|
||||
// Build a map of existing env vars by key
|
||||
existingMap := make(map[string]models.EnvironmentVariable)
|
||||
existingMap := make(map[string]models.ServiceEnvironmentVariable)
|
||||
for _, env := range existingEnvs {
|
||||
existingMap[env.Key] = env
|
||||
}
|
||||
|
||||
// Separate into updates and creates
|
||||
var toUpdate []models.EnvironmentVariableCreateRequest
|
||||
var toCreate []models.EnvironmentVariableCreateRequest
|
||||
var toUpdate []models.ServiceEnvironmentVariableCreateRequest
|
||||
var toCreate []models.ServiceEnvironmentVariableCreateRequest
|
||||
|
||||
for _, envVar := range envVars {
|
||||
req := models.EnvironmentVariableCreateRequest{
|
||||
req := models.ServiceEnvironmentVariableCreateRequest{
|
||||
Key: envVar.Key,
|
||||
Value: envVar.Value,
|
||||
}
|
||||
@@ -81,12 +81,12 @@ Example: coolify service env sync abc123 --file .env.production`,
|
||||
if cmd.Flags().Changed("build-time") {
|
||||
req.IsBuildTime = &isBuildTime
|
||||
}
|
||||
if cmd.Flags().Changed("preview") {
|
||||
req.IsPreview = &isPreview
|
||||
}
|
||||
if cmd.Flags().Changed("is-literal") {
|
||||
req.IsLiteral = &isLiteral
|
||||
}
|
||||
if cmd.Flags().Changed("runtime") {
|
||||
req.IsRuntime = &isRuntime
|
||||
}
|
||||
|
||||
// Auto-detect multiline values
|
||||
if strings.Contains(envVar.Value, "\n") {
|
||||
@@ -108,7 +108,7 @@ Example: coolify service env sync abc123 --file .env.production`,
|
||||
// Perform bulk update if there are vars to update
|
||||
if len(toUpdate) > 0 {
|
||||
fmt.Printf("Updating %d existing variables...\n", len(toUpdate))
|
||||
bulkReq := &service.BulkUpdateEnvsRequest{
|
||||
bulkReq := &models.ServiceEnvBulkUpdateRequest{
|
||||
Data: toUpdate,
|
||||
}
|
||||
_, err := serviceSvc.BulkUpdateEnvs(ctx, uuid, bulkReq)
|
||||
@@ -147,9 +147,9 @@ Example: coolify service env sync abc123 --file .env.production`,
|
||||
}
|
||||
|
||||
cmd.Flags().StringP("file", "f", "", "Path to .env file (required)")
|
||||
cmd.Flags().Bool("build-time", false, "Make all variables available at build time")
|
||||
cmd.Flags().Bool("preview", false, "Make all variables available in preview deployments")
|
||||
cmd.Flags().Bool("build-time", true, "Make all variables available at build time (default: true)")
|
||||
cmd.Flags().Bool("is-literal", false, "Treat all values as literal (don't interpolate variables)")
|
||||
cmd.Flags().Bool("runtime", true, "Make all variables available at runtime (default: true)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
Vendored
+9
-9
@@ -26,7 +26,7 @@ func NewUpdateCommand() *cobra.Command {
|
||||
return fmt.Errorf("failed to get API client: %w", err)
|
||||
}
|
||||
|
||||
req := &models.EnvironmentVariableUpdateRequest{
|
||||
req := &models.ServiceEnvironmentVariableUpdateRequest{
|
||||
UUID: envUUID,
|
||||
}
|
||||
|
||||
@@ -43,10 +43,6 @@ func NewUpdateCommand() *cobra.Command {
|
||||
isBuildTime, _ := cmd.Flags().GetBool("build-time")
|
||||
req.IsBuildTime = &isBuildTime
|
||||
}
|
||||
if cmd.Flags().Changed("preview") {
|
||||
isPreview, _ := cmd.Flags().GetBool("preview")
|
||||
req.IsPreview = &isPreview
|
||||
}
|
||||
if cmd.Flags().Changed("is-literal") {
|
||||
isLiteral, _ := cmd.Flags().GetBool("is-literal")
|
||||
req.IsLiteral = &isLiteral
|
||||
@@ -55,10 +51,14 @@ func NewUpdateCommand() *cobra.Command {
|
||||
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
|
||||
req.IsMultiline = &isMultiline
|
||||
}
|
||||
if cmd.Flags().Changed("runtime") {
|
||||
isRuntime, _ := cmd.Flags().GetBool("runtime")
|
||||
req.IsRuntime = &isRuntime
|
||||
}
|
||||
|
||||
// Check if at least one field is being updated
|
||||
if req.Key == nil && req.Value == nil && req.IsBuildTime == nil && req.IsPreview == nil && req.IsLiteral == nil && req.IsMultiline == nil {
|
||||
return fmt.Errorf("at least one field must be provided to update (--key, --value, --build-time, --preview, --is-literal, or --is-multiline)")
|
||||
if req.Key == nil && req.Value == nil && req.IsBuildTime == nil && req.IsLiteral == nil && req.IsMultiline == nil && req.IsRuntime == nil {
|
||||
return fmt.Errorf("at least one field must be provided to update (--key, --value, --build-time, --is-literal, --is-multiline, or --runtime)")
|
||||
}
|
||||
|
||||
serviceSvc := service.NewService(client)
|
||||
@@ -74,10 +74,10 @@ func NewUpdateCommand() *cobra.Command {
|
||||
|
||||
cmd.Flags().String("key", "", "New environment variable key")
|
||||
cmd.Flags().String("value", "", "New environment variable value")
|
||||
cmd.Flags().Bool("build-time", false, "Available at build time")
|
||||
cmd.Flags().Bool("preview", false, "Available in preview deployments")
|
||||
cmd.Flags().Bool("build-time", true, "Available at build time (default: true)")
|
||||
cmd.Flags().Bool("is-literal", false, "Treat value as literal (don't interpolate variables)")
|
||||
cmd.Flags().Bool("is-multiline", false, "Value is multiline")
|
||||
cmd.Flags().Bool("runtime", true, "Available at runtime (default: true)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
+17
-10
@@ -1,6 +1,10 @@
|
||||
package service
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/coollabsio/coolify-cli/cmd/service/env"
|
||||
)
|
||||
|
||||
// NewServiceCommand creates the service parent command with all subcommands
|
||||
func NewServiceCommand() *cobra.Command {
|
||||
@@ -19,15 +23,18 @@ func NewServiceCommand() *cobra.Command {
|
||||
cmd.AddCommand(NewRestartCommand())
|
||||
cmd.AddCommand(NewDeleteCommand())
|
||||
|
||||
// Add env subcommand (placeholder for now)
|
||||
// TODO: Implement env commands
|
||||
// envCmd := &cobra.Command{
|
||||
// Use: "env",
|
||||
// Short: "Manage service environment variables",
|
||||
// }
|
||||
// envCmd.AddCommand(env.NewListCommand())
|
||||
// ... more env commands
|
||||
// cmd.AddCommand(envCmd)
|
||||
// Add env subcommand
|
||||
envCmd := &cobra.Command{
|
||||
Use: "env",
|
||||
Short: "Manage service environment variables",
|
||||
}
|
||||
envCmd.AddCommand(env.NewListCommand())
|
||||
envCmd.AddCommand(env.NewGetCommand())
|
||||
envCmd.AddCommand(env.NewCreateCommand())
|
||||
envCmd.AddCommand(env.NewUpdateCommand())
|
||||
envCmd.AddCommand(env.NewDeleteCommand())
|
||||
envCmd.AddCommand(env.NewSyncCommand())
|
||||
cmd.AddCommand(envCmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ type EnvironmentVariableCreateRequest struct {
|
||||
IsPreview *bool `json:"is_preview,omitempty"`
|
||||
IsLiteral *bool `json:"is_literal,omitempty"`
|
||||
IsMultiline *bool `json:"is_multiline,omitempty"`
|
||||
IsRuntime *bool `json:"is_runtime,omitempty"`
|
||||
}
|
||||
|
||||
// EnvironmentVariableUpdateRequest represents the request to update an environment variable
|
||||
@@ -131,4 +132,5 @@ type EnvironmentVariableUpdateRequest struct {
|
||||
IsPreview *bool `json:"is_preview,omitempty"`
|
||||
IsLiteral *bool `json:"is_literal,omitempty"`
|
||||
IsMultiline *bool `json:"is_multiline,omitempty"`
|
||||
IsRuntime *bool `json:"is_runtime,omitempty"`
|
||||
}
|
||||
|
||||
@@ -68,3 +68,52 @@ type ServiceUpdateRequest struct {
|
||||
type ServiceLifecycleResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ServiceEnvironmentVariable represents an environment variable for a service
|
||||
// Services don't have preview deployments, so IsPreview is excluded from output
|
||||
type ServiceEnvironmentVariable struct {
|
||||
ID int `json:"-" table:"-"`
|
||||
UUID string `json:"uuid"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value" sensitive:"true"`
|
||||
IsBuildTime bool `json:"is_buildtime"`
|
||||
IsLiteralValue bool `json:"is_literal"`
|
||||
IsShownOnce bool `json:"is_shown_once"`
|
||||
IsRuntime bool `json:"is_runtime"`
|
||||
IsShared bool `json:"is_shared"`
|
||||
RealValue *string `json:"real_value,omitempty" sensitive:"true"`
|
||||
ServiceID *int `json:"-" table:"-"`
|
||||
CreatedAt string `json:"-" table:"-"`
|
||||
UpdatedAt string `json:"-" table:"-"`
|
||||
}
|
||||
|
||||
// ServiceEnvironmentVariableCreateRequest represents the request to create a service environment variable
|
||||
type ServiceEnvironmentVariableCreateRequest struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
IsBuildTime *bool `json:"is_build_time,omitempty"`
|
||||
IsLiteral *bool `json:"is_literal,omitempty"`
|
||||
IsMultiline *bool `json:"is_multiline,omitempty"`
|
||||
IsRuntime *bool `json:"is_runtime,omitempty"`
|
||||
}
|
||||
|
||||
// ServiceEnvironmentVariableUpdateRequest represents the request to update a service environment variable
|
||||
type ServiceEnvironmentVariableUpdateRequest struct {
|
||||
UUID string `json:"uuid"`
|
||||
Key *string `json:"key,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
IsBuildTime *bool `json:"is_build_time,omitempty"`
|
||||
IsLiteral *bool `json:"is_literal,omitempty"`
|
||||
IsMultiline *bool `json:"is_multiline,omitempty"`
|
||||
IsRuntime *bool `json:"is_runtime,omitempty"`
|
||||
}
|
||||
|
||||
// ServiceEnvBulkUpdateRequest represents the request to bulk update service environment variables
|
||||
type ServiceEnvBulkUpdateRequest struct {
|
||||
Data []ServiceEnvironmentVariableCreateRequest `json:"data"`
|
||||
}
|
||||
|
||||
// ServiceEnvBulkUpdateResponse represents the response from service bulk update
|
||||
type ServiceEnvBulkUpdateResponse struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ func (s *Service) Restart(ctx context.Context, uuid string) (*models.ServiceLife
|
||||
}
|
||||
|
||||
// ListEnvs retrieves all environment variables for a service
|
||||
func (s *Service) ListEnvs(ctx context.Context, uuid string) ([]models.EnvironmentVariable, error) {
|
||||
var envs []models.EnvironmentVariable
|
||||
func (s *Service) ListEnvs(ctx context.Context, uuid string) ([]models.ServiceEnvironmentVariable, error) {
|
||||
var envs []models.ServiceEnvironmentVariable
|
||||
err := s.client.Get(ctx, fmt.Sprintf("services/%s/envs", uuid), &envs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list environment variables for service %s: %w", uuid, err)
|
||||
@@ -111,7 +111,7 @@ func (s *Service) ListEnvs(ctx context.Context, uuid string) ([]models.Environme
|
||||
}
|
||||
|
||||
// GetEnv retrieves a single environment variable by UUID or key
|
||||
func (s *Service) GetEnv(ctx context.Context, serviceUUID, envIdentifier string) (*models.EnvironmentVariable, error) {
|
||||
func (s *Service) GetEnv(ctx context.Context, serviceUUID, envIdentifier string) (*models.ServiceEnvironmentVariable, error) {
|
||||
envs, err := s.ListEnvs(ctx, serviceUUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -128,8 +128,8 @@ func (s *Service) GetEnv(ctx context.Context, serviceUUID, envIdentifier string)
|
||||
}
|
||||
|
||||
// CreateEnv creates a new environment variable for a service
|
||||
func (s *Service) CreateEnv(ctx context.Context, uuid string, req *models.EnvironmentVariableCreateRequest) (*models.EnvironmentVariable, error) {
|
||||
var env models.EnvironmentVariable
|
||||
func (s *Service) CreateEnv(ctx context.Context, uuid string, req *models.ServiceEnvironmentVariableCreateRequest) (*models.ServiceEnvironmentVariable, error) {
|
||||
var env models.ServiceEnvironmentVariable
|
||||
err := s.client.Post(ctx, fmt.Sprintf("services/%s/envs", uuid), req, &env)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create environment variable for service %s: %w", uuid, err)
|
||||
@@ -138,8 +138,8 @@ func (s *Service) CreateEnv(ctx context.Context, uuid string, req *models.Enviro
|
||||
}
|
||||
|
||||
// UpdateEnv updates an environment variable for a service
|
||||
func (s *Service) UpdateEnv(ctx context.Context, serviceUUID string, req *models.EnvironmentVariableUpdateRequest) (*models.EnvironmentVariable, error) {
|
||||
var env models.EnvironmentVariable
|
||||
func (s *Service) UpdateEnv(ctx context.Context, serviceUUID string, req *models.ServiceEnvironmentVariableUpdateRequest) (*models.ServiceEnvironmentVariable, error) {
|
||||
var env models.ServiceEnvironmentVariable
|
||||
err := s.client.Patch(ctx, fmt.Sprintf("services/%s/envs", serviceUUID), req, &env)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update environment variable for service %s: %w", serviceUUID, err)
|
||||
@@ -157,8 +157,8 @@ func (s *Service) DeleteEnv(ctx context.Context, serviceUUID, envUUID string) er
|
||||
}
|
||||
|
||||
// BulkUpdateEnvs updates multiple environment variables in a single request
|
||||
func (s *Service) BulkUpdateEnvs(ctx context.Context, serviceUUID string, req *BulkUpdateEnvsRequest) (*BulkUpdateEnvsResponse, error) {
|
||||
var response BulkUpdateEnvsResponse
|
||||
func (s *Service) BulkUpdateEnvs(ctx context.Context, serviceUUID string, req *models.ServiceEnvBulkUpdateRequest) (*models.ServiceEnvBulkUpdateResponse, error) {
|
||||
var response models.ServiceEnvBulkUpdateResponse
|
||||
err := s.client.Patch(ctx, fmt.Sprintf("services/%s/envs/bulk", serviceUUID), req, &response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to bulk update environment variables for service %s: %w", serviceUUID, err)
|
||||
|
||||
@@ -255,14 +255,14 @@ func TestService_ListEnvs(t *testing.T) {
|
||||
"uuid": "env-1",
|
||||
"key": "DATABASE_URL",
|
||||
"value": "postgres://localhost",
|
||||
"is_build_time": false,
|
||||
"is_buildtime": false,
|
||||
"is_preview": false
|
||||
},
|
||||
{
|
||||
"uuid": "env-2",
|
||||
"key": "API_KEY",
|
||||
"value": "secret",
|
||||
"is_build_time": true,
|
||||
"is_buildtime": true,
|
||||
"is_preview": false
|
||||
}
|
||||
]`))
|
||||
@@ -290,7 +290,7 @@ func TestService_CreateEnv(t *testing.T) {
|
||||
"uuid": "env-new",
|
||||
"key": "NEW_VAR",
|
||||
"value": "new_value",
|
||||
"is_build_time": false,
|
||||
"is_buildtime": false,
|
||||
"is_preview": false
|
||||
}`))
|
||||
}))
|
||||
@@ -299,7 +299,7 @@ func TestService_CreateEnv(t *testing.T) {
|
||||
client := api.NewClient(server.URL, "test-token")
|
||||
svc := NewService(client)
|
||||
|
||||
env, err := svc.CreateEnv(context.Background(), "service-uuid-123", &models.EnvironmentVariableCreateRequest{
|
||||
env, err := svc.CreateEnv(context.Background(), "service-uuid-123", &models.ServiceEnvironmentVariableCreateRequest{
|
||||
Key: "NEW_VAR",
|
||||
Value: "new_value",
|
||||
})
|
||||
@@ -319,7 +319,7 @@ func TestService_UpdateEnv(t *testing.T) {
|
||||
"uuid": "env-123",
|
||||
"key": "UPDATED_VAR",
|
||||
"value": "updated_value",
|
||||
"is_build_time": true,
|
||||
"is_buildtime": true,
|
||||
"is_preview": false
|
||||
}`))
|
||||
}))
|
||||
@@ -329,7 +329,7 @@ func TestService_UpdateEnv(t *testing.T) {
|
||||
svc := NewService(client)
|
||||
|
||||
newKey := "UPDATED_VAR"
|
||||
env, err := svc.UpdateEnv(context.Background(), "service-uuid-123", &models.EnvironmentVariableUpdateRequest{
|
||||
env, err := svc.UpdateEnv(context.Background(), "service-uuid-123", &models.ServiceEnvironmentVariableUpdateRequest{
|
||||
UUID: "env-123",
|
||||
Key: &newKey,
|
||||
})
|
||||
|
||||
+41
-43
@@ -5,92 +5,90 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
compareVersion "github.com/hashicorp/go-version"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Version variables injected by GoReleaser at build time via ldflags
|
||||
var (
|
||||
version = "v1.0.5"
|
||||
version = "v1.2"
|
||||
)
|
||||
|
||||
// GitHubAPIURL is the URL for fetching CLI version tags (exported for testing)
|
||||
var GitHubAPIURL = "https://api.github.com/repos/coollabsio/coolify-cli/git/refs/tags"
|
||||
|
||||
func GetVersion() string {
|
||||
return version
|
||||
}
|
||||
|
||||
// CheckInterval for version checking
|
||||
const CheckInterval = 10 * time.Minute
|
||||
|
||||
// Tag represents a git tag for version checking
|
||||
type Tag struct {
|
||||
Ref string `json:"ref"`
|
||||
}
|
||||
|
||||
// CheckLatestVersionOfCli checks for CLI updates
|
||||
func CheckLatestVersionOfCli(debug bool) (string, error) {
|
||||
lastCheck := viper.GetString("lastupdatechecktime")
|
||||
if lastCheck != "" {
|
||||
lastCheckTime, err := time.Parse(time.RFC3339, lastCheck)
|
||||
if err == nil && lastCheckTime.Add(CheckInterval).After(time.Now()) {
|
||||
if debug {
|
||||
log.Println("Skipping update check. Last check was less than 10 minutes ago.")
|
||||
}
|
||||
return GetVersion(), nil
|
||||
}
|
||||
}
|
||||
// CheckLatestVersionOfCli checks for CLI updates on every command.
|
||||
// Errors are handled silently - the function returns without printing anything
|
||||
// if the GitHub API call fails.
|
||||
func CheckLatestVersionOfCli(_ bool) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Update check time
|
||||
viper.Set("lastupdatechecktime", time.Now().Format(time.RFC3339))
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
log.Printf("Failed to write config: %v\n", err)
|
||||
}
|
||||
|
||||
url := "https://api.github.com/repos/coollabsio/coolify-cli/git/refs/tags"
|
||||
ctx := context.Background()
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", GitHubAPIURL, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
if resp.StatusCode != 200 {
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("%d - Failed to fetch data from %s. Error: %s", resp.StatusCode, url, string(body))
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
|
||||
var tags []Tag
|
||||
if err := json.Unmarshal(body, &tags); err != nil {
|
||||
return "", err
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
|
||||
if len(tags) == 0 {
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
|
||||
versionsRaw := make([]string, 0, len(tags))
|
||||
for _, tag := range tags {
|
||||
versionStr := tag.Ref[10:]
|
||||
versionsRaw = append(versionsRaw, versionStr)
|
||||
if len(tag.Ref) > 10 {
|
||||
versionStr := tag.Ref[10:]
|
||||
versionsRaw = append(versionsRaw, versionStr)
|
||||
}
|
||||
}
|
||||
|
||||
versions := make([]*compareVersion.Version, len(versionsRaw))
|
||||
for i, raw := range versionsRaw {
|
||||
if len(versionsRaw) == 0 {
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
|
||||
versions := make([]*compareVersion.Version, 0, len(versionsRaw))
|
||||
for _, raw := range versionsRaw {
|
||||
v, err := compareVersion.NewVersion(raw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
continue // Skip invalid versions
|
||||
}
|
||||
versions[i] = v
|
||||
versions = append(versions, v)
|
||||
}
|
||||
|
||||
if len(versions) == 0 {
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
|
||||
sort.Sort(compareVersion.Collection(versions))
|
||||
@@ -99,11 +97,11 @@ func CheckLatestVersionOfCli(debug bool) (string, error) {
|
||||
// Compare versions properly using semantic versioning
|
||||
currentVersion, err := compareVersion.NewVersion(GetVersion())
|
||||
if err != nil {
|
||||
return latestVersion.String(), err
|
||||
return "", nil // Silent fail
|
||||
}
|
||||
|
||||
if latestVersion.GreaterThan(currentVersion) {
|
||||
fmt.Printf("There is a new version of Coolify CLI available.\nPlease update with 'coolify update'.\n\n")
|
||||
fmt.Printf("A new version (%s) is available. Update with: coolify update\n", latestVersion.String())
|
||||
}
|
||||
return latestVersion.String(), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
v := GetVersion()
|
||||
if v == "" {
|
||||
t.Error("GetVersion() returned empty string")
|
||||
}
|
||||
// Version should start with 'v'
|
||||
if v[0] != 'v' {
|
||||
t.Errorf("GetVersion() = %q, expected to start with 'v'", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckLatestVersionOfCli_UpdateAvailable(t *testing.T) {
|
||||
// Save original values
|
||||
originalURL := GitHubAPIURL
|
||||
originalVersion := version
|
||||
defer func() {
|
||||
GitHubAPIURL = originalURL
|
||||
version = originalVersion
|
||||
}()
|
||||
|
||||
// Set a low version to ensure update is available
|
||||
version = "v0.0.1"
|
||||
|
||||
// Create mock server with newer version
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
// Return tags in GitHub API format
|
||||
_, _ = w.Write([]byte(`[{"ref":"refs/tags/v1.0.0"},{"ref":"refs/tags/v2.0.0"}]`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
GitHubAPIURL = server.URL
|
||||
|
||||
// Capture stdout to check for update message
|
||||
oldStdout := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
latestVersion, err := CheckLatestVersionOfCli(false)
|
||||
|
||||
_ = w.Close()
|
||||
os.Stdout = oldStdout
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, _ = io.Copy(&buf, r)
|
||||
output := buf.String()
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("CheckLatestVersionOfCli() error = %v, want nil", err)
|
||||
}
|
||||
|
||||
if latestVersion != "2.0.0" {
|
||||
t.Errorf("CheckLatestVersionOfCli() latestVersion = %q, want %q", latestVersion, "2.0.0")
|
||||
}
|
||||
|
||||
// Should print update message
|
||||
expectedMsg := "A new version (2.0.0) is available. Update with: coolify update\n"
|
||||
if output != expectedMsg {
|
||||
t.Errorf("CheckLatestVersionOfCli() output = %q, want %q", output, expectedMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckLatestVersionOfCli_NoUpdate(t *testing.T) {
|
||||
// Save original values
|
||||
originalURL := GitHubAPIURL
|
||||
originalVersion := version
|
||||
defer func() {
|
||||
GitHubAPIURL = originalURL
|
||||
version = originalVersion
|
||||
}()
|
||||
|
||||
// Set a high version to ensure no update is available
|
||||
version = "v99.99.99"
|
||||
|
||||
// Create mock server
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`[{"ref":"refs/tags/v1.0.0"},{"ref":"refs/tags/v2.0.0"}]`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
GitHubAPIURL = server.URL
|
||||
|
||||
// Capture stdout
|
||||
oldStdout := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
latestVersion, err := CheckLatestVersionOfCli(false)
|
||||
|
||||
_ = w.Close()
|
||||
os.Stdout = oldStdout
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, _ = io.Copy(&buf, r)
|
||||
output := buf.String()
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("CheckLatestVersionOfCli() error = %v, want nil", err)
|
||||
}
|
||||
|
||||
// Function returns the latest version from GitHub (2.0.0), not the current version
|
||||
if latestVersion != "2.0.0" {
|
||||
t.Errorf("CheckLatestVersionOfCli() latestVersion = %q, want %q", latestVersion, "2.0.0")
|
||||
}
|
||||
|
||||
// Should NOT print any message when already on latest (current v99.99.99 > latest v2.0.0)
|
||||
if output != "" {
|
||||
t.Errorf("CheckLatestVersionOfCli() should not print anything when on latest version, got: %q", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckLatestVersionOfCli_APIError_SilentFail(t *testing.T) {
|
||||
// Save original URL
|
||||
originalURL := GitHubAPIURL
|
||||
defer func() {
|
||||
GitHubAPIURL = originalURL
|
||||
}()
|
||||
|
||||
// Create mock server that returns error
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(`{"error": "internal server error"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
GitHubAPIURL = server.URL
|
||||
|
||||
// Capture stdout
|
||||
oldStdout := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
latestVersion, err := CheckLatestVersionOfCli(false)
|
||||
|
||||
_ = w.Close()
|
||||
os.Stdout = oldStdout
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, _ = io.Copy(&buf, r)
|
||||
output := buf.String()
|
||||
|
||||
// Should return empty string and nil error (silent fail)
|
||||
if err != nil {
|
||||
t.Errorf("CheckLatestVersionOfCli() error = %v, want nil on API error", err)
|
||||
}
|
||||
|
||||
if latestVersion != "" {
|
||||
t.Errorf("CheckLatestVersionOfCli() latestVersion = %q, want empty string on API error", latestVersion)
|
||||
}
|
||||
|
||||
// Should NOT print anything on error
|
||||
if output != "" {
|
||||
t.Errorf("CheckLatestVersionOfCli() should not print anything on API error, got: %q", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckLatestVersionOfCli_NetworkError_SilentFail(t *testing.T) {
|
||||
// Save original URL
|
||||
originalURL := GitHubAPIURL
|
||||
defer func() {
|
||||
GitHubAPIURL = originalURL
|
||||
}()
|
||||
|
||||
// Use invalid URL to cause network error
|
||||
GitHubAPIURL = "http://localhost:1" // Port 1 should fail to connect
|
||||
|
||||
// Capture stdout
|
||||
oldStdout := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
latestVersion, err := CheckLatestVersionOfCli(false)
|
||||
|
||||
_ = w.Close()
|
||||
os.Stdout = oldStdout
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, _ = io.Copy(&buf, r)
|
||||
output := buf.String()
|
||||
|
||||
// Should return empty string and nil error (silent fail)
|
||||
if err != nil {
|
||||
t.Errorf("CheckLatestVersionOfCli() error = %v, want nil on network error", err)
|
||||
}
|
||||
|
||||
if latestVersion != "" {
|
||||
t.Errorf("CheckLatestVersionOfCli() latestVersion = %q, want empty string on network error", latestVersion)
|
||||
}
|
||||
|
||||
// Should NOT print anything on error
|
||||
if output != "" {
|
||||
t.Errorf("CheckLatestVersionOfCli() should not print anything on network error, got: %q", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckLatestVersionOfCli_InvalidJSON_SilentFail(t *testing.T) {
|
||||
// Save original URL
|
||||
originalURL := GitHubAPIURL
|
||||
defer func() {
|
||||
GitHubAPIURL = originalURL
|
||||
}()
|
||||
|
||||
// Create mock server that returns invalid JSON
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`not valid json`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
GitHubAPIURL = server.URL
|
||||
|
||||
latestVersion, err := CheckLatestVersionOfCli(false)
|
||||
|
||||
// Should return empty string and nil error (silent fail)
|
||||
if err != nil {
|
||||
t.Errorf("CheckLatestVersionOfCli() error = %v, want nil on invalid JSON", err)
|
||||
}
|
||||
|
||||
if latestVersion != "" {
|
||||
t.Errorf("CheckLatestVersionOfCli() latestVersion = %q, want empty string on invalid JSON", latestVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckLatestVersionOfCli_EmptyTags_SilentFail(t *testing.T) {
|
||||
// Save original URL
|
||||
originalURL := GitHubAPIURL
|
||||
defer func() {
|
||||
GitHubAPIURL = originalURL
|
||||
}()
|
||||
|
||||
// Create mock server that returns empty array
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`[]`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
GitHubAPIURL = server.URL
|
||||
|
||||
latestVersion, err := CheckLatestVersionOfCli(false)
|
||||
|
||||
// Should return empty string and nil error (silent fail)
|
||||
if err != nil {
|
||||
t.Errorf("CheckLatestVersionOfCli() error = %v, want nil on empty tags", err)
|
||||
}
|
||||
|
||||
if latestVersion != "" {
|
||||
t.Errorf("CheckLatestVersionOfCli() latestVersion = %q, want empty string on empty tags", latestVersion)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user