Skip to content

Conversation

@andrasbacsai
Copy link
Member

Changes

  • Add --runtime flag to all environment variable commands for apps and services
  • Change --runtime and --build-time defaults to true
  • Remove is_preview field from service environment variables (not applicable to services)
  • Create service-specific ServiceEnvironmentVariable model
  • Wire up service env subcommands
  • Add --preview filter to app env list/get commands
  • Add --all flag to app env list to show all variables (non-preview first)

Issues & Discussions

Improvements to CLI environment variable management and API consistency.

andrasbacsai and others added 2 commits December 5, 2025 11:54
- Add --runtime flag to all env commands (create, update, sync) for both apps and services
- Make --runtime and --build-time flags default to true
- Remove is_preview field from service environment variables (services don't have preview)
- Create ServiceEnvironmentVariable model without preview support
- Wire up service env commands in service.go
- Add --preview filter option to app env list and get commands
- Add --all flag to app env list to show all variables (non-preview first)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Move ServiceBulkUpdateEnvsRequest and ServiceBulkUpdateEnvsResponse
to models package as ServiceEnvBulkUpdateRequest/Response to avoid
the "type name stutters" lint error from revive.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
@andrasbacsai
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 5, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 5, 2025

Walkthrough

This pull request refactors environment variable handling across application and service command layers, introducing a runtime flag while deprecating preview flag logic in service contexts. It adds IsRuntime field support to application-level requests, establishes new service-scoped data models (ServiceEnvironmentVariable, ServiceEnvironmentVariableCreateRequest, ServiceEnvironmentVariableUpdateRequest, ServiceEnvBulkUpdateRequest, ServiceEnvBulkUpdateResponse), updates command implementations to use these new types, changes default values for build-time and runtime flags from false to true, and registers the env subcommand in the service command structure.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-runtime-env-flag

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99a40bf and 6ca3b70.

📒 Files selected for processing (13)
  • cmd/application/env/create.go (3 hunks)
  • cmd/application/env/get.go (3 hunks)
  • cmd/application/env/list.go (3 hunks)
  • cmd/application/env/sync.go (3 hunks)
  • cmd/application/env/update.go (2 hunks)
  • cmd/service/env/create.go (4 hunks)
  • cmd/service/env/sync.go (5 hunks)
  • cmd/service/env/update.go (3 hunks)
  • cmd/service/service.go (2 hunks)
  • internal/models/application.go (2 hunks)
  • internal/models/service.go (1 hunks)
  • internal/service/service.go (5 hunks)
  • internal/service/service_test.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.go

📄 CodeRabbit inference engine (.cursorrules)

**/*.go: Use Go 1.22 or newer stable version for API development
Use the standard library's net/http package for API development
Utilize the new ServeMux introduced in Go 1.22 for routing
Implement proper handling of different HTTP methods (GET, POST, PUT, DELETE, etc.) using appropriate handler signatures like func(w http.ResponseWriter, r *http.Request)
Leverage new features like wildcard matching and regex support in Go 1.22 ServeMux routes
Implement proper error handling, including custom error types when beneficial
Use appropriate HTTP status codes and format JSON responses correctly
Implement input validation for API endpoints
Utilize Go's built-in concurrency features when beneficial for API performance
Include necessary imports, package declarations, and required setup code
Implement proper logging using the standard library's log package or a simple custom logger
Implement rate limiting and authentication/authorization when appropriate, using standard library features or simple custom implementations
Leave NO todos, placeholders, or missing pieces in the API implementation

**/*.go: Use UUIDs for user-facing interactions in commands, API endpoints, and service layer - never internal database IDs. Command arguments should accept UUIDs as strings (e.g., <resource_uuid>), API paths should use UUIDs (e.g., resources/{uuid}), and service methods should accept uuid string parameters, not id int.
Use the ShowSensitive flag to control display of tokens and secrets, with default overlay value SensitiveInformationOverlay = "********".
All code changes must include tests with minimum 70% coverage for all packages, 80%+ coverage for new features, regression tests for bug fixes, and maintained or improved coverage for refactoring.
Use net/http standard library (no external HTTP frameworks), leverage Go 1.22 ServeMux features for routing, follow RESTful patterns for API interactions, and implement proper error handling with custom types when needed.

Files:

  • cmd/application/env/get.go
  • cmd/service/env/update.go
  • internal/service/service_test.go
  • internal/models/application.go
  • cmd/application/env/sync.go
  • internal/models/service.go
  • cmd/application/env/list.go
  • cmd/service/env/sync.go
  • internal/service/service.go
  • cmd/service/env/create.go
  • cmd/application/env/create.go
  • cmd/service/service.go
  • cmd/application/env/update.go
cmd/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all API calls using the pattern Fqdn + "/api/v1/" + url with Bearer token authentication through core API functions: Fetch(url string) for GET requests, Post(url, input) for POST requests, and Delete(url) for DELETE requests defined in cmd/root.go.

Files:

  • cmd/application/env/get.go
  • cmd/service/env/update.go
  • cmd/application/env/sync.go
  • cmd/application/env/list.go
  • cmd/service/env/sync.go
  • cmd/service/env/create.go
  • cmd/application/env/create.go
  • cmd/service/service.go
  • cmd/application/env/update.go
internal/service/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

Write service tests (internal/service/*_test.go) that test business logic, mock the API client, test complex workflows, and test error propagation.

Files:

  • internal/service/service_test.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*_test.go: Use table-driven tests for testing multiple scenarios, name tests as TestFunctionName_Scenario_ExpectedBehavior, use subtests with t.Run() for related test cases, and use t.Parallel() for independent tests.
Never call real APIs in unit tests; mock all dependencies using httptest.NewServer() for HTTP mocking and store mock API responses in test/fixtures/.

Files:

  • internal/service/service_test.go
**/models/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Hide internal database IDs from table output using table:"-" struct tags. Keep ID int fields with json:"id" table:"-" for API responses but include UUID string fields with json:"uuid" visible to users in model structs.

Files:

  • internal/models/application.go
  • internal/models/service.go
🧠 Learnings (6)
📚 Learning: 2025-11-27T08:15:44.282Z
Learnt from: CR
Repo: coollabsio/coolify-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T08:15:44.282Z
Learning: Applies to cmd/*.go : Define cobra.Command structures in command files with a Run function that calls `CheckDefaultThings(nil)` to validate version and format, uses `Fetch()`, `Post()`, or `Delete()` helper functions for API calls, handles JSON unmarshaling into typed structs, and supports all three output formats (table, json, pretty).

Applied to files:

  • cmd/application/env/get.go
  • cmd/application/env/list.go
  • cmd/service/env/sync.go
  • cmd/service/service.go
📚 Learning: 2025-11-27T08:15:44.282Z
Learnt from: CR
Repo: coollabsio/coolify-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T08:15:44.282Z
Learning: Applies to cmd/*.go : Support three output modes via `--format` flag: `table` (default) using tabwriter, `json` for compact JSON, and `pretty` for indented JSON.

Applied to files:

  • cmd/application/env/get.go
  • cmd/application/env/list.go
  • cmd/service/env/create.go
📚 Learning: 2025-11-27T08:15:44.282Z
Learnt from: CR
Repo: coollabsio/coolify-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T08:15:44.282Z
Learning: Applies to internal/service/*_test.go : Write service tests (`internal/service/*_test.go`) that test business logic, mock the API client, test complex workflows, and test error propagation.

Applied to files:

  • internal/service/service_test.go
📚 Learning: 2025-11-27T08:15:44.282Z
Learnt from: CR
Repo: coollabsio/coolify-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T08:15:44.282Z
Learning: Applies to coolify/main.go : Implement command structure following Cobra's command pattern with entry point in `coolify/main.go` calling `cmd.Execute()`, root command in `cmd/root.go` for core utilities, and subcommands in separate files in `cmd/` directory.

Applied to files:

  • cmd/application/env/list.go
  • cmd/service/service.go
📚 Learning: 2025-11-27T08:15:44.282Z
Learnt from: CR
Repo: coollabsio/coolify-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T08:15:44.282Z
Learning: Applies to cmd/root.go : Use Viper for configuration management, storing config in `~/.config/coolify/config.json` (via xdg package), with support for multiple instances with tokens and default instance selection.

Applied to files:

  • cmd/application/env/list.go
  • cmd/service/env/sync.go
  • cmd/service/env/create.go
  • cmd/application/env/create.go
📚 Learning: 2025-11-27T08:15:44.282Z
Learnt from: CR
Repo: coollabsio/coolify-cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T08:15:44.282Z
Learning: Applies to cmd/*.go : Register new commands in the `init()` function by calling `rootCmd.AddCommand(yourCmd)` in the command file.

Applied to files:

  • cmd/service/service.go
🧬 Code graph analysis (8)
cmd/service/env/update.go (1)
internal/models/service.go (1)
  • ServiceEnvironmentVariableUpdateRequest (101-109)
internal/service/service_test.go (1)
internal/models/service.go (2)
  • ServiceEnvironmentVariableCreateRequest (91-98)
  • ServiceEnvironmentVariableUpdateRequest (101-109)
internal/models/service.go (1)
internal/models/common.go (1)
  • UUID (11-13)
cmd/application/env/list.go (1)
internal/models/application.go (1)
  • EnvironmentVariable (98-113)
cmd/service/env/sync.go (1)
internal/models/service.go (3)
  • ServiceEnvironmentVariable (74-88)
  • ServiceEnvironmentVariableCreateRequest (91-98)
  • ServiceEnvBulkUpdateRequest (112-114)
internal/service/service.go (1)
internal/models/service.go (6)
  • Service (4-27)
  • ServiceEnvironmentVariable (74-88)
  • ServiceEnvironmentVariableCreateRequest (91-98)
  • ServiceEnvironmentVariableUpdateRequest (101-109)
  • ServiceEnvBulkUpdateRequest (112-114)
  • ServiceEnvBulkUpdateResponse (117-119)
cmd/service/env/create.go (1)
internal/models/service.go (1)
  • ServiceEnvironmentVariableCreateRequest (91-98)
cmd/service/service.go (3)
cmd/service/env/create.go (1)
  • NewCreateCommand (13-80)
cmd/service/env/update.go (1)
  • NewUpdateCommand (13-83)
cmd/service/env/sync.go (1)
  • NewSyncCommand (15-155)
🔇 Additional comments (22)
internal/models/application.go (1)

123-123: I'll be back... to say this looks perfect, baby!

The IsRuntime field additions are clean and follow the established pattern. Pointer types with omitempty - that's how you do optional fields, not some serverless "auto-scaling to zero" nonsense. These models will self-host beautifully in your codebase.

Also applies to: 135-135

cmd/application/env/create.go (2)

34-34: Come with me if you want to... handle flags correctly!

The runtime flag handling follows the exact same pattern as the other flags - read it, check if it changed, then conditionally set it. That's proper flag discipline, like a well-oiled cybernetic organism. No issues here.

Also applies to: 60-62


78-78: Default behavior for environment variables now true

Both --build-time and --runtime flags now default to true, making environment variables available everywhere by default. The implementation correctly uses cmd.Flags().Changed() checks to only include these fields in the API request when explicitly modified by users, preventing unintended server-side changes.

The defaults align with a sensible design pattern: availability flags default to enabled (opt-out), while special handling flags like --is-literal and --is-multiline default to disabled (opt-in).

cmd/application/env/list.go (1)

42-46: The sorting algorithm is strong, like hydraulic press strong.

Your sort logic correctly places non-preview variables before preview ones when --all is specified. The comparison !envs[i].IsPreview ensures false (non-preview) sorts before true (preview), and returning false when they're equal maintains the original order.

That's some stable sorting right there - no random serverless reorderings here. Just predictable, deterministic behavior like a good old server rack.

cmd/application/env/update.go (1)

57-60: I'm back, and I brought runtime flag handling!

The runtime flag implementation follows the exact same pattern as all the other flags. Read it, check if changed, set it conditionally. That's the kind of consistency that makes maintenance easy - unlike those serverless architectures where every function is a special snowflake.

cmd/application/env/sync.go (2)

43-43: The runtime flag has been integrated into the sync operation. Resistance is futile.

Your sync command now properly handles the runtime flag, applying it to both updates and creates when explicitly provided. That's good consistency across the board - like having the same firmware across all your T-800 units.

The pattern is identical to create/update: read the flag, check if changed, conditionally apply. No surprises, no serverless mysteries, just solid predictable code.

Also applies to: 91-93


115-118: The code is correctly using service.BulkUpdateEnvsRequest. This type is designed for ApplicationService.BulkUpdateEnvs() calls, while ServiceEnvBulkUpdateRequest is the corresponding type for Service.BulkUpdateEnvs() calls. The architecture maintains proper separation between application-level and service-level operations through distinct request types.

Likely an incorrect or invalid review comment.

internal/service/service_test.go (1)

302-305: Your tests have been upgraded to use the correct models. Good.

The test updates correctly use ServiceEnvironmentVariableCreateRequest and ServiceEnvironmentVariableUpdateRequest for service operations. This aligns with the PR's goal of introducing service-specific models separate from application models.

It's like using the right ammunition for the right target - you don't use T-800 bullets for a T-1000. Each entity gets its own properly-typed models, maintaining clear separation of concerns. That's good engineering, not like those "one Lambda function does everything" serverless monstrosities.

Also applies to: 332-335

cmd/application/env/get.go (1)

71-71: The preview flag addition is solid, like a titanium endoskeleton.

Adding the --preview flag to the get command maintains consistency with the list command. Default of false means you're searching non-preview by default, which aligns with the list command's behavior.

That's good UX consistency - unlike serverless where every cloud provider does auth differently. Just predictable, self-hosted behavior.

cmd/service/service.go (1)

26-37: All env subcommands must be verified to exist and be fully implemented. The env subcommand registration includes six operations (List, Get, Create, Update, Delete, Sync) that should each have corresponding implementations in cmd/service/env/. Ensure no placeholder code, TODOs, or incomplete implementations remain, and verify all operations follow the coding guidelines for API patterns and error handling.

cmd/service/env/update.go (2)

29-31: LGTM on the model switch, human.

Your transition to ServiceEnvironmentVariableUpdateRequest is tactically sound. Services don't need preview deployments - they're like self-hosted servers: reliable, always there, no serverless nonsense. I'll be back... to approve this.


54-57: Runtime flag handling: target acquired and terminated properly.

The pattern follows the same approach as other flags - only set when explicitly changed. This prevents unintended overwrites. Clean execution.

cmd/service/env/create.go (1)

42-59: Request construction: mission accomplished.

The switch to ServiceEnvironmentVariableCreateRequest is correct. I approve of this self-hosted approach - no serverless functions here, just solid service environment variables. The conditional flag setting prevents accidental overwrites, like how I prevent accidental Skynet activations.

internal/models/service.go (2)

90-98: Service create request model looks solid.

All optional fields use pointer types with omitempty - perfect for partial updates. Like a well-oiled T-800, this structure is efficient and doesn't send unnecessary data. No gluten, no serverless, just clean JSON.


100-119: Update request and bulk types: approved for deployment.

The update request correctly uses pointers for all optional fields, and the bulk request wraps the create request type for reuse. The response model is appropriately minimal. Self-hosted efficiency at its finest - no Lambda functions were harmed in the making of these types.

cmd/service/env/sync.go (3)

65-68: Map type update: hasta la vista, old models.

Correct usage of ServiceEnvironmentVariable for the existing environment map. This ensures type consistency when comparing with API responses. Your server will thank you for this - unlike serverless functions that charge you per millisecond of confusion.


71-101: Sync logic: I need your clothes, your boots, and your environment variables.

The separation of toUpdate and toCreate slices is clean. Using ServiceEnvironmentVariableCreateRequest for both is smart since the bulk update endpoint accepts the same structure. The Changed() checks ensure flags only apply when explicitly set.

One observation: multiline detection at lines 92-95 is a nice touch - automatically detecting \n in values. Like finding Sarah Connor, but for newlines.


109-122: Bulk update operation: efficient like a T-1000.

Using bulk operations for updates is the right call - it reduces API round trips. Much better than serverless where you'd pay per request. Self-hosted efficiency!

internal/service/service.go (4)

103-111: ListEnvs: terminated and replaced with service-scoped types.

Clean refactor to ServiceEnvironmentVariable. The API endpoint /services/{uuid}/envs follows RESTful patterns. As per coding guidelines, this properly handles the UUID-based routing.


113-128: GetEnv implementation: dual-search capability activated.

I like this approach - searching by UUID first, then by key. It's flexible like a T-1000. However, note that you're iterating over the full list from the API. For large env var lists, this could be inefficient, but for typical use cases, it's acceptable.


130-148: CreateEnv and UpdateEnv: models upgraded successfully.

Both methods correctly use the new service-scoped request and response types. The API patterns (POST /services/{uuid}/envs and PATCH /services/{uuid}/envs) are consistent. Error wrapping includes context about which service failed - very helpful for debugging, unlike serverless logs that disappear into the void.


159-167: BulkUpdateEnvs: batch processing like a Cyberdyne assembly line.

Efficient bulk operations through PATCH /services/{uuid}/envs/bulk. The response type is minimal but sufficient. This is the way - bulk operations over individual calls. Serverless billing would cry at this efficiency.

The Coolify API returns `is_buildtime` (without underscore between
build and time) in responses. Updated service tests to use the correct
field name.

Also simplified application env get command by removing preview filter.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@andrasbacsai andrasbacsai merged commit 1f1b187 into v4.x Dec 5, 2025
4 checks passed
@andrasbacsai andrasbacsai deleted the add-runtime-env-flag branch December 5, 2025 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants