This commit is contained in:
2019-09-15 09:42:16 -04:00
parent a847561beb
commit ac66581088
6 changed files with 87 additions and 18 deletions
+6 -1
View File
@@ -1,4 +1,9 @@
# Bashfile: Like Make + Bash, Combined.
# `bashf`: Kinda like Make & Bash, combined.
I love using `Makefile`s for one-off tasks in projects. The problem with doing this, is you can't use familiar bashisms when doing so, as GNU Make doesn't use the familiar Bash sytnax. This project seeks to bridge these works.
## Installation
Install `bashf` via:
$ brew install kennethreitz/-/bashf --head
+33 -2
View File
@@ -4,6 +4,9 @@ bash.py module
import re
import time
import json as json_lib
import os
import stat
from tempfile import mkstemp
from shlex import quote as shlex_quote
import delegator
@@ -100,9 +103,37 @@ class Bash:
"""execute the bash process as a child of this process"""
return BashProcess(parent=self, args=args, **kwargs)
def command(self, script: str, **kwargs) -> BashProcess:
def command(self, script: str, debug=False, **kwargs) -> BashProcess:
"""form up the command with shlex and execute"""
return self._exec(f"-c {shlex_quote(script)}", **kwargs)
tf = mkstemp(suffix='.sh', prefix='bashf-')[1]
with open(tf, 'w') as f:
f.write(script)
# Mark the temporary file as executable.
st = os.stat(tf)
os.chmod(tf, st.st_mode | stat.S_IEXEC)
stdlib_path = os.path.join(
os.path.dirname(__file__), 'scripts', 'stdlib.sh'
)
# print(stdlib_path)
# cmd = f"bash -c {(script)}"
script = shlex_quote(f"unbuffer {tf} 2>&1 | bashf-indent")
cmd = f'bash --init-file {shlex_quote(stdlib_path)} -i -c {script} '
if debug:
print(cmd)
return_code = os.system(cmd)
if not debug:
# Cleanup temporary file.
os.remove(tf)
return return_code
def run(script=None, **kwargs):
-3
View File
@@ -48,9 +48,6 @@ class TaskScript:
if t.name not in [task.name for task in tasks]:
tasks.insert(i + 1, t)
# if reverse:
# tasks = list(reversed(tasks))
return tasks
@classmethod
+35 -11
View File
@@ -4,6 +4,10 @@ import crayons
from .bashfile import Bashfile
def indent(line):
return f'{" " * 4}{line}'
@click.command()
@click.argument(
'task',
@@ -31,6 +35,13 @@ from .bashfile import Bashfile
multiple=True,
help='task environment variable (can be passed multiple times).',
)
@click.option(
'--fail',
'-x',
is_flag=True,
type=click.BOOL,
help='Fail immediately, if any task fails.',
)
@click.option(
'--arg',
'-a',
@@ -39,6 +50,9 @@ from .bashfile import Bashfile
multiple=True,
help='task ARGV arguments (can be passed multiple times).',
)
@click.option(
'--quiet', '-q', is_flag=True, type=click.BOOL, help='Reduce output.'
)
@click.option(
'--environ-json',
'-j',
@@ -47,7 +61,17 @@ from .bashfile import Bashfile
help='environment variables, in JSON format.',
)
def task(
*, task, bashfile, arg, _list, environ, environ_json, shellcheck, debug
*,
task,
bashfile,
arg,
_list,
environ,
fail,
environ_json,
shellcheck,
debug,
quiet,
):
"""bashf — Bashfile runner (the familiar Bash/Make hybrid)."""
# Default to list behavior, when no task is provided.
@@ -80,22 +104,22 @@ def task(
try:
task = bashfile[task]
except KeyError:
click.echo(f'Task {task!r} does not exist!')
click.echo(crayons.red(f'Task {task!r} does not exist!'))
sys.exit(1)
# print(task)
for task in task.depends_on(recursive=True):
cmd = task.execute()
if not quiet:
click.echo(crayons.yellow(f'Executing task {task.name!r}'))
return_code = task.execute()
for line in cmd.output:
click.echo(line, nl=False, err=False)
if fail:
if not return_code == 0:
click.echo(f'Task {task.name!r} failed!')
sys.exit(return_code)
# if cmd.err:
# click.echo(cmd.err, nl=False, err=True)
if not cmd.ok:
click.echo(f'Task {task.name!r} failed!')
sys.exit(cmd.return_code)
click.echo('Done!')
sys.exit(0)
def entrypoint():
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
if [ "$(uname)" == Darwin ]; then
bashf-sed() { command sed -l "$@"; }
else
bashf-sed() { command sed -u "$@"; }
fi
# Syntax sugar.
bashf-indent() {
bashf-sed "s/^/ /"
}
+1 -1
View File
@@ -21,7 +21,7 @@ REQUIRES_PYTHON = '>=3.6.0'
VERSION = '0.1.0'
# What packages are required for this module to be executed?
REQUIRED = ['click', 'delegator.py', 'crayons', 'bash.py']
REQUIRED = ['click', 'delegator.py', 'crayons']
# What packages are optional?
EXTRAS = {