# Zetken Pipeline YAML Reference — `zetken/v1`

**Audience:** Zetken CI/CD Studio beta users and pipeline authors  
**Current schema:** `zetken/v1`

---

## 1. Minimal Studio-managed pipeline

For a pipeline created inside the Zetken browser UI:

```yaml
schema: zetken/v1

stages:
  - name: build
    jobs:
      - name: test
        steps:
          - name: Hello
            run: echo hello
```

The Studio stores the pipeline name as managed metadata. A managed `pipeline.yaml` should not need a duplicate top-level `name:` field.

---

## 2. Core hierarchy

```text
Pipeline
└── stages[]
    └── jobs[]
        └── steps[]
```

Rules:

- A pipeline must contain at least one stage.
- A stage must contain at least one job.
- A job must contain at least one step.
- A step must define exactly one executable action.

Executable step actions are:

```text
run
dockerBuild
downloadArtifacts
cache
```

---

## 3. Top-level fields

| Field | Type | Required | Purpose |
|---|---|---:|---|
| `schema` | string | Recommended | Current value: `zetken/v1`. |
| `name` | string | Standalone | Standalone pipeline name; Studio-managed pipelines use metadata. |
| `parameters` | list | No | Runtime parameters. |
| `workingDir` | string | No | Default working directory. |
| `shell` | string | No | Default shell. |
| `envFiles` | list | No | Environment files. |
| `env` | map | No | Pipeline environment variables. |
| `stages` | list | Yes | Pipeline stages. |

Use explicitly:

```yaml
schema: zetken/v1
```

A missing schema is currently treated as implicit `zetken/v1`, but Zetken warns that the explicit schema should be added.

---

## 4. Stage

Example:

```yaml
stages:
  - name: test
    maxParallel: 2
    workingDir: .
    shell: bash
    env:
      STAGE_MODE: test
    jobs:
      ...
```

Fields:

| Field | Type | Required | Purpose |
|---|---|---:|---|
| `name` | string | Yes | Stage name. |
| `maxParallel` | integer >= 1 | No | Maximum jobs running simultaneously. Default is `1`. |
| `workingDir` | string | No | Stage working directory. |
| `shell` | string | No | Stage shell. |
| `env` | map | No | Stage environment variables. |
| `jobs` | list | Yes | At least one job. |

---

## 5. Job

Example:

```yaml
jobs:
  - name: integration-test
    needs:
      - build
    workingDir: backend
    shell: bash
    env:
      TEST_MODE: integration
    steps:
      ...
```

Fields:

| Field | Type | Required | Purpose |
|---|---|---:|---|
| `name` | string | Yes | Unique job name inside the stage. |
| `needs` | list or inline value | No | Same-stage dependencies. |
| `workingDir` | string | No | Job working directory. |
| `shell` | string | No | Job shell. |
| `env` | map | No | Job environment variables. |
| `strategy.matrix` | map | No | Matrix variants. |
| `steps` | list | Yes | At least one step. |

Dependency validation rejects:

```text
Unknown jobs
Self-dependencies
Duplicate needs entries
Dependency cycles
Specific expanded matrix variants in needs
```

Use the matrix job's base name when depending on a matrix job.

---

## 6. Step

Example:

```yaml
steps:
  - name: Run tests
    workingDir: backend
    shell: bash
    env:
      MODE: test
    timeout: 5m
    retries: 1
    retryDelay: 2s
    if: success()
    continueOnError: false
    run: go test ./...
    artifacts:
      - test-result.txt
```

Fields:

| Field | Type | Required | Purpose |
|---|---|---:|---|
| `name` | string | Yes | Step name. |
| `workingDir` | string | No | Step working directory. |
| `shell` | string | No | Step shell. |
| `env` | map | No | Step environment variables. |
| `run` | string/block | One action | Execute a shell command/script. |
| `dockerBuild` | object | One action | Build a Docker image. |
| `downloadArtifacts` | object | One action | Download dependency artifacts. |
| `cache` | object | One action | Restore or save cache. |
| `artifacts` | list | No | Store files/directories after the action. |
| `timeout` | duration | No | Maximum step duration. |
| `retries` | integer >= 0 | No | Retry count after the initial attempt. |
| `retryDelay` | duration | No | Delay between attempts. |
| `if` | string | No | Step condition. |
| `continueOnError` | boolean | No | Continue after this step fails. |

A step must define exactly one of:

```yaml
run:
```

```yaml
dockerBuild:
```

```yaml
downloadArtifacts:
```

```yaml
cache:
```

---

## 7. `run`

Single command:

```yaml
- name: Test
  run: go test ./...
```

Multiline command:

```yaml
- name: Test and build
  run: |
    go test ./...
    go build ./...
```

The final run command cannot be empty.

---

## 8. Shells

Supported native shell values are currently:

```text
default
cmd
powershell
pwsh
bash
sh
```

Example:

```yaml
shell: bash
```

Shell can be set at:

```text
pipeline
stage
job
step
```

A more specific value overrides the broader value.

### Default shell

Windows:

```text
COMSPEC, otherwise cmd.exe
```

Linux/macOS:

```text
$SHELL, otherwise /bin/sh
```

The requested shell executable must exist on `PATH`. Zetken does not install or bundle external shells.

### PowerShell versions

`powershell` selects Windows PowerShell. `pwsh` selects PowerShell 7+ and is supported on Windows, Linux, and macOS when the `pwsh` executable is available on `PATH`. The values are distinct; Zetken does not fall back from `pwsh` to `powershell`.

```yaml
- name: PowerShell 7 version
  shell: pwsh
  run: |
    Write-Output "PowerShell 7"
    $PSVersionTable.PSVersion
```

---

## 9. Environment variables

Pipeline scope:

```yaml
env:
  BUILD_MODE: release
```

Stage scope:

```yaml
stages:
  - name: build
    env:
      COMPONENT: backend
```

Job scope:

```yaml
jobs:
  - name: compile
    env:
      TARGET: local
```

Step scope:

```yaml
steps:
  - name: Build
    env:
      OUTPUT: app
    run: echo ${OUTPUT}
```

Reference an environment variable with:

```text
${VARIABLE}
```

Environment resolution starts from the operating-system process environment, then applies Zetken environment files and pipeline/stage/job/step values, with more specific scopes overriding broader values.

---

## 10. Environment files

Example:

```yaml
envFiles:
  - .env.local
```

Typical file content:

```text
API_URL=http://localhost:8080
MODE=test
```

Environment files may contain sensitive values.

Do not intentionally place them into:

```text
artifacts
cache paths
shared reports
source content intended for distribution
```

---

## 11. Parameters

Example:

```yaml
parameters:
  - name: target
    type: string
    default: dev
    allowed:
      - dev
      - staging
      - prod
    description: Deployment target

  - name: packageName
    type: string
    required: true

  - name: apiToken
    type: string
    secret: true
```

Current supported parameter type:

```text
string
```

Parameter name pattern:

```text
^[A-Za-z_][A-Za-z0-9_]*$
```

Reference:

```text
${params.target}
```

Example:

```yaml
run: echo Deploying ${params.packageName} to ${params.target}
```

Standalone CLI:

```bash
zci run --param target=prod --param packageName=my-app
```

When `allowed` is defined, supplied/default values must be in that list.

---

## 12. Sensitive values

A parameter can be explicitly marked:

```yaml
secret: true
```

Names containing sensitive terms such as:

```text
secret
token
password
passwd
api_key
apikey
private_key
credential
```

are also treated as sensitive in several masking/protection paths.

Avoid putting secrets into:

```text
artifact names/paths
cache keys
cache paths
Docker image tags
source-controlled YAML
```

Secret masking is a protection layer, not a substitute for safe secret handling.

---

## 13. `workingDir`

Example at job level:

```yaml
jobs:
  - name: backend-test
    workingDir: backend
```

It may be set at:

```text
pipeline
stage
job
step
```

The resolved working directory must stay within the project safety boundary.

Do not use parent traversal such as:

```text
../outside-project
```

---

## 14. Parallel jobs with `maxParallel`

Example:

```yaml
stages:
  - name: test
    maxParallel: 3
```

Default:

```text
1
```

`maxParallel` must be an integer greater than or equal to 1.

Only jobs whose dependencies are satisfied are eligible to run.

---

## 15. Job dependencies with `needs`

List:

```yaml
needs:
  - build
  - lint
```

Single inline value is also parsed:

```yaml
needs: build
```

Example:

```yaml
stages:
  - name: ci
    jobs:
      - name: build
        steps:
          - name: Build
            run: go build ./...

      - name: test
        needs:
          - build
        steps:
          - name: Test
            run: go test ./...
```

If a dependency fails, the dependent job is skipped.

`needs` currently refers to jobs in the **same stage**.

---

## 16. Matrix jobs

Example:

```yaml
strategy:
  matrix:
    dev:
      target: dev
      mode: debug
    prod:
      target: prod
      mode: release
```

References:

```text
${matrix.target}
${matrix.mode}
${matrix.name}
```

Matrix variant name pattern:

```text
^[A-Za-z_][A-Za-z0-9_-]*$
```

Matrix variable name pattern:

```text
^[A-Za-z_][A-Za-z0-9_]*$
```

Matrix values should be strings.

Complete example:

```yaml
stages:
  - name: package
    maxParallel: 2
    jobs:
      - name: build
        strategy:
          matrix:
            dev:
              target: dev
            prod:
              target: prod
        steps:
          - name: Build
            run: echo Built ${matrix.target}

      - name: summary
        needs:
          - build
        steps:
          - name: Summary
            run: echo Matrix completed
```

The dependent `summary` job depends on the matrix base job `build`.

---

## 17. Conditions

Supported:

```text
success()
failure()
always()
```

Example:

```yaml
- name: Cleanup
  if: always()
  run: echo cleanup
```

When `if` is omitted, the default condition is:

```text
success()
```

---

## 18. Timeout and retries

Timeout:

```yaml
timeout: 2m
```

Retries:

```yaml
retries: 2
retryDelay: 5s
```

Durations use Go-style duration syntax, for example:

```text
250ms
10s
5m
1h
```

`retries: 2` means a maximum of:

```text
3 total attempts
```

(initial attempt + 2 retries).

---

## 19. `continueOnError`

Example:

```yaml
- name: Optional check
  continueOnError: true
  run: ./optional-check
```

The failure is still recorded, but Zetken can continue with later eligible steps.

---

## 20. Artifacts

Example:

```yaml
- name: Package
  run: |
    mkdir -p output
    echo result > output/result.txt
  artifacts:
    - output/result.txt
```

Artifact paths must remain safe relative project paths.

Do not use:

```text
paths outside the project
Zetken runtime storage
environment files
secret-bearing files
```

---

## 21. Job outputs

For a `run` action, Zetken supplies the temporary environment variable:

```text
ZCI_OUTPUT
```

Write output values as:

```text
name=value
```

Output-key pattern:

```text
^[A-Za-z_][A-Za-z0-9_]*$
```

Bash/sh example:

```yaml
- name: Produce output
  shell: bash
  run: |
    echo "package=backend.txt" >> "$ZCI_OUTPUT"
```

PowerShell example:

```yaml
- name: Produce output
  shell: powershell
  run: |
    Add-Content $env:ZCI_OUTPUT "package=backend.txt"
```

Consumer:

```yaml
needs:
  - producer
```

Reference:

```text
${needs.producer.outputs.package}
```

A job may reference outputs only from jobs listed in its `needs`.

---

## 22. `downloadArtifacts`

Example:

```yaml
- name: Download producer artifact
  downloadArtifacts:
    from: producer
    to: downloaded
    files:
      - backend.txt
    overwrite: true
```

Fields:

| Field | Required | Purpose |
|---|---:|---|
| `from` | Yes | Producer job name. |
| `to` | Yes | Destination path. |
| `files` | No | Optional artifact subset. |
| `overwrite` | No | Replace existing target content. |

The `from` job must:

1. exist in the stage; and
2. be included in the consumer job's `needs`.

---

## 23. Cache

Restore example:

```yaml
- name: Restore cache
  cache:
    action: restore
    key: go-${hashFiles("go.sum")}
    restoreKeys:
      - go-
    paths:
      - .cache/go
    overwrite: true
    failOnMiss: false
```

Save example:

```yaml
- name: Save cache
  cache:
    action: save
    key: go-${hashFiles("go.sum")}
    paths:
      - .cache/go
```

Fields:

| Field | Required | Purpose |
|---|---:|---|
| `action` | Yes | `restore` or `save`. |
| `key` | Yes | Exact cache key. |
| `paths` | Yes | Relative files/directories. |
| `restoreKeys` | No | Prefix fallbacks during restore. |
| `overwrite` | No | Restore overwrite behavior. |
| `failOnMiss` | No | Fail if restore has no match. |

Do not cache environment/secret files or Zetken runtime storage.

---

## 24. `hashFiles()`

Single input:

```text
${hashFiles("go.sum")}
```

Multiple inputs:

```text
${hashFiles("package-lock.json", "package.json")}
```

Typical use:

```yaml
key: node-${hashFiles("package-lock.json", "package.json")}
```

The fingerprint changes when the matched file content changes, making it useful for cache invalidation.

---

## 25. Docker image build

Preferred native syntax:

```yaml
- name: Build Docker image
  dockerBuild:
    context: .
    dockerfile: Dockerfile
    tag: my-app:local
    args:
      - VERSION=1.0.0
```

Fields:

| Field | Required | Purpose |
|---|---:|---|
| `context` | No | Build context; empty resolves to `.`. |
| `dockerfile` | Yes | Dockerfile path. |
| `tag` | Yes | Image tag. |
| `args` | No | Docker build arguments. |

Docker must be installed when this action executes.

The current parser also contains a `docker` alias, but new pipeline definitions should prefer:

```yaml
dockerBuild:
```

---

## 26. Expression reference summary

Environment:

```text
${VARIABLE}
```

Parameter:

```text
${params.name}
```

Matrix:

```text
${matrix.name}
```

Dependency output:

```text
${needs.job.outputs.output}
```

File hash:

```text
${hashFiles("path")}
```

Malformed or unknown supported-expression references are validation errors.

---

## 27. Environment/shell/working-directory inheritance

Zetken supports these values at multiple scopes:

```text
pipeline
→ stage
→ job
→ step
```

For example:

```yaml
shell: bash
workingDir: src
env:
  MODE: pipeline

stages:
  - name: test
    env:
      MODE: stage
    jobs:
      - name: unit
        workingDir: backend
        steps:
          - name: Run
            env:
              MODE: step
            run: echo ${MODE}
```

The more specific scope overrides the broader scope.

Operating-system environment variables are also available unless overridden by Zetken-defined values.

---

## 28. Complete Studio-managed example

```yaml
schema: zetken/v1

parameters:
  - name: target
    type: string
    default: dev
    allowed:
      - dev
      - prod

env:
  APP_NAME: demo-app

stages:
  - name: validate
    maxParallel: 2
    jobs:
      - name: test
        steps:
          - name: Test
            shell: bash
            run: |
              echo "Testing ${APP_NAME}"
              go test ./...

      - name: lint
        steps:
          - name: Lint
            shell: bash
            continueOnError: true
            run: echo "Run linter here"

  - name: package
    jobs:
      - name: package
        steps:
          - name: Create package
            shell: bash
            run: |
              mkdir -p output
              echo "${APP_NAME}-${params.target}" > output/package.txt
            artifacts:
              - output/package.txt
```

Note that `needs` is job-level and currently references jobs within the same stage. Stages themselves are executed in pipeline order.

---

## 29. Cross-platform guidance

The YAML structure can stay the same across operating systems, but shell commands may differ.

Portable example:

```yaml
schema: zetken/v1

stages:
  - name: test
    jobs:
      - name: go-test
        steps:
          - name: Run tests
            run: go test ./...
```

This can run on Windows, Linux, and macOS when Go is installed.

OS-specific examples:

Windows:

```yaml
shell: powershell
run: |
  New-Item -ItemType Directory -Force output
```

Linux/macOS:

```yaml
shell: bash
run: |
  mkdir -p output
```

A Zetken pipeline does not automatically install the required shell or external CLI.

---

## 30. Validation checklist

Before running:

```text
[ ] schema: zetken/v1 is present
[ ] Managed pipeline does not duplicate its UI name unnecessarily
[ ] Standalone pipeline has name:
[ ] Pipeline has at least one stage
[ ] Every stage has at least one job
[ ] Every job has at least one step
[ ] Every step has exactly one executable action
[ ] Every needs target exists in the same stage
[ ] No dependency cycles
[ ] Selected shell is supported
[ ] Selected shell exists on the machine
[ ] workingDir stays inside the project
[ ] Environment variables are defined
[ ] Parameter references are valid
[ ] Matrix references are valid
[ ] Artifact paths are safe
[ ] Cache paths/keys contain no secrets
[ ] External tools used by run commands are installed
```

In Studio, select:

```text
Validate
```

Standalone:

```bash
zci validate
```

---

## 31. Useful beta findings to report

Please report cases where:

- Migration Assistant says **Converted** but generated YAML fails native validation.
- Migration generates an unsupported native shell.
- `needs` ordering or parallel execution is incorrect.
- Environment inheritance behaves unexpectedly.
- A secret is visible in logs.
- An artifact/cache path escapes expected project boundaries.
- An empty stage/job/step is generated during migration.
- Source line numbers point to the wrong location.
- A supposedly portable pipeline behaves differently on Windows, Linux, and macOS.
