Developing CLI Plugins
Last updated May 09, 2024
Table of Contents
The Heroku CLI is based on oclif which has plugin functionality. Users install plugins with heroku plugins:install PLUGINNAME
, where PLUGINNAME
is an npm package on npmjs.com. Plugin developers can also link them into the CLI with heroku plugins:link
, as described below.
For more information on the Heroku CLI, check out the project on GitHub.
The plugins will be run with a current version of node (usually the latest) no matter what version of node is installed on the machine (if any). There is no need to support old versions of node.
We will walk you through developing a simple hello world plugin. For a more complete example, check out the heroku-git plugin.
Creating the plugin
Generate a new plugin with oclif: (npx
comes included in npm and will automatically install oclif for you)
$ npx oclif generate myplugin
Installing the plugin
While plugins on npmjs.com can be installed with heroku plugins:install
, when developing plugins locally you will want to use heroku plugins:link
. This will validate the plugin, then link it in ~/.local/share/heroku/plugins/linked.json
.
To setup this plugin locally, go to the root of your plugin directory and link the plugin:
$ cd myplugin
$ heroku plugins:link
$ heroku hello world
hello world! (./src/commands/hello.ts)
Using the Heroku API
oclif commands work just fine inside the CLI, but if we want to use the Heroku API for anything we need to use the Heroku CLI command base class.
First add the base class to the project with yarn add @heroku-cli/command
as well as the API schema types with yarn add -D @heroku-cli/schema
. Then edit the command like the following:
// note that we are using @heroku-cli/command instead of @oclif/command
// this inherits from @oclif/command but extends it with Heroku-specific functionality
import {Command, flags} from '@heroku-cli/command'
import * as Heroku from '@heroku-cli/schema'
export default class AppCommand extends Command {
static description = 'say hi to an app'
static flags = {
remote: flags.remote(),
app: flags.app({required: true})
}
async run () {
const {flags} = await this.parse(AppCommand)
const response = await this.heroku.get<Heroku.App>(`/apps/${flags.app}`)
const app = response.body
console.dir(app)
}
}
For more information on what command options are available, read the oclif documentation.
Debugging tips
Use console.dir()
to pretty-print an object.
Run a command with HEROKU_DEBUG=1
to print debugging statements. HEROKU_DEBUG_HEADERS=1
to also get the headers. Use DEBUG=*
for lots of internal debug output.
Inspect ~/Library/Caches/heroku/error.log
for extra error output.
Releasing plugins
All that is needed to run heroku plugins:install
to install a plugin is for it to be published on npmjs.com.
Create an account then log into it from your terminal:
$ npm login
Username: jdxcode
Password:
Email: (this IS public) npm@heroku.com
Logged in as jdxcode on https://registry.npmjs.org/
We recommend using np to make publishing to npm easier. You can release an update with:
$ npx np
It will prompt for a new version, run the tests, as well as other checks to make sure a proper release is being performed.
Release Channels
To have a beta, staging, or development channel for plugins, you can use npm dist-tags.
First publish a package to a dist-tag:
npm publish --tag beta
Then install the plugin with that tag:
heroku plugins:install myplugin@beta
The CLI will continue to update the plugin to any new releases on the beta channel.