posit-dev / posit-dev/rsconnect-python
Apply .posit/publish config Connect-side settings: post-deploy hook + `rsconnect sync`
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 37
- Forks
- 28
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 7
Description
Summary
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
still does not apply them to the content. As of #830, redeploy warns when a
resolved config sets any of these, so the gap is no longer silent — but the
settings themselves still aren't pushed to Connect. 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 synccommand 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.
| Config field | Channel | Status in rsconnect after #830 |
|---|---|---|
type, entrypoint, python, quarto, r, jupyter, has_parameters |
manifest.json | ✅ (from inspection) |
integration_requests |
manifest.json | ✅ propagated in #830 |
title |
PATCH /v1/content/{guid} |
✅ (already applied via executor) |
description |
PATCH /v1/content/{guid} |
❌ not applied |
connect.runtime.* |
PATCH /v1/content/{guid} |
❌ not applied |
connect.access.* |
PATCH /v1/content/{guid} |
❌ not applied |
connect.kubernetes.* |
PATCH /v1/content/{guid} |
❌ not applied |
environment (env-var map) |
env-var API (setEnvVars) |
⚠️ CLI path exists, not sourced from config |
secrets |
env-var API (setEnvVars, merged; secrets override) |
❌ not applied |
Fields to apply via PATCH /v1/content/{guid}
Built by Publisher's connectContentFromConfig:
- metadata:
title,description(Connect rejectsdescription> 4096 chars) - runtime (
connect.runtime.*):connection_timeout,read_timeout,
init_timeout,idle_timeout,max_processes,min_processes,
max_conns_per_process,load_factor - access (
connect.access.*):run_as,run_as_current_user - kubernetes (
connect.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_management
Fields to apply via the env-var API
-
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'ssecretsarray
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
environmentviamergeEnvVars(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=VALUEflags — never
from a file. Merge overenvironment(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/ executorenv_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
RSConnectClientmethod 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 aPublisherConfigto the
content-settings payload (the rsconnect analogue ofconnectContentFromConfig)
and to the merged env-var map (analogue ofmergeEnvVars). Readconnect.*,
environment,secrets,descriptionfrom the config (they currently live in
PublisherConfig.extra; consider promoting to managed fields). - Call it from
RSConnectExecutorafterwait_for_taskcompletes, gated to
Connect/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):
- Runtime settings are invalid on
staticcontent → skip/warn. - Validate
min_processes <= max_processes; clamp/validate*_request <= *_limit;
respect serverscheduler.*_limitmaxima where discoverable. run_as/service_account_namerequire admin;run_as_current_userand
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 overrideenvironmenton 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]—PATHdefaults to the current directory, like
redeploy.- Resolves the applicable
.posit/publishconfig (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, asredeploydoes). - 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
environmentis applied as env vars on deploy/redeploy. -
secretsfollow 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 overridingenvironmenton
collision. -
rsconnect sync [PATH]applies the same settings ad hoc to already-deployed
content, with no bundle rebuild. -
synctargets the config/record server by default and honors
--server/--name/--api-keyoverrides. - 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). - CHANGELOG updated.
References
- PR: #830
- Publisher config→content-settings:
extensions/vscode/src/bundler/connectContentFromConfig.ts - Publisher env-var merge:
extensions/vscode/src/publish/publishShared.ts(mergeEnvVars) - Publisher secrets model (names in config, values in-memory per session, not persisted):
extensions/vscode/webviews/homeView/src/stores/home.ts(in-memorysecretsmap, reloaded with empty values),extensions/vscode/src/views/homeView.ts - Publisher publish sequence + preflight validation:
extensions/vscode/src/publish/connectPublish.ts - Already-implemented manifest side (integration_requests):
rsconnect/bundle.py(overlay_manifest),rsconnect/publisher/store.py(config_manifest_overlay)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with RSConnectExecutor, the RSConnectClient content API, and the existing env-var path, then inspect rsconnect/publisher/ and the deployment-record logic used by redeploy. Compare the referenced Publisher implementations, especially connectContentFromConfig.ts and mergeEnvVars, before designing the shared config applier and sync entry point. Done means deploy and sync apply the documented settings with tests for mapping, hooks, server targeting, validation warnings, and no bundle rebuild.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100