Skip to content

Instantly share code, notes, and snippets.

@jdolitsky
Created April 13, 2024 06:25
Show Gist options
  • Save jdolitsky/dd100e362a0d722a0a423b4140ee8959 to your computer and use it in GitHub Desktop.
Save jdolitsky/dd100e362a0d722a0a423b4140ee8959 to your computer and use it in GitHub Desktop.
Update the "h1:..." hashes in .terraform.lock.hcl
#!/usr/bin/env bash
# This script obtains the "h1:..." hashes for all platforms
# See https://github.com/hashicorp/terraform/issues/27264
set -o errexit; set -o nounset; set -o pipefail
export GOBIN="${PWD}/bin" PATH="${PWD}/bin:${PATH}"; TMPDIR="$(mktemp -d)"
# Note: All modules in the lockfile MUST have zips for these platforms
PLATFORMS=("darwin/amd64" "darwin/arm64" "linux/amd64" "linux/arm64")
# Create simple "zip-h1-hash" tool to calculate h1 hash from zip files
echo "[setup] building zip-h1-hash binary"
(mkdir -p zip-h1-hash && cd zip-h1-hash && echo '
package main; import ( "fmt"; "os"; d "golang.org/x/mod/sumdb/dirhash" )
func main() { h, _ := d.HashZip(os.Args[1], d.Hash1); fmt.Println(h) }
' > main.go && (go mod init zip-h1-hash 2>/dev/null || true) &&
go mod tidy 2>/dev/null && go install .)
# Local installation of "tq" (github.com/jdolitsky/tq)
echo "[setup] installing tq"
go install github.com/jdolitsky/tq@27312f980cea7014f71fbb309fd99f75e808da6f # v0.3.0
# Rewrite .terraform.lock.hcl h1 hash entires, one provider at a time
for provider in $(tq '.body.blocks[] | select(.type == "provider").labels[0]' .terraform.lock.hcl | sed 's|registry.terraform.io/||'); do
version="$(tq '.body.blocks[] | select(.labels[0] == "registry.terraform.io/'${provider}'").attributes["version"]' .terraform.lock.hcl | sed 's|"||g')"
echo "[provider:${provider}] version: ${version}"
zh_hashes=()
h1_hashes=()
for zh_hash in $(tq '.body.blocks[] | select(.labels[0] == "registry.terraform.io/'${provider}'").attributes["hashes"]' .terraform.lock.hcl | grep 'zh:' | cut -d'"' -f 2); do
zh_hashes+=("${zh_hash}")
done
echo "[provider:${provider}] all zh hashes: ${zh_hashes[@]}"
for platform in ${PLATFORMS[@]}; do
meta_url="https://registry.terraform.io/v1/providers/${provider}/${version}/download/${platform}"
echo "[provider:${provider}] fetching meta from ${meta_url}"
zip_url="$(curl -sL "${meta_url}" | jq -r .download_url)"
echo "[provider:${provider}] fetching zip from ${zip_url}"
out="$(basename "${zip_url}")"
curl -sLo "${TMPDIR}/${out}" "${zip_url}"
h1_hash="$(zip-h1-hash "${TMPDIR}/${out}")"
echo "[provider:${provider}] ${h1_hash} is the h1 hash for ${platform} based on ${out}"
h1_hashes+=("${h1_hash}")
done
echo "[provider:${provider}] all h1 hashes: ${h1_hashes[@]}"
v="";
for h in $(echo "${h1_hashes[@]}" | tr ' ' '\n' | sort); do v="${v}\n\\\"${h}\\\","; done
for h in $(echo "${zh_hashes[@]}" | tr ' ' '\n' | sort); do v="${v}\n\\\"${h}\\\","; done
echo -e "# This file is maintained automatically by \"make tf-lockfile\".\n" > .terraform.lock.hcl.tmp
tq '.body.blocks[] |= if (
.labels[0] == "registry.terraform.io/'${provider}'"
) then (
.attributes["hashes"] = "['${v}'\n]"
) else . end' .terraform.lock.hcl >> .terraform.lock.hcl.tmp
mv .terraform.lock.hcl.tmp .terraform.lock.hcl
echo "[provider:${provider}] hashes updated in .terraform.lock.hcl"
done
@secustor
Copy link

secustor commented Apr 13, 2024

This will only work if the provider ZIP does not contain any subfolders, as hashZip and hashDir return not the same hash for the same content in that specific case.

You have to use DirHash to be compatible to Terraform.
Source I had to do the same for Renovate in Typescript.

@jdolitsky
Copy link
Author

@secustor ah thank you. so need to unzip the files, then run DirHash? do you have a code example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment