A set of libraries for SRE/DevOps teams to generate Concourse pipelines, task yaml files, and set defaults*.
View the full API documentation, including examples, on the Wiki!
Concourse by default accepts yaml as its configuration, which makes it nearly impossible to reuse code for it. Its main advantage is its statelessness and self-contained nature, but this makes config reuse diffucult. We can fix the code reusability issue by creating a library that generates yaml using code.
This is a meta-package that's designed to be used to create a library that's
specific to an organisation's infrastructure. The dependency chain looks like
this: @corpity-corp/project
> @corpity-corp/ci
> @decentm/concourse-ts
.
If you're a developer using a package that depends on
concourse-ts
, you shouldn't installconcourse-ts
. Consult your SRE/DevOps team's documentation about using their package. This page is for people looking to write Pipelines from scratch, or those who are creating an organisation-specific package that customisesconcourse-ts
.
For the sake of example, the name corpity-corp
is used throughout this repo
when talking about a theoretical organisation using concourse-ts
.
-
Create a new package
yarn init -y
npm init
-
Install the package as a production dependency. This will make sure your users will be able to install everything easily and have the same version
yarn add typescript @decentm/concourse-ts
npm i --save typescript @decentm/concourse-ts
The most friendly way to use concourse-ts
is to create an NPM package that
depends on concourse-ts
and publishing it to your private registry. This will
allow you to harness the power of concourse-ts
while adding
organisation-specific defaults.
-
In the entrypoint of your package, extend and export the following classes:
Pipeline
Job
Task
-
Implement defaults* by calling the
customise
static function on classes. For example, you can calltask.set_cpu_limit_shares(1)
in yourTask
customiser to make all tasks limit CPU usage on workers. -
Extend
Resource
andResourceType
as many times as needed to implement organisation-specific configuration, like e-mail notifications, webhooks, SCM repos, build processes, and test automation.
Important to note, that defaults only apply to the customiser
function. Each class can be interacted in two ways by the end user (in this
case, the developer using the @corpity-corp/ci
package). First, when a
concourse-ts
class is instantiated, it accepts an optional initialiser
function that can be used to access the constructed object. Second, class
instances are available to modify after they've been created. To make it clear:
let thing = null
const task = new Task('my_task', (my_task) => {
thing = my_task
})
task === thing // true
When you set defaults on a class with .customise(), those statements run after
the initialiser function, but that's not a guarantee that those values will
never be modified. If the end user sets properties or calls functions after the
new
statement and uses its return value, they can override defaults set here.
Take this for example:
// @corpity-corp/ci > src/build-task.ts
Task.customise((task) => {
task.set_cpu_limit_shares(1)
})
// @corpity-corp/projects/zeus-server > ci/build.task.ts
import {Task} from '@corpity-corp/ci'
const build_task = new BuildTask('my_build', (task) => {
// This will be overwritten with 1 by the base customiser
task.set_cpu_limit_shares(3)
})
// This will overwrite the value from the customiser
build_task.set_cpu_limit_shares(2)
// CPU limit is 2 here
In this above case, the final value for the cpu_limit_shares
will be 2. Since the
configuration for projects is stored in each project's repo, this library or any
project-level check is not appropriate for security checks. Also avoid directly
inserting secrets into your @corpity-corp/ci
library, as they'll be written to
disk and visible in the generated YAML config. Use credential
management to handle secrets. That way you
can also ensure that secrets do not show up in build logs.
npm i --save-dev @decentm/concourse-ts-cli
yarn add -D @decentm/concourse-ts-cli
The concourse-ts
command line package reads a typescript pipeline, compiles
it to valid Concourse YAML syntax, and writes it to disk. Run concourse-ts --help
to view documentation for the command syntax.
The input file must contain valid Typescript code, that has a default export that returns either a
Pipeline
instance or a promise that resolves to aPipeline
instance.
There are two ways to compile a concourse-ts
Pipeline file. Your needs and
team's requirements will determine which one to use, both are supported.
- First is to install
@decentm/concourse-ts-cli
as adevDependency
in your project, and set up a husky hook to compile your pipeline duringgit commit
. This means that the actual YAML pipeline file will be visible in your repository.- Advantages:
- Easy to view your pipeline evolve, and guarantee that you have the last say in what your CI will execute.
- Ability to run generated tasks locally with Concourse's
fly
CLI (requires the--extract-tasks
or-e
option). - Native support for self-setting pipelines (e.g.
set-pipeline: "self"
). Since the actual YAML files are in your repository, your set-pipeline step will be very fast.
- Disadvantages:
- You have to install
@decentm/concourse-ts-cli
in your repository as adevDependency
, and keep it up to date and in sync with your version ofconcourse-ts
in each project. - Both the pipeline source and the compiled yaml output will be present in your repository, making it slightly more cluttered.
- If another developer commits with
git commit -n
, they will be able to run a pipeline that doesn't match the output of yourconcourse-ts
pipeline file.
- You have to install
- Advantages:
- Second is to use the
decentm/concourse-ts-cli
Docker image to compile the pipeline just before theset-pipeline
step.- Advantages:
- No need to install the CLI NPM package locally or keep it up to date.
- Compiled YAML output is NOT committed to the repository, keeping the codebase cleaner.
- The pipeline executed in your CI is guaranteed to be the same as the
output of your
concourse-ts
Pipeline file. - Language agnosticity; you don't need to install NodeJS or Typescript into your project to compile pipelines.
- Disadvantages:
- Your pipeline's history is only visible in the Typescript file. If someone
is familiar with Concourse configuration, but not
concourse-ts
, they will have a harder time tracking down bugs, for example ingit blame
. - Since compiled YAML files are not readily available, you can't use
fly
to run indivisual tasks (unless you pull thedecentm/concourse-ts-cli
docker image locally and use Docker to run it) - No native
set-pipeline
speed. Before yourset-pipeline
step, you must include a step that uses the Docker image to compile your Pipeline file.
- Your pipeline's history is only visible in the Typescript file. If someone
is familiar with Concourse configuration, but not
- Advantages:
concourse-ts-cli
will look for an .rc file using
Cosmiconfig, with Typescript
support. This means that files ending with rc
, rc.json
, rc.yaml
, rc.yml
,
rc.js
, rc.ts
, rc.cjs
, .config.js
, .config.ts
, .config.cjs
are
searched in the working directory, and also ./.config
.
rc
files must export an object with CLI options for each command separately.
For .js
, .cjs
, and .ts
files, a type helper is available in the package.
Non-typescript files must export the same interface on their default export.
(e.g. module.exports
in .js
, and an object in .json
and .yml
)
For example, the .concourse-tsrc.ts
file may have the following contents:
import { rc } from '@decentm/concourse-ts-cli'
export default rc({
compile: {
clean: true,
},
decompile: {
package: '@corpity-corp/ci',
},
})
Use the compile
command to compile a concourse-ts
Typescript file into a
valid Concourse YAML file.
Examples:
cat ci/pipeline.ts | concourse-ts compile > .ci/pipeline.yml
# No output
concourse-ts compile -i ci/pipeline.ts -o .ci/pipeline.yml -f
# No output
concourse-ts compile < ci/pipeline.ts
# Outputs the resulting YAML, because no "-o" was provided, and output
# is not redirected
Use the decompile
command to convert an existing Concourse YAML pipeline file
to valid concourse-ts
typescript code. This will turn existing Concourse
configuration into concourse-ts
code, without having to reimplement pipelines,
provided that you already have a YAML file.
This command generates code in a non-human-friendly way, where components may be functionally duplicated if multiple similar configurations exist in the source YAML. After decompilation, it's strongly recommended to review the generated code and refactor it.
Examples:
cat .ci/pipeline.yml | concourse-ts decompile > ci/pipeline.ts
# No output
concourse-ts decompile -i .ci/pipeline.yml -o ci/pipeline.ts
# No output
concourse-ts decompile < .ci/pipeline.yml
# Outputs the resulting Typescript code, because no "-o" was provided,
# and output is not redirected
Use the transform
command to apply transformations to pipeline yml files. This
command can be used even on non-concourse-ts pipelines, as it reads yaml and
also writes yaml.
Examples:
cat .ci/pipeline.yml | concourse-ts transform > .ci/pipeline-new.yml
# No output
concourse-ts transform -i .ci/pipeline.yml -o .ci/pipeline.yml
# No output, output will be overwritten
concourse-ts transform < .ci/pipeline.yml
# Outputs the transformed yaml, because no "-o" was provided,
# and output is not redirected
concourse-ts transform -t apply_across_polyfill,apply_task_hoisting
# Runs transformation with options only from the .rc file, with the two
# specified transformers