From 21c27e99cca380812b88e455cfac19bf19b1aa04 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 6 Jul 2026 11:50:56 -0400 Subject: [PATCH] feat: add --format json support to apps:list The `apps:list` command now accepts a `--format` flag that defaults to `stdout` and can be set to `json` to emit the app names as a JSON array, matching the JSON output the `:report` subcommands already provide. When no apps exist, the JSON output is an empty array. --- docs/deployment/application-management.md | 14 +++++++++- plugins/apps/src/commands/commands.go | 4 +-- plugins/apps/src/subcommands/subcommands.go | 3 ++- plugins/apps/subcommands.go | 22 +++++++++++++-- tests/unit/apps_1.bats | 30 +++++++++++++++++++++ 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/docs/deployment/application-management.md b/docs/deployment/application-management.md index 9505a81f3..31812a570 100644 --- a/docs/deployment/application-management.md +++ b/docs/deployment/application-management.md @@ -8,7 +8,7 @@ apps:clone # Clones an app apps:create # Create a new app apps:destroy # Permanently destroy an app apps:exists # Checks if an app exists -apps:list # List your apps +apps:list [--format stdout|json] # List your apps apps:lock # Locks an app for deployment apps:locked # Checks if an app is locked for deployment apps:rename # Rename an app @@ -47,6 +47,18 @@ node-js-app python-app ``` +You can also retrieve the list of apps as a JSON array by using the `--format json` flag, which is useful for programmatic consumption: + +```shell +dokku apps:list --format json +``` + +```json +["node-js-app","python-app"] +``` + +When no apps exist, the JSON output is an empty array (`[]`). + ### Checking if an application exists For CI/CD pipelines, it may be useful to see if an application exists before creating a "review" application for a specific branch. You can do so via the `apps:exists` command: diff --git a/plugins/apps/src/commands/commands.go b/plugins/apps/src/commands/commands.go index 8cc38f158..ba9893743 100644 --- a/plugins/apps/src/commands/commands.go +++ b/plugins/apps/src/commands/commands.go @@ -23,7 +23,7 @@ Additional commands:` apps:create , Create a new app apps:destroy , Permanently destroy an app apps:exists , Checks if an app exists - apps:list, List your apps + apps:list [--format stdout|json], List your apps apps:lock , Locks an app for deployment apps:locked , Checks if an app is locked for deployment apps:rename , Rename an app @@ -43,7 +43,7 @@ func main() { args.Usage = usage args.Parse(os.Args[2:]) - if err := apps.CommandList(); err != nil { + if err := apps.CommandList("stdout"); err != nil { common.LogFailWithError(err) } case "apps:help": diff --git a/plugins/apps/src/subcommands/subcommands.go b/plugins/apps/src/subcommands/subcommands.go index 29c0fe4c9..7238476bc 100644 --- a/plugins/apps/src/subcommands/subcommands.go +++ b/plugins/apps/src/subcommands/subcommands.go @@ -44,8 +44,9 @@ func main() { err = apps.CommandExists(appName) case "list": args := flag.NewFlagSet("apps:list", flag.ExitOnError) + format := args.String("format", "stdout", "format: [ stdout | json ]") args.Parse(os.Args[2:]) - err = apps.CommandList() + err = apps.CommandList(*format) case "lock": args := flag.NewFlagSet("apps:lock", flag.ExitOnError) args.Parse(os.Args[2:]) diff --git a/plugins/apps/subcommands.go b/plugins/apps/subcommands.go index cda8e918b..4538f81d6 100644 --- a/plugins/apps/subcommands.go +++ b/plugins/apps/subcommands.go @@ -1,6 +1,7 @@ package apps import ( + "encoding/json" "errors" "fmt" "os" @@ -98,9 +99,26 @@ func CommandExists(appName string) error { } // CommandList lists all apps -func CommandList() error { - common.LogInfo2Quiet("My Apps") +func CommandList(format string) error { apps, err := common.DokkuApps() + + if format == "json" { + if err != nil && !errors.Is(err, common.NoAppsExist) { + return err + } + + appList := []string{} + appList = append(appList, apps...) + out, err := json.Marshal(appList) + if err != nil { + return err + } + + common.Log(string(out)) + return nil + } + + common.LogInfo2Quiet("My Apps") if err != nil { common.LogWarn(err.Error()) return nil diff --git a/tests/unit/apps_1.bats b/tests/unit/apps_1.bats index 66b456a50..7799ae7f1 100644 --- a/tests/unit/apps_1.bats +++ b/tests/unit/apps_1.bats @@ -50,6 +50,36 @@ teardown() { assert_success } +@test "(apps) apps:list --format json" { + run /bin/bash -c "dokku apps:list --format json" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "[]" + + run /bin/bash -c "dokku apps:create $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku apps:list --format json | jq '. | length'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "1" + + run /bin/bash -c "dokku apps:list --format json | jq -r '.[0]'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "$TEST_APP" + + run /bin/bash -c "dokku --force apps:destroy $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success +} + @test "(apps) apps:create" { run /bin/bash -c "dokku apps:create $TEST_APP" echo "output: $output"