feat: add json output to func run (#2893)

This commit is contained in:
Luke Kingland 2025-06-30 17:02:10 +09:00 committed by GitHub
parent 69bdcbbecd
commit 56e1b0f7f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 51 additions and 4 deletions

View File

@ -2,6 +2,7 @@ package cmd
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
@ -28,7 +29,7 @@ NAME
SYNOPSIS
{{rootCmdUse}} run [-t|--container] [-r|--registry] [-i|--image] [-e|--env]
[--build] [-b|--builder] [--builder-image] [-c|--confirm]
[--address] [-v|--verbose]
[--address] [--json] [-v|--verbose]
DESCRIPTION
Run the function locally.
@ -71,9 +72,12 @@ EXAMPLES
o Run the function locally on a specific address.
$ {{rootCmdUse}} run --address=0.0.0.0:8081
o Run the function locally and output JSON with the service address.
$ {{rootCmdUse}} run --json
`,
SuggestFor: []string{"rnu"},
PreRunE: bindEnv("address", "build", "builder", "builder-image", "confirm", "container", "env", "image", "path", "registry", "start-timeout", "verbose"),
PreRunE: bindEnv("build", "builder", "builder-image", "confirm", "container", "env", "image", "path", "registry", "start-timeout", "verbose", "address", "json"),
RunE: func(cmd *cobra.Command, _ []string) error {
return runRun(cmd, newClient)
},
@ -129,6 +133,7 @@ EXAMPLES
cmd.Flags().Lookup("build").NoOptDefVal = "true" // register `--build` as equivalient to `--build=true`
cmd.Flags().String("address", "",
"Interface and port on which to bind and listen. Default is 127.0.0.1:8080, or an available port if 8080 is not available. ($FUNC_ADDRESS)")
cmd.Flags().Bool("json", false, "Output as JSON. ($FUNC_JSON)")
// Oft-shared flags:
addConfirmFlag(cmd, cfg.Confirm)
@ -171,6 +176,11 @@ func runRun(cmd *cobra.Command, newClient ClientFactory) (err error) {
return
}
// Ignore the verbose flag if JSON output
if cfg.JSON {
cfg.Verbose = false
}
// Client
clientOptions, err := cfg.clientOptions()
if err != nil {
@ -249,7 +259,27 @@ func runRun(cmd *cobra.Command, newClient ClientFactory) (err error) {
}
}()
fmt.Fprintf(cmd.OutOrStderr(), "Running on host port %v\n", job.Port)
// Output based on format
if cfg.JSON {
// Create JSON output structure
output := struct {
Address string `json:"address"`
Host string `json:"host"`
Port string `json:"port"`
}{
Address: fmt.Sprintf("http://%s:%s", job.Host, job.Port),
Host: job.Host,
Port: job.Port,
}
jsonData, err := json.Marshal(output)
if err != nil {
return fmt.Errorf("failed to marshal JSON output: %w", err)
}
fmt.Fprintln(cmd.OutOrStdout(), string(jsonData))
} else {
fmt.Fprintf(cmd.OutOrStderr(), "Running on host port %v\n", job.Port)
}
select {
case <-cmd.Context().Done():
@ -293,6 +323,9 @@ type runConfig struct {
// Address is the interface and port to bind (e.g. "0.0.0.0:8081")
Address string
// JSON output format
JSON bool
}
func newRunConfig(cmd *cobra.Command) (c runConfig) {
@ -303,6 +336,7 @@ func newRunConfig(cmd *cobra.Command) (c runConfig) {
Container: viper.GetBool("container"),
StartTimeout: viper.GetDuration("start-timeout"),
Address: viper.GetString("address"),
JSON: viper.GetBool("json"),
}
// NOTE: .Env should be viper.GetStringSlice, but this returns unparsed
// results and appears to be an open issue since 2017:

View File

@ -21,6 +21,7 @@ func TestRun_Run(t *testing.T) {
runError error // Set the runner to yield this error
buildInvoked bool // should Builder.Build be invoked?
runInvoked bool // should Runner.Run be invoked?
jsonOutput bool // expect JSON output format
}{
{
name: "run and build by default",
@ -100,6 +101,14 @@ func TestRun_Run(t *testing.T) {
buildInvoked: true,
runInvoked: false,
},
{
name: "run with json output",
desc: "Should output JSON format when --json flag is used",
args: []string{"--json"},
buildInvoked: true,
runInvoked: true,
jsonOutput: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -11,7 +11,7 @@ NAME
SYNOPSIS
func run [-t|--container] [-r|--registry] [-i|--image] [-e|--env]
[--build] [-b|--builder] [--builder-image] [-c|--confirm]
[--address] [-v|--verbose]
[--address] [--json] [-v|--verbose]
DESCRIPTION
Run the function locally.
@ -55,6 +55,9 @@ EXAMPLES
o Run the function locally on a specific address.
$ func run --address=0.0.0.0:8081
o Run the function locally and output JSON with the service address.
$ func run --json
```
func run
@ -72,6 +75,7 @@ func run
-e, --env stringArray Environment variable to set in the form NAME=VALUE. You may provide this flag multiple times for setting multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-).
-h, --help help for run
-i, --image string Full image name in the form [registry]/[namespace]/[name]:[tag]. This option takes precedence over --registry. Specifying tag is optional. ($FUNC_IMAGE)
--json Output as JSON. ($FUNC_JSON)
-p, --path string Path to the function. Default is current directory ($FUNC_PATH)
-r, --registry string Container registry + registry namespace. (ex 'ghcr.io/myuser'). The full image name is automatically determined using this along with function name. ($FUNC_REGISTRY)
-v, --verbose Print verbose logs ($FUNC_VERBOSE)