Skip to content

Commit

Permalink
feat: auto-complete image names in images build/pull/...
Browse files Browse the repository at this point in the history
  • Loading branch information
regisb committed May 4, 2023
1 parent 60f05a8 commit 5452c4c
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 6 deletions.
1 change: 1 addition & 0 deletions changelog.d/20230412_100608_regis_palm.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
- The "openedx" Docker image in development can be built with `tutor images build openedx-dev`.
- The `tutor dev/local start --skip-build` option is removed. It is replaced by opt-in `--build`.
- [Improvement] The `IMAGES_BUILD` filter now supports relative paths as strings, and not just as tuple of strings.
- [Improvement] Auto-complete the image names in the `images build/pull/push/printtag` commands.
62 changes: 56 additions & 6 deletions tutor/commands/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from tutor import config as tutor_config
from tutor import env as tutor_env
from tutor import exceptions, hooks, images, types, utils
from tutor import exceptions, fmt, hooks, images, types, utils
from tutor.commands.context import Context
from tutor.core.hooks import Filter
from tutor.types import Config
Expand Down Expand Up @@ -87,13 +87,60 @@ def _add_core_images_to_push(
return remote_images


class ImageNameParam(click.ParamType):
"""
Convenient auto-completion of image names.
"""

def shell_complete(
self, ctx: click.Context, param: click.Parameter, incomplete: str
) -> list[click.shell_completion.CompletionItem]:
# Hackish way to get the project root and config
root = getattr(
getattr(getattr(ctx, "parent", None), "parent", None), "params", {}
).get("root", "")
config = tutor_config.load_full(root)

results = []
for name in self.iter_image_names(config):
if name.startswith(incomplete):
results.append(click.shell_completion.CompletionItem(name))
return results

def iter_image_names(self, config: Config) -> t.Iterable["str"]:
raise NotImplementedError


class BuildImageNameParam(ImageNameParam):
def iter_image_names(self, config: Config) -> t.Iterable["str"]:
for name, _path, _tag, _args in hooks.Filters.IMAGES_BUILD.iterate(config):
yield name


class PullImageNameParam(ImageNameParam):
def iter_image_names(self, config: Config) -> t.Iterable["str"]:
for name, _tag in hooks.Filters.IMAGES_PULL.iterate(config):
yield name


class PushImageNameParam(ImageNameParam):
def iter_image_names(self, config: Config) -> t.Iterable["str"]:
for name, _tag in hooks.Filters.IMAGES_PUSH.iterate(config):
yield name


@click.group(name="images", short_help="Manage docker images")
def images_command() -> None:
pass


@click.command()
@click.argument("image_names", metavar="image", nargs=-1)
@click.argument(
"image_names",
metavar="image",
nargs=-1,
type=BuildImageNameParam(),
)
@click.option(
"--no-cache", is_flag=True, help="Do not use cache when building the image"
)
Expand Down Expand Up @@ -200,14 +247,17 @@ def get_image_build_contexts(config: Config) -> dict[str, list[tuple[str, str]]]
instance to build a Docker image with a local git checkout of a remote repo.
Users configure bind-mounts with the `MOUNTS` config setting. Plugins can then
automaticall add build contexts based on these values.
automatically add build contexts based on these values.
"""
user_mounts = types.get_typed(config, "MOUNTS", list)
build_contexts: dict[str, list[tuple[str, str]]] = {}
for user_mount in user_mounts:
for image_name, stage_name in hooks.Filters.IMAGES_BUILD_MOUNTS.iterate(
user_mount
):
fmt.echo_info(
f"Adding {user_mount} to the build context '{stage_name}' of image '{image_name}'"
)
if image_name not in build_contexts:
build_contexts[image_name] = []
build_contexts[image_name].append((user_mount, stage_name))
Expand All @@ -228,7 +278,7 @@ def _mount_edx_platform(


@click.command(short_help="Pull images from the Docker registry")
@click.argument("image_names", metavar="image", nargs=-1)
@click.argument("image_names", metavar="image", type=PullImageNameParam(), nargs=-1)
@click.pass_obj
def pull(context: Context, image_names: list[str]) -> None:
config = tutor_config.load_full(context.root)
Expand All @@ -238,7 +288,7 @@ def pull(context: Context, image_names: list[str]) -> None:


@click.command(short_help="Push images to the Docker registry")
@click.argument("image_names", metavar="image", nargs=-1)
@click.argument("image_names", metavar="image", type=PushImageNameParam(), nargs=-1)
@click.pass_obj
def push(context: Context, image_names: list[str]) -> None:
config = tutor_config.load_full(context.root)
Expand All @@ -248,7 +298,7 @@ def push(context: Context, image_names: list[str]) -> None:


@click.command(short_help="Print tag associated to a Docker image")
@click.argument("image_names", metavar="image", nargs=-1)
@click.argument("image_names", metavar="image", type=BuildImageNameParam(), nargs=-1)
@click.pass_obj
def printtag(context: Context, image_names: list[str]) -> None:
config = tutor_config.load_full(context.root)
Expand Down

0 comments on commit 5452c4c

Please sign in to comment.