<Semantic Release>

How to publish a new version of your software without breaking EVERYTHING

#VTCC10

C / C++ / C# / Java / CoffeeScript / JavaScript / Node / Angular / Vue / Cycle.js / functional

EveryScape

virtual tours

MathWorks

MatLab on the web

Kensho

finance dashboards

11 people. Atlanta, Philly, Boston, LA

Fast, easy and reliable testing for anything that runs in a browser

Use JavaScript

OSS on GitHub

Let people know

I do not distribute code directly from GitHub

Missing built artifacts: transpiled code

Mutable

Extra files

https://github.com/cypress-io/schema-tools

schema-tools@1.0.0.tgz

Artifact Repository

schema-tools

1.0.0

1.0.1

1.0.2

1.1.0

2.0.0

GitHub

https://github.com/cypress-io/schema-tools

schema-tools@1.0.0.tgz

Artifact Repository

schema-tools

1.0.0

1.0.1

1.0.2

1.1.0

2.0.0

GitHub

npm i schema-tools@1.0.1

https://github.com/cypress-io/schema-tools

schema-tools@1.0.0.tgz

Artifact Repository

schema-tools

1.0.0

1.0.1

1.0.2

1.1.0

2.0.0

GitHub

npm i schema-tools@1.0.1

Workshop

Store

Distribute via NPM

tip: reload page to get new funny definition of "NPM"

NPM

NPM registry

  • There are plenty high quality JS modules in the NPM registry
  • You can always write and publish your own NPM module

I ❤️ publishing NPM modules

I 🤢 large codebases

Software Failure

Each auto part is

  1. built separately

  2. tested separately

  3. has SKU number

  4. shipped to the auto plant

  5. placed into the car

Factoring out a piece of code

  • Creates clear boundary (API)

  • Forces documenting it

  • Allows iteration behind versions

Creating your own module

$ mkdir my-app
$ cd my-app
$ git init
$ npm init -y
$ npm init -y
Wrote to my-app/package.json
{
  "name": "my-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
{
  "name": "stop-build",
  "version": "0.0.0-development",
  "description": "Exits with non-zero code if there are modified Git files",
  "author": "Gleb Bahmutov <gleb.bahmutov@gmail.com>",
  "bugs": "https://github.com/bahmutov/stop-build/issues",
  "config": {
    "next-update": {
      "commands": {
        "pre-git": "./.git/hooks/pre-commit"
      }
    },
    "pre-git": {
      "commit-msg": "simple",
      "pre-commit": [
        "npm run pretest",
        "npm run deps",
        "npm run ban",
        "git add ."
      ],
      "pre-push": [
        "npm test",
        "npm run secure",
        "npm run license",
        "npm run ban -- --all",
        "npm run size"
      ],
      "post-commit": [],
      "post-merge": []
    }
  },
  "engines": {
    "node": ">=6"
  },
  "files": [
    "src/*.js",
    "!src/*-spec.js"
  ],
  "bin": {
    "stop-build": "src/index.js"
  },
  "homepage": "https://github.com/bahmutov/stop-build#readme",
  "keywords": [
    "build",
    "ci",
    "exit",
    "git",
    "utility"
  ],
  "license": "MIT",
  "main": "src/",
  "publishConfig": {
    "registry": "http://registry.npmjs.org/"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/bahmutov/stop-build.git"
  },
  "scripts": {
    "ban": "ban",
    "deps": "deps-ok",
    "issues": "git-issues",
    "license": "license-checker --production --onlyunknown --csv",
    "pretty": "prettier-standard 'src/*.js'",
    "prelint": "npm run pretty",
    "lint": "standard --verbose --fix src/*.js",
    "pretest": "npm run lint",
    "secure": "nsp check",
    "size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
    "test": "npm run unit",
    "unit": "mocha src/*-spec.js",
    "semantic-release": "semantic-release pre && npm publish && semantic-release post"
  },
  "devDependencies": {
    "ban-sensitive-files": "1.9.0",
    "deps-ok": "1.2.0",
    "git-issues": "1.3.1",
    "license-checker": "11.0.0",
    "mocha": "3.4.2",
    "nsp": "2.6.3",
    "pre-git": "3.15.0",
    "prettier-standard": "5.1.0",
    "semantic-release": "^6.3.6",
    "standard": "10.0.2"
  },
  "dependencies": {
    "ggit": "1.15.1",
    "pluralize": "5.0.0"
  }
}

package.json can be complex

Publishing your module

Looks good!

Publishing your module

NPM info

npm publish --tag alpha

Time to publish a new version

Time to publish a new version

Use a helper like `np`

npm i -g np

Use a helper like `np`

npm i -g np

Semantic versioning

major . minor . patch

break . feature . fix

1.4.2

  • major: I changed how it works  🔥😡

  • minor: I added a new feature    👏👍

  • patch: I fixed something            ✅❤️

really

Semantic versioning

Commit message convention

$ git commit -m "minor(app): add new feature"
$ git push
$ git commit -m "patch(log): fix log rotation"
$ git commit -m "major(api): change output format"
$ git push

new minor version published

new major version published

Use a helper like `next-ver`

npm i -g next-ver

Automate SemVer + publishing

$ npm install -g semantic-release-cli
$ semantic-release-cli setup
$ git commit -m "minor(app): add new feature"
$ git push

New minor version is published by your CI if tests pass

ALL my NPM modules use semantic-release

It sets tokens and writes CI file

ALL my NPM modules use semantic-release

It tags and formats release notes on GitHub

It even comments on issues when the fix or feature has been published!

ALL my NPM modules use semantic-release

1e0d8811 ....

8315cc0d ....

7fdc9acc ....

645ad6d6 ....

major

minor

patch

everything else

CI

WHAT is this commit?

publish

tag

deploy

Reactive semantic release (action)

`npm publish` very infrequently or never

`npm publish` A LOT

Optimize for iteration

`npm publish`:

quick look at security

Kik-gate

2016

  1. Guy has bunch of NPM modules including "kik" and "left-pad"
  2. Company Kik asks him to release "kik" name from NPM registry
  3. Guy refuses
  4. Kik goes to NPM Registry Co and Co gives "kik" name to Kik
  5. Guy gets angry and removes ALL his packages from NPM registry
  6. Bunch of packages depend on "left-pad" and stop working
  7. Guy #2 grabs released names AND PUBLISHES his code under same names and versions
  8. NPM Registry Co un-unpublishes original modules
  9. NPM Registry co changes its "unpublishing policy"

You cannot unpublish after 24 hours

before 24 hours - this version will be forever empty

If you push sensitive info to public GitHub or publish to NPM registry - it is out

Even unpublishing after 1 second is a RISK

All major NPM projects has some sensitive information leaked

NPM auth token, GitHub tokens, passwords, SSH keys, etc

Solution = prevention

ban-sensitive-files

eslint-gate

2018

  1. a bad person got access to an NPM account of a good person
  2. this bad person published a new version of a popular package
  3. this new version included code that tried to steal more NPM auth tokens of any package that installed it

Enable 2FA on NPM

Enable 2FA to PUBLISH by ✋

https://www.npmjs.com/settings/<username>/tfa

Turn on 2FA to publish PER PACKAGE

https://www.npmjs.com/package/<name>/access

$ npm publish --otp

OTP = One Time Password

- no per package tokens ☹️

- no 2FA for automated tools 😡

"S" in "NPM" stands for Security

How has the NPM registry worked without a MAJOR security attack so far?

What we do at Cypress.io

  1. All developers use 2FA to authenticate
  2. All developers have READ-ONLY access to packages (no one can publish anything, but can install)
  3. A special synthetic account has a WRITE access to packages
  4. That special account has 1 IP-restricted token to publish new versions from CI

Renovate App

$ cat renovate.json 
{
  "extends": ["config:base"],
  "automerge": true,
  "major": {
    "automerge": false
  }
}

1. Enable RenovateApp GitHub application

2. Add "renovate.json" file to repo

Simple semantic commit convention allows you to automate NPM release

Focus on the ​code, not on the release process

Keep your friends close

and your NPM tokens closer

<Semantic Release>

Dr Gleb Bahmutov, PhD

Thank you

for listening 👏

#VTCC10

Semantic Release

By Gleb Bahmutov

Semantic Release

Manually releasing new software versions is dangerous. You might forget to test it before releasing, or you might release a platform-specific version, instead of a general one. Moving the release to the CI and automating it is the key to the repeatable and robust release process. In this talk, I will show how to set up a completely automated yet robust release system with semantic versioning for JavaScript using NodeJS. Presented at VermontCodeCamp 2018.

  • 4,099