Skip to content

Commit dcf188b

Browse files
authored
feat: add config dump command (#343)
Signed-off-by: Thien Trung Vuong <tvuong@microsoft.com>
1 parent f4dfe97 commit dcf188b

8 files changed

Lines changed: 242 additions & 0 deletions

File tree

internal/app/azldev/cmds/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ func OnAppInit(app *azldev.App) {
1717

1818
app.AddTopLevelCommand(cmd)
1919
generateSchemaOnAppInit(app, cmd)
20+
dumpConfigOnAppInit(app, cmd)
2021
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package config
5+
6+
import (
7+
"fmt"
8+
9+
"github.com/microsoft/azldev/internal/app/azldev"
10+
"github.com/pelletier/go-toml/v2"
11+
"github.com/spf13/cobra"
12+
"github.com/spf13/pflag"
13+
)
14+
15+
type configDumpFormat string
16+
17+
const (
18+
ConfigDumpFormatTOML configDumpFormat = "toml"
19+
)
20+
21+
// Assert that ConfigDumpFormat implements the [pflag.Value] interface.
22+
var _ pflag.Value = (*configDumpFormat)(nil)
23+
24+
func (f *configDumpFormat) String() string {
25+
return string(*f)
26+
}
27+
28+
// Parses the format from a string; used by command-line parser.
29+
func (f *configDumpFormat) Set(value string) error {
30+
switch value {
31+
case "toml":
32+
*f = ConfigDumpFormatTOML
33+
default:
34+
return fmt.Errorf("unsupported format: %#q", value)
35+
}
36+
37+
return nil
38+
}
39+
40+
// Returns a descriptive string used in command-line help.
41+
func (f *configDumpFormat) Type() string {
42+
return "fmt"
43+
}
44+
45+
// Called once when the app is initialized; registers any commands or callbacks with the app.
46+
func dumpConfigOnAppInit(_ *azldev.App, parentCmd *cobra.Command) {
47+
parentCmd.AddCommand(newDumpCmd())
48+
}
49+
50+
func newDumpCmd() *cobra.Command {
51+
configDumpFormat := ConfigDumpFormatTOML
52+
53+
cmd := &cobra.Command{
54+
Use: "dump",
55+
Short: "Dump the current configuration",
56+
RunE: azldev.RunFunc(func(env *azldev.Env) (interface{}, error) {
57+
configText, err := DumpConfig(env, configDumpFormat)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
fmt.Println(configText)
63+
64+
return "", nil
65+
}),
66+
}
67+
68+
cmd.Flags().VarP(&configDumpFormat, "format", "f", "Output format")
69+
70+
azldev.ExportAsMCPTool(cmd)
71+
72+
return cmd
73+
}
74+
75+
func DumpConfig(env *azldev.Env, format configDumpFormat) (string, error) {
76+
switch format {
77+
case ConfigDumpFormatTOML:
78+
tomlBytes, err := toml.Marshal(env.Config())
79+
if err != nil {
80+
return "", fmt.Errorf("failed to serialize config to TOML:\n%w", err)
81+
}
82+
83+
return string(tomlBytes), nil
84+
default:
85+
return "", fmt.Errorf("unsupported format: %#q", format)
86+
}
87+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package config_test
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/microsoft/azldev/internal/app/azldev"
11+
"github.com/microsoft/azldev/internal/app/azldev/cmds/config"
12+
"github.com/microsoft/azldev/internal/app/azldev/core/testutils"
13+
"github.com/microsoft/azldev/internal/projectconfig"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
func TestDumpConfig(t *testing.T) {
18+
const (
19+
testProjectRoot = "/non/existent/dir"
20+
testLogDir = "/non/existent/logs"
21+
testWorkDir = "/non/existent/work"
22+
testOutputDir = "/non/existent/output"
23+
)
24+
25+
cfg := &projectconfig.ProjectConfig{
26+
Project: projectconfig.ProjectInfo{
27+
LogDir: testLogDir,
28+
WorkDir: testWorkDir,
29+
OutputDir: testOutputDir,
30+
},
31+
}
32+
33+
ctx, cancelFunc := context.WithCancel(t.Context())
34+
defer cancelFunc()
35+
36+
testEnv := testutils.NewTestEnv(t)
37+
38+
options := azldev.NewEnvOptions()
39+
options.DryRunnable = testEnv.DryRunnable
40+
options.EventListener = testEnv.EventListener
41+
options.Interfaces = testEnv.TestInterfaces
42+
options.ProjectDir = testProjectRoot
43+
options.Config = cfg
44+
45+
env := azldev.NewEnv(ctx, options)
46+
47+
configText, err := config.DumpConfig(env, config.ConfigDumpFormatTOML)
48+
require.NoError(t, err)
49+
require.NotEmpty(t, configText)
50+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"ExitCode": 0
3+
}

scenario/__snapshots__/TestAzlDevConfigDump_stderr_1.snap

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[project]
2+
description = 'simple'
3+
log-dir = '/workdir/build/logs'
4+
work-dir = '/workdir/build/work'
5+
output-dir = '/workdir/out'
6+
7+
[component-groups]
8+
[component-groups.default]
9+
specs = ['/workdir/**/*.spec']
10+
excluded-paths = ['/workdir/build/**', '/workdir/out/**']
11+
12+
""

scenario/__snapshots__/TestMCPServerMode_1.snap.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,66 @@
7676
},
7777
"name": "component-list"
7878
},
79+
{
80+
"annotations": {
81+
"destructiveHint": true,
82+
"idempotentHint": false,
83+
"openWorldHint": true,
84+
"readOnlyHint": false
85+
},
86+
"description": "Dump the current configuration",
87+
"inputSchema": {
88+
"properties": {
89+
"accept-all": {
90+
"default": false,
91+
"description": "accept all prompts",
92+
"type": "boolean"
93+
},
94+
"color": {
95+
"description": "output colorization mode {always, auto, never}",
96+
"type": "string"
97+
},
98+
"dry-run": {
99+
"default": false,
100+
"description": "dry run only (do not take action)",
101+
"type": "boolean"
102+
},
103+
"format": {
104+
"description": "Output format",
105+
"type": "string"
106+
},
107+
"no-default-config": {
108+
"default": false,
109+
"description": "disable default configuration",
110+
"type": "boolean"
111+
},
112+
"output-format": {
113+
"description": "output format {csv, json, markdown, table}",
114+
"type": "string"
115+
},
116+
"project": {
117+
"default": "",
118+
"description": "path to Azure Linux project",
119+
"type": "string"
120+
},
121+
"quiet": {
122+
"default": false,
123+
"description": "only enable minimal output",
124+
"type": "boolean"
125+
},
126+
"verbose": {
127+
"default": false,
128+
"description": "enable verbose output",
129+
"type": "boolean"
130+
}
131+
},
132+
"required": [
133+
"project"
134+
],
135+
"type": "object"
136+
},
137+
"name": "config-dump"
138+
},
79139
{
80140
"annotations": {
81141
"destructiveHint": true,

scenario/clismoke_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,32 @@ func TestAzlDevDocsMd(t *testing.T) {
134134
expectedPath := filepath.Join(outputDir, "azldev.md")
135135
require.FileExists(t, expectedPath, "Expected markdown file")
136136
}
137+
138+
func TestAzlDevConfigDump(t *testing.T) {
139+
t.Parallel()
140+
141+
// Skip unless doing long tests
142+
if testing.Short() {
143+
t.Skip("skipping long test")
144+
}
145+
146+
config := `
147+
'$schema' = 'https://github.com/gim-home/azldev-preview/schemas/azldev.json'
148+
149+
[project]
150+
description = 'simple'
151+
log-dir = 'build/logs'
152+
work-dir = 'build/work'
153+
output-dir = 'out'
154+
155+
[component-groups]
156+
[component-groups.default]
157+
specs = ['**/*.spec']
158+
excluded-paths = ['build/**', 'out/**']
159+
`
160+
161+
// Since default config may change, we need to exclude it from the snapshot.
162+
test := cmdtest.NewScenarioTest("config", "dump", "--no-default-config").AddFileContents("azldev.toml", strings.NewReader(config)).InContainer()
163+
164+
snapshot.TestSnapshottableCmd(t, test)
165+
}

0 commit comments

Comments
 (0)