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:
Jose Diaz-Gonzalez
2026-04-29 04:47:15 -04:00
parent ec70e10c5d
commit 8282981361
61 changed files with 1379 additions and 518 deletions

View File

@@ -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

View File

@@ -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())
}

View File

@@ -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
}