Cannot convert the output of readFile into List for iteration - go-templates

I am using helmfile for my helm releases.
I have a yaml file projects.yaml
# projects.yaml
- yako
- pera
- okta
In values.yaml.gotmpl, i want to iterate thru the content of projects.yaml :
# values.yaml.gotmpl
{{ $projects := readFile 'projects.yaml' }}
{{ range $_, $project := $projects }}
# do something with $project . e.g: tpl (readFile 'proj-config.yaml') (dict $name $project)
{{ end}}
Unfortunately , i got this error :
failed to render [values.yaml.gotmpl], because of template: stringTemplate:60:41:
executing "stringTemplate" at <$projects>: range can't iterate over projects:
- yako
- pera
- okta
I made a lot of blind attempts to make it work :
{{ range $_, $project := $projects | fromYaml }}
{{ range $_, $project := $projects | toYaml }}
{{ range $_, $project := $projects | list }}
No way 😩

{{ $projects := readFile "./projects.yaml"}}
{{ range $_, $project := get "projects" $projects }}
{{ $project.attr1 }}
{{end}}

Related

Call a variable inside the nested expression in github actions

I am trying to call the variable like this:
with:
tags: ${{ inputs.push_tag_to_release && 'op/post:'[env.tag] || 'op/post:${{ env.tag }}' }}
where,
env.tag = 0.13 (value)
but both the ways [env.tag] or ${{ env.tag }} are showing as incorrect/not-supported.
I tried like this,
tags: ${{ inputs.push_tag_to_release && 'op/post:'[env.tag] || 'op/post:${{ env.tag }}' }}
and the format function as well:
tags: ${{ inputs.push_tag_to_release && 'op/post:format({0},env.tag)' || 'op/post:${{ env.tag }}' }}
but not working in any way.

Hugo Pass Shortcode Variables From Pages Into sitemap.xml

Currently Hugo's built-in sitemap generator only creates two field attributes for images -- image:loc and image:license. Unfortunately this does not tell Google Bots much about the images context for SEO. The fields, image:title & image:caption at minimum is needed with the other two for best practices; Otherwise it's basically dead XML code.
I am working on a site that is image-content driven and since a short code is given in each page labeled as "Featured-Image" and contains the needed variables where I would like to extract/pass into the sitemap.
Here is an example of the shortcode within a page.md:
{{<
featured-image
name ="THE_IMAGE_NAME.jpg"
featuredTitle ="THE_IMAGE_TITLE"
location ="THE_IMAGE_LOCATION"
alt ="THE_IMAGE_ALT/CAPTION"
>}}
Within the sitemap.xml is what I would like to do:
{{ $license := .Site.Params.schema.license }}
{{ range .Data.Pages }}
...
{{ range $i := .Resources.ByType "image" }}
// Below is the additional data I am wanting to get from the pages'
// shortcode named "featured-image" -- But How Do I Get It?.
{{ $caption := .Get "alt" }}
{{ $location := .Get "location"}}
{{ $title := .Get "featuredTitle"}}
<image:image>
<image:loc>{{ $i.Permalink }}</image:loc>
<image:license>{{ $license }}</image:license>
// Below are the additional attributes needed:
<image:geo_location>{{ $location }}</image:geo_location>
<image:title>{{ $title }}</image:title>
<image:caption>{{ $caption }}</image:caption>
</image:image>
...

Bind class to data object property in Vue.js and Slim

I have the following slim template:
.dc-credit-card-label 'v-bind:class' => "{MyClass}"
span.dc-credit-card--stars ••••
span.dc-credit-card--stars ••••
span.dc-credit-card--stars ••••
span
| {{ payment.last4 }}
So I am trying to set the MyClass = 'some-cool-class'
but this doesn't work
Is there any chance to do this?
A comment from this git issue:
You just use the directive tags like normal but make them slim syntax.
.signed-in v-if="user"
= "Wecome, {{ user.name }}"
a.sign-out-button v-on:click="signOut" Sign out
So, use it "normal":
.dc-credit-card-label :class="{MyClass}"
span.dc-credit-card--stars ••••
span.dc-credit-card--stars ••••
span.dc-credit-card--stars ••••
span
| {{ payment.last4 }}

How can I conditionally set a variable in a Go template based on an expression which may cause an error if not wrapped with an if statement

Question
How do I do something like this:
{{ $use_ssl := (ne $.Env.CERT_NAME "") }}
where $.Env.CERT_NAME may be nil/undefined. If it is nil, it gives this error:
at <ne $.Env.CERT_NAME "">: error calling ne: invalid type for comparison
Note: I have no control over the objects passed in to the Go template so have to solve this entirely within the template itself.
What I've tried
I tried to work around by first checking to see if it is non-empty:
{{ $use_ssl := ( ($.Env.CERT_NAME) && (ne $.Env.CERT_NAME "") ) }}
but it gives this error:
unexpected "&" in operand
So I switched to this, which is allowed syntactically:
{{ $use_ssl := (and ($.Env.CERT_NAME) (ne $.Env.CERT_NAME "") ) }}
but then I lose the short-circuit evaluation that I would get with the && operator and I'm back to getting this error again:
at <ne $.Env.CERT_NAME ...>: error calling ne: invalid type for comparison
Okay, I thought, if I can't do it in a nice one-liner, and Go doesn't have a ternary operator, that's fine, I'll just do it the idiomatic Go way, which is apparently if/else.
{{ if $.Env.CERT_NAME }}
{{ $use_ssl := (ne $.Env.CERT_NAME "") }}
{{ else }}
{{ $use_ssl := false }}
{{ end }}
But then of course I run into scoping issues, because if inexplicably (or at least annoyingly) creates a new variable scope (unlike in Ruby/ERB templates that I am more used to), so apparently I can't even do this:
{{ if $.Env.CERT_NAME }}
{{ $use_ssl := true }}
{{ else }}
{{ $use_ssl := false }}
{{ end }}
# $use_ssl: {{ $use_ssl }}
without getting this error now:
undefined variable "$use_ssl"
"No sweat", I thought. I'll just declare the variable in the outside scope so it will have the correct scope and the inner scope will still be able to change that variable (the one with the outer scope). (That's how it works in Ruby, by the way.)
Nope, apparently all that does is create 2 different variables with the same name but different scopes (how's that for confusing!):
{{ $use_ssl := false }}
{{ if $.Env.CERT_NAME }}
{{ $use_ssl := true }}
# $use_ssl: {{ $use_ssl }}
{{ else }}
{{ $use_ssl := false }}
{{ end }}
# $use_ssl: {{ $use_ssl }}
# $use_ssl: true
# $use_ssl: false
I've also tried inlining the if statement like this:
{{ $use_ssl := if $.Env.CERT_NAME { true } }}
but that gives this error:
unexpected <if> in command
Apparently if statements can't be used as expressions in Go like they can in Ruby?
I also get syntax errors if I try the various alternatives to the ternary operator suggested in What is the idiomatic Go equivalent of C's ternary operator?, like:
c := map[bool]int{true: 1, false: 0} [5 > 4]
And sure, you can read variables from outer scopes in inner scopes with $.something but how do you set $.something in the inner scope?
If not like that, then how??
So what have I missed? Is this even possible in a Go template?
Go templates (I'm new to them) seem very limiting. Almost everything you can do in Go itself is not possible in a template. But hopefully there is a workaround...
http://play.golang.org/p/SufZdsx-1v has a clever workaround involving creating a new object with {{$hasFemale := cell false}} and then setting a value with $hasFemale.Set true, but how can I do something like that without having access to the code that evaluates the template (where they are calling template.FuncMap)?
Others who have tried and failed
https://groups.google.com/forum/#!topic/golang-nuts/MUzNZ9dbHrg
This is the biggest (and only) problem I have with Go. I simply do not understand why this functionality has not been implemented yet.
When the template package is used in a generic context (eg. Executing
an arbitrary template file with an arbitrary json file), you might not
have the luxury of doing precalculations.
https://github.com/golang/go/issues/10608
This is probably as designed, but it would be really nice if there was a way to get the changed $v outside of the conditional.
This is the #1 template question we get over at Hugo (https://github.com/spf13/hugo). We have added a hack [$.Scratch.Set "v1" 123] to work around it for now ...
Currently working solution
If you update the scope rather than declare a variable, either by using set or by using merge you will be able to access it outside of the if clause.
{{- if eq .podKind "core" -}}
{{- $dummy := set . "matchNodePurpose" .Values.scheduling.corePods.nodeAffinity.matchNodePurpose }}
{{- else if eq .podKind "user" -}}
{{- $dummy := set . "matchNodePurpose" .Values.scheduling.userPods.nodeAffinity.matchNodePurpose }}
{{- end -}}
# Will now output what you decided within an if/else if clause
{{- .matchNodePurpose }}
Future solution (Available with Helm 2.10)
I came across the same question in the context of Helm templates which are go templates with some additional features pre-installed such as Sprig.
28 March 2018, in this pr a Terenary operator was added to Sprig, and in time Helm will have them as well solving the issue both you and I have.
terenary
true | ternary "b" "c" will return "b"
false | ternary "b" "c" will return "c"
Your fourth try would work if you'd use the assignment operator instead of the definition operator:
{{ $use_ssl := false }}
{{ if $.Env.CERT_NAME }}
{{ $use_ssl = true }} # notice the missing colon!
{{ else }}
{{ $use_ssl = false }}
{{ end }}
I think I may have found a workaround for my particular problem, though I would love to hear other answers that solve the problem more generally.
Came across this line in https://github.com/jwilder/nginx-proxy/blob/1e0b930/nginx.tmpl#L91:
{{ $default_host := or ($.Env.DEFAULT_HOST) "" }}
which gave me another idea to try. I guess the solution is to use a combination of or and set extra temporary variables...
{{ $cert_name := or $.Env.CERT_NAME "" }}
{{ $use_ssl := ne $cert_name "" }}

Compare strings in templates

I have the following template:
{{if . eq "login failed"}}
<span class="text-error">Incorrect username or password</span>
{{else if . eq "login success"}}
<span class="text-success">You have successfully logged in!</span>
{{end}}
I am passing a string when I execute the template.
However, I get the following error:
executing "login.html" at <.>: can't give argument to non-function .
How do I compare the strings within the template?
eq is function, not an operator. It is called with the form: eq <x> <y> (not <x> eq <y>).
You can fix your template by moving the operands from the the sides of eq to after it:
{{if eq . "login failed"}}
<span class="text-error">Incorrect username or password</span>
{{else if eq . "login success"}}
<span class="text-success">You have successfully logged in!</span>
{{end}}
To compare if two strings equal :
{{ if eq .Status "Approved" }}
...Do something
{{ else }}
...Do something else
{{ end }}
To compare if two strings not equal :
{{ if ne .Status "" }} // If status is not empty
...Do something
{{ else }}
...Do something else
{{ end }}
There are more golang HTML template directives here at : https://pkg.go.dev/text/template#hdr-Actions