Skip to content
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

Implement uv as an optional python package management backend #384

Merged
merged 13 commits into from
Feb 28, 2025
Merged
2 changes: 0 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

from __future__ import print_function

import sphinx.environment

from invenio_cli import __version__

# -- General configuration ------------------------------------------------
Expand Down
145 changes: 116 additions & 29 deletions invenio_cli/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Copyright (C) 2019-2021 CERN.
# Copyright (C) 2019 Northwestern University.
# Copyright (C) 2022 Forschungszentrum Jülich GmbH.
# Copyright (C) 2024-2025 Graz University of Technology.
#
# Invenio-Cli is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.
Expand Down Expand Up @@ -30,7 +31,12 @@
from .packages import packages
from .services import services
from .translations import translations
from .utils import handle_process_response, pass_cli_config, run_steps
from .utils import (
combine_decorators,
handle_process_response,
pass_cli_config,
run_steps,
)


@click.group()
Expand Down Expand Up @@ -67,9 +73,10 @@ def check_requirements(development):


@invenio_cli.command()
def shell():
@pass_cli_config
def shell(cli_config):
"""Shell command."""
Commands.shell()
Commands(cli_config).shell()


@invenio_cli.command()
Expand All @@ -80,9 +87,10 @@ def shell():
is_flag=True,
help="Enable Flask development mode (default: disabled).",
)
def pyshell(debug):
@pass_cli_config
def pyshell(cli_config, debug):
"""Python shell command."""
Commands.pyshell(debug=debug)
Commands(cli_config).pyshell(debug=debug)


@invenio_cli.command()
Expand All @@ -104,8 +112,7 @@ def pyshell(debug):
@click.option(
"--user-input/--no-input",
default=True,
help="If input is disabled, uses the defaults (if --config is"
" also passed, uses values from an .invenio config file).",
help="If input is disabled, uses the defaults (if --config is also passed, uses values from an .invenio config file).", # noqa
)
@click.option(
"--config", required=False, help="The .invenio configuration file to use."
Expand Down Expand Up @@ -142,48 +149,127 @@ def init(flavour, template, checkout, user_input, config):
cookiecutter_wrapper.remove_config()


@invenio_cli.command()
@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.")
@click.option("--port", "-p", default=5000, help="The port to bind to.")
@click.option(
"--debug/--no-debug",
"-d/",
default=True,
is_flag=True,
help="Enable/disable debug mode including auto-reloading " "(default: enabled).",
)
@click.option(
@invenio_cli.group("run", invoke_without_command=True)
@click.pass_context
def run_group(ctx):
"""Run command group."""
# For backward compatibility
if ctx.invoked_subcommand is None:
ctx.invoke(run_all)


services_option = click.option(
"--services/--no-services",
"-s/-n",
default=True,
is_flag=True,
help="Enable/disable dockerized services (default: enabled).",
)
@click.option(
"--celery-log-file",
default=None,
help="Celery log file (default: None, this means logging to stderr)",
web_options = combine_decorators(
click.option(
"--host",
"-h",
default=None,
help="The interface to bind to. The default is defined in the CLIConfig.",
),
click.option(
"--port",
"-p",
default=None,
help="The port to bind to. The default is defined in the CLIConfig.",
),
click.option(
"--debug/--no-debug",
"-d/",
default=True,
is_flag=True,
help="Enable/disable debug mode including auto-reloading (default: enabled).",
),
)


@run_group.command("web")
@services_option
@web_options
@pass_cli_config
def run(cli_config, host, port, debug, services, celery_log_file):
"""Starts the local development server.
def run_web(cli_config, host, port, debug, services):
"""Starts the local development web server."""
if services:
cmds = ServicesCommands(cli_config)
response = cmds.ensure_containers_running()
# fail and exit if containers are not running
handle_process_response(response)

host = host or cli_config.get_web_host()
port = port or cli_config.get_web_port()

commands = LocalCommands(cli_config)
processes = commands.run_web(host=host, port=str(port), debug=debug)
for proc in processes:
proc.wait()


worker_options = combine_decorators(
click.option(
"--celery-log-file",
default=None,
help="Celery log file (default: None, this means logging to stderr)",
),
click.option(
"--celery-log-level",
default="INFO",
help="Celery log level (default: INFO)",
),
)


@run_group.command("worker")
@services_option
@worker_options
@pass_cli_config
def run_worker(cli_config, services, celery_log_file, celery_log_level):
"""Starts the local development server."""
if services:
cmds = ServicesCommands(cli_config)
response = cmds.ensure_containers_running()
# fail and exit if containers are not running
handle_process_response(response)

commands = LocalCommands(cli_config)
processes = commands.run_worker(
celery_log_file=celery_log_file, celery_log_level=celery_log_level
)
for proc in processes:
proc.wait()


NOTE: this only makes sense locally so no --local option
"""
@run_group.command("all")
@services_option
@web_options
@worker_options
@pass_cli_config
def run_all(cli_config, host, port, debug, services, celery_log_file, celery_log_level):
"""Starts web and worker development servers."""
if services:
cmds = ServicesCommands(cli_config)
response = cmds.ensure_containers_running()
# fail and exit if containers are not running
handle_process_response(response)

host = host or cli_config.get_web_host()
port = port or cli_config.get_web_port()

commands = LocalCommands(cli_config)
commands.run(
processes = commands.run_all(
host=host,
port=str(port),
debug=debug,
services=services,
celery_log_file=celery_log_file,
celery_log_level=celery_log_level,
)
for proc in processes:
proc.wait()


@invenio_cli.command()
Expand All @@ -206,9 +292,10 @@ def destroy(cli_config):

@invenio_cli.command()
@click.option("--script", required=True, help="The path of custom migration script.")
def upgrade(script):
@pass_cli_config
def upgrade(cli_config, script):
"""Upgrades the current instance to a newer version."""
steps = UpgradeCommands.upgrade(script)
steps = UpgradeCommands(cli_config).upgrade(script)
on_fail = "Upgrade failed."
on_success = "Upgrade sucessfull."

Expand Down
74 changes: 69 additions & 5 deletions invenio_cli/cli/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@
from .utils import pass_cli_config, run_steps


@click.command()
@click.group(invoke_without_command=True)
@click.pass_context
def install(ctx):
"""Commands for installing the project."""
if ctx.invoked_subcommand is None:
# If no sub-command is passed, default to the install all command.
ctx.invoke(install_all)


@install.command("all")
@click.option(
"--pre",
default=False,
Expand All @@ -31,12 +40,11 @@
"-p/-d",
default=False,
is_flag=True,
help="Production mode copies statics/assets. Development mode symlinks"
" statics/assets.",
help="Production mode copies statics/assets. Development mode symlinks them.",
)
@pass_cli_config
def install(cli_config, pre, dev, production):
"""Installs the project locally.
def install_all(cli_config, pre, dev, production):
"""Installs the project locally.

Installs dependencies, creates instance directory,
links invenio.cfg + templates, copies images and other statics and finally
Expand All @@ -52,3 +60,59 @@ def install(cli_config, pre, dev, production):
on_success = "Dependencies installed successfully."

run_steps(steps, on_fail, on_success)


@install.command("python")
@click.option(
"--pre",
default=False,
is_flag=True,
help="If specified, allows the installation of alpha releases",
)
@click.option(
"--dev/--no-dev",
default=True,
is_flag=True,
help="Includes development dependencies.",
)
@pass_cli_config
def install_python(cli_config, pre, dev):
"""Install Python dependencies and packages."""
commands = InstallCommands(cli_config)
steps = commands.install_py_dependencies(pre=pre, dev=dev)
on_fail = "Failed to install Python dependencies."
on_success = "Python dependencies installed successfully."

run_steps(steps, on_fail, on_success)


@install.command("assets")
@click.option(
"--production/--development",
"-p/-d",
default=False,
is_flag=True,
help="Production mode copies statics/assets. Development mode symlinks them.",
)
@pass_cli_config
def install_assets(cli_config, production):
"""Install assets."""
commands = InstallCommands(cli_config)
flask_env = "production" if production else "development"
steps = commands.install_assets(flask_env)
on_fail = "Failed to install assets."
on_success = "Assets installed successfully."

run_steps(steps, on_fail, on_success)


@install.command()
@pass_cli_config
def symlink(cli_config):
"""Symlinks project files in the instance directory."""
commands = InstallCommands(cli_config)
steps = commands.symlink()
on_fail = "Failed to symlink project files and folders."
on_success = "Project files and folders symlinked successfully."

run_steps(steps, on_fail, on_success)
14 changes: 8 additions & 6 deletions invenio_cli/cli/packages.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020-2021 CERN.
# Copyright (C) 2022 Graz University of Technology.
# Copyright (C) 2022-2025 Graz University of Technology.
#
# Invenio-Cli is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.
Expand Down Expand Up @@ -34,7 +34,7 @@ def lock(cli_config, pre, dev):
+ f"Include dev-packages: {dev}.",
fg="green",
)
steps = PackagesCommands.lock(pre, dev)
steps = PackagesCommands(cli_config).lock(pre, dev)
on_fail = "Failed to lock dependencies."
on_success = "Dependencies locked successfully."

Expand All @@ -58,7 +58,7 @@ def install(cli_config, packages, skip_build, pip_log_file, node_log_file):
if len(packages) < 1:
raise click.UsageError("You must specify at least one package.")

steps = PackagesCommands.install_packages(packages, pip_log_file)
steps = PackagesCommands(cli_config).install_packages(packages, pip_log_file)

on_fail = f"Failed to install packages {packages}."
on_success = f"Packages {packages} installed successfully."
Expand All @@ -77,7 +77,7 @@ def install(cli_config, packages, skip_build, pip_log_file, node_log_file):
@pass_cli_config
def outdated(cli_config):
"""Show outdated Python dependencies."""
steps = PackagesCommands.outdated_packages()
steps = PackagesCommands(cli_config).outdated_packages()

on_fail = "Some of the packages need to be updated."
on_success = "All packages are up to date."
Expand All @@ -94,11 +94,13 @@ def update(cli_config, version=None):
db = cli_config.get_db_type()
search = cli_config.get_search_type()
package = f"invenio-app-rdm[{db},{search}]~="
steps = PackagesCommands.update_package_new_version(package, version)
steps = PackagesCommands(cli_config).update_package_new_version(
package, version
)
on_fail = f"Failed to update version {version}"
on_success = f"Version {version} installed successfully."
else:
steps = PackagesCommands.update_packages()
steps = PackagesCommands(cli_config).update_packages()
on_fail = "Failed to update packages."
on_success = "Packages installed successfully."

Expand Down
Loading