When building CI/CD pipelines it is often the case that you'd like to "tokenize" a configuration file, so that the values in the file can be calculated during the build/release execution process.
This can be for all kinds of reasons, such as ingesting environment variables or by basing values on the outputs of other CLI tools, such as terraform (e.g. reading IDs of recently created infrastructure).
Rather than separating the key/value "tokens" from the "token calculation" logic, I wanted a way to embed Bash scripts directly into my JSON file and then have interpret the values into an output file.
For example, the JSON file might look like:
[
{
"Key": "verbatim",
"Value": "just a string"
},
{
"Key": "environment-variable",
"Value": "$HOME"
},
{
"Key": "shell-script",
"Value": "$(dotnet --version)"
}
]
With the invokable tokens defined, the below script can be run against the JSON file in order to parse and execute the tokens:
jsonFile=$1
tempFile=$2
echo "[" > "$tempFile"
itemCount=$(jq '. | length' $jsonFile)
currentIndex=0
jq -c '.[]' $jsonFile | while read -r obj; do
key=$(echo "$obj" | jq -r '.Key')
value=$(echo "$obj" | jq -r '.Value')
if [[ "$value" == \$\(* ]]; then
command=$(echo "$value" | sed 's/^\$\((.*)\)$/\1/')
newValue=$(eval $command)
elif [[ "$value" == \$* ]]; then
# It's an environment variable
varName=${value:1}
newValue=${!varName}
else
newValue="$value"
fi
updatedObj=$(echo "$obj" | jq --arg newValue "$newValue" '.Value = $newValue')
echo "$updatedObj" >> "$tempFile"
((currentIndex++))
if [[ $currentIndex -lt $itemCount ]]; then
echo ',' >> "$tempFile"
fi
done
echo "]" >> "$tempFile"
Running the command looks like
./json_exec.sh example.json example.out.json
And the output then looks as follows:
[
{
"Key": "verbatim",
"Value": "just a string"
}
,
{
"Key": "environment-variable",
"Value": "/home/craig"
}
,
{
"Key": "shell-script",
"Value": "8.0.202"
}
]