Compare commits

...

7 Commits

Author SHA1 Message Date
github-actions[bot] ab951a561c chore: bump version to v1.6.1 2026-04-16 10:01:33 +00:00
Andras Bacsai bc36a44f2c docs: add Homebrew tap details to release guide
Document the GoReleaser-managed Homebrew tap, update verification
steps to be non-destructive, and tighten the post-release checklist.
2026-04-16 11:49:42 +02:00
Andras Bacsai d3489a49ce chore: remove conductor setup script and config
Deletes conductor-setup.sh and conductor.json, which were used for
workspace bootstrapping and hot-reload tooling via the conductor runner.
2026-04-16 11:44:58 +02:00
Andras Bacsai e2f0b47579 docs: update release guide for automated version bumping
Document that version is injected at build time via ldflags and that
the post-release update-version CI job handles committing the bump to
internal/version/checker.go. Remove the manual version bump step from
the pre-release checklist.
2026-04-16 11:39:27 +02:00
Andras Bacsai 8e35e61aa0 Merge pull request #70 from YaRissi/fix/format-flag
fix: json format for service commands
2026-04-16 11:34:52 +02:00
YaRissi 0197333e41 fix: json format for service commands 2026-04-12 22:54:12 +02:00
Andras Bacsai 5e2b3d08db feat(docs): generate quick llms.txt and full llms-full.txt
Refactor `coolify docs llms` to emit two AI-oriented artifacts:
- `llms.txt` as a concise operating guide
- `llms-full.txt` as the exhaustive command and flag catalog

Update tests to cover quick/full generation, document both files in README,
and adjust CI to fail when either generated file is out of date.
2026-03-31 17:44:24 +02:00
12 changed files with 2629 additions and 2301 deletions
+3 -3
View File
@@ -49,10 +49,10 @@ jobs:
run: go run ./coolify docs llms
- name: Check uncommitted changes
run: git diff --exit-code llms.txt
run: git diff --exit-code llms.txt llms-full.txt
- if: failure()
run: echo "::error::llms.txt is out of date. Run 'go run ./coolify docs llms' and commit the changes."
run: echo "::error::llms.txt or llms-full.txt is out of date. Run 'go run ./coolify docs llms' and commit the changes."
go-mod-tidy:
runs-on: ubuntu-latest
@@ -72,4 +72,4 @@ jobs:
run: git diff --exit-code
- if: failure()
run: echo "::error::Check failed, please run 'go mod tidy' and commit the changes."
run: echo "::error::Check failed, please run 'go mod tidy' and commit the changes."
+24 -23
View File
@@ -44,30 +44,27 @@ Once you publish the release:
- **Linux**: amd64, arm64
- **macOS (Darwin)**: amd64, arm64
- **Windows**: amd64, arm64
3. Goreleaser injects the version from the tag into the binaries
3. Goreleaser injects the version from the tag into the binaries via ldflags (into `internal/version.version`)
4. Binaries are automatically uploaded to the release
5. The release becomes available at:
5. A follow-up `update-version` job then:
- Updates the `version` constant in `internal/version/checker.go` to the new tag
- Commits the bump to `v4.x` as `chore: bump version to vX.Y.Z`
- Force-moves the release tag to point at that new commit
6. GoReleaser also publishes a Homebrew formula to the tap at [`coollabsio/homebrew-coolify-cli`](https://github.com/coollabsio/homebrew-coolify-cli) (under `Formula/coolify-cli.rb`), using the `HOMEBREW_TAP_GITHUB_TOKEN` secret
7. 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`
- Homebrew: `brew install coollabsio/coolify-cli/coolify-cli`
- `go install`: `go install github.com/coollabsio/coolify-cli/coolify@v1.x.x`
### 3. Verify the Release
After the workflow completes (usually 2-5 minutes):
After the workflow completes (usually 2-5 minutes), verify without touching your local install:
1. Check the release page has all platform binaries
2. Test the install script:
```bash
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
coolify version
```
3. Test the auto-update functionality:
```bash
# If you have an older version installed
coolify update
coolify version # Should show the new version
```
4. Verify the version matches your release
1. Check the release page has all platform binaries (Linux/macOS/Windows × amd64/arm64)
2. Confirm the `update-version` job committed the bump on `v4.x` (look for `chore: bump version to vX.Y.Z`) and that the tag now points at that commit
3. Confirm `internal/version/checker.go` on `v4.x` has the new version
4. Confirm the Homebrew tap has a new `Formula/coolify-cli.rb` commit for this version at https://github.com/coollabsio/homebrew-coolify-cli
## Troubleshooting
@@ -79,9 +76,10 @@ After the workflow completes (usually 2-5 minutes):
- GoReleaser configuration issues
### Version Not Updating
- Ensure you committed the version change in `cmd/root.go`
- The version is injected at build time via ldflags into `internal/version.version` — you do **not** need to edit it manually before releasing. The post-release `update-version` job also rewrites `internal/version/checker.go` on `v4.x`.
- If the hardcoded fallback in `internal/version/checker.go` is stale, check that the `update-version` job ran successfully after the release.
- The tag must start with `v` (e.g., `v1.2.3`, not `1.2.3`)
- Check that the workflow has write permissions
- Check that the workflow has write permissions (`contents: write` in `release-cli.yml`)
### Install Script Not Finding New Version
- Wait a few minutes for GitHub's CDN to update
@@ -94,30 +92,33 @@ Before creating a release:
- [ ] All tests pass: `go test ./internal/...`
- [ ] Code is formatted: `go fmt ./...`
- [ ] Version updated in `cmd/root.go`
- [ ] Changes merged to `v4.x` branch
- [ ] Release notes prepared
> Note: You do **not** need to bump the version manually. GoReleaser injects the tag version via ldflags, and the `update-version` CI job commits the bump to `internal/version/checker.go` after the release.
After creating a release:
- [ ] GitHub Actions workflow completed successfully
- [ ] GitHub Actions workflow completed successfully (both `release-cli` and `update-version` jobs)
- [ ] All platform binaries are present on the release page
- [ ] Install script downloads the new version
- [ ] `coolify version` returns the correct version
- [ ] `internal/version/checker.go` on `v4.x` shows the new version
- [ ] Homebrew tap has a fresh `Formula/coolify-cli.rb` commit
## Configuration Files
The release process uses these configuration files:
- `.goreleaser.yml` - GoReleaser configuration (build matrix, archives, etc.) - points to `/coolify` as entry point
- `.goreleaser.yml` - GoReleaser configuration (build matrix, archives, Homebrew tap) - entry point is `./coolify/main.go`
- `.github/workflows/release-cli.yml` - GitHub Actions workflow
- `scripts/install.sh` - User-facing install script
- `internal/version/checker.go` - Contains `GetVersion()` function that returns the current version
- `coolify/main.go` - Binary entry point for `go install` support
- [`coollabsio/homebrew-coolify-cli`](https://github.com/coollabsio/homebrew-coolify-cli) - External Homebrew tap updated automatically on each release
## Notes
- The CLI has auto-update checking built-in (checks every 10 minutes)
- Users can manually update with `coolify update`
- Install script supports version pinning: `bash install.sh v1.2.3`
- Homebrew users can install via `brew install coollabsio/coolify-cli/coolify-cli` (the tap at https://github.com/coollabsio/homebrew-coolify-cli is auto-updated by GoReleaser)
- Releases are immutable - if you need to fix something, create a new patch version
+10
View File
@@ -64,6 +64,16 @@ This will install the `coolify` binary in your `$GOPATH/bin` directory (usually
Now you can use the CLI with the token you just added.
## For LLMs / AI agents
- Quick instructions: [`llms.txt`](./llms.txt)
- Full command catalog: [`llms-full.txt`](./llms-full.txt)
- Regenerate both files:
```bash
go run ./coolify docs llms
```
## Change default context
You can change the default context with `coolify context use <context_name>` or `coolify context set-default <context_name>`
## Currently Supported Commands
+215 -24
View File
@@ -90,41 +90,168 @@ The markdown files will be written to the specified directory (default: ./docs).
var llmsCmd = &cobra.Command{
Use: "llms",
Short: "Generate llms.txt for AI agent command specification",
Long: `Generate a machine-readable llms.txt file that defines all CLI commands and their parameters.
Short: "Generate llms.txt and llms-full.txt for AI agents",
Long: `Generate AI-friendly documentation files for the Coolify CLI.
This file is intended to enable AI agents to understand and interact with the CLI.
The output file will be written to the specified path (default: ./llms.txt).`,
This creates a concise llms.txt quick reference plus a complete llms-full.txt command catalog.
The output files will be written to the specified paths (defaults: ./llms.txt and ./llms-full.txt).`,
Example: ` coolify docs llms
coolify docs llms --output=./llms.txt`,
coolify docs llms --output=./llms.txt --full-output=./llms-full.txt`,
RunE: func(cmd *cobra.Command, _ []string) error {
outputFile, _ := cmd.Flags().GetString("output")
fullOutputFile, _ := cmd.Flags().GetString("full-output")
var sb strings.Builder
sb.WriteString(llmsIntro)
writeLLMsAliases(&sb, rootCmd, "coolify")
sb.WriteString(llmsBody)
writeLLMsCommand(&sb, rootCmd, "coolify")
if err := os.WriteFile(outputFile, []byte(sb.String()), 0600); err != nil {
return fmt.Errorf("failed to write llms.txt: %w", err)
}
absPath, _ := filepath.Abs(outputFile)
fmt.Printf("llms.txt generated successfully: %s\n", absPath)
return nil
return writeLLMsArtifacts(outputFile, fullOutputFile)
},
}
// llmsIntro contains the static overview section prepended to the generated command reference.
const llmsIntro = `# Coolify CLI - llms.txt
const llmsQuickTemplate = `# Coolify CLI - llms.txt
> A CLI tool for interacting with the Coolify API, built with Go.
> Quick AI/LLM instructions for the Coolify CLI.
> Source: https://github.com/coollabsio/coolify-cli
> API Spec: https://github.com/coollabsio/coolify/blob/v4.x/openapi.json
## Operating Rules
- Prefer ` + "`--format json`" + ` for automation and parsing.
- Use Coolify UUIDs for resources; do not use internal numeric IDs.
- Team commands are the exception: they use numeric team IDs.
- Authenticate with a saved context when possible; use ` + "`--token`" + ` only for overrides.
- Use ` + "`llms-full.txt`" + ` for the exhaustive command/flag catalog.
## Installation
` + "```bash" + `
# Linux/macOS (recommended)
curl -fsSL https://raw.githubusercontent.com/coollabsio/coolify-cli/main/scripts/install.sh | bash
# Homebrew (macOS/Linux)
brew install coollabsio/coolify-cli/coolify-cli
# Windows (PowerShell)
irm https://raw.githubusercontent.com/coollabsio/coolify-cli/main/scripts/install.ps1 | iex
# Go install
go install github.com/coollabsio/coolify-cli/coolify@latest
` + "```" + `
## Authentication
1. Get an API token from your Coolify dashboard at ` + "`/security/api-tokens`" + `
2. For Coolify Cloud: ` + "`coolify context set-token cloud <token>`" + `
3. For self-hosted: ` + "`coolify context add -d <context_name> <url> <token>`" + `
4. Switch contexts with ` + "`coolify context use <context_name>`" + `
## Configuration
Config file location:
- Linux/macOS: ` + "`~/.config/coolify/config.json`" + `
- Windows: ` + "`%%APPDATA%%\\coolify\\config.json`" + `
Supports multiple contexts (instances) with ` + "`coolify context`" + ` commands.
## Output Formats
All commands support ` + "`--format`" + ` flag:
- ` + "`table`" + ` (default) - human-readable tabular output
- ` + "`json`" + ` - compact JSON for scripting
- ` + "`pretty`" + ` - indented JSON for debugging
## Global Flags
- ` + "`--context <name>`" + ` - use a specific saved context
- ` + "`--token <token>`" + ` - override token from config
- ` + "`--format table|json|pretty`" + ` - choose output format
- ` + "`--show-sensitive`" + ` - reveal sensitive values
- ` + "`--debug`" + ` - enable debug output
## Common Workflows
### Contexts
` + "```bash" + `
coolify context list
coolify context verify
coolify context version
coolify context use prod
` + "```" + `
### Inventory
` + "```bash" + `
coolify server list
coolify project list
coolify resource list
coolify app list
coolify service list
coolify database list
` + "```" + `
### Applications
` + "```bash" + `
coolify app get <uuid>
coolify app start <uuid>
coolify app stop <uuid>
coolify app restart <uuid>
coolify app logs <uuid> --follow
coolify app deployments list <app-uuid>
coolify app deployments logs <app-uuid> --follow
` + "```" + `
### Environment Variables
` + "```bash" + `
coolify app env list <app-uuid>
coolify app env create <app-uuid> --key API_KEY --value secret123
coolify app env update <app-uuid> <env-uuid-or-key> --value new-secret
coolify app env sync <app-uuid> --file .env.production --build-time --preview
` + "```" + `
### Deployments
` + "```bash" + `
coolify deploy list
coolify deploy name my-application
coolify deploy batch api,worker,frontend --force
coolify deploy cancel <deployment-uuid>
` + "```" + `
### Databases and Services
` + "```bash" + `
coolify database get <uuid>
coolify database create postgresql --server-uuid <uuid> --project-uuid <uuid> --environment-name production
coolify database backup list <database-uuid>
coolify service get <uuid>
coolify service create <type> --project-uuid <uuid> --server-uuid <uuid> --instant-deploy
` + "```" + `
## Common Aliases
- ` + "`coolify app`" + ` | ` + "`coolify apps`" + ` | ` + "`coolify application`" + ` | ` + "`coolify applications`" + `
- ` + "`coolify service`" + ` | ` + "`coolify services`" + ` | ` + "`coolify svc`" + `
- ` + "`coolify database`" + ` | ` + "`coolify databases`" + ` | ` + "`coolify db`" + ` | ` + "`coolify dbs`" + `
- ` + "`coolify teams`" + ` | ` + "`coolify team`" + `
## Full Reference
- Full command and parameter catalog: %s
- Regenerate docs: ` + "`go run ./coolify docs llms`" + `
`
const llmsFullIntro = `# Coolify CLI - llms-full.txt
> Full AI/LLM command catalog for the Coolify CLI.
> Manage Coolify instances (cloud and self-hosted), servers, projects, applications, databases, services, deployments, domains, and private keys.
> Source: https://github.com/coollabsio/coolify-cli
> API Spec: https://github.com/coollabsio/coolify/blob/v4.x/openapi.json
## Companion Files
- Quick instructions: %s
- Regenerate docs: ` + "`go run ./coolify docs llms`" + `
## Installation
` + "```bash" + `
@@ -151,7 +278,7 @@ go install github.com/coollabsio/coolify-cli/coolify@latest
Config file location:
- Linux/macOS: ` + "`~/.config/coolify/config.json`" + `
- Windows: ` + "`%APPDATA%\\coolify\\config.json`" + `
- Windows: ` + "`%%APPDATA%%\\coolify\\config.json`" + `
Supports multiple contexts (instances) with ` + "`coolify context`" + ` commands.
@@ -163,7 +290,7 @@ All commands support ` + "`--format`" + ` flag:
- ` + "`pretty`" + ` - indented JSON for debugging
`
const llmsBody = `
const llmsFullBody = `
## Supported Database Types
@@ -254,6 +381,69 @@ coolify team members list
`
func buildQuickLLMSText(fullReferencePath string) string {
return fmt.Sprintf(llmsQuickTemplate, fullReferencePath)
}
func buildFullLLMSText(quickReferencePath string) string {
var sb strings.Builder
fmt.Fprintf(&sb, llmsFullIntro, quickReferencePath)
writeLLMsAliases(&sb, rootCmd, "coolify")
sb.WriteString(llmsFullBody)
writeLLMsCommand(&sb, rootCmd, "coolify")
return sb.String()
}
func writeLLMsArtifacts(outputFile, fullOutputFile string) error {
if filepath.Clean(outputFile) == filepath.Clean(fullOutputFile) {
return fmt.Errorf("output and full-output must be different files")
}
if err := ensureParentDir(outputFile); err != nil {
return err
}
if err := ensureParentDir(fullOutputFile); err != nil {
return err
}
quickReferencePath := llmsReferencePath(fullOutputFile, outputFile)
fullReferencePath := llmsReferencePath(outputFile, fullOutputFile)
if err := os.WriteFile(outputFile, []byte(buildQuickLLMSText(fullReferencePath)), 0600); err != nil {
return fmt.Errorf("failed to write llms.txt: %w", err)
}
if err := os.WriteFile(fullOutputFile, []byte(buildFullLLMSText(quickReferencePath)), 0600); err != nil {
return fmt.Errorf("failed to write llms-full.txt: %w", err)
}
absQuickPath, _ := filepath.Abs(outputFile)
absFullPath, _ := filepath.Abs(fullOutputFile)
fmt.Printf("llms.txt generated successfully: %s\n", absQuickPath)
fmt.Printf("llms-full.txt generated successfully: %s\n", absFullPath)
return nil
}
func ensureParentDir(path string) error {
parentDir := filepath.Dir(path)
if err := os.MkdirAll(parentDir, 0750); err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}
return nil
}
func llmsReferencePath(fromFile, toFile string) string {
referencePath, err := filepath.Rel(filepath.Dir(fromFile), toFile)
if err != nil {
return filepath.ToSlash(toFile)
}
referencePath = filepath.ToSlash(referencePath)
if strings.HasPrefix(referencePath, ".") || strings.HasPrefix(referencePath, "/") {
return referencePath
}
return "./" + referencePath
}
// writeLLMsAliases writes aliases derived from the Cobra command tree.
func writeLLMsAliases(sb *strings.Builder, cmd *cobra.Command, parentPath string) {
aliases := collectLLMsAliases(cmd, parentPath)
@@ -431,6 +621,7 @@ func NewDocsCommand() *cobra.Command {
manCmd.Flags().StringP("output-dir", "o", "./man", "Output directory for man pages")
markdownCmd.Flags().StringP("output-dir", "o", "./docs", "Output directory for markdown files")
llmsCmd.Flags().StringP("output", "o", "./llms.txt", "Output file path")
llmsCmd.Flags().String("full-output", "./llms-full.txt", "Full output file path")
return docsCmd
}
+50
View File
@@ -1,6 +1,8 @@
package cmd
import (
"os"
"path/filepath"
"strings"
"testing"
@@ -66,3 +68,51 @@ func TestWriteLLMsAliasesUsesCommandTree(t *testing.T) {
}
}
}
func TestBuildQuickLLMSTextIncludesCoreGuidance(t *testing.T) {
got := buildQuickLLMSText("./llms-full.txt")
for _, want := range []string{
"# Coolify CLI - llms.txt",
"Prefer `--format json` for automation and parsing.",
"coolify context verify",
"coolify app logs <uuid> --follow",
"Full command and parameter catalog: ./llms-full.txt",
} {
if !strings.Contains(got, want) {
t.Fatalf("expected quick llms output to contain %q\nfull output:\n%s", want, got)
}
}
}
func TestWriteLLMsArtifactsWritesQuickAndFullFiles(t *testing.T) {
tempDir := t.TempDir()
quickPath := filepath.Join(tempDir, "llms.txt")
fullPath := filepath.Join(tempDir, "nested", "llms-full.txt")
if err := writeLLMsArtifacts(quickPath, fullPath); err != nil {
t.Fatalf("writeLLMsArtifacts() error = %v", err)
}
quickContent, err := os.ReadFile(quickPath)
if err != nil {
t.Fatalf("failed reading quick file: %v", err)
}
fullContent, err := os.ReadFile(fullPath)
if err != nil {
t.Fatalf("failed reading full file: %v", err)
}
for _, want := range []struct {
content string
substr string
}{
{string(quickContent), "./nested/llms-full.txt"},
{string(fullContent), "../llms.txt"},
{string(fullContent), "## Command Reference"},
} {
if !strings.Contains(want.content, want.substr) {
t.Fatalf("expected generated content to contain %q", want.substr)
}
}
}
+2 -1
View File
@@ -32,7 +32,8 @@ func NewGetCommand() *cobra.Command {
return fmt.Errorf("failed to get service: %w", err)
}
formatter, err := output.NewFormatter("table", output.Options{})
format, _ := cmd.Flags().GetString("format")
formatter, err := output.NewFormatter(format, output.Options{})
if err != nil {
return fmt.Errorf("failed to create formatter: %w", err)
}
+2 -1
View File
@@ -30,7 +30,8 @@ func NewListCommand() *cobra.Command {
return fmt.Errorf("failed to list services: %w", err)
}
formatter, err := output.NewFormatter("table", output.Options{})
format, _ := cmd.Flags().GetString("format")
formatter, err := output.NewFormatter(format, output.Options{})
if err != nil {
return fmt.Errorf("failed to create formatter: %w", err)
}
-57
View File
@@ -1,57 +0,0 @@
#!/bin/bash
set -e
echo "🔧 Setting up Coolify CLI workspace..."
# Check if Go is installed
if ! command -v go &> /dev/null; then
echo "❌ Error: Go is not installed"
echo "Please install Go 1.24+ from https://go.dev/dl/"
exit 1
fi
# Check Go version
GO_VERSION=$(go version | awk '{print $3}' | sed 's/go//')
MAJOR_MINOR=$(echo $GO_VERSION | cut -d. -f1,2)
# Compare version (must be 1.24 or higher)
if [ $(echo "$MAJOR_MINOR" | awk -F. '{print ($1 * 100) + $2}') -lt 124 ]; then
echo "❌ Error: Go version 1.24+ is required"
echo "Current version: $GO_VERSION"
echo "Please upgrade Go from https://go.dev/dl/"
exit 1
fi
echo "✅ Go version $GO_VERSION detected"
# Download dependencies
echo "📦 Downloading dependencies..."
if ! go mod download; then
echo "❌ Error: Failed to download dependencies"
exit 1
fi
echo "✅ Dependencies downloaded"
# Install air if not already installed
if ! command -v air &> /dev/null; then
echo "📦 Installing air (Go file watcher)..."
if ! go install github.com/air-verse/air@latest; then
echo "⚠️ Warning: Failed to install air, but continuing..."
else
echo "✅ air installed successfully"
fi
else
echo "✅ air already installed"
fi
# Build the binary
echo "🔨 Building coolify binary..."
if ! go build -o coolify ./coolify; then
echo "❌ Error: Build failed"
exit 1
fi
echo "✅ Binary built successfully: ./coolify/coolify"
echo "🎉 Workspace setup complete!"
echo "🔥 Use the run script for hot reload during development"
-7
View File
@@ -1,7 +0,0 @@
{
"scripts": {
"setup": "./conductor-setup.sh",
"run": "~/go/bin/air"
},
"runScriptMode": "nonconcurrent"
}
+1 -1
View File
@@ -15,7 +15,7 @@ import (
// Version variables injected by GoReleaser at build time via ldflags
var (
version = "v1.6.0"
version = "v1.6.1"
)
// GitHubAPIURL is the URL for fetching CLI version tags (exported for testing)
+2255
View File
File diff suppressed because it is too large Load Diff
+67 -2184
View File
File diff suppressed because it is too large Load Diff