This document describes how to create a new DSCode release, how the automated CI/CD pipeline works, and how to configure the OIDC-based publishing integrations.
- Overview
- Making a Release
- Release Pipeline
- OIDC & Trusted Publishing Setup
- Homebrew Tap
- Debugging Failed Releases
- Emergency Procedures
DSCode uses a tag-triggered release pipeline:
- A maintainer runs
./scripts/release.sh <version>locally. - The script bumps versions, runs checks, builds, commits, tags, and pushes.
- GitHub Actions take over to build platform-specific installers, publish to registries, and update the Homebrew cask.
All publishing to crates.io, PyPI, and npm uses OIDC-based trusted publishing where possible, eliminating long-lived API tokens from repository secrets.
Important: Two new packages —
dscode-appon npm anddscode-appon PyPI — are binary wrappers that download the platform-native DSCode application from GitHub Releases. These are distinct from the internal library packages (dscode-extension-host,@dscode/monaco-wasm,dscode-core).
- You have push access to
mainondipankar/dscode. - Your working tree is clean.
- You are on the
mainbranch. - All CI checks on
mainare green.
# Preview what will happen (does not execute)
./scripts/release.sh --dry-run 0.3.0
# Actually release
./scripts/release.sh 0.3.0The script will:
- Verify the working tree is clean.
- Bump versions in:
Cargo.toml(workspace + path deps)src-tauri/Cargo.tomlandtauri.conf.jsonpackage.json(root + extension-host + monaco-wasm + packages/npm)crates/dscode-core/python/pyproject.tomlpackages/pypi/pyproject.toml- Lock files (
package-lock.json)
- Run frontend checks (
npm run check). - Run Rust tests (
cargo test --workspace). - Build the extension host.
- Download Node.js binaries for all platforms.
- Build the frontend and Tauri app for the current platform (smoke test).
- Commit the version bump.
- Create and push an annotated tag (
v0.3.0).
After the tag is pushed, GitHub Actions builds the actual release artifacts for all platforms.
The following workflows run automatically after the tag is pushed:
| Workflow | Trigger | What it does |
|---|---|---|
release.yml |
Push tag v* |
Builds Tauri installers for macOS (Intel + Apple Silicon), Linux (x86_64 + aarch64), and Windows (x86_64). Creates portable archives (.tar.gz, .zip, .AppImage) for wrapper packages. Creates a GitHub Release with artifacts. Generates build attestations via OIDC. |
publish-crates.yml |
Release published |
Publishes Rust library crates to crates.io in dependency order using OIDC Trusted Publishing. |
publish-python.yml |
Release published |
Builds dscode-core wheels (Rust Python bindings). Publishes dscode-app binary wrapper to PyPI using OIDC Trusted Publishing. |
publish-npm.yml |
Release published |
Publishes dscode-extension-host and @dscode/monaco-wasm library packages. Publishes dscode-app binary wrapper to npm with provenance attestations. |
homebrew.yml |
Release published |
Bumps the dscode cask in dipankar/homebrew-tap. |
| Platform | Formats |
|---|---|
| macOS Apple Silicon | .dmg, .tar.gz (portable) |
| macOS Intel | .dmg, .tar.gz (portable) |
| Linux x86_64 | .deb, .AppImage |
| Linux aarch64 | .deb, .AppImage |
| Windows x86_64 | .msi, .exe, .zip (portable) |
The release.yml workflow uses actions/attest-build-provenance to cryptographically sign every installer using GitHub's OIDC identity. Users can verify an artifact with:
gh attestation verify path/to/DSCode.dmg --owner dipankarcrates.io supports Trusted Publishing (RFC #3691). Instead of a long-lived CARGO_REGISTRY_TOKEN, GitHub Actions exchanges a short-lived OIDC token for a 30-minute publish token.
One-time setup (per crate):
- Publish the crate manually once using a classic API token (required because Trusted Publishing can only be configured for crates that already exist).
- Go to
https://crates.io/crates/<crate-name>/settings→ Trusted Publishing. - Click Add → select GitHub.
- Enter:
- Repository owner:
dipankar - Repository name:
dscode - Workflow:
publish-crates.yml - Environment:
release(optional, but recommended)
- Repository owner:
- Save.
Repeat for each crate:
dscode-coredscode-lspdscode-dapdscode-extension-hostdscode-terminaldscode-session
Tip: One Trusted Publishing configuration on crates.io can cover multiple crates in the same repository. You only need to add each crate once.
After setup, published crates will display a "VIA GITHUB" badge on crates.io, linking to the workflow run that published them.
PyPI supports Trusted Publishing (OIDC). No API tokens are stored in GitHub secrets.
One-time setup for dscode-core (Python bindings):
- Go to
https://pypi.org/manage/project/dscode-core/settings/publishing/. - Click Add.
- Enter:
- Publisher:
GitHub - Owner:
dipankar - Repository name:
dscode - Workflow name:
publish-python.yml - Environment name:
release(optional)
- Publisher:
- Save.
One-time setup for dscode-app (binary wrapper):
- Go to
https://pypi.org/manage/project/dscode-app/settings/publishing/. - Click Add.
- Enter:
- Publisher:
GitHub - Owner:
dipankar - Repository name:
dscode - Workflow name:
publish-python.yml - Environment name:
release(optional)
- Publisher:
- Save.
Initial manual publish:
For PyPI Trusted Publishing, the project does not need to exist first — you can configure the trusted publisher before the first release. However, if you prefer to push the initial release manually:
cd packages/pypi
pip install build twine
python -m build
twine upload dist/*After the first release, all subsequent releases will use short-lived OIDC tokens automatically.
npm does not support fully tokenless OIDC publishing. However, npm provenance uses OIDC to cryptographically link a published package to the GitHub Actions workflow that built it.
Current setup:
- The
publish-npm.ymlworkflow passes--provenancetonpm publish. - The workflow has
id-token: writepermission, enabling the OIDC attestation. - Authentication still requires an automation token (
NPM_AUTOMATION_TOKENsecret) scoped to:dscode-extension-host@dscode/monaco-wasmdscode-app(binary wrapper)
Initial manual publish for dscode-app:
If this is the first time publishing the dscode-app binary wrapper package:
cd packages/npm
npm publish --access publicSubsequent releases will be handled automatically by publish-npm.yml.
Published packages will show a "Provenance" section on npmjs.com with a link to the GitHub workflow run.
The homebrew.yml workflow automatically bumps the dscode cask in dipankar/homebrew-tap after each release.
Prerequisites:
- The cask file
Casks/dscode.rbmust exist indipankar/homebrew-tap. - A GitHub secret
HOMEBREW_TAP_TOKENmust be configured with:contents:writepull_requests:writeondipankar/homebrew-tap.
If the cask does not exist yet, create it manually first:
brew tap-new dipankar/homebrew-tap
cd $(brew --repo dipankar/homebrew-tap)
# Create Casks/dscode.rb manuallygh run list --workflow=release.yml
gh run watch <run-id>| Symptom | Likely cause | Fix |
|---|---|---|
release.yml fails on macOS |
Codesigning issue | Ensure CODESIGN_IDENTITY='-' is set (ad-hoc signing). For real signing, add Apple Developer secrets. |
release.yml fails on Linux aarch64 |
Missing Tauri ARM64 deps | Native ARM64 runners should have all deps. Check libwebkit2gtk-4.1-dev is installed. |
publish-crates.yml fails with 403 |
crates.io Trusted Publishing not configured | Go to each crate's Settings page on crates.io and add the trusted publisher. |
publish-python.yml fails with 403 |
PyPI Trusted Publishing not configured | Add the trusted publisher at pypi.org. |
publish-npm.yml fails with ENEEDAUTH |
NPM_AUTOMATION_TOKEN is missing or expired |
Rotate the npm automation token in repo secrets. |
homebrew.yml fails |
HOMEBREW_TAP_TOKEN missing or cask doesn't exist |
Create the cask manually and verify the PAT has the right scopes. |
| crates.io index lag | Published leaf crate not visible yet | The publish-dependent job waits 60s, but crates.io index propagation can take longer. Re-run the failed job. |
All publish workflows support workflow_dispatch. You can re-run a specific job from the GitHub UI or CLI:
gh workflow run publish-crates.yml
gh workflow run publish-python.yml
gh workflow run publish-npm.yml- Yank the crates.io version (if already published):
cargo yank -p dscode-core --version 0.3.0
- Delete the GitHub Release from the Releases page.
- Delete the git tag:
git push origin :refs/tags/v0.3.0
- Unpublish from npm (within 72 hours):
npm unpublish dscode-extension-host@0.3.0 npm unpublish @dscode/monaco-wasm@0.3.0 npm unpublish dscode-app@0.3.0
- Yank the PyPI release:
pip install pypi-cleanup pypi-cleanup -u <username> -p dscode-core -r 0.3.0 pypi-cleanup -u <username> -p dscode-app -r 0.3.0
| Secret | How to rotate |
|---|---|
NPM_AUTOMATION_TOKEN |
Generate a new automation token at npmjs.com → Access Tokens. Update in repo Settings → Secrets. |
HOMEBREW_TAP_TOKEN |
Generate a new PAT at github.com/settings/tokens with repo scope for dipankar/homebrew-tap. |
If crates.io Trusted Publishing ever fails, the old CARGO_REGISTRY_TOKEN secret can be used as a fallback by temporarily reverting publish-crates.yml to use it directly.