Skip to content

Commit 4cfb709

Browse files
fix: error clearly when --draft used against unsupported Connect
should_deploy_as_draft() returned True for --draft regardless of server support, so --draft against Connect < 2025.06.0 still sent activate:false and failed with a cryptic "unknown field" error. Raise a clear RSConnectException instead; silently downgrading to a non-draft deploy would activate the bundle, the opposite of --draft's intent. Add a unit test for --draft on an unsupported server. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7cd450c commit 4cfb709

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

rsconnect/api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,10 @@ def should_deploy_as_draft(self, draft: bool, no_verify: bool) -> bool:
15281528
With ``--no-verify`` we activate immediately.
15291529
"""
15301530
if draft:
1531+
if not self.supports_verify_before_activate:
1532+
# We can't honor --draft without the activate field: silently activating
1533+
# would be the opposite of what the user asked for, so fail loudly.
1534+
raise RSConnectException("Deploying as a draft requires Posit Connect 2025.06.0 or later.")
15311535
return True
15321536
if no_verify:
15331537
return False

tests/test_main.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,70 @@ def get_active_content_callback(request, uri, response_headers):
544544
if original_server_value:
545545
os.environ["CONNECT_SERVER"] = original_server_value
546546

547+
@httpretty.activate(verbose=True, allow_net_connect=False)
548+
def test_deploy_draft_unsupported_server(self):
549+
# --draft cannot be honored on servers that reject the "activate" field, and
550+
# silently activating would be the opposite of the user's intent, so the deploy
551+
# fails with a clear error instead of sending an unsupported request.
552+
original_api_key_value = os.environ.pop("CONNECT_API_KEY", None)
553+
original_server_value = os.environ.pop("CONNECT_SERVER", None)
554+
555+
httpretty.register_uri(
556+
httpretty.GET,
557+
"http://fake_server/__api__/server_settings",
558+
body=json.dumps({"version": "2025.03.0"}),
559+
adding_headers={"Content-Type": "application/json"},
560+
status=200,
561+
)
562+
httpretty.register_uri(
563+
httpretty.GET,
564+
"http://fake_server/__api__/v1/user",
565+
body=open("tests/testdata/connect-responses/me.json", "r").read(),
566+
adding_headers={"Content-Type": "application/json"},
567+
status=200,
568+
)
569+
httpretty.register_uri(
570+
httpretty.GET,
571+
"http://fake_server/__api__/v1/content?name=app5",
572+
body=json.dumps([]),
573+
adding_headers={"Content-Type": "application/json"},
574+
status=200,
575+
)
576+
577+
# The deploy endpoint must never be called: we should fail before sending it.
578+
deploy_invoked = []
579+
580+
def post_application_deploy_callback(request, uri, response_headers):
581+
deploy_invoked.append(uri)
582+
return [201, {"Content-Type": "application/json"}, json.dumps({"task_id": "FAKE_TASK_ID"})]
583+
584+
httpretty.register_uri(
585+
httpretty.POST,
586+
"http://fake_server/__api__/v1/content/1234-5678-9012-3456/deploy",
587+
body=post_application_deploy_callback,
588+
)
589+
590+
try:
591+
runner = CliRunner()
592+
args = apply_common_args(
593+
["deploy", "manifest", "--draft", get_manifest_path("pyshiny_with_manifest", "")],
594+
server="http://fake_server",
595+
key="FAKE_API_KEY",
596+
)
597+
with mock.patch(
598+
"rsconnect.api.RSConnectExecutor.validate_app_mode",
599+
new=lambda self_, *args, **kwargs: self_,
600+
):
601+
result = runner.invoke(cli, args)
602+
assert result.exit_code != 0
603+
assert "Deploying as a draft requires Posit Connect 2025.06.0 or later." in result.output
604+
assert deploy_invoked == []
605+
finally:
606+
if original_api_key_value:
607+
os.environ["CONNECT_API_KEY"] = original_api_key_value
608+
if original_server_value:
609+
os.environ["CONNECT_SERVER"] = original_server_value
610+
547611
@httpretty.activate(verbose=True, allow_net_connect=False)
548612
def test_deploy_bundle(self, caplog):
549613
# Deploying a downloaded bundle should upload the tarball as-is (no

0 commit comments

Comments
 (0)