Skip to content

Commit 475ca03

Browse files
Add porter config show/edit commands (#3544)
* feat: add porter config show/edit commands Add CLI commands to view and edit porter config file. - show: displays raw config, preserves template vars - edit: opens in $EDITOR, creates default if missing Closes #1077 Signed-off-by: Kim Christensen <kimworking@gmail.com> * docs: Update documentation Signed-off-by: Kim Christensen <kimworking@gmail.com> * fix: Remove ineffectual assignment Signed-off-by: Kim Christensen <kimworking@gmail.com> * fix: Use Fprintln instead Signed-off-by: Kim Christensen <kimworking@gmail.com> --------- Signed-off-by: Kim Christensen <kimworking@gmail.com>
1 parent 23f826d commit 475ca03

File tree

8 files changed

+408
-0
lines changed

8 files changed

+408
-0
lines changed

cmd/porter/config.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package main
2+
3+
import (
4+
"get.porter.sh/porter/pkg/porter"
5+
"github.com/spf13/cobra"
6+
)
7+
8+
func buildConfigCommands(p *porter.Porter) *cobra.Command {
9+
cmd := &cobra.Command{
10+
Use: "config",
11+
Annotations: map[string]string{"group": "meta"},
12+
Short: "Config commands",
13+
Long: "Commands for managing Porter's configuration file.",
14+
}
15+
16+
cmd.AddCommand(buildConfigShowCommand(p))
17+
cmd.AddCommand(buildConfigEditCommand(p))
18+
19+
return cmd
20+
}
21+
22+
func buildConfigShowCommand(p *porter.Porter) *cobra.Command {
23+
opts := porter.ConfigShowOptions{}
24+
25+
cmd := &cobra.Command{
26+
Use: "show",
27+
Short: "Show the config file",
28+
Long: "Show the contents of the porter configuration file.",
29+
Example: ` porter config show`,
30+
RunE: func(cmd *cobra.Command, args []string) error {
31+
return p.ConfigShow(cmd.Context(), opts)
32+
},
33+
}
34+
35+
return cmd
36+
}
37+
38+
func buildConfigEditCommand(p *porter.Porter) *cobra.Command {
39+
opts := porter.ConfigEditOptions{}
40+
41+
cmd := &cobra.Command{
42+
Use: "edit",
43+
Short: "Edit the config file",
44+
Long: `Edit the porter configuration file.
45+
If the config file does not exist, a default template will be created.
46+
47+
Uses the EDITOR environment variable to determine which editor to use.`,
48+
Example: ` porter config edit`,
49+
RunE: func(cmd *cobra.Command, args []string) error {
50+
return p.ConfigEdit(cmd.Context(), opts)
51+
},
52+
}
53+
54+
return cmd
55+
}

cmd/porter/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ Try our QuickStart https://porter.sh/quickstart to learn how to use Porter.
236236
cmd.AddCommand(buildPluginsCommands(p))
237237
cmd.AddCommand(buildCredentialsCommands(p))
238238
cmd.AddCommand(buildParametersCommands(p))
239+
cmd.AddCommand(buildConfigCommands(p))
239240
cmd.AddCommand(buildCompletionCommand(p))
240241
//use -ldflags "-X main.includeGRPCServer=true" during build to include
241242
grpcServer, _ := strconv.ParseBool(includeGRPCServer)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
title: "porter config"
3+
slug: porter_config
4+
url: /cli/porter_config/
5+
---
6+
## porter config
7+
8+
Config commands
9+
10+
### Synopsis
11+
12+
Commands for managing Porter's configuration file.
13+
14+
### Options
15+
16+
```
17+
-h, --help help for config
18+
```
19+
20+
### Options inherited from parent commands
21+
22+
```
23+
--experimental strings Comma separated list of experimental features to enable. See https://porter.sh/configuration/#experimental-feature-flags for available feature flags.
24+
--verbosity string Threshold for printing messages to the console. Available values are: debug, info, warning, error. (default "info")
25+
```
26+
27+
### SEE ALSO
28+
29+
* [porter](/cli/porter/) - With Porter you can package your application artifact, client tools, configuration and deployment logic together as a versioned bundle that you can distribute, and then install with a single command.
30+
31+
Most commands require a Docker daemon, either local or remote.
32+
33+
Try our QuickStart https://porter.sh/quickstart to learn how to use Porter.
34+
35+
* [porter config edit](/cli/porter_config_edit/) - Edit the config file
36+
* [porter config show](/cli/porter_config_show/) - Show the config file
37+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
title: "porter config edit"
3+
slug: porter_config_edit
4+
url: /cli/porter_config_edit/
5+
---
6+
## porter config edit
7+
8+
Edit the config file
9+
10+
### Synopsis
11+
12+
Edit the porter configuration file.
13+
If the config file does not exist, a default template will be created.
14+
15+
Uses the EDITOR environment variable to determine which editor to use.
16+
17+
```
18+
porter config edit [flags]
19+
```
20+
21+
### Examples
22+
23+
```
24+
porter config edit
25+
```
26+
27+
### Options
28+
29+
```
30+
-h, --help help for edit
31+
```
32+
33+
### Options inherited from parent commands
34+
35+
```
36+
--experimental strings Comma separated list of experimental features to enable. See https://porter.sh/configuration/#experimental-feature-flags for available feature flags.
37+
--verbosity string Threshold for printing messages to the console. Available values are: debug, info, warning, error. (default "info")
38+
```
39+
40+
### SEE ALSO
41+
42+
* [porter config](/cli/porter_config/) - Config commands
43+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: "porter config show"
3+
slug: porter_config_show
4+
url: /cli/porter_config_show/
5+
---
6+
## porter config show
7+
8+
Show the config file
9+
10+
### Synopsis
11+
12+
Show the contents of the porter configuration file.
13+
14+
```
15+
porter config show [flags]
16+
```
17+
18+
### Examples
19+
20+
```
21+
porter config show
22+
```
23+
24+
### Options
25+
26+
```
27+
-h, --help help for show
28+
```
29+
30+
### Options inherited from parent commands
31+
32+
```
33+
--experimental strings Comma separated list of experimental features to enable. See https://porter.sh/configuration/#experimental-feature-flags for available feature flags.
34+
--verbosity string Threshold for printing messages to the console. Available values are: debug, info, warning, error. (default "info")
35+
```
36+
37+
### SEE ALSO
38+
39+
* [porter config](/cli/porter_config/) - Config commands
40+

docs/content/docs/references/cli/porter.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ porter [flags]
4040
* [porter build](/cli/porter_build/) - Build a bundle
4141
* [porter bundles](/cli/porter_bundles/) - Bundle commands
4242
* [porter completion](/cli/porter_completion/) - Generate completion script
43+
* [porter config](/cli/porter_config/) - Config commands
4344
* [porter copy](/cli/porter_copy/) - Copy a bundle
4445
* [porter create](/cli/porter_create/) - Create a bundle
4546
* [porter credentials](/cli/porter_credentials/) - Credentials commands

pkg/porter/config_file.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package porter
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"path/filepath"
7+
8+
"get.porter.sh/porter/pkg"
9+
"get.porter.sh/porter/pkg/editor"
10+
"get.porter.sh/porter/pkg/tracing"
11+
)
12+
13+
// ConfigShowOptions are the options for the ConfigShow command.
14+
type ConfigShowOptions struct{}
15+
16+
// ConfigEditOptions are the options for the ConfigEdit command.
17+
type ConfigEditOptions struct{}
18+
19+
// defaultConfigTemplate is the default TOML template for a new config file.
20+
const defaultConfigTemplate = `# Porter configuration
21+
# https://porter.sh/configuration/
22+
23+
# verbosity = "info"
24+
# namespace = ""
25+
# default-storage-plugin = "mongodb-docker"
26+
# default-secrets-plugin = "host"
27+
`
28+
29+
// GetConfigFilePath returns the path to the porter config file.
30+
// It checks for config files with extensions: toml, yaml, yml, json, hcl.
31+
// Returns the path, whether the file exists, and any error.
32+
func (p *Porter) GetConfigFilePath() (string, bool, error) {
33+
home, err := p.GetHomeDir()
34+
if err != nil {
35+
return "", false, fmt.Errorf("could not get porter home directory: %w", err)
36+
}
37+
38+
extensions := []string{"toml", "yaml", "yml", "json", "hcl"}
39+
for _, ext := range extensions {
40+
path := filepath.Join(home, "config."+ext)
41+
exists, err := p.FileSystem.Exists(path)
42+
if err != nil {
43+
return "", false, fmt.Errorf("could not check if config file exists: %w", err)
44+
}
45+
if exists {
46+
return path, true, nil
47+
}
48+
}
49+
50+
// Default to toml if no config file exists
51+
return filepath.Join(home, "config.toml"), false, nil
52+
}
53+
54+
// ConfigShow displays the contents of the porter config file.
55+
func (p *Porter) ConfigShow(ctx context.Context, opts ConfigShowOptions) error {
56+
_, span := tracing.StartSpan(ctx)
57+
defer span.EndSpan()
58+
59+
path, exists, err := p.GetConfigFilePath()
60+
if err != nil {
61+
return span.Error(err)
62+
}
63+
64+
if !exists {
65+
fmt.Fprintln(p.Out, "No configuration file found.")
66+
fmt.Fprintln(p.Out, "Use 'porter config edit' to create one.")
67+
return nil
68+
}
69+
70+
contents, err := p.FileSystem.ReadFile(path)
71+
if err != nil {
72+
return span.Error(fmt.Errorf("could not read config file %s: %w", path, err))
73+
}
74+
75+
fmt.Fprintln(p.Out, string(contents))
76+
return nil
77+
}
78+
79+
// ConfigEdit opens the porter config file in an editor.
80+
// If the file does not exist, it creates a default template first.
81+
func (p *Porter) ConfigEdit(ctx context.Context, opts ConfigEditOptions) error {
82+
ctx, span := tracing.StartSpan(ctx)
83+
defer span.EndSpan()
84+
85+
path, exists, err := p.GetConfigFilePath()
86+
if err != nil {
87+
return span.Error(err)
88+
}
89+
90+
var contents []byte
91+
if exists {
92+
contents, err = p.FileSystem.ReadFile(path)
93+
if err != nil {
94+
return span.Error(fmt.Errorf("could not read config file %s: %w", path, err))
95+
}
96+
} else {
97+
contents = []byte(defaultConfigTemplate)
98+
}
99+
100+
ed := editor.New(p.Context, "porter-config.toml", contents)
101+
output, err := ed.Run(ctx)
102+
if err != nil {
103+
return span.Error(fmt.Errorf("unable to open editor: %w", err))
104+
}
105+
106+
// Ensure the directory exists
107+
dir := filepath.Dir(path)
108+
if err := p.FileSystem.MkdirAll(dir, pkg.FileModeDirectory); err != nil {
109+
return span.Error(fmt.Errorf("could not create config directory %s: %w", dir, err))
110+
}
111+
112+
if err := p.FileSystem.WriteFile(path, output, pkg.FileModeWritable); err != nil {
113+
return span.Error(fmt.Errorf("could not write config file %s: %w", path, err))
114+
}
115+
116+
return nil
117+
}

0 commit comments

Comments
 (0)