diff --git a/bake/bakefile.py b/bake/bakefile.py index 73bda1c..bbd37e1 100644 --- a/bake/bakefile.py +++ b/bake/bakefile.py @@ -2,6 +2,7 @@ import json import os import stat import sys +from hashlib import sha256 from random import randint from shlex import quote as shlex_quote from tempfile import mkstemp @@ -94,9 +95,40 @@ class TaskFilter(BaseAction): question = str(click.style("?", fg="green", bold=True)) click.confirm(f" {question} Do you want to continue?", abort=True) + @staticmethod + def execute_skip_if(*, key, cache=None, **kwargs): + if cache is None: + cache = f".git/bake-hash-{sha256(key.encode('utf-8')).hexdigest()}" + + key_path = os.path.abspath(key) + cache_path = os.path.abspath(cache) + os.makedirs(os.path.dirname(cache_path), exist_ok=True) + + if not os.path.exists(key_path): + return ("skip", False) + + if os.path.exists(cache_path): + with open(cache_path, "r") as f: + old_hash = f.read().strip() + else: + old_hash = "NOPE" + + with open(key_path, "r") as f: + current_hash = sha256(f.read().encode("utf-8")).hexdigest() + + with open(cache_path, "w") as f: + f.write(current_hash) + + if old_hash == current_hash: + return ("skip", True) + + return ("skip", False) + def execute(self, yes=False, **kwargs): if self.name == "confirm": - self.execute_confirm(yes=yes, **self.arguments) + return self.execute_confirm(yes=yes, **self.arguments) + elif self.name == "skip": + return self.execute_skip_if(yes=yes, **self.arguments) class TaskScript(BaseAction): @@ -117,7 +149,7 @@ class TaskScript(BaseAction): def declaration_line(self): return self.chunk[0] - def depends_on(self, *, reverse=False, recursive=False): + def depends_on(self, *, recursive=False): def gen_actions(): task_strings = self.declaration_line.split(":", 1)[1].split() @@ -138,9 +170,9 @@ class TaskScript(BaseAction): if recursive: for i, task in enumerate(actions[:]): - for t in reversed(task.depends_on()): + for t in task.depends_on(): if t.name not in [task.name for task in actions]: - actions.insert(i + 1, t) + actions.insert(i - 1, t) return actions @@ -196,6 +228,7 @@ class TaskScript(BaseAction): script_tf = self.prepare_init(sources=[self.source], insert_source=init_tf) args = " ".join([shlex_quote(a) for a in self.bashfile.args]) + script = f"source {shlex_quote(init_tf)}; {shlex_quote(script_tf)} {args} 2>&1 | bake:indent" cmd = f"bash -c {shlex_quote(script)}" diff --git a/bake/cli.py b/bake/cli.py index 0350818..be8ac13 100644 --- a/bake/cli.py +++ b/bake/cli.py @@ -9,12 +9,13 @@ import pygments import pygments.lexers import pygments.formatters - +SKIP_NEXT = False SAFE_ENVIRONS = [ "HOME", "PATH", "LANG", "LOCALE", + "LANGUAGE", "TERM", "VIRTUAL_ENV", "BAKEFILE_PATH", @@ -305,8 +306,6 @@ def entrypoint( str(line).zfill(3), bg="black", fg="cyan", bold=True ) colored_task = click.style(str(_task), fg="yellow", bold=True) - print(locals()) - print(_task.source) actual_line = _task.source_lines[line - 1] click.echo(f"In {colored_task} line {line}:", err=True) @@ -326,6 +325,7 @@ def entrypoint( sys.exit(max(__shellcheck_statuses)) if task: + try: task = bakefile[task] except KeyError: @@ -333,19 +333,36 @@ def entrypoint( sys.exit(1) def execute_task(task, *, silent=False): - if not silent: + global SKIP_NEXT + + if not SKIP_NEXT: + if not silent: + click.echo( + click.style(" + ", fg="white") + + click.style(f"Executing {task}", fg="yellow") + + click.style(":", fg="white"), + err=True, + ) + return_code = task.execute(yes=yes, debug=debug, silent=silent) + + if not _continue: + if (not return_code == 0) and (not isinstance(return_code, tuple)): + click.echo( + click.style(f"Task {task} failed!", fg="red"), err=True + ) + sys.exit(return_code) + if isinstance(return_code, tuple): + key, value = return_code + if key == "skip" and value: + SKIP_NEXT = True + else: + SKIP_NEXT = False click.echo( - click.style(" + ", fg="white") - + click.style(f"Executing {task}", fg="yellow") - + click.style(":", fg="white"), + click.style(" + ", fg="green") + + click.style(f"Skipping {task}", fg="white") + + click.style(".", fg="white"), err=True, ) - return_code = task.execute(yes=yes, debug=debug, silent=silent) - - if not _continue: - if (not return_code == 0) and (return_code is not None): - click.echo(click.style(f"Task {task} failed!", fg="red"), err=True) - sys.exit(return_code) if not no_deps: tasks = task.depends_on(recursive=True) + [task] diff --git a/setup.py b/setup.py index f453d4e..a427ea0 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ REQUIRES_PYTHON = ">=3.6.0" VERSION = "0.2.0" # What packages are required for this module to be executed? -REQUIRED = ["click", "delegator.py", "pygments"] +REQUIRED = ["click", "delegator.py", "pygments", "appdirs"] # What packages are optional? EXTRAS = {