Skip to main content
Studio’s Deploy button calls studio.deployment.open. The bundled fused-studio/deployment service owns that command and its dialog. Replace this contribution to control deployment UI without replacing the workspace shell. Your module can define its own modal, fields, environment choices, buttons, validation, and progress display. It receives the same host context as the default deployment service. Backend authentication, feature gates, command validation, and stale-manifest checks still apply. A new deployment backend or operation requires corresponding server support; changing a button does not add one.

Declare the replacement

studio-pack.yaml
service modules receive null as their mount container. Create a dialog owned by your module, append it to the document, and remove it during cleanup. Register studio.deployment.open; the default shell’s button will invoke your handler. The resolved default deployment service is removed before activation, so it does not register a competing handler. An isolated frame cannot implement this command because it has no command bridge. This replacement requires explicit trust:
Select a project and exit Focus to access the toolbar’s Deploy button. Without pack trust, Studio retains the default deployment service. Without the server feature flag, the button is hidden and deployment APIs reject requests, including requests from trusted packs.

Use the working example

The Harnest checkout includes examples/studio-packs/team-deployment/. It contains the manifest above and a standalone module with:
  • A Team deployment dialog instead of the default modal.
  • A native environment selector with local and staging choices. Configure those environments in the project’s deployment manifest before using them.
  • Review plan, which reads the deployment overview without starting infrastructure.
  • Deploy reviewed plan, enabled only after an unblocked review.
  • A completion display driven by Studio job state.
Edit this module to change the choices, wording, fields, or button handlers. It imports no default-shell JavaScript and references no shell DOM IDs. Its only runtime asset is assets/deployment.js.

Render the modal with React

Packs are framework-independent. The plain DOM example avoids a build dependency; it is not a requirement. Use React components for your modal, forms, state, and buttons. React’s createRoot API mounts a component tree into one container, and root.unmount() cleans it up. For a visual slot, Studio passes the container as host. For a deployment service, create one container in the entry adapter, then let React own everything inside it:
src/deployment.jsx
DeploymentDialog is your component: implement focus containment, closing, environment choices, review, and job handling there. Register the command in activate, rather than waiting for a React effect, so it is available when Studio enables shell controls. Components can clean up subscriptions through their effect cleanup functions. Compile JSX and bundle react, react-dom, and your components into a production browser ES module exporting activate. Point the manifest to the generated assets/deployment.js; do not serve raw JSX, unresolved package imports, or a development server. Declare every generated JavaScript chunk in ui.assets. Put generated CSS in both ui.assets and ui.styles, scoped to your component. Studio does not supply a global React instance. Keep bundle assets within the UI size limits.

Shell commands and state

These commands are supplied by the default shell. A custom shell must implement the commands its deployment contribution uses. Subscribe to project to follow selection. Subscribe to jobs for complete job snapshots containing IDs, status, and output. Match your submitted job ID and process its completion once. Subscription and command-registration calls return cleanup functions. Stop handling pending results after cleanup, and invalidate reviews when the selected project or environment changes. The default deployment service checks selection and server capability before opening, owns an independent dialog, and cleans up its command and subscriptions. It uses the same shell commands to review generated configuration and submit deployment jobs. These interfaces do not add new frame permissions.

Review before applying

Use studio.request for authenticated requests relative to /api/:
The overview includes plan, blockers, manifest_revision, recorded deployment state, and history. It reviews saved configuration; it does not deploy. If configuration is missing, use deployment/inspect to discover inputs and deployment/propose to produce a source proposal, as the bundled service does. Only after the user selects the apply action, check unsaved edits and use the exact project, environment, and revision associated with the review:
Submission returns a job, not proof that infrastructure is ready. Observe completion and interpret its result before displaying a ready state. Preserve server-side revision checks; refresh the review after configuration changes. Never deploy during module activation or merely opening the dialog. Keep credentials on the server, outside UI assets.

Recover and verify

Test missing project selection, disabled deployment, an invalid environment, a valid review, job failure, and project changes while requests are pending. Opening or reviewing the example does not require running containers. Test actual apply only against an authorized deployment target. Use ?safe-ui=1 to restore the bundled deployment UI for comparison. Launch with --safe-ui to exclude all external packs, including builder defaults and resources. Two packs claiming fused-studio/deployment fail composition; load order cannot resolve competing replacements.