Radicle CI adapter for Ambient

By: The Radicle Project, Lars Wirzenius

2025-09-06 05:27

Table of Contents

1 Introduction

The Radicle CI adapter for Ambient integrates the Ambient CI system to Radicle CI. In other words, this adapter allows Radicle CI to use Ambient to run CI for Radicle repositories.

2 Data files for scenarios

The embedded files in this chapter are used by various scenarios later in this document.

2.1 ambient-ci configuration files

tmpdir: .
projects: projects.yaml
executor: /usr/bin/ambient-execute-plan
artifacts_max_size: 1G
cache_max_size: 50G
state: state
qemu:
  cpus: 1
  memory: 1G

2.2 Ambient adapter configuration file

image: /scratch/ambient-images/ambient-boot.qcow2
logdir: run_logs
log: adminlog.txt
base_url: https://ci.liw.fi
image: /scratch/ambient-images/ambient-boot.qcow2
logdir: run_logs
log: adminlog.txt
base_url: https://ci.liw.fi
trust_repository_name: true

2.3 Script to set environment variables and running a command

This is meant to be run as bash env.sh foo bar, which is why it doesn't start with a shebang.

set -euxo pipefail

export RAD_HOME="$(pwd)"
export RAD_PASSPHRASE="secret"
export RADICLE_CI_AMBIENT=radicle-ci-ambient.yaml

"$@"

2.4 Script to create repository and set up Radicle

This creates a Git repository in src and sets up a Radicle node called xyzzy. It also creates a trigger message as trigger.json, which can be fed to the adapter via its stdin.

This is meant to be run as bash env.sh foo bar, which is why it doesn't start with a shebang.

set -euxo pipefail

rad auth --alias xyzzy
git init src

mkdir -p src/.radicle
cp "$1" src/.radicle/ambient.yaml

cd src
echo hello, world > greeting.txt
git add .
git commit -m initial

rad init --name abracadabra --description x --private --no-confirm
rad inspect --identity

rid="$(rad .)"
commit="$(git rev-parse HEAD)"

cat <<EOF | jq -c . > ../trigger.json
{
  "request": "trigger",
  "version": 1,
  "event_type": "push",
  "repository": {
    "id": "$rid",
    "name": "abracadabra",
    "description": "x",
    "private": true,
    "default_branch": "main",
    "delegates": [
      "did:key:z6MkgEMYod7Hxfy9qCvDv5hYHkZ4ciWmLFgfvm3Wn1b2w2FV"
    ]
  },
  "pusher": {
    "id": "did:key:z6MkgEMYod7Hxfy9qCvDv5hYHkZ4ciWmLFgfvm3Wn1b2w2FV",
    "alias": "liw"
  },
  "before": "$commit",
  "after": "$commit",
  "branch": "main",
  "commits": [
    "$commit"
  ]
}
EOF

2.5 Script to run adapter

Due to limitations in Subplot we can't redirect adapter stdin directly in a scenario, so we use this helper script.

This is meant to be run as bash env.sh foo bar, which is why it doesn't start with a shebang.

set -euxo pipefail
if ! radicle-ci-ambient < trigger.json; then
    code="$?"
    cat adminlog.txt
    exit "$code"
else
    echo "=========================================================="
    echo admin log:
    nl adminlog.txt
fi

3 Acceptance criteria

3.1 Adapter reports its version

Want: The adapter can be queried for its version.

Why: This is useful for diagnosing problems, and also acts as a smoke test: if this works, we know the adapter is installed and can be run.

given an installed Ambient adapter
when I run radicle-ci-ambient --version
then stdout matches regex ^radicle-ci-ambient \d+\.\d+\.\d+@.*$

3.2 Can run a simple project

Want: The adapter can run a very simple project.

Why: If this doesn't work, woe be more intricate projects.

given an installed Ambient adapter
given file env.sh
given file rad-init.sh
given file simple.yaml
when I run bash env.sh bash rad-init.sh simple.yaml
when I run bash env.sh rad self
given file .config/ambient/config.yaml from ambient.yaml
given file radicle-ci-ambient.yaml
given file run.sh
when I run bash env.sh bash run.sh
then stdout contains ""response":"triggered""
then stdout contains ""result":"success""
then the only HTML log file in run_logs contains "hello from simple world"
then the only HTML log file in run_logs contains "&quot;request&quot;: &quot;trigger&quot;"
then the only HTML log file in run_logs contains "&quot;version&quot;: 1"
then directory state/abracadabra does not exist
plan:
- action: shell
  shell: |
    printf '%s %s\n' 'hello from' 'simple world'

3.3 Uses repository name as Ambient project name when allowed

Want: The node operator can allow use of the Radicle repository name as the Ambient project name.

Why: This is helpful for using the Ambient rsync_target_base configuration option.

given an installed Ambient adapter
given file env.sh
given file rad-init.sh
given file simple.yaml
when I run bash env.sh bash rad-init.sh simple.yaml
when I run bash env.sh rad self
given file .config/ambient/config.yaml from ambient.yaml
given file radicle-ci-ambient.yaml from radicle-ci-ambient-trust-repo-name.yaml
given file run.sh
when I run bash env.sh bash run.sh
then stdout contains ""response":"triggered""
then stdout contains ""result":"success""
then the only HTML log file in run_logs contains "hello from simple world"
then the only HTML log file in run_logs contains "&quot;request&quot;: &quot;trigger&quot;"
then the only HTML log file in run_logs contains "&quot;version&quot;: 1"
when I run ls -la state
then directory state/abracadabra exists

3.4 Give Ambient extra configuration file if needed

Want: The node operator use the radicle-ci-ambient configuration file to pass an extra configuration file to ambient.

Why: This allows the node operator to configure the CI broker to use the Ambient adapter in different ways for different CI runs. For example, to use the rsync Ambient action to deliver to a different server based on the Radicle repository.

given an installed Ambient adapter
given file env.sh
given file rad-init.sh
given file simple.yaml
when I run bash env.sh bash rad-init.sh simple.yaml
when I run bash env.sh rad self
given file .config/ambient/config.yaml from ambient.yaml
given file radicle-ci-ambient.yaml from radicle-ci-ambient-extra.yaml
given file extra.yaml
given file run.sh
when I run bash env.sh bash run.sh
when I run find -ls
then directory extra.state/abracadabra exists
image: /scratch/ambient-images/ambient-boot.qcow2
logdir: run_logs
log: adminlog.txt
base_url: https://ci.liw.fi
extra_ambient_config: extra.yaml
trust_repository_name: true
state: extra.state

3.5 Give Ambient extra configuration values if needed

Want: The node operator use the radicle-ci-ambient configuration to pass in extra configuration values to ambient, without having to put them into another file.

Why: This is for convenience.

given an installed Ambient adapter
given file env.sh
given file rad-init.sh
given file simple.yaml
when I run bash env.sh bash rad-init.sh simple.yaml
when I run bash env.sh rad self
given file .config/ambient/config.yaml from ambient.yaml
given file radicle-ci-ambient.yaml from radicle-ci-ambient-extra-values.yaml
given file extra.yaml
given file run.sh
when I run bash env.sh bash run.sh
then directory extra.state/abracadabra exists
image: /scratch/ambient-images/ambient-boot.qcow2
logdir: run_logs
log: adminlog.txt
base_url: https://ci.liw.fi
extra_ambient_config_values:
  state: extra.state
trust_repository_name: true

3.6 Handles failing project

Want: The adapter reports a failing CI run correctly.

Why: We need to be sure problems are handled correctly by CI.

given an installed Ambient adapter
given file env.sh
given file rad-init.sh
given file fail.yaml
when I run bash env.sh bash rad-init.sh fail.yaml
given file .config/ambient/config.yaml from ambient.yaml
given file radicle-ci-ambient.yaml
given file run.sh
when I run bash env.sh bash run.sh
then stdout contains ""response":"triggered""
then stdout contains ""result":"failure""
then the only HTML log file in run_logs contains "oh no"
plan:
- action: shell
  shell: |
    echo oh no
    exit 1