Call (Embedded Runs)
Embedded runs let you compose and coordinate multiple run definitions. Orchestrating your CI and CD workflows is one situation where this functionality shines.
Like packages, runs can be embedded using the call key. Before diving into the details,
let's consider the following example:
on:
github:
push:
if: ${{ event.git.branch == "main" }}
init:
git-ref: ${{ event.git.ref }}
only-at-the-top-level: some-value
base:
os: ubuntu 24.04
tag: 1.2
tasks:
- key: ci
call: ${{ run.dir }}/ci.yml
init:
git-ref: ${{ init.git-ref }}
- key: cd
call: ${{ run.dir }}/cd.yml
after: ci
init:
git-ref: ${{ init.git-ref }}
image-id: ${{ tasks.ci.tasks.build-image.values.image-id }}
There's a lot to consider in this small example. Let's dive in.
call
Runs can be embedded via the call key. To do so, you can provide a value that starts with ${{ run.dir }}.
This expression represents the .rwx directory that was present when your run was triggered. Following
${{ run.dir }}, you provide the filename of the run definition you'd like to embed.
When your run is triggered, either via an event or with the CLI, RWX keeps track of the files in the .rwx directory
and exposes them to you for use in embedded runs.
If you want to embed a run from a definition that is not in the RWX directory, you can reference an artifact produced by a task instead of referencing ${{ run.dir }}.
init
Rather than providing parameters via with (like you would in packages), you use init for embedded
runs, just like you would if you were configuring a trigger for a run.
Embedded runs only have access to the init parameters which you explicitly provided. In the example above,
init.only-at-the-top-level is not available to either embedded run and init.image-id is only available within the
cd embedded run.
use
Embedded runs are isolated from one another. They do not share filesystems to ensure that they behave the same way they would
when run in isolation. Because of this, embedded run tasks cannot use another task and other tasks cannot use an
embedded run task.
Instead, if you need to share information between embedded runs, you can access subtasks of embedded runs in
expressions. In the example above, the ci embedded run has a subtask named build-image which produces an image-id
value. The cd embedded run can access that value because it runs after ci. Packages continue to be encapsulated and
their subtasks are inaccessible (to provide better guarantees around breaking changes in packages).
after
Even though use is not permitted with embedded runs, you can still configure order of execution and dependencies via
after. In the example above, the cd embedded run will only run after the ci embedded run has successfully run.
Tool Caches
When using tool caches, embedded runs will not share the same tool cache as the triggered run. Runs should declare their own tool caches to ensure they can run while embedded and in isolation. When a run is embedded, the subtasks of that run respect the tool cache declared in the embedded run's definition.
Concurrency Pools
When your embedded run definition uses a concurrency pool, RWX respects that definition and acquires a lease within that pool prior to starting any tasks within the embedded run.
Dynamic Embedded Runs
RWX supports dynamic embedded runs in addition to referencing another run definition in your .rwx directory. To do so, first run a task that writes a run definition to a file and upload it as an artifact. Then, you can reference that artifact in the call expression: call: ${{ tasks.generate-run-definition.artifacts.definition }}. For a full example, take a look at the following tasks:
tasks:
- key: generate-run-definition
run: |
cat << EOF > definition.yml
base:
os: ubuntu 24.04
tag: 1.2
tasks:
- key: my-embedded-task
run: echo "I'm embedded"
EOF
outputs:
artifacts:
- key: definition
path: definition.yml
- key: embed-dynamic-run
call: ${{ tasks.generate-run-definition.artifacts.definition }}