Skip to content

Commit 10c1411

Browse files
[breaking] cli: format json always start with a json object (#2407)
--------- Co-authored-by: Cristian Maglie <[email protected]>
1 parent ae24226 commit 10c1411

File tree

19 files changed

+537
-467
lines changed

19 files changed

+537
-467
lines changed

Diff for: docs/UPGRADING.md

+51
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,57 @@ Here you can find a list of migration guides to handle breaking changes between
44

55
## 0.36.0
66

7+
### CLI changed JSON output for some `lib`, `core`, `config`, `board`, and `sketch` commands.
8+
9+
- `arduino-cli lib list --format json` results are now wrapped under `installed_libraries` key
10+
11+
```
12+
{ "installed_libraries": [ {...}, {...} ] }
13+
```
14+
15+
- `arduino-cli lib examples --format json` results are now wrapped under `examples` key
16+
17+
```
18+
{ "examples": [ {...}, {...} ] }
19+
```
20+
21+
- `arduino-cli core search --format json` and `arduino-cli core list --format json` results are now wrapped under
22+
`platforms` key
23+
24+
```
25+
{ "platforms": [ {...}, {...} ] }
26+
```
27+
28+
- `arduino-cli config init --format json` now correctly returns a json object containg the config path
29+
30+
```
31+
{ "config_path": "/home/user/.arduino15/arduino-cli.yaml" }
32+
```
33+
34+
- `arduino-cli config dump --format json` results are now wrapped under `config` key
35+
36+
```
37+
{ "config": { ... } }
38+
```
39+
40+
- `arduino-cli board search --format json` results are now wrapped under `boards` key
41+
42+
```
43+
{ "boards": [ {...}, {...} ] }
44+
```
45+
46+
- `arduino-cli board list --format json` results are now wrapped under `detected_ports` key
47+
48+
```
49+
{ "detected_ports": [ {...}, {...} ] }
50+
```
51+
52+
- `arduino-cli sketch new` now correctly returns a json object containing the sketch path
53+
54+
```
55+
{ "sketch_path": "/tmp/my_sketch" }
56+
```
57+
758
### The gRPC `cc.arduino.cli.commands.v1.PlatformRelease` has been changed.
859

960
We've added a new field called `compatible`. This field indicates if the current platform release is installable or not.

Diff for: internal/cli/board/list.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -114,27 +114,27 @@ func watchList(inst *rpc.Instance) {
114114
// output from this command requires special formatting, let's create a dedicated
115115
// feedback.Result implementation
116116
type listResult struct {
117-
ports []*result.DetectedPort
117+
Ports []*result.DetectedPort `json:"detected_ports"`
118118
}
119119

120120
func (dr listResult) Data() interface{} {
121-
return dr.ports
121+
return dr
122122
}
123123

124124
func (dr listResult) String() string {
125-
if len(dr.ports) == 0 {
125+
if len(dr.Ports) == 0 {
126126
return tr("No boards found.")
127127
}
128128

129-
sort.Slice(dr.ports, func(i, j int) bool {
130-
x, y := dr.ports[i].Port, dr.ports[j].Port
129+
sort.Slice(dr.Ports, func(i, j int) bool {
130+
x, y := dr.Ports[i].Port, dr.Ports[j].Port
131131
return x.Protocol < y.Protocol ||
132132
(x.Protocol == y.Protocol && x.Address < y.Address)
133133
})
134134

135135
t := table.New()
136136
t.SetHeader(tr("Port"), tr("Protocol"), tr("Type"), tr("Board Name"), tr("FQBN"), tr("Core"))
137-
for _, detectedPort := range dr.ports {
137+
for _, detectedPort := range dr.Ports {
138138
port := detectedPort.Port
139139
protocol := port.Protocol
140140
address := port.Address

Diff for: internal/cli/board/listall.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323

2424
"github.com/arduino/arduino-cli/commands/board"
2525
"github.com/arduino/arduino-cli/internal/cli/feedback"
26-
fResult "github.com/arduino/arduino-cli/internal/cli/feedback/result"
26+
"github.com/arduino/arduino-cli/internal/cli/feedback/result"
2727
"github.com/arduino/arduino-cli/internal/cli/instance"
2828
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2929
"github.com/arduino/arduino-cli/table"
@@ -64,13 +64,13 @@ func runListAllCommand(cmd *cobra.Command, args []string) {
6464
feedback.Fatal(tr("Error listing boards: %v", err), feedback.ErrGeneric)
6565
}
6666

67-
feedback.PrintResult(resultAll{fResult.NewBoardListAllResponse(list)})
67+
feedback.PrintResult(resultAll{result.NewBoardListAllResponse(list)})
6868
}
6969

7070
// output from this command requires special formatting, let's create a dedicated
7171
// feedback.Result implementation
7272
type resultAll struct {
73-
list *fResult.BoardListAllResponse
73+
list *result.BoardListAllResponse
7474
}
7575

7676
func (dr resultAll) Data() interface{} {

Diff for: internal/cli/board/search.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424

2525
"github.com/arduino/arduino-cli/commands/board"
2626
"github.com/arduino/arduino-cli/internal/cli/feedback"
27-
fResult "github.com/arduino/arduino-cli/internal/cli/feedback/result"
27+
"github.com/arduino/arduino-cli/internal/cli/feedback/result"
2828
"github.com/arduino/arduino-cli/internal/cli/instance"
2929
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3030
"github.com/arduino/arduino-cli/table"
@@ -61,32 +61,32 @@ func runSearchCommand(cmd *cobra.Command, args []string) {
6161
feedback.Fatal(tr("Error searching boards: %v", err), feedback.ErrGeneric)
6262
}
6363

64-
feedback.PrintResult(searchResults{fResult.NewBoardListItems(res.Boards)})
64+
feedback.PrintResult(searchResults{result.NewBoardListItems(res.Boards)})
6565
}
6666

6767
// output from this command requires special formatting so we create a dedicated
6868
// feedback.Result implementation
6969
type searchResults struct {
70-
boards []*fResult.BoardListItem
70+
Boards []*result.BoardListItem `json:"boards"`
7171
}
7272

7373
func (r searchResults) Data() interface{} {
74-
return r.boards
74+
return r
7575
}
7676

7777
func (r searchResults) String() string {
78-
if len(r.boards) == 0 {
78+
if len(r.Boards) == 0 {
7979
return ""
8080
}
8181

8282
t := table.New()
8383
t.SetHeader(tr("Board Name"), tr("FQBN"), tr("Platform ID"), "")
8484

85-
sort.Slice(r.boards, func(i, j int) bool {
86-
return r.boards[i].Name < r.boards[j].Name
85+
sort.Slice(r.Boards, func(i, j int) bool {
86+
return r.Boards[i].Name < r.Boards[j].Name
8787
})
8888

89-
for _, item := range r.boards {
89+
for _, item := range r.Boards {
9090
hidden := ""
9191
if item.IsHidden {
9292
hidden = tr("(hidden)")

Diff for: internal/cli/config/add.go

+4-27
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,10 @@ import (
2525
"github.com/spf13/cobra"
2626
)
2727

28-
// // TODO: When update to go 1.18 or later, convert to generic
29-
// // to allow uniquify() on any slice that supports
30-
// // `comparable`
31-
// // See https://gosamples.dev/generics-remove-duplicates-slice/
32-
// func uniquify[T comparable](s []T) []T {
33-
// // use a map, which enforces unique keys
34-
// inResult := make(map[T]bool)
35-
// var result []T
36-
// // loop through input slice **in order**,
37-
// // to ensure output retains that order
38-
// // (except that it removes duplicates)
39-
// for i := 0; i < len(s); i++ {
40-
// // attempt to use the element as a key
41-
// if _, ok := inResult[s[i]]; !ok {
42-
// // if key didn't exist in map,
43-
// // add to map and append to result
44-
// inResult[s[i]] = true
45-
// result = append(result, s[i])
46-
// }
47-
// }
48-
// return result
49-
// }
50-
51-
func uniquifyStringSlice(s []string) []string {
28+
func uniquify[T comparable](s []T) []T {
5229
// use a map, which enforces unique keys
53-
inResult := make(map[string]bool)
54-
var result []string
30+
inResult := make(map[T]bool)
31+
var result []T
5532
// loop through input slice **in order**,
5633
// to ensure output retains that order
5734
// (except that it removes duplicates)
@@ -96,7 +73,7 @@ func runAddCommand(cmd *cobra.Command, args []string) {
9673

9774
v := configuration.Settings.GetStringSlice(key)
9875
v = append(v, args[1:]...)
99-
v = uniquifyStringSlice(v)
76+
v = uniquify(v)
10077
configuration.Settings.Set(key, v)
10178

10279
if err := configuration.Settings.WriteConfig(); err != nil {

Diff for: internal/cli/config/dump.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ func runDumpCommand(cmd *cobra.Command, args []string) {
4545
// output from this command requires special formatting, let's create a dedicated
4646
// feedback.Result implementation
4747
type dumpResult struct {
48-
data map[string]interface{}
48+
Config map[string]interface{} `json:"config"`
4949
}
5050

5151
func (dr dumpResult) Data() interface{} {
52-
return dr.data
52+
return dr
5353
}
5454

5555
func (dr dumpResult) String() string {
56-
bs, err := yaml.Marshal(dr.data)
56+
bs, err := yaml.Marshal(dr.Config)
5757
if err != nil {
5858
// Should never happen
5959
panic(tr("unable to marshal config to YAML: %v", err))

Diff for: internal/cli/config/init.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,21 @@ func runInitCommand(cmd *cobra.Command, args []string) {
108108
if err := newSettings.WriteConfigAs(configFileAbsPath.String()); err != nil {
109109
feedback.Fatal(tr("Cannot create config file: %v", err), feedback.ErrGeneric)
110110
}
111+
feedback.PrintResult(initResult{ConfigFileAbsPath: configFileAbsPath})
112+
}
113+
114+
// output from this command requires special formatting, let's create a dedicated
115+
// feedback.Result implementation
116+
type initResult struct {
117+
ConfigFileAbsPath *paths.Path `json:"config_path"`
118+
}
119+
120+
func (dr initResult) Data() interface{} {
121+
return dr
122+
}
111123

112-
msg := tr("Config file written to: %s", configFileAbsPath.String())
124+
func (dr initResult) String() string {
125+
msg := tr("Config file written to: %s", dr.ConfigFileAbsPath.String())
113126
logrus.Info(msg)
114-
feedback.Print(msg)
127+
return msg
115128
}

Diff for: internal/cli/config/set.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func runSetCommand(cmd *cobra.Command, args []string) {
5757
var value interface{}
5858
switch kind {
5959
case reflect.Slice:
60-
value = uniquifyStringSlice(args[1:])
60+
value = uniquify(args[1:])
6161
case reflect.String:
6262
value = args[1]
6363
case reflect.Bool:

Diff for: internal/cli/core/list.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -89,32 +89,32 @@ func GetList(inst *rpc.Instance, all bool, updatableOnly bool) []*rpc.PlatformSu
8989
func newCoreListResult(in []*rpc.PlatformSummary, updatableOnly bool) *coreListResult {
9090
res := &coreListResult{updatableOnly: updatableOnly}
9191
for _, platformSummary := range in {
92-
res.platforms = append(res.platforms, result.NewPlatformSummary(platformSummary))
92+
res.Platforms = append(res.Platforms, result.NewPlatformSummary(platformSummary))
9393
}
9494
return res
9595
}
9696

9797
type coreListResult struct {
98-
platforms []*result.PlatformSummary
98+
Platforms []*result.PlatformSummary `json:"platforms"`
9999
updatableOnly bool
100100
}
101101

102102
// Data implements Result interface
103103
func (ir coreListResult) Data() interface{} {
104-
return ir.platforms
104+
return ir
105105
}
106106

107107
// String implements Result interface
108108
func (ir coreListResult) String() string {
109-
if len(ir.platforms) == 0 {
109+
if len(ir.Platforms) == 0 {
110110
if ir.updatableOnly {
111111
return tr("All platforms are up to date.")
112112
}
113113
return tr("No platforms installed.")
114114
}
115115
t := table.New()
116116
t.SetHeader(tr("ID"), tr("Installed"), tr("Latest"), tr("Name"))
117-
for _, platform := range ir.platforms {
117+
for _, platform := range ir.Platforms {
118118
latestVersion := platform.LatestVersion.String()
119119
if latestVersion == "" {
120120
latestVersion = "n/a"

Diff for: internal/cli/core/search.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -86,27 +86,27 @@ func runSearchCommand(cmd *cobra.Command, args []string, allVersions bool) {
8686
// output from this command requires special formatting, let's create a dedicated
8787
// feedback.Result implementation
8888
type searchResults struct {
89-
platforms []*result.PlatformSummary
89+
Platforms []*result.PlatformSummary `json:"platforms"`
9090
allVersions bool
9191
}
9292

9393
func newSearchResult(in []*rpc.PlatformSummary, allVersions bool) *searchResults {
9494
res := &searchResults{
95-
platforms: make([]*result.PlatformSummary, len(in)),
95+
Platforms: make([]*result.PlatformSummary, len(in)),
9696
allVersions: allVersions,
9797
}
9898
for i, platformSummary := range in {
99-
res.platforms[i] = result.NewPlatformSummary(platformSummary)
99+
res.Platforms[i] = result.NewPlatformSummary(platformSummary)
100100
}
101101
return res
102102
}
103103

104104
func (sr searchResults) Data() interface{} {
105-
return sr.platforms
105+
return sr
106106
}
107107

108108
func (sr searchResults) String() string {
109-
if len(sr.platforms) == 0 {
109+
if len(sr.Platforms) == 0 {
110110
return tr("No platforms matching your search.")
111111
}
112112

@@ -121,7 +121,7 @@ func (sr searchResults) String() string {
121121
t.AddRow(platform.Id, release.Version, release.FormatName())
122122
}
123123

124-
for _, platform := range sr.platforms {
124+
for _, platform := range sr.Platforms {
125125
// When allVersions is not requested we only show the latest compatible version
126126
if !sr.allVersions {
127127
addRow(platform, platform.GetLatestRelease())

Diff for: internal/cli/lib/check_deps.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
"github.com/arduino/arduino-cli/commands/lib"
2525
"github.com/arduino/arduino-cli/internal/cli/arguments"
2626
"github.com/arduino/arduino-cli/internal/cli/feedback"
27-
fResult "github.com/arduino/arduino-cli/internal/cli/feedback/result"
27+
"github.com/arduino/arduino-cli/internal/cli/feedback/result"
2828
"github.com/arduino/arduino-cli/internal/cli/instance"
2929
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3030
"github.com/fatih/color"
@@ -66,13 +66,13 @@ func runDepsCommand(cmd *cobra.Command, args []string) {
6666
feedback.Fatal(tr("Error resolving dependencies for %[1]s: %[2]s", libRef, err), feedback.ErrGeneric)
6767
}
6868

69-
feedback.PrintResult(&checkDepResult{deps: fResult.NewLibraryResolveDependenciesResponse(deps)})
69+
feedback.PrintResult(&checkDepResult{deps: result.NewLibraryResolveDependenciesResponse(deps)})
7070
}
7171

7272
// output from this command requires special formatting, let's create a dedicated
7373
// feedback.Result implementation
7474
type checkDepResult struct {
75-
deps *fResult.LibraryResolveDependenciesResponse
75+
deps *result.LibraryResolveDependenciesResponse
7676
}
7777

7878
func (dr checkDepResult) Data() interface{} {
@@ -103,7 +103,7 @@ func (dr checkDepResult) String() string {
103103
return res
104104
}
105105

106-
func outputDep(dep *fResult.LibraryDependencyStatus) string {
106+
func outputDep(dep *result.LibraryDependencyStatus) string {
107107
res := ""
108108
green := color.New(color.FgGreen)
109109
red := color.New(color.FgRed)

Diff for: internal/cli/lib/examples.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,11 @@ type libraryExamples struct {
9494
}
9595

9696
type libraryExamplesResult struct {
97-
Examples []*libraryExamples
97+
Examples []*libraryExamples `json:"examples"`
9898
}
9999

100100
func (ir libraryExamplesResult) Data() interface{} {
101-
return ir.Examples
101+
return ir
102102
}
103103

104104
func (ir libraryExamplesResult) String() string {

0 commit comments

Comments
 (0)