feat: accept --global on :report subcommands
Every `:report` subcommand now recognizes `--global` as a scope selector that limits the report to globally-configured properties, including in JSON form via `--global --format json`. Previously this combination was rejected because `--global` was treated as an info flag, conflicting with `--format`. The shared `common.ParseReportArgs` helper now returns a `ReportArgs` struct exposing the parsed scope; each Go and bash report selects a global-only flag map when scope is global, and skips per-app verification.
This commit is contained in:
@@ -713,10 +713,18 @@ func GetenvWithDefault(key string, defaultValue string) (val string) {
|
||||
return
|
||||
}
|
||||
|
||||
// ReportArgs holds the parsed inputs to a :report subcommand
|
||||
type ReportArgs struct {
|
||||
OSArgs []string
|
||||
InfoFlag string
|
||||
IsGlobal bool
|
||||
}
|
||||
|
||||
// ParseReportArgs splits out flags from non-flags for input into report commands
|
||||
func ParseReportArgs(pluginName string, arguments []string) ([]string, string, error) {
|
||||
func ParseReportArgs(pluginName string, arguments []string) (ReportArgs, error) {
|
||||
osArgs := []string{}
|
||||
infoFlags := []string{}
|
||||
isGlobal := false
|
||||
skipNext := false
|
||||
for i, argument := range arguments {
|
||||
if skipNext {
|
||||
@@ -728,6 +736,10 @@ func ParseReportArgs(pluginName string, arguments []string) ([]string, string, e
|
||||
skipNext = true
|
||||
continue
|
||||
}
|
||||
if argument == "--global" {
|
||||
isGlobal = true
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(argument, "--") {
|
||||
infoFlags = append(infoFlags, argument)
|
||||
} else {
|
||||
@@ -736,12 +748,12 @@ func ParseReportArgs(pluginName string, arguments []string) ([]string, string, e
|
||||
}
|
||||
|
||||
if len(infoFlags) == 0 {
|
||||
return osArgs, "", nil
|
||||
return ReportArgs{OSArgs: osArgs, IsGlobal: isGlobal}, nil
|
||||
}
|
||||
if len(infoFlags) == 1 {
|
||||
return osArgs, infoFlags[0], nil
|
||||
return ReportArgs{OSArgs: osArgs, InfoFlag: infoFlags[0], IsGlobal: isGlobal}, nil
|
||||
}
|
||||
return osArgs, "", fmt.Errorf("%s:report command allows only a single flag", pluginName)
|
||||
return ReportArgs{OSArgs: osArgs, IsGlobal: isGlobal}, fmt.Errorf("%s:report command allows only a single flag", pluginName)
|
||||
}
|
||||
|
||||
// ParseScaleOutput allows golang plugins to properly parse the output of ps-current-scale
|
||||
|
||||
@@ -115,3 +115,81 @@ func TestCommonStripInlineComments(t *testing.T) {
|
||||
text := StripInlineComments(strings.Join([]string{testEnvLine, "# testing comment"}, " "))
|
||||
Expect(text).To(Equal(testEnvLine))
|
||||
}
|
||||
|
||||
func TestParseReportArgsEmpty(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
args, err := ParseReportArgs("scheduler", []string{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(args.IsGlobal).To(BeFalse())
|
||||
Expect(args.InfoFlag).To(Equal(""))
|
||||
Expect(args.OSArgs).To(BeEmpty())
|
||||
}
|
||||
|
||||
func TestParseReportArgsAppOnly(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
args, err := ParseReportArgs("scheduler", []string{"myapp"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(args.IsGlobal).To(BeFalse())
|
||||
Expect(args.InfoFlag).To(Equal(""))
|
||||
Expect(args.OSArgs).To(Equal([]string{"myapp"}))
|
||||
}
|
||||
|
||||
func TestParseReportArgsFormatJSON(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
args, err := ParseReportArgs("scheduler", []string{"myapp", "--format", "json"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(args.IsGlobal).To(BeFalse())
|
||||
Expect(args.InfoFlag).To(Equal(""))
|
||||
Expect(args.OSArgs).To(Equal([]string{"myapp", "--format", "json"}))
|
||||
}
|
||||
|
||||
func TestParseReportArgsGlobalAlone(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
args, err := ParseReportArgs("scheduler", []string{"--global"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(args.IsGlobal).To(BeTrue())
|
||||
Expect(args.InfoFlag).To(Equal(""))
|
||||
Expect(args.OSArgs).To(BeEmpty())
|
||||
}
|
||||
|
||||
func TestParseReportArgsGlobalWithFormat(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
args, err := ParseReportArgs("scheduler", []string{"--global", "--format", "json"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(args.IsGlobal).To(BeTrue())
|
||||
Expect(args.InfoFlag).To(Equal(""))
|
||||
Expect(args.OSArgs).To(Equal([]string{"--format", "json"}))
|
||||
}
|
||||
|
||||
func TestParseReportArgsInfoFlag(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
args, err := ParseReportArgs("scheduler", []string{"myapp", "--scheduler-selected"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(args.IsGlobal).To(BeFalse())
|
||||
Expect(args.InfoFlag).To(Equal("--scheduler-selected"))
|
||||
Expect(args.OSArgs).To(Equal([]string{"myapp"}))
|
||||
}
|
||||
|
||||
func TestParseReportArgsAppThenGlobal(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
args, err := ParseReportArgs("scheduler", []string{"myapp", "--global"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(args.IsGlobal).To(BeTrue())
|
||||
Expect(args.InfoFlag).To(Equal(""))
|
||||
Expect(args.OSArgs).To(Equal([]string{"myapp"}))
|
||||
}
|
||||
|
||||
func TestParseReportArgsGlobalWithInfoFlag(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
args, err := ParseReportArgs("scheduler", []string{"--global", "--scheduler-global-selected"})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(args.IsGlobal).To(BeTrue())
|
||||
Expect(args.InfoFlag).To(Equal("--scheduler-global-selected"))
|
||||
Expect(args.OSArgs).To(BeEmpty())
|
||||
}
|
||||
|
||||
func TestParseReportArgsMultipleInfoFlags(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
_, err := ParseReportArgs("scheduler", []string{"--scheduler-selected", "--scheduler-global-selected"})
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
|
||||
@@ -958,3 +958,62 @@ fn-registry-docker-config-dir() {
|
||||
echo "$config_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
fn-report-parse-args() {
|
||||
declare desc="strips --format and --global flags from a report invocation, populating REPORT_FORMAT, REPORT_IS_GLOBAL, REPORT_ARGS"
|
||||
REPORT_FORMAT="stdout"
|
||||
REPORT_IS_GLOBAL="false"
|
||||
REPORT_ARGS=()
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--format)
|
||||
REPORT_FORMAT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--global)
|
||||
REPORT_IS_GLOBAL="true"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
REPORT_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
fn-report-emit-json() {
|
||||
declare desc="serializes a flag_map array (passed by name) as a JSON object, stripping the --<prefix>- prefix from each key"
|
||||
declare flag_map_name="$1" prefix="$2"
|
||||
local -n flag_map_ref="$flag_map_name"
|
||||
local json_output="{}"
|
||||
for flag in "${flag_map_ref[@]}"; do
|
||||
local key value
|
||||
key="$(echo "$flag" | cut -d':' -f1 | sed "s/^--${prefix}-//")"
|
||||
value="$(echo "$flag" | cut -d':' -f2- | sed 's/^ //')"
|
||||
json_output="$(echo "$json_output" | jq -c --arg key "$key" --arg value "$value" '. + {($key): $value}')"
|
||||
done
|
||||
echo "$json_output"
|
||||
}
|
||||
|
||||
fn-report-filter-global() {
|
||||
declare desc="filters a flag_map array (passed by name) in place, keeping only entries with -global- in their flag name"
|
||||
declare flag_map_name="$1"
|
||||
local -n flag_map_ref="$flag_map_name"
|
||||
local filtered=()
|
||||
for flag in "${flag_map_ref[@]}"; do
|
||||
local key="$(echo "$flag" | cut -d':' -f1)"
|
||||
if [[ "$key" == *"-global-"* ]]; then
|
||||
filtered+=("$flag")
|
||||
fi
|
||||
done
|
||||
flag_map_ref=("${filtered[@]}")
|
||||
}
|
||||
|
||||
fn-report-validate-format() {
|
||||
declare desc="rejects --format json when an info flag is also specified"
|
||||
declare FORMAT="$1" INFO_FLAG="$2"
|
||||
if [[ "$FORMAT" != "stdout" ]] && [[ -n "$INFO_FLAG" ]]; then
|
||||
dokku_log_fail "--format flag cannot be specified when specifying an info flag"
|
||||
fi
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user