diff --git a/rsconnect/actions_content.py b/rsconnect/actions_content.py index ce49b324..918c5327 100644 --- a/rsconnect/actions_content.py +++ b/rsconnect/actions_content.py @@ -49,12 +49,6 @@ def build_add_content( :param content_guids_with_bundle: Union[tuple[models.ContentGuidWithBundle], list[models.ContentGuidWithBundle]] """ build_store = ensure_content_build_store(connect_server) - if build_store.get_build_running(): - raise RSConnectException( - "There is already a build running on this server, " - + "please wait for it to finish before adding new content." - ) - with RSConnectClient(connect_server) as client: if len(content_guids_with_bundle) == 1: all_content = [client.content_get(content_guids_with_bundle[0].guid)] @@ -104,10 +98,6 @@ def build_remove_content( _validate_build_rm_args(guid, all, purge) build_store = ensure_content_build_store(connect_server) - if build_store.get_build_running(): - raise RSConnectException( - "There is a build running on this server, " + "please wait for it to finish before removing content." - ) guids: list[str] if all: guids = [c["guid"] for c in build_store.get_content_items()] @@ -141,10 +131,23 @@ def build_start( all: bool = False, poll_wait: int = 1, debug: bool = False, + force: bool = False, ): build_store = ensure_content_build_store(connect_server) - if build_store.get_build_running(): - raise RSConnectException("There is already a build running on this server: %s" % connect_server.url) + if build_store.get_build_running() and not force: + raise RSConnectException( + "There is already a build running on this server: %s. " + "Use the '--force' flag to override this check." % connect_server.url + ) + + # prompt the user to confirm that they want to --force a build. + if force: + logger.warning("Please ensure a build is not already running in another terminal before proceeding.") + user_input = input("Are you sure you want to proceed? Type 'yes' to confirm: ").strip().lower() + if user_input != "yes": + logger.warning("Build aborted.") + return + logger.info("Proceeding with the build operation...") # if we are re-building any already "tracked" content items, then re-add them to be safe if all: @@ -154,7 +157,8 @@ def build_start( build_add_content(connect_server, all_content) else: # --retry is shorthand for --aborted --error --running - if retry: + # --force has the same behavior as --retry and also ignores when rsconnect_build_running=true + if retry or force: aborted = True error = True running = True @@ -277,12 +281,12 @@ def _monitor_build(connect_server: RSConnectServer, content_items: list[ContentI ) if build_store.aborted(): - logger.warn("Build interrupted!") + logger.warning("Build interrupted!") aborted_builds = [i["guid"] for i in content_items if i["rsconnect_build_status"] == BuildStatus.RUNNING] if len(aborted_builds) > 0: - logger.warn("Marking %d builds as ABORTED..." % len(aborted_builds)) + logger.warning("Marking %d builds as ABORTED..." % len(aborted_builds)) for guid in aborted_builds: - logger.warn("Build aborted: %s" % guid) + logger.warning("Build aborted: %s" % guid) build_store.set_content_item_build_status(guid, BuildStatus.ABORTED) return False diff --git a/rsconnect/main.py b/rsconnect/main.py index 0902bb5c..d5f1a988 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -2755,6 +2755,11 @@ def get_build_logs( is_flag=True, help="Log stacktraces from exceptions during background operations.", ) +@click.option( + "--force", + is_flag=True, + help="Always build content even if a build is already marked as running. Builds the same content as --retry.", +) @click.pass_context def start_content_build( ctx: click.Context, @@ -2772,6 +2777,7 @@ def start_content_build( poll_wait: int, format: LogOutputFormat.All, debug: bool, + force: bool, verbose: int, ): set_verbosity(verbose) @@ -2781,7 +2787,7 @@ def start_content_build( ce = RSConnectExecutor(ctx, name, server, api_key, insecure, cacert, logger=None).validate_server() if not isinstance(ce.remote_server, RSConnectServer): raise RSConnectException("rsconnect content build run` requires a Posit Connect server.") - build_start(ce.remote_server, parallelism, aborted, error, running, retry, all, poll_wait, debug) + build_start(ce.remote_server, parallelism, aborted, error, running, retry, all, poll_wait, debug, force) @cli.group(no_args_is_help=True, help="Interact with Posit Connect's system API.") diff --git a/rsconnect/utils_package.py b/rsconnect/utils_package.py index 94ded49f..613b49a2 100644 --- a/rsconnect/utils_package.py +++ b/rsconnect/utils_package.py @@ -246,7 +246,7 @@ def fix_starlette_requirements( if compare_semvers(starlette_req.specs[0].version, "0.35.0") >= 0: # starlette is in requirements.txt, but with a version spec that is # not compatible with this version of Connect. - logger.warn( + logger.warning( "Starlette version is 0.35.0 or greater, but this version of Connect " "requires starlette<0.35.0. Setting to <0.35.0." )