feat(deployment): bring container-vm ssh deployments to the configure page - #3443
Conversation
… page Gated on onboarding_redesign_v1, the vm=true intent seeds an SSH-ready VM service (real distro image, managed port-22 tcp expose, single instance, forced SSH key) on the configure page. Per-service card behavior keys off image detection so drafts and imported VM SDLs restore VM mode for free. /deploy-linux and the Launch Container-VM card route to the new flow while the flag is on; the classic builder is untouched with the flag off.
📝 WalkthroughWalkthroughContainer-VM deployment support is added across intent parsing, URL routing, SDL initialization and validation, VM image selection, SSH and port controls, and the Linux deployment entry point. Tests cover VM-specific state, rendering, routing, and reserved SSH port behavior. ChangesContainer VM deployment flow
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3443 +/- ##
==========================================
- Coverage 72.60% 71.83% -0.78%
==========================================
Files 1167 1080 -87
Lines 29527 27319 -2208
Branches 7312 6903 -409
==========================================
- Hits 21439 19625 -1814
+ Misses 7109 6755 -354
+ Partials 979 939 -40
*This pull request uses carry forward flags. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.tsx (1)
77-110: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winForce the replica value to one when a service becomes a VM.
Disabling the stepper preserves an existing value such as
count: 3when an imported service or Docker service changes to a managed VM image. Normalizecountto1, rather than merely locking the current value.Proposed fix
const isVm = isVmImage(image ?? ""); +useEffect(() => { + if (isVm && count.field.value !== 1) { + count.field.onChange(1); + void trigger([ + `services.${serviceIndex}.profile.cpu`, + `services.${serviceIndex}.profile.ram`, + `services.${serviceIndex}.profile.gpu` + ]); + } +}, [isVm, count.field, trigger, serviceIndex]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.tsx` around lines 77 - 110, Update ReplicasField to normalize the service replica count to 1 whenever isVm becomes true, including imported or existing values greater than 1. Use the form field update mechanism for count and preserve the disabled stepper and existing validation behavior.apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ExposePortsCard/ExposePortsCard.tsx (1)
238-260: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winIdentify the managed SSH row by container port, not external port.
Line 245 checks
field.as === 22. For an imported VM mappingport: 22, as: 2222, the SSH row remains removable; conversely,port: 80, as: 22is incorrectly reserved. Matchfield.port === 22and add both cases to the tests.Proposed fix
- const managedSshIndex = isVmImage(image ?? "") ? fields.findIndex(field => field.as === 22) : -1; + const managedSshIndex = isVmImage(image ?? "") ? fields.findIndex(field => field.port === 22) : -1;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ExposePortsCard/ExposePortsCard.tsx` around lines 238 - 260, Update managed SSH detection in the ExposePortsCard logic to match the container port by changing the fields.findIndex predicate from field.as === 22 to field.port === 22. Extend the relevant tests to cover both imported mappings with port 22 and a different external port, and mappings with external port 22 but a non-SSH container port.
🧹 Nitpick comments (1)
apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.spec.tsx (1)
159-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPinning test doesn't prove pinning.
The test never passes an initial
count, so it defaults to1regardless of any VM-specific "pin to 1" logic. Passingcount: 3(or similar) would actually verify the stepper forces the value down to 1 for VM services, rather than just reflecting an already-1 default.- setup({ image: "ghcr.io/akash-network/ubuntu-2404-ssh:2" }); + setup({ image: "ghcr.io/akash-network/ubuntu-2404-ssh:2", count: 3 });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.spec.tsx` around lines 159 - 166, Update the “pins the replica stepper at a single disabled instance for a vm service” test to call setup with an initial replica count greater than 1, such as count: 3. Keep the existing assertions verifying the disabled controls, resulting value of 1, and VM-specific message so the test proves VM pinning rather than the default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/cardTooltips.tsx`:
- Around line 103-110: Update the operatingSystemTooltip text to add “on” at the
end of the sentence, so it reads as “The Linux distribution this VM-like
container runs on.”
In
`@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.tsx`:
- Around line 89-90: Update the ConfigureDeployment initialization logic so VM
entries cannot resolve or use templates: guard templateId-derived values such as
hardcodedTemplate and fetchedTemplateId, and ensure initialSdl ignores draft or
query template content when intent.vm is true. Preserve the existing template
initialization behavior for non-VM entries.
In `@apps/deploy-web/src/types/sdlBuilder/sdlBuilder.ts`:
- Around line 554-573: The reserved-port validation in the service expose loop
should target the field containing the invalid value: use the `port` path when
`expose[j].port === 22` and the `as` path when `expose[j].as === 22`. Update the
corresponding `sdlBuilder.spec.ts` expectation for the `as === 22` case to
assert the corrected path.
---
Outside diff comments:
In
`@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ExposePortsCard/ExposePortsCard.tsx`:
- Around line 238-260: Update managed SSH detection in the ExposePortsCard logic
to match the container port by changing the fields.findIndex predicate from
field.as === 22 to field.port === 22. Extend the relevant tests to cover both
imported mappings with port 22 and a different external port, and mappings with
external port 22 but a non-SSH container port.
In
`@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.tsx`:
- Around line 77-110: Update ReplicasField to normalize the service replica
count to 1 whenever isVm becomes true, including imported or existing values
greater than 1. Use the form field update mechanism for count and preserve the
disabled stepper and existing validation behavior.
---
Nitpick comments:
In
`@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.spec.tsx`:
- Around line 159-166: Update the “pins the replica stepper at a single disabled
instance for a vm service” test to call setup with an initial replica count
greater than 1, such as count: 3. Keep the existing assertions verifying the
disabled controls, resulting value of 1, and VM-specific message so the test
proves VM pinning rather than the default.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ab9b1242-f68d-48d8-b326-7e3455716648
📒 Files selected for processing (30)
apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/AdditionalSection/AdditionalSection.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/AdditionalSection/AdditionalSection.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ExposePortsCard/ExposePortsCard.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ExposePortsCard/ExposePortsCard.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ImageCard/ImageCard.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ImageCard/ImageCard.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/cardTooltips.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/DeploymentFlowProvider/DeploymentFlowProvider.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ResumeDeploymentGuard/ResumeDeploymentGuard.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/useConfigureDraft/useConfigureDraft.spec.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/deploymentIntent.spec.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/deploymentIntent.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.spec.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.tsapps/deploy-web/src/components/new-deployment/RedirectDeployLinuxToConfigure/RedirectDeployLinuxToConfigure.spec.tsxapps/deploy-web/src/components/new-deployment/RedirectDeployLinuxToConfigure/RedirectDeployLinuxToConfigure.tsxapps/deploy-web/src/hooks/useNewDeploymentUrl/useNewDeploymentUrl.spec.tsapps/deploy-web/src/hooks/useNewDeploymentUrl/useNewDeploymentUrl.tsapps/deploy-web/src/pages/deploy-linux/index.tsxapps/deploy-web/src/types/sdlBuilder/sdlBuilder.spec.tsapps/deploy-web/src/types/sdlBuilder/sdlBuilder.tsapps/deploy-web/src/utils/sdl/data.tsapps/deploy-web/src/utils/sdl/vmImages.tsapps/deploy-web/src/utils/urlUtils.ts
- resolve the template from the parsed intent so vm=true cannot be bypassed by a templateId in the raw URL - identify the managed SSH row by the exact 22-to-22 pair in both the expose card and the schema, so a squatting row gets the error instead of the read-only lock - require the ssh key on vm services at the schema level even when the deployment-wide flag is off (a pasted vm ref with the runtime card collapsed could previously submit an unreachable vm) - force the ssh flag from the runtime card itself (the collapsed body unmounts), open the card by default for vm services, and mark its header when a submit is rejected on its hidden fields
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/deploy-web/src/types/sdlBuilder/sdlBuilder.ts`:
- Around line 574-582: Update the SSH key validation in the services loop to
trim `sshPubKey` before checking whether it is missing, so whitespace-only
values trigger the existing fatal “SSH Public key is required.” issue. Add a
regression test covering a whitespace-only key for the VM/SSH-required
validation path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9e0e80e1-7619-4534-ad7f-c42c669288ba
📒 Files selected for processing (7)
apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ExposePortsCard/ExposePortsCard.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.tsxapps/deploy-web/src/types/sdlBuilder/sdlBuilder.spec.tsapps/deploy-web/src/types/sdlBuilder/sdlBuilder.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.spec.tsx
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.tsx
- apps/deploy-web/src/types/sdlBuilder/sdlBuilder.spec.ts
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ExposePortsCard/ExposePortsCard.tsx
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/RuntimeCard/RuntimeCard.tsx
Why
Closes CON-675
When the legacy SDL Builder flow is retired (CON-672), "Launch Container-VM" must survive. Today an SSH-accessible plain-linux VM can only be launched through the legacy
/deploy-linuxbuilder; the new Configure page cannot represent it. This PR makes the Container-VM experience first-class on Configure, gated on the existingonboarding_redesign_v1flag. With the flag off, the classic/deploy-linuxbuilder is untouched.What
Screen.Recording.2026-07-16.at.12.36.24.PM.mov
Entry and routing (flag-gated):
vm=trueintent param on/new-deployment/configure. It survives every URL rewrite (draft-id write-back, dseq mirroring, cancel-and-edit, bid-strategy switches) viabuildConfigureUrl, and atemplateIdcarried alongside it is ignored (the intent parser drops it and the screen resolves templates from the parsed intent)./deploy-linuxvisits route to/new-deployment/configure?vm=truewhen the flag is on (useNewDeploymentUrl+ newRedirectDeployLinuxToConfigure). Analytics event is unchanged.Seeding and form behavior:
vm=trueentry seeds a service with the real Ubuntu 24.04 image ref, a managedport 22/tcp/globalexpose,count: 1, andhasSSHKey: true(key required before quotes). The legacy flow injected all this at SDL-generation time viatransformCustomSdlFields; Configure generates SDL straight from form state, so it lives in the state itself.isVmImage), not the URL param, so draft resume and imported VM SDLs restore VM mode for free: the Docker card becomes an "Operating System" card with a Distribution select over the 4 managed distros (private registry hidden, credentials cleared), replicas pin to 1, "Expose SSH" is forced on while any VM service exists, and the Commands card is hidden (overriding the entrypoint would break the SSH bootstrap).SSH_VM_IMAGESmoved to a dependency-freeutils/sdl/vmImages.tsso the form schema can consume it without an import cycle;utils/sdl/data.tsre-exports for existing consumers.Known accepted tradeoffs (by design, from CON-675 planning): pasting a VM image ref into a normal service flips it into VM mode with no UI path back except deleting the service; a carried-in VM SDL that already has a
commandkeeps emitting it (just not editable); credentials on a carried-in VM service clear when that service is first selected.Out of scope (per CON-675 planning): a Playwright VM e2e ships as a follow-up under the E2E initiative.
Verification: full deploy-web unit suite (2672 passed),
lint --quiet,tsc --noEmit(0 new errors), plus a manifest smoke check confirming the seeded VM deployment produces a chain-valid manifest and round-trips through SDL import. The manual flag-on pass from the plan (deploy end-to-end, SSH instructions on the Lease tab) still needs a run in a flag-enabled environment.Summary by CodeRabbit