Skip to content

Commit 3f7f52b

Browse files
Add support for custom aliases for commands (#755)
1 parent 723c441 commit 3f7f52b

File tree

4 files changed

+133
-14
lines changed

4 files changed

+133
-14
lines changed

linodecli/__init__.py

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,7 @@
4646

4747
# if any of these arguments are given, we don't need to prompt for configuration
4848
skip_config = (
49-
any(
50-
c in argv
51-
for c in ["--skip-config", "--help", "--version", "completion"]
52-
)
49+
any(c in argv for c in ["--skip-config", "--version", "completion"])
5350
or TEST_MODE
5451
)
5552

@@ -110,6 +107,43 @@ def main(): # pylint: disable=too-many-branches,too-many-statements
110107
# if not spec was found and we weren't baking, we're doomed
111108
sys.exit(ExitCodes.ARGUMENT_ERROR)
112109

110+
if parsed.command in ("set-custom-alias", "remove-custom-alias"):
111+
if not parsed.target_command or not parsed.alias:
112+
print(
113+
"Both --target-command and --alias must be provided.",
114+
file=sys.stderr,
115+
)
116+
sys.exit(ExitCodes.ARGUMENT_ERROR)
117+
118+
command = parsed.target_command
119+
alias = parsed.alias
120+
121+
if command not in cli.ops:
122+
print(
123+
f"Error: '{command}' is not a valid command.", file=sys.stderr
124+
)
125+
sys.exit(ExitCodes.ARGUMENT_ERROR)
126+
127+
if parsed.command == "set-custom-alias":
128+
if (alias, command) not in cli.config.get_custom_aliases().items():
129+
cli.config.set_custom_alias(alias, command)
130+
print(f"Custom alias '{alias}' set for command '{command}'")
131+
else:
132+
print(
133+
f"Custom alias '{alias}' already set for command '{command}'"
134+
)
135+
136+
if parsed.command == "remove-custom-alias":
137+
if (alias, command) in cli.config.get_custom_aliases().items():
138+
cli.config.remove_custom_alias(alias, command)
139+
print(f"Custom alias '{alias}' removed for command '{command}'")
140+
else:
141+
print(
142+
f"Custom alias '{alias}' does not exist for command '{command}'"
143+
)
144+
145+
sys.exit(ExitCodes.SUCCESS)
146+
113147
if parsed.command == "register-plugin":
114148
if parsed.action is None:
115149
print("register-plugin requires a module name!", file=sys.stderr)
@@ -213,28 +247,36 @@ def main(): # pylint: disable=too-many-branches,too-many-statements
213247
plugin_args.remove(parsed.command) # don't include the plugin name
214248
plugins.invoke(parsed.command, plugin_args, context)
215249
sys.exit(ExitCodes.SUCCESS)
216-
217250
# unknown commands
218251
if (
219252
parsed.command not in cli.ops
220253
and parsed.command not in plugins.available(cli.config)
221254
and parsed.command not in HELP_TOPICS
255+
and parsed.command not in cli.config.get_custom_aliases().keys()
222256
):
223257
print(f"Unrecognized command {parsed.command}", file=sys.stderr)
224258
sys.exit(ExitCodes.UNRECOGNIZED_COMMAND)
225259

226260
# handle a help for a command - either --help or no action triggers this
227-
if (
228-
parsed.command is not None
229-
and parsed.action is None
230-
and parsed.command in cli.ops
231-
):
232-
print_help_command_actions(cli.ops, parsed.command)
233-
sys.exit(ExitCodes.SUCCESS)
261+
if parsed.command is not None and parsed.action is None:
262+
if parsed.command in cli.ops:
263+
print_help_command_actions(cli.ops, parsed.command)
264+
sys.exit(ExitCodes.SUCCESS)
265+
if parsed.command in cli.config.get_custom_aliases().keys():
266+
print_help_command_actions(
267+
cli.ops, cli.config.get_custom_aliases()[parsed.command]
268+
)
234269

235270
if parsed.command is not None and parsed.action is not None:
236271
if parsed.help:
237-
print_help_action(cli, parsed.command, parsed.action)
272+
if parsed.command in cli.config.get_custom_aliases().keys():
273+
print_help_action(
274+
cli,
275+
cli.config.get_custom_aliases()[parsed.command],
276+
parsed.action,
277+
)
278+
else:
279+
print_help_action(cli, parsed.command, parsed.action)
238280
sys.exit(ExitCodes.SUCCESS)
239281

240282
cli.handle_command(parsed.command, parsed.action, args)

linodecli/arg_helpers.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ def register_args(parser: ArgumentParser) -> ArgumentParser:
6767
help="Prints version information and exits.",
6868
)
6969

70+
parser.add_argument(
71+
"--target-command",
72+
"-t",
73+
nargs="?",
74+
type=str,
75+
help="The command to set or remove an alias for.",
76+
)
77+
78+
parser.add_argument(
79+
"--alias",
80+
"-a",
81+
nargs="?",
82+
type=str,
83+
help="The alias to set or remove.",
84+
)
85+
7086
# Register shared argument groups
7187
register_output_args_shared(parser)
7288
register_pagination_args_shared(parser)

linodecli/cli.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,11 @@ def find_operation(self, command, action):
259259
Finds the corresponding operation for the given command and action.
260260
"""
261261
if command not in self.ops:
262-
raise ValueError(f"Command not found: {command}")
262+
# Check that the passed command is not an alias before raising an error
263+
if command in self.config.get_custom_aliases().keys():
264+
command = self.config.get_custom_aliases()[command]
265+
else:
266+
raise ValueError(f"Command not found: {command}")
263267

264268
command_dict = self.ops[command]
265269

linodecli/configuration/config.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,3 +597,60 @@ def _handle_no_default_user(self): # pylint: disable=too-many-branches
597597
self.write_config()
598598
return
599599
print(f"No user {username}")
600+
601+
def set_custom_alias(self, alias: str, command: str):
602+
"""
603+
Sets a custom alias for a Linode CLI command.
604+
605+
:param alias: The custom alias name.
606+
:param command: The command the custom alias maps to.
607+
"""
608+
if not self.config.has_section("custom_aliases"):
609+
self.config.add_section("custom_aliases")
610+
611+
self.config.set("custom_aliases", alias, command)
612+
self.write_config()
613+
614+
def remove_custom_alias(self, alias: str, command: str):
615+
"""
616+
Removes a custom alias from the Linode CLI configuration.
617+
618+
:param alias: The alias name to remove.
619+
:param command: The command the alias is mapped to.
620+
"""
621+
if not self.config.has_section("custom_aliases"):
622+
print("Error: No custom aliases have been set.", file=sys.stderr)
623+
return
624+
625+
if not self.config.has_option("custom_aliases", alias):
626+
print(
627+
f"Error: Custom alias '{alias}' does not exist.",
628+
file=sys.stderr,
629+
)
630+
return
631+
632+
# Check if the alias maps to the given command
633+
existing_command = self.config.get("custom_aliases", alias)
634+
if existing_command != command:
635+
print(
636+
f"Error: Custom alias '{alias}' is mapped to "
637+
f"'{existing_command}', not '{command}'.",
638+
file=sys.stderr,
639+
)
640+
return
641+
642+
# Remove the alias and update the config
643+
self.config.remove_option("custom_aliases", alias)
644+
self.write_config()
645+
646+
def get_custom_aliases(self) -> Dict[str, str]:
647+
"""
648+
Retrieves all stored custom command aliases from the config.
649+
650+
:return: A dictionary mapping custom alias names to their respective commands.
651+
"""
652+
return (
653+
dict(self.config.items("custom_aliases"))
654+
if (self.config.has_section("custom_aliases"))
655+
else {}
656+
)

0 commit comments

Comments
 (0)