Skip to content

refactor: use @heroku/sdk for pipelines commands#3717

Merged
eablack merged 3 commits into
feat/heroku-sdk-integrationfrom
eb/refactor/sdk-pipelines-commands
May 20, 2026
Merged

refactor: use @heroku/sdk for pipelines commands#3717
eablack merged 3 commits into
feat/heroku-sdk-integrationfrom
eb/refactor/sdk-pipelines-commands

Conversation

@eablack
Copy link
Copy Markdown
Contributor

@eablack eablack commented May 18, 2026

Summary

  • Replace direct Platform API calls in pipelines (list), pipelines:create, pipelines:info (via disambiguate's UUID branch), pipelines:promote, pipelines:destroy, and pipelines:update with @heroku/sdk equivalents (pipeline.list/info/create/delete, pipelineCoupling.create/infoByApp/update, account.infoByUser, team.info, promotePipeline).
  • Wire pipelines:promote to promotePipeline's onReleaseStream callback. The release-command output streaming logic that lived in this file (busl fetch with retry, single-target early-exit, release info lookup) now lives in the SDK; the CLI just pipes the resulting ReadableStream<Uint8Array> to process.stdout. Drops ~80 lines of poll/stream/2FA helpers.
  • Drop the listPipelineApps wrapper from src/lib/api.ts and have its four consumers (info, diff, transfer, promote) import the new SDK composition directly. The AppWithPipelineCoupling type also moves to the SDK; render-pipeline.ts widens it locally with Record<string, unknown> to satisfy hux.table's row constraint. Removes the now-unused getAppFilter/listCouplings helpers and FILTERS_HEADER constant from lib/api.ts.
  • Add a local PipelineCreateBody type that extends @heroku/types' PipelineCreateOpts with the undocumented generation field the API accepts but the heroku/api schema doesn't declare. Comment in source explains why.
  • Expose Promote.promotePipeline as a static reference so tests can stub the SDK call without changing production code (mirrors the prior Cmd.sleep convention).
  • Add tmp/**/* to the eslint ignore list (build artifacts were producing 138k unrelated lint errors).

Type of Change

  • refactor: Refactoring existing code without changing behavior

Testing

Notes: Smoke-tested against real apps in the heroku-dev-tools team using a locally built CLI. Both promotion paths exercised: standard polling and the single-target release-command streaming path (the more complex behavior the SDK's onReleaseStream callback now drives). update and destroy exercised against the same pipeline.

Steps:

  1. npm run build to compile the refactored CLI.
  2. Create two staging/prod apps:
    heroku apps:create eb-sdk-test-staging --team heroku-dev-tools
    heroku apps:create eb-sdk-test-prod --team heroku-dev-tools
    
  3. Build the pipeline using the refactored commands:
    ./bin/run pipelines:create eb-sdk-test-pipeline -a eb-sdk-test-staging -s staging -t heroku-dev-tools
    ./bin/run pipelines:add eb-sdk-test-pipeline -a eb-sdk-test-prod -s production
    ./bin/run pipelines:info eb-sdk-test-pipeline   # confirms the assembled pipeline (renders apps via SDK listPipelineApps)
    ./bin/run pipelines                              # confirms list output
    
  4. Verify pipelines:update re-stages an app:
    ./bin/run pipelines:update -a eb-sdk-test-staging -s production
    ./bin/run pipelines:info eb-sdk-test-pipeline   # confirms eb-sdk-test-staging now shows in production
    
  5. Seed the staging app with a release. POST /sources for a signed S3 pair, upload a small tarball containing a Procfile with both web: and release: processes (release script prints lines over ~10s so the streaming path is observable), then POST /apps/eb-sdk-test-staging/builds with the get_url. Wait for status succeeded.
  6. Promote and observe the release-command output stream live to stdout:
    ./bin/run pipelines:promote -a eb-sdk-test-staging
    
    Expected output: Running release command... followed by the streamed step 1 / 10 ... step 10 / 10 lines emitted in real time, then Promotion successful and eb-sdk-test-prod: succeeded. This exercises HerokuApiClient.stream() → SDK onReleaseStreamstream.pipeTo(WritableStream)process.stdout.
  7. Verify pipelines:destroy removes the pipeline:
    ./bin/run pipelines:destroy eb-sdk-test-pipeline
    ./bin/run pipelines                              # the pipeline is gone
    
  8. Cleanup: heroku apps:destroy for both apps.

Related Issues

GUS work item: W-22265744

Replace direct Platform API calls in pipelines (list), pipelines:create,
pipelines:info (via disambiguate), and pipelines:promote with
@heroku/sdk equivalents:
- pipeline.list() / pipeline.info() / pipeline.create()
- pipelineCoupling.create()
- account.infoByUser() / team.info()
- promotePipeline() composition with onReleaseStream callback

The release-command output streaming previously implemented inline in
promote.ts now flows through the SDK's onReleaseStream hook, which
hands a web ReadableStream to the CLI to pipe to stdout. The local
poll/stream/2FA helpers are removed.

Promote.promotePipeline is exposed as a static reference so tests can
stub the SDK call (matches the prior Cmd.sleep convention).

Adds a local PipelineCreateBody type that extends @heroku/types'
PipelineCreateOpts with the undocumented `generation` request field
the platform accepts but the schema doesn't declare.

Adds tmp/ to the eslint ignore list.
@eablack eablack requested a review from a team as a code owner May 18, 2026 23:25
…tion

- pipelines:destroy now calls SDK pipeline.delete().
- pipelines:update now calls SDK pipelineCoupling.infoByApp() +
  pipelineCoupling.update().
- Drop the lib/api.ts listPipelineApps wrapper and have the four
  consumers (info, diff, transfer, promote) import the SDK
  composition directly. AppWithPipelineCoupling type also moves to
  the SDK; render-pipeline.ts widens it locally to satisfy hux.table's
  Record<string, unknown> row constraint.
- Removes the now-unused getAppFilter helper, listCouplings helper,
  and FILTERS_HEADER constant from lib/api.ts.
- Bumps @heroku/sdk to the eb/feat/list-pipeline-apps branch which
  exposes the new listPipelineApps composition.
Copy link
Copy Markdown
Contributor

@tlowrimore-heroku tlowrimore-heroku left a comment

Choose a reason for hiding this comment

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

LGTM!

@eablack eablack merged commit b15f593 into feat/heroku-sdk-integration May 20, 2026
5 of 18 checks passed
@eablack eablack deleted the eb/refactor/sdk-pipelines-commands branch May 20, 2026 17:07
eablack added a commit that referenced this pull request May 21, 2026
The integration branch's #3717 imports from @heroku/sdk/compositions/pipeline,
but the SDK's exports cleanup (heroku-sdk#26) replaced compositions/ with
resources/. Update each call site to use the new shape:

  - listPipelineApps → pipelineCouplingExtensions.listApps via HerokuSDK
  - promotePipeline → standalone import (kept as a static reference on
    the Promote command class so existing sinon stubs continue to work)
  - AppWithPipelineCoupling, ReleaseStreamContext → type imports from
    resources/platform/pipeline-{coupling,promotion}

The promote test's stub callback now sees a 3-arg signature (ctx, body,
options) instead of 2-arg, and firstCall.args[0] becomes [1] for body
assertions.

Test scope cleanup for addons/index and addons/info: split the 'apiSdk'
nock scope (Accept-Expansion required) from the 'api' scope (no
expansion). Global /addons and /addons/<id>/addon-attachments don't
accept the expansion header, matching the SDK's per-call header
scoping in heroku-sdk#27.
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