This seems to have stoped working
special_steps = var.prefix == "dev" ? ["go"] : null
│ 118: for_each = local.special_steps[*]
│ ├────────────────
│ │ local.special_steps is null
│
│ Splat expressions (with the * symbol) cannot be applied to null sequences.
Any thoughts on that?
Hi @vongohren,
The [*]
behavior differs depending on whether you apply it to a sequence (list, set, or tuple) or to some other kind of value.
It seems like your local.special_steps
is a null list or null tuple, and so you can’t apply [*]
to it like this. The typical way to represent no items in a list is as an empty list rather than a null list, and so I’d suggest changing the definition of local.special_steps
to always be a non-null list which might sometimes have zero elements.
Thanks for feedback, and yeah I did this basically and it helped me.
But might there be a cleaner and better way of doing this?
I have an incomming flag, and I want to set a variable that shall affect many dynamic blocks so want to wrap them all under one flag. But having to make an array with “go” inside just seems not tooo clean 
Without some more context about what you’re doing (including, ideally, some full code examples) I’m not really sure what to suggest. The purpose of a dynamic block is to declare a block for each element of a collection, and the [*]
operator can be helpful to turn a nullable non-collection value into a list but if you are already working with lists anyway then you likely won’t need it, because for_each
can work with the list values directly.
Below you find variables and the code itself. Im building a dynamic CI pipeline module for all our frontend builds in a micro frontend architecture.
I like it all, except for the if else statemenst with the arrays of [“go”]. I wish I could decalare things a bit more cleaner. But I guess I just dont understand the splat command completly, so im just doing what works and moving on.
But if you have any immediate feedback on how to make use of splat even cleaner, that would be awesome
variable "prefix" {
description = "The prefix for the environment"
type = string
}
variable "appname" {
description = "The app name for naming things"
type = string
}
variable "build_env" {
description = "Any special build envs for the build step of the pipeline"
type = list(string)
default = null
}
variable "path_for_app" {
description = "The path for the app and where to find its code"
type = string
}
variable "release_tag" {
description = "Release tag if we are doing production pipeline"
type = string
default = null
}
variable "push_types" {
description = "This is a boolean to declare if push types is set or not"
type = bool
default = false
}
variable "have_assets" {
description = "This is a boolean to declare if push types is set or not"
type = bool
default = true
}
variable "folder_release" {
description = "This is a boolean to declare this is app is ready to be release via a folder or per file basis. It is backward compatible meaning it will set default false"
type = bool
default = false
}
module "import_map_service" {
source = "github.com/sourcecode"
prefix = var.prefix
}
locals {
bucket_name = "${var.prefix}-courier"
special_steps = var.prefix == "dev" ? ["go"] : []
type_publish = var.push_types == true ? ["go"] : []
prep_command = var.push_types == true ? "pre:publish" : "bump:version:commit"
push_assets = var.have_assets == true ? ["go"] : []
gsutil_js_file_args = var.folder_release == true ? ["cp", "-r", "dist/*${var.appname}*", "gs://${local.bucket_name}/${var.appname}/build-$COMMIT_SHA-$BUILD_ID/"] : ["cp", "dist/${var.appname}.js", "gs://${local.bucket_name}/${var.appname}/${var.appname}-$COMMIT_SHA-$BUILD_ID.js"]
js_file_location = var.folder_release == true ? "build-$COMMIT_SHA-$BUILD_ID/${var.appname}.js" : "${var.appname}-$COMMIT_SHA-$BUILD_ID.js"
assets_file_location = var.folder_release == true ? "build-$COMMIT_SHA-$BUILD_ID/src/" : "src/"
}
data "google_secret_manager_secret_version" "github_access_token_enc" {
secret = "github_access_token_enc"
}
data "google_secret_manager_secret_version" "npm_token" {
secret = "npm_ci_token"
}
resource "google_cloudbuild_trigger" "deployer" {
provider = google-beta
name = "${var.prefix}-web-${var.appname}-deployer"
substitutions = {
_PATH = var.path_for_app
}
github {
name = "repo"
owner = "owner"
push {
branch = var.prefix == "dev" ? "main" : null
tag = var.prefix == "dev" ? null : var.release_tag
}
}
build {
dynamic "step" {
for_each = local.special_steps[*]
content {
name = "gcr.io/cloud-builders/git"
entrypoint = "bash"
dir = "$${_PATH}"
args = [
"-c",
<<-EOF
git config --global user.name username
git config --global user.email email
git remote set-url origin url
git clone url
git fetch
git checkout $BRANCH_NAME
EOF
]
secret_env = [ "GITHUB_TOKEN" ]
}
}
step {
name = "gcr.io/cloud-builders/yarn"
id = "Install packages"
entrypoint = "yarn"
dir = "$${_PATH}"
args = ["install"]
}
step {
name = "gcr.io/cloud-builders/yarn"
entrypoint = "yarn"
dir = "$${_PATH}"
args = ["lint:prod"]
}
dynamic "step" {
for_each = local.special_steps[*]
content {
name = "gcr.io/cloud-builders/yarn"
entrypoint = "yarn"
dir = "$${_PATH}"
args = [local.prep_command]
}
}
step {
name = "gcr.io/cloud-builders/yarn"
id = "Build the app"
entrypoint = "yarn"
dir = "$${_PATH}"
env = var.build_env
args = ["build"]
}
step {
name = "gcr.io/cloud-builders/gsutil"
id = "Send js files to bucket"
dir = "$${_PATH}"
args = local.gsutil_js_file_args
}
dynamic "step" {
for_each = local.push_assets[*]
content {
name = "gcr.io/cloud-builders/gsutil"
id = "Send assets to bucket"
dir = "$${_PATH}"
args = ["cp", "-r", "src/assets", "gs://${local.bucket_name}/${var.appname}/${local.assets_file_location}"]
}
}
# https://httpie.io/ for easier deployment of the rootmap deployer
step {
name = "alpine/httpie"
id = "Call import map deployer with new url"
args = [
"--check-status",
"--ignore-stdin",
"-a",
"${module.import_map_service.username}:${module.import_map_service.password}",
"PATCH",
"${module.import_map_service.url}/services?env=${var.prefix}",
"service=\\@company/${var.appname}",
"url=https://storage.googleapis.com/${local.bucket_name}/${var.appname}/${local.js_file_location}"
]
}
dynamic "step" {
for_each = local.type_publish[*]
content {
name = "bash"
dir = "$${_PATH}"
args = ["mv",".npmrcpublish", ".npmrc"]
}
}
dynamic "step" {
for_each = local.type_publish[*]
content {
name = "gcr.io/cloud-builders/npm"
entrypoint = "npm"
dir = "$${_PATH}"
args = ["publish","--access", "public"]
env = ["NPM_TOKEN=${data.google_secret_manager_secret_version.npm_token.secret_data}"]
}
}
dynamic "step" {
for_each = local.special_steps[*]
content {
name = "gcr.io/cloud-builders/git"
entrypoint = "bash"
dir = "$${_PATH}"
args = [
"-c",
<<-EOF
git pull origin $BRANCH_NAME
git push origin $BRANCH_NAME
EOF
]
}
}
dynamic "secret" {
for_each = local.special_steps[*]
content {
kms_key_name = "kmsname"
secret_env = {
GITHUB_TOKEN = "${data.google_secret_manager_secret_version.github_access_token_enc.secret_data}"
}
}
}
}
included_files = ["${var.path_for_app}/**"]
ignored_files = [
"${var.path_for_app}/terraform/**",
"${var.path_for_app}/package.json"
]
}