
* Add primer CI tool 💩 - Run in PATH `black` binary on configured projects - Can set wether we expect changes or not per project - Can set what python versions are supported for a project - if `long_checkout` True project will not be ran on CI Will add to CI after I finish unit tests to avoid silly bugs I'm sure I have 🤪 Tests: - Manual Run - Will add unit tests if people think it will be useful - Output: ```shell (b) cooper-mbp1:black cooper$ time /tmp/b/bin/black-primer -k -w /tmp/cooper_primer_1 [2020-05-10 08:48:25,696] INFO: 4 projects to run black over (lib.py:212) [2020-05-10 08:48:25,697] INFO: Skipping aioexabgp as it's disabled via config (lib.py:166) [2020-05-10 08:48:25,699] INFO: Skipping bandersnatch as it's disabled via config (lib.py:166) [2020-05-10 08:48:28,676] INFO: Analyzing results (lib.py:225) -- primer results 📊 -- 2 / 4 succeeded (50.0%) ✅ 0 / 4 FAILED (0.0%) 💩 - 2 projects Disabled by config - 0 projects skipped due to Python Version - 0 skipped due to long checkout real 0m3.304s user 0m9.529s sys 0m1.019s ``` - ls of /tmp/cooper_primer_1 ``` (b) cooper-mbp1:black cooper$ ls -lh /tmp/cooper_primer_1 total 0 drwxr-xr-x 21 cooper wheel 672B May 10 08:48 attrs drwxr-xr-x 14 cooper wheel 448B May 10 08:48 flake8-bugbear ``` * Address mypy 3.6 type errors - Don't use asyncio.run() ... go back to the past :P - Refactor results into a named tuple of two dicts to avoid typing nightmare - Fix some variable names - Fix bug with rebase logic in git_checkout_or_rebase * Prettier the JSON config file for primer * Delete projects when finished, move dir to be timestamped + shallow copy * Re-enable disabled projects post @JelleZijlstra's docstring fix * Workaround for future annotations until someone tells me the correct fix
136 lines
3.1 KiB
Python
136 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import asyncio
|
|
import logging
|
|
import sys
|
|
from datetime import datetime
|
|
from os import cpu_count
|
|
from pathlib import Path
|
|
from shutil import rmtree, which
|
|
from tempfile import gettempdir
|
|
from typing import Any, Union
|
|
|
|
import click
|
|
|
|
from black_primer import lib
|
|
|
|
|
|
DEFAULT_CONFIG = Path(__file__).parent / "primer.json"
|
|
_timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
DEFAULT_WORKDIR = Path(gettempdir()) / f"primer.{_timestamp}"
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def _handle_debug(
|
|
ctx: click.core.Context,
|
|
param: Union[click.core.Option, click.core.Parameter],
|
|
debug: Union[bool, int, str],
|
|
) -> Union[bool, int, str]:
|
|
"""Turn on debugging if asked otherwise INFO default"""
|
|
log_level = logging.DEBUG if debug else logging.INFO
|
|
logging.basicConfig(
|
|
format="[%(asctime)s] %(levelname)s: %(message)s (%(filename)s:%(lineno)d)",
|
|
level=log_level,
|
|
)
|
|
return debug
|
|
|
|
|
|
async def async_main(
|
|
config: str,
|
|
debug: bool,
|
|
keep: bool,
|
|
long_checkouts: bool,
|
|
rebase: bool,
|
|
workdir: str,
|
|
workers: int,
|
|
) -> int:
|
|
work_path = Path(workdir)
|
|
if not work_path.exists():
|
|
LOG.debug(f"Creating {work_path}")
|
|
work_path.mkdir()
|
|
|
|
if not which("black"):
|
|
LOG.error(f"Can not find 'black' executable in PATH. No point in running")
|
|
return -1
|
|
|
|
try:
|
|
ret_val = await lib.process_queue(
|
|
config, work_path, workers, keep, long_checkouts, rebase
|
|
)
|
|
return int(ret_val)
|
|
finally:
|
|
if not keep and work_path.exists():
|
|
LOG.debug(f"Removing {work_path}")
|
|
rmtree(work_path)
|
|
|
|
return -1
|
|
|
|
|
|
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
|
|
@click.option(
|
|
"-c",
|
|
"--config",
|
|
default=str(DEFAULT_CONFIG),
|
|
type=click.Path(exists=True),
|
|
show_default=True,
|
|
help="JSON config file path",
|
|
)
|
|
@click.option(
|
|
"--debug",
|
|
is_flag=True,
|
|
callback=_handle_debug,
|
|
show_default=True,
|
|
help="Turn on debug logging",
|
|
)
|
|
@click.option(
|
|
"-k",
|
|
"--keep",
|
|
is_flag=True,
|
|
show_default=True,
|
|
help="Keep workdir + repos post run",
|
|
)
|
|
@click.option(
|
|
"-L",
|
|
"--long-checkouts",
|
|
is_flag=True,
|
|
show_default=True,
|
|
help="Pull big projects to test",
|
|
)
|
|
@click.option(
|
|
"-R",
|
|
"--rebase",
|
|
is_flag=True,
|
|
show_default=True,
|
|
help="Rebase project if already checked out",
|
|
)
|
|
@click.option(
|
|
"-w",
|
|
"--workdir",
|
|
default=str(DEFAULT_WORKDIR),
|
|
type=click.Path(exists=False),
|
|
show_default=True,
|
|
help="Directory Path for repo checkouts",
|
|
)
|
|
@click.option(
|
|
"-W",
|
|
"--workers",
|
|
default=int((cpu_count() or 4) / 2) or 1,
|
|
type=int,
|
|
show_default=True,
|
|
help="Number of parallel worker coroutines",
|
|
)
|
|
@click.pass_context
|
|
def main(ctx: click.core.Context, **kwargs: Any) -> None:
|
|
"""primer - prime projects for blackening ... 🏴"""
|
|
LOG.debug(f"Starting {sys.argv[0]}")
|
|
# TODO: Change to asyncio.run when black >= 3.7 only
|
|
loop = asyncio.get_event_loop()
|
|
try:
|
|
ctx.exit(loop.run_until_complete(async_main(**kwargs)))
|
|
finally:
|
|
loop.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|