|
| 1 | +# Write custom extension scripts using the Code42 CLI and py42 |
| 2 | + |
| 3 | +While the Code42 CLI aims to provide an easy way to automate many common Code42 tasks, there will likely be times when |
| 4 | +you need to script something the CLI doesn't have out-of-the-box. |
| 5 | + |
| 6 | +To accommodate for those scenarios, the Code42 CLI exposes a few helper objects in the `code42cli.extensions` module |
| 7 | +that make it easy to write custom scripts with `py42` that use features of the CLI (like profiles) to reduce the amount |
| 8 | +of boilerplate needed to be productive. |
| 9 | + |
| 10 | +## Before you begin |
| 11 | + |
| 12 | +The Code42 CLI is a python application written using the [click framework](https://click.palletsprojects.com/en/7.x/), |
| 13 | +and the exposed extension objects are custom `click` classes. A basic knowledge of how to define `click` commands, |
| 14 | +arguments, and options is required. |
| 15 | + |
| 16 | +### The `sdk_options` decorator |
| 17 | + |
| 18 | +The most important extension object is the `sdk_options` decorator. When you decorate a command you've defined in your |
| 19 | +script with `@sdk_options`, it will automatically add `--profile` and `--debug` options to your command. These work the |
| 20 | +same as in the main CLI commands. |
| 21 | + |
| 22 | +Decorating a command with `@sdk_options` also causes the first argument to your command function to be the `state` |
| 23 | +object, which contains the initialized py42 sdk. There's no need to handle user credentials or login, the `sdk_options` |
| 24 | +does all that for you using the CLI profiles. |
| 25 | + |
| 26 | +### The `script` group |
| 27 | + |
| 28 | +The `script` object exposed in the extensions module is a `click.Group` subclass, which allows you to add multiple |
| 29 | +sub-commands and group functionality together. While not explicitly required when writing custom scripts, the `script` |
| 30 | +group has logic to help handle and log any uncaught exceptions to the `~/.code42cli/log/code42_errors.log` file. |
| 31 | + |
| 32 | +If only a single command is added to the `script` group, the group will default to that command, so you don't need to |
| 33 | +explicitly provide the sub-command name. |
| 34 | + |
| 35 | +An example command that just prints the username and ID that the sdk is authenticated with: |
| 36 | + |
| 37 | +```python |
| 38 | +import click |
| 39 | +from code42cli.extensions import script, sdk_options |
| 40 | + |
| 41 | +@click.command() |
| 42 | +@sdk_options |
| 43 | +def my_command(state): |
| 44 | + user = state.sdk.users.get_current() |
| 45 | + print(user["username"], user["userId"]) |
| 46 | + |
| 47 | +if __name__ == "__main__": |
| 48 | + script.add_command(my_command) |
| 49 | + script() |
| 50 | +``` |
| 51 | + |
| 52 | +## Ensuring your script runs in the Code42 CLI python environment |
| 53 | + |
| 54 | +The above example works as a standalone script, if it were named `my_script.py` you could execute it by running: |
| 55 | + |
| 56 | +```bash |
| 57 | +python3 my_script.py |
| 58 | +``` |
| 59 | + |
| 60 | +However, if the Code42 CLI is installed in a different python environment than your `python3` command, it might fail to |
| 61 | +import the extensions. |
| 62 | + |
| 63 | +To workaround environment and path issues, the CLI has a `--python` option that prints out the path to the python |
| 64 | +executable the CLI uses, so you can execute your script with`$(code42 --python) script.py` on Mac/Linux or |
| 65 | +`&$(code42 --python) script.py` on Windows to ensure it always uses the correct python path for the extension script to |
| 66 | +work. |
| 67 | + |
| 68 | +## Installing your extension script as a Code42 CLI plugin |
| 69 | + |
| 70 | +The above example works as a standalone script, but it's also possible to install that same script as a plugin into the |
| 71 | +main CLI itself. |
| 72 | + |
| 73 | +Assuming the above example code is in a file called `my_script.py`, just add a file `setup.py` in the same directory |
| 74 | +with the following: |
| 75 | + |
| 76 | +```python |
| 77 | +from distutils.core import setup |
| 78 | + |
| 79 | +setup( |
| 80 | + name="my_script", |
| 81 | + version="0.1", |
| 82 | + py_modules=["my_script"], |
| 83 | + install_requires=["code42cli"], |
| 84 | + entry_points=""" |
| 85 | + [code42cli.plugins] |
| 86 | + my_command=my_script:my_command |
| 87 | + """, |
| 88 | +) |
| 89 | +``` |
| 90 | + |
| 91 | +The `entry_points` section tells the Code42 CLI where to look for the commands to add to its main group. If you have |
| 92 | +multiple commands defined in your script you can add one per line in the `entry_points` and they'll all get installed |
| 93 | +into the Code42 CLI. |
| 94 | + |
| 95 | +Once your `setup.py` is ready, install it with pip while in the directory of `setup.py`: |
| 96 | + |
| 97 | +``` |
| 98 | +$(code42 --python) -m pip install . |
| 99 | +``` |
| 100 | + |
| 101 | +Then running `code42 -h` should show `my-command` as one of the available commands to run! |
0 commit comments