feat: migrate run commands to @heroku/sdk#3819
Conversation
Replace the raw `this.heroku` calls in the run, run:detached, and run:inside command family with the platform SDK: `account.info()` for the pre-flight check in `run` and `platform.dyno.run` for one-off and exec-inside dyno creation. The manual 409-retry loop is dropped since the SDK layer handles it. The `run` unit test no longer exercises the 2FA prompt path because the SDK stub sits above the credentials layer; a note flags the need for integration coverage.
Construct one HerokuSDK in run/index.ts and pass it to Dyno via opts.sdk so account.info() and dyno.run share the same instance instead of instantiating the SDK twice per invocation. Also switch this.dyno from @heroku-cli/schema's APIDyno to @heroku/types/3.sdk's Dyno (typed as Partial<SDKDyno> to accommodate callers that assign partial shapes) and drop the `as APIDyno` casts left over from the migration.
Replace ad-hoc HerokuSDK.prototype stubs with the shared
mockSDKPlatform helper (matches the pattern used across the migrated
command test suites) and switch dead .catch() assertions to
`const {error} = await runCommand(...)` since runCommand catches
internally and returns { error }.
Guards against a regression to the pre-SDK behavior where the CLI retried on 409. The SDK now owns the release-conflict retry loop; this test locks in that runStub.calledOnce holds when platform.dyno.run rejects with a 409.
jdodson
left a comment
There was a problem hiding this comment.
Approved. With a note below.
- Looked over with my eyes and looks good.
- Hand tested with the steps you had, looks good.
- Code review with Claude & ChatGPT. ChatGPT had the below that may be a consideration to fix.
"run:inside is a special path where the SDK creates a raw HerokuApiClient for POST /apps/{app}/dynos/{dyno}. Unless we pass the CLI client options through, that client uses SDK defaults instead of the CLI’s configured API host/TLS settings from this.heroku. That can regress staging, acceptance, or custom API-host environments where the old this.heroku.post(...) call worked because it inherited those settings automatically."
|
I believe I have accounted for any special issues with |
Summary
Migrates the
run,run:detached, andrun:insidecommand family off rawthis.herokuAPI calls and onto@heroku/sdk.Specifically:
account.info()and one-off/exec-inside dyno creation withplatform.dyno.run.platform.dyno.runis called exactly once when it rejects with 409, so the pre-SDK retry behavior cannot silently return.HerokuSDKinrun/index.tsand passes it toDynoviaopts.sdksoaccount.info()anddyno.runshare the same instance instead of instantiating the SDK twice per invocation.this.dynofrom@heroku-cli/schema'sAPIDynoto@heroku/types/3.sdk'sDyno(typed asPartial<SDKDyno>to accommodate callers that assign partial shapes) and drops the leftoveras APIDynocasts.mockSDKPlatformhelper across theruntest suites and switches assertions toconst {error} = await runCommand(...).Note: the
rununit test no longer exercises the 2FA prompt path because the SDK stub sits above the credentials layer. Integration coverage is needed to preserve that scenario.Type of Change
Breaking Changes (major semver update)
!after your change type to denote a change that breaks current behaviorFeature Additions (minor semver update)
Patch Updates (patch semver update)
Testing
Notes:
run:insidecommand requires a fir app with a dyno.Steps:
heroku run -a <app> -- echo hello— confirm the command runs, streams output, and exits cleanly.heroku run:detached -a <app> -- sleep 5— confirm a detached dyno is created and the CLI returns immediately with the dyno name.heroku run:inside <dyno-name> -a <fir-app-with-dyno> -- ls— confirm exec-inside connects to a running dyno and streams output.heroku ps -a <fir-app-with-dyno>to get the dyno nameScreenshots (if applicable)
Related Issues
GUS work item: W-23374053