CLI tool
algolia/shipjs avatar
algolia/shipjs

shipjs: releasing a package through a pull request instead of a local script

Take control of what is going to be your next release.

777 stars27 forksJavaScriptMIT

At a glance

What is it?
Ship.js turns a release into a reviewable pull request. The prepare step works out the next version, updates the changelog and opens a PR, a human merges it, and the trigger step publishes, tags and creates the release, optionally from CI. The trade-off is explicit: you get a gate and a place to discuss what is shipping, at the cost of an extra pull request in every release.
Who is it for?
Adopt Ship.js if your team releases a package from more than one machine, if you want a human to approve the version and the changelog before anything reaches the registry, and if you are willing to give up a fully automatic publish. Do not adopt it for a solo project with one package and a simple pipeline, where an extra pull request is pure overhead, and do not expect it to replace a build and test system.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 135 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The release ritual the tool tries to shorten

Ship.js comes from Algolia, and its description is a statement of intent: take control of what is going to be your next release.

The problem it attacks is stated in the README with more candour than most tools manage. Coding is fun, debugging and testing are okay, but releasing is not. A release goes through something like this list: update the version in package.json, update the changelog, actually release it, for example with npm run build and npm publish, create a git tag, and create a release on GitHub. Six steps, each of which can be forgotten or done wrongly.

Two failure modes are called out. The first is mistakes during the release, and the reasons given are organisational rather than technical: environments are different across team members, you are releasing alone because the whole process happens on your local machine, and it is not your everyday job so mistakes happen.

The second is that you are blocked and cannot do anything else until it is done. Even with a release script, you have to watch until the script finishes, and the README says plainly that you do not want to switch to another feature branch and work there until the script finishes.

The three advertised properties follow directly from that diagnosis. Automated, to minimize your effort and make fewer mistakes. Asynchronous, so you do not have to release on your local machine and can continue your work. Collaborative, because you can review the next release on a pull request with your colleagues instead of sweating it alone.

The implementation is JavaScript, the licence is MIT, the documentation lives at community.algolia.com/shipjs, and the last push was on 2026-06-10 with releases v0.28.4, v0.28.3 and v0.28.2 in 2026.

Prepare, review, trigger

The release process in Ship.js has three parts, and the split is the whole idea.

Part one is preparation, run with shipjs prepare. It figures out the next version, updates the version and the changelog, and creates a pull request. The README says it takes less than a couple of minutes. Nothing is published at this point; the pull request is a proposal.

Part two is review, and it is a human activity. You review the pull request yourself or with your colleagues, and you can add more commits to it if you want. You can hold the release, build from the staging branch and test it manually. If you want to cancel the release, you close the pull request and delete the staging branch. When you think it is ready, you merge the pull request.

Part three is the trigger, run with shipjs trigger. It runs a final test, unit and end to end as you have configured them, releases the package to npm or elsewhere as you have configured it, creates a git tag for the version, and creates a release for that tag on GitHub.

The trigger step can be run manually on the base branch after the pull request is merged, which is the simple case for a single maintainer. But you can also configure your CI service, CircleCI is the example given, to run it for you, and the README explains why you would: it means the longest process is on the CI service asynchronously, not occupying your working environment.

That is the answer to the blocking complaint. The slow part runs where blocking does not matter, and the fast part, the part that needs judgement, is a pull request you can review in the morning.

How it differs from a fully automatic releaser

The README has a section titled How is it different from semantic-release, and it is the clearest statement of the project's position.

semantic-release is described as a tool for fully automated version management and package publishing. Ship.js gives you more control over the release process, and the mechanism of that control is that Ship.js automatically creates a pull request before publishing every release.

What that pull request buys you is listed explicitly. You can confirm the next version is correct. You can confirm which commits are going to be released and discuss them with your colleagues. You can edit the automatically generated changelog for clarity and readability, which matters because the changelog is generated from commit messages and generated text is rarely exactly what a user should read. You can run any automated tests on the package release candidate. And you can build a release candidate automatically, with Pika CI or CodeSandbox named as the options.

So the difference in approach is not that Ship.js computes versions differently, it is where the automation stops. Both tools infer the next version from your history. One of them then publishes, and the other one proposes, and a human decides.

That is a real difference in kind for a team, and a real cost for a solo maintainer. Every release gains a branch, a pull request, a review and a merge. In exchange, nothing reaches the registry without a person having looked at the version number and the changelog, and the person doing the release does not have to be the person who wrote the code.

Setting it up and the config file it writes

Installation is one command, and it is interactive:

bash
npx shipjs setup

The README explains what that does: the interactive CLI will help you install Ship.js into your package and create a tailored config file for your project.

That last part is the interesting one. Ship.js is not configured by a flag soup at release time; it reads a config file, and the setup command writes that file for your project rather than making you assemble it from documentation. The repository has a ship.config.js at the top level, which is the shipped example of what that file looks like.

After setup you have the two commands and the flow described above. The getting started guide on the documentation site covers the rest, and there is a video if you prefer watching to reading.

The version detection is the part you should check carefully before trusting it. The prepare step figures out the next version from your commit history, which means the tool has to understand the convention your team already uses. The repository topics list conventional-commits, conventional-changelog and semver alongside release and versioning, which is the vocabulary of the conventional commits approach, and the project name in the ecosystem of the topics, semantic-release, is the tool it is differentiating itself from.

If your team writes its own version bumps into commits by hand, or uses a different convention, the version Ship.js proposes will not be the version you expected. That check is cheap to do once, on a branch, before you let it open its first pull request against your main branch.

The repository releases itself with Ship.js

The top level of the repository shows a Lerna monorepo with a website workspace alongside the packages, and a few details are worth noticing because they describe the project rather than the tool.

The workspace configuration covers packages/* and website, and the root package is private with a version of 0.28.4. The build script delegates to the shipjs-lib workspace, lint and test run across the workspaces with lerna run, and there is a bootstrap script that points at packages/shipjs-lib/tests/bootstrap.sh for setting up test fixtures. The package manager is pinned to yarn 1.22.19 and the engines field requires Node 20 or newer.

The most telling entry is the release script. The root package defines release as shipjs prepare. The project that argues for a pull request before every publish releases itself with a pull request before every publish, which is the clearest possible statement of the philosophy.

There is also a patches directory and a postinstall script that runs patch-package, which is the mechanism for carrying fixes to dependencies through installs, and a patches directory is exactly what you would expect from a tool that has to work identically on every machine of a team.

The rest of the tree is documentation and community infrastructure. There is a website directory, a netlify.toml for hosting it, a _redirects file, assets and scripts directories, a CHANGELOG.md, a CLAUDE.md, a .codacy.yml for code analysis, an .all-contributorsrc with a contributors:add script, and .node-version and .nvmrc pinning the Node version for contributors.

The documentation site, the changelog and the contribution guide are all reachable from the README, and the contributor list is maintained with the all-contributors tool.

Where the workflow is the wrong shape

The fit depends on how your team actually releases, and there are clear cases where this is the wrong tool.

For a single maintainer with one package and a simple pipeline, a pull request per release is overhead. The review step, which is the entire point, has only one reviewer, and the benefit the README describes, discussing the release with colleagues, does not exist. A conventional publish script in CI is less machinery.

The second case is a repository that does not use a version in package.json at all, or a monorepo where each package is versioned and released independently. The tool's model is one version for the project, and a pull request that updates a version and a changelog, which means the independent publishing case needs a different arrangement or several releases prepared separately.

The third case is a project where the changelog is written by hand as prose. The prepare step updates the changelog, and its usefulness depends on that changelog being generated from commits. If the file is a hand-maintained document, the tool is proposing an edit to a document that was never machine-generated, and you will be reverting it every time.

The fourth is a team that wants full automation and has decided that reviews are not worth the latency. Then a fully automatic releaser with a well-configured version detection is the right shape, and the pull request is an extra step between you and the registry.

On maintenance, the project is not archived and the last push was on 2026-06-10, with the three most recent releases in February, April and June 2026. The version is still 0.x, which in a tool that has to be trusted with your publishing credentials is a signal to read the configuration carefully rather than a detail to ignore.

Editorial conclusion

Adopt Ship.js if your team releases a package from more than one machine, if you want a human to approve the version and the changelog before anything reaches the registry, and if you are willing to give up a fully automatic publish. Do not adopt it for a solo project with one package and a simple pipeline, where an extra pull request is pure overhead, and do not expect it to replace a build and test system. Before you set it up, run npx shipjs setup on a branch, read the config file it generates, and confirm the version-detection rules match your commit convention, since the tool has to infer the next version from your history before it can open the pull request.

Frequently asked questions

How do I install Ship.js into a project?

Run npx shipjs setup. The interactive CLI installs Ship.js into your package and creates a tailored config file for your project, ship.config.js, so the release commands can find the version, changelog and publishing settings.

What is the difference between Ship.js and semantic-release?

semantic-release is described as a tool for fully automated version management and package publishing, while Ship.js gives more control by automatically creating a pull request before every release, so you can confirm the version, review which commits are shipping, edit the generated changelog, test the release candidate and build it.

How do I cancel a Ship.js release?

You close the pull request and delete the staging branch. While the pull request is open you can also add more commits to it, hold the release, build from the staging branch and test it manually before merging.

Can the release run in CI instead of on my machine?

Yes. You can run shipjs trigger manually on the base branch after the pull request is merged, or configure your CI service, with CircleCI given as the example, so the longest process runs on the CI service asynchronously instead of occupying your working environment.

What does the shipjs trigger step actually do?

It runs a final test, unit and end to end, releases the package to npm or elsewhere as you have configured it, creates a git tag for the version, and creates a release for that tag on GitHub. The licence is MIT and the last push was on 2026-06-10.

Official sources

  1. algolia/shipjs on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/algolia-shipjs.svg)](https://hysenlabs.com/projects/algolia-shipjs)

Was this useful?