Source code
- The
template-fileaction - contains the actual implementation of the templating. - An example of how to call the action
Github Actions and reusable workflows
Github Actions are one of the best tools for building and publishing your systems (especially when your company already uses Github, and especially when you don't even have a choice, welcome to enterprise development).
Github Actions have a concept of reusable workflows. Those allow you to expose / reuse parts of your actions to other parties, thus giving a possibility of creating a standardized flow.
Github itself provides several reusable workflows, and you've probably already used them many times. One of them is a checkout action which allows you to check out a repo.
Passing dynamic number of parameters
Reusable workflows allow defining a set of input and output params. The above checkout action can be a relatively comprehensive example (see here). And while in most of the cases it it is convenient to specify all the inputs as static variables, in some cases you don't know all the parameters in advance.
One of the examples is config templating. The templating action itself depends on the variables the config file contents, which are not known in advance. In this article I will go through one of the approaches to pass dynamic number of parameters using JSON and create a template-file action as an example.
Assumptions
- Inputs are considered trusted. The approach below allows someone to introduce a list of env vars into your workflow. While in most of the cases it doesn't matter, because the context of the action is defined by the caller, please take a second to see what an attacker can do with the action's environment in your particular case.
- Make sure that you need it. Please note it is always better to be explicit if you can, and specify all the parameters as inputs. If you are not sure - don't use this approach, and go with the standard static inputs instead.
General approach
To do that we will use the following approach
- Introduce a JSON object with all the variables we need as one of the inputs;
- Convert the variables from the object into the environment in the action using
jqwhich is included in the actions by default; - Introduce them into the environment using
exportandevalstatements; - Use
envsubstto do the actual substitution.
Step 0: Passing multiline strings
We will be getting our dynamic params as a JSON object. To pass a JSON string to an action, you can use one of the YAML miltiline strings, for example here:
TEMPLATE_VARS: |
{
"APP_ENVIRONMENT": "dev",
"LOG_LEVEL": "DEBUG"
}
Step 1: Checking out the repository
In order to access the repo code in the action, we'd need to checkout it. We can use the above checkout action provided by Github:
- name: Checkout
uses: actions/checkout@v4
Step 2: Converting JSON variables into the environment variables
Here is where the (standard Linux command) magic happens!
We can define our action step as the following:
- name: Template File
env:
# Expose as an env var to handle double quotes correctly in the JSON
TEMPLATE_VARS: ${{ inputs.TEMPLATE_VARS }}
run: |
set -ex
# Export vars from the input JSON
PARSED_VARS=$(echo "${TEMPLATE_VARS}" | jq 'to_entries[] | "\(.key)=\(.value)"' | xargs -I '{}' echo "export {}")
# Load the vars into the environment
eval "${PARSED_VARS}"
# Create envsubst template
ENVSUBST_VAR_LIST=$(echo "${TEMPLATE_VARS}" | jq 'to_entries[] | "${\(.key)}"' | xargs)
# Template the config file
envsubst "${ENVSUBST_VAR_LIST}" < ${{ inputs.TEMPLATE_FILE_PATH }} > ${{ inputs.TARGET_FILE_PATH }}
The following things are happening in the above:
- We define our input variable as an environment. This makes sure the quotes are not messed up in the output.
- Then we introduce a
PARSED_VARSvariable which contains a list ofexport $VARIABLE_1=valuestatements. We do that usingjqutility to extract keys and values from the input JSON. - We introduce the parsed variables into the environment using
eval. - We create a list of variables
envsubstshould substitute. This makes sure that standard variables like$PATHare not substituted, unless they are specified in the input JSON. - We run the
envsubstcommand to template our input file and store it in the target.
That's it! The output file should contain a result of substitution of the variables.
Top comments (0)