mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2026-05-16 12:51:55 -04:00
238 lines
8.3 KiB
Python
238 lines
8.3 KiB
Python
import asyncio
|
|
from pathlib import Path
|
|
from typing import Any, Final, Optional, Tuple
|
|
|
|
import click
|
|
from packaging.version import Version
|
|
from python_discovery import PythonInfo
|
|
|
|
from redbot.core._cli import asyncio_run
|
|
|
|
from . import cmd, common, updater
|
|
|
|
|
|
_CHECK_OTHER_PYTHON_INSTALLS_CMD_ARG_NAME: Final = "--check-other-python-installs"
|
|
|
|
|
|
def _help_major_update_example() -> str:
|
|
version = common.get_current_red_version().__replace__(dev=None, local=None)
|
|
release = (version.major, version.minor + 1) + (0,) * (len(version.release) - 2)
|
|
next_major_version = version.__replace__(release=release)
|
|
return f"updating from Red {version} to Red {next_major_version}"
|
|
|
|
|
|
def _help_minor_update_example() -> str:
|
|
version = common.get_current_red_version().__replace__(dev=None, local=None)
|
|
release = (version.major, version.minor, version.micro + 1) + (0,) * (len(version.release) - 3)
|
|
next_minor_version = version.__replace__(release=release)
|
|
return f"updating from Red {version} to Red {next_minor_version}"
|
|
|
|
|
|
class _PythonInfoParamType(click.ParamType):
|
|
name = "Python interpreter"
|
|
|
|
def convert(
|
|
self, value: Any, param: Optional[click.Parameter], ctx: Optional[click.Context]
|
|
) -> PythonInfo:
|
|
if isinstance(value, PythonInfo):
|
|
return value
|
|
|
|
try:
|
|
return PythonInfo.from_exe(value)
|
|
except RuntimeError:
|
|
self.fail(f"{value!r} is not a valid Python executable.", param, ctx)
|
|
|
|
|
|
@click.group(invoke_without_command=True)
|
|
# command-specific options
|
|
@click.option(
|
|
"--include-instance",
|
|
"included_instances",
|
|
multiple=True,
|
|
type=click.Choice(common.INSTANCE_LIST),
|
|
help="The list of instances to backup and check cog compatibility for. If not specified,"
|
|
" all instances that use the current virtual environment will be backed up and checked.",
|
|
)
|
|
@click.option(
|
|
"--exclude-instance",
|
|
"excluded_instances",
|
|
multiple=True,
|
|
type=click.Choice(common.INSTANCE_LIST),
|
|
help="Exclude an instance from the list of instances to backup"
|
|
" and check cog compatibility for.",
|
|
)
|
|
@click.option(
|
|
"--backup-dir",
|
|
default=None,
|
|
type=click.Path(
|
|
dir_okay=True, file_okay=False, resolve_path=True, writable=True, path_type=Path
|
|
),
|
|
help="The directory to place the backups of the virtual environment and instances.",
|
|
)
|
|
@click.option(
|
|
"--no-backup",
|
|
help="Do not make backups of the virtual environment and instances before update.",
|
|
is_flag=True,
|
|
)
|
|
@click.option(
|
|
"--red-version",
|
|
"--version",
|
|
type=common.VersionParamType(),
|
|
default=None,
|
|
help="Version of Red to update to instead of the latest.",
|
|
)
|
|
@click.option(
|
|
"--no-major-updates",
|
|
help=f"Skip major updates. For example: {_help_major_update_example()} is a major update"
|
|
f" but {_help_minor_update_example()} isn't.",
|
|
is_flag=True,
|
|
)
|
|
@click.option(
|
|
"--no-full-changelog",
|
|
help='Skip showing full changelog in a terminal user interface. The "Read before updating"'
|
|
" sections will still be printed.",
|
|
is_flag=True,
|
|
)
|
|
@click.option(
|
|
"--no-cog-compatibility-check",
|
|
help="Skip performing cog compatibility check before the update.",
|
|
is_flag=True,
|
|
)
|
|
@click.option(
|
|
"--new-python-interpreter",
|
|
type=_PythonInfoParamType(),
|
|
help="The new Python interpreter that should be used when creating a virtual environment"
|
|
" for Red. This can either be a path to a Python executable or a name of a Python executable"
|
|
" on the PATH.",
|
|
)
|
|
@click.option(
|
|
"--update-cogs/--no-update-cogs",
|
|
default=None,
|
|
help="When this option is used, it determines whether the cogs should be updated after Red"
|
|
" is updated. By default, you'll be asked, if you want to update.\n"
|
|
"In non-interactive mode, cogs will be updated unless this option is used to override"
|
|
" the default behavior.",
|
|
)
|
|
@click.option(
|
|
# `pip install` having an option with the same name is coincidental,
|
|
# this does not call `pip install` with the `--force-reinstall` option.
|
|
# Not that there would be any point in doing so - we create a fresh virtual environment.
|
|
"--force-reinstall",
|
|
type=bool,
|
|
is_flag=True,
|
|
help="Force the update process to proceed even, if there is no new version detected."
|
|
" This will essentially reinstall latest Red version into a fresh virtual environment. You can"
|
|
" combine it with the --new-python-interpreter option to change Red's Python interpreter.",
|
|
)
|
|
@click.option(
|
|
"--no-prompt",
|
|
"interactive",
|
|
type=bool,
|
|
is_flag=True,
|
|
default=True,
|
|
help="Don't ask for user input during the process (non-interactive mode).\n"
|
|
"NOTE: If you want to use this to automate Red updates, consider specifying --no-major-update"
|
|
" to avoid performing major updates without making an explicit decision to.\n"
|
|
"When performing a major update where the current Python interpreter is no longer compatible,"
|
|
" the --new-python-interpreter option has to be specified or the command will fail.",
|
|
)
|
|
# global options
|
|
@click.option(
|
|
cmd.arg_names.DEBUG,
|
|
"--verbose",
|
|
"-v",
|
|
"logging_level",
|
|
count=True,
|
|
help=(
|
|
"Increase the verbosity of the logs, each usage of this flag increases the verbosity"
|
|
" level by 1."
|
|
),
|
|
)
|
|
@click.option(
|
|
"--check-other-venvs",
|
|
_CHECK_OTHER_PYTHON_INSTALLS_CMD_ARG_NAME,
|
|
"ignore_prefix",
|
|
help="Check the compatibility of cogs for instances that are normally ran with"
|
|
" a different Python installation and/or virtual environment than the current one.",
|
|
is_flag=True,
|
|
)
|
|
@click.pass_context
|
|
def cli(
|
|
ctx: click.Context,
|
|
included_instances: Tuple[str, ...],
|
|
excluded_instances: Tuple[str, ...],
|
|
backup_dir: Optional[Path],
|
|
no_backup: bool,
|
|
red_version: Optional[Version],
|
|
no_major_updates: bool,
|
|
no_full_changelog: bool,
|
|
no_cog_compatibility_check: bool,
|
|
new_python_interpreter: Optional[PythonInfo],
|
|
update_cogs: Optional[bool],
|
|
force_reinstall: bool,
|
|
interactive: bool,
|
|
logging_level: int,
|
|
ignore_prefix: bool,
|
|
) -> None:
|
|
common.ensure_supported_env()
|
|
common.configure_logging(logging_level)
|
|
|
|
ctx.ensure_object(dict)
|
|
ctx.obj["IGNORE_PREFIX"] = ignore_prefix
|
|
|
|
if ctx.invoked_subcommand is None:
|
|
if included_instances:
|
|
# de-duplicate with order intact
|
|
instances = list(dict.fromkeys(included_instances))
|
|
else:
|
|
instances = list(common.INSTANCE_LIST)
|
|
options = updater.UpdaterOptions(
|
|
instances=instances,
|
|
excluded_instances=set(excluded_instances),
|
|
ignore_prefix=ignore_prefix,
|
|
backup_dir=backup_dir,
|
|
no_backup=no_backup,
|
|
red_version=red_version,
|
|
no_major_updates=no_major_updates,
|
|
no_full_changelog=no_full_changelog,
|
|
no_cog_compatibility_check=no_cog_compatibility_check,
|
|
new_python_interpreter=new_python_interpreter,
|
|
update_cogs=update_cogs,
|
|
force_reinstall=force_reinstall,
|
|
interactive=interactive,
|
|
)
|
|
app = updater.Updater(options)
|
|
asyncio_run(app.run())
|
|
# these should not be available to subcommands
|
|
elif included_instances:
|
|
raise click.NoSuchOption("--include-instance", ctx=ctx)
|
|
elif excluded_instances:
|
|
raise click.NoSuchOption("--exclude-instance", ctx=ctx)
|
|
elif backup_dir is not None:
|
|
raise click.NoSuchOption("--backup-dir", ctx=ctx)
|
|
elif no_backup:
|
|
raise click.NoSuchOption("--no-backup", ctx=ctx)
|
|
elif red_version:
|
|
raise click.NoSuchOption("--red-version", ctx=ctx)
|
|
elif no_major_updates:
|
|
raise click.NoSuchOption("--no-major-updates", ctx=ctx)
|
|
elif no_cog_compatibility_check:
|
|
raise click.NoSuchOption("--no-cog-compatibility-check", ctx=ctx)
|
|
elif new_python_interpreter:
|
|
raise click.NoSuchOption("--new-python-interpreter", ctx=ctx)
|
|
elif update_cogs is True:
|
|
raise click.NoSuchOption("--update-cogs", ctx=ctx)
|
|
elif update_cogs is False:
|
|
raise click.NoSuchOption("--no-update-cogs", ctx=ctx)
|
|
elif not interactive:
|
|
raise click.NoSuchOption("--no-prompt", ctx=ctx)
|
|
elif force_reinstall:
|
|
raise click.NoSuchOption("--force-reinstall", ctx=ctx)
|
|
|
|
|
|
cli.add_command(cmd.cog_compatibility.check_cog_compatibility)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
cli()
|