Skip to content

πŸ”˜ Generate composable, shareable CircleCI configs

Notifications You must be signed in to change notification settings

deecewan/circle-config-creator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

51 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

circle-config-creator

A simple helper to create CircleCI configs

NPM Version CircleCI Build Status code style: prettier Blazing Fast

Overview

I noticed that a lot of my CircleCI config was repetitive. I had written a small lib to help generate the config, but it got messy quickly. So I thought it through a little more and wrote a decent config generation tool. This is the result of that.

It allows the generation of workflows and jobs for CircleCI 2.0, and the sharing of parts of config.

Installation

This lib is written in JS. I'd recommend adding it to your devDependencies so that everyone on your team can use it.

yarn add -D circle-config-creator

Usage

  • create a file to generate your config. I use .circleci/config.js.
  • create a Config, Workflow and Job to get started
    • see API for details
import Config, { Workflow, Job, executors } from 'circle-config-creator';

const buildContainer = new executors.Docker('circleci/node:latest');
const config = new Config();

const build = new Job('build')
  .executor(buildContainer)
  .checkout()
  .run('./my-build-script.sh')
  .saveCache('v1-repo-{{ .Revision }}', '~/project');

const test = new Job('test')
  .executor(buildContainer)
  .restoreCache('v1-repo-{{ .Revision }}')
  .run({ command: './tests.sh', workingDirectory: '~/project/test' });

const workflow = new Workflow('build-and-test')
  .job(build)
  .job(test, [build]);

config
  .workflow(workflow)
  .writeSync();
  • Ensure you add Config#write or Config#writeSync to put your config into .circleci/config.yml

API

Methods are designed to build out configs in a chain. All chainable methods return a copy (operations are immutable).

Config

constructor() => Config;


workflow(workflow: Workflow) => Config

Add a workflow to the config


location(directory: string) => Config

Params

Name Type Default Description
directory string (required) The project directory this config is for. Saves to .circleci/config.yml of that directory.

Change the location that the config will be saved to. This will always save to the directory you pass in, in the .circleci/config.yml file. Defaults to __dirname (the current directory)


compose() => Object

Generate a JavaScript object based on the Jobs and Workflows added


dump() => string

Generate yaml from the Jobs and Workflows added


write(disclaimer: boolean, callback: ?((?ErrnoError) => mixed)) => Promise<void>

Write the config to .circleci/config.yml

Params

Name Type Default Description
disclaimer boolean true Add a disclaimer to the top of the generated file, warning that changes will be overwritten.
callback (optional) (?ErrnoError) => any undefined Node-style callback for write completion

writeSync(disclaimer: boolean) => void

Write the config synchronously to .circleci/config.yml

Params

Name Type Default Description
disclaimer boolean true Add a disclaimer to the top of the generated file, warning that changes will be overwritten.

Workflow

constructor(name: string) => Workflow


job(
  job: Job,
  requires: ?Array<Job>,
  filter: ?Branches,
  type: ?'approval', // Deprecated - use [Job](#job)#type
  context: ?string,
) => Workflow

Add a job to this workflow

Params

Name Type Default Description
job Job (required) The job to add to the workflow
requires (optional) Array<Job> [] Any jobs that this job requires
filter (optional) Branches undefined Filter the branches that this job runs for
type (optional) oneOf('approval') undefined The job type. See CircleCI config docs for more (Deprecated - use Job#type)
context (optional) string undefined The context of the job. See CircleCI config docs for more

schedule(cron: string, filter: Branches) => Workflow

Add a schedule to run this job against (see triggers on CircleCI docs)

Params

Name Type Default Description
cron string (required) The cron string to run this job on
filter Branches (required) The branches to run this schedule against

Job

constructor(name: string) => Job


updateName(name: string) => Job

Update the name of a job. Useful if you want to take an existing job and modify it slightly to create a new job from it.

Params

Name Type Default Description
name string (required) The shell to run this job in

shell(shell: string) => Job

CircleCI Docs

Change the shell this job runs with

Params

Name Type Default Description
shell string (required) The shell to run this job in

workingDirectory(directory: string) => Job

CircleCI Docs

Params

Name Type Default Description
directory string (required) The directory this job runs in

parallelism(p: number) => Job

CircleCI Docs

Params

Name Type Default Description
p number (required) The number to run in parallel

executor(executor: Executor) => Job

CircleCI Docs

The executor to run the job in. This must be set.

Params

Name Type Default Description
executor Executor (required) The executor to run this job in

environment(environment: { [key: string]: string }) => Job
environment(key: string, value: string) => Job

CircleCI Docs

This will append to any environment you've already added. Callable multiple times.

Params

Name Type Default Description
environment { [string]: string } (required) A map of environment variables to inject into the job
key string (required) The environment key
value string (required) The environment value

branches(branches: Branches) => Job

CircleCI Docs

The branch filter config to apply to this job. Note that this will apply at the workflow level, not at the job level. See CircleCI Docs. You can take advantage of immutability by settings branches on a job as you pass it in to the workflow. This was, you have one Job, and can set branches differently per-workflow.

Params

Name Type Default Description
branches Branches (required) The branch config for this job to run inside of

resourceClass(resourceClass: 'small' | 'medium' | 'medium+' | 'large' | 'xlarge') => Job

CircleCI Docs

Params

Name Type Default Description
resourceClass string (required) The resource class for this job's container

run(command: string) => Job
run(config: {
  background?: boolean,
  command: string,
  environment?: { [string]: string },
  name?: string,
  noOutputTimeout?: string,
  shell?: string,
  when?: 'always' | 'on_success' | 'on_fail',
  workingDirectory?: string,
}) => Job

CircleCI Docs

See the docs for the param meanings


checkout(path: ?string) => Job

CircleCi Docs

Params

Name Type Default Description
path (optional) string ~/project The path to checkout the code to

setupRemoteDocker(dockerLayerCaching: boolean = false) => Job

CircleCI Docs

Params

Name Type Default Description
dockerLayerCaching (optional) boolean false Enable docker layer caching

saveCache(
  key: string,
  paths: string | Array<string>,
  name: string = 'Saving Cache',
  when: 'always' | 'on_success' | 'on_fail' = 'on_success',
) => Job

CircleCI Docs

Params

Name Type Default Description
key string (required) The key to save the cache to
paths string Array (required)
name string 'Saving Cache' The message to display when this step is running
when oneOf('always', 'on_success', 'on_fail') 'on_success' When to save this cache. Defaults to saving when the job is successful

restoreCache(key: string | Array<string>, name: string = 'Restoring Cache') => Job

CircleCI Docs

Params

Name Type Default Description
key string (required) The keys to (attempt) to restore from
name string 'Restoring Cache' The message to display when this step is running

progressiveRestoreCache(key: string, base: ?string) => Job

Experimental: This will likely not work for everyone's existing config. It works for most of my use-cases, and it's super handy.

It makes use the CircleCI ability to fallback on caches. It takes the key you pass in, and sets the restoreCache job with each key, falling back to base ( which defaults to two chunks). It only splits on -s.

Example: with a key of v1-dependencies-{{ checksum "yarn.lock" }}, this will result in attempting to restore the cache in the following order:

  • v1-dependencies-{{ checksum "yarn.lock" }}
  • v1-dependencies-

If you have a different base, you can specify it. For example, calling

job.progressiveRestoreCache(
  'v1-yarn-deps-{{ checksum "yarn.lock" }}-{{ .Revision }}',
  'v1-yarn-deps'
)

Will result in trying to restore the following

  • v1-yarn-deps-{{ checksum "yarn.lock" }}-{{ .Revision }}
  • v1-yarn-deps-{{ checksum "yarn.lock" }}-
  • v1-yarn-deps-

Params

Name Type Default Description
key string (required) The full key to try restoring from
base (optional) string (see examples) The base key of the cache

deploy(command: string) => Job
deploy(config: {
  background?: boolean,
  command: string,
  environment?: { [string]: string },
  name?: string,
  noOutputTimeout?: string,
  shell?: string,
  when?: 'always' | 'on_success' | 'on_fail',
  workingDirectory?: string,
}) => Job

CircleCI Docs

See Job#run and the related CircleCI docs for param information.


storeArtifacts(path: string, destination: ?string) => Job

CircleCI Docs

Params

Name Type Default Description
path string (required) The directory to save as build artifacts
destination (optional) string undefined Prefix added to the artifact paths in the artifacts API

storeTestResults(path: string) => Job

CircleCI Docs

Params

Name Type Default Description
path string (required) The path to the test results

persistToWorkspace(root: string, paths: string | Array<string>) => Job

CircleCI Docs

Params

Name Type Default Description
root string (required) An absolute path or one relative to the working_directory
paths string Array (required)

attachWorkspace(at: string) => Job

CircleCI Docs

Params

Name Type Default Description
at string (required) The directory to attach the workspace at

addSSHKeys(fingerprints: ?(string | Array<string>)) => Job

CircleCI Docs

Params

Name Type Default Description
fingerprints (optional) string Array (all SSH keys)

type(type: 'approval') => Job

CircleCI Docs

This is technically a configuration setting for a Workflow, but is used in the workflow config as a job. If you apply this setting to a job, it will not appear in your Job list (so it makes no sense to give it any other configuration options). Jobs with a type of approval also do not need to have an executor set.

At this point, there is only one type supported by CircleCI: approval.

Params

Name Type Default Description
type 'approval' (required) The type for the job

Executors

Executors are the environment in which your job will run. Every job must be assigned exactly one environment. The generator will throw an error if none are specified. If you try and set more than one, the second will overwrite the first.

It is likely a good idea to create your standard executor as a variable, and pass the same one into all the jobs that need it.

You can access the executors in the following way:

import { executors } from 'circle-config-generator';

Docker

CircleCI Docs

constructor(): Docker

There is an alternative constructor, which is likely the one you'll use. It accepts an image string which is a convenient way to instantiate the executor if you don't need multiple images to run together.

constructor(image: string) => Docker


The remainder of the Docker API is adding an image, configuring that image, and finally calling .done() on the image to close it and add it to the Docker executor. You can add as many images as you like to the Docker container.

To add an image,

image(image: string) => Image

Params

Name Type Default Description
image string (required) The name of the docker image to be used

Image

See the non-required fields of the CircleCI Docker Docs for the meanings of the fields

auth(auth: { username: string, password: string }) => Image

awsAuth(auth: {
  aws_access_key_id: string,
  aws_secret_access_key: string,
}) => Image

command(...command: Array<string>) => Image

entrypoint(...entrypoint: Array<string>) => Image

environment(env: { [key: string]: string }) => Image

name(name: string) => Image

user(user: string) => Image

done() => Docker

This closes up the image and returns back the parent Docker container.

Machine

Creates the machine executor.

CircleCI Docs

constructor(enabled: ?boolean) => Machine

Params

Name Type Default Description
enabled boolean true Is the machine enabled?

enabled(enabled: boolean) => Machine

Params

Name Type Default Description
enabled boolean (required) Is the machine enabled?

image(image: string) => Machine

Params

Name Type Default Description
image string (required) The image to use for the machine

dockerLayerCaching(enabled: boolean) => Machine

Params

Name Type Default Description
enabled boolean (required) Should this machine enable Docker layer caching

MacOS

Creates a macOS executor.

CircleCI Docs

constructor(version: string) => MacOS

Params

Name Type Default Description
version string (required) Version of macOS to run - check here for full list

Branches

This is used to set which branches a particular Job or Workflow will run on.

constructor() => Branches


ignore(...branches: Array<string>) => Branches

Sets branches that should be ignored by the Job or Trigger using this instance Note: Setting this will override any Branches#only branches that have been set.


only(...branches: Array<string>) => Branches

Filter down to only run on the branches passed in.

Packages

No packages published