From ae26014d7f82078f26065829f60f830628df48e2 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Thu, 14 Sep 2023 20:42:16 -0400 Subject: [PATCH 01/20] mini-cleanup --- pros/conductor/conductor.py | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 7592c659..8b381e36 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -3,6 +3,7 @@ from enum import Enum from pathlib import Path from typing import * +import re import click from semantic_version import Spec, Version @@ -230,8 +231,6 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], if curr_proj.kernel: if template.version[0] == '4' and curr_proj.kernel[0] == '3': confirm = ui.confirm(f'Warning! Upgrading project to PROS 4 will cause breaking changes. ' - f'For PROS 4 LLEMU/LVGL to function, the library liblvgl is required. ' - f'Run \'pros conductor apply liblvgl --beta\' in the project directory. ' f'Do you still want to upgrade?') if not confirm: raise dont_send( @@ -282,10 +281,8 @@ def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Pro self.is_beta = kwargs.get('beta', False) if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) - for char in str(Path(path)): - if char in ['?', '<', '>', '*', '|', '^', '#', '%', '&', '$', '+', '!', '`', '\'', '=', - '@', '\'', '{', '}', '[', ']', '(', ')', '~'] or ord(char) > 127: - raise dont_send(ValueError(f'Invalid character found in directory name: \'{char}\'')) + if re.match(r'^[\w\-. /]+$', str(Path(path))) is None: + raise dont_send(ValueError('Invalid characters found in path')) proj = Project(path=path, create=True) if 'target' in kwargs: proj.target = kwargs['target'] @@ -300,23 +297,13 @@ def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Pro proj.save() if not no_default_libs: - if self.is_beta: - #libraries = self.beta_libraries if self.is_beta else self.default_libraries - for library in self.beta_libraries[proj.target]: - try: - # remove kernel version so that latest template satisfying query is correctly selected - if 'version' in kwargs: - kwargs.pop('version') - self.apply_template(proj, library, **kwargs) - except Exception as e: - logger(__name__).exception(e) - else: - for library in self.default_libraries[proj.target]: - try: - # remove kernel version so that latest template satisfying query is correctly selected - if 'version' in kwargs: - kwargs.pop('version') - self.apply_template(proj, library, **kwargs) - except Exception as e: - logger(__name__).exception(e) + libraries = self.beta_libraries if self.is_beta else self.default_libraries + for library in libraries[proj.target]: + try: + # remove kernel version so that latest template satisfying query is correctly selected + if 'version' in kwargs: + kwargs.pop('version') + self.apply_template(proj, library, **kwargs) + except Exception as e: + logger(__name__).exception(e) return proj From 6302d082d0df0c2d006e9c096ad35634ac561c20 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Thu, 14 Sep 2023 20:57:24 -0400 Subject: [PATCH 02/20] start "stablizing" beta --- pros/cli/conductor.py | 28 ++++++++--------- pros/conductor/conductor.py | 60 ++++++++++++++++++------------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/pros/cli/conductor.py b/pros/cli/conductor.py index ee6048d9..99d41a9b 100644 --- a/pros/cli/conductor.py +++ b/pros/cli/conductor.py @@ -90,8 +90,8 @@ def fetch(query: c.BaseTemplate): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--beta', is_flag=True, default=False, show_default=True, - help='Allow applying beta templates') +@click.option('--modern', is_flag=True, default=False, show_default=True, + help='Allow applying PROS v4 templates') @project_option() @template_query(required=True) @default_options @@ -116,8 +116,8 @@ def apply(project: c.Project, query: c.BaseTemplate, **kwargs): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--beta', is_flag=True, default=False, show_default=True, - help='Allow applying beta templates') +@click.option('--modern', is_flag=True, default=False, show_default=True, + help='Allow applying PROS v4 templates') @project_option() @template_query(required=True) @default_options @@ -143,8 +143,8 @@ def install(ctx: click.Context, **kwargs): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--beta', is_flag=True, default=False, show_default=True, - help='Allow upgrading to beta templates') +@click.option('--modern', is_flag=True, default=False, show_default=True, + help='Allow upgrading to PROS v4 templates') @project_option() @template_query(required=False) @default_options @@ -205,8 +205,8 @@ def uninstall_template(project: c.Project, query: c.BaseTemplate, remove_user: b help='Compile the project after creation') @click.option('--build-cache', is_flag=True, default=None, show_default=False, help='Build compile commands cache after creation. Overrides --compile-after if both are specified.') -@click.option('--beta', is_flag=True, default=False, show_default=True, - help='Create a project with a beta template') +@click.option('--modern', is_flag=True, default=False, show_default=True, + help='Create a project with a PROS v4 template') @click.pass_context @default_options def new_project(ctx: click.Context, path: str, target: str, version: str, @@ -255,13 +255,13 @@ def new_project(ctx: click.Context, path: str, target: str, version: str, help='Force update all remote depots, ignoring automatic update checks') @click.option('--limit', type=int, default=15, help='The maximum number of displayed results for each library') -@click.option('--beta', is_flag=True, default=False, show_default=True, - help='View beta templates in the listing') +@click.option('--modern', is_flag=True, default=False, show_default=True, + help='View PROS v4 templates in the listing') @template_query(required=False) @click.pass_context @default_options def query_templates(ctx, query: c.BaseTemplate, allow_offline: bool, allow_online: bool, force_refresh: bool, - limit: int, beta: bool): + limit: int, modern: bool): """ Query local and remote templates based on a spec @@ -271,10 +271,10 @@ def query_templates(ctx, query: c.BaseTemplate, allow_offline: bool, allow_onlin if limit < 0: limit = 15 templates = c.Conductor().resolve_templates(query, allow_offline=allow_offline, allow_online=allow_online, - force_refresh=force_refresh, beta=beta) - if beta: + force_refresh=force_refresh, modern=modern) + if modern: templates += c.Conductor().resolve_templates(query, allow_offline=allow_offline, allow_online=allow_online, - force_refresh=force_refresh, beta=False) + force_refresh=force_refresh, modern=False) render_templates = {} for template in templates: diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 8b381e36..c389a514 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -18,11 +18,11 @@ MAINLINE_NAME = 'pros-mainline' MAINLINE_URL = 'https://purduesigbots.github.io/pros-mainline/pros-mainline.json' -BETA_NAME = 'kernel-beta-mainline' -BETA_URL = 'https://raw.githubusercontent.com/purduesigbots/pros-mainline/master/beta/kernel-beta-mainline.json' +V4_NAME = 'kernel-v4-mainline' +V4_URL = 'https://raw.githubusercontent.com/purduesigbots/pros-mainline/master/beta/kernel-beta-mainline.json' """ -# TBD? Currently, beta value is stored in config file +# TBD? Currently, v4 value is stored in config file class ReleaseChannel(Enum): Stable = 'stable' Beta = 'beta' @@ -36,12 +36,12 @@ def __init__(self, file=None): if not file: file = os.path.join(click.get_app_dir('PROS'), 'conductor.pros') self.local_templates: Set[LocalTemplate] = set() - self.beta_local_templates: Set[LocalTemplate] = set() + self.v4_local_templates: Set[LocalTemplate] = set() self.depots: Dict[str, Depot] = {} self.default_target: str = 'v5' self.default_libraries: Dict[str, List[str]] = None - self.beta_libraries: Dict[str, List[str]] = None - self.is_beta = False + self.v4_libraries: Dict[str, List[str]] = None + self.is_v4 = False super(Conductor, self).__init__(file) needs_saving = False if MAINLINE_NAME not in self.depots or \ @@ -49,11 +49,11 @@ def __init__(self, file=None): self.depots[MAINLINE_NAME].location != MAINLINE_URL: self.depots[MAINLINE_NAME] = HttpDepot(MAINLINE_NAME, MAINLINE_URL) needs_saving = True - # add beta depot as another remote depot - if BETA_NAME not in self.depots or \ - not isinstance(self.depots[BETA_NAME], HttpDepot) or \ - self.depots[BETA_NAME].location != BETA_URL: - self.depots[BETA_NAME] = HttpDepot(BETA_NAME, BETA_URL) + # add v4 depot as another remote depot + if V4_NAME not in self.depots or \ + not isinstance(self.depots[V4_NAME], HttpDepot) or \ + self.depots[V4_NAME].location != V4_URL: + self.depots[V4_NAME] = HttpDepot(V4_NAME, V4_URL) needs_saving = True if self.default_target is None: self.default_target = 'v5' @@ -64,8 +64,8 @@ def __init__(self, file=None): 'cortex': [] } needs_saving = True - if self.beta_libraries is None or len(self.beta_libraries['v5']) != 2: - self.beta_libraries = { + if self.v4_libraries is None or len(self.v4_libraries['v5']) != 2: + self.v4_libraries = { 'v5': ['liblvgl', 'okapilib'], 'cortex': [] } @@ -76,11 +76,11 @@ def __init__(self, file=None): if 'cortex' not in self.default_libraries: self.default_libraries['cortex'] = [] needs_saving = True - if 'v5' not in self.beta_libraries: - self.beta_libraries['v5'] = [] + if 'v5' not in self.v4_libraries: + self.v4_libraries['v5'] = [] needs_saving = True - if 'cortex' not in self.beta_libraries: - self.beta_libraries['cortex'] = [] + if 'cortex' not in self.v4_libraries: + self.v4_libraries['cortex'] = [] needs_saving = True if needs_saving: self.save() @@ -107,8 +107,8 @@ def fetch_template(self, depot: Depot, template: BaseTemplate, **kwargs) -> Loca local_template = LocalTemplate(orig=template, location=destination) local_template.metadata['origin'] = depot.name click.echo(f'Adding {local_template.identifier} to registry...', nl=False) - if depot.name == BETA_NAME: # check for beta - self.beta_local_templates.add(local_template) + if depot.name == V4_NAME: # check for v4 + self.v4_local_templates.add(local_template) else: self.local_templates.add(local_template) self.save() @@ -118,11 +118,11 @@ def fetch_template(self, depot: Depot, template: BaseTemplate, **kwargs) -> Loca return local_template def purge_template(self, template: LocalTemplate): - if template.metadata['origin'] == BETA_NAME: - if template not in self.beta_local_templates: - logger(__name__).info(f"{template.identifier} was not in the Conductor's local beta templates cache.") + if template.metadata['origin'] == V4_NAME: + if template not in self.v4_local_templates: + logger(__name__).info(f"{template.identifier} was not in the Conductor's local v4 templates cache.") else: - self.beta_local_templates.remove(template) + self.v4_local_templates.remove(template) else: if template not in self.local_templates: logger(__name__).info(f"{template.identifier} was not in the Conductor's local templates cache.") @@ -140,14 +140,14 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: unique: bool = True, **kwargs) -> List[BaseTemplate]: results = list() if not unique else set() kernel_version = kwargs.get('kernel_version', None) - self.is_beta = kwargs.get('beta', False) + self.is_v4 = kwargs.get('modern', False) if isinstance(identifier, str): query = BaseTemplate.create_query(name=identifier, **kwargs) else: query = identifier if allow_offline: - if self.is_beta: - offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.beta_local_templates) + if self.is_v4: + offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.v4_local_templates) else: offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.local_templates) if unique: @@ -156,8 +156,8 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: results.extend(offline_results) if allow_online: for depot in self.depots.values(): - # beta depot will only be accessed when the --beta flag is true - if depot.name != BETA_NAME or (depot.name == BETA_NAME and self.is_beta): + # v4 depot will only be accessed when the --v4 flag is true + if depot.name != V4_NAME or (depot.name == V4_NAME and self.is_v4): online_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), depot.get_remote_templates(force_check=force_refresh, **kwargs)) if unique: @@ -278,7 +278,7 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo remove_empty_directories=remove_empty_directories) def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: - self.is_beta = kwargs.get('beta', False) + self.is_v4 = kwargs.get('modern', False) if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) if re.match(r'^[\w\-. /]+$', str(Path(path))) is None: @@ -297,7 +297,7 @@ def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Pro proj.save() if not no_default_libs: - libraries = self.beta_libraries if self.is_beta else self.default_libraries + libraries = self.v4_libraries if self.is_v4 else self.default_libraries for library in libraries[proj.target]: try: # remove kernel version so that latest template satisfying query is correctly selected From f4f917fd042ea0a02d1a670549dc9960955d67bb Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Sun, 17 Sep 2023 17:41:27 -0400 Subject: [PATCH 03/20] deprecate v3 --- pros/conductor/conductor.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index c389a514..b0f5f2dd 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -41,7 +41,8 @@ def __init__(self, file=None): self.default_target: str = 'v5' self.default_libraries: Dict[str, List[str]] = None self.v4_libraries: Dict[str, List[str]] = None - self.is_v4 = False + self.use_v4 = False + self.warned_v3_deprecated = False super(Conductor, self).__init__(file) needs_saving = False if MAINLINE_NAME not in self.depots or \ @@ -140,13 +141,13 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: unique: bool = True, **kwargs) -> List[BaseTemplate]: results = list() if not unique else set() kernel_version = kwargs.get('kernel_version', None) - self.is_v4 = kwargs.get('modern', False) + self.use_v4 = kwargs.get('modern', False) if isinstance(identifier, str): query = BaseTemplate.create_query(name=identifier, **kwargs) else: query = identifier if allow_offline: - if self.is_v4: + if self.use_v4: offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.v4_local_templates) else: offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.local_templates) @@ -156,8 +157,8 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: results.extend(offline_results) if allow_online: for depot in self.depots.values(): - # v4 depot will only be accessed when the --v4 flag is true - if depot.name != V4_NAME or (depot.name == V4_NAME and self.is_v4): + # v4 depot will only be accessed when the --modern flag is true + if depot.name != V4_NAME or (depot.name == V4_NAME and self.use_v4): online_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), depot.get_remote_templates(force_check=force_refresh, **kwargs)) if unique: @@ -236,11 +237,20 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], raise dont_send( InvalidTemplateException(f'Not upgrading')) if template.version[0] == '3' and curr_proj.kernel[0] == '4': - confirm = ui.confirm(f'Warning! Downgrading project to PROS 3 will cause breaking changes. ' + confirm = ui.confirm(f'PROS 3 is now deprecated. Downgrading project to PROS 3 will also cause breaking changes. ' f'Do you still want to downgrade?') if not confirm: raise dont_send( InvalidTemplateException(f'Not downgrading')) + elif template.version[0] == '3' and not self.warned_v3_deprecated: + confirm = ui.confirm(f'PROS 3 is now deprecated. PROS 4 is now the latest version. ' + f'Do you still want to use PROS 3?\n' + f'To use PROS 4, please use the --modern flag. We will remember your choice.') + self.warned_v3_deprecated = True + self.save() + if not confirm: + raise dont_send( + InvalidTemplateException(f'Not using PROS 3')) if not isinstance(template, LocalTemplate): with ui.Notification(): template = self.fetch_template(self.get_depot(template.metadata['origin']), template, **kwargs) @@ -278,7 +288,7 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo remove_empty_directories=remove_empty_directories) def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: - self.is_v4 = kwargs.get('modern', False) + self.use_v4 = kwargs.get('modern', False) if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) if re.match(r'^[\w\-. /]+$', str(Path(path))) is None: @@ -297,7 +307,7 @@ def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Pro proj.save() if not no_default_libs: - libraries = self.v4_libraries if self.is_v4 else self.default_libraries + libraries = self.v4_libraries if self.use_v4 else self.default_libraries for library in libraries[proj.target]: try: # remove kernel version so that latest template satisfying query is correctly selected From 9a10769afaa6c71781b30491c9a8dc01b051aed0 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Mon, 18 Sep 2023 15:08:41 -0400 Subject: [PATCH 04/20] v4 -> pros4 --- pros/conductor/conductor.py | 62 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index b0f5f2dd..e221f898 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -18,11 +18,11 @@ MAINLINE_NAME = 'pros-mainline' MAINLINE_URL = 'https://purduesigbots.github.io/pros-mainline/pros-mainline.json' -V4_NAME = 'kernel-v4-mainline' -V4_URL = 'https://raw.githubusercontent.com/purduesigbots/pros-mainline/master/beta/kernel-beta-mainline.json' +PROS4_NAME = 'kernel-4-mainline' +PROS4_URL = 'https://raw.githubusercontent.com/purduesigbots/pros-mainline/master/beta/kernel-beta-mainline.json' """ -# TBD? Currently, v4 value is stored in config file +# TBD? Currently, PROS 4 value is stored in config file class ReleaseChannel(Enum): Stable = 'stable' Beta = 'beta' @@ -36,13 +36,13 @@ def __init__(self, file=None): if not file: file = os.path.join(click.get_app_dir('PROS'), 'conductor.pros') self.local_templates: Set[LocalTemplate] = set() - self.v4_local_templates: Set[LocalTemplate] = set() + self.pros4_local_templates: Set[LocalTemplate] = set() self.depots: Dict[str, Depot] = {} self.default_target: str = 'v5' self.default_libraries: Dict[str, List[str]] = None - self.v4_libraries: Dict[str, List[str]] = None - self.use_v4 = False - self.warned_v3_deprecated = False + self.pros4_libraries: Dict[str, List[str]] = None + self.use_pros4 = False + self.warned_pros3_deprecated = False super(Conductor, self).__init__(file) needs_saving = False if MAINLINE_NAME not in self.depots or \ @@ -50,11 +50,11 @@ def __init__(self, file=None): self.depots[MAINLINE_NAME].location != MAINLINE_URL: self.depots[MAINLINE_NAME] = HttpDepot(MAINLINE_NAME, MAINLINE_URL) needs_saving = True - # add v4 depot as another remote depot - if V4_NAME not in self.depots or \ - not isinstance(self.depots[V4_NAME], HttpDepot) or \ - self.depots[V4_NAME].location != V4_URL: - self.depots[V4_NAME] = HttpDepot(V4_NAME, V4_URL) + # add PROS 4 depot as another remote depot + if PROS4_NAME not in self.depots or \ + not isinstance(self.depots[PROS4_NAME], HttpDepot) or \ + self.depots[PROS4_NAME].location != PROS4_URL: + self.depots[PROS4_NAME] = HttpDepot(PROS4_NAME, PROS4_URL) needs_saving = True if self.default_target is None: self.default_target = 'v5' @@ -65,8 +65,8 @@ def __init__(self, file=None): 'cortex': [] } needs_saving = True - if self.v4_libraries is None or len(self.v4_libraries['v5']) != 2: - self.v4_libraries = { + if self.pros4_libraries is None or len(self.pros4_libraries['v5']) != 2: + self.pros4_libraries = { 'v5': ['liblvgl', 'okapilib'], 'cortex': [] } @@ -77,11 +77,11 @@ def __init__(self, file=None): if 'cortex' not in self.default_libraries: self.default_libraries['cortex'] = [] needs_saving = True - if 'v5' not in self.v4_libraries: - self.v4_libraries['v5'] = [] + if 'v5' not in self.pros4_libraries: + self.pros4_libraries['v5'] = [] needs_saving = True - if 'cortex' not in self.v4_libraries: - self.v4_libraries['cortex'] = [] + if 'cortex' not in self.pros4_libraries: + self.pros4_libraries['cortex'] = [] needs_saving = True if needs_saving: self.save() @@ -108,8 +108,8 @@ def fetch_template(self, depot: Depot, template: BaseTemplate, **kwargs) -> Loca local_template = LocalTemplate(orig=template, location=destination) local_template.metadata['origin'] = depot.name click.echo(f'Adding {local_template.identifier} to registry...', nl=False) - if depot.name == V4_NAME: # check for v4 - self.v4_local_templates.add(local_template) + if depot.name == PROS4_NAME: # check for PROS 4 + self.pros4_local_templates.add(local_template) else: self.local_templates.add(local_template) self.save() @@ -119,11 +119,11 @@ def fetch_template(self, depot: Depot, template: BaseTemplate, **kwargs) -> Loca return local_template def purge_template(self, template: LocalTemplate): - if template.metadata['origin'] == V4_NAME: - if template not in self.v4_local_templates: - logger(__name__).info(f"{template.identifier} was not in the Conductor's local v4 templates cache.") + if template.metadata['origin'] == PROS4_NAME: + if template not in self.pros4_local_templates: + logger(__name__).info(f"{template.identifier} was not in the Conductor's local PROS 4 templates cache.") else: - self.v4_local_templates.remove(template) + self.pros4_local_templates.remove(template) else: if template not in self.local_templates: logger(__name__).info(f"{template.identifier} was not in the Conductor's local templates cache.") @@ -141,14 +141,14 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: unique: bool = True, **kwargs) -> List[BaseTemplate]: results = list() if not unique else set() kernel_version = kwargs.get('kernel_version', None) - self.use_v4 = kwargs.get('modern', False) + self.use_pros4 = kwargs.get('modern', False) if isinstance(identifier, str): query = BaseTemplate.create_query(name=identifier, **kwargs) else: query = identifier if allow_offline: - if self.use_v4: - offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.v4_local_templates) + if self.use_pros4: + offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.pros4_local_templates) else: offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.local_templates) if unique: @@ -157,8 +157,8 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: results.extend(offline_results) if allow_online: for depot in self.depots.values(): - # v4 depot will only be accessed when the --modern flag is true - if depot.name != V4_NAME or (depot.name == V4_NAME and self.use_v4): + # PROS 4 depot will only be accessed when the --modern flag is true + if depot.name != PROS4_NAME or (depot.name == PROS4_NAME and self.use_pros4): online_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), depot.get_remote_templates(force_check=force_refresh, **kwargs)) if unique: @@ -288,7 +288,7 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo remove_empty_directories=remove_empty_directories) def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: - self.use_v4 = kwargs.get('modern', False) + self.use_pros4 = kwargs.get('modern', False) if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) if re.match(r'^[\w\-. /]+$', str(Path(path))) is None: @@ -307,7 +307,7 @@ def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Pro proj.save() if not no_default_libs: - libraries = self.v4_libraries if self.use_v4 else self.default_libraries + libraries = self.pros4_libraries if self.use_pros4 else self.default_libraries for library in libraries[proj.target]: try: # remove kernel version so that latest template satisfying query is correctly selected From ac9e8045615a64656656fb50314d973283a79f65 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Mon, 18 Sep 2023 16:18:16 -0400 Subject: [PATCH 05/20] oops + deprecation notice --- pros/conductor/conductor.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index e221f898..9210eb35 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -19,7 +19,7 @@ MAINLINE_NAME = 'pros-mainline' MAINLINE_URL = 'https://purduesigbots.github.io/pros-mainline/pros-mainline.json' PROS4_NAME = 'kernel-4-mainline' -PROS4_URL = 'https://raw.githubusercontent.com/purduesigbots/pros-mainline/master/beta/kernel-beta-mainline.json' +PROS4_URL = 'https://purduesigbots.github.io/pros-mainline/beta/kernel-beta-mainline.json' """ # TBD? Currently, PROS 4 value is stored in config file @@ -242,11 +242,11 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], if not confirm: raise dont_send( InvalidTemplateException(f'Not downgrading')) - elif template.version[0] == '3' and not self.warned_v3_deprecated: + elif template.version[0] == '3' and not self.warned_pros3_deprecated: confirm = ui.confirm(f'PROS 3 is now deprecated. PROS 4 is now the latest version. ' f'Do you still want to use PROS 3?\n' f'To use PROS 4, please use the --modern flag. We will remember your choice.') - self.warned_v3_deprecated = True + self.warned_pros3_deprecated = True self.save() if not confirm: raise dont_send( @@ -289,6 +289,10 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: self.use_pros4 = kwargs.get('modern', False) + if not self.use_pros4 and self.warned_pros3_deprecated: + ui.echo(f"PROS 3 is deprecated. PROS 4 is now the latest version. " + f"To use PROS 4 use the --modern flag.") + if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) if re.match(r'^[\w\-. /]+$', str(Path(path))) is None: From d031bb4a5639d9ffa21326fe7c8777240d009123 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Thu, 21 Sep 2023 20:15:51 -0400 Subject: [PATCH 06/20] use early-access --- pros/cli/conductor.py | 18 +++------ pros/conductor/conductor.py | 76 ++++++++++++++++++------------------- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/pros/cli/conductor.py b/pros/cli/conductor.py index 99d41a9b..e60704b0 100644 --- a/pros/cli/conductor.py +++ b/pros/cli/conductor.py @@ -90,8 +90,6 @@ def fetch(query: c.BaseTemplate): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--modern', is_flag=True, default=False, show_default=True, - help='Allow applying PROS v4 templates') @project_option() @template_query(required=True) @default_options @@ -116,8 +114,6 @@ def apply(project: c.Project, query: c.BaseTemplate, **kwargs): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--modern', is_flag=True, default=False, show_default=True, - help='Allow applying PROS v4 templates') @project_option() @template_query(required=True) @default_options @@ -143,8 +139,6 @@ def install(ctx: click.Context, **kwargs): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--modern', is_flag=True, default=False, show_default=True, - help='Allow upgrading to PROS v4 templates') @project_option() @template_query(required=False) @default_options @@ -205,7 +199,7 @@ def uninstall_template(project: c.Project, query: c.BaseTemplate, remove_user: b help='Compile the project after creation') @click.option('--build-cache', is_flag=True, default=None, show_default=False, help='Build compile commands cache after creation. Overrides --compile-after if both are specified.') -@click.option('--modern', is_flag=True, default=False, show_default=True, +@click.option('--early-access', '--early', '-ea', 'early_access', is_flag=True, default=False, show_default=True, help='Create a project with a PROS v4 template') @click.pass_context @default_options @@ -255,13 +249,13 @@ def new_project(ctx: click.Context, path: str, target: str, version: str, help='Force update all remote depots, ignoring automatic update checks') @click.option('--limit', type=int, default=15, help='The maximum number of displayed results for each library') -@click.option('--modern', is_flag=True, default=False, show_default=True, +@click.option('--early-access', '--early', '-ea', 'early_access', is_flag=True, default=False, show_default=True, help='View PROS v4 templates in the listing') @template_query(required=False) @click.pass_context @default_options def query_templates(ctx, query: c.BaseTemplate, allow_offline: bool, allow_online: bool, force_refresh: bool, - limit: int, modern: bool): + limit: int, early_access: bool): """ Query local and remote templates based on a spec @@ -271,10 +265,10 @@ def query_templates(ctx, query: c.BaseTemplate, allow_offline: bool, allow_onlin if limit < 0: limit = 15 templates = c.Conductor().resolve_templates(query, allow_offline=allow_offline, allow_online=allow_online, - force_refresh=force_refresh, modern=modern) - if modern: + force_refresh=force_refresh, early_access=early_access) + if early_access: templates += c.Conductor().resolve_templates(query, allow_offline=allow_offline, allow_online=allow_online, - force_refresh=force_refresh, modern=False) + force_refresh=force_refresh, early_access=False) render_templates = {} for template in templates: diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 9210eb35..5a158372 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -18,11 +18,11 @@ MAINLINE_NAME = 'pros-mainline' MAINLINE_URL = 'https://purduesigbots.github.io/pros-mainline/pros-mainline.json' -PROS4_NAME = 'kernel-4-mainline' -PROS4_URL = 'https://purduesigbots.github.io/pros-mainline/beta/kernel-beta-mainline.json' +EARLY_ACCESS_NAME = 'kernel-4-mainline' +EARLY_ACCESS_URL = 'https://purduesigbots.github.io/pros-mainline/beta/kernel-beta-mainline.json' """ -# TBD? Currently, PROS 4 value is stored in config file +# TBD? Currently, EarlyAccess value is stored in config file class ReleaseChannel(Enum): Stable = 'stable' Beta = 'beta' @@ -36,13 +36,13 @@ def __init__(self, file=None): if not file: file = os.path.join(click.get_app_dir('PROS'), 'conductor.pros') self.local_templates: Set[LocalTemplate] = set() - self.pros4_local_templates: Set[LocalTemplate] = set() + self.early_access_local_templates: Set[LocalTemplate] = set() self.depots: Dict[str, Depot] = {} self.default_target: str = 'v5' self.default_libraries: Dict[str, List[str]] = None - self.pros4_libraries: Dict[str, List[str]] = None - self.use_pros4 = False - self.warned_pros3_deprecated = False + self.early_access_libraries: Dict[str, List[str]] = None + self.use_early_access = False + self.warn_early_access = False super(Conductor, self).__init__(file) needs_saving = False if MAINLINE_NAME not in self.depots or \ @@ -51,10 +51,10 @@ def __init__(self, file=None): self.depots[MAINLINE_NAME] = HttpDepot(MAINLINE_NAME, MAINLINE_URL) needs_saving = True # add PROS 4 depot as another remote depot - if PROS4_NAME not in self.depots or \ - not isinstance(self.depots[PROS4_NAME], HttpDepot) or \ - self.depots[PROS4_NAME].location != PROS4_URL: - self.depots[PROS4_NAME] = HttpDepot(PROS4_NAME, PROS4_URL) + if EARLY_ACCESS_NAME not in self.depots or \ + not isinstance(self.depots[EARLY_ACCESS_NAME], HttpDepot) or \ + self.depots[EARLY_ACCESS_NAME].location != EARLY_ACCESS_URL: + self.depots[EARLY_ACCESS_NAME] = HttpDepot(EARLY_ACCESS_NAME, EARLY_ACCESS_URL) needs_saving = True if self.default_target is None: self.default_target = 'v5' @@ -65,8 +65,8 @@ def __init__(self, file=None): 'cortex': [] } needs_saving = True - if self.pros4_libraries is None or len(self.pros4_libraries['v5']) != 2: - self.pros4_libraries = { + if self.early_access_libraries is None or len(self.early_access_libraries['v5']) != 2: + self.early_access_libraries = { 'v5': ['liblvgl', 'okapilib'], 'cortex': [] } @@ -77,11 +77,11 @@ def __init__(self, file=None): if 'cortex' not in self.default_libraries: self.default_libraries['cortex'] = [] needs_saving = True - if 'v5' not in self.pros4_libraries: - self.pros4_libraries['v5'] = [] + if 'v5' not in self.early_access_libraries: + self.early_access_libraries['v5'] = [] needs_saving = True - if 'cortex' not in self.pros4_libraries: - self.pros4_libraries['cortex'] = [] + if 'cortex' not in self.early_access_libraries: + self.early_access_libraries['cortex'] = [] needs_saving = True if needs_saving: self.save() @@ -108,8 +108,8 @@ def fetch_template(self, depot: Depot, template: BaseTemplate, **kwargs) -> Loca local_template = LocalTemplate(orig=template, location=destination) local_template.metadata['origin'] = depot.name click.echo(f'Adding {local_template.identifier} to registry...', nl=False) - if depot.name == PROS4_NAME: # check for PROS 4 - self.pros4_local_templates.add(local_template) + if depot.name == EARLY_ACCESS_NAME: # check for PROS 4 + self.early_access_local_templates.add(local_template) else: self.local_templates.add(local_template) self.save() @@ -119,11 +119,11 @@ def fetch_template(self, depot: Depot, template: BaseTemplate, **kwargs) -> Loca return local_template def purge_template(self, template: LocalTemplate): - if template.metadata['origin'] == PROS4_NAME: - if template not in self.pros4_local_templates: + if template.metadata['origin'] == EARLY_ACCESS_NAME: + if template not in self.early_access_local_templates: logger(__name__).info(f"{template.identifier} was not in the Conductor's local PROS 4 templates cache.") else: - self.pros4_local_templates.remove(template) + self.early_access_local_templates.remove(template) else: if template not in self.local_templates: logger(__name__).info(f"{template.identifier} was not in the Conductor's local templates cache.") @@ -141,14 +141,14 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: unique: bool = True, **kwargs) -> List[BaseTemplate]: results = list() if not unique else set() kernel_version = kwargs.get('kernel_version', None) - self.use_pros4 = kwargs.get('modern', False) + self.use_early_access = kwargs.get('early_access', False) if isinstance(identifier, str): query = BaseTemplate.create_query(name=identifier, **kwargs) else: query = identifier if allow_offline: - if self.use_pros4: - offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.pros4_local_templates) + if self.use_early_access: + offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.early_access_local_templates) else: offline_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.local_templates) if unique: @@ -157,8 +157,8 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: results.extend(offline_results) if allow_online: for depot in self.depots.values(): - # PROS 4 depot will only be accessed when the --modern flag is true - if depot.name != PROS4_NAME or (depot.name == PROS4_NAME and self.use_pros4): + # EarlyAccess depot will only be accessed when the --early-access flag is true + if depot.name != EARLY_ACCESS_NAME or (depot.name == EARLY_ACCESS_NAME and self.use_early_access): online_results = filter(lambda t: t.satisfies(query, kernel_version=kernel_version), depot.get_remote_templates(force_check=force_refresh, **kwargs)) if unique: @@ -237,16 +237,16 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], raise dont_send( InvalidTemplateException(f'Not upgrading')) if template.version[0] == '3' and curr_proj.kernel[0] == '4': - confirm = ui.confirm(f'PROS 3 is now deprecated. Downgrading project to PROS 3 will also cause breaking changes. ' + confirm = ui.confirm(f'Warning! Downgrading project to PROS 3 will cause breaking changes. ' f'Do you still want to downgrade?') if not confirm: raise dont_send( InvalidTemplateException(f'Not downgrading')) - elif template.version[0] == '3' and not self.warned_pros3_deprecated: - confirm = ui.confirm(f'PROS 3 is now deprecated. PROS 4 is now the latest version. ' - f'Do you still want to use PROS 3?\n' - f'To use PROS 4, please use the --modern flag. We will remember your choice.') - self.warned_pros3_deprecated = True + elif template.version[0] == '3' and not self.warn_early_access: + confirm = ui.confirm(f'PROS 4 is now in early access. ' + f'Please use the --early-access flag if you would like to use it.\n' + f'Do you still want to use PROS 3?') + self.warn_early_access = True self.save() if not confirm: raise dont_send( @@ -288,10 +288,10 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo remove_empty_directories=remove_empty_directories) def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: - self.use_pros4 = kwargs.get('modern', False) - if not self.use_pros4 and self.warned_pros3_deprecated: - ui.echo(f"PROS 3 is deprecated. PROS 4 is now the latest version. " - f"To use PROS 4 use the --modern flag.") + self.use_early_access = kwargs.get('early_access', False) + if not self.use_early_access and self.warn_early_access: + ui.echo(f"PROS 4 is now in early access." + f"If you would like to use it, use the --early-access flag.") if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) @@ -311,7 +311,7 @@ def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Pro proj.save() if not no_default_libs: - libraries = self.pros4_libraries if self.use_pros4 else self.default_libraries + libraries = self.early_access_libraries if self.use_early_access else self.default_libraries for library in libraries[proj.target]: try: # remove kernel version so that latest template satisfying query is correctly selected From fd62dddb91e4d3f537fb876449cdb668ecb61aa6 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Thu, 21 Sep 2023 20:19:09 -0400 Subject: [PATCH 07/20] use new urls --- pros/conductor/conductor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 5a158372..37170c03 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -17,9 +17,9 @@ from .templates import BaseTemplate, ExternalTemplate, LocalTemplate, Template MAINLINE_NAME = 'pros-mainline' -MAINLINE_URL = 'https://purduesigbots.github.io/pros-mainline/pros-mainline.json' +MAINLINE_URL = 'https://pros.cs.purdue.edu/v5/_static/releases/pros-mainline.json' EARLY_ACCESS_NAME = 'kernel-4-mainline' -EARLY_ACCESS_URL = 'https://purduesigbots.github.io/pros-mainline/beta/kernel-beta-mainline.json' +EARLY_ACCESS_URL = 'https://pros.cs.purdue.edu/v5/_static/beta/beta-pros-mainline.json' """ # TBD? Currently, EarlyAccess value is stored in config file From e121fb6dabcadfbb412d0e9626112e1a19e2ff43 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Thu, 28 Sep 2023 19:31:00 -0400 Subject: [PATCH 08/20] revert file validator --- pros/conductor/conductor.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 37170c03..5f471df8 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -3,7 +3,6 @@ from enum import Enum from pathlib import Path from typing import * -import re import click from semantic_version import Spec, Version @@ -295,8 +294,11 @@ def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Pro if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) - if re.match(r'^[\w\-. /]+$', str(Path(path))) is None: - raise dont_send(ValueError('Invalid characters found in path')) + for char in str(Path(path)): + if char in ['?', '<', '>', '*', '|', '^', '#', '%', '&', '$', '+', '!', '`', '\'', '=', + '@', '\'', '{', '}', '[', ']', '(', ')', '~'] or ord(char) > 127: + raise dont_send(ValueError(f'Invalid character found in directory name: \'{char}\'')) + proj = Project(path=path, create=True) if 'target' in kwargs: proj.target = kwargs['target'] From 53341b3e06d3a719e1e9c59d1c0fa3cacbe5671c Mon Sep 17 00:00:00 2001 From: ayushuk Date: Thu, 5 Oct 2023 11:21:56 -0400 Subject: [PATCH 09/20] Add beta alias for early access --- pros/cli/conductor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pros/cli/conductor.py b/pros/cli/conductor.py index e60704b0..33c41859 100644 --- a/pros/cli/conductor.py +++ b/pros/cli/conductor.py @@ -199,7 +199,7 @@ def uninstall_template(project: c.Project, query: c.BaseTemplate, remove_user: b help='Compile the project after creation') @click.option('--build-cache', is_flag=True, default=None, show_default=False, help='Build compile commands cache after creation. Overrides --compile-after if both are specified.') -@click.option('--early-access', '--early', '-ea', 'early_access', is_flag=True, default=False, show_default=True, +@click.option('--early-access', '--early', '-ea', 'early_access', '--beta', is_flag=True, default=False, show_default=True, help='Create a project with a PROS v4 template') @click.pass_context @default_options @@ -249,7 +249,7 @@ def new_project(ctx: click.Context, path: str, target: str, version: str, help='Force update all remote depots, ignoring automatic update checks') @click.option('--limit', type=int, default=15, help='The maximum number of displayed results for each library') -@click.option('--early-access', '--early', '-ea', 'early_access', is_flag=True, default=False, show_default=True, +@click.option('--early-access', '--early', '-ea', '--beta', 'early_access', is_flag=True, default=False, show_default=True, help='View PROS v4 templates in the listing') @template_query(required=False) @click.pass_context From fefcd9fbe34a4470683687d0d409d9ee76f900d5 Mon Sep 17 00:00:00 2001 From: ayushuk Date: Thu, 5 Oct 2023 11:30:20 -0400 Subject: [PATCH 10/20] Slight naming adjustments --- pros/conductor/conductor.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 5f471df8..2b936f2c 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -17,7 +17,7 @@ MAINLINE_NAME = 'pros-mainline' MAINLINE_URL = 'https://pros.cs.purdue.edu/v5/_static/releases/pros-mainline.json' -EARLY_ACCESS_NAME = 'kernel-4-mainline' +EARLY_ACCESS_NAME = 'kernel-early-access-mainline' EARLY_ACCESS_URL = 'https://pros.cs.purdue.edu/v5/_static/beta/beta-pros-mainline.json' """ @@ -49,7 +49,7 @@ def __init__(self, file=None): self.depots[MAINLINE_NAME].location != MAINLINE_URL: self.depots[MAINLINE_NAME] = HttpDepot(MAINLINE_NAME, MAINLINE_URL) needs_saving = True - # add PROS 4 depot as another remote depot + # add early access depot as another remote depot if EARLY_ACCESS_NAME not in self.depots or \ not isinstance(self.depots[EARLY_ACCESS_NAME], HttpDepot) or \ self.depots[EARLY_ACCESS_NAME].location != EARLY_ACCESS_URL: @@ -107,7 +107,7 @@ def fetch_template(self, depot: Depot, template: BaseTemplate, **kwargs) -> Loca local_template = LocalTemplate(orig=template, location=destination) local_template.metadata['origin'] = depot.name click.echo(f'Adding {local_template.identifier} to registry...', nl=False) - if depot.name == EARLY_ACCESS_NAME: # check for PROS 4 + if depot.name == EARLY_ACCESS_NAME: # check for early access self.early_access_local_templates.add(local_template) else: self.local_templates.add(local_template) @@ -120,7 +120,7 @@ def fetch_template(self, depot: Depot, template: BaseTemplate, **kwargs) -> Loca def purge_template(self, template: LocalTemplate): if template.metadata['origin'] == EARLY_ACCESS_NAME: if template not in self.early_access_local_templates: - logger(__name__).info(f"{template.identifier} was not in the Conductor's local PROS 4 templates cache.") + logger(__name__).info(f"{template.identifier} was not in the Conductor's local early access templates cache.") else: self.early_access_local_templates.remove(template) else: @@ -289,7 +289,7 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: self.use_early_access = kwargs.get('early_access', False) if not self.use_early_access and self.warn_early_access: - ui.echo(f"PROS 4 is now in early access." + ui.echo(f"PROS 4 is now in early access. " f"If you would like to use it, use the --early-access flag.") if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): From 975f16cb45460bb2be6cb607ee790627b23e4f38 Mon Sep 17 00:00:00 2001 From: ayushuk Date: Thu, 5 Oct 2023 12:45:35 -0400 Subject: [PATCH 11/20] Save early access preference --- pros/conductor/conductor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 2b936f2c..15c7178e 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -287,10 +287,12 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo remove_empty_directories=remove_empty_directories) def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: - self.use_early_access = kwargs.get('early_access', False) + self.use_early_access = kwargs.get('early_access', False) or self.use_early_access if not self.use_early_access and self.warn_early_access: ui.echo(f"PROS 4 is now in early access. " f"If you would like to use it, use the --early-access flag.") + elif self.use_early_access: + ui.echo(f'Early access is enabled. Using PROS 4.') if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) From cefe43054ef4f15027f757c6feb41a7ddd982ec7 Mon Sep 17 00:00:00 2001 From: ayushuk Date: Thu, 5 Oct 2023 14:14:02 -0400 Subject: [PATCH 12/20] Fix early access preference issue --- pros/conductor/conductor.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index ba47c9a5..08db17e0 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -140,13 +140,13 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: unique: bool = True, **kwargs) -> List[BaseTemplate]: results = list() if not unique else set() kernel_version = kwargs.get('kernel_version', None) - self.use_early_access = kwargs.get('early_access', False) + self.use_early_access = kwargs.get('early_access', False) or self.use_early_access if isinstance(identifier, str): query = BaseTemplate.create_query(name=identifier, **kwargs) else: query = identifier if allow_offline: - if self.is_beta: + if self.use_early_access: offline_results = list(filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.early_access_local_templates)) else: offline_results = list(filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.local_templates)) @@ -253,7 +253,8 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], if not confirm: raise dont_send( InvalidTemplateException(f'Not downgrading')) - elif template.version[0] == '3' and not self.warn_early_access: + elif not self.use_early_access and template.version[0] == '3' and not self.warn_early_access: + print('SELF USE EARLY ACCESS', self.use_early_access) confirm = ui.confirm(f'PROS 4 is now in early access. ' f'Please use the --early-access flag if you would like to use it.\n' f'Do you still want to use PROS 3?') @@ -300,6 +301,7 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: self.use_early_access = kwargs.get('early_access', False) or self.use_early_access + if not self.use_early_access and self.warn_early_access: ui.echo(f"PROS 4 is now in early access. " f"If you would like to use it, use the --early-access flag.") From 7d8a0a3ab69733847d2c70cf699f587101ce9373 Mon Sep 17 00:00:00 2001 From: ayushuk Date: Thu, 5 Oct 2023 14:14:50 -0400 Subject: [PATCH 13/20] Remove whitespace --- pros/conductor/conductor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 08db17e0..5aa23718 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -301,7 +301,6 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: self.use_early_access = kwargs.get('early_access', False) or self.use_early_access - if not self.use_early_access and self.warn_early_access: ui.echo(f"PROS 4 is now in early access. " f"If you would like to use it, use the --early-access flag.") From fae144f8f4a56961869ba7a315f4718f335e07f9 Mon Sep 17 00:00:00 2001 From: ayushuk Date: Thu, 5 Oct 2023 14:22:43 -0400 Subject: [PATCH 14/20] Remove old print statement --- pros/conductor/conductor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 5aa23718..6b245e26 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -254,7 +254,6 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], raise dont_send( InvalidTemplateException(f'Not downgrading')) elif not self.use_early_access and template.version[0] == '3' and not self.warn_early_access: - print('SELF USE EARLY ACCESS', self.use_early_access) confirm = ui.confirm(f'PROS 4 is now in early access. ' f'Please use the --early-access flag if you would like to use it.\n' f'Do you still want to use PROS 3?') From f5c9409436dab076d227b7130b46818b62341dc3 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Thu, 5 Oct 2023 13:29:32 -0500 Subject: [PATCH 15/20] Apply suggestions from code review Co-authored-by: BennyBot <48661356+BennyBot@users.noreply.github.com> --- pros/cli/conductor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pros/cli/conductor.py b/pros/cli/conductor.py index cb835df4..84f3b8ad 100644 --- a/pros/cli/conductor.py +++ b/pros/cli/conductor.py @@ -201,7 +201,7 @@ def uninstall_template(project: c.Project, query: c.BaseTemplate, remove_user: b @click.option('--build-cache', is_flag=True, default=None, show_default=False, help='Build compile commands cache after creation. Overrides --compile-after if both are specified.') @click.option('--early-access', '--early', '-ea', 'early_access', '--beta', is_flag=True, default=False, show_default=True, - help='Create a project with a PROS v4 template') + help='Create a project using the PROS 4 kernel') @click.pass_context @default_options def new_project(ctx: click.Context, path: str, target: str, version: str, @@ -251,7 +251,7 @@ def new_project(ctx: click.Context, path: str, target: str, version: str, @click.option('--limit', type=int, default=15, help='The maximum number of displayed results for each library') @click.option('--early-access', '--early', '-ea', '--beta', 'early_access', is_flag=True, default=False, show_default=True, - help='View PROS v4 templates in the listing') + help='View a list of early access templates') @template_query(required=False) @click.pass_context @default_options From 76ac7fb7828291a8610a6b4001d3918a8c11917d Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Thu, 19 Oct 2023 19:35:37 -0400 Subject: [PATCH 16/20] upgrade fixes --- pros/cli/conductor.py | 7 ++++++- pros/conductor/conductor.py | 30 +++++++++++++++--------------- pros/conductor/project/__init__.py | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pros/cli/conductor.py b/pros/cli/conductor.py index 84f3b8ad..ad3fe1c8 100644 --- a/pros/cli/conductor.py +++ b/pros/cli/conductor.py @@ -91,6 +91,8 @@ def fetch(query: c.BaseTemplate): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') +@click.option('--early-access', '--early', '-ea', 'early_access', '--beta', is_flag=True, default=False, show_default=True, + help='Create a project using the PROS 4 kernel') @project_option() @template_query(required=True) @default_options @@ -140,6 +142,8 @@ def install(ctx: click.Context, **kwargs): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') +@click.option('--early-access', '--early', '-ea', 'early_access', '--beta', is_flag=True, default=False, show_default=True, + help='Create a project using the PROS 4 kernel') @project_option() @template_query(required=False) @default_options @@ -213,6 +217,7 @@ def new_project(ctx: click.Context, path: str, target: str, version: str, Visit https://pros.cs.purdue.edu/v5/cli/conductor.html to learn more """ analytics.send("new-project") + version_source = version.lower() == 'latest' if version.lower() == 'latest' or not version: version = '>0' if not force_system and c.Project.find_project(path) is not None: @@ -223,7 +228,7 @@ def new_project(ctx: click.Context, path: str, target: str, version: str, _conductor = c.Conductor() if target is None: target = _conductor.default_target - project = _conductor.new_project(path, target=target, version=version, + project = _conductor.new_project(path, target=target, version=version, version_source=version_source, force_user=force_user, force_system=force_system, no_default_libs=no_default_libs, **kwargs) ui.echo('New PROS Project was created:', output_machine=False) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 6b245e26..1c033f8b 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -151,10 +151,6 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: else: offline_results = list(filter(lambda t: t.satisfies(query, kernel_version=kernel_version), self.local_templates)) - if len(offline_results) == 0 and kernel_version and list(filter(lambda t: t.satisfies(query, kernel_version=None), self.local_templates)): - raise dont_send( - InvalidTemplateException(f'{identifier.name} does not support kernel version {kernel_version}')) - if unique: results.update(offline_results) else: @@ -167,17 +163,16 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: online_results = list(filter(lambda t: t.satisfies(query, kernel_version=kernel_version), remote_templates)) - if len(online_results) == 0 and kernel_version and list(filter(lambda t: t.satisfies(query, kernel_version=None), - remote_templates)): - raise dont_send( - InvalidTemplateException(f'{identifier.name} does not support kernel version {kernel_version}')) - if unique: results.update(online_results) else: results.extend(online_results) logger(__name__).debug('Saving Conductor config after checking for remote updates') self.save() # Save self since there may have been some updates from the depots + + if len(results) == 0: + raise dont_send( + InvalidTemplateException(f'{identifier.name} does not support kernel version {kernel_version}')) return list(results) @@ -209,7 +204,7 @@ def resolve_template(self, identifier: Union[str, BaseTemplate], **kwargs) -> Op if len(local_templates) > 1: # This should never happen! Conductor state must be invalid raise Exception(f'Multiple local templates satisfy {query.identifier}!') - return [t for t in templates if isinstance(t, LocalTemplate)][0] + return local_templates[0] # prefer pros-mainline template second mainline_templates = [t for t in templates if t.metadata['origin'] == 'pros-mainline'] @@ -281,11 +276,13 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], force_user=kwargs.pop('force_user', False), remove_empty_directories=kwargs.pop('remove_empty_directories', False)) ui.finalize('apply', f'Finished applying {template.identifier} to {project.location}') - else: + elif valid_action != TemplateAction.AlreadyInstalled: raise dont_send( InvalidTemplateException(f'Could not install {template.identifier} because it is {valid_action.name},' f' and that is not allowed.', reason=valid_action) ) + else: + ui.finalize('apply', f'{template.identifier} is already installed in {project.location}') @staticmethod def remove_template(project: Project, identifier: Union[str, BaseTemplate], remove_user: bool = True, @@ -300,11 +297,14 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: self.use_early_access = kwargs.get('early_access', False) or self.use_early_access - if not self.use_early_access and self.warn_early_access: - ui.echo(f"PROS 4 is now in early access. " - f"If you would like to use it, use the --early-access flag.") + if kwargs["version_source"]: # If true, then the user has not specified a version + if not self.use_early_access and self.warn_early_access: + ui.echo(f"PROS 4 is now in early access. " + f"If you would like to use it, use the --early-access flag.") + elif self.use_early_access: + ui.echo(f'Early access is enabled. Using PROS 4.') elif self.use_early_access: - ui.echo(f'Early access is enabled. Using PROS 4.') + ui.echo(f'Early access is enabled.') if Path(path).exists() and Path(path).samefile(os.path.expanduser('~')): raise dont_send(ValueError('Will not create a project in user home directory')) diff --git a/pros/conductor/project/__init__.py b/pros/conductor/project/__init__.py index 76e4d192..b30733b5 100644 --- a/pros/conductor/project/__init__.py +++ b/pros/conductor/project/__init__.py @@ -126,7 +126,7 @@ def apply_template(self, template: LocalTemplate, force_system: bool = False, fo deprecated_user_files = installed_user_files.intersection(self.all_files) - set(template.user_files) if any(deprecated_user_files): if force_user or confirm(f'The following user files have been deprecated: {deprecated_user_files}. ' - f'Do you want to remove them?'): + f'Do you want to update them?'): transaction.extend_rm(deprecated_user_files) else: logger(__name__).warning(f'Deprecated user files may cause weird quirks. See migration guidelines from ' From db9ab992d7f5adcdf2fe9dec2dfabe2708a2e140 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Mon, 27 Nov 2023 18:33:29 -0500 Subject: [PATCH 17/20] disable ea --- pros/cli/conductor.py | 8 ++++---- pros/conductor/conductor.py | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pros/cli/conductor.py b/pros/cli/conductor.py index ad3fe1c8..3212cbc8 100644 --- a/pros/cli/conductor.py +++ b/pros/cli/conductor.py @@ -91,7 +91,7 @@ def fetch(query: c.BaseTemplate): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--early-access', '--early', '-ea', 'early_access', '--beta', is_flag=True, default=False, show_default=True, +@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None, help='Create a project using the PROS 4 kernel') @project_option() @template_query(required=True) @@ -142,7 +142,7 @@ def install(ctx: click.Context, **kwargs): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--early-access', '--early', '-ea', 'early_access', '--beta', is_flag=True, default=False, show_default=True, +@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None, help='Create a project using the PROS 4 kernel') @project_option() @template_query(required=False) @@ -204,7 +204,7 @@ def uninstall_template(project: c.Project, query: c.BaseTemplate, remove_user: b help='Compile the project after creation') @click.option('--build-cache', is_flag=True, default=None, show_default=False, help='Build compile commands cache after creation. Overrides --compile-after if both are specified.') -@click.option('--early-access', '--early', '-ea', 'early_access', '--beta', is_flag=True, default=False, show_default=True, +@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None, help='Create a project using the PROS 4 kernel') @click.pass_context @default_options @@ -255,7 +255,7 @@ def new_project(ctx: click.Context, path: str, target: str, version: str, help='Force update all remote depots, ignoring automatic update checks') @click.option('--limit', type=int, default=15, help='The maximum number of displayed results for each library') -@click.option('--early-access', '--early', '-ea', '--beta', 'early_access', is_flag=True, default=False, show_default=True, +@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None, help='View a list of early access templates') @template_query(required=False) @click.pass_context diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 1c033f8b..cdb6d413 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -140,7 +140,7 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: unique: bool = True, **kwargs) -> List[BaseTemplate]: results = list() if not unique else set() kernel_version = kwargs.get('kernel_version', None) - self.use_early_access = kwargs.get('early_access', False) or self.use_early_access + self.use_early_access = [kwargs.get('early_access', False), self.use_early_access][kwargs.get('early_access', None) is None] if isinstance(identifier, str): query = BaseTemplate.create_query(name=identifier, **kwargs) else: @@ -255,6 +255,7 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], self.warn_early_access = True self.save() if not confirm: + project.delete() raise dont_send( InvalidTemplateException(f'Not using PROS 3')) if not isinstance(template, LocalTemplate): @@ -296,7 +297,7 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo remove_empty_directories=remove_empty_directories) def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: - self.use_early_access = kwargs.get('early_access', False) or self.use_early_access + self.use_early_access = [kwargs.get('early_access', False), self.use_early_access][kwargs.get('early_access', None) is None] if kwargs["version_source"]: # If true, then the user has not specified a version if not self.use_early_access and self.warn_early_access: ui.echo(f"PROS 4 is now in early access. " From 45e6ad9fbbbb54a2d2217aac3aa3ffabd1de2a31 Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Mon, 27 Nov 2023 20:00:27 -0500 Subject: [PATCH 18/20] more readable --- pros/conductor/conductor.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index cdb6d413..3899164a 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -140,7 +140,8 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: unique: bool = True, **kwargs) -> List[BaseTemplate]: results = list() if not unique else set() kernel_version = kwargs.get('kernel_version', None) - self.use_early_access = [kwargs.get('early_access', False), self.use_early_access][kwargs.get('early_access', None) is None] + if kwargs.get('early_access', None) is not None: + self.use_early_access = kwargs.get('early_access', False) if isinstance(identifier, str): query = BaseTemplate.create_query(name=identifier, **kwargs) else: @@ -297,7 +298,8 @@ def remove_template(project: Project, identifier: Union[str, BaseTemplate], remo remove_empty_directories=remove_empty_directories) def new_project(self, path: str, no_default_libs: bool = False, **kwargs) -> Project: - self.use_early_access = [kwargs.get('early_access', False), self.use_early_access][kwargs.get('early_access', None) is None] + if kwargs.get('early_access', None) is not None: + self.use_early_access = kwargs.get('early_access', False) if kwargs["version_source"]: # If true, then the user has not specified a version if not self.use_early_access and self.warn_early_access: ui.echo(f"PROS 4 is now in early access. " From 830d93dfcfed433f695801e50779d5886c4f33ae Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Wed, 13 Dec 2023 15:34:56 -0500 Subject: [PATCH 19/20] redo early access warning --- pros/conductor/conductor.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pros/conductor/conductor.py b/pros/conductor/conductor.py index 3899164a..b3a8e045 100644 --- a/pros/conductor/conductor.py +++ b/pros/conductor/conductor.py @@ -171,7 +171,7 @@ def resolve_templates(self, identifier: Union[str, BaseTemplate], allow_online: logger(__name__).debug('Saving Conductor config after checking for remote updates') self.save() # Save self since there may have been some updates from the depots - if len(results) == 0: + if len(results) == 0 and (kernel_version.split('.')[0] == '3' and not self.use_early_access): raise dont_send( InvalidTemplateException(f'{identifier.name} does not support kernel version {kernel_version}')) @@ -252,13 +252,16 @@ def apply_template(self, project: Project, identifier: Union[str, BaseTemplate], elif not self.use_early_access and template.version[0] == '3' and not self.warn_early_access: confirm = ui.confirm(f'PROS 4 is now in early access. ' f'Please use the --early-access flag if you would like to use it.\n' - f'Do you still want to use PROS 3?') + f'Do you want to use PROS 4 instead?') self.warn_early_access = True + if confirm: # use pros 4 + self.use_early_access = True + kwargs['version'] = '>=0' + self.save() + # Recall the function with early access enabled + return self.apply_template(project, identifier, **kwargs) + self.save() - if not confirm: - project.delete() - raise dont_send( - InvalidTemplateException(f'Not using PROS 3')) if not isinstance(template, LocalTemplate): with ui.Notification(): template = self.fetch_template(self.get_depot(template.metadata['origin']), template, **kwargs) From 93173b9723b62b265939a47d88a23ee352a7913e Mon Sep 17 00:00:00 2001 From: 12944qwerty Date: Wed, 13 Dec 2023 18:39:22 -0500 Subject: [PATCH 20/20] rename flag --- pros/cli/conductor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pros/cli/conductor.py b/pros/cli/conductor.py index 3212cbc8..d860e6f8 100644 --- a/pros/cli/conductor.py +++ b/pros/cli/conductor.py @@ -91,7 +91,7 @@ def fetch(query: c.BaseTemplate): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None, +@click.option('--early-access/--disable-early-access', '--early/--disable-early', '-ea/-dea', 'early_access', '--beta/--disable-beta', default=None, help='Create a project using the PROS 4 kernel') @project_option() @template_query(required=True) @@ -142,7 +142,7 @@ def install(ctx: click.Context, **kwargs): help="Force apply the template, disregarding if the template is already installed.") @click.option('--remove-empty-dirs/--no-remove-empty-dirs', 'remove_empty_directories', is_flag=True, default=True, help='Remove empty directories when removing files') -@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None, +@click.option('--early-access/--disable-early-access', '--early/--disable-early', '-ea/-dea', 'early_access', '--beta/--disable-beta', default=None, help='Create a project using the PROS 4 kernel') @project_option() @template_query(required=False) @@ -204,7 +204,7 @@ def uninstall_template(project: c.Project, query: c.BaseTemplate, remove_user: b help='Compile the project after creation') @click.option('--build-cache', is_flag=True, default=None, show_default=False, help='Build compile commands cache after creation. Overrides --compile-after if both are specified.') -@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None, +@click.option('--early-access/--disable-early-access', '--early/--disable-early', '-ea/-dea', 'early_access', '--beta/--disable-beta', default=None, help='Create a project using the PROS 4 kernel') @click.pass_context @default_options @@ -255,7 +255,7 @@ def new_project(ctx: click.Context, path: str, target: str, version: str, help='Force update all remote depots, ignoring automatic update checks') @click.option('--limit', type=int, default=15, help='The maximum number of displayed results for each library') -@click.option('--early-access/--no-early-access', '--early/--no-early', '-ea/-nea', 'early_access', '--beta/--no-beta', default=None, +@click.option('--early-access/--disable-early-access', '--early/--disable-early', '-ea/-dea', 'early_access', '--beta/--disable-beta', default=None, help='View a list of early access templates') @template_query(required=False) @click.pass_context