Skip to content

Writing a contract

apiVersion: oren.sh/v1
kind: Task
metadata:
name: count-lines
namespace: acme # required in order to publish
version: 1.0.0
summary: Counts the lines of code in a project
spec:
effects: none
inputs:
type: object
properties:
extension:
type: string
default: js
outputs:
type: object
required: [total]
properties:
total: { type: integer }
dependencies:
source:
type: directory
mutable: false

Inputs and outputs are JSON Schema, with one extra keyword: secret.

A field that is not in the contract fails the step, with a suggestion:

unknown field: "incremenMajorVersion" — did you mean "incrementMajorVersion"?

This is not gratuitous strictness. In the pipeline that gave rise to this project, the worker read incremenMajorVersion while the pipeline sent incrementMajorVersion — without the “t”. Major and patch were never incremented, for sixteen releases, without a single error.

Determines whether the result may come from the cache.

Value Cacheable Safe to repeat Example
none yes analysing commits, computing a version
reads-external no yes terraform plan
external no no publishing an image, creating a remote tag, deploying

The criterion: if I run it again with the same inputs and nothing happens, is the pipeline still correct? If not, it is external.

The mistakes are not symmetric. Marking external too often costs cache. Marking it too rarely produces a silently absent deploy, with the pipeline reporting success. When in doubt, external.

reads-external is the middle case: it changes nothing, but reads mutable external state. Caching would return a stale reading, but repeating is safe.

token:
type: string
secret: true

Valid in inputs and outputs. The value is redacted in logs, stays out of the lockfile and of the on-disk state, and contaminates any field that interpolates it.