Metadata-Version: 2.1
Name: taskr-cli
Version: 1.2.0
Summary: A language agnostic make-like tool meant for python projects
Home-page: https://gitlab.com/peasleyk/taskr
Author: Kyle Peasley
Description-Content-Type: text/markdown
License-File: LICENSE

# Taskr
[![PyPI version](https://badge.fury.io/py/taskr-cli.svg)](https://badge.fury.io/py/taskr-cli)

A magical, zero dependency, easy to use task runner with an original name. Inspired by [Mage](https://github.com/magefile/mage), a task runner for go. Made for python projects, but usable for any.

All that's needed is a tasks.py file.

A few highlights

- Basically make(1) without a build system
- Zero dependencies
- Auto load environment variable files
- Easily pass arguments
- Auto generated cli docs
- Usable in any subdirectory of your project
- Built functions to easily run system commands inside venvs
- Built in helper functions for python development

## Installing

```bash
pip install taskr-cli
```

Check the version
```bash
taskr --version
```

Generate a default tasks file
```bash
taskr --init
```

## Using

Make sure you have a tasks.py file defined in the root of your repo.
`taskr` can be used from any sub directory if `PYENV_DIR` or 'TASKR_DIR' is set to the projects root. There is a utility function "injectPath()" that can do this for you, included in the template. See other utility functions at the bottom of the readme.

CLI:

```bash
[master●] » taskr -h
usage: taskr [-h] [-d] [-i] [-l] [-p] [-v]

A cli utility to run generic tasks

options:
  -h, --help     show this help message and exit
  -d, --docker   run a task in an image with docker. Requires IMAGE_NAME set in tasks.py
  -i, --init     generate a template task file
  -l, --list     show defined tasks
  -p, --podman   run a task in an image with podman. Requires IMAGE_NAME set in tasks.py
  -v, --version  show the version number

```

When listing tasks, taskr will attempt to grab either the docblock or a single # comment above the function name and show it for cli documentation. Taskr will prefer the single # comment over the docblock to display if both exist.

Also note, any functions in `tasks.py` that start with an underscore are ignored when listing.

```bash
[master●] » taskr -l

Tasks:
 all         : Runs all static analysis tools
 build       : Builds the wheel
 clean       : Remove build artifacts, cache, etc.
 flake       : Check flake8
 fmt         : Run black
 mypy        : Checks types
 *reinstall  : Re-installs taskr
 sort        : Sort imports
 test        : Run tests
 upload_prod : Send it for real!
 upload_test : Send it!

* = default
```

To run a task, just pass the name of a function after `taskr`. Output from a task will be displayed

```bash
[master●] » taskr format
All done! ✨ 🍰 ✨
11 files left unchanged.
```


## Configuration Variables

There are a few configuration setting, set in tasks.py itself. They are all uppercase and generally should be at the top of the file

`VENV_REQUIRED = True` in your tasks.py file, taskr will only run if it detects it's running in a virtual environment. You can delete it otherwise

`DEFAULT = "some_func"` marks the task as default. Default tasks are run when `taskr` is run without any arguments

`ENV = path/to/file.env` will load that environment file before every task

`IMAGE_NAME = "my_docker_image"` will enable running tasks inside an image, though docker or podman (the image needs taskr-cli on the path). Details are down below

```python
from taskr import runners

DEFAULT = "test"
VENV_REQUIRED = True
ENVS = "dev.env"
IMAGE_NAME = "dev_image"

# Run tests, and passed dev.env vars when running
def test
  runners.run("python -m pytest tests/")
```

## Running Any Task in an Image

Taskr can also attempt to run inside a built image. `-d` will attempt docker, and `-p` will attemty to use podman
`IMAGE_NAME` Needs to be set in taskr.py so taskr knows what to run inside of. Right now, this is just though a subprocess call of
`run --rm -a stdin -a stdout` to an image that has taskr-cli in its path

## Helpful functions for running tasks

A few task running methods are provided for system running tasks. Taskr expects task functions to return either  ```True``` (The task was successful) for ```False```it failed. To determine if a subprocess/system call was successful or not, taskr looks at the return code of the called program. 0 is success, anything else fails.

Taskr will auto copy your existing environment variables when running tasks, so running tasks with programs installed in a virual environment (i.e. dev tools though pip) will work.

You can also run any code you want as well under a task, these are just helpful wrappers around subprocess that work nicely with taskr.

### run
`run`'s argument can be either a list, or a string. A list is parsed into one command, not multiple

Optionally pass a an environment dictionary to be used at runtime.

```python
from taskr import runners

# Run flake8
def flake_list() -> bool:
    return runners.run(["python", "-m", "flake8", "taskr/*.py"])

# Run tests
def flake() -> bool:
    return runners.run("python -m pytest tests/ -v")

# Build a wheel
def build():
  ENV = {
    "PRODUCTION": "true"
  }
  return runners.run("python setup.py install", ENV)

```

### run_conditional
`run_conditional` is a way to run tasks (functions) in order, as long as the previous task returns a non failure return code (False).
You can throw normal python functions in here to

```python
from taskr import runners
import some_package as sp

# Run black
def fmt():
    return runners.run("python -m black .")

# Check flake8
def flake():
    return runners.run(["python", "-m", "flake8", "taskr/*.py"])

# Run all static tools
def all():
    return runners.run_conditional(flake, fmt, sp.function)
```

### run_output
`run_output`' will run a command and return the output

```python
from taskr import runners

# Get the number of env variables
def _get_count():
    ret = runners.run_output("env | wc -l")
    print(ret.status) # True 
    print(ret.stdout) # "90"
    print(ret.sterr)  # ""
```

You can an environment dict to this function.

## Passing arguments to functions

You can also pass arbitrary arguments to any defined function. For example, passing the environment to starting a server.
This requires the function to have a default argument set.

```python
def start(env: str = "Dev"):
  ENVS = {
    "ENV": env
    "WATCH": "true"
  }
  return taskr.run("python start.py", ENVS)

```

And from the command line
```bash
taskr start dev
# Or
taskr start
# Or
taskr start prod

```

You can also use key word arguments so pass only selected arguments. This requires all previous arguments to have a default value.

```python
def start(env: str = "dev", timeout: int = 180):
  ENVS = {
    "ENV": env
    "WATCH": "true"
    "TIMEOUT": timeout
  }
  return taskr.run("python start.py", ENVS)

```

Only passing timeout in this example, and keeping env to be the default `dev`

```bash
taskr start timeout=60
```

## Utilities

There are a few utility functions included, mostly for python package development.

```python
from taskr import utils

# Removes dist/build folders
utils.cleanBuilds()

# Remove compiled files and folders
utils.cleanCompiles()

# In a venv or not
utils.inVenv()

# Transforms an ENV file into a dict
utils.readEnvFile(filename)

# Bumps setup.py's version number by 0.0.1, or replaces it with argument
utils.bumpVersion(version=None):

# Adds `export TASKR_DIR=CWD' to your VENV activation, so
# you can use taskr from any location in the VENV (e.g. sub directories)
utils.addTaskrToEnv()
```

## Developing

This project uses pipenv. Make sure it's installed. Then call
```bash
python -m pipenv shell
pipenv install --dev
taskr check
taskr test
```

There are numerous tests in ```taskr/tests``` which cover most functionality that's testable, as well as examples
