Skip to content

Release Lifecycle Hooks

The following hooks are all called during various release commands (ex: latest, next, canary, shipit). These hooks is where the publishing of your package actually happens.

prCheck

Called before auto pr-check is ran/

Context Object:

  • pr - All github information related to the PR
auto.hooks.prCheck.tapPromise("NPM", async ({ pr }) => {
  // Do stuff
});
auto.hooks.prCheck.tapPromise("NPM", async ({ pr }) => {
  // Do stuff
});

beforeShipIt

Happens before shipit is run. This is a great way to throw an error if a token or key is not present.

Context Object:

  • releaseType (latest | old | next | canary) - The type of release shipit will attempt to make.
  • dryRun - Whether the run is a dry run
auto.hooks.beforeShipIt.tapPromise("NPM", async (context) => {
  if (!process.env.NPM_TOKEN) {
    throw new Error("NPM Token is needed for the NPM plugin!");
  }
});
auto.hooks.beforeShipIt.tapPromise("NPM", async (context) => {
  if (!process.env.NPM_TOKEN) {
    throw new Error("NPM Token is needed for the NPM plugin!");
  }
});

Other examples:

  • all-contributors - Manage manual contribution additions in PRs
  • gh-pages - Publishes your docs to GitHub pages on a latest release
  • npm - Exits early when lerna won't need to make a release

beforeCommitChangelog

Ran before the changelog command commits the new release notes to CHANGELOG.md. Useful for modifying the changelog as a whole or creating extra changelog files. These files can be apart of the commit that updates the changelog.

  • bump - the semver bump
  • commits - the commits in the changelog
  • currentVersion - version that was just released
  • lastRelease - the version before the current version
  • releaseNotes - generated release notes for the release
auto.hooks.beforeCommitChangelog.tap(
  "MyPlugin",
  async ({ currentVersion, commits, releaseNotes, lastRelease }) => {
    // do something
  }
);
auto.hooks.beforeCommitChangelog.tap(
  "MyPlugin",
  async ({ currentVersion, commits, releaseNotes, lastRelease }) => {
    // do something
  }
);

Other examples:

  • In Core: Create major version branches when major happens
  • npm - Create sub-package changelogs

afterChangelog

Ran after the changelog command adds the new release notes to CHANGELOG.md. Useful for getting extra commits into a release before publishing.

  • bump - the semver bump
  • commits - the commits in the changelog
  • currentVersion - version that was just released
  • lastRelease - the version before the current version
  • releaseNotes - generated release notes for the release
auto.hooks.afterChangelog.tap(
  "MyPlugin",
  async ({ currentVersion, commits, releaseNotes, lastRelease }) => {
    // do something
  }
);
auto.hooks.afterChangelog.tap(
  "MyPlugin",
  async ({ currentVersion, commits, releaseNotes, lastRelease }) => {
    // do something
  }
);

Other examples:

version

Increment the version the package. This is a good opportunity to git tag the release also. This hooks is required for plugin that facilitate publishing.

Here npm does it for us.

auto.hooks.version.tapPromise("NPM", async ({ bump, dryRun, quiet }) => {
  if (dryRun) {
    const { version } = await loadPackageJson();
    console.log(inc(version, bump));
    return;
  }

  await execPromise("npm", [
    "version",
    bump,
    "-m",
    "Bump version to: %s [skip ci]",
  ]);
});
auto.hooks.version.tapPromise("NPM", async ({ bump, dryRun, quiet }) => {
  if (dryRun) {
    const { version } = await loadPackageJson();
    console.log(inc(version, bump));
    return;
  }

  await execPromise("npm", [
    "version",
    bump,
    "-m",
    "Bump version to: %s [skip ci]",
  ]);
});

If you're implementing this hook for a publishing plugin you must commit (with [skip ci] in the message) and tag the version.

afterVersion

Ran after the package has been versioned.

Examples:

  • In Core: Used to exit early is new commits are detected on the remote
  • brew - Create a new brew formula once the package a=has been versioned
auto.hooks.afterVersion.tapPromise("NPM", async ({ dryRun }) => {});
auto.hooks.afterVersion.tapPromise("NPM", async ({ dryRun }) => {});

publish

Publish the package to some package distributor. You must push the tags to github! This hooks is required for plugin that facilitate publishing.

auto.hooks.publish.tapPromise("NPM", async ({ bump }) => {
  await execPromise("npm", [
    "version",
    bump,
    "-m",
    "Bump version to: %s [skip ci]",
  ]);
  await execPromise("npm", ["publish"]);
  await execPromise("git", [
    "push",
    "--follow-tags",
    "--set-upstream",
    auto.remote,
    "$branch",
  ]);
});
auto.hooks.publish.tapPromise("NPM", async ({ bump }) => {
  await execPromise("npm", [
    "version",
    bump,
    "-m",
    "Bump version to: %s [skip ci]",
  ]);
  await execPromise("npm", ["publish"]);
  await execPromise("git", [
    "push",
    "--follow-tags",
    "--set-upstream",
    auto.remote,
    "$branch",
  ]);
});

afterPublish

Ran after the package has been published.

canary

Used to publish a canary release. In this hook you get the semver bump and the unique canary postfix ID.

You can either return a string value of just the version or an object containing the following which will be rendered within and HTML details element.

  • newVersion - The version published in the canary release or a header for the details element.
  • details - The body of the details element
auto.hooks.canary.tapPromise(
  this.name,
  async ({ bump, canaryIdentifier, dryRun, quiet }) => {
    const lastRelease = await auto.git!.getLatestRelease();
    const current = await auto.getCurrentVersion(lastRelease);
    const nextVersion = inc(current, bump as ReleaseType);
    const isScopedPackage = name.match(/@\S+\/\S+/);
    const canaryVersion = `${nextVersion}-canary${canaryIdentifier}`;

    await execPromise("npm", [
      "version",
      canaryVersion,
      "--no-git-tag-version",
    ]);
    await execPromise("npm", ["publish", "--tag", "canary"]);

    auto.logger.verbose.info("Successfully published canary version");
    return canaryVersion;
  }
);
auto.hooks.canary.tapPromise(
  this.name,
  async ({ bump, canaryIdentifier, dryRun, quiet }) => {
    const lastRelease = await auto.git!.getLatestRelease();
    const current = await auto.getCurrentVersion(lastRelease);
    const nextVersion = inc(current, bump as ReleaseType);
    const isScopedPackage = name.match(/@\S+\/\S+/);
    const canaryVersion = `${nextVersion}-canary${canaryIdentifier}`;

    await execPromise("npm", [
      "version",
      canaryVersion,
      "--no-git-tag-version",
    ]);
    await execPromise("npm", ["publish", "--tag", "canary"]);

    auto.logger.verbose.info("Successfully published canary version");
    return canaryVersion;
  }
);

canary version should not produce any of the following:

  • a new tag
  • any commits
  • a new release
  • any changes to changelogs

It is "test" release and has no impact on any of your real releases.

next

Used to publish a next release. In this hook you get the semver bump and a list of all pre-releases made.

This hook must add the version it published to preReleaseVersions and return it.

  • preReleaseVersions - A list of prerelease version publish during the current run
  • bump - The version bump to apply
import {
  determineNextVersion,
  execPromise,
  getCurrentBranch,
} from "@auto-it/core";

auto.hooks.next.tapPromise(
  this.name,
  async (preReleaseVersions, { bump, dryRun }) => {
    const branch = getCurrentBranch() || "";
    const lastRelease = await auto.git.getLatestRelease();
    const current =
      (await auto.git.getLastTagNotInBaseBranch(branch)) ||
      (await auto.getCurrentVersion(lastRelease));
    // Use this helper function to determine the next prerelease version
    const prerelease = determineNextVersion(lastRelease, current, bump, "next");

    // Make sure to add the version to the list
    preReleaseVersions.push(prerelease);

    if (dryRun) {
      return preReleaseVersions;
    }

    // Create a new tag for it
    await execPromise("git", [
      "tag",
      prerelease,
      "-m",
      `"Tag pre-release: ${prerelease}"`,
    ]);
    // Push the tag
    await execPromise("git", ["push", auto.remote, "--tags"]);

    return preReleaseVersions;
  }
);
import {
  determineNextVersion,
  execPromise,
  getCurrentBranch,
} from "@auto-it/core";

auto.hooks.next.tapPromise(
  this.name,
  async (preReleaseVersions, { bump, dryRun }) => {
    const branch = getCurrentBranch() || "";
    const lastRelease = await auto.git.getLatestRelease();
    const current =
      (await auto.git.getLastTagNotInBaseBranch(branch)) ||
      (await auto.getCurrentVersion(lastRelease));
    // Use this helper function to determine the next prerelease version
    const prerelease = determineNextVersion(lastRelease, current, bump, "next");

    // Make sure to add the version to the list
    preReleaseVersions.push(prerelease);

    if (dryRun) {
      return preReleaseVersions;
    }

    // Create a new tag for it
    await execPromise("git", [
      "tag",
      prerelease,
      "-m",
      `"Tag pre-release: ${prerelease}"`,
    ]);
    // Push the tag
    await execPromise("git", ["push", auto.remote, "--tags"]);

    return preReleaseVersions;
  }
);

next version should not produce any of the following:

  • any commits
  • any changes to changelogs

makeRelease

Ran when trying to make a release during the release command has run. This async hook gets the following arguments:

  • dryRun - Whether this is a dry run
  • from - Commit to start calculating the version from
  • newVersion - The version being released
  • isPrerelease - Whether the release being made is a prerelease
  • fullReleaseNotes - The generated release notes for all of the commits
  • commits - The commits included in the release
auto.hooks.makeRelease.tapPromise("MyPlugin", async (options) => {
  if (!options.dryRun) {
    this.logger.log.info(`Releasing ${options.newVersion} to GitHub.`);

    return this.git!.publish(
      options.fullReleaseNotes,
      options.newVersion,
      options.isPrerelease
    );
  }
});
auto.hooks.makeRelease.tapPromise("MyPlugin", async (options) => {
  if (!options.dryRun) {
    this.logger.log.info(`Releasing ${options.newVersion} to GitHub.`);

    return this.git!.publish(
      options.fullReleaseNotes,
      options.newVersion,
      options.isPrerelease
    );
  }
});

Other examples:

  • npm - Create GitHub releases for sub-packages

afterRelease

Ran after the release command has run, creating a new release on GitHub. This async hook gets the following arguments:

  • lastRelease - the version that existed prior to the current release
  • newVersion - version that was just released
  • commits - the commits in the release
  • releaseNotes - generated release notes for the release
  • response - the response returned from making the release
auto.hooks.afterRelease.tapPromise(
  "MyPlugin",
  async ({ lastRelease, newVersion, commits, releaseNotes, response }) => {
    // do something
  }
);
auto.hooks.afterRelease.tapPromise(
  "MyPlugin",
  async ({ lastRelease, newVersion, commits, releaseNotes, response }) => {
    // do something
  }
);

Other examples:

  • release - Post comment on issues and PRs about release version
  • s3 - Upload release assets to s3
  • slack - Post the release notes to slack
  • twitter - Create a tweet about the new release
  • upload-assets - Uses hook to upload files to newly created release

afterShipIt

Ran after the shipit command has run.

  • data
    • newVersion - The new version that was release
    • commits - the commits in the release
    • context - The type of release that was created (latest, next, canary, or old)
    • dryRun - Whether the run is a dry run
auto.hooks.afterShipIt.tap("MyPlugin", async ({ context }) => {
  // do something
});
auto.hooks.afterShipIt.tap("MyPlugin", async ({ context }) => {
  // do something
});

Other examples:

  • gradle - Uses hook to setup next snapshot version