You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #830. That PR made rsconnect-python read/write Posit Publisher's .posit/publish TOML, drive bundling from a config's files, and propagate integration_requests into manifest.json. What it does not do is apply the
config's Connect-side settings — the fields that Publisher applies through the
Connect API after upload rather than through manifest.json.
Today those fields (description, connect.runtime.*, connect.access.*, connect.kubernetes.*, environment, secrets) are round-tripped and preserved
in the config, but a rsconnect deploy/redeploy from a Publisher-authored config
silently does not apply them to the content. This issue tracks closing that gap
in two pieces:
A post-deploy hook that applies the config's Connect-side settings after a
successful deploy (mirroring Publisher).
A rsconnect sync command that applies the current TOML settings to
already-deployed content ad hoc, without rebuilding/redeploying a bundle —
for when a user edits the config and just wants Connect updated.
Where each config field is applied (reference: Posit Publisher)
Publisher builds the manifest from the config, then makes two kinds of API calls.
Verified in the publisher repo: extensions/vscode/src/bundler/{manifestFromConfig,connectContentFromConfig}.ts
and extensions/vscode/src/publish/{connectPublish,publishShared}.ts.
environment (non-secret name→value map) is read straight from the config
and set on the content.
secrets — follow Publisher's model exactly. The config's secrets array
holds secret names only; values are never stored in the TOML or anywhere
on disk. Publisher keeps values in-memory in its webview for the session, does
not persist them (only credentials/Snowflake tokens use VS Code SecretStorage),
and re-prompts (names reload with empty values) on the next session. At publish
it sends only the names the user actually filled in, merges them over environment via mergeEnvVars (secret values win on key collision), sets them
on the content flagged as secret (encrypted/masked), and omits any
declared name with no value (it never sends empty values).
CLI analogue (the faithful equivalent of Publisher's per-session entry): at
deploy/sync time, source each declared secret's value ad hoc — from the
process environment ($NAME) and/or explicit --secret NAME=VALUE flags — never
from a file. Merge over environment (secret wins), set flagged as secret, and skip any declared name with no supplied value (warn, don't fail). rsconnect
already has an env-var code path (--environment / executor env_vars) to reuse
for the actual set call.
Part 1 — Post-deploy hook
Goal: after a successful Connect/SPCS deploy that has an applicable .posit/publish config, apply that config's Connect-side settings to the deployed
content.
Design sketch:
Add a RSConnectClient method for the content-settings PATCH
(PATCH v1/content/{guid}) if one does not already exist, plus reuse the existing
env-var setter.
Add a builder in rsconnect/publisher/ that maps a PublisherConfig to the
content-settings payload (the rsconnect analogue of connectContentFromConfig)
and to the merged env-var map (analogue of mergeEnvVars). Read connect.*, environment, secrets, description from the config (they currently live in PublisherConfig.extra; consider promoting to managed fields).
Call it from RSConnectExecutor after wait_for_task completes, gated to
Connect/SPCS (same gate as _save_publisher_metadata), best-effort (warn,
don't fail the deploy — the bundle already deployed).
Runtime settings are invalid on static content → skip/warn.
Validate min_processes <= max_processes; clamp/validate *_request <= *_limit;
respect server scheduler.*_limit maxima where discoverable.
run_as / service_account_name require admin; run_as_current_user and
Kubernetes require server license/config — surface a clear warning when the
server rejects, rather than aborting.
secrets: follow Publisher's model (see the env-var section above) — names live
in the TOML, values are sourced ad hoc at run time ($NAME / --secret NAME=VALUE), never persisted; unset names are skipped, not sent empty; supplied
values are set flagged as secret and override environment on key collision.
No-op cleanly when the config declares none of these fields.
Part 2 — rsconnect sync command
Goal: apply the TOML config's Connect-side settings to already-deployed content ad hoc, without rebuilding or redeploying a bundle. This is the "I edited the
config, push the settings to Connect" path.
Behavior:
rsconnect sync [PATH] — PATH defaults to the current directory, like redeploy.
Resolves the applicable .posit/publish config (single, --config-name, or
entrypoint match) and the target content GUID from the matching deployment
record.
Server targeting: accept --server/--name/--api-key; when none are
given, default to the server in the config / deployment record, resolving a
saved rsconnect-python credential by normalized URL (reuse _find_saved_server_by_url, as redeploy does).
Applies exactly the Part 1 post-deploy hook logic against the resolved content — no bundle build, no upload, no new deployment record (though it may refresh
the record's timestamp/settings snapshot; TBD).
Errors clearly if there is no deployment record (nothing to sync to) and no
destination was supplied.
Shared code: Part 1 and Part 2 must call the same
config→(content-settings + env-vars)→API applier so behavior can't drift.
Acceptance criteria
A config's description, connect.runtime.*, connect.access.*, and connect.kubernetes.* are applied to the content on deploy/redeploy
(Connect/SPCS only), best-effort.
A config's environment is applied as env vars on deploy/redeploy.
secrets follow Publisher's model: names only in the TOML; values sourced ad
hoc ($NAME / --secret NAME=VALUE), never persisted; unset names skipped;
supplied values set flagged as secret and overriding environment on
collision.
rsconnect sync [PATH] applies the same settings ad hoc to already-deployed
content, with no bundle rebuild.
sync targets the config/record server by default and honors --server/--name/--api-key overrides.
Server-side validation failures (limits, licensing, admin-only) warn clearly
without corrupting the deploy.
Tests cover the config→payload mapping, the deploy hook, and sync
(including default vs. explicit server targeting).
Publisher secrets model (names in config, values in-memory per session, not persisted): extensions/vscode/webviews/homeView/src/stores/home.ts (in-memory secrets map, reloaded with empty values), extensions/vscode/src/views/homeView.ts
Summary
Follow-up to #830. That PR made rsconnect-python read/write Posit Publisher's
.posit/publishTOML, drive bundling from a config'sfiles, and propagateintegration_requestsintomanifest.json. What it does not do is apply theconfig's Connect-side settings — the fields that Publisher applies through the
Connect API after upload rather than through
manifest.json.Today those fields (
description,connect.runtime.*,connect.access.*,connect.kubernetes.*,environment,secrets) are round-tripped and preservedin the config, but a
rsconnect deploy/redeployfrom a Publisher-authored configsilently does not apply them to the content. This issue tracks closing that gap
in two pieces:
successful deploy (mirroring Publisher).
rsconnect synccommand that applies the current TOML settings toalready-deployed content ad hoc, without rebuilding/redeploying a bundle —
for when a user edits the config and just wants Connect updated.
Where each config field is applied (reference: Posit Publisher)
Publisher builds the manifest from the config, then makes two kinds of API calls.
Verified in the publisher repo:
extensions/vscode/src/bundler/{manifestFromConfig,connectContentFromConfig}.tsand
extensions/vscode/src/publish/{connectPublish,publishShared}.ts.type,entrypoint,python,quarto,r,jupyter,has_parametersintegration_requeststitlePATCH /v1/content/{guid}descriptionPATCH /v1/content/{guid}connect.runtime.*PATCH /v1/content/{guid}connect.access.*PATCH /v1/content/{guid}connect.kubernetes.*PATCH /v1/content/{guid}environment(env-var map)setEnvVars)secretssetEnvVars, merged; secrets override)Fields to apply via
PATCH /v1/content/{guid}Built by Publisher's
connectContentFromConfig:title,description(Connect rejectsdescription> 4096 chars)connect.runtime.*):connection_timeout,read_timeout,init_timeout,idle_timeout,max_processes,min_processes,max_conns_per_process,load_factorconnect.access.*):run_as,run_as_current_userconnect.kubernetes.*):memory_request,memory_limit,cpu_request,cpu_limit,amd_gpu_limit,nvidia_gpu_limit,service_account_name,default_image_name,default_r_environment_management,default_py_environment_managementFields to apply via the env-var API
environment(non-secret name→value map) is read straight from the configand set on the content.
secrets— follow Publisher's model exactly. The config'ssecretsarrayholds secret names only; values are never stored in the TOML or anywhere
on disk. Publisher keeps values in-memory in its webview for the session, does
not persist them (only credentials/Snowflake tokens use VS Code SecretStorage),
and re-prompts (names reload with empty values) on the next session. At publish
it sends only the names the user actually filled in, merges them over
environmentviamergeEnvVars(secret values win on key collision), sets themon the content flagged as secret (encrypted/masked), and omits any
declared name with no value (it never sends empty values).
CLI analogue (the faithful equivalent of Publisher's per-session entry): at
deploy/sync time, source each declared secret's value ad hoc — from the
process environment (
$NAME) and/or explicit--secret NAME=VALUEflags — neverfrom a file. Merge over
environment(secret wins), set flagged as secret, andskip any declared name with no supplied value (warn, don't fail). rsconnect
already has an env-var code path (
--environment/ executorenv_vars) to reusefor the actual set call.
Part 1 — Post-deploy hook
Goal: after a successful Connect/SPCS deploy that has an applicable
.posit/publishconfig, apply that config's Connect-side settings to the deployedcontent.
Design sketch:
RSConnectClientmethod for the content-settings PATCH(
PATCH v1/content/{guid}) if one does not already exist, plus reuse the existingenv-var setter.
rsconnect/publisher/that maps aPublisherConfigto thecontent-settings payload (the rsconnect analogue of
connectContentFromConfig)and to the merged env-var map (analogue of
mergeEnvVars). Readconnect.*,environment,secrets,descriptionfrom the config (they currently live inPublisherConfig.extra; consider promoting to managed fields).RSConnectExecutorafterwait_for_taskcompletes, gated toConnect/SPCS (same gate as
_save_publisher_metadata), best-effort (warn,don't fail the deploy — the bundle already deployed).
Validation / edge cases (mirror Publisher's preflight):
staticcontent → skip/warn.min_processes <= max_processes; clamp/validate*_request <= *_limit;respect server
scheduler.*_limitmaxima where discoverable.run_as/service_account_namerequire admin;run_as_current_userandKubernetes require server license/config — surface a clear warning when the
server rejects, rather than aborting.
secrets: follow Publisher's model (see the env-var section above) — names livein the TOML, values are sourced ad hoc at run time (
$NAME/--secret NAME=VALUE), never persisted; unset names are skipped, not sent empty; suppliedvalues are set flagged as secret and override
environmenton key collision.Part 2 —
rsconnect synccommandGoal: apply the TOML config's Connect-side settings to already-deployed content
ad hoc, without rebuilding or redeploying a bundle. This is the "I edited the
config, push the settings to Connect" path.
Behavior:
rsconnect sync [PATH]—PATHdefaults to the current directory, likeredeploy..posit/publishconfig (single,--config-name, orentrypoint match) and the target content GUID from the matching deployment
record.
--server/--name/--api-key; when none aregiven, default to the server in the config / deployment record, resolving a
saved rsconnect-python credential by normalized URL (reuse
_find_saved_server_by_url, asredeploydoes).no bundle build, no upload, no new deployment record (though it may refresh
the record's timestamp/settings snapshot; TBD).
destination was supplied.
Shared code: Part 1 and Part 2 must call the same
config→(content-settings + env-vars)→API applier so behavior can't drift.
Acceptance criteria
description,connect.runtime.*,connect.access.*, andconnect.kubernetes.*are applied to the content on deploy/redeploy(Connect/SPCS only), best-effort.
environmentis applied as env vars on deploy/redeploy.secretsfollow Publisher's model: names only in the TOML; values sourced adhoc (
$NAME/--secret NAME=VALUE), never persisted; unset names skipped;supplied values set flagged as secret and overriding
environmentoncollision.
rsconnect sync [PATH]applies the same settings ad hoc to already-deployedcontent, with no bundle rebuild.
synctargets the config/record server by default and honors--server/--name/--api-keyoverrides.without corrupting the deploy.
sync(including default vs. explicit server targeting).
References
extensions/vscode/src/bundler/connectContentFromConfig.tsextensions/vscode/src/publish/publishShared.ts(mergeEnvVars)extensions/vscode/webviews/homeView/src/stores/home.ts(in-memorysecretsmap, reloaded with empty values),extensions/vscode/src/views/homeView.tsextensions/vscode/src/publish/connectPublish.tsrsconnect/bundle.py(overlay_manifest),rsconnect/publisher/store.py(config_manifest_overlay)