In the previous section we have seen how we can use input variables to configure dependencies, but what if our dependency has created some very interesting output that we would like to re-use or reference in our parent? For example: we may be deploying a website at a certain URL, a database that generates a new connection string, a Kubernetes cluster with new credentials, etc.
What we need is a programmatic way to deal with outputs coming out of our
dependencies. Let’s go back to our original escape.yml
for the
quickstart/hello-world
package and add an outputs
field:
name: quickstart/hello-world
version: 0.0.@
description:
logo:
includes:
- README.md
build: hello_world.sh
deploy: hello_world.sh
inputs:
- id: who
default: World
type: string
description: Who should we be greeting?
outputs:
- id: literal
default: "Some static configuration value"
description: Defining outputs as literals
- id: who
default: $this.inputs.who
description: Defining outputs using the Escape scripting language.
- id: script_output
description: This variable should be set in the hello_world.sh script.
This is very similar to configuring dependencies in that we can use literals or
the scripting language to define output
values. However, crucially, we can also let our script define output variables,
and if no default
output value is set Escape actually requires it:
$ escape run build
Build: Running build step /home/user/workspace/hello_world.sh.
Build: hello_world.sh: Hello World!
Build: Error: Missing value for variable 'script_output'
When Escape runs a script (e.g. build
, deploy
) it passes the location of a
file as a first argument. We can use this location in our script to write a
JSON object containing key-value pairs defining our outputs. BASH isn’t great
for JSON, but our assumption is that packages that are complex enough to
warrant outputs are probably not going to be written in BASH (although there’s
always jq). That being said, let’s make our
above plan work by changing hello_world.sh
to:
#!/bin/bash -e
echo "Hello ${INPUT_who}!"
echo '{"script_output": "We had a great time running this script. Thanks."}' > $1
This will make our build work again:
$ escape run build
Build: Running build step /home/user/workspace/hello_world.sh.
Build: hello_world.sh: Hello World!
Build: ✔️ Completed build%
You can’t tell from this log output, but behind the scenes Escape has persisted the outputs in the state file:
escape state show-deployment
Before we try and use the refurbished hello-world
package we should release
it to our Inventory so that it can be picked up by our parent package:
escape run release
The “parent” package we’ve built in the previous
section was using the following Escape plan
(saved in escape-dep.yml
):
name: quickstart/introduction
version: 0.0.@
description:
logo:
depends:
- release_id: quickstart/hello-world-latest
mapping:
who: $this.inputs.who
inputs:
- id: who
default: Everyone
Since this is a contrived example we’ll just show a few patterns:
Sometimes we’d like to make our dependency outputs available to our upstream dependencies. This can be useful for writing wrappers or exposing certain configuration outputs that may be useful down the line (e.g. URLs, …). We can once again use the scripting language to wire things up without having to fall-back to BASH scripts:
name: quickstart/introduction
version: 0.0.@
description:
logo:
depends:
- quickstart/hello-world-latest as hello
outputs:
- id: who
default: $hello.outputs.who
In our previous example we have used the parent input to configure a dependency, but we can also use a dependency output to configure a parent:
name: quickstart/introduction
version: 0.0.@
description:
logo:
depends:
- quickstart/hello-world-latest as who
inputs:
- id: literal
default: $hello.outputs.literal
eval_before_dependencies: false
Note the use of eval_before_dependencies
. Normally inputs are evaluated
before dependencies, but using this option we can defer it to after. Using
this technique we can for instance depend on a database package and use its
outputs to configure our application. However, there might be a slightly more
appropriate way to model this, which we’ll explore in the next section: