mirror of
https://github.com/kennethreitz/bake.git
synced 2026-06-05 23:00:17 +00:00
improvements all around
This commit is contained in:
@@ -3,7 +3,7 @@ echo:
|
||||
format:
|
||||
black .
|
||||
|
||||
full-install: system-deps python-deps
|
||||
full-install: system-deps python-deps @confirm
|
||||
install: node-deps python-deps
|
||||
|
||||
python-deps:
|
||||
|
||||
+8
-10
@@ -36,9 +36,7 @@ class BashProcess:
|
||||
args = " ".join(args)
|
||||
self.start_time = time.time()
|
||||
self.sub = delegator.run(
|
||||
f"{self.parent.path} {args}",
|
||||
env=self.parent.environ,
|
||||
block=blocking,
|
||||
f"{self.parent.path} {args}", env=self.parent.environ, block=blocking
|
||||
)
|
||||
if blocking:
|
||||
self.elapsed_time = time.time() - self.start_time
|
||||
@@ -75,7 +73,9 @@ class BashProcess:
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""string representation of the bash process"""
|
||||
return f"<BashProcess pid={self.sub.pid!r} return_code={self.sub.return_code!r}>"
|
||||
return (
|
||||
f"<BashProcess pid={self.sub.pid!r} return_code={self.sub.return_code!r}>"
|
||||
)
|
||||
|
||||
|
||||
class Bash:
|
||||
@@ -106,23 +106,21 @@ class Bash:
|
||||
def command(self, script: str, debug=False, **kwargs) -> BashProcess:
|
||||
"""form up the command with shlex and execute"""
|
||||
|
||||
tf = mkstemp(suffix='.sh', prefix='bashf-')[1]
|
||||
tf = mkstemp(suffix=".sh", prefix="bashf-")[1]
|
||||
|
||||
with open(tf, 'w') as f:
|
||||
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'
|
||||
)
|
||||
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} '
|
||||
cmd = f"bash --init-file {shlex_quote(stdlib_path)} -i -c {script} "
|
||||
|
||||
if debug:
|
||||
print(cmd)
|
||||
|
||||
+21
-19
@@ -3,7 +3,7 @@ import json
|
||||
|
||||
from .bash import Bash
|
||||
|
||||
INDENT_STYLES = ('\t', ' ' * 4)
|
||||
INDENT_STYLES = ("\t", " " * 4)
|
||||
|
||||
|
||||
class NoBashfileFound(RuntimeError):
|
||||
@@ -14,6 +14,15 @@ class TaskNotInBashfile(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class FlagNotAvailable(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class Flag:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
class TaskScript:
|
||||
def __init__(self, bashfile, chunk_index=None):
|
||||
self.bashfile = bashfile
|
||||
@@ -33,10 +42,8 @@ class TaskScript:
|
||||
|
||||
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
|
||||
]
|
||||
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)
|
||||
|
||||
@@ -67,7 +74,7 @@ class TaskScript:
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.chunk[0].split(':')[0].strip()
|
||||
return self.chunk[0].split(":")[0].strip()
|
||||
|
||||
@property
|
||||
def chunk(self):
|
||||
@@ -81,7 +88,7 @@ class TaskScript:
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return '\n'.join([s for s in self._iter_source()])
|
||||
return "\n".join([s for s in self._iter_source()])
|
||||
|
||||
@property
|
||||
def source_lines(self):
|
||||
@@ -132,7 +139,7 @@ class Bashfile:
|
||||
|
||||
def find_chunk(self, task_name):
|
||||
for i, chunk in enumerate(self.chunks):
|
||||
if chunk[0].split(':')[0].strip() == task_name:
|
||||
if chunk[0].split(":")[0].strip() == task_name:
|
||||
return i
|
||||
|
||||
def __iter__(self):
|
||||
@@ -147,7 +154,7 @@ class Bashfile:
|
||||
except json.JSONDecodeError:
|
||||
assert os.path.exists(s)
|
||||
# Assume a path was passed, instead.
|
||||
with open(s, 'r') as f:
|
||||
with open(s, "r") as f:
|
||||
j = json.load(f)
|
||||
|
||||
self.environ.update(j)
|
||||
@@ -158,34 +165,29 @@ class Bashfile:
|
||||
|
||||
@classmethod
|
||||
def find(
|
||||
Class,
|
||||
*,
|
||||
filename='Bashfile',
|
||||
root=os.getcwd(),
|
||||
max_depth=4,
|
||||
topdown=True,
|
||||
Class, *, filename="Bashfile", root=os.getcwd(), max_depth=4, topdown=True
|
||||
):
|
||||
"""Returns the path of a Pipfile in parent directories."""
|
||||
i = 0
|
||||
for c, d, f in os.walk(root, topdown=topdown):
|
||||
if i > max_depth:
|
||||
raise NoBashfileFound('No {filename} found!')
|
||||
raise NoBashfileFound("No {filename} found!")
|
||||
elif filename in f:
|
||||
return Class(path=os.path.join(c, filename))
|
||||
i += 1
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
with open(self.path, 'r') as f:
|
||||
with open(self.path, "r") as f:
|
||||
return f.read()
|
||||
|
||||
@property
|
||||
def source_lines(self):
|
||||
return self.source.split('\n')
|
||||
return self.source.split("\n")
|
||||
|
||||
@staticmethod
|
||||
def _is_declaration_line(line):
|
||||
return not (line.startswith(' ') or line.startswith('\t'))
|
||||
return not (line.startswith(" ") or line.startswith("\t"))
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
|
||||
+40
-46
@@ -10,78 +10,66 @@ def indent(line):
|
||||
|
||||
@click.command()
|
||||
@click.argument(
|
||||
'task',
|
||||
"task",
|
||||
type=click.STRING,
|
||||
default='__LIST_ALL__',
|
||||
default="__LIST_ALL__",
|
||||
envvar="BASHFILE_TASK",
|
||||
# required=False,
|
||||
)
|
||||
@click.option(
|
||||
'--bashfile',
|
||||
'-b',
|
||||
default='__BASHFILE__',
|
||||
envvar='BASHFILE_PATH',
|
||||
"--bashfile",
|
||||
"-b",
|
||||
default="__BASHFILE__",
|
||||
envvar="BASHFILE_PATH",
|
||||
nargs=1,
|
||||
type=click.Path(),
|
||||
)
|
||||
@click.option('--list', '-l', '_list', default=False, is_flag=True)
|
||||
@click.option('--debug', default=False, is_flag=True, hidden=True)
|
||||
@click.option('--shellcheck', default=False, is_flag=True, hidden=True)
|
||||
@click.option("--list", "-l", "_list", default=False, is_flag=True)
|
||||
@click.option("--debug", default=False, is_flag=True, hidden=True)
|
||||
@click.option("--shellcheck", default=False, is_flag=True, hidden=True)
|
||||
@click.option(
|
||||
'--environ',
|
||||
'-e',
|
||||
"--environ",
|
||||
"-e",
|
||||
nargs=2,
|
||||
type=click.STRING,
|
||||
multiple=True,
|
||||
help='task environment variable (can be passed multiple times).',
|
||||
help="task environment variable (can be passed multiple times).",
|
||||
)
|
||||
@click.option(
|
||||
'--fail',
|
||||
'-x',
|
||||
"--fail",
|
||||
"-x",
|
||||
is_flag=True,
|
||||
type=click.BOOL,
|
||||
help='Fail immediately, if any task fails.',
|
||||
help="Fail immediately, if any task fails.",
|
||||
)
|
||||
@click.option(
|
||||
'--arg',
|
||||
'-a',
|
||||
"--arg",
|
||||
"-a",
|
||||
nargs=1,
|
||||
type=click.STRING,
|
||||
multiple=True,
|
||||
help='task ARGV arguments (can be passed multiple times).',
|
||||
help="task ARGV arguments (can be passed multiple times).",
|
||||
)
|
||||
@click.option("--quiet", "-q", is_flag=True, type=click.BOOL, help="Reduce output.")
|
||||
@click.option(
|
||||
'--quiet', '-q', is_flag=True, type=click.BOOL, help='Reduce output.'
|
||||
)
|
||||
@click.option(
|
||||
'--environ-json',
|
||||
'-j',
|
||||
"--environ-json",
|
||||
"-j",
|
||||
nargs=1,
|
||||
type=click.STRING,
|
||||
help='environment variables, in JSON format.',
|
||||
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
|
||||
):
|
||||
"""bashf — Bashfile runner (the familiar Bash/Make hybrid)."""
|
||||
# Default to list behavior, when no task is provided.
|
||||
|
||||
if task == '__LIST_ALL__':
|
||||
if task == "__LIST_ALL__":
|
||||
_list = True
|
||||
task = None
|
||||
|
||||
if bashfile == '__BASHFILE__':
|
||||
bashfile = Bashfile.find(root='.')
|
||||
if bashfile == "__BASHFILE__":
|
||||
bashfile = Bashfile.find(root=".")
|
||||
|
||||
if environ_json:
|
||||
bashfile.add_environ_json(environ_json)
|
||||
@@ -104,21 +92,27 @@ 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!r} does not exist!"))
|
||||
sys.exit(1)
|
||||
|
||||
# print(task)
|
||||
for task in task.depends_on(recursive=True):
|
||||
def execute_task(task):
|
||||
if not quiet:
|
||||
click.echo(crayons.yellow(f'Executing task {task.name!r}…'))
|
||||
click.echo(
|
||||
crayons.white(" · ")
|
||||
+ crayons.yellow(f"Executing task {task.name!r}")
|
||||
+ crayons.white("…")
|
||||
)
|
||||
return_code = task.execute()
|
||||
|
||||
if fail:
|
||||
if not return_code == 0:
|
||||
click.echo(f'Task {task.name!r} failed!')
|
||||
click.echo(f"Task {task.name!r} failed!")
|
||||
sys.exit(return_code)
|
||||
|
||||
click.echo('Done!')
|
||||
for task in task.depends_on(recursive=True) + [task]:
|
||||
execute_task(task)
|
||||
|
||||
click.echo(crayons.white(" · ") + crayons.green("Done") + crayons.white("!"))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@@ -126,12 +120,12 @@ def entrypoint():
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print('ool beans.')
|
||||
print("ool beans.")
|
||||
|
||||
|
||||
def main():
|
||||
task()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
entrypoint()
|
||||
|
||||
@@ -12,16 +12,16 @@ from shutil import rmtree
|
||||
from setuptools import find_packages, setup, Command
|
||||
|
||||
# Package meta-data.
|
||||
NAME = 'bashf'
|
||||
DESCRIPTION = 'The familar Make / Bash hybrid.'
|
||||
URL = 'https://github.com/kennethreitz/bashfile'
|
||||
EMAIL = 'me@kennethreitz.org'
|
||||
AUTHOR = 'Kenneth Reitz'
|
||||
REQUIRES_PYTHON = '>=3.6.0'
|
||||
VERSION = '0.1.0'
|
||||
NAME = "bashf"
|
||||
DESCRIPTION = "The familar Make / Bash hybrid."
|
||||
URL = "https://github.com/kennethreitz/bashfile"
|
||||
EMAIL = "me@kennethreitz.org"
|
||||
AUTHOR = "Kenneth Reitz"
|
||||
REQUIRES_PYTHON = ">=3.6.0"
|
||||
VERSION = "0.1.0"
|
||||
|
||||
# What packages are required for this module to be executed?
|
||||
REQUIRED = ['click', 'delegator.py', 'crayons']
|
||||
REQUIRED = ["click", "delegator.py", "crayons"]
|
||||
|
||||
# What packages are optional?
|
||||
EXTRAS = {
|
||||
@@ -38,8 +38,8 @@ here = os.path.abspath(os.path.dirname(__file__))
|
||||
# Import the README and use it as the long-description.
|
||||
# Note: this will only work if 'README.md' is present in your MANIFEST.in file!
|
||||
try:
|
||||
with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f:
|
||||
long_description = '\n' + f.read()
|
||||
with io.open(os.path.join(here, "README.md"), encoding="utf-8") as f:
|
||||
long_description = "\n" + f.read()
|
||||
except FileNotFoundError:
|
||||
long_description = DESCRIPTION
|
||||
|
||||
@@ -47,22 +47,22 @@ except FileNotFoundError:
|
||||
about = {}
|
||||
if not VERSION:
|
||||
project_slug = NAME.lower().replace("-", "_").replace(" ", "_")
|
||||
with open(os.path.join(here, project_slug, '__version__.py')) as f:
|
||||
with open(os.path.join(here, project_slug, "__version__.py")) as f:
|
||||
exec(f.read(), about)
|
||||
else:
|
||||
about['__version__'] = VERSION
|
||||
about["__version__"] = VERSION
|
||||
|
||||
|
||||
class UploadCommand(Command):
|
||||
"""Support setup.py upload."""
|
||||
|
||||
description = 'Build and publish the package.'
|
||||
description = "Build and publish the package."
|
||||
user_options = []
|
||||
|
||||
@staticmethod
|
||||
def status(s):
|
||||
"""Prints things in bold."""
|
||||
print('\033[1m{0}\033[0m'.format(s))
|
||||
print("\033[1m{0}\033[0m".format(s))
|
||||
|
||||
def initialize_options(self):
|
||||
pass
|
||||
@@ -72,22 +72,20 @@ class UploadCommand(Command):
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.status('Removing previous builds…')
|
||||
rmtree(os.path.join(here, 'dist'))
|
||||
self.status("Removing previous builds…")
|
||||
rmtree(os.path.join(here, "dist"))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
self.status('Building Source and Wheel (universal) distribution…')
|
||||
os.system(
|
||||
'{0} setup.py sdist bdist_wheel --universal'.format(sys.executable)
|
||||
)
|
||||
self.status("Building Source and Wheel (universal) distribution…")
|
||||
os.system("{0} setup.py sdist bdist_wheel --universal".format(sys.executable))
|
||||
|
||||
self.status('Uploading the package to PyPI via Twine…')
|
||||
os.system('twine upload dist/*')
|
||||
self.status("Uploading the package to PyPI via Twine…")
|
||||
os.system("twine upload dist/*")
|
||||
|
||||
self.status('Pushing git tags…')
|
||||
os.system('git tag v{0}'.format(about['__version__']))
|
||||
os.system('git push --tags')
|
||||
self.status("Pushing git tags…")
|
||||
os.system("git tag v{0}".format(about["__version__"]))
|
||||
os.system("git push --tags")
|
||||
|
||||
sys.exit()
|
||||
|
||||
@@ -95,34 +93,32 @@ class UploadCommand(Command):
|
||||
# Where the magic happens:
|
||||
setup(
|
||||
name=NAME,
|
||||
version=about['__version__'],
|
||||
version=about["__version__"],
|
||||
description=DESCRIPTION,
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/markdown',
|
||||
long_description_content_type="text/markdown",
|
||||
author=AUTHOR,
|
||||
author_email=EMAIL,
|
||||
python_requires=REQUIRES_PYTHON,
|
||||
url=URL,
|
||||
packages=find_packages(
|
||||
exclude=["tests", "*.tests", "*.tests.*", "tests.*"]
|
||||
),
|
||||
packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]),
|
||||
# If your package is a single module, use this instead of 'packages':
|
||||
# py_modules=['mypackage'],
|
||||
entry_points={'console_scripts': ['bashf=bashf.cli:entrypoint']},
|
||||
entry_points={"console_scripts": ["bashf=bashf.cli:entrypoint"]},
|
||||
install_requires=REQUIRED,
|
||||
extras_require=EXTRAS,
|
||||
include_package_data=True,
|
||||
license='MIT',
|
||||
license="MIT",
|
||||
classifiers=[
|
||||
# Trove classifiers
|
||||
# Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Programming Language :: Python :: Implementation :: PyPy',
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
],
|
||||
# $ setup.py publish support.
|
||||
cmdclass={'upload': UploadCommand},
|
||||
cmdclass={"upload": UploadCommand},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user