Nike / NikeLab / Free Rift Sandal / Shoes / 2015
WordPress Form Plugin Review – Fluent Forms (Lifetime Deal)
Easy Automatic npm Publishes
One common question from people using npm to publish, especially on CI systems, is how best to automate the process, especially when dealing with multiple branches.
For a while now, I’ve been using a pattern that takes almost all of the human interaction out of it, since I tend to mess stuff up when I type it with my fingers. This works well for automatic publishes from CI or when publishing manually from a terminal.
I haven’t manually typed
npm publish
in a while, which is a good thing.First things first, have good tests
I am a huge fan of running tests with 100% test coverage. It isn’t a perfect guard against every problem out there, but it does keep me from doing stupid things, like assuming that I know what my program does.
My go-to test library is
tap
, but you can do this with any testing library that supports code coverage. If it doesn’t support code coverage out of the box, you can usenyc
to run any Node.js process with coverage tracking.To use it, run
npm i tap -D
, and then add this to yourscripts
section in package.json:{ "scripts": { "test": "tap" }, "tap": { "check-coverage": true } }
The
npm version
CommandThe
npm version
command will figure out what the next version should be, edit your package.json file, and even check it into git with a signed tag. The beauty of this is that it also prevents you from bumping the version when you have untracked changes in your git working directory, and provides some hook scripts that can do other things before or after the version bump.In the
scripts
section of mypackage.json
file, I add apreversion
script that runs my tests:{ "scripts": { "preversion": "npm test", "test": "tap" }, "tap": { "check-coverage": true } }
Now, before I can bump the version, npm will make sure that my tests pass. If the test fails (or if coverage isn’t 100%), then it’ll fail and the version command fails.
Publishing on Version Change
Bumping the version is fine, but then it’s time to share it. Along with
preversion
, thepostversion
command does actions after the version is bumped. So, let’s hook onto that to publish the package.{ "scripts": { "postversion": "npm publish", "preversion": "npm test", "test": "tap" }, "tap": { "check-coverage": true } }
Keeping Git and npm in Sync
That’s fine for pushing to npm, but then I have to remember to push the changes to git. (I have many times forgotten to do this, and gotten issues because the code on npm is not in GitHub, which is generally a bad sign.)
Thankfully, npm also gives us a way to hook a script around the
publish
event, so let’s use that:{ "scripts": { "postpublish": "git push origin --all; git push origin --tags", "postversion": "npm publish", "preversion": "npm test", "test": "tap" }, "tap": { "check-coverage": true } }
This runs two commands. The first pushes all branches, and the second pushes all the tags (including my newly published version tag).
Branches and Dist-Tags
Occasionally, I’ll find myself working on some big feature for a new release that is not yet ready for prime time.
In the feature branch, I’ll modify the scripts by adding a
--tag
argument to the npm publish command to put it on a dist-tag other thanlatest
.{ "scripts": { "postversion": "npm publish --tag=next", "postpublish": "git push origin --all; git push origin --tags", "preversion": "npm test", "test": "tap" }, "tap": { "check-coverage": true } }
Now, I can tell people to run
npm install my-module@next
to try out the new prerelease version.On the other side, I might want to land a bugfix or backport a feature for a legacy version. To do that, I create a git branch with the old version, and update
package.json
to add alegacy
tag instead.{ "scripts": { "postversion": "npm publish --tag=legacy", "postpublish": "git push origin --all; git push origin --tags", "preversion": "npm test", "test": "tap" }, "tap": { "check-coverage": true } }
Bonus Points: Sign Your Versions
Git has support for PGP signing tagged commits. To tell npm to take advantage of this, set these two config values:
npm config set sign-git-commit true npm config set sign-git-tag true
If setting up PGP and hooking it up with Git is too much of a pain, you’re not alone. I’m a nerd who’s been computering for a very long time, and I can’t handle it. Also, I’m always worried about my keys just sitting on my machine in text files, even if they are encrypted with a passphrase. And if they are encrypted with a passphrase, then I have to type it in all the time, and that’s just too much work.
I’m a huge fan of Krypton. It stores your PGP and SSH private keys in your mobile device’s secure storage vault, and then sends a push notification to allow it to do things with those keys. It’s dead-simple to set up, and extremely easy to use, and gives you a hardware second factor for everything that matters.
Of course, and I don’t exactly know if this is a bug or a feature, it does mean that whenever I run
npm version
, between the commit, the signature, the tag, and the two SSH connections, my phone does a lot of buzzing.Running
npm version
to Test and PublishFrom there, I use the
npm version
command to do all my publishing. For bug fixes, I runnpm version patch
. For new features, I runnpm version minor
. For breaking changes, I runnpm version major
.If you use Conventional Commits or similar tools, you could even automate the detection of what kind of version bump it should be, though that’s left as an exercise for the reader.
This approach of using npm scripts to automate the process works well with any system where you’ll be publishing and committing. Set it up in your next project, and trust your human fingers a little bit less :)
PS: npm Configuration is Very Flexible
You’ll note that I did
--tag=
in the publish commands above. You can also configure npm in many other ways. Any configuration value (includingtag
in the case ofnpm publish
) can be set:
- explicitly on the command line, like
--tag=whatever
- in the environment, like
NPM_CONFIG_TAG=whatever
- in a
.npmrc
file in the root of your project, liketag = whatever
- in a
.npmrc
file in your home directory- in
/usr/local/etc/npmrc
(or/usr/etc/npmrc
on some systems).This works inheritance-style, so the higher up on that list a setting is, the higher the priority.
<
p>For CI/CD systems, this means that you can sometimes set environment variables to control the behavior of npm commands, without having to change the code or inject files into places. If it’s easier to control it with a file (for example, checking a
.npmrc
file into git), then that’s also fine.
Next Generation Package Management
What if installs were so fast they could happen in the background, just by using Node? What if every file in your dependencies could be guaranteed to be bit-by-bit identical to what’s on the registry? What if working on a new project was as simple as clone and run? What if your build tools got out of your way?
Introducing
tink
, a proof of concept implementation of an install-less installer.
tink
acts as a replacement for Node.js itself, working from your existingpackage-lock.json
. Try it out on a project without anode_modules
and you’ll find that you can stillrequire
any of your dependencies even though you never ran an install. The first run will take a few seconds while it downloads and extracts the package tarballs, but subsequent runs are nearly instantaneous even though it’s still verifying that everything in yourpackage-lock.json
is on your system.The first thing you’ll notice is that none of those modules are put into your
node_modules
folder — in fact, the only thing you’ll find there is a.package-map.json
file. This contains hashes of all of the files in every package you have installed. These are verified before they’re loaded, so you can have confidence that you’re getting what you asked for (if a verification fails then the file is fetched from its original source, all transparently).We’re not throwing the baby out with the bathwater though. You can still install things in your
node_modules
folder and those versions will be used in preference to the cached version. This opens a path to live-editing of dependencies (sometimes a necessary debugging technique) and support for things like postinstall scripts that mutate the package distribution.
tink
is an opportunity to change how we relate to our Node.js projects and the npm Registry. Should usingrequire
orimport
on a module not in yourpackage.json
just add it to yourpackage.json
? Should extremely popular features like babel-compatible esm, typescript, or jsx be available by default? These are the questions we’ve been asking ourselves and we’d love to hear what you would want from a next generation experience. Come by npm.community and let us know!
HEERA.IT turned 6 today!
Introducing Aurelia from EisenbergEffect on Vimeo.
Robots Are Learning to Fake Empathy
Emotional intelligence is a cornerstone of human interactions—an essential part of what it means to be human. But now, artificial intelligences are being developed to better read and process human emotions, which is already changing the way we interact with robots.