# Pipeline file reference

> Every setting of .squidci/pipelines.yaml — components, sections, steps, runner, services, aws, needs, after, paths, artifacts, cache and environments — with its values, defaults and limits.

`.squidci/pipelines.yaml`, read at each commit. The JSON Schema at
[`squidci.com/schema/pipelines.json`](https://squidci.com/schema/pipelines.json)
states the same rules for editors and agents; what it cannot check is marked
below. A file SquidCI refuses runs nothing, and the run says why.

**Every value is read as text.** `tag: 1.10` is the tag `1.10`, not the number
1.1, and `push: false` is the word false. Write values as you mean them;
quoting is never needed for that.

## The file

A map. Its keys are component names, and `environments`.

- At least one component; at most **50 jobs** in all, a deploy counting once
  per environment.
- At most **256 KB**.
- No key twice, anywhere. YAML aliases (`*name`) are refused.

## Components

```yaml
<name>:
  base: <directory>
  runner: {image, arch, vcpus, spot, lambda}
  services: {<name>: {image, env, healthcheck}}
  aws: {profile, profiles, region}
  needs: [<component>, ...]
  after: [<component>, ...]
  paths: [<directory>, ...]
  artifacts: [<path>, ...]
  cache: true | false
  check | test | build | publish | run | deploy: [<step>, ...]
```

A **name** is segments of letters, digits, `.`, `_` and `-`, each starting
with a letter or digit, joined by `/`; at most 64 characters. Any key not
listed here is refused.

At least one **section** is required. Each is a list of 1 to **100** steps,
and one job.

| section   | waits for                                                                              | pull requests |
|-----------|----------------------------------------------------------------------------------------|---------------|
| `check`   | `needs`, `after`                                                                       | run           |
| `test`    | `needs`, `after`                                                                       | run           |
| `build`   | `needs`, `after`                                                                       | run           |
| `publish` | its component's check, test and build; `needs`, `after`                                 | not run       |
| `run`     | `needs`, `after`                                                                       | not run       |
| `deploy`  | every check, test and build in the run; its component's publish; earlier environments | not run       |

### base

The directory the component's steps run in, relative to the repository's
root: `./web`, `infra/cdk`. The root when left out. Letters, digits, `.`, `_`,
`-` and `/`; not absolute, and never climbing out with `..`.

### runner

| key      | value                                                                      | default            |
|----------|----------------------------------------------------------------------------|--------------------|
| `image`  | an image reference: `[registry/]name[:tag][@sha256:digest]`, lower case    | SquidCI's runner   |
| `arch`   | `amd64` or `arm64`. Jobs run natively, on a worker group of that architecture | `amd64`        |
| `vcpus`  | what of an instance each job reserves, in halves: `0.5` to `192`           | `1`                |
| `spot`   | `false` keeps the jobs off spot instances                                  | spot preferred     |
| `lambda` | `nodejs26.x`: run each step as code in a Lambda function                   | —                  |

At least one key. `lambda` cannot be given with `image`, `vcpus` or `spot`.
A component with `lambda`:

- has no checkout: no `base`, `services` or `artifacts`;
- takes commands and `script` steps only — each run as an ES module of
  TypeScript or JavaScript, with the runtime's AWS SDK clients importable —
  not `pnpm` or `docker`;
- runs for at most 14½ minutes;
- runs on a Lambda worker group of its runtime and `arch`.

### services

A map of containers that run beside each job of the component, on its
network, reached by their name. At most **10**.

| key           | value                                                                | required |
|---------------|----------------------------------------------------------------------|----------|
| `image`       | an image reference                                                   | yes      |
| `env`         | a map of variables: names like `A_B1`, values at most 10,000 characters; at most 100 | no |
| `healthcheck` | a command run in the service until it succeeds; the job starts after | no       |

A name is a DNS label: lower-case letters, digits and `-`, up to 63
characters, and not `job`. Without a `healthcheck` the job starts as soon as
the service's container has.

### aws

What the component's jobs may reach in AWS. See
[AWS access for jobs](/docs/pipelines/aws/).

| key        | value                                                                        |
|------------|------------------------------------------------------------------------------|
| `profile`  | the target AWS account plain `aws` uses: set as `AWS_PROFILE`                |
| `profiles` | every target AWS account the jobs may use: 1 to **10**, none twice           |
| `region`   | the region the jobs work in: set as `AWS_REGION` and `AWS_DEFAULT_REGION`    |

`profile` or `profiles` is required; `profile` is always among those the jobs
may use. A profile is the name of a target AWS account of one of your
execution planes — lower-case letters, digits and `-`, starting with a letter,
at most 32 characters, not `default`. A name no plane of yours has is refused
*(not checked by the schema)*. `region` is shaped like `eu-west-2`; the
plane's region when left out.

Pull requests ignore `aws`. A deploy uses its environment's `aws` instead.

### needs and after

Lists of component names, each once, never the component itself. Both make
every job of this component wait for every job of those — but a deploy waits
only for their deploys to the same environment.

- `needs` also brings their **artifacts** and **values**, and makes their
  files part of whether this component is unchanged.
- `after` brings nothing.

A component in both is refused, as is one that is not in the file, and any
circle of `needs` and `after` *(not checked by the schema)*.

### paths

More directories, beside `base`, that the component uses: a change in one
means its jobs run again rather than end unchanged. 1 to **10**, as `base`
is written, and not the whole repository.

### artifacts

Paths inside `base` that the component's `build` leaves for the rest of the
run. 1 to **10**, not `.`, and nothing under `.squidci`. A path the build did
not leave fails it. Restored before the component's own `publish`, `run` and
`deploy` at the same paths, and before every job of a component that `needs`
it under `.squidci/artifacts/<component>/<path>`.

### cache

`false` turns the component's build cache off. On when left out.

## Steps

A step is a command, or a map with exactly one of the keys below.

**A command**: a string, run by bash. Not empty; at most 10,000 characters.

**`pnpm: <args>`**: runs `pnpm <args>`, on one line. Before the job's first
`pnpm` step, Node is installed as `.nvmrc` or `.node-version` pins it (looked
for from `base` upwards) and `pnpm install --frozen-lockfile` is run.

**`script: <path>`**: a file of the repository, relative to `base`, run by its
extension — `.js`, `.mjs`, `.cjs`, `.ts`, `.mts`, `.cts` with Node, `.sh` with
bash, anything else by its shebang. Letters, digits, `.`, `_`, `-` and `/`;
inside the repository. In a process of its own, so unlike a command it cannot
`cd` or `export` for the steps after it. Under `runner.lambda`, the file is
read at the run's commit, at most 256 KB, and its relative imports do not
resolve.

**`docker: {image, tag, push}`**:

| key     | value                                                                    | default |
|---------|--------------------------------------------------------------------------|---------|
| `image` | the repository, without a tag: `public.ecr.aws/example/web`              | —       |
| `tag`   | the tag: `latest`, `1.2.0`                                               | —       |
| `push`  | `false` builds without pushing                                           | `true`  |

Builds the Dockerfile in `base`. A push to ECR Public or a private ECR
registry logs in first with the job's AWS credentials, so the job needs `aws`
with access to it. A push exports `digest`, `mediaType`, `size` and
`architecture`.

## Values

A step appends `name=value` lines to the file `$SQUIDCI_OUTPUT` names. Names
are letters, digits and `_`; values are one line, at most 1 KB; at most 50 a
job. A job that `needs` the component gets each as `SQUIDCI_<COMPONENT>_<NAME>`
— upper-cased, anything not a letter or digit made `_` — and all of them as
JSON, by component, in the file `$SQUIDCI_INPUTS` names. Two sections of one
component exporting the same name leave the dependent job skipped rather than
guessing. Values are shown with the job: never put secrets in them.

## environments

A map of environment names to settings; at least one when any component has
a `deploy`. A name is lower-case letters, digits and `-`, starting with a
letter, at most 32 characters. An environment may have no settings (`dev:`).

| key       | value                                                                     |
|-----------|---------------------------------------------------------------------------|
| `aws`     | as a component's `aws`; replaces it, wholly, for deploys here             |
| `after`   | an environment, or a list: each must have had the version deployed first  |
| `trigger` | `every-version`, or a schedule; without one, deployed only by hand        |

A schedule:

| key        | value                                                                      | default |
|------------|----------------------------------------------------------------------------|---------|
| `schedule` | a cron of five fields — minute, hour, day of month, month, day of week     | —       |
| `timezone` | an IANA time zone: `Europe/London`                                         | `UTC`   |
| `soaked`   | `{in: <environment>, hours: 1–744}`: only versions that reached `in` at least that long ago | — |

`after` and `soaked.in` must name environments in the file, not this one, and
`after` must not go round in a circle; a cron must be valid and a time zone
real *(none of these checked by the schema)*.

## Refused with a pointer

Older spellings are refused with what to write instead: `environment:` on a
component is now `aws:`, `artifact:` is now `artifacts:`, and an
environment's `approval:` is gone — leave the trigger out instead.
