Skip to content

ux improvements #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 82 additions & 11 deletions devtools/__main__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,74 @@
import click
import subprocess
import sys
import os
from pathlib import Path

try:
import click
import rich
from .ctx import Context
from . import ci
from . import update_pyproject
from .subproject import Subproject
from .progress import progress
except (ImportError, ModuleNotFoundError):
print("Installing robotpy dev requirements...")
subprocess.check_call(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely do not install the requirements automatically. Just detect the first import error and output the appropriate command for the user to copy/paste, but do not execute it.

[
sys.executable,
"-m",
"pip",
"install",
"--disable-pip-version-check",
"-r",
(Path(__file__).parent.parent / "rdev_requirements.txt").resolve().as_posix(),
]
)

print("Try running the command again.")
exit(0)

from .ctx import Context
from . import ci
from . import update_pyproject


@click.group()
@click.option("--only", required=False, default=None, help="Only run for the specified project")
@click.option("--till", required=False, default=None, help="Only run until the specified project")
@click.pass_context
def main(ctx: click.Context):
def main(ctx: click.Context, only: str, till: str):
"""RobotPy development tool"""
ctx.obj = Context()
obj = ctx.obj = Context()

if obj.cfg.params.parallel is not None:
os.environ.setdefault("RPYBUILD_PARALLEL", str(int(obj.cfg.params.parallel)))
if obj.cfg.params.cc_launcher is not None:
os.environ.setdefault("RPYBUILD_CC_LAUNCHER", obj.cfg.params.cc_launcher)
if obj.cfg.params.strip_libpython is not None:
os.environ.setdefault("RPYBUILD_STRIP_LIBPYTHON", str(int(obj.cfg.params.strip_libpython)))
if obj.cfg.params.macosx_deployment_target is not None:
os.environ.setdefault("MACOSX_DEPLOYMENT_TARGET", obj.cfg.params.macosx_deployment_target)

subprocess.check_call(
[
sys.executable,
"-m",
"pip",
"install",
"--disable-pip-version-check",
"robotpy-build"+obj.cfg.params.robotpy_build_req,
],
stdout=subprocess.DEVNULL,
)

if only is not None:
obj.subprojects = {only: obj.subprojects[only]}
elif till is not None:
subprojects = {}
for name, project in obj.subprojects.items():
subprojects[name] = project
if name == till:
subprojects[name] = project
break
obj.subprojects = subprojects


main.add_command(ci.ci)
Expand All @@ -20,25 +79,37 @@ def main(ctx: click.Context):
@click.pass_obj
def info(ctx: Context):
"""Display information"""
for project in ctx.subprojects.values():
print(project.name, project.requires)
for project in progress(ctx.subprojects.values()):
rich.print(project.name, ":", project.requires)


@main.command()
@click.pass_obj
def develop(ctx: Context):
"""Install all robotpy packages in editable mode"""
for project in ctx.subprojects.values():
for project in progress(ctx.subprojects.values()):
project.develop()

@main.command()
@click.pass_obj
def build(ctx: Context):
"""Build all robotpy packages"""
for project in progress(ctx.subprojects.values()):
project.bdist_wheel(wheel_path=ctx.wheel_path, install=True)

@main.command()
@click.pass_obj
def test(ctx: Context):
"""Run all test scripts"""
for project in ctx.subprojects.values():
for project in progress(ctx.subprojects.values()):
project.test()

@main.command()
@click.pass_obj
def clean(ctx: Context):
"""Clean all projects"""
for project in progress(ctx.subprojects.values()):
project.clean()

if __name__ == "__main__":
main()
main()
5 changes: 5 additions & 0 deletions devtools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ class Parameters(Model):

exclude_artifacts: typing.Set[str]

parallel: typing.Optional[bool]
cc_launcher: typing.Optional[str]
strip_libpython: typing.Optional[bool]
macosx_deployment_target: typing.Optional[str]


class UpdateConfig(Model):
params: Parameters
Expand Down
43 changes: 43 additions & 0 deletions devtools/progress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Iterable, ValuesView
import rich.progress
from datetime import datetime
import inspect

import rich.text

from .subproject import Subproject

def progress(
sequence: ValuesView[Subproject],
) -> Iterable[Subproject]:

taskname = inspect.stack()[1].function

class NumTaskColumn(rich.progress.ProgressColumn):
def render(self, task: rich.progress.Task) -> rich.text.Text:
return rich.text.Text(f"{task.completed}/{task.total}", style="purple")

current_hour = datetime.now().hour
if 7 <= current_hour < (12 + 9):
spinner_name = "earth"
else:
spinner_name = "moon"

progress = rich.progress.Progress(
rich.progress.SpinnerColumn(spinner_name=spinner_name),
rich.progress.TextColumn("[progress.description]{task.description}"),
rich.progress.BarColumn(),
NumTaskColumn(),
rich.progress.TimeElapsedColumn(),
redirect_stdout=True,
redirect_stderr=True,
# refresh_per_second=50
)

with progress:
for item in progress.track(
sequence, total=len(sequence)
):
progress.update(progress.task_ids[0], description=f"{taskname} {item.name}")
yield item

10 changes: 10 additions & 0 deletions devtools/subproject.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ def test(self, *, install_requirements=False):
cwd=tests_path,
)

def clean(self):
self._cmd(
sys.executable,
"setup.py",
"clean",
"--all",
cwd=self.path,
)
shutil.rmtree(self.path / "build", ignore_errors=True)

def bdist_wheel(self, *, wheel_path: pathlib.Path, install: bool):
wheel_path.mkdir(parents=True, exist_ok=True)

Expand Down
6 changes: 6 additions & 0 deletions rdev.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

[params]

parallel = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about these interacting poorly with CI. Maybe have a user-specific file that is added to .gitignore instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default should be parallel.

Also, if ci sets an environment variable, this won't override it.

Cache should also be on by default imo. But I do need to check if ccache is available and not use it if it doesn't exist.

cc_launcher = "ccache"
# strip_libpython = true
# macosx_deployment_target = "12"


wpilib_bin_url = "https://frcmaven.wpi.edu/artifactory/release"
wpilib_bin_version = "2024.1.1-beta-3"
# wpilib_bin_url = "https://frcmaven.wpi.edu/artifactory/development"
Expand Down
1 change: 1 addition & 0 deletions rdev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ click
packaging
pydantic<2
requests
rich
setuptools
tomlkit
tomli
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# file generated by setuptools_scm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should add this to .gitignore

# don't change, don't track in version control
__version__ = version = '2024.0.0b3.post2.dev3+g375e0468.d20231113'
__version_tuple__ = version_tuple = (2024, 0, 0, 'dev3', 'g375e0468.d20231113')