Skip to content

feat: migrate run commands to @heroku/sdk#3819

Merged
k80bowman merged 6 commits into
v12.0.0from
k80/run-sdk
Jul 21, 2026
Merged

feat: migrate run commands to @heroku/sdk#3819
k80bowman merged 6 commits into
v12.0.0from
k80/run-sdk

Conversation

@k80bowman

Copy link
Copy Markdown
Contributor

Summary

Migrates the run, run:detached, and run:inside command family off raw this.heroku API calls and onto @heroku/sdk.

Specifically:

  1. Replaces the pre-flight account check with account.info() and one-off/exec-inside dyno creation with platform.dyno.run.
  2. Drops the CLI's manual 409 release-conflict retry loop — the SDK now owns that behavior. A test locks in that platform.dyno.run is called exactly once when it rejects with 409, so the pre-SDK retry behavior cannot silently return.
  3. Constructs one HerokuSDK in run/index.ts and passes it to Dyno via opts.sdk so account.info() and dyno.run share the same instance instead of instantiating the SDK twice per invocation.
  4. Switches 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 drops the leftover as APIDyno casts.
  5. Adopts the shared mockSDKPlatform helper across the run test suites and switches assertions to const {error} = await runCommand(...).

Note: the run unit 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)

  • Add a ! after your change type to denote a change that breaks current behavior

Feature Additions (minor semver update)

  • feat: Introduces a new feature to the codebase

Patch Updates (patch semver update)

  • fix: Bug fix
  • deps: Dependency upgrade
  • revert: Revert a previous commit
  • chore: Change that does not affect production code
  • refactor: Refactoring existing code without changing behavior
  • test: Add/update/remove tests

Testing

Notes:

  • Requires an app you can run one-off dynos against.
  • The observable behavior (streaming output, exit codes, detached/inside modes) should be unchanged; verify no regressions vs. the pre-SDK path.
  • The run:inside command requires a fir app with a dyno.

Steps:

  1. heroku run -a <app> -- echo hello — confirm the command runs, streams output, and exits cleanly.
  2. heroku run:detached -a <app> -- sleep 5 — confirm a detached dyno is created and the CLI returns immediately with the dyno name.
  3. heroku run:inside <dyno-name> -a <fir-app-with-dyno> -- ls — confirm exec-inside connects to a running dyno and streams output.
    • use heroku ps -a <fir-app-with-dyno> to get the dyno name

Screenshots (if applicable)

Related Issues

GUS work item: W-23374053

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.
@k80bowman
k80bowman requested a review from a team as a code owner July 21, 2026 16:48
@k80bowman k80bowman changed the title K80/run sdk feat: migrate run commands to @heroku/sdk Jul 21, 2026

@jdodson jdodson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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."

@k80bowman

Copy link
Copy Markdown
Contributor Author

I believe I have accounted for any special issues with run:inside in the SDK. I'm assuming the note is referring to the fact that the original CLI command uses version 3.run-inside of the API while the SDK is using 3.sdk. I did some testing and investigation and found that 3.sdk includes the functionality that we were previously getting from 3.run-inside, so there is no longer a need to use that API variant.

@k80bowman
k80bowman merged commit b3bf021 into v12.0.0 Jul 21, 2026
19 checks passed
@k80bowman
k80bowman deleted the k80/run-sdk branch July 21, 2026 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants