Plugin development
To create a plugin for semantic-release, decide which release steps your plugin needs to participate in by implementing the corresponding lifecycle hooks.
In practice, most plugins should implement at least the verifyConditions lifecycle hook so they can validate user input and fail early with a clear error. A plugin can implement any of the following lifecycle hooks:
verifyConditionsanalyzeCommitsverifyReleasegenerateNotesaddChannelpreparepublishsuccessfail
semantic-release loads the plugin in Node.js and looks for exported functions whose names match lifecycle hooks. Those exported functions are your plugin’s lifecycle methods.
For example, exporting lifecycle methods named verifyConditions and success binds your plugin to the verifyConditions and success lifecycle hooks, which run during the Verify Conditions and Notify release steps respectively.
When semantic-release calls a lifecycle method, it passes two arguments:
pluginConfig- plugin options from the user’s semantic-release configuration (for example,release.config.js)context- runtime information from semantic-release, including environment variables and release metadata
As you add lifecycle methods, make sure each one accepts pluginConfig and context in its function signature.
Creating a Plugin Project
Section titled “Creating a Plugin Project”Start by creating a new Node.js package with your preferred package manager, for example npm init, pnpm init, or yarn init. Then create an index.js file, set "type": "module" in package.json, and point your package entry to that file with main or exports. We will use index.js to expose the plugin lifecycle methods.
Next, create a lib folder in the root of the project. This is where the lifecycle implementation code will live. Finally, create a test folder so you can add automated tests for your plugin.
We recommend setting up linting so the plugin stays consistent and easy to maintain. ESLint is a common choice, but any tooling that fits your team is fine.
Exposing Lifecycle Methods
Section titled “Exposing Lifecycle Methods”In your index.js file, you can start with the following code:
import verify from "./lib/verify.js";
let verified;
/** * Called by semantic-release during the Verify Conditions release step via the verifyConditions lifecycle hook * @param {*} pluginConfig The semantic-release plugin config * @param {*} context The context provided by semantic-release */export async function verifyConditions(pluginConfig, context) { await verify(pluginConfig, context); verified = true;}Then, in your lib folder, create a file called verify.js and add the following:
import AggregateError from "aggregate-error";
/** * Verify that the plugin has the configuration it needs. */export default async (pluginConfig, context) => { const { logger } = context; const errors = [];
// Throw any errors we accumulated during the validation if (errors.length > 0) { throw new AggregateError(errors); }};As of right now, this code won’t do anything. However, if you were to run this plugin via semantic-release, this verifyConditions() lifecycle method would run when semantic-release executes the Verify Conditions release step.
Following this structure, you can add other lifecycle methods and checks throughout the release process.
Supporting Options
Section titled “Supporting Options”Let’s say you want to verify that an option is passed. Plugin options are configured in the plugins array in the semantic-release configuration. For example:
{ plugins: [ [ "@semantic-release/my-special-plugin", { message: "My cool release message", }, ], ];}That message value is passed to your plugin as part of pluginConfig. You can validate it in verify.js before using it later in the release process:
import AggregateError from "aggregate-error";import SemanticReleaseError from "@semantic-release/error";
/** * Verify that the plugin has the configuration it needs. */export default async (pluginConfig, context) => { const { logger } = context; const errors = []; const { message } = pluginConfig;
if (!message) { // Add a SemanticReleaseError to AggregateError. errors.push( new SemanticReleaseError( "Missing `message` option.", "EMISSINGMESSAGE", "Add a `message` option to this plugin's entry in the `plugins` array.", ), ); }
// Throw any errors we accumulated during the validation if (errors.length > 0) { throw new AggregateError(errors); }};Context
Section titled “Context”The context object is the runtime state that semantic-release builds and passes to every plugin lifecycle method during a release run. It gives your plugin access to release data (for example branch, commits, and next release info), execution details (for example cwd and options), integration values (for example env and CI metadata), and helper utilities such as logger.
Think of context as the shared source of truth for the current release execution: each lifecycle can read from it, and semantic-release may add more keys to it as the run progresses.
Common context keys
Section titled “Common context keys”The following keys are commonly available on context across lifecycle hooks:
stdout: Writable stream for standard output.stderr: Writable stream for error output.logger: semantic-release logger- Available methods:
logwarnsuccesserror
- Available methods:
Context object keys by lifecycle hook
Section titled “Context object keys by lifecycle hook”The context object evolves as semantic-release moves through each release step. Start with verifyConditions to see the baseline shape, then use the later lifecycle sections to understand which additional keys are available at that point in the run.
verifyConditions
Section titled “verifyConditions”Initially the context object contains the following keys for the verifyConditions lifecycle hook:
cwd(String): Current working directoryenv(Object): Environment variablesenvCi(Object): Information about CI environment- Contains (at least) the following keys:
isCi(Boolean):trueif the environment is a CI environmentcommit(String): Commit hashbranch(String): Current branch
- Contains (at least) the following keys:
options(Object): Options passed tosemantic-releasevia CLI, configuration files etc.branch(Object): Information on the current branch- Object keys:
channel(String | null)tags(Array)type(String)name(String)range(String)accept(Array)main(Boolean)
- Object keys:
branches(Array): Information on branches- List of branch objects (see above)
analyzeCommits
Section titled “analyzeCommits”The analyzeCommits lifecycle hook adds the following keys:
commits(Array): List of commits considered when determining the next version.- Each commit object can include:
commit(Object): Commit hash metadatalong(String): Full commit hashshort(String): Short commit hash
tree(Object): Tree hash metadatalong(String): Full tree hashshort(String): Short tree hash
author(Object): Commit author informationname(String): Author nameemail(String): Author emaildate(String): ISO 8601 timestamp
committer(Object): Committer informationname(String): Committer nameemail(String): Committer emaildate(String): ISO 8601 timestamp
subject(String): Commit message subjectbody(String): Commit message bodyhash(String): Commit hashcommitterDate(String): ISO 8601 timestampmessage(String): Full commit messagegitTags(String): Git tags associated with the commit
- Each commit object can include:
releases(Array): List of releases created in the current runlastRelease(Object): Information about the most recent releaseversion(String): VersiongitTag(String): Git tagchannels(Array): List of channelsgitHead(String): Commit hashname(String): Release name
verifyRelease
Section titled “verifyRelease”The verifyRelease lifecycle hook adds:
nextRelease(Object): Information about the calculated next releasetype(String): Release typechannel(String | null): Release channelgitHead(String): Git hashversion(String): Version withoutvgitTag(String): Version withvname(String): Release name
generateNotes
Section titled “generateNotes”When a plugin implements this hook (for example, @semantic-release/release-notes-generator), the generateNotes lifecycle hook populates a new key on context’s nextRelease (Object):
nextRelease.notes(String): Generated release notes.
addChannel
Section titled “addChannel”This lifecycle runs only if there are releases merged from a higher branch that have not been added to the current branch channel.
Context content is similar to the verifyRelease lifecycle hook.
prepare
Section titled “prepare”The prepare lifecycle hook does not add new context keys.
It runs after generateNotes, so nextRelease.notes is available when it was populated by a generateNotes plugin.
publish
Section titled “publish”When a plugin implements this hook (for example, @semantic-release/npm or @semantic-release/github), the publish lifecycle hook can populate a new top-level context key:
releases(Array): Release entries returned bypublishplugins.
success
Section titled “success”The success lifecycle hook runs only when the release execution completes successfully. In failure scenarios, semantic-release runs the fail lifecycle hook instead.
Available keys:
releases(Array): Releases already populated during thepublishlifecycle hook
The fail lifecycle hook runs only when the release execution fails. In successful scenarios, semantic-release runs the success lifecycle hook instead.
Additional keys:
errors(Array): Errors collected during the failed release execution
Supporting Environment Variables
Section titled “Supporting Environment Variables”Similar to options, environment variables can be used for tokens and service-specific URLs. These values are available on context, not pluginConfig. For example, if your plugin needs GITHUB_TOKEN to call the GitHub API, you can check for it in your verify function:
const { env } = context;
if (env.GITHUB_TOKEN) { //...}Logger
Section titled “Logger”Use context.logger to provide debug logging in the plugin. Available logging functions: log, warn, success, error.
const { logger } = context;
logger.log("Some message from plugin.");The above usage yields the following where PLUGIN_PACKAGE_NAME is automatically inferred.
[3:24:04 PM] [semantic-release] [PLUGIN_PACKAGE_NAME] › ℹ Some message from plugin.Execution order
Section titled “Execution order”Release step order is defined in Release Steps. For lifecycle hooks implemented by multiple plugins, semantic-release executes those lifecycle methods in the order the plugins are declared in the plugins configuration.
Handling errors
Section titled “Handling errors”To be detected and handled properly, errors thrown by the plugin must be instances of SemanticReleaseError (or subclasses), as shown in the earlier verify.js validation example. If you need to report multiple validation problems at once, wrap those SemanticReleaseError instances in AggregateError. Other error types are treated as unexpected failures, bubble up to the final catch, and do not trigger fail plugins.
Advanced
Section titled “Advanced”Knowledge that might be useful for plugin developers.
Multiple analyzeCommits plugins
Section titled “Multiple analyzeCommits plugins”It is straightforward to define multiple plugins that implements the analyzeCommits hook, but it is less obvious that plugins executed after the first one (for example, the default commit-analyzer) can change the result. This makes it possible to create more advanced rules or fallback behavior, such as defining a default when none of the commits would produce a new release.
The returned value must be a known release type. For example, commit-analyzer recognizes the following default types:
- major
- premajor
- minor
- preminor
- patch
- prepatch
- prerelease
If an analyzeCommits lifecycle hook plugin does not return anything, the earlier result is used. If it returns a supported string value, that value overrides the previous result.