Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save koshuang/0ba921cf4a542726d52e4622b6393eab to your computer and use it in GitHub Desktop.
Save koshuang/0ba921cf4a542726d52e4622b6393eab to your computer and use it in GitHub Desktop.
An opinionated CircleCI configuration for Lerna + Yarn-powered NPM modules.
# ---------------------------------------------------------------------------- #
# CI INSTRUCTIONS #
# ~~~~~~~~~~~~~~~ #
# This configuration is optimized for continuous delivery of NPM packages #
# using Lerna (fixed version) + Yarn. #
# ---------------------------------------------------------------------------- #
# #
# 1) Install & initialize Lerna: `yarn add -D lerna && lerna init` #
# #
# #
# 2) Publish the initial version of your NPM packages manually, ensuring the #
# correct org scope and settings: `lerna publish` #
# #
# #
# 3) Environment variables required in CircleCI: #
# #
# $NPM_TOKEN -- NPM publishing auth token. #
# $GITHUB_REPO_TOKEN -- GitHub repo-scoped auth token (for use with GREN). #
# #
# #
# 4) The following configuration should be added in your root `package.json`: #
# #
# { #
# "workspaces": [ #
# "packages/*" #
# ] #
# } #
# #
# #
# 5) The following configuration should be added to your `lerna.json`: #
# #
# { #
# "npmClient": "yarn", #
# "useWorkspaces": true #
# } #
# #
# #
# 6) The following branches should be created & protected on GitHub: #
# ^^^^^^^^^ #
# master -- Production code (currently published NPM version). #
# next -- Pre-release code (published under the `next` tag on NPM). #
# development -- Work-in-progress code (not published). This should be set #
# as the default branch! #
# #
# #
# 7) The following scripts should be created in each lerna package's own #
# `package.json`: #
# #
# lint -- Run a linter against source files. #
# build -- Build output required for publishing to NPM. #
# test -- Run unit/integration/e2e tests. #
# #
# #
# 8) Ensure the aliases for `&dependency-paths` and `&build-output-paths` #
# below properly reflect the dependency and output directories of your #
# app or module. #
# #
# #
# 9) [OPTIONAL] Configure GREN to your liking using `.grenrc`. #
# #
# See: https://github.com/github-tools/github-release-notes #
# #
# ---------------------------------------------------------------------------- #
version: 2.1
# --- YAML Aliases ----------------------------------------------------------- #
aliases: [
# List of dependency paths that should be persisted to the
# CircleCI workspace.
&dependency-paths [
"node_modules",
"packages/my-package/node_modules",
],
# List of build output paths that should be persisted to the
# CircleCI workspace.
&build-output-paths [
"packages/my-package/dist",
],
# Yarn lockfile cache key (update "vN" => "vN+1" to cache-bust).
&dependency-cache-key "v1-dependency-cache-{{ checksum \"yarn.lock\" }}",
&workspace-root "/home/circleci/project",
&attach-workspace {
attach_workspace: {
at: *workspace-root
}
},
# Filter pull requests not in "master" or "next" (development code)
&filter-default-branches {
filters: {
branches: {
ignore: "/^master$|^next$/"
}
}
},
# Filter pull requests in "master" only (production code).
&filter-release-branches-only {
filters: {
branches: {
only: "master"
}
}
},
# Filter pull requests in "next" only (pre-release code).
&filter-prerelease-branches-only {
filters: {
branches: {
only: "next"
}
}
},
]
# --- Executor definitions --------------------------------------------------- #
executors:
default:
docker:
- image: circleci/node:10-browsers
# --- Job definitions -------------------------------------------------------- #
jobs:
# Installs Node dependencies via Yarn, caches them, then persists
# to the workspace.
install-dependencies:
executor: default
steps:
- checkout
- *attach-workspace
- restore_cache:
key: *dependency-cache-key
- run:
name: Install Module Dependencies
command: yarn install --frozen-lockfile
- run:
name: Lerna Bootstrap
command: npx lerna bootstrap
- save_cache:
paths: *dependency-paths
key: *dependency-cache-key
- persist_to_workspace:
paths: *dependency-paths
root: *workspace-root
# Runs the linter against relevant source files.
lint:
executor: default
steps:
- checkout
- *attach-workspace
- run:
name: Lint source files
command: npx lerna run lint
# Builds modules and persists the build output to the workspace.
build:
executor: default
steps:
- checkout
- *attach-workspace
- run:
name: Build modules
command: npx lerna run build
- persist_to_workspace:
paths: *build-output-paths
root: *workspace-root
# Run unit/integration/e2e tests.
test:
executor: default
steps:
- checkout
- *attach-workspace
- run:
name: Run tests
command: npx lerna run test
# Publish the packages to NPM. This should depend on the `build` job.
create-release:
executor: default
steps:
- checkout
- *attach-workspace
- run:
name: Authenticate with registry
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
- run:
name: Publish packages to NPM
command: npx lerna publish from-package --yes
# Publish the packages as a pre-release versions to NPM. This should depend on
# the `build` job.
create-prerelease:
executor: default
steps:
- checkout
- *attach-workspace
- run:
name: Authenticate with registry
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
- run:
name: Publish pre-release packages to NPM
command: npx lerna publish from-package --canary --yes
# Create a git tag for this release and push to the remote repository.
tag-release:
executor: default
steps:
- checkout
- *attach-workspace
- run:
name: Git tag the release with the `lerna.json` version number
command: |
PACKAGE_VERSION=$(node -pe "require('./lerna.json')['version']")
git tag v$PACKAGE_VERSION
- run:
name: Push git tag to the remote repository
command: |
PACKAGE_VERSION=$(node -pe "require('./lerna.json')['version']")
git push -q https://[email protected]/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME.git v$PACKAGE_VERSION
# Create release notes on GitHub using the `github-release-notes` package.
create-release-notes:
executor: default
steps:
- checkout
- *attach-workspace
- run:
name: Install github-release-notes package
command: yarn add -D -W github-release-notes
- run:
name: Generate release notes and publish to GitHub
command: npx gren release --override --token $GITHUB_REPO_TOKEN
# Create release notes for a prerelease version on GitHub using the
# `github-release-notes` package.
create-prerelease-notes:
executor: default
steps:
- checkout
- *attach-workspace
- run:
name: Install github-release-notes package
command: yarn add -D -W github-release-notes
- run:
name: Generate release notes and publish to GitHub
command: npx gren release --override --prerelease --token $GITHUB_REPO_TOKEN
# --- Workflow definitions --------------------------------------------------- #
workflows:
# Builds modules, verifies code with the linter, and runs unit tests.
pull-request:
jobs:
- install-dependencies: *filter-default-branches
- build:
requires:
- install-dependencies
- test:
requires:
- install-dependencies
- lint:
requires:
- build
# Builds modules, verifies code with the linter, runs unit tests, and
# publishes the built packages to NPM.
publish-to-npm:
jobs:
- install-dependencies: *filter-release-branches-only
- build:
requires:
- install-dependencies
- test:
requires:
- install-dependencies
- lint:
requires:
- build
# Manual approval step as a final gatekeeper to prevent
# possible mistakes!
- confirm-release:
type: approval
requires:
- build
- test
- create-release:
requires:
- confirm-release
- lint
- tag-release:
requires:
- confirm-release
- create-release
- create-release-notes:
requires:
- confirm-release
- tag-release
# Builds modules, verifies code with the linter, runs unit tests, and
# publishes a pre-release version of the built package to NPM.
publish-prerelease-to-npm:
jobs:
- install-dependencies: *filter-prerelease-branches-only
- build:
requires:
- install-dependencies
- test:
requires:
- install-dependencies
- lint:
requires:
- build
- create-prerelease:
requires:
- build
- test
- lint
- tag-release:
requires:
- create-prerelease
- create-prerelease-notes:
requires:
- tag-release
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment