Flow Variables
In Liquibase 4.17.0+, you can define variables as key-value pairs to use in one or more stages of your flow files. When you run the flow
command, Liquibase substitutes the variable names for their values.
You can define variables in three ways:
- As global variables in the flow file's header section
- As stage variables within individual stages
- Included in an external YAML file that you reference in the flow file's header section
In Liquibase 4.24.0+, flow files inherit variables from other flow files that you call on. This lets you pass variables through multiple nested layers of flow files.
Stage variables have the highest precedence, followed by included variables, then global variables, and then variables inherited from another flow file.
Uses
You can define a variable once but reuse it many times in different situations in your flow file. This provides flexibility and reduces code duplication. For example, variables may be useful to efficiently run multiple kinds of commands that contain the same file, directory, or other string.
It is a best practice to use global variables for things that can apply to your whole flow file, and stage variables for things that only apply to specific stages. Included variables are best for things that apply to multiple flow files.
You can also check the values of variables by using Flow Conditionals to dynamically run code in your flow files.
In Liquibase 4.26.0+, you can specify a default variable value for Liquibase to use if it doesn't receive an explicit one. For example, you could use this to specify optional command arguments (such as contexts and labels) as NULL
if not explicitly specified in your definition.
Syntax
Define variables
Variable definitions always use the syntax key: "value"
. You must specify quotation marks around the value.
You can define included variables in an external YAML file, in this case liquibase.flowvariables.yaml
:
PROJECT_NAME: "MyFlowProject"
THIS_DATE: "2024-05-12T15-00-20"
You can define global variables at the top of your flow file, in this case liquibase.myflowfile.yaml
:
globalVariables:
STATUS_FILE: "status.txt"
UPDATE_LOG: "update.log"
HISTORY_FILE: "history.txt"
You can define stage variables within a particular stage, in this case stage-status
:
stages:
stage-status:
stageVariables:
STATUS_FILE: "status.txt"
Use variables
Variable calls in your flow files use the following syntax:
- Included variables:
${NAMESPACE.VARIABLE_KEY}
- Global and stage variables:
${VARIABLE_KEY}
For example, we can run the status
command in a flow file using included, global, and stage variables. In the following example, the included variables all have the namespace FLOW_VARS
and reference variables we previously defined in the file liquibase.flowvariables.yaml
:
include:
FLOW_VARS: liquibase.flowvariables.yaml
globalVariables:
DIRECTORY_NAME: "./${FLOW_VARS.PROJECT_NAME}_${FLOW_VARS.THIS_DATE}"
STATUS_FILE: "status.txt"
stages:
stage-status:
stageVariables:
VERBOSE_STATE: "true"
actions:
- type: liquibase
command: status
globalArgs: {output-file: "${DIRECTORY_NAME}/${STATUS_FILE}"}
cmdArgs: {verbose: "${VERBOSE_STATE}"}
Default values
In Liquibase 4.26.0 and later, you can give variables default values using the following syntax:
Example: VARIABLE_KEY: "${VARIABLE_KEY:-defaultValue}"
This is useful if you want to dynamically specify optional arguments for commands in your flow file. For example:
globalVariables:
VERBOSE_STATE: "${VERBOSE_STATE:-null}"
# Set the default value to null
stages:
stage-status:
stageVariables:
VERBOSE_STATE: "true"
# Set a value for this stage only
actions:
- type: liquibase
command: status
cmdArgs: {verbose: "${VERBOSE_STATE}"}
# This stage passes a non-null value to verboseState
other-stage:
actions:
- type: liquibase
command: status
cmdArgs: {verbose: "${VERBOSE_STATE}"}
# This stage doesn't define any stage variables
# Liquibase uses the definition from the global variable, which defaults to null
In the preceding example, VERBOSE_STATE
is set to null by default as a global variable and is set to a non-null value as a stage variable. During stage-status
, Liquibase passes the non-null value. During stage-other
, VERBOSE_STATE
is not defined as a stage variable, so Liquibase passes the global value, which is null. When Liquibase detects a null value in a global or command argument, it omits the entire argument at runtime.
Note: It's possible to define a variable without default values both globally and in a stage. In this design, you simply rely on the stage variable's higher precedence to control the value of the output in different situations. However, explicitly specifying default values can make the intended behavior of a flow file clearer to developers.
Default values can also be useful when you use them with Flow Conditionals. For example:
globalVariables:
UPDATE: "${UPDATE:-null}"
CHANGELOG_FILE: "${CHANGELOG_FILE:-simple-changelog.xml}"
stages:
someStage:
actions:
- type: liquibase
if: "UPDATE != null"
command: update
cmdArgs: {changelog-file: "${CHANGELOG_FILE}"}
In the preceding example, Liquibase checks whether the value of the global variable UPDATE
is not null before executing the rest of the action.
Note: If you define a default value for a variable, the default is inherited by any nested flow files that call on the parent flow file. The default value also applies to the nested flow files.
Environment variables
You can substitute the values of environment variables in your flow file. The syntax is the same as substituting other variables. For example:
stages:
systemEnvars:
actions:
- type: shell
command: echo "Running Liquibase from ${LIQUIBASE_HOME}"
commandStuff:
actions:
- type: liquibase
command: update-sql
globalArgs: {changelog-file: "${LIQUIBASE_OUTPUT_FILE}"}
This lets you avoid manually updating information in your flow file, such as the path of the system environment variable LIQUIBASE_HOME
, which could change if you later modify your installation directory.
Substituting environment variables may also be useful if you want to avoid modifying your flow file whenever you change the value of Liquibase parameters like LIQUIBASE_OUTPUT_FILE
. Instead of defining and updating variables in your flow file, you can simply update the values of the external environment variables the flow file calls on. Then, when you run your flow file, Liquibase uses the new values without you ever having modified the flow file itself.
Command substitutions
In Liquibase 4.28.0 and later, you can also use variables in the command
field of a flow file action. For example:
globalVariables:
MY_COMMAND: "status"
MY_CHANGELOG: "newChangelog.txt"
stages:
myStage:
actions:
- type: liquibase
command: ${MY_COMMAND}
cmdArgs: {changelog-file: "${MY_CHANGELOG}"}
The preceding example calls on a global variable from the command
field. You can use the same syntax to call on environment variables instead.
Empty strings
You can specify the empty string as the value of a variable. For example, if you want to run all changesets in your changelog regardless of their contexts/labels:
stages:
default:
stageVariables:
LABEL_NAMES: ""
actions:
- type: liquibase
command: update
cmdArgs: {label-filter: "${LABEL_NAMES}"}
Property substitution with full YAML strings
In Liquibase 4.28.0 and later, you can also use variables to dynamically expand into full YAML strings during property substitution. The YAML strings you insert can also contain other variables, which can expand into more YAML strings.
For example, this may be useful if you want multiple stages of a single flow file to run a command, but you want to substitute different variables to modify that command in each stage of the flow file.
globalVariables:
COMMAND_ARGS: "changelog-file: './liquibase.${STAGE_CHANGELOG}.yaml'"
stages:
stage01:
# Set up vars for property substitution in THIS stage only
stageVariables:
STAGE_CHANGELOG: "stage01"
actions:
# Run the update command
- type: liquibase
command: update
cmdArgs: {"${COMMAND_ARGS}"}
# This expands to: cmdArgs: { changelog-file: './liquibase.${STAGE_CHANGELOG}.yaml'}
# which expands to: cmdArgs: { changelog-file: './liquibase.stage01.yaml'}
Example Flow File
Defines variables that you can use in other flow files.
# List of variables
PROJNAME: "MyFlowProj"
THISDATE: "2022-11-28T15-00-20"
Includes command arguments, global arguments, and a referenced include file (YAML).
# Bring in and namespace an external file with yaml 'key: val' pairs for use in this file
include:
FLOWVARS: liquibase.flowvariables.yaml
# Set up some global variables for property substitution in ANY stage
globalVariables:
DIRNAME: "./${FLOWVARS.PROJNAME}_${FLOWVARS.THISDATE}"
STATUSFILE: "status.txt"
UPDATELOG: "update.log"
HISTORYFILE: "history.txt"
# Start of the stages.
stages:
# A prep stage. There can be more than one stage if desired.
stage-prep:
actions:
- type: shell
command: bash -c "mkdir -p ${DIRNAME}"
# Another stage.
stage-dowork:
# set up vars for property substitution in THIS stage only
stageVariables:
VERBOSESTATE: TRUE
actions:
# Do a validate command
- type: liquibase
command: validate
# Tell me what is pending a deployment
- type: liquibase
command: status
globalArgs:
outputfile: "${DIRNAME}/${STATUSFILE}"
showbanner: false
cmdArgs: {verbose: "${VERBOSESTATE}"}
# Policy Checks for changelog
- type: liquibase
command: checks run
cmdArgs: {checks-scope: changelog}
# Run update
- type: liquibase
command: update
# Policy Checks for database
- type: liquibase
command: checks run
cmdArgs: {checks-scope: database}
# Create a history file
- type: liquibase
command: history
globalArgs: {outputfile: "${DIRNAME}/${HISTORYFILE}"}