Closed zacwest closed 10 months ago
Is there a specific reason for using printf
to obtain the URL? Does the following method meet your requirements?
{{- $version := (gitHubLatestRelease "junegunn/fzf").TagName -}}
https://github.com/junegunn/fzf/releases/download/{{ $version }}/fzf-{{ $version }}-{{ .chezmoi.os }}_{{ .chezmoi.arch }}.tar.gz
Output:
https://github.com/junegunn/fzf/releases/download/0.44.0/fzf-0.44.0-darwin_amd64.tar.gz
Largely because I use the variable in a few other places (e.g. another $var set to print rather than printing inline). Just tried to slim down the examples here to the minimum case.
(I also find it much more readable, personally, when there's several substitutions.)
https://github.com/twpayne/chezmoi/releases/download/v%!s(*string=0x14000154540)/chezmoi-%!s(*string=0x14000154540)-aarch64.rpm
What's happening here is that the printf verbs don't match the type being passed. Go's text/template does some automatic translation of types which resolves the problem when you use trimPrefix
. Specifically:
(gitHubLatestRelease "twpayne/chezmoi").TagName
is actually a pointer to a string (*string
).trimPrefix
, text/template sees that trimPrefix
wants a string
so text/template dereferences the pointer for you. The return value from trimPrefix
is a string
.printf "%s"
wants a string
, but text/template doesn't know this as the type is in the value passed to printf, not in the function signature of printf itself. printf then prints %!s(*string=0x14000154540)
instead, which means "I didn't get something I could convert to a string, I got something else instead, and that thing is a *string
with this value".
sprig has a toString
template function, but, sadly, like most things in sprig, the function is broken and does not help you here.
The ugly quick short-term work-around is to leave the call to trimPrefix
in. I'm not sure what the best long term solution is.
@zacwest The sprig semver
function uses https://pkg.go.dev/github.com/Masterminds/semver/v3 which optionally supports v
-prefixed versions:
❯ '{{ $version := semver "v1.2.3" }}{{ printf "%s" $version }}' | chezmoi execute-template
1.2.3
❯ '{{ $version := semver "1.2.3" }}{{ printf "%s" $version }}' | chezmoi execute-template
1.2.3
❯ '{{ $version := semver (gitHubLatestRelease "twpayne/chezmoi").TagName }}{{ printf "%s" $version }}' | chezmoi execute-template
2.41.0
❯ '{{ $version := semver (gitHubLatestRelease "gorhill/uBlock").TagName }}{{ printf "%s" $version }}' | chezmoi execute-template
1.53.4
uBlock tags are not prefixed.
I'm not quite sure why this is showing incorrectly‡, but you can just call trim
(removes trailing and leading spaces) which will properly convert this to a usable string value.
{{- $version := (gitHubLatestRelease "twpayne/chezmoi").TagName | trim -}}
{{- printf "https://github.com/twpayne/chezmoi/releases/download/v%s/chezmoi-%s-aarch64.rpm" $version $version }}
There is another alternative that does the right thing, but would be a breaking change to adopt in chezmoi:
$ chezmoi execute-template '{{- $version := (gitHubLatestRelease "twpayne/chezmoi" | toJson | fromJson).tag_name -}}
{{- printf "https://github.com/twpayne/chezmoi/releases/download/v%s/chezmoi-%s-aarch64.rpm" $version $version }}'
https://github.com/twpayne/chezmoi/releases/download/vv2.41.0/chezmoi-v2.41.0-aarch64.rpm
‡ It appears that the value is returned as a string pointer, so changing %s
to %v
results in seeing the pointer address. I don't think that there's a way to dereference a pointer in go text templates, but sprig's trim*
functions appear to work correctly with either string or string pointers.
If there is a fix to be found in chezmoi, I think it would be in simulating a JSON
roundtrip for the GitHub values, rebuilding the result map with scalars rather than pointers. This would also be somewhat incompatible (it would break the exact flow of the fromJson | toJson
because the key shape would no longer switch from TagName
to tag_name
), but I suspect that is less disruptive than forcing everyone to use .tag_name
.
Here is another solution:
{{- $version := output "bash" "-c" "curl https://api.github.com/repos/junegunn/fzf/tags | jq -r '.[0].name'" | trim -}}
{{- printf "https://github.com/junegunn/fzf/releases/download/%s/fzf-%s-%s_%s.tar.gz" $version $version .chezmoi.os .chezmoi.arch -}}
Output:
https://github.com/junegunn/fzf/releases/download/0.44.0/fzf-0.44.0-linux_amd64.tar.gz
Because chezmoi only supports some basic GitHub template functions, this method uses GitHub's REST API and jq
to get any info you want.
That could be done with gitHubLatestTag
or gitHubTags
. If there are other GitHub APIs required, we would consider pull requests or feature suggestions — but these seem to be the ones most required. We also have jq
built into chezmoi (via itchyny/gojq).
$ chezmoi execute-template '{{-
$version := index ((gitHubTags "junegunn/fzf" | toJson | fromJson) | jq ".[0].name") 0
-}}{{-
printf "https://github.com/junegunn/fzf/releases/download/%s/fzf-%s-%s_%s.tar.gz" $version $version .chezmoi.os .chezmoi.arch
-}}'
https://github.com/junegunn/fzf/releases/download/0.44.0/fzf-0.44.0-darwin_arm64.tar.gz
The use of index $list 0
is required because the output of jq
is an array; this is unavoidable.
Hopefully this is now resolved. Please re-open if needed.
What exactly are you trying to do?
I'm guessing this is a Go Template thing that I'm holding wrong, but perhaps there's a better way to accomplish this or something can be done to improve it.
I'm trying to construct resource download URLs for GitHub released assets. For example, something like:
Works great, it outputs a URL like:
However, when I take out the
trimPrefix
for a repo that does not prefix release tags with 'v' I end up with something less expected:produces:
What have you tried so far?
My temporary hack is to use
trim
to do nothing but 'materialize' the pointer to an actual string value.Some cursory searching produced
toString
as an option, but the version included in Chezmoi turns it into:So slightly nicer pointer addresses, but pointer addresses nonetheless.
Where else have you checked for solutions?
Output of any commands you've tried with
--verbose
flagNo difference to the template rendering.
Output of
chezmoi doctor
Additional context
Add any other context about the problem here.