From 8e5071ec7a4b90fa0773c269c53c15f7705fa855 Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Thu, 19 Dec 2024 23:00:31 -0600 Subject: [PATCH 1/8] fix: add content build run --force flag Signed-off-by: Lucas Rodriguez --- CHANGELOG.md | 2 + rsconnect/actions_content.py | 36 ++++++----- rsconnect/main.py | 8 ++- rsconnect/utils_package.py | 2 +- tests/test_main_content.py | 120 +++++++++++++++++++++++++++++++++-- 5 files changed, 145 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92cc1fae..d96c7127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added validation for required flags for the `rsconnect system caches delete` command. +- Added `--force` flag to `rsconnect content build run` command. This allows users + to force builds when a build is already marked as running. (#630) ## [1.25.0] - 2024-12-18 diff --git a/rsconnect/actions_content.py b/rsconnect/actions_content.py index ce49b324..996b160e 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("Proceed with the build? Type 'yes' to confirm, any other key to cancel: ").strip().lower() + if user_input != "yes": + logger.warning("Build aborted.") + return + logger.info("Proceeding with the build...") # 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." ) diff --git a/tests/test_main_content.py b/tests/test_main_content.py index c13eed4b..d1c6b8aa 100644 --- a/tests/test_main_content.py +++ b/tests/test_main_content.py @@ -3,6 +3,7 @@ import shutil import tarfile import unittest +from unittest.mock import patch import httpretty from click.testing import CliRunner @@ -11,7 +12,8 @@ from rsconnect import VERSION from rsconnect.api import RSConnectServer from rsconnect.models import BuildStatus -from rsconnect.metadata import ContentBuildStore, _normalize_server_url +from rsconnect.actions_content import ensure_content_build_store +from rsconnect.metadata import _normalize_server_url from .utils import apply_common_args @@ -98,6 +100,8 @@ def tearDownClass(cls): def setUp(self): self.connect_server = "http://localhost:3939" self.api_key = "testapikey123" + self.build_store = ensure_content_build_store(RSConnectServer(self.connect_server, self.api_key)) + self.build_store.set_build_running(False) def test_version(self): runner = CliRunner() @@ -218,10 +222,9 @@ def test_build_retry(self): self.assertTrue(os.path.exists("%s/%s.json" % (TEMP_DIR, _normalize_server_url(self.connect_server)))) # change the content build status so it looks like it was interrupted/failed - store = ContentBuildStore(RSConnectServer(self.connect_server, self.api_key)) - store.set_content_item_build_status("7d59c5c7-c4a7-4950-acc3-3943b7192bc4", BuildStatus.RUNNING) - store.set_content_item_build_status("ab497e4b-b706-4ae7-be49-228979a95eb4", BuildStatus.ABORTED) - store.set_content_item_build_status("cdfed1f7-0e09-40eb-996d-0ef77ea2d797", BuildStatus.ERROR) + self.build_store.set_content_item_build_status("7d59c5c7-c4a7-4950-acc3-3943b7192bc4", BuildStatus.RUNNING) + self.build_store.set_content_item_build_status("ab497e4b-b706-4ae7-be49-228979a95eb4", BuildStatus.ABORTED) + self.build_store.set_content_item_build_status("cdfed1f7-0e09-40eb-996d-0ef77ea2d797", BuildStatus.ERROR) # run the build args = ["content", "build", "run", "--retry"] @@ -250,6 +253,113 @@ def test_build_retry(self): self.assertEqual(listing[1]["rsconnect_build_status"], BuildStatus.COMPLETE) self.assertEqual(listing[2]["rsconnect_build_status"], BuildStatus.COMPLETE) + @httpretty.activate(verbose=True, allow_net_connect=False) + def test_build_already_running_error(self): + register_uris(self.connect_server) + runner = CliRunner() + + args = ["content", "build", "add", "-g", "7d59c5c7-c4a7-4950-acc3-3943b7192bc4"] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0, result.output) + self.assertTrue(os.path.exists("%s/%s.json" % (TEMP_DIR, _normalize_server_url(self.connect_server)))) + + # set rsconnect_build_running to true to trigger "already a build running" error + self.build_store.set_build_running(True) + + # build without --force flag should fail + args = ["content", "build", "run"] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 1) + self.assertRegex(result.output, "There is already a build running on this server") + self.assertRegex(result.output, "Use the '--force' flag to override this check") + + @httpretty.activate(verbose=True, allow_net_connect=False) + def test_build_force_abort(self): + register_uris(self.connect_server) + runner = CliRunner() + + args = ["content", "build", "add", "-g", "7d59c5c7-c4a7-4950-acc3-3943b7192bc4"] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0, result.output) + self.assertTrue(os.path.exists("%s/%s.json" % (TEMP_DIR, _normalize_server_url(self.connect_server)))) + + # set rsconnect_build_running to true + # --force flag should ignore this and not fail. + self.build_store.set_build_running(True) + + # mock "no" input to simulate user response to prompt + with patch("builtins.input", return_value="no"), self.assertLogs("rsconnect") as log: + args = ["content", "build", "run", "--force"] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0) + self.assertIn("Please ensure a build is not already running in another terminal", log.output[0]) + self.assertIn("Build aborted", log.output[1]) + + @httpretty.activate(verbose=True, allow_net_connect=False) + def test_build_force_success(self): + register_uris(self.connect_server) + runner = CliRunner() + + # add 3 content items + args = [ + "content", + "build", + "add", + "-g", + "7d59c5c7-c4a7-4950-acc3-3943b7192bc4", + "-g", + "ab497e4b-b706-4ae7-be49-228979a95eb4", + "-g", + "cdfed1f7-0e09-40eb-996d-0ef77ea2d797", + ] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0, result.output) + self.assertTrue(os.path.exists("%s/%s.json" % (TEMP_DIR, _normalize_server_url(self.connect_server)))) + + # change the content build status so it looks like it was interrupted/failed + self.build_store.set_content_item_build_status("7d59c5c7-c4a7-4950-acc3-3943b7192bc4", BuildStatus.RUNNING) + self.build_store.set_content_item_build_status("ab497e4b-b706-4ae7-be49-228979a95eb4", BuildStatus.ABORTED) + self.build_store.set_content_item_build_status("cdfed1f7-0e09-40eb-996d-0ef77ea2d797", BuildStatus.ERROR) + + # set rsconnect_build_running to true + # --force flag should ignore this and not fail. + self.build_store.set_build_running(True) + + # mock "yes" input to simulate user response to prompt + with patch("builtins.input", return_value="yes"), self.assertLogs("rsconnect") as log: + args = ["content", "build", "run", "--force"] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0) + self.assertIn("Please ensure a build is not already running in another terminal", log.output[0]) + self.assertIn("Proceeding with the build...", log.output[1]) + + # check that the build succeeded + args = [ + "content", + "build", + "ls", + "-g", + "7d59c5c7-c4a7-4950-acc3-3943b7192bc4", + "-g", + "ab497e4b-b706-4ae7-be49-228979a95eb4", + "-g", + "cdfed1f7-0e09-40eb-996d-0ef77ea2d797", + ] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0, result.output) + listing = json.loads(result.output) + self.assertTrue(len(listing) == 3) + self.assertEqual(listing[0]["rsconnect_build_status"], BuildStatus.COMPLETE) + self.assertEqual(listing[1]["rsconnect_build_status"], BuildStatus.COMPLETE) + self.assertEqual(listing[2]["rsconnect_build_status"], BuildStatus.COMPLETE) + @httpretty.activate(verbose=True, allow_net_connect=False) def test_build_rm(self): register_uris(self.connect_server) From 73199782d1c41b2ffc96ce6cd9317bf0ab67765a Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 20 Dec 2024 10:33:58 -0600 Subject: [PATCH 2/8] update error message wording Signed-off-by: Lucas Rodriguez --- rsconnect/actions_content.py | 4 ++-- tests/test_main_content.py | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/rsconnect/actions_content.py b/rsconnect/actions_content.py index 996b160e..656ff828 100644 --- a/rsconnect/actions_content.py +++ b/rsconnect/actions_content.py @@ -136,8 +136,8 @@ def build_start( build_store = ensure_content_build_store(connect_server) 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 + "A content build operation targeting '%s' is still running, or exited abnormally. " + "Use the '--force' option to override this check." % connect_server.url ) # prompt the user to confirm that they want to --force a build. diff --git a/tests/test_main_content.py b/tests/test_main_content.py index d1c6b8aa..3b913da3 100644 --- a/tests/test_main_content.py +++ b/tests/test_main_content.py @@ -272,8 +272,11 @@ def test_build_already_running_error(self): apply_common_args(args, server=self.connect_server, key=self.api_key) result = runner.invoke(cli, args) self.assertEqual(result.exit_code, 1) - self.assertRegex(result.output, "There is already a build running on this server") - self.assertRegex(result.output, "Use the '--force' flag to override this check") + self.assertRegex( + result.output, + "A content build operation targeting 'http://localhost:3939' is still running, or exited abnormally", + ) + self.assertRegex(result.output, "Use the '--force' option to override this check") @httpretty.activate(verbose=True, allow_net_connect=False) def test_build_force_abort(self): From e440ee6177a7b88997c69d3f6313c050513ac191 Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 20 Dec 2024 10:43:38 -0600 Subject: [PATCH 3/8] do not prompt user for confirmation when force provided Signed-off-by: Lucas Rodriguez --- rsconnect/actions_content.py | 9 --------- tests/test_main_content.py | 38 +++++------------------------------- 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/rsconnect/actions_content.py b/rsconnect/actions_content.py index 656ff828..abab62c2 100644 --- a/rsconnect/actions_content.py +++ b/rsconnect/actions_content.py @@ -140,15 +140,6 @@ def build_start( "Use the '--force' option 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("Proceed with the build? Type 'yes' to confirm, any other key to cancel: ").strip().lower() - if user_input != "yes": - logger.warning("Build aborted.") - return - logger.info("Proceeding with the build...") - # if we are re-building any already "tracked" content items, then re-add them to be safe if all: logger.info("Adding all content to build...") diff --git a/tests/test_main_content.py b/tests/test_main_content.py index 3b913da3..01817bc6 100644 --- a/tests/test_main_content.py +++ b/tests/test_main_content.py @@ -279,31 +279,7 @@ def test_build_already_running_error(self): self.assertRegex(result.output, "Use the '--force' option to override this check") @httpretty.activate(verbose=True, allow_net_connect=False) - def test_build_force_abort(self): - register_uris(self.connect_server) - runner = CliRunner() - - args = ["content", "build", "add", "-g", "7d59c5c7-c4a7-4950-acc3-3943b7192bc4"] - apply_common_args(args, server=self.connect_server, key=self.api_key) - result = runner.invoke(cli, args) - self.assertEqual(result.exit_code, 0, result.output) - self.assertTrue(os.path.exists("%s/%s.json" % (TEMP_DIR, _normalize_server_url(self.connect_server)))) - - # set rsconnect_build_running to true - # --force flag should ignore this and not fail. - self.build_store.set_build_running(True) - - # mock "no" input to simulate user response to prompt - with patch("builtins.input", return_value="no"), self.assertLogs("rsconnect") as log: - args = ["content", "build", "run", "--force"] - apply_common_args(args, server=self.connect_server, key=self.api_key) - result = runner.invoke(cli, args) - self.assertEqual(result.exit_code, 0) - self.assertIn("Please ensure a build is not already running in another terminal", log.output[0]) - self.assertIn("Build aborted", log.output[1]) - - @httpretty.activate(verbose=True, allow_net_connect=False) - def test_build_force_success(self): + def test_build_force(self): register_uris(self.connect_server) runner = CliRunner() @@ -333,14 +309,10 @@ def test_build_force_success(self): # --force flag should ignore this and not fail. self.build_store.set_build_running(True) - # mock "yes" input to simulate user response to prompt - with patch("builtins.input", return_value="yes"), self.assertLogs("rsconnect") as log: - args = ["content", "build", "run", "--force"] - apply_common_args(args, server=self.connect_server, key=self.api_key) - result = runner.invoke(cli, args) - self.assertEqual(result.exit_code, 0) - self.assertIn("Please ensure a build is not already running in another terminal", log.output[0]) - self.assertIn("Proceeding with the build...", log.output[1]) + args = ["content", "build", "run", "--force"] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0, result.output) # check that the build succeeded args = [ From 89a41c214483c4dd98653ba95ad2614ae804cf9c Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 20 Dec 2024 10:53:55 -0600 Subject: [PATCH 4/8] --force only builds NEEDS_BUILD content by default Signed-off-by: Lucas Rodriguez --- rsconnect/actions_content.py | 3 +- tests/test_main_content.py | 55 +++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/rsconnect/actions_content.py b/rsconnect/actions_content.py index abab62c2..562c09a7 100644 --- a/rsconnect/actions_content.py +++ b/rsconnect/actions_content.py @@ -148,8 +148,7 @@ def build_start( build_add_content(connect_server, all_content) else: # --retry is shorthand for --aborted --error --running - # --force has the same behavior as --retry and also ignores when rsconnect_build_running=true - if retry or force: + if retry: aborted = True error = True running = True diff --git a/tests/test_main_content.py b/tests/test_main_content.py index 01817bc6..9e6e1015 100644 --- a/tests/test_main_content.py +++ b/tests/test_main_content.py @@ -300,7 +300,60 @@ def test_build_force(self): self.assertEqual(result.exit_code, 0, result.output) self.assertTrue(os.path.exists("%s/%s.json" % (TEMP_DIR, _normalize_server_url(self.connect_server)))) + # set rsconnect_build_running to true + # --force flag should ignore this and not fail. + self.build_store.set_build_running(True) + + args = ["content", "build", "run", "--force"] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0, result.output) + + # check that the build succeeded + args = [ + "content", + "build", + "ls", + "-g", + "7d59c5c7-c4a7-4950-acc3-3943b7192bc4", + "-g", + "ab497e4b-b706-4ae7-be49-228979a95eb4", + "-g", + "cdfed1f7-0e09-40eb-996d-0ef77ea2d797", + ] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0, result.output) + listing = json.loads(result.output) + self.assertTrue(len(listing) == 3) + self.assertEqual(listing[0]["rsconnect_build_status"], BuildStatus.COMPLETE) + self.assertEqual(listing[1]["rsconnect_build_status"], BuildStatus.COMPLETE) + self.assertEqual(listing[2]["rsconnect_build_status"], BuildStatus.COMPLETE) + + @httpretty.activate(verbose=True, allow_net_connect=False) + def test_build_force_retry(self): + register_uris(self.connect_server) + runner = CliRunner() + + # add 3 content items + args = [ + "content", + "build", + "add", + "-g", + "7d59c5c7-c4a7-4950-acc3-3943b7192bc4", + "-g", + "ab497e4b-b706-4ae7-be49-228979a95eb4", + "-g", + "cdfed1f7-0e09-40eb-996d-0ef77ea2d797", + ] + apply_common_args(args, server=self.connect_server, key=self.api_key) + result = runner.invoke(cli, args) + self.assertEqual(result.exit_code, 0, result.output) + self.assertTrue(os.path.exists("%s/%s.json" % (TEMP_DIR, _normalize_server_url(self.connect_server)))) + # change the content build status so it looks like it was interrupted/failed + # --retry used with --force should successfully build content with these statuses. self.build_store.set_content_item_build_status("7d59c5c7-c4a7-4950-acc3-3943b7192bc4", BuildStatus.RUNNING) self.build_store.set_content_item_build_status("ab497e4b-b706-4ae7-be49-228979a95eb4", BuildStatus.ABORTED) self.build_store.set_content_item_build_status("cdfed1f7-0e09-40eb-996d-0ef77ea2d797", BuildStatus.ERROR) @@ -309,7 +362,7 @@ def test_build_force(self): # --force flag should ignore this and not fail. self.build_store.set_build_running(True) - args = ["content", "build", "run", "--force"] + args = ["content", "build", "run", "--force", "--retry"] apply_common_args(args, server=self.connect_server, key=self.api_key) result = runner.invoke(cli, args) self.assertEqual(result.exit_code, 0, result.output) From 3161aaf5c0b795eb2dc283c540e819457de6c03e Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 20 Dec 2024 10:56:01 -0600 Subject: [PATCH 5/8] remove unused import Signed-off-by: Lucas Rodriguez --- tests/test_main_content.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_main_content.py b/tests/test_main_content.py index 9e6e1015..64984dac 100644 --- a/tests/test_main_content.py +++ b/tests/test_main_content.py @@ -3,7 +3,6 @@ import shutil import tarfile import unittest -from unittest.mock import patch import httpretty from click.testing import CliRunner From 3b832b856debbfd5c113d65e9b15275e5d5d0c8f Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 20 Dec 2024 10:57:24 -0600 Subject: [PATCH 6/8] update --force option description Signed-off-by: Lucas Rodriguez --- rsconnect/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/main.py b/rsconnect/main.py index d5f1a988..28470478 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -2758,7 +2758,7 @@ def get_build_logs( @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.", + help="Always build content even if a build is already marked as running.", ) @click.pass_context def start_content_build( From 68575fd5ea4987f06e51e4e3df75bd0b42c2e70d Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 20 Dec 2024 11:03:25 -0600 Subject: [PATCH 7/8] document --force option in readme Signed-off-by: Lucas Rodriguez --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 98847982..a99fdd65 100644 --- a/README.md +++ b/README.md @@ -885,6 +885,8 @@ build all "tracked" content that has the status `NEEDS_BUILD`. > To re-run failed builds, use `rsconnect content build run --retry`. This will build all tracked content in any of the following states: `[NEEDS_BUILD, ABORTED, ERROR, RUNNING]`. +> +> If you encounter an error stating that a build operation is already running, you can use `rsconnect content build run --force`. This will override this check and build any content marked as `NEEDS_BUILD`. ```bash rsconnect content build run From cbbd89528d35acd4a18627a239beeb81c384fd0d Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 20 Dec 2024 13:46:59 -0600 Subject: [PATCH 8/8] Add note about ensuring no active build process before using --force Also, improve overall flow of --force docs in readme Signed-off-by: Lucas Rodriguez --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a99fdd65..8596cf20 100644 --- a/README.md +++ b/README.md @@ -886,7 +886,9 @@ build all "tracked" content that has the status `NEEDS_BUILD`. > To re-run failed builds, use `rsconnect content build run --retry`. This will build all tracked content in any of the following states: `[NEEDS_BUILD, ABORTED, ERROR, RUNNING]`. > -> If you encounter an error stating that a build operation is already running, you can use `rsconnect content build run --force`. This will override this check and build any content marked as `NEEDS_BUILD`. +> If you encounter an error indicating that a build operation is already in progress, +you can use `rsconnect content build run --force` to bypass the check and proceed with building content marked as `NEEDS_BUILD`. +Ensure no other build operation is actively running before using the `--force` option. ```bash rsconnect content build run