Deploy Atomically with Travis & npm
Tags: travis, npm, git
Published: 2017-10-31 13:28:38

---
title: Deploy Atomically with Travis & npm
published: true
description: Use your CI server to build and commit assets
tags: Travis, npm, git
---

# Deploy Atomically with Travis & npm

I think I am a software developer because I am lazy.

The second or third time I have to perform the same exact task, I find myself saying, “[Ugh, can’t I tell the computer how to do it?](https://xkcd.com/1319/)”Â 

So imagine my reaction when our team’s deployment process started looking like this:

1. `git pull`
1. `npm run build` to create the minified packages
1. `git commit -am "Create Distribution" && git push`
1. Navigate to GitHub
1. Create a new release

I was thinking steps 1–3 are easy enough to put in a shell script and steps 4–5 are probably scriptable, but is that all? What else needs to be done?

* The version in `package.json` was never getting updated and it would be nice to have that in synch with the GitHub release.
* Can this script be run after the CI build without having to task a human to manually run it?

## GitHub andÂ Releases
My first step to full automation was digging into the details of GitHub releases. For starters, tags and releases are not the same thing.

### Tags
[Tags are part of Git](https://git-scm.com/book/en/v2/Git-Basics-Tagging), the underlying version control system. They come in two types:

* Lightweightâ€Š–â€ŠA pointer to an existing commit
* Annotatedâ€Š–â€ŠA full object with name, timestamp and checksum hash

However, they do not contain any files or changes to the code in the repository.

### Releases
[Releases are a GitHub extension of tags](https://help.github.com/articles/about-releases/). They provide all the functionality of tags while also providing:

* A zipped archive of the codebase at that point in time
* Any associated release binaries (likeÂ `.jar` orÂ `.dll`)

### Does this fit our useÂ case?
Not 100%. In our case, the minified files had to exist within the repository as part of a Django plugin in a larger architecture. We couldn’t really take advantage of GitHub releases since their files exist outside the code base.

Also, there seems to be a chicken and egg problem with the `package.json` version. I don’t know what the version number is until I tag, but I can’t store any code changes with the tag.

## npm version to theÂ rescue
The clever folks at node already figured out this problem for me:

```
npm version [major | minor | patch] -m "message"
```

When running [the command](https://docs.npmjs.com/cli/version), npm will:

1. Read the current git tag
1. Bump the version in `package.json` according to the type of bump (major, minor or patch)
1. Run the `version` script indicated inside of `package.json`
1. `git add`
1. `git commit -m "message"`
1. `git tag <new tag number>`

Perfect! And if we run `npm run build` during the version script, the minified files will be packaged along with the updated `package.json`.

## That’s great, how do I get Travis to doÂ that?
We use Travis as our continuous integration server, but this step could be easily ported to CircleCI, AppVeyor or a number of other CI services.

### The default use case is forÂ Releases
If you start reading the Travis documentation, it is clear that their main use case is to build assets and push them to GitHub Releases. Since we already determined that isn’t for us, it was time to start experimenting and Googling.

### NaÃ¯ve FirstÂ Steps
In `package.json`, I wrote the version script to build the minified assets:

```javascript
"scripts": {
   ...
   "version": "npm run build && git add ."
   "postversion": "git push && git push --tags"
}
```

In `travis.yml`, I had it call `npm version` before deploying:

```yaml
before_deploy:
- npm version patch
deploy:
  provider: releases
  skip_cleanup: true
  on:
    branch: master
```

And then ran it in Travis:

```
> foo@0.6.2 postversion /home/travis/build/foo/bar
> git push && git push --tags
fatal: You are not currently on a branch.
To push the history leading to the current (detached HEAD)
state now, use
    git push origin HEAD:<name-of-remote-branch>
```

### Detached HeadÂ Mode
After a bit of Googling, it turns out that [Travis always runs in detached HEAD mode](https://github.com/travis-ci/travis-ci/issues/1701). It’s sort of a safety measure, but really puts a dent in how this auto deployment is supposed to work.

I may be lazy, but I like a challenge.

Basically, we have to have Travis perform the version steps at the tip of `master` and then push it to our branch. Wait, how is it going to have the credentials to do that?

### API Key
Usually, Travis is performing read-only actions on the repository. When it has to write back to the repository, it needs to know which account to use. We could specify the credentials in the `travis.yml` file, but then it would be exposed to everyone who views the file online. Doing this properly and safely takes the following (one time) setup:

1. Create a new token at https://github.com/settings/tokens
1. Copy the string of characters
1. Open the settings in Travis
1. Create a new environment variable `GITHUB_API_KEY`
1. Paste the characters in the value and make sure to uncheck “Display value in the build log”

Ok, so the credentials can be exchanged, but we still haven’t fixed the detached head problem.

## Writing a TravisÂ Script
A few Google search results ([1](https://gist.github.com/willprice/e07efd73fb7f13f917ea), [2](https://github.com/JemsFramework/di/blob/trunk/.travis.yml)) showed me that writing a shell script which gets executed by Travis was the solution to the detached head problem.

#### `.travis.yml`

{% gist https://gist.github.com/JeffreyMFarley/a06401893087c65fd8a6d8166f72e4c8 %}

#### `.travis/pu.sh`

{% gist https://gist.github.com/JeffreyMFarley/d8d736abbe100222e87df2b78a8c7ef4 %}

This does everything we set out to do:

1. Builds the minified files
1. Increments the version in `package.json`
1. Creates a new tag
1. Pushes it back to the repository
1. Which triggers another Travis build that…

oh no!

## Avoiding the InfiniteÂ Loop
Luckily, Travis has a way of saying “don’t run”. Change line 31 to say:

`npm version patch -m "chore: release version %s [skip ci]"`

Whenever it encounters a commit message that contains `[skip ci]`, Travis will not run.

## Next Steps
Wouldn’t it be nice if Travis could also update `CHANGELOG.MD` too?