Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions cli/cmd/cache_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import (
)

func getApp(appSlugOrID string, kotsClient *kotsclient.VendorV3Client) (*types.App, error) {
cache, err := getCache()
if err != nil {
return nil, errors.Wrap(err, "initialize cache")
}

app, err := cache.GetApp(appSlugOrID)
if err == nil && app != nil {
return app, nil
Expand Down
41 changes: 41 additions & 0 deletions cli/cmd/completion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,52 @@ package cmd

import (
"bytes"
"os"
"sync"
"testing"

"github.com/spf13/cobra"
)

// TestShellCompletionsNoHome verifies that completion commands work even when
// HOME is unset or invalid (e.g., during Nix builds). This is a regression
// test for https://github.com/replicatedhq/replicated/issues/535.
func TestShellCompletionsNoHome(t *testing.T) {
// Save and clear HOME to simulate an environment where cache initialization
// would fail.
origHome := os.Getenv("HOME")
os.Unsetenv("HOME")
os.Unsetenv("XDG_CACHE_HOME")
defer func() {
os.Setenv("HOME", origHome)
}()

// Reset the lazy cache state so this test doesn't interfere with others.
cacheOnce = sync.Once{}
cacheInstance = nil
cacheErr = nil
defer func() {
cacheOnce = sync.Once{}
cacheInstance = nil
cacheErr = nil
}()

parentCmd := &cobra.Command{
Use: "replicated",
}
out := bytes.NewBufferString("")
cmd := NewCmdCompletion(out, parentCmd.Name())
parentCmd.AddCommand(cmd)

err := RunCompletion(out, cmd, []string{"bash"})
if err != nil {
t.Fatalf("Unexpected error with no HOME: %v", err)
}
if out.Len() == 0 {
t.Fatalf("Output was not written")
}
}

// This unit-test was adapted from kubectl repo
// Link: https://github.com/kubernetes/kubectl/blob/826006cdb947f80a679ff1eb3cb53f183a6a9bf2/pkg/cmd/completion/completion_test.go
func TestShellCompletions(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions cli/cmd/default_clear.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ replicated default clear app`,
}

func (r *runners) clearDefault(cmd *cobra.Command, defaultType string) error {
cache, err := getCache()
if err != nil {
return errors.Wrap(err, "initialize cache")
}

if err := cache.ClearDefault(defaultType); err != nil {
return errors.Wrap(err, "clear default")
}
Expand Down
10 changes: 9 additions & 1 deletion cli/cmd/default_clearall.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package cmd

import "github.com/spf13/cobra"
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

func (r *runners) InitDefaultClearAllCommand(parent *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Expand All @@ -22,6 +25,11 @@ replicated default clear-all`,
}

func (r *runners) clearAllDefaults(cmd *cobra.Command) error {
cache, err := getCache()
if err != nil {
return errors.Wrap(err, "initialize cache")
}

if err := cache.ClearDefault("app"); err != nil {
return err
}
Expand Down
5 changes: 5 additions & 0 deletions cli/cmd/default_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ func (r *runners) setDefault(cmd *cobra.Command, defaultType string, defaultValu
return errors.Wrap(err, "get app")
}

cache, err := getCache()
if err != nil {
return errors.Wrap(err, "initialize cache")
}

if err := cache.SetDefault(defaultType, defaultValue); err != nil {
return errors.Wrap(err, "set default in cache")
}
Expand Down
5 changes: 5 additions & 0 deletions cli/cmd/default_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ replicated default show app
}

func (r *runners) showDefault(cmd *cobra.Command, defaultType string, outputFormat string) error {
cache, err := getCache()
if err != nil {
return errors.Wrap(err, "initialize cache")
}

defaultValue, err := cache.GetDefault(defaultType)
if err != nil {
return errors.Wrap(err, "get default value")
Expand Down
7 changes: 5 additions & 2 deletions cli/cmd/enterprise_portal_preview.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,11 @@ func (r *runners) enterprisePortalPreview(cmd *cobra.Command, args []string) err
// 2. default app set via `replicated default app <slug>` (cache.DefaultApp)
// 3. REPLICATED_APP env var
appSlug := appSlugOrID
if appSlug == "" && cache != nil {
appSlug = cache.DefaultApp
if appSlug == "" {
cache, err := getCache()
if err == nil && cache != nil {
appSlug = cache.DefaultApp
}
}
if appSlug == "" {
appSlug = os.Getenv("REPLICATED_APP")
Expand Down
29 changes: 22 additions & 7 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"os"
"strings"
"sync"
"text/tabwriter"

"github.com/Masterminds/sprig/v3"
Expand Down Expand Up @@ -35,7 +36,9 @@ var (
profileNameFlag string
platformOrigin = "https://api.replicated.com/vendor"
kurlDotSHOrigin = "https://kurl.sh"
cache *replicatedcache.Cache
cacheInstance *replicatedcache.Cache
Comment thread
divolgin marked this conversation as resolved.
Outdated
cacheOnce sync.Once
cacheErr error
debugFlag bool
)

Expand All @@ -45,19 +48,26 @@ func init() {
platformOrigin = originFromEnv
}

c, err := replicatedcache.InitCache()
if err != nil {
panic(err)
}
cache = c

// Set debug mode from environment variable
if os.Getenv("REPLICATED_DEBUG") == "1" || os.Getenv("REPLICATED_DEBUG") == "true" {
debugFlag = true
version.SetDebugMode(true)
}
}

// getCache returns the singleton cache instance, initializing it on first call.
// This lazy initialization ensures that commands like 'completion' which don't
// need the cache won't fail when HOME is not writable (e.g., during Nix builds).
func getCache() (*replicatedcache.Cache, error) {
cacheOnce.Do(func() {
cacheInstance, cacheErr = replicatedcache.InitCache()
})
if cacheErr != nil {
return nil, cacheErr
}
return cacheInstance, nil
}

// RootCmd represents the base command when called without any subcommands
func GetRootCmd() *cobra.Command {
rootCmd := &cobra.Command{
Expand Down Expand Up @@ -446,6 +456,11 @@ func Execute(rootCmd *cobra.Command, stdin io.Reader, stdout io.Writer, stderr i
return errors.Wrap(err, "set up APIs")
}

cache, err := getCache()
if err != nil {
return errors.Wrap(err, "initialize cache")
}

if appSlugOrID == "" {
if cache.DefaultApp != "" {
appSlugOrID = cache.DefaultApp
Expand Down