# Lariat
Lariat is a powerful command-line tool designed to streamline the execution of
commands and running of executables on remote Android devices. It achieves this
by leveraging [DeviceFarmer](https://github.com/DeviceFarmer/stf)'s API and the
Android Debug Bridge (ADB). With Lariat, the cumbersome process of manually
connecting to devices, pushing files, running commands, and retrieving results
is simplified and made more efficient. It is an ideal solution for automating
tasks in continuous integration (CI) pipelines.
## Prerequisites
- Python 3.8 or higher
- ADB installed and added to the system's PATH
- DeviceFarmer [Access
Token](https://github.com/DeviceFarmer/stf/blob/master/doc/API.md#authentication)
- JSON configuration file containing the Swagger spec URL and DeviceFarmer API
token
## Installation
Lariat is available on PyPI:
```sh
python -m pip install lariat
```
Lariat officially supports Python 3.8+.
## Configuration File
Lariat utilizes a JSON configuration file. The default location for this
config file is in the `.lariat` directory within the user's home directory
(`~/.lariat/config.json`). The configuration file is used to specify the
following:
- `device_farmer_url`: The URL of the DeviceFarmer instance to connect to.
- NOTE: Lariat will append the standard API JSON spec to the base url. If
using a custom path for your spec file, specify a full url ending in
`.json` or `.yaml`.
- `access_token`: Your DeviceFarmer access token.
- This can be obtained by logging into your DeviceFarmer UI, and going to
Settings->Keys->Access Tokens
- `adb_private_key_path`: (Optional) Path to a non-default adb private key.
Defaults to `~/.android/adbkey` if not specified
- `adb_shell_default_read_timeout_s`: (Optional) Default total timeout (in
seconds) for reading data from a device. This value may need to be increased
from the default value of 10s for certain device operations.
```json
{
"access_token": "ef02da4fb3884395af4cf011061a2318ca5e9a04abd04de59c5c99afcce0b7fz",
"device_farmer_url": "https://my.device-farmer-instance.com/",
"adb_private_key_path": "/custom/path/adb.key",
"adb_shell_default_read_timeout_s": 32.5
}
```
All config options can be specified on the command line to override any
defaults set in the config file. For example, the command line option
`--device_farmer_url` can be used to override the DeviceFarmer URL set in the
config file.
## Usage
```sh
usage: lariat [-h] [-g | -e EXEC_FILE | -c COMMAND] [--config CONFIG] [-s SELECT [SELECT ...]] [-f FILTER [FILTER ...]]
[-p PUSH_FILES]
DeviceFarmer automation tool
optional arguments:
-h, --help show this help message and exit
-g, --get-devices Enumerate devices on DeviceFarmer instance. Does not execute any commands on devices. Prints JSON results
to stdout.
-e EXEC_FILE, --exec-file EXEC_FILE
Push a file and execute it. Pushes to /data/local/tmp/.
-c COMMAND, --command COMMAND
Run a command.
--config CONFIG Override the default path to the configuration file. Default:/home/larry.espenshade/.lariat/config.json
-s SELECT [SELECT ...], --select SELECT [SELECT ...]
Select the fields to be returned by --get-devices (-g). If not specified, all fields are returned.
-f FILTER [FILTER ...], --filter FILTER [FILTER ...]
Filter devices via a list of key-value pairs (e.g., sdk=27 manufacturer=SAMSUNG). Non boolean values are
regex matched
-p PUSH_FILES, --push-files PUSH_FILES
Specify the path to the file or directory to be pushed to the device. Pushes to /data/local/tmp/.
-v, --verbose Increase log level. Can be supplied multiple times to further increase log verbosity (e.g. -vv)
```
### Device Fields
The `--select` and `--filter` options both use "field names" to perform their
respective actions. These field names are JSON keys as defined by DeviceFarmer
as part of its REST API. You can view supported fields for your DeviceFarmer
installation by navigating to the following URL:
`https://<device_farmer_url>/api/v1/devices/<serial>` where `device_farmer_url`
is the URL to your DeviceFarmer installation and `serial` is the serial number
of one of your devices. The `/<serial>` component can be omitted to view all
fields of all devices.
Field names support dot notation to access nested keys. For example,
`battery.health` can be used to access the nested `health` key within `battery`.
Note that specifying a `--filter` along with a `--select` will automatically
include any field names specified in the filter in the resulting JSON.
#### Commonly Used Fields
- `abi` - Device Application Binary Interface (eg. `armeabi-v7a`)
- `manufacturer` - Device Manufacturer
- `marketName` - Device Marketing Name
- `model` - Device Model Name
- `provider.name` - STF Provider hosting this device
- `version` - Android version
### Examples
- Enumerate all devices on a DeviceFarmer instance:
```sh
lariat --get-devices
```
- Limit the fields returned by `--get-devices`:
```sh
lariat --get-devices --select serial model status
```
- Filter devices based on specific fields and values (e.g. all Samsung devices
with SDK level 25-27):
```sh
lariat --get-devices --filter manufacturer=SAMSUNG sdk=2[5-7]
```
- Filter devices based on JSON sub-key using dot notation:
```sh
lariat --get-devices --filter provider.name=my.devicefarmer.com
```
- Push a local file to the device and execute it:
```sh
lariat --exec-file path/to/hello
```
- Run a command on all arm64 devices:
```sh
lariat --command "echo hello" --filter abi=arm64-v8a
```
- Push files to the device:
```sh
lariat --push-files path/to/files
```
### Results
Lariat returns results for each device that met the filtering criteria.
The following is sample output for an `echo` command:
```sh
lariat --command "echo hello" --filter abi=arm64-v8a
"A12345": {
"output": "hello",
"exitcode": 0,
},
"B54321": {
"output": "hello",
"exitcode": 0,
},
"C678910": {
"reason": "Device is currently in use",
}
```
Each result contains different fields depending on the device's availability:
- **If a device is unavailable**, the result will have a single field:
- `reason`: Specifies why the device was not available for use, such as it being currently in use, not present on the range, etc.
- **If a device is available** and an operation was performed on it, the result will include two fields:
- `output`: Contains the ADB shell output of the command executed, or details about the action performed.
- `exitcode`: The return code of the command or action executed on the device.
Remember that a device will either have a 'reason' field (if it was unavailable) or 'output' and 'exitcode' fields
## Docker Image
For convenience, an official Lariat Docker image is available.
To use the docker image, simply run
```sh
docker run --rm -v ~/.android:/root/.android:ro -v ~/.lariat:/root/.lariat:ro ghcr.io/zetier/lariat:latest -c 'echo hello'
```
This docker run command creates and runs a Docker container based on the
ghcr.io/zetier/lariat:latest image. It performs the following actions:
- Creates a read-only volume mount for the .android directory on the host
machine, which contains ADB keys, to the /root/.android directory inside the
container.
- Creates a read-only volume mount for the .lariat directory on the host
machine, which contains the lariat configuration file, to the
/root/.lariat directory inside the container.
- The --rm flag ensures that the container is automatically removed after it
exits.
- Inside the container, the command `lariat -c 'echo hello'` is executed,
which prints "hello" as the output on every lockable device on your
DeviceFarmer range.
Please note that if your ADB keys and configuration file are located in
different directories on the host machine, you may need to modify the docker run
command accordingly to provide the correct paths for volume mounting.
## CI Integration
Lariat was designed for simple integration into CI pipelines. Below is an
example of a GitLab job that tests a binary built in the pipeline on a
DeviceFarmer range:
```yml
.lariat-test:
image:
name: ghcr.io/zetier/lariat:latest
entrypoint: [""]
stage: test
before_script:
# Copy keys and configs from private CI variables
- mkdir -p ~/.android
- echo "$CI_ADB_PUB_KEY" > ~/.android/adbkey.pub
- echo "$CI_ADB_PRI_KEY" > ~/.android/adbkey
- echo "$CI_FARMHAND_CONFIG" -> ~/.lariat/config.json
script:
- lariat --file $BINARY > test_results.json
artifacts:
paths:
- test_results.json
# Assumes a `build-android-binary` job that produces `android_binary`
# as an artifact.
android-range-test:
extends: .lariat-test
variables:
BINARY: android_binary
needs:
- build-android-binary
```
## Notes
- Lariat will lock devices before performing any operations to ensure
exclusive access. After the operations have been completed, the devices will
be unlocked.
- Make sure to provide the correct path to the ADB private key file via
`~/.lariat/config.json` if it is different from the default location
(`~/.android/adbkey`)
- By default, Lariat will enumerate ~every~ device on the DeviceFarmer range,
including ones not `present` or `ready`. You can modify this as needed by
specifying `--filter ready=true present=true` if needed.
## Development Install
NOTE: Lariat is developed on and regularly tested with Ubuntu 18.04 with
Python 3.8. Other distributions and versions may work, but are currently
untested.
1. Clone the repository
2. Install dependencies along with the Lariat python package
```sh
sudo apt-get update
sudo apt-get install python3-venv python3.8-venv -y
python3.8 -m venv venv
source venv/bin/activate
(venv) pip install --upgrade pip
(venv) pip install .
```
3. Create a [configuration file](#configuration-file)
## Contributing
Contributions are welcome! If you find any issues or have suggestions for
improvements, please open an issue or submit a pull request.
## License
This project is licensed under the GPLv2 License.
Raw data
{
"_id": null,
"home_page": null,
"name": "lariat",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "lariat, devicefarmer, stf, automation, android, testing, openstf",
"author": "Zetier",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/da/e8/c0b0e5fdf1c3e2110d99dfbd269c703ad0946168bc194d0b5777d94dff83/lariat-0.2.0.tar.gz",
"platform": null,
"description": "# Lariat\n\nLariat is a powerful command-line tool designed to streamline the execution of\ncommands and running of executables on remote Android devices. It achieves this\nby leveraging [DeviceFarmer](https://github.com/DeviceFarmer/stf)'s API and the\nAndroid Debug Bridge (ADB). With Lariat, the cumbersome process of manually\nconnecting to devices, pushing files, running commands, and retrieving results\nis simplified and made more efficient. It is an ideal solution for automating\ntasks in continuous integration (CI) pipelines.\n\n## Prerequisites\n\n- Python 3.8 or higher\n- ADB installed and added to the system's PATH\n- DeviceFarmer [Access\n Token](https://github.com/DeviceFarmer/stf/blob/master/doc/API.md#authentication)\n- JSON configuration file containing the Swagger spec URL and DeviceFarmer API\n token\n\n## Installation\n\nLariat is available on PyPI:\n\n```sh\npython -m pip install lariat\n```\n\nLariat officially supports Python 3.8+.\n\n## Configuration File\n\nLariat utilizes a JSON configuration file. The default location for this\nconfig file is in the `.lariat` directory within the user's home directory\n(`~/.lariat/config.json`). The configuration file is used to specify the\nfollowing:\n\n- `device_farmer_url`: The URL of the DeviceFarmer instance to connect to.\n - NOTE: Lariat will append the standard API JSON spec to the base url. If\n using a custom path for your spec file, specify a full url ending in\n `.json` or `.yaml`.\n\n- `access_token`: Your DeviceFarmer access token.\n - This can be obtained by logging into your DeviceFarmer UI, and going to\n Settings->Keys->Access Tokens\n\n- `adb_private_key_path`: (Optional) Path to a non-default adb private key.\n Defaults to `~/.android/adbkey` if not specified\n\n- `adb_shell_default_read_timeout_s`: (Optional) Default total timeout (in\n seconds) for reading data from a device. This value may need to be increased\n from the default value of 10s for certain device operations.\n\n```json\n{\n \"access_token\": \"ef02da4fb3884395af4cf011061a2318ca5e9a04abd04de59c5c99afcce0b7fz\",\n \"device_farmer_url\": \"https://my.device-farmer-instance.com/\",\n \"adb_private_key_path\": \"/custom/path/adb.key\",\n \"adb_shell_default_read_timeout_s\": 32.5\n}\n```\n\nAll config options can be specified on the command line to override any\ndefaults set in the config file. For example, the command line option\n`--device_farmer_url` can be used to override the DeviceFarmer URL set in the\nconfig file.\n\n## Usage\n\n```sh\nusage: lariat [-h] [-g | -e EXEC_FILE | -c COMMAND] [--config CONFIG] [-s SELECT [SELECT ...]] [-f FILTER [FILTER ...]]\n [-p PUSH_FILES]\n\nDeviceFarmer automation tool\n\noptional arguments:\n -h, --help show this help message and exit\n -g, --get-devices Enumerate devices on DeviceFarmer instance. Does not execute any commands on devices. Prints JSON results\n to stdout.\n -e EXEC_FILE, --exec-file EXEC_FILE\n Push a file and execute it. Pushes to /data/local/tmp/.\n -c COMMAND, --command COMMAND\n Run a command.\n --config CONFIG Override the default path to the configuration file. Default:/home/larry.espenshade/.lariat/config.json\n -s SELECT [SELECT ...], --select SELECT [SELECT ...]\n Select the fields to be returned by --get-devices (-g). If not specified, all fields are returned.\n -f FILTER [FILTER ...], --filter FILTER [FILTER ...]\n Filter devices via a list of key-value pairs (e.g., sdk=27 manufacturer=SAMSUNG). Non boolean values are\n regex matched\n -p PUSH_FILES, --push-files PUSH_FILES\n Specify the path to the file or directory to be pushed to the device. Pushes to /data/local/tmp/.\n -v, --verbose Increase log level. Can be supplied multiple times to further increase log verbosity (e.g. -vv)\n```\n\n### Device Fields\n\nThe `--select` and `--filter` options both use \"field names\" to perform their\nrespective actions. These field names are JSON keys as defined by DeviceFarmer\nas part of its REST API. You can view supported fields for your DeviceFarmer\ninstallation by navigating to the following URL:\n`https://<device_farmer_url>/api/v1/devices/<serial>` where `device_farmer_url`\nis the URL to your DeviceFarmer installation and `serial` is the serial number\nof one of your devices. The `/<serial>` component can be omitted to view all\nfields of all devices.\n\nField names support dot notation to access nested keys. For example,\n`battery.health` can be used to access the nested `health` key within `battery`.\n\nNote that specifying a `--filter` along with a `--select` will automatically\ninclude any field names specified in the filter in the resulting JSON.\n\n#### Commonly Used Fields\n\n- `abi` - Device Application Binary Interface (eg. `armeabi-v7a`)\n- `manufacturer` - Device Manufacturer\n- `marketName` - Device Marketing Name\n- `model` - Device Model Name\n- `provider.name` - STF Provider hosting this device\n- `version` - Android version\n\n### Examples\n\n- Enumerate all devices on a DeviceFarmer instance:\n\n```sh\nlariat --get-devices\n```\n\n- Limit the fields returned by `--get-devices`:\n\n```sh\nlariat --get-devices --select serial model status\n```\n\n- Filter devices based on specific fields and values (e.g. all Samsung devices\n with SDK level 25-27):\n\n```sh\nlariat --get-devices --filter manufacturer=SAMSUNG sdk=2[5-7]\n```\n\n- Filter devices based on JSON sub-key using dot notation:\n\n```sh\nlariat --get-devices --filter provider.name=my.devicefarmer.com\n```\n\n- Push a local file to the device and execute it:\n\n```sh\nlariat --exec-file path/to/hello\n```\n\n- Run a command on all arm64 devices:\n\n```sh\nlariat --command \"echo hello\" --filter abi=arm64-v8a\n```\n\n- Push files to the device:\n\n```sh\nlariat --push-files path/to/files\n```\n\n### Results\n\nLariat returns results for each device that met the filtering criteria.\n\nThe following is sample output for an `echo` command:\n\n```sh\nlariat --command \"echo hello\" --filter abi=arm64-v8a\n\n\"A12345\": {\n \"output\": \"hello\",\n \"exitcode\": 0,\n},\n\"B54321\": {\n \"output\": \"hello\",\n \"exitcode\": 0,\n},\n\"C678910\": {\n \"reason\": \"Device is currently in use\",\n}\n```\n\nEach result contains different fields depending on the device's availability:\n\n- **If a device is unavailable**, the result will have a single field:\n - `reason`: Specifies why the device was not available for use, such as it being currently in use, not present on the range, etc.\n\n- **If a device is available** and an operation was performed on it, the result will include two fields:\n - `output`: Contains the ADB shell output of the command executed, or details about the action performed.\n - `exitcode`: The return code of the command or action executed on the device.\n\nRemember that a device will either have a 'reason' field (if it was unavailable) or 'output' and 'exitcode' fields\n\n## Docker Image\n\nFor convenience, an official Lariat Docker image is available.\n\nTo use the docker image, simply run\n\n```sh\ndocker run --rm -v ~/.android:/root/.android:ro -v ~/.lariat:/root/.lariat:ro ghcr.io/zetier/lariat:latest -c 'echo hello'\n```\n\nThis docker run command creates and runs a Docker container based on the\nghcr.io/zetier/lariat:latest image. It performs the following actions:\n\n- Creates a read-only volume mount for the .android directory on the host\n machine, which contains ADB keys, to the /root/.android directory inside the\n container.\n\n- Creates a read-only volume mount for the .lariat directory on the host\n machine, which contains the lariat configuration file, to the\n /root/.lariat directory inside the container.\n\n- The --rm flag ensures that the container is automatically removed after it\n exits.\n\n- Inside the container, the command `lariat -c 'echo hello'` is executed,\n which prints \"hello\" as the output on every lockable device on your\n DeviceFarmer range.\n\nPlease note that if your ADB keys and configuration file are located in\ndifferent directories on the host machine, you may need to modify the docker run\ncommand accordingly to provide the correct paths for volume mounting.\n\n## CI Integration\n\nLariat was designed for simple integration into CI pipelines. Below is an\nexample of a GitLab job that tests a binary built in the pipeline on a\nDeviceFarmer range:\n\n```yml\n.lariat-test:\n image:\n name: ghcr.io/zetier/lariat:latest\n entrypoint: [\"\"]\n stage: test\n before_script:\n # Copy keys and configs from private CI variables\n - mkdir -p ~/.android\n - echo \"$CI_ADB_PUB_KEY\" > ~/.android/adbkey.pub\n - echo \"$CI_ADB_PRI_KEY\" > ~/.android/adbkey\n - echo \"$CI_FARMHAND_CONFIG\" -> ~/.lariat/config.json\n script:\n - lariat --file $BINARY > test_results.json\n artifacts:\n paths:\n - test_results.json\n\n# Assumes a `build-android-binary` job that produces `android_binary`\n# as an artifact.\nandroid-range-test:\n extends: .lariat-test\n variables:\n BINARY: android_binary\n needs:\n - build-android-binary\n\n```\n\n## Notes\n\n- Lariat will lock devices before performing any operations to ensure\n exclusive access. After the operations have been completed, the devices will\n be unlocked.\n\n- Make sure to provide the correct path to the ADB private key file via\n `~/.lariat/config.json` if it is different from the default location\n (`~/.android/adbkey`)\n\n- By default, Lariat will enumerate ~every~ device on the DeviceFarmer range,\n including ones not `present` or `ready`. You can modify this as needed by\n specifying `--filter ready=true present=true` if needed.\n\n## Development Install\n\nNOTE: Lariat is developed on and regularly tested with Ubuntu 18.04 with\nPython 3.8. Other distributions and versions may work, but are currently\nuntested.\n\n1. Clone the repository\n\n2. Install dependencies along with the Lariat python package\n\n ```sh\n sudo apt-get update\n sudo apt-get install python3-venv python3.8-venv -y\n python3.8 -m venv venv\n source venv/bin/activate\n (venv) pip install --upgrade pip\n (venv) pip install .\n ```\n\n3. Create a [configuration file](#configuration-file)\n\n## Contributing\n\nContributions are welcome! If you find any issues or have suggestions for\nimprovements, please open an issue or submit a pull request.\n\n## License\n\nThis project is licensed under the GPLv2 License.\n",
"bugtrack_url": null,
"license": null,
"summary": "Python utility for interfacing with the Device Farmer API",
"version": "0.2.0",
"project_urls": {
"Bug Tracker": "https://github.com/Zetier/lariat/issues",
"Homepage": "https://github.com/Zetier/lariat"
},
"split_keywords": [
"lariat",
" devicefarmer",
" stf",
" automation",
" android",
" testing",
" openstf"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "3d5b7ddd5ce27fcd8989914d1a8d94cb0baa020bec5508c8286da3bdc5e1fc10",
"md5": "9411918fd9664d8be7f005a294fca704",
"sha256": "a1cef66b494f3605f66e4ebd6e94082813ab9b5318e38ac9f074e07299dd3e6b"
},
"downloads": -1,
"filename": "lariat-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "9411918fd9664d8be7f005a294fca704",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 20628,
"upload_time": "2024-09-12T17:32:19",
"upload_time_iso_8601": "2024-09-12T17:32:19.383094Z",
"url": "https://files.pythonhosted.org/packages/3d/5b/7ddd5ce27fcd8989914d1a8d94cb0baa020bec5508c8286da3bdc5e1fc10/lariat-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "dae8c0b0e5fdf1c3e2110d99dfbd269c703ad0946168bc194d0b5777d94dff83",
"md5": "70c54de7791660d4434af1a8a98cf7f3",
"sha256": "768b908778f5a33bd84e92a0ac0f4713ad863f6549c8417e1290051efca79e76"
},
"downloads": -1,
"filename": "lariat-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "70c54de7791660d4434af1a8a98cf7f3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 23520,
"upload_time": "2024-09-12T17:32:21",
"upload_time_iso_8601": "2024-09-12T17:32:21.096768Z",
"url": "https://files.pythonhosted.org/packages/da/e8/c0b0e5fdf1c3e2110d99dfbd269c703ad0946168bc194d0b5777d94dff83/lariat-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-12 17:32:21",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Zetier",
"github_project": "lariat",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "lariat"
}