Repository: digitalocean/doctl
Pull request #1269 (2022-10-04 13:35:00)

commands: Consistent aliases for "list" and "delete"
This pull request
- Adds missing `ls` and `rm` aliases to their respective `list` and `delete` subcommands
- Changes the alias order of `doctl compute volume delete` to `d, rw` in order to be consistent with other `delete` alias uses
- Adds the test suggested in the linked issue in order to ensure any future `list` and `remove`  sub-commands have the expected aliases.

Fixes https://github.com/digitalocean/doctl/issues/1262

Please let me know if I missed any aliases!

@andrewsomething 



Patch:
diff --git a/commands/activations.go b/commands/activations.go
index 08023933..b8e77edd 100644
--- a/commands/activations.go
+++ b/commands/activations.go
@@ -46,7 +46,7 @@ logs.`,
 	list := CmdBuilder(cmd, RunActivationsList, "list [<activation_name>]", "Lists Activations for which records exist",
 		`Use `+"`"+`doctl serverless activations list`+"`"+` to list the activation records that are present in the cloud for previously
 invoked functions.`,
-		Writer)
+		Writer, aliasOpt("ls"))
 	AddStringFlag(list, "limit", "l", "", "only return LIMIT number of activations (default 30, max 200)")
 	AddStringFlag(list, "skip", "s", "", "exclude the first SKIP number of activations from the result")
 	AddStringFlag(list, "since", "", "", "return activations with timestamps later than SINCE; measured in milliseconds since Th, 01, Jan 1970")
diff --git a/commands/apps.go b/commands/apps.go
index 759074d6..5288247f 100644
--- a/commands/apps.go
+++ b/commands/apps.go
@@ -112,7 +112,7 @@ Only basic information is included with the text output format. For complete app
 
 This permanently deletes the app and all its associated deployments.`,
 		Writer,
-		aliasOpt("d"),
+		aliasOpt("d", "rm"),
 	)
 	AddBoolFlag(deleteApp, doctl.ArgForce, doctl.ArgShortForce, false, "Delete the App without a confirmation prompt")
 
@@ -787,7 +787,7 @@ func appsTier() *Command {
 		},
 	}
 
-	CmdBuilder(cmd, RunAppsTierList, "list", "List all app tiers", `Use this command to list all the available app tiers.`, Writer)
+	CmdBuilder(cmd, RunAppsTierList, "list", "List all app tiers", `Use this command to list all the available app tiers.`, Writer, aliasOpt("ls"))
 	CmdBuilder(cmd, RunAppsTierGet, "get <tier slug>", "Retrieve an app tier", `Use this command to retrieve information about a specific app tier.`, Writer)
 
 	cmd.AddCommand(appsTierInstanceSize())
@@ -830,7 +830,7 @@ func appsTierInstanceSize() *Command {
 		},
 	}
 
-	CmdBuilder(cmd, RunAppsTierInstanceSizeList, "list", "List all app instance sizes", `Use this command to list all the available app instance sizes.`, Writer)
+	CmdBuilder(cmd, RunAppsTierInstanceSizeList, "list", "List all app instance sizes", `Use this command to list all the available app instance sizes.`, Writer, aliasOpt("ls"))
 	CmdBuilder(cmd, RunAppsTierInstanceSizeGet, "get <instance size slug>", "Retrieve an app instance size", `Use this command to retrieve information about a specific app instance size.`, Writer)
 
 	return cmd
diff --git a/commands/domains.go b/commands/domains.go
index 53c06028..978be479 100644
--- a/commands/domains.go
+++ b/commands/domains.go
@@ -73,7 +73,7 @@ func Domain() *Command {
 	AddStringFlag(cmdRecordCreate, doctl.ArgRecordTag, "", "", "The parameter tag for CAA records. Valid values are `issue`, `issuewild`, or `iodef`")
 
 	cmdRunRecordDelete := CmdBuilder(cmdRecord, RunRecordDelete, "delete <domain> <record-id>...", "Delete a DNS record", `Use this command to delete DNS records for a domain.`, Writer,
-		aliasOpt("d"))
+		aliasOpt("d", "rm"))
 	AddBoolFlag(cmdRunRecordDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Delete record without confirmation prompt")
 
 	cmdRecordUpdate := CmdBuilder(cmdRecord, RunRecordUpdate, "update <domain>", "Update a DNS record", `Use this command to update or change DNS records for a domain.`, Writer,
diff --git a/commands/functions.go b/commands/functions.go
index fc53f772..0f36777d 100644
--- a/commands/functions.go
+++ b/commands/functions.go
@@ -64,7 +64,7 @@ You can provide inputs and inspect outputs.`,
 
 	list := CmdBuilder(cmd, RunFunctionsList, "list [<packageName>]", "Lists the functions in your functions namespace",
 		`Use `+"`"+`doctl serverless functions list`+"`"+` to list the functions in your functions namespace.`,
-		Writer, displayerType(&displayers.Functions{}))
+		Writer, aliasOpt("ls"), displayerType(&displayers.Functions{}))
 	AddStringFlag(list, "limit", "l", "", "only return LIMIT number of functions (default 30, max 200)")
 	AddStringFlag(list, "skip", "s", "", "exclude the first SKIP number of functions from the result")
 	AddBoolFlag(list, "count", "", false, "show only the total number of functions")
diff --git a/commands/images.go b/commands/images.go
index 3c0624a2..b01b266c 100644
--- a/commands/images.go
+++ b/commands/images.go
@@ -83,7 +83,8 @@ Currently, there are five types of images: snapshots, backups, custom images, di
 		displayerType(&displayers.Image{}))
 	AddStringFlag(cmdImagesUpdate, doctl.ArgImageName, "", "", "Image name", requiredOpt())
 
-	cmdRunImagesDelete := CmdBuilder(cmd, RunImagesDelete, "delete <image-id>", "Permanently delete an image from your account", `This command deletes the specified image from your account. This is irreversible.`, Writer)
+	cmdRunImagesDelete := CmdBuilder(cmd, RunImagesDelete, "delete <image-id>", "Permanently delete an image from your account", `This command deletes the specified image from your account. This is irreversible.`, Writer,
+		aliasOpt("rm"))
 	AddBoolFlag(cmdRunImagesDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force image delete")
 
 	cmdRunImagesCreate := CmdBuilder(cmd, RunImagesCreate, "create <image-name>", "Create custom image", `This command creates an image in your DigitalOcean account. You can specify a URL for the image contents, the region at which to store the image, and image metadata.`, Writer)
diff --git a/commands/monitoring.go b/commands/monitoring.go
index 3c23e4cf..c629034c 100644
--- a/commands/monitoring.go
+++ b/commands/monitoring.go
@@ -84,7 +84,7 @@ func alertPolicies() *Command {
 	CmdBuilder(cmd, RunCmdAlertPolicyList, "list", "List all alert policies", `Use this command to retrieve a list of all the alert policies in your account.`, Writer,
 		aliasOpt("ls"), displayerType(&displayers.AlertPolicy{}))
 
-	cmdRunAlertPolicyDelete := CmdBuilder(cmd, RunCmdAlertPolicyDelete, "delete <alert-policy-uuid>...", "Delete an alert policy", `Use this command to delete an alert policy.`, Writer)
+	cmdRunAlertPolicyDelete := CmdBuilder(cmd, RunCmdAlertPolicyDelete, "delete <alert-policy-uuid>...", "Delete an alert policy", `Use this command to delete an alert policy.`, Writer, aliasOpt("rm"))
 	AddBoolFlag(cmdRunAlertPolicyDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Delete an alert policy without confirmation prompt")
 
 	return cmd
diff --git a/commands/namespaces.go b/commands/namespaces.go
index 2f364663..7b91fc3b 100644
--- a/commands/namespaces.go
+++ b/commands/namespaces.go
@@ -59,12 +59,12 @@ Both a region and a label must be specified.`,
 		`Use `+"`"+`doctl serverless namespaces delete`+"`"+` to delete a functions namespace.
 The full label or full id of the namespace is required as an argument.
 You are prompted for confirmation unless `+"`"+`--force`+"`"+` is specified.`,
-		Writer)
+		Writer, aliasOpt("rm"))
 	AddBoolFlag(delete, "force", "f", false, "Just do it, omitting confirmatory prompt")
 
 	CmdBuilder(cmd, RunNamespacesList, "list", "Lists your namespaces",
 		`Use `+"`"+`doctl serverless namespaces list`+"`"+` to list your functions namespaces.`,
-		Writer, displayerType(&displayers.Namespaces{}))
+		Writer, aliasOpt("ls"), displayerType(&displayers.Namespaces{}))
 
 	CmdBuilder(cmd, RunNamespacesListRegions, "list-regions", "Lists the accepted 'region' values",
 		`Use `+"`"+`doctl serverless namespaces list-regions`+"`"+` to list the values that are accepted
diff --git a/commands/snapshots.go b/commands/snapshots.go
index babec451..65490b11 100644
--- a/commands/snapshots.go
+++ b/commands/snapshots.go
@@ -57,7 +57,7 @@ func Snapshot() *Command {
 
 	cmdRunSnapshotDelete := CmdBuilder(cmd, RunSnapshotDelete, "delete <snapshot-id>...",
 		"Delete a snapshot of a Droplet or volume", "Delete a snapshot of a Droplet or volume by specifying its ID.",
-		Writer, aliasOpt("d"), displayerType(&displayers.Snapshot{}))
+		Writer, aliasOpt("d", "rm"), displayerType(&displayers.Snapshot{}))
 	AddBoolFlag(cmdRunSnapshotDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Delete the snapshot without confirmation")
 
 	return cmd
diff --git a/commands/sshkeys.go b/commands/sshkeys.go
index 728f9fab..a24f86c7 100644
--- a/commands/sshkeys.go
+++ b/commands/sshkeys.go
@@ -60,7 +60,7 @@ Note that importing a key to your account will not add it to any Droplets`, Writ
 	cmdRunKeyDelete := CmdBuilder(cmd, RunKeyDelete, "delete <key-id|key-fingerprint>", "Permanently delete an SSH key from your account", `Use this command to permanently delete an SSH key from your account.
 
 Note that this does not delete an SSH key from any Droplets.`, Writer,
-		aliasOpt("d"))
+		aliasOpt("d", "rm"))
 	AddBoolFlag(cmdRunKeyDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Delete the key without a confirmation prompt")
 
 	cmdSSHKeysUpdate := CmdBuilder(cmd, RunKeyUpdate, "update <key-id|key-fingerprint>", "Update an SSH key's name", `Use this command to update the name of an SSH key.`, Writer,
diff --git a/commands/tags.go b/commands/tags.go
index 288dd3c4..8a5f6dea 100644
--- a/commands/tags.go
+++ b/commands/tags.go
@@ -50,7 +50,7 @@ resources attribute with information about resources that have been tagged.`,
 
 	cmdRunTagDelete := CmdBuilder(cmd, RunCmdTagDelete, "delete <tag-name>...", "Delete a tag", `Use this command to delete a tag.
 
-Deleting a tag also removes the tag from all the resources that had been tagged with it.`, Writer)
+Deleting a tag also removes the tag from all the resources that had been tagged with it.`, Writer, aliasOpt("rm"))
 	AddBoolFlag(cmdRunTagDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Delete tag without confirmation prompt")
 
 	cmdApplyTag := CmdBuilder(cmd, RunCmdApplyTag, "apply <tag-name> --resource=<urn> [--resource=<urn> ...]", "Apply a tag to resources", `Use this command to tag one or more resources. Resources must be specified as URNs ("do:<resource_type>:<identifier>").`, Writer)
diff --git a/commands/triggers.go b/commands/triggers.go
index 28bd69f7..3ea2a178 100644
--- a/commands/triggers.go
+++ b/commands/triggers.go
@@ -39,7 +39,7 @@ when events from that source type occur.  Currently, only the ` + "`" + `schedul
 	}
 	list := CmdBuilder(cmd, RunTriggersList, "list", "Lists your triggers",
 		`Use `+"`"+`doctl serverless triggers list`+"`"+` to list your triggers.`,
-		Writer, displayerType(&displayers.Triggers{}))
+		Writer, aliasOpt("ls"), displayerType(&displayers.Triggers{}))
 	AddStringFlag(list, "function", "f", "", "list only triggers for the chosen function")
 
 	CmdBuilder(cmd, RunTriggersGet, "get <triggerName>", "Get the details for a trigger",
diff --git a/commands/volumes.go b/commands/volumes.go
index 9e10c6b5..65c52aa6 100644
--- a/commands/volumes.go
+++ b/commands/volumes.go
@@ -57,7 +57,7 @@ You can use flags to specify the volume size, region, description, filesystem ty
 	AddStringSliceFlag(cmdVolumeCreate, doctl.ArgTag, "", []string{}, "Tags to apply to the volume; comma separate or repeat `--tag` to add multiple tags at once")
 
 	cmdRunVolumeDelete := CmdBuilder(cmd, RunVolumeDelete, "delete <volume-id>", "Delete a block storage volume", `Use this command to delete a block storage volume by ID, destroying all of its data and removing it from your account.`, Writer,
-		aliasOpt("rm", "d"))
+		aliasOpt("d", "rm"))
 	AddBoolFlag(cmdRunVolumeDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force volume delete")
 
 	CmdBuilder(cmd, RunVolumeGet, "get <volume-id>", "Retrieve an existing block storage volume", `Use this command to retrieve information about a block storage volume using its ID.`, Writer, aliasOpt("g"),


Test patch:
diff --git a/commands/commands_test.go b/commands/commands_test.go
index 87e9d448..561fef6b 100644
--- a/commands/commands_test.go
+++ b/commands/commands_test.go
@@ -14,6 +14,7 @@ limitations under the License.
 package commands
 
 import (
+	"github.com/spf13/cobra"
 	"io/ioutil"
 	"sort"
 	"testing"
@@ -285,3 +286,29 @@ func withTestClient(t *testing.T, tFn testFn) {
 
 	tFn(config, tm)
 }
+
+func assertCommandAliases(t *testing.T, cmd *cobra.Command) {
+	for _, c := range cmd.Commands() {
+		if c.Name() == "list" {
+			assert.Contains(t, c.Aliases, "ls", "Missing 'ls' alias for 'list' command.")
+		}
+		if c.Name() == "delete" {
+			assert.Contains(t, c.Aliases, "rm", "Missing 'rm' alias for 'delete' command.")
+		}
+	}
+}
+
+func recurseCommand(t *testing.T, cmd *cobra.Command) {
+	t.Run(cmd.Name(), func(t *testing.T) {
+		assertCommandAliases(t, cmd)
+	})
+	for _, c := range cmd.Commands() {
+		recurseCommand(t, c)
+	}
+}
+
+func TestCommandAliases(t *testing.T) {
+	for _, cmd := range DoitCmd.Commands() {
+		recurseCommand(t, cmd)
+	}
+}