- Contributing
These instructions will give you a copy of the project up and running on your local machine for development and testing purposes.
- Just
- Node.js (LTS recommended; nvm is a good version manager)
- R with the
renvpackage — needed for R-related tests. rig is a convenient installer. After installing R, runR -e 'install.packages("renv")'. - Visual Studio Code with the recommended workspace extensions (see below)
When you open the extensions/vscode workspace, VS Code will prompt you to install recommended extensions. If you miss the prompt, install them manually:
connor4312.esbuild-problem-matchers— Required to run the extension debug launch configurations. This is the most commonly missed prerequisite.dbaeumer.vscode-eslint— ESLint integrationesbenp.prettier-vscode— Prettier formatting
To get your development environment up and running, invoke the default Just command.
justThis installs dependencies and packages the VS Code extension.
See the Posit Publisher VS Code Extension CONTRIBUTING guide next for setting up your development workflow.
The repo utilizes git hooks (through husky) to implement some standard formatting and linting.
The hooks depend on the root workspace's npm packages, which cover every workspace member (extensions/vscode, webviews/homeView, packages/*, and the TypeScript test packages). A single install at the repo root is enough:
npm installIf you also work on the e2e Cypress suite, install its deps separately — that package is deliberately outside the workspace:
npm install --prefix=test/e2ePosit Publisher is a pure TypeScript project with two major sections:
- A TypeScript Positron / VS Code extension that implements all publishing logic and communicates with Connect / Connect Cloud servers directly.
- A Vue webview that provides the sidebar UI in the IDE.
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│ Posit Connect Server │ │ `.posit/publish/` (on disk) │
│ (or Connect Cloud) │ │ │
│ │ │ `*.toml` — configurations │
│ Content management, bundle upload, │ │ How content should be deployed: │
│ deployment, task polling, │ │ type, entrypoint, packages, etc. │
│ authentication │ │ │
│ │ │ `deployments/*.toml` — records │
│ │ │ Where content was deployed and │
│ │ │ how it was configured at the │
│ │ │ time of deployment. │
│ │ │ │
│ │ │ The extension watches these files │
│ │ │ and refreshes the webview. │
└──────────────────┬──────────────────┘ └──────────────────┬──────────────────┘
│ │
HTTPS │ │ reads/writes
│ │
┌──────────────────┴────────────────────────────────────────┴──────────────────┐
│ VSCode Extension Host (TypeScript) │
│ `extensions/vscode/src/` │
│ │
│ Owns all business logic: │
│ - Project inspection (Python/R/Quarto) │
│ - Configuration & deployment record I/O │
│ - Credential storage (keyring or file) │
│ - Bundle creation (file packaging) │
│ - Publishing orchestration │
│ - All communication with Connect / Connect Cloud servers │
│ │
│ Uses the shared npm packages: │
│ - `@posit-dev/connect-api` — axios client for Posit Connect │
│ - `@posit-dev/connect-cloud-api` — client + OAuth for Connect Cloud │
└───────────────────────────┬──────────────────────────────────────────────────┘
│
postMessage │
(JSON) │
Typed messages │
serialized by │
"conduit" │
classes: │
`WebviewConduit` │
`HostConduit` │
│
┌───────────────────────────┴──────────────────────────────────────────────────┐
│ Vue Webview (Sidebar UI) │
│ `webviews/homeView/` │
│ │
│ Vue + Pinia app rendered in a VSCode webview iframe. A webview gives us │
│ full control over the UI beyond what the IDE's APIs provide. Has no │
│ direct access to Node.js, the filesystem, or the VS Code API. Uses │
│ `postMessage` from VS Code's `WebviewApi` to communicate with the │
│ extension host. │
└──────────────────────────────────────────────────────────────────────────────┘
Deployment flow (end to end):
- User clicks Deploy
- Webview sends deploy message via
postMessage - Extension host receives it, inspects the project, bundles files, and uploads to Connect
- Deployment progress events flow through the extension's in-process
EventStream - Extension forwards status to webview via
postMessage - Webview updates UI reactively via Pinia stores
This project follows the guidance written by Ham Vocke in the The Practical Test Pyramid. Please read the article for a detailed overview of different test types and how they are utilized.
The VSCode extension uses Mocha for integration tests (under extensions/vscode/src/test/) and Vitest for unit tests (src/**/*.test.ts alongside the source).
From extensions/vscode/:
just testTypeScript packages under packages/ use Vitest.
End-to-end tests use Cypress to test the Publisher extension in a real VS Code environment with a Posit Connect server. Tests run automatically in GitHub Actions CI after unit tests pass.
See the E2E Testing Guide for setup and usage instructions.
The build tooling entrypoint is just. See the installation instructions for your operating system to install Just.
Execute just -l for a list of available commands and documentation.
When executing commands the following variables are accepted to change behavior.
| Variable | Default | Type | Description |
|---|---|---|---|
| CI | false | bool | Enable CI mode. When set to true, npm ci is used instead of npm install. |
| DEBUG | false | bool | Enable DEBUG mode. When set to true, set +x is enabled for all Justfile targets. |
| MODE | dev | enum | When set to dev, development is enabled. All other values disable development mode. |
When running in GitHub Actions, the env variable CI is set to true by GitHub. When CI=true, the defaults for the following values are adjusted.
This mode can be reproduced on your local machine by setting CI=true.
| Variable | Default |
|---|---|
| MODE | prod |
Debug configurations for the extension itself live at
extensions/vscode/.vscode/launch.json.
See the extension CONTRIBUTING guide for details.
See the Contribution Guide for the VSCode Extension.
Schemas live at extensions/vscode/src/toml/schemas/.
Non-breaking or additive changes to the schema do not require a version bump. Breaking changes to the schema require a version bump.
To update the schema:
- Update the jsonschema file (
posit-publishing-schema-v3.jsonorposit-publishing-record-schema-v3.jsonfor the Configuration or Deployment schemas, respectively) - Update the corresponding example file (
config.tomlorrecord.toml). - If you're using VSCode with the Even Better TOML extension, you can use the in-editor validation by putting the full local path to your updated schema in the
$schemafield in your TOML files. - Verify that the unit tests pass. They load the example files and validate them against the schemas.
- The
draftfolder contains schemas that are a superset of the main schemas, and have ideas for the other settings we have considered adding. Usually we have added any new fields to those schemas and example files as well. - Version bump only: update in-code references to the schema URL (code that writes the TOML files, tests)
When Pull Requests that modify schema files are merged into main, a GitHub Actions workflow automatically uploads the updated schemas to S3, making them available on the CDN.
The Even Better TOML extension caches schemas. To force it to update remove the cached schemas from the extension's cache directory.
On macOS this can be done with the following command, replacing $USERNAME with
your username:
rm /Users/$USERNAME/Library/Application\ Support/Code/User/globalStorage/tamasfe.even-better-toml/*
The Posit Publisher VSCode extension releases follow guidelines from the VSCode Publishing Extensions docs.
SemVer versioning is used, but the
VSCode Marketplace only supports major.minor.patch for extension versions;
semver pre-release tags are not supported.
The recommendation is releases use major.EVEN_NUMBER.patch for release
versions and major.ODD_NUMBER.patch for pre-release versions.
CI facilitates this and will automatically publish as a pre-release if the minor version number is odd.
- Merge any "Update licenses" PRs to
main - Merge any Dependabot PRs to
main - Wait for the
main.yamlworkflows to complete
The release process is automated via GitHub Actions workflows.
- Go to Actions > Prepare Release
- Click "Run workflow"
- Enter the version number (e.g.,
1.34.0)- Must use even minor version for production releases
- Do not include the
vprefix
- Click "Run workflow"
This workflow will:
- Create a
release/v{version}branch - Run the prepare-release script to update changelog files
- Create a pull request for review
- Review the PR to verify changelog entries are complete and accurate
- Ensure all PRs with user-facing changes since the last release have changelog entries
- Merge the PR when ready
When the release PR is merged, automation takes over:
- The
tag-on-release-mergeworkflow automatically creates the version tag - The tag triggers the
releaseworkflow which:- Builds release artifacts
- Creates a GitHub release
- Publishes to VS Code Marketplace and Open VSX
- Sends a Slack notification to announce the release
Once the workflows complete, verify:
- The release appears on the Releases page
- The new version shows up in Visual Studio Marketplace and Open VSX
- Works With / Target Platforms is "Universal"
It may take some time after the workflows complete for the new version to appear in the marketplaces.
Use the Prepare Patch Release workflow when you need to ship a fix on an older release line (e.g., 2.0.1 while 2.1.x pre-releases are in flight on main).
| Scenario | Workflow |
|---|---|
New production release from main (e.g., 2.2.0) |
Prepare Release |
Patch for the current release line (e.g., 2.2.1 and main is still on 2.2.x or 2.3.x) |
Prepare Patch Release |
Hotfix for an older release line (e.g., 2.0.1 while main has moved to 2.2.x) |
Prepare Patch Release |
- Go to Actions > Prepare Patch Release
- Click "Run workflow"
- Fill in:
- version — the patch version (e.g.,
2.0.1). Must be even minor, patch > 0. - base_tag (optional) — the tag to branch from. If omitted, the workflow finds the highest
vMAJOR.MINOR.*tag automatically. - pr_numbers — comma-separated PR numbers to cherry-pick (e.g.,
4082,4087).
- version — the patch version (e.g.,
- Click "Run workflow"
The workflow will:
- Branch from the base tag
- Cherry-pick each PR's squash-merge commit
- Run
prepare-release.pywith--allow-downgrade --from-cherry-pick - Open a draft PR for review (this PR must NOT be merged — it targets main only for diff visibility)
If a cherry-pick conflict occurs, the workflow opens a draft PR with instructions for manual resolution. Resolve locally and push to the release branch.
Once the draft PR has been reviewed:
- Go to Actions > Tag Patch Release
- Click "Run workflow"
- Enter the version (e.g.,
2.0.1) - Click "Run workflow"
This will tag the release branch directly, close the review PR, and trigger the release pipeline. The release branch can be deleted after the release workflow completes.
Dependencies can be updated through two approaches:
- Automated (Preferred): Via Dependabot PRs that are automatically created
- Manual: For cases where Dependabot cannot handle the update or special handling is needed
Dependabot is GitHub's automated dependency management tool that helps keep your project's dependencies up to date. It scans your repository for dependency files and automatically creates pull requests when newer versions of your dependencies are available.
Each PR includes release notes, changelog entries, and compatibility information to help you assess the impact of the update.
After review and approval, Dependabot PRs can be merged like regular code changes.
- Configuration is maintained in
.github/dependabot.yml - Multiple ecosystems are monitored:
- npm packages (root, VSCode extension, and test directories)
- Python dependencies
- Docker images
- GitHub Actions
- Test data dependencies
Dependencies can be manually adjusted at any time; the process of updating after a release keeps us proactive.
This includes our JavaScript packages and tooling dependencies.
Any significantly difficult dependency updates should have an issue created to track the work and can be triaged alongside our other issues.
Updates to dependencies should be done in a separate PR.
The VS Code extension declares two related versions:
engines.vscodeinextensions/vscode/package.json— the minimum VS Code version users need to install the extension.@types/vscodeinextensions/vscode/package.jsonandtest/extension-contract-tests/package.json— the API surface the TypeScript code compiles against.
vsce package fails if @types/vscode is higher than engines.vscode — it refuses to ship an extension that compiles against APIs newer than the minimum runtime it claims to support. Pin @types/vscode to an exact version (so the code can't drift onto newer APIs) and use a caret on engines.vscode (so the extension installs on that VS Code version and any newer one). Use the same version number in all three places:
// test/extension-contract-tests/package.json
"devDependencies": {
"@types/vscode": "1.105.0"
}When adding code that needs a VS Code API not present in the current pinned version, bump all three places to the lowest version that exposes the API you need (check the VS Code API release notes), then run npm install. Raising the pin raises the minimum VS Code version users need, so don't bump further than required.