# Introduction

`rad-ci` makes it easy to see if the current state of the source tree
would pass CI, without committing anything, or pushing to another
node. This is great when you're working on a change and want to see if
it would pass the checks done by CI. It's much less waiting than doing
it via the CI server, because everything runs on your machine.

Radicle CI runs CI for a change to a repository by using a CI adapter
to get execute the run on some external CI system or by using a local
CI engine. For example, the adapter might trigger CI to run on an
external CI system such as Concourse, or it might execute the steps
that CI would run locally.

For projects that use either the Radicle native CI adapter, or the
Ambient adapter, `rad-ci` executes the steps locally, without pushing
changes to another node, or even to the local node.

* For the **native CI adapter**, `rad-ci` reads `.radicle/native.yaml`,
  extracts the shell snippet from the `shell` field, and executes the
  using the Bash shell. This is what the native CI adapter also does.

* For the **Ambient adapter**, `rad-ci` invokes `ambient-driver` to
  execute the pre-plan, plan, and post-plan defined in
  `.radicle/ambient.yaml`, the same way the Ambient adapter does.

This document describes the acceptance criteria for `rad-ci`, and how
to automatically verify that they are met.

# Data file for verification

## Native CI run specification

~~~{#native.yaml .file .yaml}
shell: |
  echo hello, world
~~~

## Ambient CI run specification

~~~{#ambient.yaml .file .yaml}
plan:
- action: shell
  shell: |
    echo hello, world
~~~

# Acceptance criteria

## Smoke test: reports its version

_Want:_ The tool can report its version.

_Why:_ This is useful information for supporting a remote user.

This is also a useful "smoke test": it verifies that the `rad-ci`
executable exists, can be invoked, and it can run at all. If this
doesn't work, there's no real hope that anything else works, either.

~~~scenario
given an installed rad-ci
when I run rad-ci --version
then stdout matches regex ^rad-ci \d+\.\d+\.\d+
~~~

## Shows its actual configuration

_Want:_ The tool can show the actual configuration it uses at run
time, including the effect of command line options.

_Why:_ This allows the user to know what the configuration is, without
having to guess.

~~~scenario
given an installed rad-ci
given an Ambient VM image ambient.qcow2

when I run rad-ci config
then stdout contains ""dry_run": false"
then stdout contains ""engine": null"
then stdout contains ""ambient_image": null"

when I run rad-ci --dry-run config
then stdout contains ""dry_run": true"

when I run rad-ci --engine ambient config
then stdout contains ""engine": "Ambient""

when I run rad-ci --engine native config
then stdout contains ""engine": "Native""

when I run rad-ci --ambient-image ambient.qcow2 config
then stdout contains ""ambient_image": "/"
~~~

## User can choose the configuration file

_Want:_ The user can specify a configuration file when invoking
`rad-ci`, or the default one can be used, with built-in defaults as a
fallback.

_Why:_ Ideally, the user doesn't have to specify a file when invoking
the command, for convenience, but can, if they need to.

~~~scenario
given an installed rad-ci
given an Ambient VM image ambient.qcow2

when I run rad-ci config
then stdout contains ""ambient_image": null,"

given file rad-ci-config.yaml
when I run rad-ci --config rad-ci-config.yaml config
then stdout contains ""ambient_image": "/"

given file .config/rad-ci/config.yaml from rad-ci-config.yaml
when I run rad-ci config
then stdout contains ""ambient_image": "/"
~~~

## Reports error when it can't determine what to emulate

_Want:_ When user queries what CI engine will be emulated, the tool
gives an error when it can't determine that.

_Why:_ This helps users to be confident about what will happen if they
run the tool to emulate CI.

~~~scenario
given an installed rad-ci

when I try to run rad-ci --dry-run
then command fails
then stderr contains "CI engine"
~~~

## Show selected CI to emulate: native

_Want:_ User can query the tool for what CI engine to emulate, and
why, when the native CI is chosen.

_Why:_ This helps users to be confident about what will happen if they
run the tool to emulate CI.

~~~scenario
given an installed rad-ci

given file .radicle/native.yaml from native.yaml
when I run rad-ci --dry-run
then stdout contains ""Native":"
then stdout contains ""shell": "echo hello, world\\n""
~~~

## Show selected CI to emulate: Ambient

_Want:_ User can query the tool for what CI engine to emulate, and
why, when the Ambient engine is chosen.

_Why:_ This helps users to be confident about what will happen if they
run the tool to emulate CI.

~~~scenario
given an installed rad-ci

given file .radicle/ambient.yaml from ambient.yaml
when I run env RAD_CI_LOG=debug  rad-ci --dry-run
then stdout contains ""Ambient":"
then stdout contains ""shell": "echo hello, world\\n""
~~~
## Show selected CI to emulate: force

_Want:_ User can force the choice of CI engine to emulate.

_Why:_ This helps users to be confident about what will happen if they
run the tool to emulate CI.

~~~scenario
given an installed rad-ci

given file .radicle/native.yaml from native.yaml
given file .radicle/ambient.yaml from ambient.yaml
when I run env RAD_CI_LOG=debug  rad-ci --dry-run
then stdout contains ""Ambient":"
when I run env RAD_CI_LOG=debug  rad-ci --dry-run --engine ambient
then stdout contains ""Ambient":"
when I run env RAD_CI_LOG=debug  rad-ci --dry-run --engine native
then stdout contains ""Native":"
~~~

## Emulate native CI

_What:_ The tool can emulate the native CI adapter running CI.

_Why:_ This is fundamental to the purpose of the tool.

~~~scenario
given an installed rad-ci
given file .radicle/native.yaml from native.yaml
when I run env RAD_CI_LOG=debug rad-ci --vague
then stdout is exactly "hello, world\n"
~~~

## Emulate Ambient CI

_What:_ The tool can emulate the Ambient CI adapter.

_Why:_ This is fundamental to the purpose of the tool.

~~~scenario
given an installed rad-ci
given an Ambient VM image ambient.qcow2
given file xyzzy/.radicle/ambient.yaml from ambient.yaml
given file rad-ci-config.yaml
given file .config/ambient/config.yaml from ambient-config.yaml
given a directory state/projects
when I run env RAD_CI_LOG=trace AMBIENT_LOG=trace rad-ci --config rad-ci-config.yaml --source xyzzy
then stdout doesn't contain "hello, world"
when I run env RAD_CI_LOG=trace AMBIENT_LOG=trace rad-ci --config rad-ci-config.yaml --source xyzzy --verbose
then stdout contains "hello, world"
~~~

~~~{#ambient-config.yaml .file .yaml}
projects: "/dev/null"
state: "state"
executor: "ambient-execute-plan"
qemu:
  cpus: 1
  memory: "1 GB"
artifacts_max_size: "1 GB"
cache_max_size: "1 GB"
~~~


~~~{#rad-ci-config.yaml .file .yaml}
ambient_image: ambient.qcow2
~~~
