Skip to content
Open
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
11 changes: 9 additions & 2 deletions cmd/login/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

"github.com/smartcontractkit/cre-cli/internal/creconfig"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/environments"
"github.com/smartcontractkit/cre-cli/internal/oauth"
Expand Down Expand Up @@ -57,7 +58,10 @@ func TestFetchTenantConfig_GQLError_ReturnsError(t *testing.T) {
t.Errorf("expected fetch user context error, got: %v", err)
}

contextPath := filepath.Join(tmp, credentials.ConfigDir, tenantctx.ContextFile)
contextPath, err := creconfig.FilePath(tenantctx.ContextFile)
if err != nil {
t.Fatalf("failed to resolve context path: %v", err)
}
if _, statErr := os.Stat(contextPath); statErr == nil {
t.Errorf("expected %s not to be written on fetch failure", tenantctx.ContextFile)
}
Expand Down Expand Up @@ -103,7 +107,10 @@ func TestSaveCredentials_WritesYAML(t *testing.T) {
t.Fatalf("saveCredentials error: %v", err)
}

path := filepath.Join(tmp, credentials.ConfigDir, credentials.ConfigFile)
path, err := creconfig.FilePath(credentials.ConfigFile)
if err != nil {
t.Fatalf("failed to resolve config path: %v", err)
}
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("cannot read config file: %v", err)
Expand Down
15 changes: 8 additions & 7 deletions cmd/logout/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import (
"net/http"
"net/url"
"os"
"path/filepath"

"github.com/rs/zerolog"
"github.com/spf13/cobra"

"github.com/smartcontractkit/cre-cli/internal/constants"
"github.com/smartcontractkit/cre-cli/internal/creconfig"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/environments"
"github.com/smartcontractkit/cre-cli/internal/runtime"
Expand Down Expand Up @@ -49,11 +49,10 @@ func newHandler(ctx *runtime.Context) *handler {
}

func (h *handler) execute() error {
home, err := os.UserHomeDir()
credPath, err := creconfig.FilePath(credentials.ConfigFile)
if err != nil {
return fmt.Errorf("could not determine home directory: %w", err)
return fmt.Errorf("could not determine config path: %w", err)
}
credPath := filepath.Join(home, credentials.ConfigDir, credentials.ConfigFile)

// Load credentials directly (logout is excluded from global credential loading)
creds, err := credentials.New(h.log)
Expand Down Expand Up @@ -90,11 +89,13 @@ func (h *handler) execute() error {

if err := credentials.SecureRemove(credPath); err != nil {
spinner.Stop()
return fmt.Errorf("failed to delete credentials file: %w", err)
return fmt.Errorf("failed to delete credentials file %s: %w", credPath, err)
}

contextPath := filepath.Join(home, credentials.ConfigDir, tenantctx.ContextFile)
if err := os.Remove(contextPath); err != nil && !os.IsNotExist(err) {
contextPath, err := creconfig.FilePath(tenantctx.ContextFile)
if err != nil {
h.log.Warn().Err(err).Msgf("failed to resolve %s path", tenantctx.ContextFile)
} else if err := os.Remove(contextPath); err != nil && !os.IsNotExist(err) {
h.log.Warn().Err(err).Msgf("failed to delete %s", tenantctx.ContextFile)
}

Expand Down
15 changes: 11 additions & 4 deletions cmd/logout/logout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"gopkg.in/yaml.v3"

"github.com/smartcontractkit/cre-cli/internal/creconfig"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/environments"
"github.com/smartcontractkit/cre-cli/internal/runtime"
Expand All @@ -17,8 +18,8 @@ import (

func setupCredentialFile(t *testing.T, home string, token string) {
t.Helper()
dir := filepath.Join(home, credentials.ConfigDir)
if err := os.MkdirAll(dir, 0o700); err != nil {
dir, err := creconfig.EnsureDir()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we name the variable and function a bit more explicit? It looks quite generic now.

if err != nil {
t.Fatalf("failed to create config dir: %v", err)
}
path := filepath.Join(dir, credentials.ConfigFile)
Expand Down Expand Up @@ -112,7 +113,10 @@ func TestExecute_SuccessRevocationAndRemoval(t *testing.T) {
t.Error("expected revocation request, but none received")
}

credPath := filepath.Join(tDir, credentials.ConfigDir, credentials.ConfigFile)
credPath, err := creconfig.FilePath(credentials.ConfigFile)
if err != nil {
t.Fatalf("failed to resolve credentials path: %v", err)
}
if _, err := os.Stat(credPath); !os.IsNotExist(err) {
t.Errorf("expected credentials file to be removed, but it exists")
}
Expand Down Expand Up @@ -152,7 +156,10 @@ func TestExecute_RevocationFails_StillRemovesFile(t *testing.T) {
t.Fatalf("expected no error despite revocation failure, got %v", err)
}

credPath := filepath.Join(tDir, credentials.ConfigDir, credentials.ConfigFile)
credPath, err := creconfig.FilePath(credentials.ConfigFile)
if err != nil {
t.Fatalf("failed to resolve credentials path: %v", err)
}
if _, err := os.Stat(credPath); !os.IsNotExist(err) {
t.Errorf("expected credentials file to be removed, but it exists")
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/smartcontractkit/cre-cli/cmd/workflow"
"github.com/smartcontractkit/cre-cli/internal/constants"
"github.com/smartcontractkit/cre-cli/internal/context"
"github.com/smartcontractkit/cre-cli/internal/creconfig"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/logger"
"github.com/smartcontractkit/cre-cli/internal/runtime"
Expand Down Expand Up @@ -242,7 +243,7 @@ func newRootCommand() *cobra.Command {
}
ui.ErrorWithSuggestions("Failed to load user context", []string{
"Run `cre login` to fetch your user context",
fmt.Sprintf("Ensure ~/.cre/%s exists and is readable", tenantctx.ContextFile),
fmt.Sprintf("Ensure %s exists and is readable", creconfig.FilePathHint(tenantctx.ContextFile)),
})
return fmt.Errorf("user context required: %w", err)
}
Expand Down Expand Up @@ -308,7 +309,7 @@ func newRootCommand() *cobra.Command {
}
ui.ErrorWithSuggestions("Failed to load user context", []string{
"Run `cre login` to fetch your user context",
fmt.Sprintf("Ensure ~/.cre/%s exists and is readable", tenantctx.ContextFile),
fmt.Sprintf("Ensure %s exists and is readable", creconfig.FilePathHint(tenantctx.ContextFile)),
})
return fmt.Errorf("user context required: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/secrets/common/browser_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func digestHexString(digest [32]byte) string {
// executeBrowserUpsert handles secrets create/update when the user signs in with their organization account.
// It encrypts the payload, binds a digest, requests a platform authorization URL, completes OAuth in the browser,
// exchanges the code for a short-lived vault JWT, and POSTs the same JSON-RPC body to the gateway with Bearer auth.
// Login tokens in ~/.cre/cre.yaml are not modified; that session stays separate from this vault-only token.
// Login tokens in the CLI credentials file are not modified; that session stays separate from this vault-only token.
func (h *Handler) executeBrowserUpsert(ctx context.Context, inputs UpsertSecretsInputs, method string) error {
if h.Credentials.AuthType == credentials.AuthTypeApiKey {
return fmt.Errorf("this sign-in flow requires an interactive login; API keys are not supported")
Expand Down Expand Up @@ -224,7 +224,7 @@ func (h *Handler) ExecuteBrowserVaultAuthorization(ctx context.Context, method s
})
var exchangeResp struct {
ExchangeAuthCodeToToken struct {
AccessToken string `json:"accessToken"`
AccessToken string `json:"accessToken"` // #nosec G117 -- matches OAuth token exchange response field
ExpiresIn int `json:"expiresIn"`
} `json:"exchangeAuthCodeToToken"`
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/secrets/common/gateway/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// ResolveVaultGatewayURL returns the vault gateway URL for secrets operations.
// Precedence: CRE_VAULT_DON_GATEWAY_URL env var, then context.yaml vault_gateway_url,
// Precedence: CRE_VAULT_DON_GATEWAY_URL env var, then the user context file vault_gateway_url,
// then the embedded default from EnvironmentSet.
func ResolveVaultGatewayURL(tenantCtx *tenantctx.EnvironmentContext, envSet *environments.EnvironmentSet) string {
if os.Getenv(environments.EnvVarVaultGatewayURL) != "" && envSet != nil {
Expand Down
3 changes: 2 additions & 1 deletion cmd/templates/add/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/rs/zerolog"
"github.com/spf13/cobra"

"github.com/smartcontractkit/cre-cli/internal/creconfig"
"github.com/smartcontractkit/cre-cli/internal/runtime"
"github.com/smartcontractkit/cre-cli/internal/templateconfig"
"github.com/smartcontractkit/cre-cli/internal/templaterepo"
Expand All @@ -20,7 +21,7 @@ func New(runtimeContext *runtime.Context) *cobra.Command {
return &cobra.Command{
Use: "add <owner/repo[@ref]>...",
Short: "Adds a template repository source",
Long: `Adds one or more template repository sources to ~/.cre/template.yaml. These repositories are used by cre init to discover available templates.`,
Long: fmt.Sprintf("Adds one or more template repository sources to %s. These repositories are used by cre init to discover available templates.", creconfig.FileRelPath(templateconfig.TemplateConfigFile)),
Args: cobra.MinimumNArgs(1),
Example: "cre templates add smartcontractkit/cre-templates@main myorg/my-templates",
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down
3 changes: 2 additions & 1 deletion cmd/templates/remove/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/rs/zerolog"
"github.com/spf13/cobra"

"github.com/smartcontractkit/cre-cli/internal/creconfig"
"github.com/smartcontractkit/cre-cli/internal/runtime"
"github.com/smartcontractkit/cre-cli/internal/templateconfig"
"github.com/smartcontractkit/cre-cli/internal/templaterepo"
Expand All @@ -20,7 +21,7 @@ func New(runtimeContext *runtime.Context) *cobra.Command {
return &cobra.Command{
Use: "remove <owner/repo>...",
Short: "Removes a template repository source",
Long: `Removes one or more template repository sources from ~/.cre/template.yaml. The ref portion is optional and ignored during matching.`,
Long: fmt.Sprintf("Removes one or more template repository sources from %s. The ref portion is optional and ignored during matching.", creconfig.FileRelPath(templateconfig.TemplateConfigFile)),
Args: cobra.MinimumNArgs(1),
Example: "cre templates remove smartcontractkit/cre-templates myorg/my-templates",
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down
2 changes: 1 addition & 1 deletion cmd/workflow/hash/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Inputs struct {
WorkflowName string
WorkflowPath string
OwnerFromSettings string
PrivateKey string
PrivateKey string // #nosec G117 -- CLI flag for optional signing key input
SkipTypeChecks bool
RegistryType settings.RegistryType
DerivedOwner string
Expand Down
2 changes: 1 addition & 1 deletion docs/cre_templates_add.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Adds a template repository source

### Synopsis

Adds one or more template repository sources to ~/.cre/template.yaml. These repositories are used by cre init to discover available templates.
Adds one or more template repository sources to .cre/template.yaml. These repositories are used by cre init to discover available templates.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it intentional to remove information that this file is stored by default in the hoemdir? Maybe we could write something like $HOME_DIR/.cre/template.yaml


```
cre templates add <owner/repo[@ref]>... [flags]
Expand Down
2 changes: 1 addition & 1 deletion docs/cre_templates_remove.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Removes a template repository source

### Synopsis

Removes one or more template repository sources from ~/.cre/template.yaml. The ref portion is optional and ignored during matching.
Removes one or more template repository sources from .cre/template.yaml. The ref portion is optional and ignored during matching.

```
cre templates remove <owner/repo>... [optional flags]
Expand Down
63 changes: 63 additions & 0 deletions internal/creconfig/creconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package creconfig

import (
"fmt"
"os"
"path/filepath"
)

const Dir = ".cre"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it could be beneficial to use more explicit name. Or is it against Go rules?


// DirPath returns the absolute path to the CLI config directory.
func DirPath() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("get home dir: %w", err)
}
return filepath.Join(home, Dir), nil
}

// EnsureDir creates the CLI config directory with 0700 permissions if missing.
func EnsureDir() (string, error) {
dir, err := DirPath()
if err != nil {
return "", err
}
if err := os.MkdirAll(dir, 0o700); err != nil {
return "", fmt.Errorf("create config dir: %w", err)
}
return dir, nil
}

// FilePath returns the absolute path to a file directly under the CLI config directory.
func FilePath(name string) (string, error) {
dir, err := DirPath()
if err != nil {
return "", err
}
return filepath.Join(dir, name), nil
}

// FileRelPath returns a home-relative path for static help text and committed docs.
// It uses the host OS path separator (e.g. ".cre/template.yaml" or ".cre\template.yaml").
func FileRelPath(name string) string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe we should name it FileHomeRelPath?

return filepath.Join(Dir, name)
}

// FilePathHint returns the absolute config file path for user-facing messages,
// or a home-relative path if the home directory cannot be resolved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How does it work if we can't resolve home dir?

func FilePathHint(name string) string {
if path, err := FilePath(name); err == nil {
return path
}
return FileRelPath(name)
}

// JoinPath returns an absolute path under the CLI config directory.
func JoinPath(elem ...string) (string, error) {
dir, err := DirPath()
if err != nil {
return "", err
}
return filepath.Join(append([]string{dir}, elem...)...), nil
}
Loading
Loading