Skip to content

Commit 1f1b187

Browse files
authored
Merge pull request #45 from coollabsio/add-runtime-env-flag
Add runtime env flag and improve service env handling
2 parents 99a40bf + 4af598c commit 1f1b187

File tree

13 files changed

+166
-59
lines changed

13 files changed

+166
-59
lines changed

cmd/application/env/create.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func NewCreateEnvCommand() *cobra.Command {
3131
isPreview, _ := cmd.Flags().GetBool("preview")
3232
isLiteral, _ := cmd.Flags().GetBool("is-literal")
3333
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
34+
isRuntime, _ := cmd.Flags().GetBool("runtime")
3435

3536
if key == "" {
3637
return fmt.Errorf("--key is required")
@@ -56,6 +57,9 @@ func NewCreateEnvCommand() *cobra.Command {
5657
if cmd.Flags().Changed("is-multiline") {
5758
req.IsMultiline = &isMultiline
5859
}
60+
if cmd.Flags().Changed("runtime") {
61+
req.IsRuntime = &isRuntime
62+
}
5963

6064
appSvc := service.NewApplicationService(client)
6165
env, err := appSvc.CreateEnv(ctx, appUUID, req)
@@ -71,9 +75,10 @@ func NewCreateEnvCommand() *cobra.Command {
7175

7276
cmd.Flags().String("key", "", "Environment variable key (required)")
7377
cmd.Flags().String("value", "", "Environment variable value (required)")
74-
cmd.Flags().Bool("build-time", false, "Available at build time")
78+
cmd.Flags().Bool("build-time", true, "Available at build time (default: true)")
7579
cmd.Flags().Bool("preview", false, "Available in preview deployments")
7680
cmd.Flags().Bool("is-literal", false, "Treat value as literal (don't interpolate variables)")
7781
cmd.Flags().Bool("is-multiline", false, "Value is multiline")
82+
cmd.Flags().Bool("runtime", true, "Available at runtime (default: true)")
7883
return cmd
7984
}

cmd/application/env/get.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
func NewGetEnvCommand() *cobra.Command {
14-
return &cobra.Command{
14+
cmd := &cobra.Command{
1515
Use: "get <app_uuid> <env_uuid_or_key>",
1616
Short: "Get environment variable details",
1717
Long: `Get detailed information about a specific environment variable by UUID or key name.`,
@@ -27,6 +27,8 @@ func NewGetEnvCommand() *cobra.Command {
2727
}
2828

2929
appSvc := service.NewApplicationService(client)
30+
31+
// First try to get by the identifier directly
3032
env, err := appSvc.GetEnv(ctx, appUUID, envUUIDOrKey)
3133
if err != nil {
3234
return fmt.Errorf("failed to get environment variable: %w", err)
@@ -53,4 +55,6 @@ func NewGetEnvCommand() *cobra.Command {
5355
return formatter.Format(env)
5456
},
5557
}
58+
59+
return cmd
5660
}

cmd/application/env/list.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@ package env
22

33
import (
44
"fmt"
5+
"sort"
56

67
"github.com/spf13/cobra"
78

89
"github.com/coollabsio/coolify-cli/internal/cli"
10+
"github.com/coollabsio/coolify-cli/internal/models"
911
"github.com/coollabsio/coolify-cli/internal/output"
1012
"github.com/coollabsio/coolify-cli/internal/service"
1113
)
1214

1315
func NewListEnvCommand() *cobra.Command {
14-
return &cobra.Command{
16+
cmd := &cobra.Command{
1517
Use: "list <app_uuid>",
1618
Short: "List all environment variables for an application",
17-
Long: `List all environment variables for a specific application.`,
19+
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).`,
1820
Args: cli.ExactArgs(1, "<uuid>"),
1921
RunE: func(cmd *cobra.Command, args []string) error {
2022
ctx := cmd.Context()
@@ -31,6 +33,29 @@ func NewListEnvCommand() *cobra.Command {
3133
return fmt.Errorf("failed to list environment variables: %w", err)
3234
}
3335

36+
// Filter by preview/all flags
37+
showAll, _ := cmd.Flags().GetBool("all")
38+
showPreview, _ := cmd.Flags().GetBool("preview")
39+
40+
if showAll {
41+
// Sort: non-preview first, then preview
42+
sort.SliceStable(envs, func(i, j int) bool {
43+
if envs[i].IsPreview != envs[j].IsPreview {
44+
return !envs[i].IsPreview // non-preview (false) comes before preview (true)
45+
}
46+
return false // maintain original order within groups
47+
})
48+
} else {
49+
// Filter by preview flag
50+
var filtered []models.EnvironmentVariable
51+
for _, env := range envs {
52+
if env.IsPreview == showPreview {
53+
filtered = append(filtered, env)
54+
}
55+
}
56+
envs = filtered
57+
}
58+
3459
format, _ := cmd.Flags().GetString("format")
3560
showSensitive, _ := cmd.Flags().GetBool("show-sensitive")
3661

@@ -54,4 +79,9 @@ func NewListEnvCommand() *cobra.Command {
5479
return formatter.Format(envs)
5580
},
5681
}
82+
83+
cmd.Flags().Bool("preview", false, "Show preview environment variables instead of regular ones")
84+
cmd.Flags().Bool("all", false, "Show all environment variables (non-preview first, then preview)")
85+
86+
return cmd
5787
}

cmd/application/env/sync.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Example: coolify app env sync abc123 --file .env.production`,
4040
isBuildTime, _ := cmd.Flags().GetBool("build-time")
4141
isPreview, _ := cmd.Flags().GetBool("preview")
4242
isLiteral, _ := cmd.Flags().GetBool("is-literal")
43+
isRuntime, _ := cmd.Flags().GetBool("runtime")
4344

4445
// Parse the .env file
4546
envVars, err := parser.ParseEnvFile(filePath)
@@ -87,6 +88,9 @@ Example: coolify app env sync abc123 --file .env.production`,
8788
if cmd.Flags().Changed("is-literal") {
8889
req.IsLiteral = &isLiteral
8990
}
91+
if cmd.Flags().Changed("runtime") {
92+
req.IsRuntime = &isRuntime
93+
}
9094

9195
// Auto-detect multiline values
9296
if strings.Contains(envVar.Value, "\n") {
@@ -147,8 +151,9 @@ Example: coolify app env sync abc123 --file .env.production`,
147151
}
148152

149153
syncEnvCmd.Flags().StringP("file", "f", "", "Path to .env file (required)")
150-
syncEnvCmd.Flags().Bool("build-time", false, "Make all variables available at build time")
154+
syncEnvCmd.Flags().Bool("build-time", true, "Make all variables available at build time (default: true)")
151155
syncEnvCmd.Flags().Bool("preview", false, "Make all variables available in preview deployments")
152156
syncEnvCmd.Flags().Bool("is-literal", false, "Treat all values as literal (don't interpolate variables)")
157+
syncEnvCmd.Flags().Bool("runtime", true, "Make all variables available at runtime (default: true)")
153158
return syncEnvCmd
154159
}

cmd/application/env/update.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,12 @@ func NewUpdateEnvCommand() *cobra.Command {
5454
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
5555
req.IsMultiline = &isMultiline
5656
}
57+
if cmd.Flags().Changed("runtime") {
58+
isRuntime, _ := cmd.Flags().GetBool("runtime")
59+
req.IsRuntime = &isRuntime
60+
}
5761

58-
if req.Key == nil && req.Value == nil && req.IsBuildTime == nil && req.IsPreview == nil && req.IsLiteral == nil && req.IsMultiline == nil {
62+
if req.Key == nil && req.Value == nil && req.IsBuildTime == nil && req.IsPreview == nil && req.IsLiteral == nil && req.IsMultiline == nil && req.IsRuntime == nil {
5963
return fmt.Errorf("at least one field must be provided to update")
6064
}
6165

@@ -72,9 +76,10 @@ func NewUpdateEnvCommand() *cobra.Command {
7276

7377
cmd.Flags().String("key", "", "New environment variable key")
7478
cmd.Flags().String("value", "", "New environment variable value")
75-
cmd.Flags().Bool("build-time", false, "Available at build time")
79+
cmd.Flags().Bool("build-time", true, "Available at build time (default: true)")
7680
cmd.Flags().Bool("preview", false, "Available in preview deployments")
7781
cmd.Flags().Bool("is-literal", false, "Treat value as literal")
7882
cmd.Flags().Bool("is-multiline", false, "Value is multiline")
83+
cmd.Flags().Bool("runtime", true, "Available at runtime (default: true)")
7984
return cmd
8085
}

cmd/service/env/create.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ func NewCreateCommand() *cobra.Command {
2828
key, _ := cmd.Flags().GetString("key")
2929
value, _ := cmd.Flags().GetString("value")
3030
isBuildTime, _ := cmd.Flags().GetBool("build-time")
31-
isPreview, _ := cmd.Flags().GetBool("preview")
3231
isLiteral, _ := cmd.Flags().GetBool("is-literal")
3332
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
33+
isRuntime, _ := cmd.Flags().GetBool("runtime")
3434

3535
if key == "" {
3636
return fmt.Errorf("--key is required")
@@ -39,7 +39,7 @@ func NewCreateCommand() *cobra.Command {
3939
return fmt.Errorf("--value is required")
4040
}
4141

42-
req := &models.EnvironmentVariableCreateRequest{
42+
req := &models.ServiceEnvironmentVariableCreateRequest{
4343
Key: key,
4444
Value: value,
4545
}
@@ -48,15 +48,15 @@ func NewCreateCommand() *cobra.Command {
4848
if cmd.Flags().Changed("build-time") {
4949
req.IsBuildTime = &isBuildTime
5050
}
51-
if cmd.Flags().Changed("preview") {
52-
req.IsPreview = &isPreview
53-
}
5451
if cmd.Flags().Changed("is-literal") {
5552
req.IsLiteral = &isLiteral
5653
}
5754
if cmd.Flags().Changed("is-multiline") {
5855
req.IsMultiline = &isMultiline
5956
}
57+
if cmd.Flags().Changed("runtime") {
58+
req.IsRuntime = &isRuntime
59+
}
6060

6161
serviceSvc := service.NewService(client)
6262
env, err := serviceSvc.CreateEnv(ctx, uuid, req)
@@ -71,10 +71,10 @@ func NewCreateCommand() *cobra.Command {
7171

7272
cmd.Flags().String("key", "", "Environment variable key (required)")
7373
cmd.Flags().String("value", "", "Environment variable value (required)")
74-
cmd.Flags().Bool("build-time", false, "Available at build time")
75-
cmd.Flags().Bool("preview", false, "Available in preview deployments")
74+
cmd.Flags().Bool("build-time", true, "Available at build time (default: true)")
7675
cmd.Flags().Bool("is-literal", false, "Treat value as literal (don't interpolate variables)")
7776
cmd.Flags().Bool("is-multiline", false, "Value is multiline")
77+
cmd.Flags().Bool("runtime", true, "Available at runtime (default: true)")
7878

7979
return cmd
8080
}

cmd/service/env/sync.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ Example: coolify service env sync abc123 --file .env.production`,
3838
}
3939

4040
isBuildTime, _ := cmd.Flags().GetBool("build-time")
41-
isPreview, _ := cmd.Flags().GetBool("preview")
4241
isLiteral, _ := cmd.Flags().GetBool("is-literal")
42+
isRuntime, _ := cmd.Flags().GetBool("runtime")
4343

4444
// Parse the .env file
4545
envVars, err := parser.ParseEnvFile(filePath)
@@ -62,17 +62,17 @@ Example: coolify service env sync abc123 --file .env.production`,
6262
}
6363

6464
// Build a map of existing env vars by key
65-
existingMap := make(map[string]models.EnvironmentVariable)
65+
existingMap := make(map[string]models.ServiceEnvironmentVariable)
6666
for _, env := range existingEnvs {
6767
existingMap[env.Key] = env
6868
}
6969

7070
// Separate into updates and creates
71-
var toUpdate []models.EnvironmentVariableCreateRequest
72-
var toCreate []models.EnvironmentVariableCreateRequest
71+
var toUpdate []models.ServiceEnvironmentVariableCreateRequest
72+
var toCreate []models.ServiceEnvironmentVariableCreateRequest
7373

7474
for _, envVar := range envVars {
75-
req := models.EnvironmentVariableCreateRequest{
75+
req := models.ServiceEnvironmentVariableCreateRequest{
7676
Key: envVar.Key,
7777
Value: envVar.Value,
7878
}
@@ -81,12 +81,12 @@ Example: coolify service env sync abc123 --file .env.production`,
8181
if cmd.Flags().Changed("build-time") {
8282
req.IsBuildTime = &isBuildTime
8383
}
84-
if cmd.Flags().Changed("preview") {
85-
req.IsPreview = &isPreview
86-
}
8784
if cmd.Flags().Changed("is-literal") {
8885
req.IsLiteral = &isLiteral
8986
}
87+
if cmd.Flags().Changed("runtime") {
88+
req.IsRuntime = &isRuntime
89+
}
9090

9191
// Auto-detect multiline values
9292
if strings.Contains(envVar.Value, "\n") {
@@ -108,7 +108,7 @@ Example: coolify service env sync abc123 --file .env.production`,
108108
// Perform bulk update if there are vars to update
109109
if len(toUpdate) > 0 {
110110
fmt.Printf("Updating %d existing variables...\n", len(toUpdate))
111-
bulkReq := &service.BulkUpdateEnvsRequest{
111+
bulkReq := &models.ServiceEnvBulkUpdateRequest{
112112
Data: toUpdate,
113113
}
114114
_, err := serviceSvc.BulkUpdateEnvs(ctx, uuid, bulkReq)
@@ -147,9 +147,9 @@ Example: coolify service env sync abc123 --file .env.production`,
147147
}
148148

149149
cmd.Flags().StringP("file", "f", "", "Path to .env file (required)")
150-
cmd.Flags().Bool("build-time", false, "Make all variables available at build time")
151-
cmd.Flags().Bool("preview", false, "Make all variables available in preview deployments")
150+
cmd.Flags().Bool("build-time", true, "Make all variables available at build time (default: true)")
152151
cmd.Flags().Bool("is-literal", false, "Treat all values as literal (don't interpolate variables)")
152+
cmd.Flags().Bool("runtime", true, "Make all variables available at runtime (default: true)")
153153

154154
return cmd
155155
}

cmd/service/env/update.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func NewUpdateCommand() *cobra.Command {
2626
return fmt.Errorf("failed to get API client: %w", err)
2727
}
2828

29-
req := &models.EnvironmentVariableUpdateRequest{
29+
req := &models.ServiceEnvironmentVariableUpdateRequest{
3030
UUID: envUUID,
3131
}
3232

@@ -43,10 +43,6 @@ func NewUpdateCommand() *cobra.Command {
4343
isBuildTime, _ := cmd.Flags().GetBool("build-time")
4444
req.IsBuildTime = &isBuildTime
4545
}
46-
if cmd.Flags().Changed("preview") {
47-
isPreview, _ := cmd.Flags().GetBool("preview")
48-
req.IsPreview = &isPreview
49-
}
5046
if cmd.Flags().Changed("is-literal") {
5147
isLiteral, _ := cmd.Flags().GetBool("is-literal")
5248
req.IsLiteral = &isLiteral
@@ -55,10 +51,14 @@ func NewUpdateCommand() *cobra.Command {
5551
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
5652
req.IsMultiline = &isMultiline
5753
}
54+
if cmd.Flags().Changed("runtime") {
55+
isRuntime, _ := cmd.Flags().GetBool("runtime")
56+
req.IsRuntime = &isRuntime
57+
}
5858

5959
// Check if at least one field is being updated
60-
if req.Key == nil && req.Value == nil && req.IsBuildTime == nil && req.IsPreview == nil && req.IsLiteral == nil && req.IsMultiline == nil {
61-
return fmt.Errorf("at least one field must be provided to update (--key, --value, --build-time, --preview, --is-literal, or --is-multiline)")
60+
if req.Key == nil && req.Value == nil && req.IsBuildTime == nil && req.IsLiteral == nil && req.IsMultiline == nil && req.IsRuntime == nil {
61+
return fmt.Errorf("at least one field must be provided to update (--key, --value, --build-time, --is-literal, --is-multiline, or --runtime)")
6262
}
6363

6464
serviceSvc := service.NewService(client)
@@ -74,10 +74,10 @@ func NewUpdateCommand() *cobra.Command {
7474

7575
cmd.Flags().String("key", "", "New environment variable key")
7676
cmd.Flags().String("value", "", "New environment variable value")
77-
cmd.Flags().Bool("build-time", false, "Available at build time")
78-
cmd.Flags().Bool("preview", false, "Available in preview deployments")
77+
cmd.Flags().Bool("build-time", true, "Available at build time (default: true)")
7978
cmd.Flags().Bool("is-literal", false, "Treat value as literal (don't interpolate variables)")
8079
cmd.Flags().Bool("is-multiline", false, "Value is multiline")
80+
cmd.Flags().Bool("runtime", true, "Available at runtime (default: true)")
8181

8282
return cmd
8383
}

cmd/service/service.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package service
22

3-
import "github.com/spf13/cobra"
3+
import (
4+
"github.com/spf13/cobra"
5+
6+
"github.com/coollabsio/coolify-cli/cmd/service/env"
7+
)
48

59
// NewServiceCommand creates the service parent command with all subcommands
610
func NewServiceCommand() *cobra.Command {
@@ -19,15 +23,18 @@ func NewServiceCommand() *cobra.Command {
1923
cmd.AddCommand(NewRestartCommand())
2024
cmd.AddCommand(NewDeleteCommand())
2125

22-
// Add env subcommand (placeholder for now)
23-
// TODO: Implement env commands
24-
// envCmd := &cobra.Command{
25-
// Use: "env",
26-
// Short: "Manage service environment variables",
27-
// }
28-
// envCmd.AddCommand(env.NewListCommand())
29-
// ... more env commands
30-
// cmd.AddCommand(envCmd)
26+
// Add env subcommand
27+
envCmd := &cobra.Command{
28+
Use: "env",
29+
Short: "Manage service environment variables",
30+
}
31+
envCmd.AddCommand(env.NewListCommand())
32+
envCmd.AddCommand(env.NewGetCommand())
33+
envCmd.AddCommand(env.NewCreateCommand())
34+
envCmd.AddCommand(env.NewUpdateCommand())
35+
envCmd.AddCommand(env.NewDeleteCommand())
36+
envCmd.AddCommand(env.NewSyncCommand())
37+
cmd.AddCommand(envCmd)
3138

3239
return cmd
3340
}

internal/models/application.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ type EnvironmentVariableCreateRequest struct {
120120
IsPreview *bool `json:"is_preview,omitempty"`
121121
IsLiteral *bool `json:"is_literal,omitempty"`
122122
IsMultiline *bool `json:"is_multiline,omitempty"`
123+
IsRuntime *bool `json:"is_runtime,omitempty"`
123124
}
124125

125126
// EnvironmentVariableUpdateRequest represents the request to update an environment variable
@@ -131,4 +132,5 @@ type EnvironmentVariableUpdateRequest struct {
131132
IsPreview *bool `json:"is_preview,omitempty"`
132133
IsLiteral *bool `json:"is_literal,omitempty"`
133134
IsMultiline *bool `json:"is_multiline,omitempty"`
135+
IsRuntime *bool `json:"is_runtime,omitempty"`
134136
}

0 commit comments

Comments
 (0)