cirrus-ci-docs
cirrus-ci-docs copied to clipboard
Branch specific environment variables
Often development branches should access different resources than e.g. a release branch. To specify that in an economical way, it is useful to set environment variables to different values, depending on the branch.
Right now there is, as far as I can tell, no good way to have branch specific variables. only_if only works on the basis of entire tasks, but typically one would have several tasks that all need the different environment variable.
As far as I know, the only way to implement this right now is to do something like
foo_task:
matrix:
- only_if: $CIRRUS_BRANCH=='main'
env:
BRANCH_SPECIFIC_VALUE: I am the main branch
- only_if: $CIRRUS_BRANCH!='main'
env:
BRANCH_SPECIFIC_VALUE: I am not on the main branch
but that would need to be copied into every task. And one cannot use yaml templates, at least not in a simple way, because "matrix can be defined only under a task, docker_builder or pipe", e.g. https://cirrus-ci.com/build/5782179685859328
I guess this feature in Starlark configuration can help you to define some of the environment variables globally. You can add .cirrus.star file with the following content:
load("cirrus", "env")
def main():
return {
"env": branchSpecificEnv(),
}
def branchSpecificEnv():
if env.get("CIRRUS_BRANCH") == "main":
return { BRANCH_SPECIFIC_VALUE": "I am the main branch" }
return { BRANCH_SPECIFIC_VALUE": "I am not on the main branch" }
I'll let @edigaryev to chip in since he is more familiar with the matter. But IMO the example above should work as expected.
Yes, I'm now using starlark (thanks to #962 being fixed). Unfortunately it doesn't easily allow to use branch specific secrets etc, because starlark doesn't see those.
@anarazel what do you mean by "branch specific secrets"? You want to be able to access value of an encrypted variable in Starlark by its name?
@anarazel what do you mean by "branch specific secrets"?
The concrete scenario: I have a task building VM images and containers. For pull requests and development branches the resulting images should go into a "-dev" GCP project. On the main branch they should go into the "main" GCP project. Both of the projects have dedicated and different service accounts for this purpose. To be able to use the file: instructions an environment variable needs to contain the secret. The file: variable_name: variable reference cannot itself be a variable at the moment.
My current solution is that I have two file: containing secrets, the .cirrus.yml sets ${BRANCH_TYPE} and GOOGLE_APPLICATION_CREDENTIALS is defined as gcp-service-account-${BRANCH_TYPE}.json.
You want to be able to access value of an encrypted variable in Starlark by its name?
Yes, that'd be one way, although I don't think it's necessarily a great one.
It'd be nicer if I could configure a different secret for the "main" branch than for other branches, so that pull requests don't even have access to the same secrets. While pull requests are set to be manually approved before running, I would still prefer if they didn't have access to the "main" branch secret.
I think something like that should work:
# .cirrus.star
load("cirrus", "env")
def main():
return {
"env": {
"GCP_CREDENTIALS_CONTENT": "ENCRYPTED[{}]".format(branchSpecificCredentialsName())
},
}
def branchSpecificCredentialsName():
if env.get("CIRRUS_BRANCH") == "main":
return "qwerty123"
return "asdfgh987"
Then you can use the file instruction in YAML:
# .cirrus.yml
env:
GOOGLE_APPLICATION_CREDENTIALS: /tmp/gcp-service-account.json
task:
...
gcp_creds_file:
path: $GOOGLE_APPLICATION_CREDENTIALS
variable_name: GCP_CREDENTIALS_CONTENT
This way only one secret will be always used.
If you are launching gce_instances within the same project you want to push the image in, then something like that will be even simplier:
def main():
return {
"gcp_credentials": "ENCRYPTED[{}]".format(branchSpecificCredentialsName())
}