Opinionated CLI for continuous delivery of npm packages.
- Installation
- Overview
- Recipes
- CLI
dripip get-current-commit-version
dripip get-current-pr-num
dripip help [COMMAND]
dripip log
dripip pr
dripip preview
dripip preview-or-pr
dripip stable
npm install --save-dev dripip
Dripip is a command line interface (CLI) for continuously releasing npm packages. It has support for three kinds of releases: pull-request releases, canary releases, stable releases. It builds on top of Git, Semver, Conventional Commit, and GitHub. Support for alternative version control systems (e.g. Darcs) and platforms (e.g. GitLab) are not currently supported but they probably could be. The concepts of dripip are relatively general.
Continuous delivery means that every single meaningful commit to your library will be released. The benefit of this approach is that when you ship value your users get access to it immediately, and as an author you don't need to manage incoming queries like "when will this be released?". You get to to auto-reply: "It already it! :)".
Pull-request releases occur on, surprise, pull-requests. You can have CI run them or do them ad-hoc from your machine, either workflow is fine (but choose one, as mixing will naturally lead to already-published errors). These kinds of releases are useful when link workflows are not good enough. For example your feature is a CLI that you want to make sure plays well with npx. Unlike other release types pull-request releases do not result in git tags. Instead, Dripip uses the npm registry to maintain its release state. The version pattern of a pull-request release is:
0.0.0-pr.${pr_num}.${pr_release_num}.${short_sha}
For example:
0.0.0-pr.45.4.9a3b2c1
The 0.0.0
version is used because there is no meaningful semantic version to put here. It cannot be the version you're branching from since this version syntax would indicate it is a pre-release for that version, which it isn't, it's the opposite, a release after it; It cannot be the version you're going towards because it cannot be generally known if a PR will result in a patch minor or major semver change; Even if Dripip tried to apply conventional commit analysis to the PR commits it wouldn't account for concurrent PRs and the generally async nature that PRs live in with regard to one another. So ultimately 0.0.0
is the most sensible general choice.
The pr_num
part is the pull-request number. The same one that you see on the GitHub UI, URL etc. of a pull-request.
The pr_release_num
is a monotonically increasing 1-based (starts from 1, not 0) integer. It serves a few purposes. It provides orientation for humans at a glance, like how many releases has a PR had or where does a given release fall within the PR release set. Its functional purpose is to support correct lexical sorting. Without this little number it would be impossible to sort PR releases without some kind of additional metadata e.g. publish time. Thanks to this, when you run e.g. npm versions
, you get an accurate ordering.
The short_sha
is what you see next to commits in much of the GitHub UI, including PR pages. Its primary purpose is to make it easy for you to tie a release back to something in your Git history. For example when looking at a PR page you can copy-paste the sha into search to find the exact commit for that release. Whatever the particular, this is just a convenient piece of information for you. Ultimately we developers practice many a crude workflow, habits (console.log vs debugger anyone?).
When Dripip makes a pr release, it includes an upsert of a dist-tag of pattern pr.${pr_num}
. This makes it very easy to install the latest published version for a given pull-request.
Canary releases occur on trunk branches. These releases should be automated by your CI. They give your users access to what your stable version will become. The version pattern of a canary release is:
${next_stable}-next.${series_release_num}
For example:
1.2.0-next.4
The next_stable
is whatever your upcoming stable version will be. The series_release_num
is a monotonically increasing 1-based (starts from 1, not 0) integer. The first canary release after a stable release starts at 1
, increasing by 1 at each canary release, until a stable is cut at which point the numbering is reset upon the next canary release.
When Dripip makes a canary release, it includes an upsert of a dist-tag called next
. This makes it very easy to install the bleeding edge of your package. Additionally, a git tag called next
is also maintained. Whatever commit the next
tag is on is the same instance of your package that would be installed if a user did npm install <your package>@next
.
Stable releases occur on trunk branches. The version pattern of a stable release is just a regular version, whatever the next stable is:
${next_stable}
For example:
1.2.0
These releases should be managed however your team works, of course, but here are some general tips:
- Think very carefully before automating stable releases. You need to have very very high confidence in your/your team's git commit message practices to take this route. One wrong
BREAKING CHANGE
line can be all it takes to make an accidental major release that npm won't allow you to undo (unless you catch very quickly). - Releases on a cadence, such as every week or two, either manually or by automation, is often a good way to go.
- Do not publish stables too often. It dillutes their meaningfulness and creates churn for users of tools like Renovate and Dependabot. Its probably better for your users to get one semi-exciting PR to update their dep on your package once every week or two than once a day! This approach also plays better with the GitHub releases UI where only a handful of releases are rendered for every page and even less visible without scrolling.
When Dripip makes a canary release, it includes an upsert of a dist-tag called latest
. This is actually what npm already does by default. Dripip does not deviate here.
Typically when an npm package is released its package.json version
field will be updated and committed into version control. This is a bad match for continuous delivery however, because it means that, more or less, half of the git history will become meta commits. To solve this, dripip
takes a novel approach of keeping the version out of version control. It uses Git tags to store that state, and when publishing your package, will set the package.json version
field right before publishing, and then unset it right after publishing completes. You notice nothing, your users notice nothing, and your git history looks ideal. This is unorthodox, but it works well.
Having a valid semver value in the version
field is required by npm. Dripip puts the following value into your project's package.json to satisfy that constraint. This is what you should check into version control.
0.0.0-dripip
Dripip generates release notes for canary and stable releases. Notes are published within GitHub releases. Canary release notes are published under the next
tag. Stable release notes are published under its respective version tag. Stable release notes are stable, while Canary release notes are always changing. When a stable release is cut, the canary release notes are cleared since next
becomes the same as stable, until the next Canary is published.
Because the canary release notes have a stable tag, it means you and your uses can always check out what's coming in the next stable version by visiting:
https://github.com/<org>/<repo>/releases/tag/next
For example checkout what's coming up in dripip right now by visiting https://github.com/prisma-labs/dripip/releases/tag/next.
There is a Dripip GitHub action.
It has these prerequisites:
- Have
dripip
installed as a dev dependency - Upload an
NPM_TOKEN
to your repo (gh docs)
It can be used like this:
name: trunk
on:
push:
branches: [main]
jobs:
release-canary:
runs-on: ubuntu-latest
steps:
- uses: prisma-labs/dripip@master
with:
npmToken: ${{secrets.NPM_TOKEN}}
githubToken: ${{secrets.DRIPIP_GITHUB_TOKEN}}
If the action does not work for your use-case please open an issue about what your needs are. Meanwhile look at the
action.yml
source and inline a variant of it that works in your own GitHub workflow.
A nice way to do stable releases is use GitHub workflow dispatch. This keeps stable releases manual but moves the usage onto GitHub actions via Workflow Dispatch UI. This has the following benefits:
- Anyone on your team can cut a stable release without need for working local setup.
- Every stable release has an auditable CI run associated to it.
It can be done like this:
# .github/workflows/release.yml
name: Release
on: workflow_dispatch
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: prisma-labs/dripip@master
with:
isStable: true
npmToken: ${{secrets.NPM_TOKEN}}
githubToken: ${{secrets.DRIPIP_GITHUB_TOKEN}}
dripip get-current-commit-version
dripip get-current-pr-num
dripip help [COMMAND]
dripip log
dripip pr
dripip preview
dripip preview-or-pr
dripip stable
USAGE
$ dripip get-current-commit-version
OPTIONS
-r, --optional Exit 0 if a version for the commit cannot be found
See code: dist/cli/commands/get-current-commit-version.ts
USAGE
$ dripip get-current-pr-num
OPTIONS
-r, --optional Exit 0 if a pr number cannot be found for whatever reason
See code: dist/cli/commands/get-current-pr-num.ts
display help for dripip
USAGE
$ dripip help [COMMAND]
ARGUMENTS
COMMAND command to show help for
OPTIONS
--all see all commands in CLI
See code: @oclif/plugin-help
USAGE
$ dripip log
OPTIONS
-j, --json format output as JSON
-m, --markdown format output as Markdown
See code: dist/cli/commands/log.ts
USAGE
$ dripip pr
OPTIONS
-d, --dry-run output what the next version would be if released now
-j, --json format output as JSON
USAGE
$ dripip preview
OPTIONS
-d, --dry-run output what the next version would be if released now
-j, --json format output as JSON
-n, --build-num=build-num Force a build number. Should not be needed generally. For exceptional cases.
--skip-npm skip the step of publishing the package to npm
--trunk=trunk State which branch is trunk. Defaults to honoring the "base" branch setting in the GitHub
repo settings.
USAGE
$ dripip preview-or-pr
See code: dist/cli/commands/preview-or-pr.ts
USAGE
$ dripip stable
OPTIONS
-d, --dry-run output what the next version would be if released now
-j, --json format output as JSON
--skip-npm skip the step of publishing the package to npm
--trunk=trunk State which branch is trunk. Defaults to honoring the "base" branch setting in the GitHub repo
settings.