Skip to content

feat: Implement devcenter open and preview commands in oclif v4#46

Open
michaelmalave wants to merge 6 commits intofeat/ruby-to-typescriptfrom
feat/ruby-to-typescript-1
Open

feat: Implement devcenter open and preview commands in oclif v4#46
michaelmalave wants to merge 6 commits intofeat/ruby-to-typescriptfrom
feat/ruby-to-typescript-1

Conversation

@michaelmalave
Copy link
Copy Markdown

@michaelmalave michaelmalave commented Apr 9, 2026

Summary

This PR introduces @heroku-cli/heroku-cli-plugin-devcenter, a Node 22+ oclif plugin that implements heroku devcenter:open and heroku devcenter:preview using TypeScript, HTTP calls to Dev Center for open, local markdown/YAML handling and a local Express preview server for preview.

  • Adds src/dist/ build, ESLint (oclif config), CI (multi-OS, Node 22 and 24), Dependabot for npm, and README/CONTRIBUTING updates for the new workflow.
  • Adds shared libraries (paths, Dev Center HTTP client, article parsing/rendering, ~/.netrc helpers for future auth), preview templates and server, plus nock/supertest/unit tests and a runCommand helper for command tests.
  • Uses DEVCENTER_CLI_CWD and DEVCENTER_CLI_TEST so tests stay isolated and non-interactive (no real browser open in tests).

The Ruby devcenter gem and bin/devcenter remain the supported way to run pull and push until a follow-up release.

Breaking change (for users of a published npm plugin only): N/A for gem users; installing/linking this package is additive. A later PR that removes the gem will document the full breaking change.

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

Setup:

npm install
npm run build
heroku plugins:link .

Help:

heroku devcenter --help
heroku devcenter:open --help
heroku devcenter:preview --help

Open a published article in the browser:

heroku devcenter:open error-pages

Preview a local article:

heroku devcenter:preview error-pages

Related Issues:

GUS work item: W-20888065

@michaelmalave michaelmalave requested a review from a team as a code owner April 9, 2026 20:50
@michaelmalave michaelmalave changed the title feat: add Heroku devcenter plugin with open and preview commands feat: Implement devcenter pull and push command in oclif v4 Apr 13, 2026
@michaelmalave michaelmalave changed the title feat: Implement devcenter pull and push command in oclif v4 feat: Implement devcenter open and preview commands in oclif v4 Apr 13, 2026
Copy link
Copy Markdown

@k80bowman k80bowman left a comment

Choose a reason for hiding this comment

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

The commands work just fine, thank you for all of this. I left quite a few comments, though. Mostly they are smaller pattern/oclif things. I also noticed that there are no bin files.

Comment on lines +17 to +20
static flags = {
debug: Flags.boolean({
description: 'print internal debug messages',
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this flag necessary? If we're using the CLI, we can just use the debug package and then people can view the debug messages via DEBUG=* or DEBUG=devcenter.

Comment on lines +45 to +46
const host = flags.host ?? '127.0.0.1'
const port = flags.port ?? 3000
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we need this if we have flag defaults set? I think we can just expand the flags object.

Comment on lines +18 to +21
debug: Flags.boolean({
description:
'log preview server activity (HTTP handling, file saves); enables oclif debug for this command (see `DEBUG` e.g. oclif:heroku:devcenter:preview)',
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this flag in the original? I couldn't find it. Also, I don't think it's necessary. We don't need to specifically enable oclif debug. In other commands, if we want to label specific debug messages, we do that through the debug package itself.

Comment on lines +48 to +56
// oclif Command wires `this.debug` to the `debug` package as `oclif:${bin}:${commandId}` (see @oclif/core getLogger).
const oclifDebugNs = `oclif:${this.config.bin}:${this.id}`
if (flags.debug && !debug.enabled(oclifDebugNs)) {
const existing = process.env.DEBUG?.trim()
debug.enable(existing ? `${existing},${oclifDebugNs}` : oclifDebugNs)
}

const verbose = Boolean(flags.debug || debug.enabled(oclifDebugNs))
const debugLog = verbose ? this.debug.bind(this) : () => {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't think this is necessary. Especially if we remove the debug flag. It looks like in the runPreview function there's only a couple of debug messages being printed anyway. We can do that directly using the debug package, I don't think we need to have a flag to enable it.

* Heroku API token for `api.heroku.com` from netrc, same resolution as the Heroku CLI
* (`netrc-parser`: plain `~/.netrc` or `~/.netrc.gpg` when present, decrypted via `gpg`).
*/
export function getHerokuApiToken(): string {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm ok with this implementation for now, but we should open a work item for updating this to use the credential manager.

"node": ">=22"
},
"scripts": {
"build": "npm run clean && tsc && oclif manifest && mv oclif.manifest.json ./dist/oclif.manifest.json",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
"build": "npm run clean && tsc && oclif manifest && mv oclif.manifest.json ./dist/oclif.manifest.json",
"build": "npm run clean && tsc && oclif manifest && oclif readme",

It's better to include the oclif.manifest.json file in the files array rather than move it to the dist directory. Also, we should include the oclif readme command here.

"yaml": "^2.8.3"
},
"devDependencies": {
"@oclif/plugin-help": "^6.2.36",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This should be in dependencies

"typescript": "^5.4.0"
},
"overrides": {
"serialize-javascript": "^7.0.3"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this necessary? Which package is causing the issue?

$ devcenter push dynos

This will save the title and content from your local article in Dev Center, using your Heroku credentials from `~/.netrc`, which you can set by doing `heroku auth:login`.
This will save the title and content from your local article in Dev Center, using your Heroku API credentials from netrc: plain `~/.netrc` or encrypted `~/.netrc.gpg` (decrypted with `gpg` when applicable), the same store as the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) after `heroku login`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We need to update the README so that it can be autogenerated by oclif.

Comment on lines +7 to +8
"module": "Node16",
"moduleResolution": "Node16",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should these both be set to NodeNext like the CLI?

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