This commit is contained in:
2019-09-15 11:57:35 -04:00
parent 15eb5740c5
commit 7f5609de0d
4 changed files with 161 additions and 26 deletions
+85 -16
View File
@@ -1,5 +1,9 @@
import sys
import os
import json
from random import randint
import click
from .bash import Bash
@@ -14,13 +18,60 @@ class TaskNotInBashfile(ValueError):
pass
class FlagNotAvailable(ValueError):
class FilterNotAvailable(ValueError):
pass
class Flag:
def __init__(self, name):
self.name = name
class TaskFilter:
def __init__(self, s):
self.source = s
def __str__(self):
return f"{self.source!r}"
@property
def name(self):
return self.source.split(":", 1)[0][len("@") :]
@property
def args(self):
args = {}
try:
for arg in self.source.split(":", 1)[1].split(":"):
split = arg.split("=", 1)
key = split[0]
value = split[1] if len(split) == 2 else True
args[key] = value
except IndexError:
pass
return args
def depends_on(self, **kwargs):
return []
@staticmethod
def execute_confirm(*, prompt=False, yes=False, secure=False, **kwargs):
if not yes:
if secure:
int1 = randint(0, 12)
int2 = randint(0, 12)
user_value = click.prompt(f" What is {int1} times {int2}?")
if int(user_value) != int1 * int2:
sys.exit(1)
else:
click.confirm(" Do you want to continue?", abort=True)
def execute(self, yes=False, **kwargs):
if self.name == "confirm":
self.execute_confirm(yes=yes, **self.args)
class TaskScript:
@@ -34,6 +85,9 @@ class TaskScript:
def __repr__(self):
return f"<TaskScript name={self.name!r} depends_on={self.depends_on(recursive=True)!r}>"
def __str__(self):
return f"{self.name!r}"
@property
def declaration_line(self):
for line in self.bashfile.source_lines:
@@ -41,21 +95,36 @@ class TaskScript:
return line
def depends_on(self, *, reverse=False, recursive=False):
def gen_tasks():
task_names = self.declaration_line.split(":")[1].split()
task_indexes = [self.bashfile.find_chunk(task_name=n) for n in task_names]
for i in task_indexes:
yield TaskScript(bashfile=self.bashfile, chunk_index=i)
def gen_actions():
task_strings = self.declaration_line.split(":", 1)[1].split()
# Filter out filters.
# filters = [t for t in task_names if t.startswith("@")]
tasks = [t for t in gen_tasks()]
# for filter in filters:
# del task_names[task_names.index(filter)]
task_name_index_tuples = [
(self.bashfile.find_chunk(task_name=s), s) for s in task_strings
]
for i, task_string in task_name_index_tuples:
if i is None:
# Create the filter.
yield TaskFilter(task_string)
else:
# Otherwise, create the task.
yield TaskScript(bashfile=self.bashfile, chunk_index=i)
actions = [t for t in gen_actions()]
if recursive:
for i, task in enumerate(tasks[:]):
for i, task in enumerate(actions[:]):
for t in reversed(task.depends_on()):
if t.name not in [task.name for task in tasks]:
tasks.insert(i + 1, t)
if t.name not in [task.name for task in actions]:
actions.insert(i + 1, t)
return tasks
return actions
@classmethod
def _from_chunk_index(Class, bashfile, *, i):
@@ -68,7 +137,7 @@ class TaskScript:
if line.startswith(indent_style):
return line[len(indent_style) :]
def execute(self, blocking=False):
def execute(self, *, blocking=False, **kwargs):
bash = Bash(environ=self.bashfile.environ)
return bash.command(self.source, blocking=False)
@@ -100,7 +169,7 @@ class TaskScript:
class Bashfile:
def __init__(self, *, path):
self.path = path
self.environ = {}
self.environ = os.environ
self._chunks = []
if not os.path.exists(path):
+42 -10
View File
@@ -3,6 +3,8 @@ import click
import crayons
from .bashfile import Bashfile
SAFE_ENVIRONS = ["HOME"]
def indent(line):
return f'{" " * 4}{line}'
@@ -35,6 +37,7 @@ def indent(line):
multiple=True,
help="task environment variable (can be passed multiple times).",
)
@click.option("--yes", is_flag=True, help="Set prompts to yes.")
@click.option(
"--fail",
"-x",
@@ -42,6 +45,13 @@ def indent(line):
type=click.BOOL,
help="Fail immediately, if any task fails.",
)
@click.option(
"--secure",
"-s",
is_flag=True,
type=click.BOOL,
help="Ignore parent shell's environment variables.",
)
@click.option(
"--arg",
"-a",
@@ -59,7 +69,19 @@ def indent(line):
help="environment variables, in JSON format.",
)
def task(
*, task, bashfile, arg, _list, environ, fail, environ_json, shellcheck, debug, quiet
*,
task,
bashfile,
arg,
_list,
environ,
fail,
environ_json,
shellcheck,
debug,
quiet,
secure,
yes,
):
"""bashf — Bashfile runner (the familiar Bash/Make hybrid)."""
# Default to list behavior, when no task is provided.
@@ -70,6 +92,10 @@ def task(
if bashfile == "__BASHFILE__":
bashfile = Bashfile.find(root=".")
if secure:
for key in bashfile.environ:
if key not in SAFE_ENVIRONS:
del bashfile.environ[key]
if environ_json:
bashfile.add_environ_json(environ_json)
@@ -92,27 +118,33 @@ def task(
try:
task = bashfile[task]
except KeyError:
click.echo(crayons.red(f"Task {task!r} does not exist!"))
click.echo(crayons.red(f"Task {task} does not exist!"))
sys.exit(1)
def execute_task(task):
def execute_task(task, *, next_task=None):
if not quiet:
click.echo(
crayons.white(" · ")
+ crayons.yellow(f"Executing task {task.name!r}")
+ crayons.white("")
+ crayons.yellow(f"Executing {task}")
+ crayons.white(" · ")
)
return_code = task.execute()
return_code = task.execute(yes=yes, next_task=next_task)
if fail:
if not return_code == 0:
click.echo(f"Task {task.name!r} failed!")
click.echo(f"Task {task} failed!")
sys.exit(return_code)
for task in task.depends_on(recursive=True) + [task]:
execute_task(task)
tasks = task.depends_on(recursive=True) + [task]
for task in tasks:
try:
next_task = tasks[tasks.index(task) + 1]
except IndexError:
next_task = None
click.echo(crayons.white(" · ") + crayons.green("Done") + crayons.white("!"))
execute_task(task, next_task=next_task)
click.echo(crayons.white(" · ") + crayons.green("Done") + crayons.white(" · "))
sys.exit(0)
View File
+34
View File
@@ -10,3 +10,37 @@ fi
bashf-indent() {
bashf-sed "s/^/ /"
}
# ---------------------
# From: https://github.com/heroku/buildpack-stdlib/blob/master/stdlib.sh
# Buildpack Steps.
puts_step() {
if [[ "$*" == "-" ]]; then
read -r output
else
output=$*
fi
echo -e "\\e[1m\\e[36m=== $output\\e[0m"
unset output
}
# Buildpack Error.
puts_error() {
if [[ "$*" == "-" ]]; then
read -r output
else
output=$*
fi
echo -e "\\e[1m\\e[31m=!= $output\\e[0m"
}
# Buildpack Warning.
puts_warn() {
if [[ "$*" == "-" ]]; then
read -r output
else
output=$*
fi
echo -e "\\e[1m\\e[33m=!= $output\\e[0m"
}