⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ workspace: your-workspace-id
### Environment Variables

```bash
export CTRLPLANE_URL="https://app.ctrlplane.dev"
export CTRLPLANE_API_KEY="your-api-key-here"
export CTRLPLANE_WORKSPACE="your-workspace-id"
export CTRLPLANE_CLUSTER_IDENTIFIER="my-cluster"
export CTRLC_URL="https://app.ctrlplane.dev"
export CTRLC_API_KEY="your-api-key-here"
export CTRLC_WORKSPACE="your-workspace-id"
export CTRLC_CLUSTER_IDENTIFIER="my-cluster"
```

Any flag or config key can also be set by prefixing it with `CTRLC_` and replacing `-` or `.` with `_`.

### Command-Line Flags

```bash
Expand Down Expand Up @@ -407,7 +409,7 @@ The CLI includes GitHub Actions for CI/CD workflows. See the `actions/` director
- uses: ctrlplanedev/cli/actions/get-resource@main
with:
resource-id: ${{ env.RESOURCE_ID }}
api-key: ${{ secrets.CTRLPLANE_API_KEY }}
api-key: ${{ secrets.CTRLC_API_KEY }}
```

## Docker
Expand Down
25 changes: 18 additions & 7 deletions cmd/ctrlc/ctrlc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"os"
"strings"

"github.com/charmbracelet/log"
"github.com/ctrlplanedev/cli/cmd/ctrlc/root"
Expand All @@ -16,23 +17,28 @@ var (
)

func init() {
viper.SetEnvPrefix("CTRLC")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
viper.AutomaticEnv()

cobra.OnInitialize(initConfig)
cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "Config file (default is $HOME/.ctrlc.yaml)")
viper.BindEnv("config", "CTRLPLANE_CONFIG")
viper.BindPFlag("config", cmd.PersistentFlags().Lookup("config"))
viper.BindEnv("config", "CTRLC_CONFIG", "CTRLPLANE_CONFIG")

cmd.PersistentFlags().String("url", "https://app.ctrlplane.dev", "API URL")
viper.BindPFlag("url", cmd.PersistentFlags().Lookup("url"))
viper.BindEnv("url", "CTRLPLANE_URL")
viper.BindEnv("url", "CTRLC_URL", "CTRLPLANE_URL")

cmd.PersistentFlags().String("api-key", "", "API key for authentication")
viper.BindPFlag("api-key", cmd.PersistentFlags().Lookup("api-key"))
viper.BindEnv("api-key", "CTRLPLANE_API_KEY")
viper.BindEnv("api-key", "CTRLC_API_KEY", "CTRLPLANE_API_KEY")

cmd.PersistentFlags().String("workspace", "", "Ctrlplane Workspace ID")
viper.BindPFlag("workspace", cmd.PersistentFlags().Lookup("workspace"))
viper.BindEnv("workspace", "CTRLPLANE_WORKSPACE")
viper.BindEnv("workspace", "CTRLC_WORKSPACE", "CTRLPLANE_WORKSPACE")

viper.BindEnv("cluster-identifier", "CTRLPLANE_CLUSTER_IDENTIFIER")
viper.BindEnv("cluster-identifier", "CTRLC_CLUSTER_IDENTIFIER", "CTRLPLANE_CLUSTER_IDENTIFIER")
}

func main() {
Expand All @@ -42,8 +48,13 @@ func main() {
}

func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
configFile := cfgFile
if configFile == "" {
configFile = viper.GetString("config")
}

if configFile != "" {
viper.SetConfigFile(configFile)
} else {
// Find home directory.
home, err := homedir.Dir()
Expand Down
24 changes: 8 additions & 16 deletions cmd/ctrlc/root/root.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package root

import (
"os"

"github.com/MakeNowJust/heredoc/v2"

"github.com/charmbracelet/log"
Expand All @@ -14,11 +12,10 @@ import (
"github.com/ctrlplanedev/cli/cmd/ctrlc/root/sync"
"github.com/ctrlplanedev/cli/cmd/ctrlc/root/version"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func NewRootCmd() *cobra.Command {
var logLevel string

cmd := &cobra.Command{
Use: "ctrlc <command> <subcommand> [subcommand] [flags]",
Short: "Ctrlplane CLI",
Expand All @@ -28,6 +25,11 @@ func NewRootCmd() *cobra.Command {
$ ctrlc connect <agent-name>
`),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
logLevel := viper.GetString("log-level")
if logLevel == "" {
logLevel = "info"
}

switch logLevel {
case "debug":
log.SetLevel(log.DebugLevel)
Expand All @@ -45,7 +47,8 @@ func NewRootCmd() *cobra.Command {
},
}

cmd.PersistentFlags().StringVar(&logLevel, "log-level", defaultOrEnv("info", "CTRLC_LOG_LEVEL"), "Set the logging level (debug, info, warn, error)")
cmd.PersistentFlags().String("log-level", "info", "Set the logging level (debug, info, warn, error)")
viper.BindPFlag("log-level", cmd.PersistentFlags().Lookup("log-level"))

cmd.AddCommand(agent.NewAgentCmd())
cmd.AddCommand(api.NewAPICmd())
Expand All @@ -58,14 +61,3 @@ func NewRootCmd() *cobra.Command {

return cmd
}

func defaultOrEnv(defaultValue string, envVarName string) string {
if envVarName == "" {
return defaultValue
}
value, set := os.LookupEnv(envVarName)
if !set {
value = defaultValue
}
return value
}
4 changes: 2 additions & 2 deletions cmd/ctrlc/root/sync/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func NewSyncHelmCmd() *cobra.Command {
}

cmd.Flags().StringVarP(&providerName, "provider", "p", "", "Name of the resource provider")
cmd.Flags().StringVarP(&clusterIdentifier, "cluster-identifier", "c", "", "The identifier of the parent cluster in ctrlplane (if not provided, will use the CLUSTER_IDENTIFIER environment variable)")
cmd.Flags().StringVarP(&clusterIdentifier, "cluster-identifier", "c", "", "The identifier of the parent cluster in ctrlplane (if not provided, will use the CTRLC_CLUSTER_IDENTIFIER environment variable)")
cmd.Flags().StringVarP(&clusterName, "cluster-name", "n", "", "The name of the cluster")
cmd.Flags().StringVar(&namespace, "namespace", "", "Kubernetes namespace to sync Helm releases from (if not provided, syncs from all namespaces)")

Expand All @@ -165,7 +165,7 @@ func initializeCtrlplaneClient() (*api.ClientWithResponses, string, error) {

// resolveClusterConfig determines the cluster name and identifier from multiple sources:
// 1. Explicit --cluster-identifier flag (takes precedence)
// 2. Environment variable CLUSTER_IDENTIFIER
// 2. Environment variable CTRLC_CLUSTER_IDENTIFIER
// 3. Kubeconfig current-context (fallback)
//
// If a cluster identifier is provided, we try to fetch the cluster resource from Ctrlplane
Expand Down
2 changes: 1 addition & 1 deletion cmd/ctrlc/root/sync/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func NewSyncKubernetesCmd() *cobra.Command {
},
}
cmd.Flags().StringVarP(&providerName, "provider", "p", "", "Name of the resource provider")
cmd.Flags().StringVarP(&clusterIdentifier, "cluster-identifier", "c", "", "The identifier of the parent cluster in ctrlplane (if not provided, will use the CLUSTER_IDENTIFIER environment variable)")
cmd.Flags().StringVarP(&clusterIdentifier, "cluster-identifier", "c", "", "The identifier of the parent cluster in ctrlplane (if not provided, will use the CTRLC_CLUSTER_IDENTIFIER environment variable)")
cmd.Flags().StringVarP(&clusterName, "cluster-name", "n", "", "The name of the cluster")

return cmd
Expand Down
4 changes: 2 additions & 2 deletions cmd/ctrlc/root/sync/kubernetes/vcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func NewSyncVclusterCmd() *cobra.Command {
}

if clusterIdentifier == "" {
return fmt.Errorf("cluster identifier is required, please set the CTRLPLANE_CLUSTER_IDENTIFIER environment variable or use the --cluster-identifier flag")
return fmt.Errorf("cluster identifier is required, please set the CTRLC_CLUSTER_IDENTIFIER environment variable or use the --cluster-identifier flag")
}

ctrlplaneClient, err := api.NewAPIKeyClientWithResponses(apiURL, apiKey)
Expand Down Expand Up @@ -255,7 +255,7 @@ func NewSyncVclusterCmd() *cobra.Command {
},
}

cmd.Flags().StringVarP(&clusterIdentifier, "cluster-identifier", "c", "", "The identifier of the parent cluster in ctrlplane (if not provided, will use the CLUSTER_IDENTIFIER environment variable)")
cmd.Flags().StringVarP(&clusterIdentifier, "cluster-identifier", "c", "", "The identifier of the parent cluster in ctrlplane (if not provided, will use the CTRLC_CLUSTER_IDENTIFIER environment variable)")
cmd.Flags().StringVarP(&providerName, "provider", "p", "", "The name of the resource provider (optional)")

return cmd
Expand Down
8 changes: 4 additions & 4 deletions cmd/ctrlc/root/sync/salesforce/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ Sync Salesforce CRM data (Accounts, Opportunities) into Ctrlplane as resources.

```bash
# Set credentials (via environment or flags)
export SALESFORCE_DOMAIN="https://mycompany.my.salesforce.com"
export SALESFORCE_CONSUMER_KEY="your-key"
export SALESFORCE_CONSUMER_SECRET="your-secret"
export CTRLC_SALESFORCE_DOMAIN="https://mycompany.my.salesforce.com"
export CTRLC_SALESFORCE_CONSUMER_KEY="your-key"
export CTRLC_SALESFORCE_CONSUMER_SECRET="your-secret"

# Sync all accounts
ctrlc sync salesforce accounts
Expand All @@ -27,7 +27,7 @@ ctrlc sync salesforce accounts \
Requires Salesforce OAuth2 credentials from a Connected App with `api` and `refresh_token` scopes.

Credentials can be provided via:
- Environment variables: `SALESFORCE_DOMAIN`, `SALESFORCE_CONSUMER_KEY`, `SALESFORCE_CONSUMER_SECRET`
- Environment variables: `CTRLC_SALESFORCE_DOMAIN`, `CTRLC_SALESFORCE_CONSUMER_KEY`, `CTRLC_SALESFORCE_CONSUMER_SECRET`
- Command flags: `--salesforce-domain`, `--salesforce-consumer-key`, `--salesforce-consumer-secret`

## Common Flags
Expand Down
20 changes: 9 additions & 11 deletions cmd/ctrlc/root/sync/salesforce/salesforce.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,18 @@ func NewSalesforceCmd() *cobra.Command {
`),
}

cmd.PersistentFlags().String("salesforce-domain", "", "Salesforce domain (e.g., https://my-domain.my.salesforce.com) (can also be set via SALESFORCE_DOMAIN env var)")
cmd.PersistentFlags().String("salesforce-consumer-key", "", "Salesforce consumer key (can also be set via SALESFORCE_CONSUMER_KEY env var)")
cmd.PersistentFlags().String("salesforce-consumer-secret", "", "Salesforce consumer secret (can also be set via SALESFORCE_CONSUMER_SECRET env var)")
cmd.PersistentFlags().String("salesforce-domain", "", "Salesforce domain (e.g., https://my-domain.my.salesforce.com) (can also be set via CTRLC_SALESFORCE_DOMAIN env var)")
cmd.PersistentFlags().String("salesforce-consumer-key", "", "Salesforce consumer key (can also be set via CTRLC_SALESFORCE_CONSUMER_KEY env var)")
cmd.PersistentFlags().String("salesforce-consumer-secret", "", "Salesforce consumer secret (can also be set via CTRLC_SALESFORCE_CONSUMER_SECRET env var)")

viper.AutomaticEnv()

if err := viper.BindEnv("salesforce-domain", "SALESFORCE_DOMAIN"); err != nil {
panic(fmt.Errorf("failed to bind SALESFORCE_DOMAIN env var: %w", err))
if err := viper.BindEnv("salesforce-domain", "CTRLC_SALESFORCE_DOMAIN", "SALESFORCE_DOMAIN"); err != nil {
panic(fmt.Errorf("failed to bind CTRLC_SALESFORCE_DOMAIN env var: %w", err))
}
if err := viper.BindEnv("salesforce-consumer-key", "SALESFORCE_CONSUMER_KEY"); err != nil {
panic(fmt.Errorf("failed to bind SALESFORCE_CONSUMER_KEY env var: %w", err))
if err := viper.BindEnv("salesforce-consumer-key", "CTRLC_SALESFORCE_CONSUMER_KEY", "SALESFORCE_CONSUMER_KEY"); err != nil {
panic(fmt.Errorf("failed to bind CTRLC_SALESFORCE_CONSUMER_KEY env var: %w", err))
}
if err := viper.BindEnv("salesforce-consumer-secret", "SALESFORCE_CONSUMER_SECRET"); err != nil {
panic(fmt.Errorf("failed to bind SALESFORCE_CONSUMER_SECRET env var: %w", err))
if err := viper.BindEnv("salesforce-consumer-secret", "CTRLC_SALESFORCE_CONSUMER_SECRET", "SALESFORCE_CONSUMER_SECRET"); err != nil {
panic(fmt.Errorf("failed to bind CTRLC_SALESFORCE_CONSUMER_SECRET env var: %w", err))
}

if err := viper.BindPFlag("salesforce-domain", cmd.PersistentFlags().Lookup("salesforce-domain")); err != nil {
Expand Down
6 changes: 3 additions & 3 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ docker run ctrlplane/cli ctrlc [your-command]

### Required environment variables

- `CTRLPLANE_API_KEY`: Your Ctrlplane API key.
- `CTRLPLANE_URL`: The URL of your Ctrlplane instance (e.g. `https://app.ctrlplane.dev`).
- `CTRLC_API_KEY`: Your Ctrlplane API key.
- `CTRLC_URL`: The URL of your Ctrlplane instance (e.g. `https://app.ctrlplane.dev`).

### Terraform sync

Expand All @@ -35,6 +35,6 @@ In order to sync Terraform resources into Ctrlplane, you need to set the followi
- `TFE_ADDRESS` (optional): The URL of your Terraform Cloud instance (e.g. `https://app.terraform.io`). If not set, the default address (`https://app.terraform.io`) is used.

```sh
docker run -e TFE_TOKEN=my-token -e CTRLPLANE_API_KEY=my-api-key -e CTRLPLANE_URL=https://app.ctrlplane.dev \
docker run -e TFE_TOKEN=my-token -e CTRLC_API_KEY=my-api-key -e CTRLC_URL=https://app.ctrlplane.dev \
ctrlplane/cli ctrlc sync terraform --organization my-org --workspace 2a7c5560-75c9-4dbe-be74-04ee33bf8188
```