Compare commits

...

76 Commits

Author SHA1 Message Date
github-actions[bot] 26c0925854 chore: bump version to v1.1 2025-12-05 12:04:57 +00:00
Andras Bacsai 1f1b187ed2 Merge pull request #45 from coollabsio/add-runtime-env-flag
Add runtime env flag and improve service env handling
2025-12-05 13:01:46 +01:00
Andras Bacsai 4af598c213 fix: use is_buildtime JSON tag to match Coolify API response
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>
2025-12-05 12:58:18 +01:00
Andras Bacsai 6ca3b700ce fix: resolve lint errors for stuttering type names
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>
2025-12-05 12:00:41 +01:00
Andras Bacsai 8cf0b71ebf feat: add runtime env flag and improve service env handling
- 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>
2025-12-05 11:54:22 +01:00
github-actions[bot] 99a40bfa1d chore: bump version to v1.0.5 2025-11-27 08:20:33 +00:00
Andras Bacsai 188834fd6d Merge pull request #39 from YaRissi/fix/version
fix: update  release workflow
2025-11-27 09:17:39 +01:00
Andras Bacsai f9c3b9869a Merge pull request #43 from coollabsio/dependabot/go_modules/golang.org/x/crypto-0.45.0
chore(deps): bump golang.org/x/crypto from 0.43.0 to 0.45.0
2025-11-27 09:16:36 +01:00
dependabot[bot] 6044a2107e chore(deps): bump golang.org/x/crypto from 0.43.0 to 0.45.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.43.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.43.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-27 08:16:04 +00:00
Andras Bacsai 2f9dc6e8d7 Merge pull request #42 from coollabsio/fix-env-is-build-time-value
Fix is_buildtime JSON tag and add is_runtime, is_shared fields
2025-11-27 09:15:01 +01:00
Andras Bacsai 1e741309cb fix: correct is_buildtime JSON tag and add is_runtime, is_shared fields
Fixed critical bug where is_buildtime field was not unmarshaling from API
responses due to JSON tag mismatch (was expecting 'is_build_time' with
underscore but API returns 'is_buildtime' without underscore). Also added
missing is_runtime and is_shared fields that are present in API responses.

Added comprehensive tests for EnvironmentVariable model and service layer
to ensure proper marshaling/unmarshaling of all fields. Achieved 100%
coverage for EnvironmentVariable struct.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 09:11:35 +01:00
YaRissi 51f759c38f fix: improve error handling in Stop-WithError function and update git tag push command 2025-11-20 02:34:22 +01:00
Andras Bacsai 51e9ec5ec8 Update install.sh 2025-11-18 23:25:17 +01:00
Andras Bacsai e071fd81d4 Merge branch 'v4.x' into fix/version 2025-11-18 23:19:32 +01:00
Andras Bacsai cb0bbfc5cb Merge pull request #41 from ncryptedV1/fix/install-script-release-download-link
fix: leading 'v' for release filename of install script
2025-11-18 23:18:53 +01:00
ncryptedV1 87b6b8fdf7 fix: leading 'v' for release filename of install script 2025-11-18 17:24:25 +01:00
YaRissi 3dbe2507f4 fix deprecated builds tag 2025-11-16 16:39:08 +01:00
YaRissi 1e82217a50 feat: update installation instructions for Windows and add PowerShell script 2025-11-16 16:29:27 +01:00
YaRissi 6f33fa00f1 fix: readd version in binary name 2025-11-16 16:06:03 +01:00
YaRissi d7841b3b5a chore: readd version in binaryname 2025-11-16 15:59:41 +01:00
YaRissi 234f6e9ed6 fix: update binary name in installation script 2025-11-10 21:45:31 +01:00
YaRissi fa86ceb5cc fix: update filename format in download function for consistency 2025-11-10 21:37:22 +01:00
YaRissi 646bf9de36 fix: update version tagging logic in release workflow 2025-11-10 21:07:02 +01:00
github-actions[bot] be29a6e05d chore: bump version to v1.0.4 2025-11-10 13:24:40 +00:00
Andras Bacsai 63a882107a Merge pull request #37 from coollabsio/add-deployment-logs-cli
Add deployment management commands for improved user experience
2025-11-10 14:21:49 +01:00
Andras Bacsai 08cd3b8ac7 fix: resolve nilerr linting error in deployment logs pretty-print logic
Restructure JSON pretty-print logic to check for success instead of
failure, avoiding nilerr linting violation while maintaining fallback
behavior. The change ensures proper error handling patterns without
hiding potential bugs.
2025-11-10 14:19:29 +01:00
Andras Bacsai 06f191e9ba Merge branch 'v4.x' into add-deployment-logs-cli 2025-11-10 14:18:09 +01:00
Andras Bacsai 7a19a02c02 fix: resolve remaining nilerr linting errors
- Explicitly ignore errors where we have intentional fallback behavior
- Use blank identifier (_) for errors we don't need to check
- Restructure error checking to avoid nilerr pattern
- All errors are properly handled or explicitly ignored
2025-11-10 13:55:10 +01:00
Andras Bacsai 806a6b9716 fix: resolve linting errors (errcheck, nilerr, revive, gci)
- Add error checking for w.Write calls in tests
- Rename unused http.Request parameters to _ in test handlers
- Fix nilerr errors by using different variable names to avoid shadowing
- Fix gci import grouping (stdlib, external, internal)
2025-11-10 13:48:58 +01:00
Andras Bacsai a18d751ad4 style: fix gofmt formatting in deployment_test.go 2025-11-10 13:44:42 +01:00
Andras Bacsai 9283717821 fix: add --format flag support and filter hidden logs in JSON output
Add proper support for --format flag (json/pretty/table) in deployment logs:
- Respect global --format flag for output formatting
- Filter hidden logs by default in JSON/pretty formats
- Only show debug logs when --debuglogs flag is present
- Apply --lines limit only to table format (JSON shows complete data)

Changes:
- Add GetLogsByDeploymentWithFormat() and GetLogsByApplicationWithFormat()
- Filter log entries with "hidden": true unless --debuglogs is set
- Return raw JSON for --format json
- Return pretty-printed JSON for --format pretty
- Return human-readable text for --format table (default)
2025-11-10 13:26:29 +01:00
Andras Bacsai a44c712163 feat: add deployment logs commands with human-readable output
Add new commands to query and display deployment logs:
- coolify app deployments list <app-uuid>: List all deployments for an application
- coolify app deployments logs <app-uuid> [deployment-uuid>]: Get deployment logs

Features:
- Retrieves latest deployment logs by default or specific deployment by UUID
- Parses JSON log format into human-readable line-by-line output
- Supports --lines flag to limit output to last N lines
- Supports --follow flag for real-time log streaming
- Supports --debuglogs flag to show hidden debug commands
- Uses API pagination (skip/take) for efficient deployment fetching
- Sorts deployments by timestamp to ensure latest is selected

Implementation:
- Add DeploymentsListResponse to handle paginated API responses
- Add log parsing utilities to format JSON logs as plain text
- Add comprehensive test coverage for deployment service methods
- Update README with new command documentation
2025-11-10 13:18:15 +01:00
Andras Bacsai 7c42af6203 Merge pull request #36 from coollabsio/fix/remove-file-check
Remove gzip file validation check
2025-11-10 12:34:29 +01:00
Andras Bacsai ed2dbd4947 Merge pull request #34 from YaRissi/version-inject
feat: Version inject
2025-11-10 12:33:57 +01:00
Laurence Jones f1301c1dbd Remove gzip file validation check
fix #35 

Removed basic check for gzip compressed file, since tar will error if not valid type anyways.
2025-11-07 08:08:31 +00:00
YaRissi 8a58cdd72f fix: update documentation to reflect new version retrieval location 2025-10-29 22:07:58 +01:00
Yassir Elmarissi 5ca4c3a8e3 Merge branch 'v4.x' into version-inject 2025-10-29 15:33:27 +01:00
Andras Bacsai 89cd744696 Merge pull request #33 from YaRissi/ci
feat: testing ci with linter
2025-10-29 15:22:39 +01:00
Andras Bacsai 694b4f8e32 Merge pull request #31 from YaRissi/go-install
feat: installable via go
2025-10-29 15:18:10 +01:00
Yassir Elmarissi ef15d013da fix: update regex for version injection
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-10-29 00:14:51 +01:00
YaRissi 9f5a44be04 fix: comment 2025-10-28 23:51:16 +01:00
YaRissi 1078a9d3ca fix: improve instance validation and error handling in context switching 2025-10-28 23:42:04 +01:00
YaRissi 9cef9ebee7 fix: improve error handling in commands 2025-10-28 23:27:03 +01:00
YaRissi 51f33cfc5e reactor: change context.Background zu cmd.Context 2025-10-28 23:15:02 +01:00
YaRissi 559d4e2709 chore: update commit message for version bump in release workflow 2025-10-21 01:02:43 +02:00
YaRissi 94e237e2b1 fix: specify branch reference in action and fix readme 2025-10-21 00:57:52 +02:00
YaRissi 3dd0dfdce1 fix: stage version file before committing 2025-10-21 00:34:10 +02:00
YaRissi aac44a0ddb fix: remove parallelism setting in goreleaser configuration 2025-10-21 00:28:22 +02:00
YaRissi a4c96d2803 fix: correct regex for version update in checker.go 2025-10-21 00:23:43 +02:00
YaRissi dc9670992a feat: implement version update automation 2025-10-21 00:13:55 +02:00
YaRissi c369689131 add coverage to test command 2025-10-18 13:50:45 +02:00
YaRissi dcf0b39c1b fix: golangci-lint version 2025-10-18 13:46:46 +02:00
YaRissi 14a3f00c57 refactor: update command handlers to use RunE for error handling 2025-10-18 13:37:31 +02:00
YaRissi 86f77716ee fix: all found problems by the linter 2025-10-18 13:13:35 +02:00
YaRissi ef91ed987e feat: testing ci with linter and pull request template 2025-10-18 13:12:21 +02:00
Yassir Elmarissi d8cf7a5986 docs: update installation section to emphasize the recommended install script 2025-10-17 21:32:44 +00:00
YaRissi d30b0b90de fix: run commands paths 2025-10-17 23:08:13 +02:00
YaRissi 4a8a659090 fix: update build commands and paths in configuration and documentation 2025-10-17 22:57:05 +02:00
Andras Bacsai aa1bda4063 Merge pull request #32 from coollabsio/andrasbacsai/env-sync-analysis
Add flags to env sync command
2025-10-17 22:29:43 +02:00
Andras Bacsai 69f2a7ac1f Changes auto-committed by Conductor 2025-10-17 22:29:14 +02:00
Yassir Elmarissi ad185a42ee update goreleaser 2025-10-17 14:58:05 +00:00
Yassir Elmarissi 22e34fb72e add dist to gitignore 2025-10-17 11:32:39 +00:00
Yassir Elmarissi c5b0ad4218 feat: installable via go and update gorealeser 2025-10-17 11:29:04 +00:00
Andras Bacsai 1a6fa9e397 Merge pull request #29 from coollabsio/andrasbacsai/parallel-goreleaser-builds
Enable parallel GoReleaser builds
2025-10-17 11:41:30 +02:00
Andras Bacsai b767468b29 Changes auto-committed by Conductor 2025-10-17 11:40:59 +02:00
Andras Bacsai 6b742d37dd Update checker.go 2025-10-17 11:35:47 +02:00
Andras Bacsai 0b2d277e41 Merge pull request #27 from coollabsio/contributing-guide
Add CONTRIBUTING.md file
2025-10-17 11:35:23 +02:00
Andras Bacsai ac9a486d46 Merge pull request #28 from coollabsio/andrasbacsai/add-min-version-reqs
Add minimum version checks to CLI commands
2025-10-17 11:35:15 +02:00
Andras Bacsai ba8f05769f Changes auto-committed by Conductor 2025-10-17 11:34:49 +02:00
Andras Bacsai 5c799410e5 Changes auto-committed by Conductor 2025-10-17 11:33:48 +02:00
Andras Bacsai d5dd1b5bdf Changes auto-committed by Conductor 2025-10-17 11:32:53 +02:00
Andras Bacsai 0d6a2bb1e9 Changes auto-committed by Conductor 2025-10-17 11:32:53 +02:00
Andras Bacsai 9eba5b97f7 Merge pull request #26 from coollabsio/andrasbacsai/check-readme
Update README with CLI command fixes
2025-10-17 11:26:10 +02:00
Andras Bacsai 76c1434711 Changes auto-committed by Conductor 2025-10-17 11:24:00 +02:00
Andras Bacsai f35d299f6e Update README.md 2025-10-17 11:17:17 +02:00
Andras Bacsai 680264ab3f Update README.md 2025-10-17 11:15:38 +02:00
138 changed files with 3556 additions and 818 deletions
+2 -2
View File
@@ -5,14 +5,14 @@ tmp_dir = "tmp"
[build]
args_bin = []
bin = "./coolify"
cmd = "go build -o ./coolify ."
cmd = "go build -o ./coolify ./coolify"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", ".git", ".conductor"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = "echo 'Build complete. Binary: ./coolify'"
full_bin = "echo 'Build complete. Binary: ./coolify/coolify'"
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
+7
View File
@@ -0,0 +1,7 @@
## Changes
-
## Issues & Discussions
- fix #
+34 -5
View File
@@ -10,18 +10,47 @@ jobs:
release-cli:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: ${{ env.GITHUB_REF_NAME }}
args: release --clean
workdir: ./
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
update-version:
needs: [release-cli]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: v4.x
fetch-depth: 0
- name: Update version file
run: |
TAG=${GITHUB_REF#refs/tags/}
echo "Updating version to $TAG"
sed -i "s/^\tversion = \".*\"/\tversion = \"$TAG\"/" internal/version/checker.go
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add internal/version/checker.go
git commit -m "chore: bump version to $TAG"
git push origin v4.x
# Move the tag to point to the new commit with updated version
git tag -d "$TAG" || true
git tag "$TAG"
git push origin "refs/tags/$TAG" --force
+55
View File
@@ -0,0 +1,55 @@
name: Testing CLI
on:
push:
branches: ["v4.x"]
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Run gofmt
run: diff -u <(echo -n) <(gofmt -d -s .)
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v8
with:
version: v2.5.0 # pin version for consistency
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: Run tests
run: go test -v -race -cover ./...
go-mod-tidy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: Run go mod tidy
run: go mod tidy
- name: Check uncommitted changes
run: git diff --exit-code
- if: failure()
run: echo "::error::Check failed, please run 'go mod tidy' and commit the changes."
+2 -1
View File
@@ -1,11 +1,12 @@
coolify-cli
coolify
/coolify
config.json
.claude
# Generated documentation (can be regenerated)
man/
docs/cli/
dist/
# Test coverage
coverage.out
+75
View File
@@ -0,0 +1,75 @@
version: "2"
run:
timeout: 5m
linters:
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- contextcheck
- durationcheck
- errchkjson
- errorlint
- exhaustive
- gocheckcompilerdirectives
- gochecksumtype
- gocritic
- gomoddirectives
- gomodguard
- gosec
- gosmopolitan
- loggercheck
- makezero
- musttag
- nilerr
- nilnesserr
- noctx
- protogetter
- reassign
- recvcheck
- revive
- rowserrcheck
- spancheck
- sqlclosecheck
- testifylint
- unparam
- zerologlint
settings:
exhaustive:
default-signifies-exhaustive: true
staticcheck:
checks: ["all", "-ST1005", "-S1016"]
gosec:
excludes:
- G115
gosmopolitan:
allow-time-local: true
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- std-error-handling
formatters:
enable:
- gci
- goimports
settings:
gci:
sections:
- standard
- default
- prefix(github.com/coollabsio)
exclusions:
generated: lax
+27 -2
View File
@@ -1,8 +1,19 @@
version: 2
before:
hooks:
- go mod tidy
builds:
- binary: coolify
- id: coolify
binary: coolify
flags:
- -trimpath
ldflags:
- -s
- -w
- -X github.com/coollabsio/coolify-cli/internal/version.version={{ .Version }}
main: ./coolify/main.go
goos:
- darwin
- linux
@@ -11,4 +22,18 @@ builds:
- amd64
- arm64
env:
- CGO_ENABLED=0
- CGO_ENABLED=0
checksum:
name_template: checksums.txt
algorithm: sha256
archives:
- id: coolify-archive
ids:
- coolify
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
format_overrides:
- goos: windows
formats: [zip]
+4 -1
View File
@@ -550,7 +550,10 @@ c.httpClient = &http.Client{
```bash
# Local build
go build -o coolify .
go build -o coolify ./coolify
# Install locally
go install ./coolify
# Multi-platform release
goreleaser release --clean
+6 -6
View File
@@ -21,7 +21,7 @@ All commands in this CLI are wrappers around API endpoints defined in the OpenAP
### Command Structure
The codebase follows Cobra's command pattern with a root command and subcommands:
- Entry point: `main.go` calls `cmd.Execute()`
- Entry point: `coolify/main.go` calls `cmd.Execute()`
- Root command: `cmd/root.go` - contains core utilities (HTTP client, authentication, version checking, config management)
- Subcommands: Each command is in its own file in `cmd/`:
- `context.go` - manage Coolify context (add, remove, list, set default/token)
@@ -62,23 +62,23 @@ Three output modes supported via `--format` flag:
### Build
```bash
go build -o coolify .
go build -o coolify ./coolify
```
### Run locally
```bash
go run main.go [command]
go run ./coolify [command]
```
### Test a command
```bash
go run main.go instances list
go run main.go servers list --debug
go run ./coolify context list
go run ./coolify servers list --debug
```
### Install locally
```bash
go install
go install ./coolify
```
### Run tests
+620
View File
@@ -0,0 +1,620 @@
# Contributing to Coolify CLI
Thank you for your interest in contributing to the Coolify CLI! This document provides guidelines and instructions for contributing to the project.
## Table of Contents
- [Getting Started](#getting-started)
- [Development Setup](#development-setup)
- [Project Architecture](#project-architecture)
- [Adding a New Command](#adding-a-new-command)
- [Testing Requirements](#testing-requirements)
- [Code Style & Conventions](#code-style--conventions)
- [Submitting Changes](#submitting-changes)
## Getting Started
Before you start contributing:
1. **Read the [ARCHITECTURE.md](ARCHITECTURE.md)** for detailed architectural guidance
2. **Review the [OpenAPI specification](https://github.com/coollabsio/coolify/blob/v4.x/openapi.json)** to understand available API endpoints
3. **Check existing issues** to see if your feature/bug is already being worked on
4. **Open an issue** to discuss your proposed changes (for large features)
### Prerequisites
- Go 1.24 or higher
- Git
## Development Setup
### Clone and Build
```bash
# Fork the repository on GitHub
# Clone your fork
git clone https://github.com/YOUR_USERNAME/coolify-cli.git
cd coolify-cli
# Build the CLI
go build -o coolify ./coolify
# Install locally
go install
```
### Running the CLI
```bash
# Run without installing
go run ./coolify [command]
# Example commands
go run ./coolify context list
go run ./coolify server list --debug
# With flags
go run ./coolify server list --format json --debug
```
### Project Structure
```
cmd/ # CLI commands (organized by feature)
├── root.go # Root command and global flags
├── application/ # Application management commands
├── context/ # Manage Coolify instances
├── server/ # Server management
├── project/ # Project management
├── database/ # Database management
├── deployment/ # Deployment operations
├── service/ # Service management
└── ...
internal/ # Internal packages
├── api/ # API client (HTTP communication)
├── cli/ # CLI utilities (GetAPIClient helper)
├── config/ # Configuration management
├── models/ # Data models and structs
├── output/ # Output formatters (table, json, pretty)
├── parser/ # Input parsing utilities
├── service/ # Business logic layer
└── version/ # Version management
test/ # Test utilities and fixtures
└── fixtures/ # Mock API response data
```
## Project Architecture
The Coolify CLI follows a **layered architecture**:
```
User → Commands (cmd/) → Services (internal/service/) → API Client (internal/api/) → Coolify API
```
### Layer Responsibilities
1. **Command Layer** (`cmd/`)
- Parse CLI arguments and flags
- Call service layer methods
- Format output using output formatters
2. **Service Layer** (`internal/service/`)
- Business logic
- Coordinate API calls
- Transform data
3. **API Client Layer** (`internal/api/`)
- HTTP communication
- Retry logic with exponential backoff
- Authentication (Bearer tokens)
- Error handling
### Key Dependencies
- **cobra**: CLI framework
- **viper**: Configuration management
- **stretchr/testify**: Testing assertions
## Adding a New Command
Follow these steps to add a new command:
### 1. Create Command Directory Structure
```bash
# Create directory for your command
mkdir -p cmd/myfeature
```
### 2. Create Parent Command
Create `cmd/myfeature/myfeature.go`:
```go
package myfeature
import "github.com/spf13/cobra"
// NewMyFeatureCommand creates the myfeature parent command
func NewMyFeatureCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "myfeature",
Aliases: []string{"mf"},
Short: "MyFeature related commands",
Long: `Manage MyFeature resources.`,
}
// Add subcommands
cmd.AddCommand(NewListCommand())
cmd.AddCommand(NewGetCommand())
// ... more subcommands
return cmd
}
```
### 3. Create Subcommand
Create `cmd/myfeature/list.go`:
```go
package myfeature
import (
"context"
"fmt"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/output"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewListCommand() *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List all myfeature resources",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
// Get API client
client, err := cli.GetAPIClient(cmd)
if err != nil {
return fmt.Errorf("failed to get API client: %w", err)
}
// Use service layer
svc := service.NewMyFeatureService(client)
items, err := svc.List(ctx)
if err != nil {
return fmt.Errorf("failed to list items: %w", err)
}
// Format output
format, _ := cmd.Flags().GetString("format")
showSensitive, _ := cmd.Flags().GetBool("show-sensitive")
formatter, err := output.NewFormatter(format, output.Options{
ShowSensitive: showSensitive,
})
if err != nil {
return err
}
return formatter.Format(items)
},
}
}
```
### 4. Create Service Layer
Create `internal/service/myfeature.go`:
```go
package service
import (
"context"
"github.com/coollabsio/coolify-cli/internal/api"
"github.com/coollabsio/coolify-cli/internal/models"
)
type MyFeatureService struct {
client *api.Client
}
func NewMyFeatureService(client *api.Client) *MyFeatureService {
return &MyFeatureService{client: client}
}
func (s *MyFeatureService) List(ctx context.Context) ([]models.MyFeature, error) {
var items []models.MyFeature
err := s.client.Get(ctx, "myfeature", &items)
return items, err
}
func (s *MyFeatureService) Get(ctx context.Context, uuid string) (*models.MyFeature, error) {
var item models.MyFeature
err := s.client.Get(ctx, "myfeature/"+uuid, &item)
return &item, err
}
func (s *MyFeatureService) Create(ctx context.Context, req models.MyFeatureCreateRequest) (*models.Response, error) {
var response models.Response
err := s.client.Post(ctx, "myfeature", req, &response)
return &response, err
}
func (s *MyFeatureService) Delete(ctx context.Context, uuid string) error {
return s.client.Delete(ctx, "myfeature/"+uuid)
}
```
### 5. Create Models
Create `internal/models/myfeature.go`:
```go
package models
type MyFeature struct {
ID int `json:"id" table:"-"` // Hidden from table output
UUID string `json:"uuid"` // Shown to users
Name string `json:"name"`
Description string `json:"description"`
Status string `json:"status"`
// Add more fields...
}
type MyFeatureCreateRequest struct {
Name string `json:"name"`
Description string `json:"description"`
}
```
**Important**: Always use `UUID` for user-facing identifiers, not database `ID`. Hide `ID` field from table output using `table:"-"` tag.
### 6. Register Command
Add your command to `cmd/root.go`:
```go
import (
// ... existing imports
"github.com/coollabsio/coolify-cli/cmd/myfeature"
)
func init() {
// ... existing code
rootCmd.AddCommand(myfeature.NewMyFeatureCommand())
}
```
### 7. Create Tests
Create `internal/service/myfeature_test.go`:
```go
package service
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/coollabsio/coolify-cli/internal/api"
"github.com/coollabsio/coolify-cli/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMyFeatureService_List(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/v1/myfeature", r.URL.Path)
assert.Equal(t, "GET", r.Method)
items := []models.MyFeature{
{UUID: "uuid-1", Name: "item-1"},
{UUID: "uuid-2", Name: "item-2"},
}
json.NewEncoder(w).Encode(items)
}))
defer server.Close()
client := api.NewClient(server.URL, "test-token")
svc := NewMyFeatureService(client)
items, err := svc.List(cmd.Context())
require.NoError(t, err)
assert.Len(t, items, 2)
assert.Equal(t, "uuid-1", items[0].UUID)
}
```
### 8. Update Documentation
- Add command documentation to `README.md`
- Include usage examples and flag descriptions
## Testing Requirements
**All code changes MUST include tests.** This is non-negotiable.
### Coverage Requirements
- **Minimum coverage**: 70% for all packages
- **New features**: 80%+ coverage required
- **Bug fixes**: Must include regression tests
- **Refactoring**: Must maintain or improve existing coverage
### Running Tests
```bash
# Run all tests
go test ./internal/...
# Run with coverage
go test ./internal/... -cover
# Run specific package
go test ./internal/service/... -v
# Run specific test
go test ./internal/service -run TestServerService_List -v
# Generate coverage report
go test ./internal/... -coverprofile=coverage.out
go tool cover -html=coverage.out
```
### Writing Tests
#### Use Table-Driven Tests
```go
func TestMyFunction(t *testing.T) {
tests := []struct {
name string
input string
want string
wantErr bool
}{
{
name: "successful case",
input: "test",
want: "expected",
wantErr: false,
},
{
name: "error case",
input: "",
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := MyFunction(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("MyFunction() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("MyFunction() = %v, want %v", got, tt.want)
}
})
}
}
```
#### Mock HTTP Requests
**IMPORTANT**: Never call real APIs in tests. Use `httptest.NewServer()`:
```go
func TestServiceMethod(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verify request
assert.Equal(t, "/api/v1/endpoint", r.URL.Path)
assert.Equal(t, "GET", r.Method)
// Return mock response
response := models.MyResponse{Data: "test"}
json.NewEncoder(w).Encode(response)
}))
defer server.Close()
client := api.NewClient(server.URL, "test-token")
// ... test your service
}
```
### Test Guidelines
- **Test naming**: `TestFunctionName_Scenario_ExpectedBehavior`
- **Use subtests**: `t.Run()` for related test cases
- **Use testify**: `require.NoError()` for must-pass assertions, `assert.Equal()` for comparisons
- **Mock HTTP**: Use `httptest.NewServer()` for all API tests
- **Test contexts**: Always pass `context.Background()` in tests
- **Test errors**: Verify error messages and types
## Code Style & Conventions
### Go Standards
- Follow standard Go idioms and conventions
- Use `gofmt` for code formatting
- Run `go vet` to catch common issues
- Prefer standard library over external dependencies
### Project Conventions
#### API Client Usage
```go
// Create client (usually done via cli.GetAPIClient())
client := api.NewClient(baseURL, token, api.WithDebug(true))
// GET request
var result MyStruct
err := client.Get(ctx, "endpoint", &result)
// POST request
err := client.Post(ctx, "endpoint", requestBody, &result)
// DELETE request
err := client.Delete(ctx, "endpoint")
// PATCH request
err := client.Patch(ctx, "endpoint", requestBody, &result)
```
#### Service Layer Pattern
```go
type MyService struct {
client *api.Client
}
func NewMyService(client *api.Client) *MyService {
return &MyService{client: client}
}
func (s *MyService) List(ctx context.Context) ([]models.Item, error) {
var items []models.Item
err := s.client.Get(ctx, "items", &items)
return items, err
}
```
#### Error Handling
```go
// Wrap errors with context
if err != nil {
return fmt.Errorf("failed to fetch data: %w", err)
}
// Check and handle specific error types
if apiErr, ok := err.(*api.Error); ok {
if apiErr.StatusCode == 404 {
return fmt.Errorf("resource not found")
}
}
```
#### Global Flags
All commands automatically inherit these global flags:
- `--format` (table|json|pretty) - Output format
- `--show-sensitive` - Show sensitive information
- `--debug` - Enable debug mode
- `--context` - Use specific context by name
- `--token` - Override context token
Access flags in commands:
```go
format, _ := cmd.Flags().GetString("format")
showSensitive, _ := cmd.Flags().GetBool("show-sensitive")
debug, _ := cmd.Flags().GetBool("debug")
```
## Submitting Changes
### Before Committing
```bash
# 1. Format code
go fmt ./...
# 2. Run tests
go test ./internal/...
# 3. Check coverage
go test ./internal/... -cover
# 4. Run vet
go vet ./...
```
### Commit Messages
Write clear, descriptive commit messages following conventional commits format:
```
<type>: <short summary>
<detailed description>
<footer>
```
Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`
Example:
```
feat: add server domains list command
- Implement GET /servers/{uuid}/domains endpoint
- Add server domains subcommand
- Include tests for domain listing
- Update README with new command documentation
```
### Pull Requests
1. **Fork** the repository
2. **Create a branch** from `v4.x`: `git checkout -b feature/my-feature v4.x`
3. **Make your changes** with tests
4. **Push** to your fork: `git push origin feature/my-feature`
5. **Open a pull request** against the `v4.x` branch
6. **Describe your changes** clearly in the PR description
7. **Link related issues** using "Fixes #123" or "Closes #123"
### PR Checklist
- [ ] Tests pass locally (`go test ./internal/...`)
- [ ] Code coverage meets requirements (70%+ minimum)
- [ ] Code is formatted (`go fmt ./...`)
- [ ] README.md updated (if adding new commands)
- [ ] CLAUDE.md updated (if changing architecture)
- [ ] Commit messages are descriptive
- [ ] PR description explains the changes
- [ ] All global flags are supported (format, show-sensitive, debug)
- [ ] Used UUIDs (not IDs) for resource identifiers
## Release Process (not for contributors :) )
Releases are automated using GoReleaser:
1. Tag a new version: `git tag v1.2.3`
2. Push the tag: `git push origin v1.2.3`
3. Create a GitHub release
4. GoReleaser builds binaries for all platforms automatically
## Getting Help
- **Discord**: https://coolify.io/discord
- **Issues**: [Open an issue](https://github.com/coollabsio/coolify-cli/issues) for bugs or feature requests
- **Architecture**: Read [ARCHITECTURE.md](ARCHITECTURE.md) for detailed design documentation
- **API Reference**: See the [OpenAPI specification](https://github.com/coollabsio/coolify/blob/v4.x/openapi.json)
- **Code Guidance**: See [CLAUDE.md](CLAUDE.md) for AI assistant guidance
## License
By contributing, you agree that your contributions will be licensed under the same license as the project.
---
Thank you for contributing to Coolify CLI! 🚀
+10 -28
View File
@@ -10,28 +10,7 @@ This guide explains the release process for the Coolify CLI.
## Release Process
### 1. Update Version Number
Edit `cmd/root.go` and update the `CliVersion` variable:
```go
var CliVersion = "1.x.x" // Change to your new version
```
**Version Format:** Use semantic versioning: `MAJOR.MINOR.PATCH` (e.g., `1.2.3`)
- **MAJOR**: Breaking changes
- **MINOR**: New features (backwards compatible)
- **PATCH**: Bug fixes (backwards compatible)
### 2. Commit and Push Version Change
```bash
git add cmd/root.go
git commit -m "chore: bump version to 1.x.x"
git push origin v4.x
```
### 3. Create a GitHub Release
### 1. Create a GitHub Release
1. Go to https://github.com/coollabsio/coolify-cli/releases/new
2. Click "Choose a tag" and create a new tag:
@@ -56,7 +35,7 @@ git push origin v4.x
```
5. Click "Publish release"
### 4. Automated Build Process
### 2. Automated Build Process
Once you publish the release:
@@ -65,12 +44,14 @@ Once you publish the release:
- **Linux**: amd64, arm64
- **macOS (Darwin)**: amd64, arm64
- **Windows**: amd64, arm64
3. Binaries are automatically uploaded to the release
4. The release becomes available at:
3. Goreleaser injects the version from the tag into the binaries
4. Binaries are automatically uploaded to the release
5. The release becomes available at:
- GitHub: `https://github.com/coollabsio/coolify-cli/releases/tag/v1.x.x`
- Install script: `curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash`
- `go install`: `go install github.com/coollabsio/coolify-cli/coolify@v1.x.x`
### 5. Verify the Release
### 3. Verify the Release
After the workflow completes (usually 2-5 minutes):
@@ -128,10 +109,11 @@ After creating a release:
The release process uses these configuration files:
- `.goreleaser.yml` - GoReleaser configuration (build matrix, archives, etc.)
- `.goreleaser.yml` - GoReleaser configuration (build matrix, archives, etc.) - points to `/coolify` as entry point
- `.github/workflows/release-cli.yml` - GitHub Actions workflow
- `scripts/install.sh` - User-facing install script
- `cmd/root.go` - Contains `CliVersion` variable (line 22)
- `internal/version/checker.go` - Contains `GetVersion()` function that returns the current version
- `coolify/main.go` - Binary entry point for `go install` support
## Notes
+82 -36
View File
@@ -2,13 +2,44 @@
## Installation
### Install script (recommended)
#### Linux/macOS
```bash
curl -fsSL https://raw.githubusercontent.com/coollabsio/coolify-cli/main/scripts/install.sh | bash
```
It will install the CLI in `/usr/local/bin/coolify` and the configuration file in `~/.config/coolify/config.json`
> If you are a windows or mac user, please test the installation script and let us know if it works for you.
#### Windows (PowerShell)
```powershell
irm https://raw.githubusercontent.com/coollabsio/coolify-cli/main/scripts/install.ps1 | iex
```
It will install the CLI in `%ProgramFiles%\Coolify\coolify.exe` and the configuration file in `%USERPROFILE%\.config\coolify\config.json`
For user installation (no admin rights required):
```powershell
$env:COOLIFY_USER_INSTALL=1; irm https://raw.githubusercontent.com/coollabsio/coolify-cli/main/scripts/install.ps1 | iex
```
For a specific version:
```powershell
$env:COOLIFY_VERSION='v1.0.0'; irm https://raw.githubusercontent.com/coollabsio/coolify-cli/main/scripts/install.ps1 | iex
```
### Using `go install`
```bash
go install github.com/coollabsio/coolify-cli/coolify@latest
```
This will install the `coolify` binary in your `$GOPATH/bin` directory (usually `~/go/bin`). Make sure this directory is in your `$PATH`.
### Using the install script
## Getting Started
1. Get a `<token>` from your Coolify dashboard (Cloud or self-hosted) at `/security/api-tokens`
@@ -37,6 +68,10 @@ You can change the default context with `coolify context use <context_name>` or
### Configuration
- `coolify config` - Show configuration file location
### Shell Completion
- `coolify completion <shell>` - Generate shell completion script
- Supported shells: `bash`, `zsh`, `fish`, `powershell`
### Context Management
- `coolify context list` - List all configured contexts
- `coolify context add <context_name> <url> <token>` - Add a new context
@@ -51,25 +86,23 @@ You can change the default context with `coolify context use <context_name>` or
- `--url <new_url>` - Change the context URL
- `--token <new_token>` - Change the context token
- `coolify context use <context_name>` - Switch to a different context (set as default)
- `coolify context verify` - Verify current context connection and authentication
- `coolify context version` - Get the Coolify API version of the current context
### Servers
- `coolify servers list` - List all servers
- `coolify servers get <uuid>` - Get a server by UUID
Commands can use `server` or `servers` interchangeably.
- `coolify server list` - List all servers
- `coolify server get <uuid>` - Get a server by UUID
- `--resources` - Get the resources and their status of a server
<<<<<<< HEAD
- `coolify servers add <server_name> <ip_address> <private_key_uuid>` - Add a new server
- `--port <port>` - SSH port (default: 22)
- `--user <user>` - SSH user (default: root)
=======
- `coolify servers add <name> <ip> <private_key_uuid>` - Add a new server
- `coolify server add <name> <ip> <private_key_uuid>` - Add a new server
- `-p, --port <port>` - SSH port (default: 22)
- `-u, --user <user>` - SSH user (default: root)
>>>>>>> origin/v4.x
- `--validate` - Validate server immediately after adding
- `coolify servers remove <uuid>` - Remove a server
- `coolify servers validate <uuid>` - Validate a server connection
- `coolify servers domains <uuid>` - Get server domains by UUID
- `coolify server remove <uuid>` - Remove a server
- `coolify server validate <uuid>` - Validate a server connection
- `coolify server domains <uuid>` - Get server domains by UUID
### Projects
- `coolify projects list` - List all projects
@@ -120,6 +153,19 @@ You can change the default context with `coolify context use <context_name>` or
- `coolify app env delete <app_uuid> <env_uuid>` - Delete an environment variable
- `coolify app env sync <app_uuid>` - Sync environment variables from a .env file
- `--file <path>` - Path to .env file (required)
- `--build-time` - Make all variables available at build time
- `--preview` - Make all variables available in preview deployments
- `--is-literal` - Treat all values as literal (don't interpolate variables)
- **Behavior**: Updates existing variables, creates missing ones. Does NOT delete variables not in the file.
#### Application Deployments
- `coolify app deployments list <app-uuid>` - List all deployments for an application
- `coolify app deployments logs <app-uuid> [deployment-uuid]` - Get deployment logs (formatted as human-readable text)
- If only `app-uuid` is provided: retrieves logs from the **latest/most recent deployment only**
- If `deployment-uuid` is also provided: retrieves logs for that **specific deployment**
- `-n, --lines <n>` - Number of log lines to display (default: 0 = all lines)
- `-f, --follow` - Follow log output in real-time (like tail -f)
- `--debuglogs` - Show debug logs (includes hidden commands and internal operations)
### Databases
- `coolify database list` - List all databases
@@ -190,18 +236,16 @@ You can change the default context with `coolify context use <context_name>` or
- `coolify service env delete <service_uuid> <env_uuid>` - Delete an environment variable
- `coolify service env sync <service_uuid>` - Sync environment variables from a .env file
- `--file <path>` - Path to .env file (required)
- `--build-time` - Make all variables available at build time
- `--preview` - Make all variables available in preview deployments
- `--is-literal` - Treat all values as literal (don't interpolate variables)
- **Behavior**: Updates existing variables, creates missing ones. Does NOT delete variables not in the file.
### Deployments
- `coolify deploy uuid <uuid>` - Deploy a resource by UUID
<<<<<<< HEAD
- `--force` - Force deployment
- `coolify deploy name <resource_name>` - Deploy a resource by name
- `--force` - Force deployment
=======
- `-f, --force` - Force deployment
- `coolify deploy name <name>` - Deploy a resource by name
- `-f, --force` - Force deployment
>>>>>>> origin/v4.x
- `coolify deploy batch <name1,name2,...>` - Deploy multiple resources at once
- `-f, --force` - Force all deployments
- `coolify deploy list` - List all deployments
@@ -239,20 +283,19 @@ You can change the default context with `coolify context use <context_name>` or
- `coolify team members list [team_id]` - List team members
### Private Keys
- `coolify privatekeys list` - List all private keys
- `coolify privatekeys create <key_name> <private-key>` - Create a new private key
- Use `@filename` to read from file: `coolify privatekeys create mykey @~/.ssh/id_rsa`
- `coolify privatekeys delete <uuid>` - Delete a private key
Commands can use `private-key`, `private-keys`, `key`, or `keys` interchangeably.
- `coolify private-key list` - List all private keys
- `coolify private-key add <key_name> <private-key>` - Add a new private key
- Use `@filename` to read from file: `coolify private-key add mykey @~/.ssh/id_rsa`
- `coolify private-key remove <uuid>` - Remove a private key
## Global Flags
All commands support these global flags:
<<<<<<< HEAD
- `--context <name>` - Use a specific context instead of default
=======
- `--instance <name>` - Use a specific instance profile instead of default
>>>>>>> origin/v4.x
- `--host <fqdn>` - Override the Coolify instance hostname
- `--token <token>` - Override the authentication token
- `--format <format>` - Output format: `table` (default), `json`, or `pretty`
@@ -302,7 +345,10 @@ coolify app logs <uuid>
# Environment variables
coolify app env list <uuid>
coolify app env create <uuid> --key API_KEY --value secret123
# Sync from .env file (updates existing, creates new, keeps others unchanged)
coolify app env sync <uuid> --file .env
coolify app env sync <uuid> --file .env.production --build-time --preview
```
### Database Management
@@ -417,13 +463,13 @@ coolify team members list
```bash
# List servers in production
coolify --context=prod servers list
coolify --context=prod server list
# Add a server with validation
coolify servers add myserver 192.168.1.100 <key-uuid> --validate
coolify server add myserver 192.168.1.100 <key-uuid> --validate
# Get server details with resources
coolify servers get <uuid> --resources
coolify server get <uuid> --resources
```
## Output Formats
@@ -432,13 +478,13 @@ The CLI supports three output formats:
```bash
# Table format (default, human-readable)
coolify servers list
coolify server list
# JSON format (for scripts)
coolify servers list --format=json
coolify server list --format=json
# Pretty JSON (for debugging)
coolify servers list --format=pretty
coolify server list --format=pretty
```
## Architecture
@@ -453,7 +499,7 @@ This CLI follows a clean architecture with:
```bash
# Build
go build -o coolify .
go build -o coolify ./coolify
# Run tests
go test ./...
@@ -462,12 +508,12 @@ go test ./...
go test -cover ./...
# Install locally
go install
go install ./coolify
```
## Contributing
Contributions are welcome! Please check the [restructure documentation](RESTRUCTURE_PLAN.md) for architecture guidelines.
Contributions are welcome!
## License
+1
View File
@@ -24,6 +24,7 @@ func NewAppCommand() *cobra.Command {
cmd.AddCommand(NewStopCommand())
cmd.AddCommand(NewRestartCommand())
cmd.AddCommand(NewLogsCommand())
cmd.AddCommand(NewDeploymentsCommand())
// Add env subcommand with its children
envCmd := &cobra.Command{
+8 -4
View File
@@ -1,12 +1,12 @@
package application
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewDeleteCommand() *cobra.Command {
@@ -16,7 +16,7 @@ func NewDeleteCommand() *cobra.Command {
Long: `Delete an application. This action cannot be undone.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
force, _ := cmd.Flags().GetBool("force")
@@ -24,7 +24,11 @@ func NewDeleteCommand() *cobra.Command {
if !force {
var response string
fmt.Printf("Are you sure you want to delete application %s? This cannot be undone. (yes/no): ", uuid)
fmt.Scanln(&response)
_, err := fmt.Scanln(&response)
if err != nil {
return fmt.Errorf("failed to read input: %w", err)
}
if response != "yes" && response != "y" {
fmt.Println("Delete cancelled.")
+182
View File
@@ -0,0 +1,182 @@
package application
import (
"context"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
"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 NewDeploymentsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "deployments",
Short: "Deployment related commands for an application",
Long: `Manage deployments for a specific application. List deployments or view deployment logs.`,
}
cmd.AddCommand(NewListDeploymentsCommand())
cmd.AddCommand(NewLogsDeploymentsCommand())
return cmd
}
func NewListDeploymentsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "list <app-uuid>",
Short: "List all deployments for an application",
Long: `Retrieve a list of all deployments for a specific application.`,
Args: cli.ExactArgs(1, "<app-uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
appUUID := args[0]
client, err := cli.GetAPIClient(cmd)
if err != nil {
return fmt.Errorf("failed to get API client: %w", err)
}
deploySvc := service.NewDeploymentService(client)
deployments, err := deploySvc.ListByApplication(ctx, appUUID)
if err != nil {
return fmt.Errorf("failed to list deployments: %w", err)
}
format, _ := cmd.Flags().GetString("format")
formatter, err := output.NewFormatter(format, output.Options{})
if err != nil {
return err
}
return formatter.Format(deployments)
},
}
return cmd
}
func NewLogsDeploymentsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "logs <app-uuid> [deployment-uuid]",
Short: "Get deployment logs for an application",
Long: `Retrieve deployment logs for a specific application or deployment.
If only app-uuid is provided, retrieves logs from the latest deployment.
If deployment-uuid is also provided, retrieves logs for that specific deployment.
Use --follow to continuously stream new logs.`,
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
appUUID := args[0]
var deploymentUUID string
if len(args) == 2 {
deploymentUUID = args[1]
}
client, err := cli.GetAPIClient(cmd)
if err != nil {
return fmt.Errorf("failed to get API client: %w", err)
}
lines, _ := cmd.Flags().GetInt("lines")
follow, _ := cmd.Flags().GetBool("follow")
debugLogs, _ := cmd.Flags().GetBool("debuglogs")
format, _ := cmd.Flags().GetString("format")
deploySvc := service.NewDeploymentService(client)
// Function to get logs based on whether we have a deployment UUID
// Returns raw or formatted based on format flag
getLogs := func() (string, error) {
if deploymentUUID != "" {
return deploySvc.GetLogsByDeploymentWithFormat(ctx, deploymentUUID, debugLogs, format)
}
// Get logs from the latest deployment
// Use take=1 internally to efficiently fetch only the most recent deployment
return deploySvc.GetLogsByApplicationWithFormat(ctx, appUUID, 1, debugLogs, format)
}
if !follow {
logs, err := getLogs()
if err != nil {
return fmt.Errorf("failed to get deployment logs: %w", err)
}
// Apply line limit if specified (only for text output)
if lines > 0 && format == "table" {
logs = limitLogLines(logs, lines)
}
fmt.Print(logs)
return nil
}
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
lastLogs := ""
logs, err := getLogs()
if err != nil {
return fmt.Errorf("failed to get deployment logs: %w", err)
}
fmt.Print(logs)
lastLogs = logs
for {
select {
case <-sigChan:
fmt.Println("\nStopping log follow...")
return nil
case <-ticker.C:
logs, err := getLogs()
if err != nil {
continue
}
if logs != lastLogs {
if len(logs) > len(lastLogs) && strings.HasPrefix(logs, lastLogs) {
fmt.Print(logs[len(lastLogs):])
} else {
fmt.Print(logs)
}
lastLogs = logs
}
}
}
},
}
cmd.Flags().IntP("lines", "n", 0, "Number of log lines to display (0 = all)")
cmd.Flags().BoolP("follow", "f", false, "Follow log output (like tail -f)")
cmd.Flags().Bool("debuglogs", false, "Show debug logs (includes hidden commands and internal operations)")
return cmd
}
// limitLogLines limits the output to the last N lines
func limitLogLines(logs string, n int) string {
if n <= 0 {
return logs
}
// Trim trailing newline to avoid empty element at the end
logs = strings.TrimRight(logs, "\n")
lines := strings.Split(logs, "\n")
// If we have fewer lines than requested, return all
if len(lines) <= n {
return logs + "\n"
}
// Get the last N lines
lastLines := lines[len(lines)-n:]
return strings.Join(lastLines, "\n") + "\n"
}
+12 -7
View File
@@ -1,13 +1,13 @@
package env
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
func NewCreateEnvCommand() *cobra.Command {
@@ -15,10 +15,10 @@ func NewCreateEnvCommand() *cobra.Command {
Use: "create <app_uuid>",
Short: "Create an environment variable for an application",
Long: `Create a new environment variable for a specific application. Use --key and --value flags to specify the variable.`,
Args: cli.ExactArgs(1, "<uuid>"),
Args: cli.ExactArgs(1, "<app_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
uuid := args[0]
ctx := cmd.Context()
appUUID := args[0]
client, err := cli.GetAPIClient(cmd)
if err != nil {
@@ -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,9 +57,12 @@ 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, uuid, req)
env, err := appSvc.CreateEnv(ctx, appUUID, req)
if err != nil {
return fmt.Errorf("failed to create environment variable: %w", err)
}
@@ -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
}
+8 -4
View File
@@ -1,12 +1,12 @@
package env
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewDeleteEnvCommand() *cobra.Command {
@@ -16,7 +16,7 @@ func NewDeleteEnvCommand() *cobra.Command {
Long: `Delete an environment variable from an application. First UUID is the application, second is the specific environment variable to delete.`,
Args: cli.ExactArgs(2, "<uuid1> <uuid2>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
appUUID := args[0]
envUUID := args[1]
@@ -31,7 +31,11 @@ func NewDeleteEnvCommand() *cobra.Command {
if !force {
var response string
fmt.Printf("Are you sure you want to delete this environment variable? (yes/no): ")
fmt.Scanln(&response)
_, 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.")
+11 -7
View File
@@ -1,25 +1,25 @@
package env
import (
"context"
"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"
"github.com/spf13/cobra"
)
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.`,
Args: cli.ExactArgs(2, "<uuid1> <uuid2>"),
Args: cli.ExactArgs(2, "<app_uuid> <env_uuid_or_key>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
appUUID := args[0]
envUUID := args[1]
envUUIDOrKey := args[1]
client, err := cli.GetAPIClient(cmd)
if err != nil {
@@ -27,7 +27,9 @@ func NewGetEnvCommand() *cobra.Command {
}
appSvc := service.NewApplicationService(client)
env, err := appSvc.GetEnv(ctx, appUUID, envUUID)
// 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
}
+35 -5
View File
@@ -1,23 +1,25 @@
package env
import (
"context"
"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"
"github.com/spf13/cobra"
)
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 := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -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
}
+9 -4
View File
@@ -1,15 +1,15 @@
package env
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/models"
"github.com/coollabsio/coolify-cli/internal/parser"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewSyncEnvCommand() *cobra.Command {
@@ -24,7 +24,7 @@ func NewSyncEnvCommand() *cobra.Command {
Example: coolify app env sync abc123 --file .env.production`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -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
}
+10 -5
View File
@@ -1,13 +1,13 @@
package env
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
func NewUpdateEnvCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewUpdateEnvCommand() *cobra.Command {
Long: `Update an existing environment variable. First UUID is the application, second is the specific environment variable to update.`,
Args: cli.ExactArgs(2, "<uuid1> <uuid2>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
appUUID := args[0]
envUUID := args[1]
@@ -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
}
+3 -3
View File
@@ -1,13 +1,13 @@
package application
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewGetCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewGetCommand() *cobra.Command {
Long: `Retrieve detailed information about a specific application.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+4 -4
View File
@@ -1,14 +1,14 @@
package application
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewListCommand() *cobra.Command {
@@ -16,8 +16,8 @@ func NewListCommand() *cobra.Command {
Use: "list",
Short: "List all applications",
Long: `List all applications in Coolify.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
+3 -3
View File
@@ -1,7 +1,6 @@
package application
import (
"context"
"fmt"
"os"
"os/signal"
@@ -9,9 +8,10 @@ import (
"syscall"
"time"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewLogsCommand() *cobra.Command {
@@ -21,7 +21,7 @@ func NewLogsCommand() *cobra.Command {
Long: `Retrieve logs for an application. Use --follow to continuously stream new logs.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,12 +1,12 @@
package application
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewRestartCommand() *cobra.Command {
@@ -16,7 +16,7 @@ func NewRestartCommand() *cobra.Command {
Long: `Restart a running application.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,12 +1,12 @@
package application
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewStartCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewStartCommand() *cobra.Command {
Long: `Start an application (initiates a deployment).`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,12 +1,12 @@
package application
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewStopCommand() *cobra.Command {
@@ -16,7 +16,7 @@ func NewStopCommand() *cobra.Command {
Long: `Stop a running application.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,14 +1,14 @@
package application
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewUpdateCommand() *cobra.Command {
@@ -18,7 +18,7 @@ func NewUpdateCommand() *cobra.Command {
Long: `Update configuration for a specific application. Only specified fields will be updated.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+2 -1
View File
@@ -4,8 +4,9 @@ import (
"fmt"
"os"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
)
func NewCompletionsCommand() *cobra.Command {
+3 -2
View File
@@ -3,8 +3,9 @@ package config
import (
"fmt"
"github.com/coollabsio/coolify-cli/internal/config"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/config"
)
// NewConfigCommand creates the config command
@@ -13,7 +14,7 @@ func NewConfigCommand() *cobra.Command {
Use: "config",
Short: "Show configuration file location",
Long: "Display the path to the Coolify CLI configuration file",
Run: func(cmd *cobra.Command, args []string) {
Run: func(_ *cobra.Command, _ []string) {
fmt.Println(config.Path())
},
}
+11 -7
View File
@@ -3,10 +3,11 @@ package context
import (
"fmt"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/config"
)
// NewAddCommand creates the add command
@@ -16,7 +17,7 @@ func NewAddCommand() *cobra.Command {
Example: `context add myserver https://coolify.example.com your-api-token`,
Args: cli.ExactArgs(3, "<context_name> <url> <token>"),
Short: "Add a new context",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
host := args[1]
token := args[2]
@@ -44,12 +45,14 @@ func NewAddCommand() *cobra.Command {
fmt.Printf("%s already exists. Force overwriting.\n", name)
}
viper.Set("instances", instances)
viper.WriteConfig()
return
if err := viper.WriteConfig(); err != nil {
return fmt.Errorf("failed to write config: %w", err)
}
return nil
}
fmt.Printf("%s already exists.\n", name)
fmt.Println("\nNote: Use --force to force overwrite.")
return
return nil
}
}
@@ -77,8 +80,9 @@ func NewAddCommand() *cobra.Command {
viper.Set("instances", instances)
if err := viper.WriteConfig(); err != nil {
fmt.Printf("failed to write config: %v\n", err)
return fmt.Errorf("failed to write config: %w", err)
}
return nil
},
}
+12 -7
View File
@@ -2,11 +2,12 @@ package context
import (
"fmt"
"slices"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"slices"
"github.com/coollabsio/coolify-cli/internal/cli"
)
// NewDeleteCommand creates the delete command
@@ -17,7 +18,7 @@ func NewDeleteCommand() *cobra.Command {
Args: cli.ExactArgs(1, "<context_name>"),
Short: "Delete a context",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(_ *cobra.Command, args []string) error {
Name := args[0]
instances := viper.Get("instances").([]interface{})
for i, instance := range instances {
@@ -25,13 +26,17 @@ func NewDeleteCommand() *cobra.Command {
if instanceMap["name"] == Name {
instances = slices.Delete(instances, i, i+1)
viper.Set("instances", instances)
viper.WriteConfig()
if err := viper.WriteConfig(); err != nil {
return fmt.Errorf("failed to write config: %w", err)
}
if instanceMap["default"] == true {
if len(instances) > 0 {
instances[0].(map[string]interface{})["default"] = true
viper.Set("instances", instances)
viper.WriteConfig()
if err := viper.WriteConfig(); err != nil {
return fmt.Errorf("failed to write config: %w", err)
}
newDefaultName := instances[0].(map[string]interface{})["name"]
fmt.Printf("Context '%s' deleted. '%s' is now the default context.\n", Name, newDefaultName)
} else {
@@ -40,10 +45,10 @@ func NewDeleteCommand() *cobra.Command {
} else {
fmt.Printf("Context '%s' deleted.\n", Name)
}
return
return nil
}
}
fmt.Printf("Context '%s' not found.\n", Name)
return fmt.Errorf("context '%s' not found", Name)
},
}
}
+3 -2
View File
@@ -3,11 +3,12 @@ package context
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/config"
"github.com/coollabsio/coolify-cli/internal/output"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// NewGetCommand creates the get command
+4 -3
View File
@@ -1,10 +1,11 @@
package context
import (
"github.com/coollabsio/coolify-cli/internal/config"
"github.com/coollabsio/coolify-cli/internal/output"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/coollabsio/coolify-cli/internal/config"
"github.com/coollabsio/coolify-cli/internal/output"
)
// NewListCommand creates the list command
@@ -12,7 +13,7 @@ func NewListCommand() *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List all configured contexts",
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, _ []string) error {
// Get instances from viper (returns []interface{})
instancesRaw := viper.Get("instances")
if instancesRaw == nil {
+23 -11
View File
@@ -3,9 +3,10 @@ package context
import (
"fmt"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/coollabsio/coolify-cli/internal/cli"
)
// NewSetTokenCommand creates the set-token command
@@ -18,13 +19,20 @@ func NewSetDefaultCommand() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
instances := viper.Get("instances").([]interface{})
raw := viper.Get("instances")
instances, ok := raw.([]interface{})
if !ok {
return fmt.Errorf("invalid instances configuration")
}
// Check if instance exists
var found bool
for _, instance := range instances {
instanceMap := instance.(map[string]interface{})
if instanceMap["name"] == name {
instanceMap, ok := instance.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid instance configuration")
}
if val, ok := instanceMap["name"].(string); ok && val == name {
found = true
instanceMap["default"] = true
}
@@ -32,13 +40,17 @@ func NewSetDefaultCommand() *cobra.Command {
if !found {
return fmt.Errorf("Context '%s' not found", name)
} else {
// Only unset other defaults if we found the target instance
for _, instance := range instances {
instanceMap := instance.(map[string]interface{})
if instanceMap["name"] != name {
instanceMap["default"] = false
}
}
// Only unset other defaults if we found the target instance
for _, instance := range instances {
instanceMap, ok := instance.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid instance configuration")
}
if val, ok := instanceMap["name"].(string); ok && val != name {
instanceMap["default"] = false
}
}
+8 -5
View File
@@ -3,9 +3,10 @@ package context
import (
"fmt"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/coollabsio/coolify-cli/internal/cli"
)
// NewSetTokenCommand creates the set-token command
@@ -15,7 +16,7 @@ func NewSetTokenCommand() *cobra.Command {
Example: `context set-token myserver your-new-api-token`,
Args: cli.ExactArgs(2, "<context_name> <token>"),
Short: "Update the API token for a context",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(_ *cobra.Command, args []string) error {
name := args[0]
token := args[1]
var found interface{}
@@ -27,8 +28,7 @@ func NewSetTokenCommand() *cobra.Command {
}
}
if found == nil {
fmt.Printf("Context '%s' not found.\n", name)
return
return fmt.Errorf("context '%s' not found", name)
}
instances := viper.Get("instances").([]interface{})
for _, instance := range instances {
@@ -38,8 +38,11 @@ func NewSetTokenCommand() *cobra.Command {
}
}
viper.Set("instances", instances)
viper.WriteConfig()
if err := viper.WriteConfig(); err != nil {
return fmt.Errorf("failed to update token for context '%s': %w", name, err)
}
fmt.Printf("Token updated for context '%s'.\n", name)
return nil
},
}
}
+9 -13
View File
@@ -3,9 +3,10 @@ package context
import (
"fmt"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/coollabsio/coolify-cli/internal/cli"
)
// NewUpdateCommand creates the update command
@@ -15,7 +16,7 @@ func NewUpdateCommand() *cobra.Command {
Example: `context update myserver --name newname --url https://new.coolify.com --token newtoken`,
Args: cli.ExactArgs(1, "<context_name>"),
Short: "Update a context's properties (name, URL, token)",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
oldName := args[0]
instances := viper.Get("instances").([]interface{})
@@ -26,9 +27,7 @@ func NewUpdateCommand() *cobra.Command {
// Check if at least one flag is provided
if newName == "" && newURL == "" && newToken == "" {
fmt.Println("Error: At least one of --name, --url, or --token must be provided")
fmt.Println("\nUsage: coolify context update <context_name> [--name <new_name>] [--url <new_url>] [--token <new_token>]")
return
return fmt.Errorf("at least one of --name, --url, or --token must be provided")
}
// Find the context
@@ -44,8 +43,7 @@ func NewUpdateCommand() *cobra.Command {
}
if !found {
fmt.Printf("Context '%s' not found.\n", oldName)
return
return fmt.Errorf("context '%s' not found", oldName)
}
// If renaming, check if new name already exists
@@ -53,8 +51,7 @@ func NewUpdateCommand() *cobra.Command {
for _, instance := range instances {
instanceMap := instance.(map[string]interface{})
if instanceMap["name"] == newName {
fmt.Printf("Error: Context with name '%s' already exists.\n", newName)
return
return fmt.Errorf("context with name '%s' already exists", newName)
}
}
contextToUpdate["name"] = newName
@@ -72,10 +69,8 @@ func NewUpdateCommand() *cobra.Command {
// Save changes
viper.Set("instances", instances)
err := viper.WriteConfig()
if err != nil {
fmt.Printf("Error saving config: %v\n", err)
return
if err := viper.WriteConfig(); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
// Use the new name if renamed, otherwise use old name
@@ -84,6 +79,7 @@ func NewUpdateCommand() *cobra.Command {
finalName = newName
}
fmt.Printf("Context '%s' updated successfully.\n", finalName)
return nil
},
}
+19 -7
View File
@@ -3,9 +3,10 @@ package context
import (
"fmt"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/coollabsio/coolify-cli/internal/cli"
)
// NewUseCommand creates the use command
@@ -15,15 +16,22 @@ func NewUseCommand() *cobra.Command {
Example: `context use myserver`,
Args: cli.ExactArgs(1, "<context_name>"),
Short: "Switch to a different context (set as default)",
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
name := args[0]
instances := viper.Get("instances").([]interface{})
raw := viper.Get("instances")
instances, ok := raw.([]interface{})
if !ok {
return fmt.Errorf("invalid instances configuration")
}
// Check if instance exists
var found bool
for _, instance := range instances {
instanceMap := instance.(map[string]interface{})
if instanceMap["name"] == name {
instanceMap, ok := instance.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid instance configuration")
}
if val, ok := instanceMap["name"].(string); ok && val == name {
found = true
break
}
@@ -35,8 +43,12 @@ func NewUseCommand() *cobra.Command {
// Update default
for _, instance := range instances {
instanceMap := instance.(map[string]interface{})
if instanceMap["name"] == name {
instanceMap, ok := instance.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid instance configuration")
}
if val, ok := instanceMap["name"].(string); ok && val == name {
instanceMap["default"] = true
} else {
delete(instanceMap, "default")
+4 -4
View File
@@ -1,11 +1,11 @@
package context
import (
"context"
"fmt"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
)
// NewVerifyCommand creates the verify command for contexts
@@ -15,8 +15,8 @@ func NewVerifyCommand() *cobra.Command {
Short: "Verify current context connection and authentication",
Long: `Verify that the current context is properly configured by testing the connection
to the Coolify instance and validating the API token.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
// Get API client - this will use the current default context
client, err := cli.GetAPIClient(cmd)
+9 -8
View File
@@ -7,9 +7,10 @@ import (
"net/http/httptest"
"testing"
"github.com/coollabsio/coolify-cli/internal/api"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coollabsio/coolify-cli/internal/api"
)
// TestVerifyCommand_APIIntegration tests the verify logic using the API client directly
@@ -21,7 +22,7 @@ func TestVerifyCommand_APIIntegration(t *testing.T) {
assert.Equal(t, "/api/v1/version", r.URL.Path)
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"))
w.WriteHeader(http.StatusOK)
w.Write([]byte("4.0.0-beta.383"))
_, _ = w.Write([]byte("4.0.0-beta.383"))
}))
defer server.Close()
@@ -36,9 +37,9 @@ func TestVerifyCommand_APIIntegration(t *testing.T) {
t.Run("unauthorized - invalid token", func(t *testing.T) {
// Create a test HTTP server that returns 401
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{
_ = json.NewEncoder(w).Encode(map[string]string{
"message": "Invalid token",
})
}))
@@ -55,9 +56,9 @@ func TestVerifyCommand_APIIntegration(t *testing.T) {
t.Run("server error", func(t *testing.T) {
// Create a test HTTP server that returns 500
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
_ = json.NewEncoder(w).Encode(map[string]string{
"error": "Internal server error",
})
}))
@@ -76,9 +77,9 @@ func TestVerifyCommand_APIIntegration(t *testing.T) {
t.Run("not found", func(t *testing.T) {
// Create a test HTTP server that returns 404
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{
_ = json.NewEncoder(w).Encode(map[string]string{
"message": "Endpoint not found",
})
}))
+4 -4
View File
@@ -1,11 +1,11 @@
package context
import (
"context"
"fmt"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
)
// NewVersionCommand creates the version command for contexts
@@ -13,8 +13,8 @@ func NewVersionCommand() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Get current context's Coolify version",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
// Get API client
client, err := cli.GetAPIClient(cmd)
+8 -3
View File
@@ -1,14 +1,14 @@
package backup
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewCreateCommand creates a new database
@@ -21,7 +21,7 @@ func NewCreateCommand() *cobra.Command {
Example: coolify database backup create abc123 --frequency "0 0 * * *" --enabled`,
Args: cli.ExactArgs(1, "<database_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
dbUUID := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -29,6 +29,11 @@ Example: coolify database backup create abc123 --frequency "0 0 * * *" --enabled
return fmt.Errorf("failed to get API client: %w", err)
}
// Check minimum version requirement
if err := cli.CheckMinimumVersion(ctx, client, "4.0.0-beta.436"); err != nil {
return err
}
req := &models.DatabaseBackupCreateRequest{}
// Apply flags if provided
+3 -3
View File
@@ -2,14 +2,14 @@ package backup
import (
"bufio"
"context"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewDeleteExecutionCommand lists all databases
@@ -20,7 +20,7 @@ func NewDeleteExecutionCommand() *cobra.Command {
Long: `Delete a specific backup execution and optionally from S3. First UUID is the database, second is the backup configuration, third is the specific execution.`,
Args: cli.ExactArgs(3, "<database_uuid> <backup_uuid> <execution_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
dbUUID := args[0]
backupUUID := args[1]
executionUUID := args[2]
+3 -3
View File
@@ -2,14 +2,14 @@ package backup
import (
"bufio"
"context"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewDeleteCommand deletes a database
@@ -20,7 +20,7 @@ func NewDeleteCommand() *cobra.Command {
Long: `Delete a backup configuration and optionally all its executions from S3. First UUID is the database, second is the specific backup configuration.`,
Args: cli.ExactArgs(2, "<database_uuid> <backup_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
dbUUID := args[0]
backupUUID := args[1]
+3 -3
View File
@@ -1,13 +1,13 @@
package backup
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewExecutionCommand lists all databases
@@ -18,7 +18,7 @@ func NewExecutionCommand() *cobra.Command {
Long: `List all executions for a backup configuration. First UUID is the database, second is the specific backup configuration.`,
Args: cli.ExactArgs(2, "<database_uuid> <backup_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
dbUUID := args[0]
backupUUID := args[1]
+3 -3
View File
@@ -1,13 +1,13 @@
package backup
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand lists all databases
@@ -18,7 +18,7 @@ func NewListCommand() *cobra.Command {
Long: `List all backup configurations for a specific database.`,
Args: cli.ExactArgs(1, "<database_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
dbUUID := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,13 +1,13 @@
package backup
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
// NewTriggerCommand triggers a database backup
@@ -18,7 +18,7 @@ func NewTriggerCommand() *cobra.Command {
Long: `Trigger an immediate backup for a specific backup configuration. First UUID is the database, second is the specific backup configuration to trigger.`,
Args: cli.ExactArgs(2, "<database_uuid> <backup_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
dbUUID := args[0]
backupUUID := args[1]
+3 -3
View File
@@ -1,13 +1,13 @@
package backup
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
// NewUpdateCommand updates a database
@@ -18,7 +18,7 @@ func NewUpdateCommand() *cobra.Command {
Long: `Update a backup configuration settings (frequency, retention, S3, etc.). First UUID is the database, second is the specific backup configuration.`,
Args: cli.ExactArgs(2, "<database_uuid> <backup_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
dbUUID := args[0]
backupUUID := args[1]
+4 -4
View File
@@ -1,15 +1,15 @@
package database
import (
"context"
"fmt"
"strings"
"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"
"github.com/spf13/cobra"
)
func NewCreateCommand() *cobra.Command {
@@ -25,7 +25,7 @@ Examples:
coolify databases create mysql --server-uuid=<uuid> --project-uuid=<uuid> --environment-name=production --name="My MySQL"`,
Args: cli.ExactArgs(1, "<type>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
dbType := args[0]
validTypes := []string{"postgresql", "mysql", "mariadb", "mongodb", "redis", "keydb", "clickhouse", "dragonfly"}
isValid := false
@@ -116,7 +116,7 @@ Examples:
}
if cmd.Flags().Changed("postgres-db") {
db, _ := cmd.Flags().GetString("postgres-db")
req.PostgresDb = &db
req.PostgresDB = &db
}
}
+3 -3
View File
@@ -2,14 +2,14 @@ package database
import (
"bufio"
"context"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewDeleteCommand deletes a database
@@ -20,7 +20,7 @@ func NewDeleteCommand() *cobra.Command {
Long: `Delete a database and optionally clean up its configurations, volumes, and networks.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
force, _ := cmd.Flags().GetBool("force")
+3 -3
View File
@@ -1,13 +1,13 @@
package database
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewGetCommand gets database details
@@ -18,7 +18,7 @@ func NewGetCommand() *cobra.Command {
Long: `Get detailed information about a specific database by UUID.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+4 -4
View File
@@ -1,13 +1,13 @@
package database
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand lists all databases
@@ -16,8 +16,8 @@ func NewListCommand() *cobra.Command {
Use: "list",
Short: "List all databases",
Long: `List all databases in Coolify.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
+3 -3
View File
@@ -1,12 +1,12 @@
package database
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewRestartCommand restarts a database
@@ -17,7 +17,7 @@ func NewRestartCommand() *cobra.Command {
Long: `Restart a database by UUID.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,12 +1,12 @@
package database
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewStartCommand starts a database
@@ -17,7 +17,7 @@ func NewStartCommand() *cobra.Command {
Long: `Start a database by UUID.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,12 +1,12 @@
package database
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewStopCommand stops a database
@@ -17,7 +17,7 @@ func NewStopCommand() *cobra.Command {
Long: `Stop a database by UUID.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,13 +1,13 @@
package database
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
// NewUpdateCommand updates a database
@@ -18,7 +18,7 @@ func NewUpdateCommand() *cobra.Command {
Long: `Update a database's configuration by UUID.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
req := &models.DatabaseUpdateRequest{}
+3 -3
View File
@@ -1,13 +1,13 @@
package deployment
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewBatchCommand deploys multiple resources by name
@@ -20,7 +20,7 @@ Provide resource names as comma-separated values.
Example: coolify deploy batch app1,app2,app3`,
Args: cli.ExactArgs(1, "<names>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
namesStr := args[0]
client, err := cli.GetAPIClient(cmd)
+21 -7
View File
@@ -1,13 +1,13 @@
package deployment
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewCancelCommand cancels a deployment
@@ -18,7 +18,7 @@ func NewCancelCommand() *cobra.Command {
Long: `Cancel an in-progress deployment. This will stop the deployment process and clean up any temporary resources.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -26,13 +26,23 @@ func NewCancelCommand() *cobra.Command {
return fmt.Errorf("failed to get API client: %w", err)
}
force, _ := cmd.Flags().GetBool("force")
// Check minimum version requirement
if err := cli.CheckMinimumVersion(ctx, client, "4.0.0-beta.436"); err != nil {
return err
}
force, err := cmd.Flags().GetBool("force")
if err != nil {
return fmt.Errorf("failed to parse force flag: %w", err)
}
// Prompt for confirmation unless --force is used
if !force {
var response string
fmt.Printf("Are you sure you want to cancel deployment %s? (yes/no): ", uuid)
fmt.Scanln(&response)
var response string
if _, err := fmt.Scanln(&response); err != nil {
return fmt.Errorf("failed to read confirmation: %w", err)
}
if response != "yes" && response != "y" {
fmt.Println("Cancel aborted.")
@@ -46,7 +56,11 @@ func NewCancelCommand() *cobra.Command {
return fmt.Errorf("failed to cancel deployment: %w", err)
}
format, _ := cmd.Flags().GetString("format")
format, err := cmd.Flags().GetString("format")
if err != nil {
return fmt.Errorf("failed to get format flag: %w", err)
}
formatter, err := output.NewFormatter(format, output.Options{})
if err != nil {
return fmt.Errorf("failed to create formatter: %w", err)
+3 -3
View File
@@ -1,13 +1,13 @@
package deployment
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewGetCommand gets deployment details
@@ -18,7 +18,7 @@ func NewGetCommand() *cobra.Command {
Long: `Get detailed information about a specific deployment by its UUID.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+4 -4
View File
@@ -1,13 +1,13 @@
package deployment
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand lists all deployments
@@ -16,8 +16,8 @@ func NewListCommand() *cobra.Command {
Use: "list",
Short: "List all deployments",
Long: `List all currently running deployments across all resources.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
+3 -3
View File
@@ -1,13 +1,13 @@
package deployment
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewNameCommand deploys a resource by name
@@ -17,7 +17,7 @@ func NewNameCommand() *cobra.Command {
Short: "Deploy by resource name",
Args: cli.ExactArgs(1, "<resource_name>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
name := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,13 +1,13 @@
package deployment
import (
"context"
"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"
"github.com/spf13/cobra"
)
// ResultDisplay represents a deploy result for table display
@@ -23,7 +23,7 @@ func NewUUIDCommand() *cobra.Command {
Short: "Deploy by uuid",
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+4 -4
View File
@@ -23,11 +23,11 @@ var manCmd = &cobra.Command{
The man pages will be written to the specified directory (default: ./man).`,
Example: ` coolify docs man
coolify docs man --output-dir=/usr/local/share/man/man1`,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, _ []string) error {
outputDir, _ := cmd.Flags().GetString("output-dir")
// Create output directory if it doesn't exist
if err := os.MkdirAll(outputDir, 0755); err != nil {
if err := os.MkdirAll(outputDir, 0750); err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}
@@ -65,11 +65,11 @@ var markdownCmd = &cobra.Command{
The markdown files will be written to the specified directory (default: ./docs).`,
Example: ` coolify docs markdown
coolify docs markdown --output-dir=./documentation`,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, _ []string) error {
outputDir, _ := cmd.Flags().GetString("output-dir")
// Create output directory if it doesn't exist
if err := os.MkdirAll(outputDir, 0755); err != nil {
if err := os.MkdirAll(outputDir, 0750); err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}
+3 -3
View File
@@ -1,13 +1,13 @@
package github
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewListBranchesCommand() *cobra.Command {
@@ -19,7 +19,7 @@ func NewListBranchesCommand() *cobra.Command {
Example: coolify github branches abc-123-def owner/repository`,
Args: cli.ExactArgs(2, "<app_uuid> <owner/repo>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
appUUID := args[0]
// Parse owner/repo
+12 -12
View File
@@ -1,14 +1,14 @@
package github
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewCreateCommand() *cobra.Command {
@@ -20,8 +20,8 @@ func NewCreateCommand() *cobra.Command {
Required flags: --name, --api-url, --html-url, --app-id, --installation-id, --client-id, --client-secret, --private-key-uuid
Example: coolify github create --name "My GitHub App" --api-url "https://api.github.com" --html-url "https://github.com" --app-id 123456 --installation-id 789012 --client-id "Iv1.abc123" --client-secret "secret123" --private-key-uuid "abc-123-def-456"`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
@@ -100,14 +100,14 @@ Example: coolify github create --name "My GitHub App" --api-url "https://api.git
createCmd.Flags().String("private-key-uuid", "", "UUID of existing private key (required)")
createCmd.Flags().Bool("system-wide", false, "Is this app system-wide (cloud only)")
createCmd.MarkFlagRequired("name")
createCmd.MarkFlagRequired("api-url")
createCmd.MarkFlagRequired("html-url")
createCmd.MarkFlagRequired("app-id")
createCmd.MarkFlagRequired("installation-id")
createCmd.MarkFlagRequired("client-id")
createCmd.MarkFlagRequired("client-secret")
createCmd.MarkFlagRequired("private-key-uuid")
_ = createCmd.MarkFlagRequired("name")
_ = createCmd.MarkFlagRequired("api-url")
_ = createCmd.MarkFlagRequired("html-url")
_ = createCmd.MarkFlagRequired("app-id")
_ = createCmd.MarkFlagRequired("installation-id")
_ = createCmd.MarkFlagRequired("client-id")
_ = createCmd.MarkFlagRequired("client-secret")
_ = createCmd.MarkFlagRequired("private-key-uuid")
return createCmd
}
+8 -4
View File
@@ -1,12 +1,12 @@
package github
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewDeleteCommand() *cobra.Command {
@@ -16,7 +16,7 @@ func NewDeleteCommand() *cobra.Command {
Long: `Delete a GitHub App integration. The app must not be used by any applications.`,
Args: cli.ExactArgs(1, "<app_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
appUUID := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -30,7 +30,11 @@ func NewDeleteCommand() *cobra.Command {
if !force {
var response string
fmt.Printf("Are you sure you want to delete GitHub App %s? This cannot be undone. (yes/no): ", appUUID)
fmt.Scanln(&response)
_, 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.")
+3 -3
View File
@@ -1,13 +1,13 @@
package github
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewGetCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewGetCommand() *cobra.Command {
Long: `Get detailed information about a specific GitHub App integration.`,
Args: cli.ExactArgs(1, "<app_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
appUUID := args[0]
client, err := cli.GetAPIClient(cmd)
+9 -4
View File
@@ -1,13 +1,13 @@
package github
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewListCommand() *cobra.Command {
@@ -15,14 +15,19 @@ func NewListCommand() *cobra.Command {
Use: "list",
Short: "List all GitHub App integrations",
Long: `List all GitHub App integrations configured in Coolify.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
return fmt.Errorf("failed to get API client: %w", err)
}
// Check minimum version requirement
if err := cli.CheckMinimumVersion(ctx, client, "4.0.0-beta.436"); err != nil {
return err
}
svc := service.NewGitHubAppService(client)
apps, err := svc.List(ctx)
if err != nil {
+3 -3
View File
@@ -1,13 +1,13 @@
package github
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewListRepositoriesCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewListRepositoriesCommand() *cobra.Command {
Long: `List all repositories that are accessible by the specified GitHub App.`,
Args: cli.ExactArgs(1, "<app_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
appUUID := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,13 +1,13 @@
package github
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
func NewUpdateCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewUpdateCommand() *cobra.Command {
Long: `Update an existing GitHub App integration. Provide the app UUID and the fields you want to update.`,
Args: cli.ExactArgs(1, "<app_uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
appUUID := args[0]
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,14 +1,14 @@
package privatekeys
import (
"context"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/models"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewCreateCommand creates the create command
@@ -19,7 +19,7 @@ func NewCreateCommand() *cobra.Command {
Args: cli.ExactArgs(2, "<key_name> <private_key_or_file>"),
Short: "Add a private key",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
name := args[0]
privateKeyInput := args[1]
+3 -3
View File
@@ -1,12 +1,12 @@
package privatekeys
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewDeleteCommand creates the delete command
@@ -16,7 +16,7 @@ func NewDeleteCommand() *cobra.Command {
Args: cli.ExactArgs(1, "<uuid>"),
Short: "Remove a private key",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
+4 -4
View File
@@ -1,13 +1,13 @@
package privatekeys
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand creates the list command
@@ -15,8 +15,8 @@ func NewListCommand() *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List all private keys",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
+4 -4
View File
@@ -1,13 +1,13 @@
package project
import (
"context"
"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"
"github.com/spf13/cobra"
)
// EnvironmentRow represents an environment for display
@@ -23,7 +23,7 @@ func NewGetCommand() *cobra.Command {
Short: "Get a project by uuid",
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -55,7 +55,7 @@ func NewGetCommand() *cobra.Command {
var rows []EnvironmentRow
// If the project has environments, expand them
if project.Environments != nil && len(project.Environments) > 0 {
if len(project.Environments) > 0 {
for _, env := range project.Environments {
rows = append(rows, EnvironmentRow{
UUID: env.UUID,
+4 -4
View File
@@ -1,13 +1,13 @@
package project
import (
"context"
"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"
"github.com/spf13/cobra"
)
// ListRow represents a project for list display (without environments)
@@ -22,8 +22,8 @@ func NewListCommand() *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List all projects",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
+4 -4
View File
@@ -1,13 +1,13 @@
package resources
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand returns the list projects command
@@ -15,8 +15,8 @@ func NewListCommand() *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List all resources",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
+16 -13
View File
@@ -1,10 +1,14 @@
package cmd
import (
"errors"
"fmt"
"log"
"os"
"text/tabwriter"
compareVersion "github.com/hashicorp/go-version"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/coollabsio/coolify-cli/cmd/application"
"github.com/coollabsio/coolify-cli/cmd/completion"
@@ -23,9 +27,6 @@ import (
cliversion "github.com/coollabsio/coolify-cli/cmd/version"
"github.com/coollabsio/coolify-cli/internal/config"
"github.com/coollabsio/coolify-cli/internal/version"
compareVersion "github.com/hashicorp/go-version"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// Legacy global variables - kept for backward compatibility during migration
@@ -39,10 +40,9 @@ var (
Debug bool
ShowSensitive bool
Format string
JsonMode bool
JSONMode bool
PrettyMode bool
SetDefaultInstance bool
w = tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', tabwriter.Debug)
)
var rootCmd = &cobra.Command{
@@ -51,7 +51,7 @@ var rootCmd = &cobra.Command{
Long: `A CLI tool to interact with Coolify API.`,
SilenceUsage: true, // Don't show usage on errors
SilenceErrors: false, // Still print errors
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
return nil
},
}
@@ -60,7 +60,7 @@ var rootCmd = &cobra.Command{
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(0)
os.Exit(1)
}
}
@@ -68,10 +68,10 @@ func init() {
rootCmd = &cobra.Command{
Use: "coolify",
Short: "Coolify CLI",
Long: fmt.Sprintf("A CLI tool to interact with Coolify API.\nVersion: %s", version.CliVersion),
Long: fmt.Sprintf("A CLI tool to interact with Coolify API.\nVersion: %s", version.GetVersion()),
SilenceUsage: true, // Don't show usage on errors
SilenceErrors: false, // Still print errors
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
return nil
},
}
@@ -112,11 +112,14 @@ func initConfig() {
// Ensure config directory exists
configDir := config.Path()[:len(config.Path())-len("/config.json")]
if _, err := os.Stat(configDir); os.IsNotExist(err) {
os.MkdirAll(configDir, 0755)
if err := os.MkdirAll(configDir, 0750); err != nil {
log.Printf("Failed to create config directory: %v\n", err)
}
}
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
var notFoundErr viper.ConfigFileNotFoundError
if errors.As(err, &notFoundErr) {
log.Println("Config file not found. Creating a new one at", config.Path())
if err := config.CreateDefault(); err != nil {
log.Printf("Failed to create default config: %v\n", err)
@@ -153,7 +156,7 @@ func initConfig() {
if latestVersionStr != "" {
latestVersion, err := compareVersion.NewVersion(latestVersionStr)
if err == nil {
currentVersion, err := compareVersion.NewVersion(version.CliVersion)
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)
+5 -5
View File
@@ -1,13 +1,13 @@
package server
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
// NewAddCommand creates the add command
@@ -17,7 +17,7 @@ func NewAddCommand() *cobra.Command {
Args: cli.ExactArgs(3, "<server_name> <ip_address> <private_key_uuid>"),
Short: "Add a server",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
// Get API client
client, err := cli.GetAPIClient(cmd)
@@ -28,7 +28,7 @@ func NewAddCommand() *cobra.Command {
// Parse arguments and flags
name := args[0]
ip := args[1]
privateKeyUuid := args[2]
privateKeyUUID := args[2]
port, _ := cmd.Flags().GetInt("port")
user, _ := cmd.Flags().GetString("user")
validate, _ := cmd.Flags().GetBool("validate")
@@ -39,7 +39,7 @@ func NewAddCommand() *cobra.Command {
IP: ip,
Port: port,
User: user,
PrivateKeyUUID: privateKeyUuid,
PrivateKeyUUID: privateKeyUUID,
InstantValidate: validate,
}
+3 -3
View File
@@ -1,13 +1,13 @@
package server
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewGetCommand creates the get command
@@ -18,7 +18,7 @@ func NewGetDomainsCommand() *cobra.Command {
Args: cli.ExactArgs(1, "<uuid>"),
Short: "Get server domains by uuid",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
// Get API client
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,13 +1,13 @@
package server
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewGetCommand creates the get command
@@ -17,7 +17,7 @@ func NewGetCommand() *cobra.Command {
Args: cli.ExactArgs(1, "<uuid>"),
Short: "Get server details by uuid",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
// Get API client
client, err := cli.GetAPIClient(cmd)
+4 -4
View File
@@ -1,13 +1,13 @@
package server
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand creates the list command
@@ -15,8 +15,8 @@ func NewListCommand() *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List all servers",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
// Get API client
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,12 +1,12 @@
package server
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewRemoveCommand creates the remove command
@@ -16,7 +16,7 @@ func NewRemoveCommand() *cobra.Command {
Args: cli.ExactArgs(1, "<uuid>"),
Short: "Remove a server",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
// Get API client
client, err := cli.GetAPIClient(cmd)
+3 -3
View File
@@ -1,12 +1,12 @@
package server
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewValidateCommand creates the validate command
@@ -16,7 +16,7 @@ func NewValidateCommand() *cobra.Command {
Args: cli.ExactArgs(1, "<uuid>"),
Short: "Validate a server",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
// Get API client
client, err := cli.GetAPIClient(cmd)
+9 -5
View File
@@ -1,12 +1,12 @@
package service
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewDeleteCommand deletes a service
@@ -17,7 +17,7 @@ func NewDeleteCommand() *cobra.Command {
Long: `Delete a service and optionally clean up its configurations, volumes, and networks.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -35,7 +35,11 @@ func NewDeleteCommand() *cobra.Command {
if !force {
var response string
fmt.Printf("Are you sure you want to delete this service? (yes/no): ")
fmt.Scanln(&response)
_, 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.")
@@ -43,7 +47,7 @@ func NewDeleteCommand() *cobra.Command {
}
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
err = serviceSvc.Delete(ctx, uuid, deleteConfigurations, deleteVolumes, dockerCleanup, deleteConnectedNetworks)
if err != nil {
return fmt.Errorf("failed to delete service: %w", err)
+11 -11
View File
@@ -1,13 +1,13 @@
package env
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
func NewCreateCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewCreateCommand() *cobra.Command {
Long: `Create a new environment variable for a specific service. Use --key and --value flags to specify the variable.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -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,17 +48,17 @@ 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.NewServiceService(client)
serviceSvc := service.NewService(client)
env, err := serviceSvc.CreateEnv(ctx, uuid, req)
if err != nil {
return fmt.Errorf("failed to create environment variable: %w", err)
@@ -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
}
+5 -5
View File
@@ -1,12 +1,12 @@
package env
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewDeleteCommand() *cobra.Command {
@@ -16,7 +16,7 @@ func NewDeleteCommand() *cobra.Command {
Long: `Delete an environment variable from a service. First UUID is the service, second is the specific environment variable to delete.`,
Args: cli.ExactArgs(2, "<uuid1> <uuid2>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
serviceUUID := args[0]
envUUID := args[1]
@@ -31,7 +31,7 @@ func NewDeleteCommand() *cobra.Command {
if !force {
var response string
fmt.Printf("Are you sure you want to delete this environment variable? (yes/no): ")
fmt.Scanln(&response)
_, _ = fmt.Scanln(&response)
if response != "yes" && response != "y" {
fmt.Println("Delete cancelled.")
@@ -39,7 +39,7 @@ func NewDeleteCommand() *cobra.Command {
}
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
err = serviceSvc.DeleteEnv(ctx, serviceUUID, envUUID)
if err != nil {
return fmt.Errorf("failed to delete environment variable: %w", err)
+4 -4
View File
@@ -1,13 +1,13 @@
package env
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewGetCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewGetCommand() *cobra.Command {
Long: `Get detailed information about a specific environment variable. First UUID is the service, second is the environment variable UUID or key name.`,
Args: cli.ExactArgs(2, "<uuid1> <uuid2>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
serviceUUID := args[0]
envUUID := args[1]
@@ -26,7 +26,7 @@ func NewGetCommand() *cobra.Command {
return fmt.Errorf("failed to get API client: %w", err)
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
env, err := serviceSvc.GetEnv(ctx, serviceUUID, envUUID)
if err != nil {
return fmt.Errorf("failed to get environment variable: %w", err)
+4 -4
View File
@@ -1,13 +1,13 @@
package env
import (
"context"
"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"
"github.com/spf13/cobra"
)
func NewListCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewListCommand() *cobra.Command {
Long: `List all environment variables for a specific service.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -25,7 +25,7 @@ func NewListCommand() *cobra.Command {
return fmt.Errorf("failed to get API client: %w", err)
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
envs, err := serviceSvc.ListEnvs(ctx, uuid)
if err != nil {
return fmt.Errorf("failed to list environment variables: %w", err)
+15 -15
View File
@@ -1,15 +1,15 @@
package env
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/models"
"github.com/coollabsio/coolify-cli/internal/parser"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
func NewSyncCommand() *cobra.Command {
@@ -24,7 +24,7 @@ func NewSyncCommand() *cobra.Command {
Example: coolify service env sync abc123 --file .env.production`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -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)
@@ -55,24 +55,24 @@ Example: coolify service env sync abc123 --file .env.production`,
fmt.Printf("Found %d environment variables in file. Syncing...\n", len(envVars))
// Fetch existing environment variables
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
existingEnvs, err := serviceSvc.ListEnvs(ctx, uuid)
if err != nil {
return fmt.Errorf("failed to list existing environment variables: %w", err)
}
// 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
}
+15 -15
View File
@@ -1,13 +1,13 @@
package env
import (
"context"
"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/service"
"github.com/spf13/cobra"
)
func NewUpdateCommand() *cobra.Command {
@@ -17,7 +17,7 @@ func NewUpdateCommand() *cobra.Command {
Long: `Update an existing environment variable. First UUID is the service, second is the specific environment variable to update.`,
Args: cli.ExactArgs(2, "<uuid1> <uuid2>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
serviceUUID := args[0]
envUUID := args[1]
@@ -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,13 +51,17 @@ func NewUpdateCommand() *cobra.Command {
isMultiline, _ := cmd.Flags().GetBool("is-multiline")
req.IsMultiline = &isMultiline
}
// 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 cmd.Flags().Changed("runtime") {
isRuntime, _ := cmd.Flags().GetBool("runtime")
req.IsRuntime = &isRuntime
}
serviceSvc := service.NewServiceService(client)
// Check if at least one field is being updated
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)
env, err := serviceSvc.UpdateEnv(ctx, serviceUUID, req)
if err != nil {
return fmt.Errorf("failed to update environment variable: %w", err)
@@ -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
}
+4 -4
View File
@@ -1,13 +1,13 @@
package service
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewGetCommand gets service details
@@ -18,7 +18,7 @@ func NewGetCommand() *cobra.Command {
Long: `Get detailed information about a specific service.`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -26,7 +26,7 @@ func NewGetCommand() *cobra.Command {
return fmt.Errorf("failed to get API client: %w", err)
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
svc, err := serviceSvc.Get(ctx, uuid)
if err != nil {
return fmt.Errorf("failed to get service: %w", err)
+5 -5
View File
@@ -1,13 +1,13 @@
package service
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand lists all services
@@ -16,15 +16,15 @@ func NewListCommand() *cobra.Command {
Use: "list",
Short: "List all services",
Long: `List all services in Coolify.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
return fmt.Errorf("failed to get API client: %w", err)
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
services, err := serviceSvc.List(ctx)
if err != nil {
return fmt.Errorf("failed to list services: %w", err)
+4 -4
View File
@@ -1,12 +1,12 @@
package service
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewRestartCommand restarts a service
@@ -17,7 +17,7 @@ func NewRestartCommand() *cobra.Command {
Long: `Restart a service (restart all containers).`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -25,7 +25,7 @@ func NewRestartCommand() *cobra.Command {
return fmt.Errorf("failed to get API client: %w", err)
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
resp, err := serviceSvc.Restart(ctx, uuid)
if err != nil {
return fmt.Errorf("failed to restart service: %w", err)
+17 -10
View File
@@ -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
}
+4 -4
View File
@@ -1,12 +1,12 @@
package service
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewStartCommand starts a service
@@ -17,7 +17,7 @@ func NewStartCommand() *cobra.Command {
Long: `Start a service (deploy all containers).`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -25,7 +25,7 @@ func NewStartCommand() *cobra.Command {
return fmt.Errorf("failed to get API client: %w", err)
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
resp, err := serviceSvc.Start(ctx, uuid)
if err != nil {
return fmt.Errorf("failed to start service: %w", err)
+4 -4
View File
@@ -1,12 +1,12 @@
package service
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/coollabsio/coolify-cli/internal/cli"
"github.com/coollabsio/coolify-cli/internal/service"
"github.com/spf13/cobra"
)
// NewStopCommand stops a service
@@ -17,7 +17,7 @@ func NewStopCommand() *cobra.Command {
Long: `Stop a service (stop all containers).`,
Args: cli.ExactArgs(1, "<uuid>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
uuid := args[0]
client, err := cli.GetAPIClient(cmd)
@@ -25,7 +25,7 @@ func NewStopCommand() *cobra.Command {
return fmt.Errorf("failed to get API client: %w", err)
}
serviceSvc := service.NewServiceService(client)
serviceSvc := service.NewService(client)
resp, err := serviceSvc.Stop(ctx, uuid)
if err != nil {
return fmt.Errorf("failed to stop service: %w", err)
+4 -4
View File
@@ -1,13 +1,13 @@
package teams
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewCurrentCommand creates the current command
@@ -16,8 +16,8 @@ func NewCurrentCommand() *cobra.Command {
Use: "current",
Short: "Get currently authenticated team",
Long: `Get details of the team associated with the current authentication token.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
+3 -3
View File
@@ -1,13 +1,13 @@
package teams
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewGetCommand creates the get command
@@ -18,7 +18,7 @@ func NewGetCommand() *cobra.Command {
Long: `Get detailed information about a specific team by its ID.`,
Args: cli.ExactArgs(1, "<team_id>"),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
teamID := args[0]
client, err := cli.GetAPIClient(cmd)
+4 -4
View File
@@ -1,13 +1,13 @@
package teams
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand creates the list command
@@ -16,8 +16,8 @@ func NewListCommand() *cobra.Command {
Use: "list",
Short: "List all teams",
Long: `List all teams you have access to.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
RunE: func(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {
+3 -3
View File
@@ -1,13 +1,13 @@
package members
import (
"context"
"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"
"github.com/spf13/cobra"
)
// NewListCommand creates the list command
@@ -18,7 +18,7 @@ func NewListCommand() *cobra.Command {
Long: `List members of a specific team by ID, or list members of the current team if no ID is provided.`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
ctx := cmd.Context()
client, err := cli.GetAPIClient(cmd)
if err != nil {

Some files were not shown because too many files have changed in this diff Show More