Skip to content

Plugins API Reference

The plugin system extends pubm without forking the core publish pipeline.

export interface PubmPlugin {
name: string;
registries?: PackageRegistry[];
ecosystems?: Ecosystem[];
hooks?: PluginHooks;
commands?: PluginCommand[];
credentials?: (ctx: PubmContext) => PluginCredential[];
checks?: (ctx: PubmContext) => PluginCheck[];
}
  • credentials returns the list of tokens this plugin needs. Pubm calls it once during initialization so it can prompt for missing tokens, inject them into the environment, and sync them to GitHub Secrets when running --ci-prepare.
  • checks returns preflight checks that run alongside the core prerequisite and condition phases. Each check uses its phase field to choose when it runs.
export interface PluginHooks {
// Lifecycle hooks
beforeTest?: HookFn;
afterTest?: HookFn;
beforeBuild?: HookFn;
afterBuild?: HookFn;
beforeVersion?: HookFn;
afterVersion?: HookFn;
beforePublish?: HookFn;
afterPublish?: HookFn;
beforePush?: HookFn;
afterPush?: HookFn;
afterRelease?: AfterReleaseHookFn;
onError?: ErrorHookFn;
onSuccess?: HookFn;
// Asset pipeline hooks
resolveAssets?: AssetPipelineHooks["resolveAssets"];
transformAsset?: AssetPipelineHooks["transformAsset"];
compressAsset?: AssetPipelineHooks["compressAsset"];
nameAsset?: AssetPipelineHooks["nameAsset"];
generateChecksums?: AssetPipelineHooks["generateChecksums"];
uploadAssets?: AssetPipelineHooks["uploadAssets"];
}

These hooks let plugins intercept each stage of the release asset pipeline. They run between the build step and the GitHub Release upload.

HookWhen it runsInput → Output
resolveAssetsAfter glob matchingResolvedAsset[]ResolvedAsset[]
transformAssetPer asset, before compressionResolvedAssetTransformedAsset | TransformedAsset[]
compressAssetPer asset, replaces built-in compressionTransformedAssetCompressedAsset
nameAssetPer asset, after compressionCompressedAssetstring
generateChecksumsAfter all assets are named and hashedPreparedAsset[]PreparedAsset[]
uploadAssetsAfter GitHub Release uploadPreparedAsset[]UploadedAsset[]

When multiple plugins register the same asset pipeline hook, they are chained in registration order. uploadAssets is an exception: each plugin receives the same input independently and all results are concatenated.

Read Asset Pipeline Hooks for full signatures, examples, and composition rules.

type HookFn = (ctx: PubmContext) => Promise<void> | void;
type ErrorHookFn = (ctx: PubmContext, error: Error) => Promise<void> | void;
type AfterReleaseHookFn = (
ctx: PubmContext,
releaseCtx: ReleaseContext,
) => Promise<void> | void;

Hooks receive the current pipeline context:

export interface PubmContext {
readonly config: ResolvedPubmConfig;
readonly options: ResolvedOptions;
readonly cwd: string;
runtime: {
tag: string;
promptEnabled: boolean;
cleanWorkingTree: boolean;
pluginRunner: PluginRunner;
versionPlan?: VersionPlan;
releaseContext?: ReleaseContext;
tempDir?: string;
pluginTokens?: Record<string, string>;
};
}

Key fields:

  • ctx.config: resolved pubm config (packages, registries, plugins, releaseAssets, etc.)
  • ctx.options: resolved CLI options (version, tag, mode, dryRun, etc.)
  • ctx.cwd: working directory
  • ctx.runtime: mutable runtime state (version plan, temp directory, etc.)
  • ctx.runtime.pluginTokens: map of resolved plugin credential values keyed by PluginCredential.key. Populated after the credential collection step and available in all subsequent hooks.

Plugins can register rollback actions that execute automatically when the publish pipeline fails. Actions run in LIFO (last-in, first-out) order.

Register rollback actions with ctx.runtime.rollback.add() inside any hook:

hooks: {
afterVersion: async (ctx) => {
// Perform side effect
const backup = readFileSync(filePath, "utf-8");
modifyFile(filePath);
// Register rollback to undo it
ctx.runtime.rollback.add({
label: `Restore ${filePath}`,
fn: async () => {
writeFileSync(filePath, backup, "utf-8");
},
});
},
}
PropertyTypeDescription
labelstringLabel shown during rollback
fn(ctx: PubmContext) => Promise<void>Async function that reverses the side effect
confirmboolean?If true, prompts for confirmation in TTY mode before executing. Auto-executes in CI.

Declarative descriptor for a token or secret that a plugin requires.

export interface PluginCredential {
key: string;
env: string;
label: string;
tokenUrl?: string;
tokenUrlLabel?: string;
ghSecretName?: string;
required?: boolean;
resolve?: () => Promise<string | null>;
validate?: (token: string, task: PluginTaskContext) => Promise<boolean>;
}
FieldTypeDescription
keystringInternal identifier used to look up the value in ctx.runtime.pluginTokens.
envstringEnvironment variable name that provides the token in CI.
labelstringHuman-readable label shown in interactive prompts.
tokenUrlstringOptional URL where the user can create the token.
tokenUrlLabelstringOptional display label for tokenUrl.
ghSecretNamestringName of the GitHub Secret that should hold this token, used by --ci-prepare sync.
requiredbooleanWhen true, pubm errors if the credential cannot be resolved. Defaults to true.
resolvefunctionOptional custom resolver (no arguments, returns Promise<string | null>). Called before the keyring/prompt fallback.
validatefunctionOptional token validator. Receives the resolved token and a PluginTaskContext; return false to re-prompt.

Resolution order: env variable → resolve() → keyring (SecureStore) → interactive prompt.

Descriptor for a preflight check contributed by a plugin.

export interface PluginCheck {
title: string;
phase: "prerequisites" | "conditions";
task: (ctx: PubmContext, task: PluginTaskContext) => Promise<void> | void;
}
FieldTypeDescription
titlestringLabel shown in the task list UI.
phase"prerequisites" | "conditions"When to run: "prerequisites" runs before network calls; "conditions" runs after registry connectivity is confirmed.
taskfunctionThe check implementation. Throw to fail the check.

A listr2-agnostic wrapper passed as the second argument to PluginCheck.task.

export interface PluginTaskContext {
output: string;
title: string;
prompt<T = unknown>(options: {
type: string;
message: string;
[key: string]: unknown;
}): Promise<T>;
}
  • output displays a status line beneath the check title in the task list UI.
  • title changes the task title dynamically.
  • prompt() runs an enquirer prompt, available in interactive mode.
const notifyPlugin = {
name: "notify-plugin",
hooks: {
async afterPublish(ctx) {
const label =
ctx.versions && ctx.versions.size > 0
? [...ctx.versions].map(([name, version]) => `${name}@${version}`).join(", ")
: `v${ctx.version}`;
console.log(`Published ${label}`);
},
},
};
export interface PluginCommand {
name: string;
description: string;
subcommands?: PluginSubcommand[];
}
export interface PluginSubcommand {
name: string;
description: string;
options?: PluginCommandOption[];
action: (args: Record<string, unknown>) => Promise<void>;
}

Use custom registries when you need another publish destination. Use custom ecosystems when you need pubm to read and write versions for another package type.