From fdc00cbe71bc40b76558291242d354c2cab7fbc5 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 22:10:16 -0400 Subject: [PATCH 01/23] Add and config files --- .editorconfig | 37 +++++++++++++++++++++++++++++++++++++ .shellcheckrc | 2 ++ buildpack.toml | 2 ++ 3 files changed, 41 insertions(+) create mode 100644 .editorconfig create mode 100644 .shellcheckrc diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..52615237 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,37 @@ +# https://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.sh] +binary_next_line = true +# We use tabs in shell scripts since heredoc indent stripping (<<-) requires them: +# https://www.gnu.org/software/bash/manual/html_node/Redirections.html#Here-Documents +indent_style = tab +shell_variant = bash +switch_case_indent = true + +# Catches scripts without a .sh file extension, such as the Buildpack API scripts. +[**/bin/**] +binary_next_line = true +indent_style = tab +shell_variant = bash +switch_case_indent = true + +# Catches sbin/ scripts without a .sh extension. +[**/sbin/**] +binary_next_line = true +indent_style = tab +switch_case_indent = true + +# Vendored third-party script that must not be modified. +[test/shunit2.sh] +ignore = true + +[Makefile] +indent_style = tab diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 00000000..da9f6a16 --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,2 @@ +# Enable all checks, including the optional ones that are off by default. +enable=all diff --git a/buildpack.toml b/buildpack.toml index 1b3a949b..6d2c265f 100644 --- a/buildpack.toml +++ b/buildpack.toml @@ -7,6 +7,8 @@ name = "Go" "test/", "sbin/", "file-cache/", + ".editorconfig", ".gitignore", + ".shellcheckrc", "Makefile" ] \ No newline at end of file From f6351693582bc57f47f8e380662c9e0c210a6cec Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 22:13:33 -0400 Subject: [PATCH 02/23] Add Makefile format and lint targets --- Makefile | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index de837f1c..41cb7472 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,17 @@ -.PHONY: test test-parallel run run-ci publish +.PHONY: lint lint-scripts check-format format test test-parallel run run-ci publish + +lint: lint-scripts check-format + +lint-scripts: + @git ls-files -z --cached --others --exclude-standard 'bin/*' 'sbin/*' '*.sh' \ + | grep -zv '^test/shunit2\.sh$$' \ + | xargs -0 shellcheck --check-sourced --color=always + +check-format: + @shfmt --diff . + +format: + @shfmt --write --list . STACK ?= heroku-24 FIXTURE ?= test/fixtures/mod-basic-go126 From 30f83f5d6ab31c775b724fd694481078659a9a52 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 22:14:28 -0400 Subject: [PATCH 03/23] Run `shmft` in CI --- .github/workflows/ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6148c9bd..3abe775e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,22 @@ on: permissions: contents: read +env: + FORCE_COLOR: 1 + jobs: + lint: + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Run ShellCheck + run: make lint-scripts + - name: Run shfmt + uses: docker://mvdan/shfmt:latest + with: + args: "--diff ." + integration-test: runs-on: ubuntu-latest strategy: From 646c786f819cadd40f41e2f1f09deb041d52022b Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 22:16:41 -0400 Subject: [PATCH 04/23] Apply `shfmt` formatting --- bin/compile | 341 +++++------ bin/detect | 61 +- bin/test | 42 +- bin/test-compile | 14 +- lib/common.sh | 379 +++++++------ sbin/add-version | 10 +- sbin/publish.sh | 6 +- sbin/update-go-versions | 82 +-- .../mod-basic-with-hooks/bin/go-post-compile | 2 +- .../mod-basic-with-hooks/bin/go-pre-compile | 2 +- test/run.sh | 530 +++++++++--------- test/utils.sh | 345 ++++++------ 12 files changed, 913 insertions(+), 901 deletions(-) diff --git a/bin/compile b/bin/compile index 9a57a0ea..40bbe621 100755 --- a/bin/compile +++ b/bin/compile @@ -31,37 +31,37 @@ snapshotBinBefore # Clean up old cache files if migrating from old cache structure. # We detect this legacy structure by looking for 'go-path' in the root. if [ -d "${cache_root}/go-path" ] && [ ! -d "${cache}/go-path" ]; then - output::step "Clearing old build cache artifacts" - - # Remove artifacts that were previously stored in the cache root - rm -rf "${cache_root}"/go[0-9]* \ - "${cache_root}"/devel-* \ - "${cache_root}/go-path" \ - "${cache_root}/go-build-cache" \ - "${cache_root}/glide" \ - "${cache_root}/gb" \ - "${cache_root}/dep" \ - "${cache_root}/godep" \ - "${cache_root}/govendor" \ - "${cache_root}/.tq" \ - "${cache_root}/.jq" \ - "${cache_root}/github.com/golang-migrate" \ - "${cache_root}/github.com/mattes" 2>/dev/null || true + output::step "Clearing old build cache artifacts" + + # Remove artifacts that were previously stored in the cache root + rm -rf "${cache_root}"/go[0-9]* \ + "${cache_root}"/devel-* \ + "${cache_root}/go-path" \ + "${cache_root}/go-build-cache" \ + "${cache_root}/glide" \ + "${cache_root}/gb" \ + "${cache_root}/dep" \ + "${cache_root}/godep" \ + "${cache_root}/govendor" \ + "${cache_root}/.tq" \ + "${cache_root}/.jq" \ + "${cache_root}/github.com/golang-migrate" \ + "${cache_root}/github.com/mattes" 2>/dev/null || true fi DefaultGoVersion="$(<${DataJSON} jq -r '.Go.DefaultVersion')" handleDefaultPkgSpec() { - if [ "${pkgs}" = "default" ]; then - output::warning <<-EOF + if [ "${pkgs}" = "default" ]; then + output::warning <<-EOF Installing package '.' (default) To install a different package spec add a comment in the following form to your go.mod file: // +heroku install ./cmd/... EOF - pkgs="." - fi + pkgs="." + fi } # Expand a version identifier to supported versions of Go. If the version @@ -70,125 +70,128 @@ handleDefaultPkgSpec() { # data.json for supported expansions. All others are returned as is without go # prepended to it. expandVer() { - local v="${1}" - if [[ "${v}" =~ ^[[:digit:]]+ ]]; then - v="go${v}" - fi - echo $(<${DataJSON} jq -r 'if .Go.VersionExpansion."'${v}'" then .Go.VersionExpansion."'${v}'" else "'${v}'" end') + local v="${1}" + if [[ "${v}" =~ ^[[:digit:]]+ ]]; then + v="go${v}" + fi + echo $(<${DataJSON} jq -r 'if .Go.VersionExpansion."'${v}'" then .Go.VersionExpansion."'${v}'" else "'${v}'" end') } # Report development versions to user # Use after expandVer reportVer() { - local ver="${1}" - case $ver in - devel*) - output::warning <<-EOF + local ver="${1}" + case $ver in + devel*) + output::warning <<-EOF You are using a development build of Go. This is provided for users requiring an unreleased Go version but is otherwise unsupported. Build tests are NOT RUN!! EOF - ;; - esac + ;; + esac } ensureGo() { - local goVersion="${1}" - local goPath="${cache}/${goVersion}/go" - local goFile="" - local txt="Installing ${goVersion}" - if [ -d "${goPath}" ]; then - output::step "Using ${goVersion}" - else - #For a go version change, we delete everything in our namespace - output::step "New Go Version, clearing old cache" - if [ -d "${cache}/go-path" ]; then - find "${cache}/go-path" ! -perm -u=w -print0 | xargs -r -0 chmod u+w 2>&1 - fi - rm -rf ${cache}/* - case "${goVersion}" in - devel*) - local bGoVersion="$(expandVer ${DefaultGoVersion})" - goFile="${bGoVersion}.linux-amd64.tar.gz" - goPath="${cache}/${bGoVersion}/go" - txt="Installing bootstrap ${bGoVersion}" - ;; - go1) - goFile="go.go1.linux-amd64.tar.gz" - ;; - *) - goFile="${goVersion}.linux-amd64.tar.gz" - ;; - esac - - output::step "${txt}" - ensureFile "${goFile}" "${goPath}" "tar -C ${goPath} --strip-components=1 -zxf" - rm -f "${goPath}/${goFile}" - - case "${goVersion}" in - devel*) - pushd "${cache}" &> /dev/null - mkdir -p "${goVersion}" - pushd "${goVersion}" &> /dev/null - local sha=$(echo ${goVersion} | cut -d - -f 2) #assumes devel- or devel- - local url="https://github.com/golang/go/archive/$sha.tar.gz" - output::step "Downloading development Go version ${goVersion}" - ${CURL} ${url} | tar zxf - - mv go-${sha}* go - output::step "Compiling development Go version ${goVersion}" - pushd go/src &> /dev/null - echo "devel +${sha} $(date "+%a %b %H:%M:%S %G %z")"> ../VERSION - GOROOT_BOOTSTRAP=$(pushd ${cache}/${bGoVersion}/go > /dev/null; pwd; popd > /dev/null) ./make.bash 2>&1 - popd &> /dev/null - go/bin/go version - rm -rf "${goPath}" - popd &> /dev/null - popd &> /dev/null - goPath="${cache}/${goVersion}/go" - ;; - *) - ;; - esac - fi - - export GOROOT="${goPath}" - PATH="${goPath}/bin:${PATH}" - - # Export GOCACHE if Go >= 1.10 - if go env | grep -q '^GOCACHE='; then - export GOCACHE="${cache}/go-build-cache" - fi + local goVersion="${1}" + local goPath="${cache}/${goVersion}/go" + local goFile="" + local txt="Installing ${goVersion}" + if [ -d "${goPath}" ]; then + output::step "Using ${goVersion}" + else + #For a go version change, we delete everything in our namespace + output::step "New Go Version, clearing old cache" + if [ -d "${cache}/go-path" ]; then + find "${cache}/go-path" ! -perm -u=w -print0 | xargs -r -0 chmod u+w 2>&1 + fi + rm -rf ${cache}/* + case "${goVersion}" in + devel*) + local bGoVersion="$(expandVer ${DefaultGoVersion})" + goFile="${bGoVersion}.linux-amd64.tar.gz" + goPath="${cache}/${bGoVersion}/go" + txt="Installing bootstrap ${bGoVersion}" + ;; + go1) + goFile="go.go1.linux-amd64.tar.gz" + ;; + *) + goFile="${goVersion}.linux-amd64.tar.gz" + ;; + esac + + output::step "${txt}" + ensureFile "${goFile}" "${goPath}" "tar -C ${goPath} --strip-components=1 -zxf" + rm -f "${goPath}/${goFile}" + + case "${goVersion}" in + devel*) + pushd "${cache}" &>/dev/null + mkdir -p "${goVersion}" + pushd "${goVersion}" &>/dev/null + local sha=$(echo ${goVersion} | cut -d - -f 2) #assumes devel- or devel- + local url="https://github.com/golang/go/archive/$sha.tar.gz" + output::step "Downloading development Go version ${goVersion}" + ${CURL} ${url} | tar zxf - + mv go-${sha}* go + output::step "Compiling development Go version ${goVersion}" + pushd go/src &>/dev/null + echo "devel +${sha} $(date "+%a %b %H:%M:%S %G %z")" >../VERSION + GOROOT_BOOTSTRAP=$( + pushd ${cache}/${bGoVersion}/go >/dev/null + pwd + popd >/dev/null + ) ./make.bash 2>&1 + popd &>/dev/null + go/bin/go version + rm -rf "${goPath}" + popd &>/dev/null + popd &>/dev/null + goPath="${cache}/${goVersion}/go" + ;; + *) ;; + esac + fi + + export GOROOT="${goPath}" + PATH="${goPath}/bin:${PATH}" + + # Export GOCACHE if Go >= 1.10 + if go env | grep -q '^GOCACHE='; then + export GOCACHE="${cache}/go-build-cache" + fi } warnGoVersionOverride() { - if [ ! -z "${GOVERSION}" ]; then - output::warning <<-EOF + if [ ! -z "${GOVERSION}" ]; then + output::warning <<-EOF Using \$GOVERSION override. \$GOVERSION = ${GOVERSION} If this isn't what you want please run: heroku config:unset GOVERSION -a EOF - fi + fi } warnPackageSpecOverride() { - if [ ! -z "${GO_INSTALL_PACKAGE_SPEC}" ]; then - output::warning <<-EOF + if [ ! -z "${GO_INSTALL_PACKAGE_SPEC}" ]; then + output::warning <<-EOF Using \$GO_INSTALL_PACKAGE_SPEC override. \$GO_INSTALL_PACKAGE_SPEC = ${GO_INSTALL_PACKAGE_SPEC} If this isn't what you want please run: heroku config:unset GO_INSTALL_PACKAGE_SPEC -a EOF - fi + fi } installPkgs() { - output::step "Running: go install -v ${FLAGS[*]} ${pkgs}" - go install -v "${FLAGS[@]}" ${pkgs} 2>&1 | output::indent + output::step "Running: go install -v ${FLAGS[*]} ${pkgs}" + go install -v "${FLAGS[@]}" ${pkgs} 2>&1 | output::indent } loadEnvDir "${env_dir}" @@ -209,21 +212,21 @@ build_data::set_string "go_version" "${ver}" # Track whether the version was pinned (fully specified) if [ "${requested_go_version}" = "${ver}" ]; then - build_data::set_raw "go_version_pinned" "true" + build_data::set_raw "go_version_pinned" "true" else - build_data::set_raw "go_version_pinned" "false" + build_data::set_raw "go_version_pinned" "false" fi # If $GO_LINKER_SYMBOL and GO_LINKER_VALUE are set, tell the linker to DTRT if [ -n "${GO_LINKER_SYMBOL}" -a -n "${GO_LINKER_VALUE}" ]; then - FLAGS+=(-ldflags "-X ${GO_LINKER_SYMBOL}=${GO_LINKER_VALUE}") + FLAGS+=(-ldflags "-X ${GO_LINKER_SYMBOL}=${GO_LINKER_VALUE}") fi if [ -e "${build}/bin" -a ! -d "${build}/bin" ]; then - output::error <<-EOF + output::error <<-EOF Error: File bin exists and is not a directory. EOF - exit 1 + exit 1 fi reportVer "${ver}" @@ -237,40 +240,40 @@ mkdir -p "${build}/bin" export GOPATH mainPackagesInModule() { - # For an explanation of what this is doing, see https://dave.cheney.net/2014/09/14/go-list-your-swiss-army-knife - # Stderr (module download progress) is indented and redirected back to stderr so it displays - # neatly under the current step without being captured into the command substitution's stdout. - go list -find "${FLAGS[@]}" -f '{{ if eq .Name "main" }} {{.ImportPath}} {{ end }}' ./... 2> >(output::indent >&2) + # For an explanation of what this is doing, see https://dave.cheney.net/2014/09/14/go-list-your-swiss-army-knife + # Stderr (module download progress) is indented and redirected back to stderr so it displays + # neatly under the current step without being captured into the command substitution's stdout. + go list -find "${FLAGS[@]}" -f '{{ if eq .Name "main" }} {{.ImportPath}} {{ end }}' ./... 2> >(output::indent >&2) } setupProcfile() { - if [ -f "${build}/Procfile" ]; then - return - fi - local pkgs=$(mainPackagesInModule) - if [ -z "${pkgs}" ]; then - return - fi - local pf=() - for pkg in ${pkgs}; do - local bn=$(basename ${pkg}) - pf+=("${bn}: bin/${bn}") - done - if [ ${#pf} -eq 0 ]; then - return - fi - output::step "Created a Procfile with the following entries:" - if [ ${#pf[@]} -eq 1 ]; then - local line="web: bin/$(echo ${pf[0]} | cut -d / -f 2)" - echo "${line}" > ${build}/Procfile - echo "${line}" | output::indent - elif [ ${#pf[@]} -gt 1 ]; then - for pfl in "${pf[@]}"; do - echo "${pfl}" >> ${build}/Procfile - echo "${pfl}" | output::indent - done - fi - output::notice <<-EOF + if [ -f "${build}/Procfile" ]; then + return + fi + local pkgs=$(mainPackagesInModule) + if [ -z "${pkgs}" ]; then + return + fi + local pf=() + for pkg in ${pkgs}; do + local bn=$(basename ${pkg}) + pf+=("${bn}: bin/${bn}") + done + if [ ${#pf} -eq 0 ]; then + return + fi + output::step "Created a Procfile with the following entries:" + if [ ${#pf[@]} -eq 1 ]; then + local line="web: bin/$(echo ${pf[0]} | cut -d / -f 2)" + echo "${line}" >${build}/Procfile + echo "${line}" | output::indent + elif [ ${#pf[@]} -gt 1 ]; then + for pfl in "${pf[@]}"; do + echo "${pfl}" >>${build}/Procfile + echo "${pfl}" | output::indent + done + fi + output::notice <<-EOF If these entries look incomplete or incorrect please create a Procfile with the required entries. See https://devcenter.heroku.com/articles/procfile for more details about Procfiles @@ -283,10 +286,10 @@ cd ${build} export GOBIN="${build}/bin" if [ "${GO_SETUP_GOPATH_FOR_MODULE_CACHE}" = "true" ]; then - export GOPATH="${build}/.heroku/go-path" - mkdir -p $GOPATH # ensure that it's created + export GOPATH="${build}/.heroku/go-path" + mkdir -p $GOPATH # ensure that it's created else - export GOPATH="${cache}/go-path" + export GOPATH="${cache}/go-path" fi # TODO: Check the desired language version (eg `go mod edit -json | jq -r '.Go'`) @@ -294,26 +297,26 @@ fi # when the desired language version is 1.14+ and vendoring should be used. # https://golang.org/doc/go1.14#vendor if [ -d "${build}/vendor" -a -s "${build}/go.sum" ]; then - FLAGS+=(-mod=vendor) + FLAGS+=(-mod=vendor) fi output::step "Determining packages to install" pkgs=${GO_INSTALL_PACKAGE_SPEC:-$(awk '{ if ($1 == "//" && $2 == "+heroku" && $3 == "install" ) { print substr($0, index($0,$4)); exit }}' ${goMOD})} if [ -z "${pkgs}" ]; then - pkgs=$(mainPackagesInModule) - if [ -z "${pkgs}" ]; then - output::warning <<-EOF + pkgs=$(mainPackagesInModule) + if [ -z "${pkgs}" ]; then + output::warning <<-EOF No main packages found in module ${name} Unsure what package(s) to install, defaulting to '.' EOF - pkgs="default" - else - output::step "Detected the following main packages to install:" - for pkg in ${pkgs}; do - echo "${pkg}" | output::indent - done - fi + pkgs="default" + else + output::step "Detected the following main packages to install:" + for pkg in ${pkgs}; do + echo "${pkg}" | output::indent + done + fi fi warnPackageSpecOverride @@ -321,17 +324,17 @@ handleDefaultPkgSpec unset GIT_DIR # unset git dir or it will mess with goinstall if [[ -f bin/go-pre-compile ]]; then - output::step "Running bin/go-pre-compile hook" - chmod a+x bin/go-pre-compile - bin/go-pre-compile 2>&1 | output::indent + output::step "Running bin/go-pre-compile hook" + chmod a+x bin/go-pre-compile + bin/go-pre-compile 2>&1 | output::indent fi installPkgs if [[ -f bin/go-post-compile ]]; then - output::step "Running bin/go-post-compile hook" - chmod a+x bin/go-post-compile - bin/go-post-compile 2>&1 | output::indent + output::step "Running bin/go-post-compile hook" + chmod a+x bin/go-post-compile + bin/go-post-compile 2>&1 | output::indent fi build_data::set_duration "dependencies_install_duration" "${dependencies_install_start_time}" @@ -339,27 +342,27 @@ build_data::set_duration "dependencies_install_duration" "${dependencies_install _newOrUpdatedBinFiles=$(binDiff) output::step "Installed the following binaries:" for fyle in ${_newOrUpdatedBinFiles}; do - echo "${fyle}" | output::indent + echo "${fyle}" | output::indent done setupProcfile cd $build mkdir -p $build/.profile.d -echo 'PATH=$PATH:$HOME/bin' > $build/.profile.d/go.sh +echo 'PATH=$PATH:$HOME/bin' >$build/.profile.d/go.sh if [ "${GO_INSTALL_TOOLS_IN_IMAGE}" = "true" ]; then - output::step "Copying go tool chain to \$GOROOT=\$HOME/.heroku/go" - mkdir -p "${build}/.heroku/go" - cp -a "${GOROOT}/"* "${build}/.heroku/go" - echo 'export GOROOT=$HOME/.heroku/go' > "${build}/.profile.d/goroot.sh" - echo 'PATH=$PATH:$GOROOT/bin' >> "${build}/.profile.d/goroot.sh" + output::step "Copying go tool chain to \$GOROOT=\$HOME/.heroku/go" + mkdir -p "${build}/.heroku/go" + cp -a "${GOROOT}/"* "${build}/.heroku/go" + echo 'export GOROOT=$HOME/.heroku/go' >"${build}/.profile.d/goroot.sh" + echo 'PATH=$PATH:$GOROOT/bin' >>"${build}/.profile.d/goroot.sh" fi t="${build}/.heroku/go" mkdir -p "${t}" t="${t}/.meta" -echo "TOOL=${TOOL}" > "${t}" -echo "NAME=${name}" >> "${t}" +echo "TOOL=${TOOL}" >"${t}" +echo "NAME=${name}" >>"${t}" build_data::set_duration "total_duration" "${compile_start_time}" diff --git a/bin/detect b/bin/detect index f8cee7d6..10234959 100755 --- a/bin/detect +++ b/bin/detect @@ -10,39 +10,48 @@ source "${buildpack}/lib/output.sh" # Detect go.mod (supported) and legacy dependency manager files (unsupported, # but we still want to pass detection so that bin/compile can provide a helpful # migration error message rather than the generic "can't detect language" output). -if test -f "${build}/go.mod" || #go modules - test -f "${build}/Gopkg.lock" || #dep - test -f "${build}/Godeps/Godeps.json" || # godeps - test -f "${build}/vendor/vendor.json" || # govendor - test -f "${build}/glide.yaml" || # glide - (test -d "${build}/src" && test -n "$(find "${build}/src" -mindepth 2 -type f -name '*.go' | sed 1q)") # gb -then - echo Go +if test -f "${build}/go.mod" \ + || + #go modules + test -f "${build}/Gopkg.lock" \ + || + #dep + test -f "${build}/Godeps/Godeps.json" \ + || + # godeps + test -f "${build}/vendor/vendor.json" \ + || + # govendor + test -f "${build}/glide.yaml" \ + || + # glide + (test -d "${build}/src" && test -n "$(find "${build}/src" -mindepth 2 -type f -name '*.go' | sed 1q)"); then # gb + echo Go else - output::error <<-EOF - Error: Your app is configured to use the Go buildpack, - but we couldn't find a go.mod file. + output::error <<-EOF + Error: Your app is configured to use the Go buildpack, + but we couldn't find a go.mod file. - A Go app on Heroku must have a 'go.mod' file in the root - directory of its source code. + A Go app on Heroku must have a 'go.mod' file in the root + directory of its source code. - Currently the root directory of your app contains: + Currently the root directory of your app contains: - $(ls -1A --indicator-style=slash "${build}" || true) + $(ls -1A --indicator-style=slash "${build}" || true) - If your app already has a go.mod file, check that it: + If your app already has a go.mod file, check that it: - 1. Is in the top level directory (not a subdirectory). - 2. Has the correct spelling (the filenames are case-sensitive). - 3. Isn't listed in '.gitignore' or '.slugignore'. - 4. Has been added to Git using 'git add go.mod' and committed. + 1. Is in the top level directory (not a subdirectory). + 2. Has the correct spelling (the filenames are case-sensitive). + 3. Isn't listed in '.gitignore' or '.slugignore'. + 4. Has been added to Git using 'git add go.mod' and committed. - To create a go.mod file, run 'go mod init ' in your - project directory. + To create a go.mod file, run 'go mod init ' in your + project directory. - For help with using Go on Heroku, see: - https://devcenter.heroku.com/articles/getting-started-with-go - https://devcenter.heroku.com/articles/go-support + For help with using Go on Heroku, see: + https://devcenter.heroku.com/articles/getting-started-with-go + https://devcenter.heroku.com/articles/go-support EOF - exit 1 + exit 1 fi diff --git a/bin/test b/bin/test index 8ffe14a0..144511c1 100755 --- a/bin/test +++ b/bin/test @@ -13,26 +13,26 @@ source "${testpack}/lib/common.sh" # loadEnvDir "${env_dir}" # For now, load all of env_dir if [ ! -z "${env_dir}" ]; then - for f in ${env_dir}/*; do - key=$(basename $f) - if [ -f "${f}" ]; then - export "${key}=$(cat "${f}" | sed -e "s:\${build_dir}:${build}:")" - fi - done + for f in ${env_dir}/*; do + key=$(basename $f) + if [ -f "${f}" ]; then + export "${key}=$(cat "${f}" | sed -e "s:\${build_dir}:${build}:")" + fi + done fi if [ -f "${build}/.heroku/go/.meta" ]; then - source "${build}/.heroku/go/.meta" + source "${build}/.heroku/go/.meta" fi export GOROOT="${build}/.heroku/go" # Use the GOPATH that was setup during compile, if it exists, so that # any modules downloaded during compile are re-used. if [ -d "${build}/.heroku/go-path" ]; then - export GOPATH="${build}/.heroku/go-path" + export GOPATH="${build}/.heroku/go-path" else - # Otherwise use a GOPATH that is outside of ${build} - export GOPATH="$(mktemp -d)" + # Otherwise use a GOPATH that is outside of ${build} + export GOPATH="$(mktemp -d)" fi PATH="${build}/bin:${GOROOT}/bin:${PATH}" @@ -43,27 +43,27 @@ output=$(mktemp) cd "${build}" if [ -f "${build}/.golangci.yml" -o -f "${build}/.golangci.toml" -o -f "${build}/.golangci.json" ]; then - output::step "Running: golangci-lint -v --build-tags heroku run" - ${build}/.heroku/golangci/bin/golangci-lint -v --build-tags heroku run 2>&1 | output::indent + output::step "Running: golangci-lint -v --build-tags heroku run" + ${build}/.heroku/golangci/bin/golangci-lint -v --build-tags heroku run 2>&1 | output::indent fi _tflags=("-race" "-v") if [ -s "./go.sum" -a -d "./vendor" ]; then - _tflags+=("-mod=vendor") + _tflags+=("-mod=vendor") else - _tflags+=("-mod=readonly") + _tflags+=("-mod=readonly") fi output::step "Running Tests With: go test ${_tflags[*]} ./... | patter" go test ${_tflags[@]} ./... 2>&1 | tee -a ${output} | patter echo if [[ -z "${GO_TEST_SKIP_BENCHMARK}" ]]; then - _tflags=("${_tflags[@]:1}") # push -race off - _tflags+=("-run=_") - _tflags+=("-bench=.") - _tflags+=("-benchmem") - output::step "Running Benchmarks With: go test ${_tflags[*]} ./..." - go test ${_tflags[@]} ./... 2>&1 | output::indent + _tflags=("${_tflags[@]:1}") # push -race off + _tflags+=("-run=_") + _tflags+=("-bench=.") + _tflags+=("-benchmem") + output::step "Running Benchmarks With: go test ${_tflags[*]} ./..." + go test ${_tflags[@]} ./... 2>&1 | output::indent fi output::step "Standard (Non TAP) test output" -output::indent < "${output}" +output::indent <"${output}" output::step "Finished" diff --git a/bin/test-compile b/bin/test-compile index 93ca00c7..4414979f 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -7,15 +7,15 @@ cache=$(cd "$2/" && pwd) env_dir="${3}" testpack=$(cd "$(dirname $0)/.." && pwd) -echo "true" > "${env_dir}/GO_INSTALL_TOOLS_IN_IMAGE" -echo "true" > "${env_dir}/GO_SETUP_GOPATH_FOR_MODULE_CACHE" +echo "true" >"${env_dir}/GO_INSTALL_TOOLS_IN_IMAGE" +echo "true" >"${env_dir}/GO_SETUP_GOPATH_FOR_MODULE_CACHE" buildpack="${testpack}" source ${testpack}/bin/compile "${build}" "${cache}" "${env_dir}" if [ -f "${build}/.golangci.yml" -o -f "${build}/.golangci.toml" -o -f "${build}/.golangci.json" ]; then - output::step "/.golangci.{yml,toml,json} detected" - tmp="$(mktemp -d)" - mkdir -p "${build}/.heroku/golangci/bin" - ensureFile "golangci-lint-1.20.0-linux-amd64.tar.gz" "${tmp}" "tar -C ${build}/.heroku/golangci/bin --strip-components=1 -zxf" -fi \ No newline at end of file + output::step "/.golangci.{yml,toml,json} detected" + tmp="$(mktemp -d)" + mkdir -p "${build}/.heroku/golangci/bin" + ensureFile "golangci-lint-1.20.0-linux-amd64.tar.gz" "${tmp}" "tar -C ${build}/.heroku/golangci/bin --strip-components=1 -zxf" +fi diff --git a/lib/common.sh b/lib/common.sh index 2e254f16..db247f14 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -19,59 +19,58 @@ TOOL="" GO_LINKER_VALUE=${SOURCE_VERSION} snapshotBinBefore() { - if [ ! -d "${build}/bin" ]; then - return 0 - fi - _oifs=$IFS - IFS=$'\n' - _binBefore=() - for f in ${build}/bin/*; do - if [ -f $f ]; then - _binBefore+=($(shasum $f)) - fi - done - IFS=$_oifs + if [ ! -d "${build}/bin" ]; then + return 0 + fi + _oifs=$IFS + IFS=$'\n' + _binBefore=() + for f in ${build}/bin/*; do + if [ -f $f ]; then + _binBefore+=($(shasum $f)) + fi + done + IFS=$_oifs } binDiff() { - _oifs=$IFS - IFS=$'\n' - local binAfter=() - for f in ${build}/bin/*; do - if [ -f $f ]; then - binAfter+=($(shasum $f)) - fi - done - - local new=() - for a in "${binAfter[@]}"; do - local let found=0 - - for b in "${_binBefore[@]}"; do - if [ "${a}" = "${b}" ]; then - let found+=1 - fi - done - - if [ $found -eq 0 ]; then - new+=( "./bin/$(basename $(echo $a | awk '{print $2}' ) )" ) - fi - done - IFS=$_oifs - echo ${new[@]} + _oifs=$IFS + IFS=$'\n' + local binAfter=() + for f in ${build}/bin/*; do + if [ -f $f ]; then + binAfter+=($(shasum $f)) + fi + done + + local new=() + for a in "${binAfter[@]}"; do + local let found=0 + + for b in "${_binBefore[@]}"; do + if [ "${a}" = "${b}" ]; then + let found+=1 + fi + done + + if [ $found -eq 0 ]; then + new+=("./bin/$(basename $(echo $a | awk '{print $2}'))") + fi + done + IFS=$_oifs + echo ${new[@]} } - knownFile() { - local fileName="${1}" - <${FilesJSON} jq -e 'to_entries | map(select(.key == "'${fileName}'")) | any' &> /dev/null + local fileName="${1}" + <${FilesJSON} jq -e 'to_entries | map(select(.key == "'${fileName}'")) | any' &>/dev/null } downloadFile() { - local fileName="${1}" + local fileName="${1}" - if ! knownFile ${fileName}; then - output::error <<-EOF + if ! knownFile ${fileName}; then + output::error <<-EOF Error: The requested file (${fileName}) is unknown to the buildpack! The buildpack tracks and validates the SHA256 sums of the files @@ -81,106 +80,106 @@ downloadFile() { To find out more info about this error please visit: https://devcenter.heroku.com/articles/unknown-go-buildack-files EOF - exit 1 - fi - - local targetDir="${2}" - local xCmd="${3}" - local targetFile="${targetDir}/${fileName}" - - mkdir -p "${targetDir}" - pushd "${targetDir}" &> /dev/null - output::step "Fetching ${fileName}" - local url="$(<"${FilesJSON}" jq -r '."'${fileName}'".URL')" - ${CURL} -o "${fileName}" "${url}" 2>&1 | output::indent - if ! SHAValid "${fileName}" "${targetFile}"; then - output::error <<-EOF - Error: Downloaded file (${fileName}) SHA does not match recorded SHA. - - Unable to continue. - EOF - exit 1 - fi - if [ -n "${xCmd}" ]; then - ${xCmd} ${targetFile} 2>&1 | output::indent - fi - popd &> /dev/null + exit 1 + fi + + local targetDir="${2}" + local xCmd="${3}" + local targetFile="${targetDir}/${fileName}" + + mkdir -p "${targetDir}" + pushd "${targetDir}" &>/dev/null + output::step "Fetching ${fileName}" + local url="$(<"${FilesJSON}" jq -r '."'${fileName}'".URL')" + ${CURL} -o "${fileName}" "${url}" 2>&1 | output::indent + if ! SHAValid "${fileName}" "${targetFile}"; then + output::error <<-EOF + Error: Downloaded file (${fileName}) SHA does not match recorded SHA. + + Unable to continue. + EOF + exit 1 + fi + if [ -n "${xCmd}" ]; then + ${xCmd} ${targetFile} 2>&1 | output::indent + fi + popd &>/dev/null } SHAValid() { - local fileName="${1}" - local targetFile="${2}" - local expected="$(<"${FilesJSON}" jq -r '."'${fileName}'".SHA')" - local actual="$(shasum -a256 "${targetFile}" | cut -d \ -f 1)" - [ "${actual}" = "${expected}" ] + local fileName="${1}" + local targetFile="${2}" + local expected="$(<"${FilesJSON}" jq -r '."'${fileName}'".SHA')" + local actual="$(shasum -a256 "${targetFile}" | cut -d \ -f 1)" + [ "${actual}" = "${expected}" ] } ensureFile() { - local fileName="${1}" - local targetDir="${2}" - local xCmd="${3}" - local targetFile="${targetDir}/${fileName}" - local download="false" - if [ ! -f "${targetFile}" ]; then - download="true" - elif ! SHAValid "${fileName}" "${targetFile}"; then - download="true" - fi - if [ "${download}" = "true" ]; then - downloadFile "${fileName}" "${targetDir}" "${xCmd}" - fi + local fileName="${1}" + local targetDir="${2}" + local xCmd="${3}" + local targetFile="${targetDir}/${fileName}" + local download="false" + if [ ! -f "${targetFile}" ]; then + download="true" + elif ! SHAValid "${fileName}" "${targetFile}"; then + download="true" + fi + if [ "${download}" = "true" ]; then + downloadFile "${fileName}" "${targetDir}" "${xCmd}" + fi } addToPATH() { - local targetDir="${1}" - if echo "${PATH}" | grep -v "${targetDir}" &> /dev/null; then - PATH="${targetDir}:${PATH}" - fi + local targetDir="${1}" + if echo "${PATH}" | grep -v "${targetDir}" &>/dev/null; then + PATH="${targetDir}:${PATH}" + fi } ensureInPath() { - local fileName="${1}" - local targetDir="${2}" - local xCmd="${3:-chmod a+x}" - addToPATH "${targetDir}" - ensureFile "${fileName}" "${targetDir}" "${xCmd}" + local fileName="${1}" + local targetDir="${2}" + local xCmd="${3:-chmod a+x}" + addToPATH "${targetDir}" + ensureFile "${fileName}" "${targetDir}" "${xCmd}" } loadEnvDir() { - local envFlags=() - envFlags+=("CGO_CFLAGS") - envFlags+=("CGO_CPPFLAGS") - envFlags+=("CGO_CXXFLAGS") - envFlags+=("CGO_LDFLAGS") - envFlags+=("GO_LINKER_SYMBOL") - envFlags+=("GO_LINKER_VALUE") - envFlags+=("GOFLAGS") - envFlags+=("GOPROXY") - envFlags+=("GOPRIVATE") - envFlags+=("GONOPROXY") - envFlags+=("GOVERSION") - envFlags+=("GO_INSTALL_PACKAGE_SPEC") - envFlags+=("GO_INSTALL_TOOLS_IN_IMAGE") - envFlags+=("GO_SETUP_GOPATH_FOR_MODULE_CACHE") - envFlags+=("GO_TEST_SKIP_BENCHMARK") - local env_dir="${1}" - if [ ! -z "${env_dir}" ]; then - mkdir -p "${env_dir}" - env_dir=$(cd "${env_dir}/" && pwd) - for key in ${envFlags[@]}; do - if [ -f "${env_dir}/${key}" ]; then - export "${key}=$(cat "${env_dir}/${key}" | sed -e "s:\${build_dir}:${build}:")" - fi - done - fi + local envFlags=() + envFlags+=("CGO_CFLAGS") + envFlags+=("CGO_CPPFLAGS") + envFlags+=("CGO_CXXFLAGS") + envFlags+=("CGO_LDFLAGS") + envFlags+=("GO_LINKER_SYMBOL") + envFlags+=("GO_LINKER_VALUE") + envFlags+=("GOFLAGS") + envFlags+=("GOPROXY") + envFlags+=("GOPRIVATE") + envFlags+=("GONOPROXY") + envFlags+=("GOVERSION") + envFlags+=("GO_INSTALL_PACKAGE_SPEC") + envFlags+=("GO_INSTALL_TOOLS_IN_IMAGE") + envFlags+=("GO_SETUP_GOPATH_FOR_MODULE_CACHE") + envFlags+=("GO_TEST_SKIP_BENCHMARK") + local env_dir="${1}" + if [ ! -z "${env_dir}" ]; then + mkdir -p "${env_dir}" + env_dir=$(cd "${env_dir}/" && pwd) + for key in ${envFlags[@]}; do + if [ -f "${env_dir}/${key}" ]; then + export "${key}=$(cat "${env_dir}/${key}" | sed -e "s:\${build_dir}:${build}:")" + fi + done + fi } clearGitCredHelper() { - git config --global --unset credential.helper + git config --global --unset credential.helper } setGitCredHelper() { - git config --global credential.helper '!#GoGitCredHelper + git config --global credential.helper '!#GoGitCredHelper env_dir="'$(cd ${1}/ && pwd)'" gitCredHelper() { #echo "${1}\n" >&2 #debug @@ -250,50 +249,50 @@ setGitCredHelper() { } supportsGoModules() { - local version="${1}" - # Ex: "go1.10.4" | ["go1","10", "4"] | ["1","10","4"] | [1,10,4] | [1] [10] == exit 1 (fail) - echo "\"${version}\"" | jq -e 'split(".") | map(gsub("go";"")) | map(tonumber) | .[0] >= 1 and .[1] < 11' &> /dev/null + local version="${1}" + # Ex: "go1.10.4" | ["go1","10", "4"] | ["1","10","4"] | [1,10,4] | [1] [10] == exit 1 (fail) + echo "\"${version}\"" | jq -e 'split(".") | map(gsub("go";"")) | map(tonumber) | .[0] >= 1 and .[1] < 11' &>/dev/null } determineTool() { - # Check GOVERSION first - it overrides all tool-specific configurations - if [ -n "${GOVERSION}" ]; then - ver="${GOVERSION}" - go_version_origin="GOVERSION" - build_data::set_string "go_version_origin" "${go_version_origin}" - build_data::set_string "go_version_requested" "${ver}" - fi - - if [ -f "${goMOD}" ]; then - TOOL="gomodules" - build_data::set_string "go_tool" "${TOOL}" - - output::step "Detected go modules via go.mod" - - # Determine Go version from go.mod if not already set by GOVERSION - if [ -z "${ver}" ]; then - ver=$(awk '{ if ($1 == "//" && $2 == "+heroku" && $3 == "goVersion" ) { print $4; exit } }' ${goMOD}) - if [ -n "${ver}" ]; then - go_version_origin="go.mod (heroku comment)" - else - ver=$(awk '{ if ($1 == "go" ) { print "go" $2; exit } }' ${goMOD}) - if [ -n "${ver}" ]; then - go_version_origin="go.mod" - else - ver=${DefaultGoVersion} - go_version_origin="default" - fi - fi - build_data::set_string "go_version_origin" "${go_version_origin}" - build_data::set_string "go_version_requested" "${ver}" - fi - - name=$(awk '{ if ($1 == "module" ) { gsub(/"/, "", $2); print $2; exit } }' < ${goMOD}) - output::step "Detected Module Name: ${name}" - warnGoVersionOverride - - if [ "${go_version_origin}" = "default" ]; then - output::warning <<-EOF + # Check GOVERSION first - it overrides all tool-specific configurations + if [ -n "${GOVERSION}" ]; then + ver="${GOVERSION}" + go_version_origin="GOVERSION" + build_data::set_string "go_version_origin" "${go_version_origin}" + build_data::set_string "go_version_requested" "${ver}" + fi + + if [ -f "${goMOD}" ]; then + TOOL="gomodules" + build_data::set_string "go_tool" "${TOOL}" + + output::step "Detected go modules via go.mod" + + # Determine Go version from go.mod if not already set by GOVERSION + if [ -z "${ver}" ]; then + ver=$(awk '{ if ($1 == "//" && $2 == "+heroku" && $3 == "goVersion" ) { print $4; exit } }' ${goMOD}) + if [ -n "${ver}" ]; then + go_version_origin="go.mod (heroku comment)" + else + ver=$(awk '{ if ($1 == "go" ) { print "go" $2; exit } }' ${goMOD}) + if [ -n "${ver}" ]; then + go_version_origin="go.mod" + else + ver=${DefaultGoVersion} + go_version_origin="default" + fi + fi + build_data::set_string "go_version_origin" "${go_version_origin}" + build_data::set_string "go_version_requested" "${ver}" + fi + + name=$(awk '{ if ($1 == "module" ) { gsub(/"/, "", $2); print $2; exit } }' <${goMOD}) + output::step "Detected Module Name: ${name}" + warnGoVersionOverride + + if [ "${go_version_origin}" = "default" ]; then + output::warning <<-EOF The go.mod file for this project does not specify a Go version. Defaulting to ${ver} @@ -301,10 +300,10 @@ determineTool() { For more details see: https://devcenter.heroku.com/articles/go-apps-with-modules#build-configuration EOF - fi + fi - if supportsGoModules "${ver}"; then - output::error <<-EOF + if supportsGoModules "${ver}"; then + output::error <<-EOF Error: You are using ${ver}, which does not support Go modules. Go modules are supported by go1.11 and above. @@ -315,25 +314,25 @@ determineTool() { Then commit and push again. EOF - exit 1 - fi - else - local legacy_tool="" - if [ -f "${build}/Gopkg.lock" ]; then - legacy_tool="dep" - elif [ -f "${build}/Godeps/Godeps.json" ]; then - legacy_tool="godep" - elif [ -f "${build}/vendor/vendor.json" ]; then - legacy_tool="govendor" - elif [ -f "${build}/glide.yaml" ]; then - legacy_tool="glide" - elif [ -d "${build}/src" ] && [ -n "$(find "${build}/src" -mindepth 2 -type f -name '*.go' | sed 1q)" ]; then - legacy_tool="gb" - fi - - if [ -n "${legacy_tool}" ]; then - build_data::set_string "go_tool" "${legacy_tool}" - output::error <<-EOF + exit 1 + fi + else + local legacy_tool="" + if [ -f "${build}/Gopkg.lock" ]; then + legacy_tool="dep" + elif [ -f "${build}/Godeps/Godeps.json" ]; then + legacy_tool="godep" + elif [ -f "${build}/vendor/vendor.json" ]; then + legacy_tool="govendor" + elif [ -f "${build}/glide.yaml" ]; then + legacy_tool="glide" + elif [ -d "${build}/src" ] && [ -n "$(find "${build}/src" -mindepth 2 -type f -name '*.go' | sed 1q)" ]; then + legacy_tool="gb" + fi + + if [ -n "${legacy_tool}" ]; then + build_data::set_string "go_tool" "${legacy_tool}" + output::error <<-EOF Error: Your app appears to use '${legacy_tool}' for dependency management, but support for ${legacy_tool} has been removed. @@ -346,14 +345,14 @@ determineTool() { For more details see: https://devcenter.heroku.com/articles/go-modules EOF - else - output::error <<-EOF + else + output::error <<-EOF Error: A go.mod file is required. For help with using Go on Heroku, see: https://devcenter.heroku.com/articles/go-support EOF - fi - exit 1 - fi + fi + exit 1 + fi } diff --git a/sbin/add-version b/sbin/add-version index 1a6a4569..9cc11b9f 100755 --- a/sbin/add-version +++ b/sbin/add-version @@ -6,8 +6,8 @@ BASEURL="https://dl.google.com/go" V="$1" if [ -z "$V" ]; then - echo "usage: $0 " >&2 - exit 1 + echo "usage: $0 " >&2 + exit 1 fi tgz_fn="go$V.linux-amd64.tar.gz" @@ -15,11 +15,11 @@ tgz_url="$BASEURL/$tgz_fn" sha256_url="$tgz_url.sha256" if ! sha256_content="$(curl -s -f "$sha256_url")"; then - echo "error: adding $V: couldn't fetch $sha256_url" >&2 - exit 1 + echo "error: adding $V: couldn't fetch $sha256_url" >&2 + exit 1 fi -TGZ_FN="$tgz_fn" TGZ_URL="$tgz_url" SHA256="$sha256_content" jq -S '.[env.TGZ_FN] = {URL: env.TGZ_URL, SHA: env.SHA256 }' files.json > files.json.tmp +TGZ_FN="$tgz_fn" TGZ_URL="$tgz_url" SHA256="$sha256_content" jq -S '.[env.TGZ_FN] = {URL: env.TGZ_URL, SHA: env.SHA256 }' files.json >files.json.tmp mv files.json.tmp files.json echo "added $V to files.json" diff --git a/sbin/publish.sh b/sbin/publish.sh index 2bace6a9..f12228b6 100755 --- a/sbin/publish.sh +++ b/sbin/publish.sh @@ -9,9 +9,9 @@ newVersion="v$((curVersion + 1))" read -p "Deploy as version: $newVersion [y/n]? " choice case "$choice" in - y|Y ) echo "";; - n|N ) exit 0;; - * ) exit 1;; + y | Y) echo "" ;; + n | N) exit 0 ;; + *) exit 1 ;; esac git fetch origin diff --git a/sbin/update-go-versions b/sbin/update-go-versions index 01df36ef..d1132901 100755 --- a/sbin/update-go-versions +++ b/sbin/update-go-versions @@ -5,56 +5,56 @@ set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")/.." if [ -z "${1:-}" ]; then - echo "Usage: $0 [comma-separated-versions]" - echo "" - echo "Examples:" - echo " $0 1.24.12,1.25.6 # Add specific versions" - echo " $0 # Auto-detect and add latest stable versions" - echo "" - echo "No versions specified, fetching latest stable versions from go.dev..." + echo "Usage: $0 [comma-separated-versions]" + echo "" + echo "Examples:" + echo " $0 1.24.12,1.25.6 # Add specific versions" + echo " $0 # Auto-detect and add latest stable versions" + echo "" + echo "No versions specified, fetching latest stable versions from go.dev..." - # Fetch latest stable versions from go.dev - latest_versions=$(curl \ - --no-progress-meter \ - --fail \ - --connect-timeout 3 \ - --max-time 60 \ - --retry 5 \ - --retry-max-time 60 \ - --retry-connrefused \ - 'https://go.dev/dl/?mode=json&include=stable' | \ - jq -r '.[].version | sub("^go"; "")') + # Fetch latest stable versions from go.dev + latest_versions=$(curl \ + --no-progress-meter \ + --fail \ + --connect-timeout 3 \ + --max-time 60 \ + --retry 5 \ + --retry-max-time 60 \ + --retry-connrefused \ + 'https://go.dev/dl/?mode=json&include=stable' \ + | jq -r '.[].version | sub("^go"; "")') - if [ -z "$latest_versions" ]; then - echo "Error: Failed to fetch latest versions from go.dev" - exit 1 - fi + if [ -z "$latest_versions" ]; then + echo "Error: Failed to fetch latest versions from go.dev" + exit 1 + fi - # Convert newline-separated versions to comma-separated - versions_arg=$(echo "$latest_versions" | tr '\n' ',' | sed 's/,$//') - echo "Found latest stable versions: $versions_arg" - IFS=',' read -ra VERSION_ARRAY <<< "$versions_arg" + # Convert newline-separated versions to comma-separated + versions_arg=$(echo "$latest_versions" | tr '\n' ',' | sed 's/,$//') + echo "Found latest stable versions: $versions_arg" + IFS=',' read -ra VERSION_ARRAY <<<"$versions_arg" else - IFS=',' read -ra VERSION_ARRAY <<< "$1" + IFS=',' read -ra VERSION_ARRAY <<<"$1" fi TEMP_CHANGELOG=$(mktemp) -awk '/## \[Unreleased\]/{print; print ""; exit} {print}' CHANGELOG.md > "$TEMP_CHANGELOG" +awk '/## \[Unreleased\]/{print; print ""; exit} {print}' CHANGELOG.md >"$TEMP_CHANGELOG" for version in "${VERSION_ARRAY[@]}"; do - tarball="go${version}.linux-amd64.tar.gz" + tarball="go${version}.linux-amd64.tar.gz" - if jq -e --arg key "$tarball" 'has($key)' files.json > /dev/null 2>&1; then - echo "Version $version already exists, skipping" - continue - fi + if jq -e --arg key "$tarball" 'has($key)' files.json >/dev/null 2>&1; then + echo "Version $version already exists, skipping" + continue + fi - sbin/add-version "$version" - echo "* Add go${version}" >> "$TEMP_CHANGELOG" + sbin/add-version "$version" + echo "* Add go${version}" >>"$TEMP_CHANGELOG" - major_minor=$(echo "$version" | cut -d. -f1,2) + major_minor=$(echo "$version" | cut -d. -f1,2) - jq --arg major_minor "go${major_minor}" --arg version "go${version}" ' + jq --arg major_minor "go${major_minor}" --arg version "go${version}" ' .Go.VersionExpansion[$major_minor] = $version | # Sort by descending Go version .Go.VersionExpansion = ( @@ -68,11 +68,11 @@ for version in "${VERSION_ARRAY[@]}"; do reverse | from_entries ) - ' data.json > data.json.tmp - mv data.json.tmp data.json - echo "* go${major_minor} defaults to ${version}" >> "$TEMP_CHANGELOG" + ' data.json >data.json.tmp + mv data.json.tmp data.json + echo "* go${major_minor} defaults to ${version}" >>"$TEMP_CHANGELOG" done -awk '/## \[Unreleased\]/{flag=1; next} flag{if(flag==1 && NF==0){flag=2; next}} flag==2' CHANGELOG.md >> "$TEMP_CHANGELOG" +awk '/## \[Unreleased\]/{flag=1; next} flag{if(flag==1 && NF==0){flag=2; next}} flag==2' CHANGELOG.md >>"$TEMP_CHANGELOG" mv "$TEMP_CHANGELOG" CHANGELOG.md diff --git a/test/fixtures/mod-basic-with-hooks/bin/go-post-compile b/test/fixtures/mod-basic-with-hooks/bin/go-post-compile index 7a2ddc45..00a64e00 100755 --- a/test/fixtures/mod-basic-with-hooks/bin/go-post-compile +++ b/test/fixtures/mod-basic-with-hooks/bin/go-post-compile @@ -1,3 +1,3 @@ #!/usr/bin/env bash -echo "POST COMPILE" \ No newline at end of file +echo "POST COMPILE" diff --git a/test/fixtures/mod-basic-with-hooks/bin/go-pre-compile b/test/fixtures/mod-basic-with-hooks/bin/go-pre-compile index 15b91a11..e850e057 100644 --- a/test/fixtures/mod-basic-with-hooks/bin/go-pre-compile +++ b/test/fixtures/mod-basic-with-hooks/bin/go-pre-compile @@ -1,3 +1,3 @@ #!/usr/bin/env bash -echo "PRE COMPILE" \ No newline at end of file +echo "PRE COMPILE" diff --git a/test/run.sh b/test/run.sh index 03c89c28..aeef76fe 100755 --- a/test/run.sh +++ b/test/run.sh @@ -2,352 +2,352 @@ # See README.md for info on running these tests. testTestPackModulesVendoredGolangLintCI() { - fixture "mod-deps-vendored-with-tests" - - dotest - assertCapturedExitSuccess - assertCaptured "RUN Test_BasicTest" - assertCaptured "PASS: Test_BasicTest" - assertCaptured "/.golangci.{yml,toml,json} detected" - assertCaptured "Running: golangci-lint -v --build-tags heroku run" + fixture "mod-deps-vendored-with-tests" + + dotest + assertCapturedExitSuccess + assertCaptured "RUN Test_BasicTest" + assertCaptured "PASS: Test_BasicTest" + assertCaptured "/.golangci.{yml,toml,json} detected" + assertCaptured "Running: golangci-lint -v --build-tags heroku run" } testTestPackModulesGolangLintCI() { - fixture "mod-deps-with-tests" + fixture "mod-deps-with-tests" - dotest - assertCapturedExitSuccess + dotest + assertCapturedExitSuccess - # The other deps are downloaded/installed - assertCaptured " + # The other deps are downloaded/installed + assertCaptured " go: finding github.com/gorilla/mux v1.6.2 go: finding github.com/gorilla/context v1.1.1 go: downloading github.com/gorilla/mux v1.6.2 go: extracting github.com/gorilla/mux v1.6.2 github.com/gorilla/mux " - assertCaptured "RUN Test_BasicTest" - assertCaptured "PASS: Test_BasicTest" - assertCaptured "/.golangci.{yml,toml,json} detected" - assertCaptured "Running: golangci-lint -v --build-tags heroku run" + assertCaptured "RUN Test_BasicTest" + assertCaptured "PASS: Test_BasicTest" + assertCaptured "/.golangci.{yml,toml,json} detected" + assertCaptured "Running: golangci-lint -v --build-tags heroku run" } testTestPackModulesGolangLintCI116() { - fixture "mod-deps-with-tests-116" + fixture "mod-deps-with-tests-116" - dotest - assertCapturedExitSuccess + dotest + assertCapturedExitSuccess - # The other deps are downloaded/installed - assertCaptured " + # The other deps are downloaded/installed + assertCaptured " go: finding github.com/gorilla/mux v1.6.2 go: finding github.com/gorilla/context v1.1.1 go: downloading github.com/gorilla/mux v1.6.2 go: extracting github.com/gorilla/mux v1.6.2 github.com/gorilla/mux " - assertCaptured "RUN Test_BasicTest" - assertCaptured "PASS: Test_BasicTest" - assertCaptured "/.golangci.{yml,toml,json} detected" - assertCaptured "Running: golangci-lint -v --build-tags heroku run" + assertCaptured "RUN Test_BasicTest" + assertCaptured "PASS: Test_BasicTest" + assertCaptured "/.golangci.{yml,toml,json} detected" + assertCaptured "Running: golangci-lint -v --build-tags heroku run" } testModProcfileCreation() { - fixture "mod-cmd-web" + fixture "mod-cmd-web" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured "go1.12.17" - assertCaptured "Running: go install -v -tags heroku github.com/heroku/fixture/cmd/web + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured "go1.12.17" + assertCaptured "Running: go install -v -tags heroku github.com/heroku/fixture/cmd/web github.com/heroku/fixture/cmd/other" - assertCapturedExitSuccess - assertFile "other: bin/other + assertCapturedExitSuccess + assertFile "other: bin/other web: bin/web" "Procfile" } testModDepsRecompile() { - fixture "mod-deps" + fixture "mod-deps" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertGoInstallOnlyFixturePackageCaptured - # The other deps are downloaded/installed - assertCaptured " + # The other deps are downloaded/installed + assertCaptured " go: finding github.com/gorilla/mux v1.6.2 go: finding github.com/gorilla/context v1.1.1 go: downloading github.com/gorilla/mux v1.6.2 go: extracting github.com/gorilla/mux v1.6.2 github.com/gorilla/mux " - assertCapturedExitSuccess - assertInstalledFixtureBinary - - # Second compile - compile - assertModulesBoilerplateCaptured - assertGoInstallOnlyFixturePackageCaptured - - # On the second compile go should already be fetched and installed & the packages should be downloaded already. - assertNotCaptured "Fetching ${DEFAULT_GO_VERSION}" - assertNotCaptured "Installing ${DEFAULT_GO_VERSION}" - assertNotCaptured "go: finding github.com/gorilla/mux v1.6.2" - assertNotCaptured "go: finding github.com/gorilla/context v1.1.1" - assertNotCaptured "go: downloading github.com/gorilla/mux v1.6.2" - assertNotCaptured "go: extracting github.com/gorilla/mux v1.6.2" - - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary + + # Second compile + compile + assertModulesBoilerplateCaptured + assertGoInstallOnlyFixturePackageCaptured + + # On the second compile go should already be fetched and installed & the packages should be downloaded already. + assertNotCaptured "Fetching ${DEFAULT_GO_VERSION}" + assertNotCaptured "Installing ${DEFAULT_GO_VERSION}" + assertNotCaptured "go: finding github.com/gorilla/mux v1.6.2" + assertNotCaptured "go: finding github.com/gorilla/context v1.1.1" + assertNotCaptured "go: downloading github.com/gorilla/mux v1.6.2" + assertNotCaptured "go: extracting github.com/gorilla/mux v1.6.2" + + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModWithQuotesModule() { - fixture "mod-with-quoted-module" + fixture "mod-with-quoted-module" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured "go1.12.17" - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured "go1.12.17" + assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertInstalledFixtureBinary - assertFile "web: bin/fixture" "Procfile" + assertCapturedExitSuccess + assertInstalledFixtureBinary + assertFile "web: bin/fixture" "Procfile" } testModWithNonFilesInBin() { - fixture "mod-with-non-files-in-bin" + fixture "mod-with-non-files-in-bin" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertGoInstallOnlyFixturePackageCaptured - assertNotCaptured "go: finding github.com/gorilla/mux v1.6.2" - assertNotCaptured "go: finding github.com/gorilla/context v1.1.1" - assertNotCaptured "go: downloading github.com/gorilla/mux v1.6.2" - assertNotCaptured "go: extracting github.com/gorilla/mux v1.6.2" + assertNotCaptured "go: finding github.com/gorilla/mux v1.6.2" + assertNotCaptured "go: finding github.com/gorilla/context v1.1.1" + assertNotCaptured "go: downloading github.com/gorilla/mux v1.6.2" + assertNotCaptured "go: extracting github.com/gorilla/mux v1.6.2" - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModcmdDetection() { - fixture "mod-cmd" + fixture "mod-cmd" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured "go1.12.17" - assertCaptured "Detected the following main packages to install: + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured "go1.12.17" + assertCaptured "Detected the following main packages to install: github.com/heroku/fixture/cmd/fixture github.com/heroku/fixture/cmd/other" - assertCaptured "Running: go install -v -tags heroku github.com/heroku/fixture/cmd/fixture github.com/heroku/fixture/cmd/other + assertCaptured "Running: go install -v -tags heroku github.com/heroku/fixture/cmd/fixture github.com/heroku/fixture/cmd/other github.com/heroku/fixture/cmd/fixture github.com/heroku/fixture/cmd/other" - assertCaptured "Installed the following binaries: + assertCaptured "Installed the following binaries: ./bin/fixture ./bin/other" - assertFile "fixture: bin/fixture + assertFile "fixture: bin/fixture other: bin/other" "Procfile" - assertCapturedExitSuccess - assertInstalledFixtureBinary - assertCompiledBinaryExists other + assertCapturedExitSuccess + assertInstalledFixtureBinary + assertCompiledBinaryExists other } testModWithHooks() { - fixture "mod-basic-with-hooks" + fixture "mod-basic-with-hooks" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured - assertCaptured "Running bin/go-pre-compile hook + assertCaptured "Running bin/go-pre-compile hook PRE COMPILE" - assertGoInstallOnlyFixturePackageCaptured - assertCaptured "Running bin/go-post-compile hook + assertGoInstallOnlyFixturePackageCaptured + assertCaptured "Running bin/go-post-compile hook POST COMPILE" - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModNoVersion() { - fixture "mod-no-version" + fixture "mod-no-version" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertCapturedStderr "does not specify a Go version" - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertCapturedStderr "does not specify a Go version" + assertInstalledFixtureBinary } testModOldVersion() { - fixture "mod-old-version" + fixture "mod-old-version" - assertDetected + assertDetected - compile - assertCaptured "Detected go modules via go.mod" - assertCaptured "Detected Module Name: github.com/heroku/fixture" - assertCapturedError 1 "a Go version >= go1.11 like so:" + compile + assertCaptured "Detected go modules via go.mod" + assertCaptured "Detected Module Name: github.com/heroku/fixture" + assertCapturedError 1 "a Go version >= go1.11 like so:" } testModInstall() { - fixture "mod-install" + fixture "mod-install" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured - assertCaptured "Running: go install -v -tags heroku ./cmd/... ./other + assertCaptured "Running: go install -v -tags heroku ./cmd/... ./other github.com/heroku/fixture/cmd/fixture1 github.com/heroku/fixture/cmd/fixture2 github.com/heroku/fixture/other" - assertCaptured "Installed the following binaries: + assertCaptured "Installed the following binaries: ./bin/fixture1 ./bin/fixture2 ./bin/other" - assertCapturedExitSuccess - assertCompiledBinaryExists "fixture1" - assertCompiledBinaryExists "fixture2" - assertCompiledBinaryExists "other" + assertCapturedExitSuccess + assertCompiledBinaryExists "fixture1" + assertCompiledBinaryExists "fixture2" + assertCompiledBinaryExists "other" } testModBasic() { - fixture "mod-basic" + fixture "mod-basic" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModBasicGo111() { - fixture "mod-basic-go111" + fixture "mod-basic-go111" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertCaptured "Installing go1.11.13" - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertCaptured "Installing go1.11.13" + assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModBasicGo125() { - fixture "mod-basic-go125" + fixture "mod-basic-go125" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertCaptured "Installing go1.25" - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertCaptured "Installing go1.25" + assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModBasicGo126() { - fixture "mod-basic-go126" + fixture "mod-basic-go126" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertCaptured "Installing go1.26" - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertCaptured "Installing go1.26" + assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModBasicWithoutProcfile() { - fixture "mod-basic-wo-procfile" + fixture "mod-basic-wo-procfile" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertInstalledFixtureBinary - assertFile "web: bin/fixture" "Procfile" + assertCapturedExitSuccess + assertInstalledFixtureBinary + assertFile "web: bin/fixture" "Procfile" } testModPrivateProxy() { - local repo="${BUILDPACK_HOME}/test/fixtures/mod-private-proxy/repo" - fixture "mod-private-proxy/app" + local repo="${BUILDPACK_HOME}/test/fixtures/mod-private-proxy/repo" + fixture "mod-private-proxy/app" - env "GOPROXY" "file://$repo" - env "GOPRIVATE" "git.fury.io/*" - env "GONOPROXY" "none" + env "GOPROXY" "file://$repo" + env "GOPRIVATE" "git.fury.io/*" + env "GONOPROXY" "none" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured "go1.15.15" - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured "go1.15.15" + assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModDeps() { - fixture "mod-deps" + fixture "mod-deps" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertGoInstallOnlyFixturePackageCaptured - # The other deps are downloaded/installed - assertCaptured " + # The other deps are downloaded/installed + assertCaptured " go: finding github.com/gorilla/mux v1.6.2 go: finding github.com/gorilla/context v1.1.1 go: downloading github.com/gorilla/mux v1.6.2 go: extracting github.com/gorilla/mux v1.6.2 github.com/gorilla/mux " - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } # Ensure that a project works when: @@ -360,129 +360,129 @@ github.com/gorilla/mux # activates new consistency checks between go.mod and the vendor # directory, described at https://golang.org/doc/go1.14#vendor. testModDeps114() { - fixture "mod-deps-114" + fixture "mod-deps-114" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertCaptured "Installing go1.14.2" - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertCaptured "Installing go1.14.2" + assertGoInstallOnlyFixturePackageCaptured - # The other deps are downloaded/installed - assertCaptured " + # The other deps are downloaded/installed + assertCaptured " go: finding github.com/gorilla/mux v1.6.2 go: finding github.com/gorilla/context v1.1.1 go: downloading github.com/gorilla/mux v1.6.2 go: extracting github.com/gorilla/mux v1.6.2 github.com/gorilla/mux " - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModDepsVendored() { - fixture "mod-deps-vendored" + fixture "mod-deps-vendored" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertGoInstallOnlyFixturePackageCaptured + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertGoInstallOnlyFixturePackageCaptured - assertNotCaptured "go: finding github.com/gorilla/mux v1.6.2" - assertNotCaptured "go: finding github.com/gorilla/context v1.1.1" - assertNotCaptured "go: downloading github.com/gorilla/mux v1.6.2" - assertNotCaptured "go: extracting github.com/gorilla/mux v1.6.2" + assertNotCaptured "go: finding github.com/gorilla/mux v1.6.2" + assertNotCaptured "go: finding github.com/gorilla/context v1.1.1" + assertNotCaptured "go: downloading github.com/gorilla/mux v1.6.2" + assertNotCaptured "go: extracting github.com/gorilla/mux v1.6.2" - assertCapturedExitSuccess - assertInstalledFixtureBinary + assertCapturedExitSuccess + assertInstalledFixtureBinary } testModPackageSpecOverride() { - fixture "mod-cmd" + fixture "mod-cmd" - env "GO_INSTALL_PACKAGE_SPEC" "./cmd/fixture" + env "GO_INSTALL_PACKAGE_SPEC" "./cmd/fixture" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured "go1.12.17" - assertCapturedStderr "Using \$GO_INSTALL_PACKAGE_SPEC override." - assertCaptured "Running: go install -v -tags heroku ./cmd/fixture" - assertCapturedExitSuccess - assertCompiledBinaryExists "fixture" - assertBuildDirFileDoesNotExist "bin/other" + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured "go1.12.17" + assertCapturedStderr "Using \$GO_INSTALL_PACKAGE_SPEC override." + assertCaptured "Running: go install -v -tags heroku ./cmd/fixture" + assertCapturedExitSuccess + assertCompiledBinaryExists "fixture" + assertBuildDirFileDoesNotExist "bin/other" } testModGOVERSIONOverride() { - fixture "mod-basic" + fixture "mod-basic" - env "GOVERSION" "go1.24" + env "GOVERSION" "go1.24" - assertDetected + assertDetected - compile - assertCaptured "Installing go1.24" - assertCapturedStderr "Using \$GOVERSION override." - assertGoInstallOnlyFixturePackageCaptured - assertCapturedExitSuccess - assertCompiledBinaryExists + compile + assertCaptured "Installing go1.24" + assertCapturedStderr "Using \$GOVERSION override." + assertGoInstallOnlyFixturePackageCaptured + assertCapturedExitSuccess + assertCompiledBinaryExists } testModBinFile() { - fixture "mod-bin-file" + fixture "mod-bin-file" - assertDetected + assertDetected - compile - assertCapturedError 1 "File bin exists and is not a directory." + compile + assertCapturedError 1 "File bin exists and is not a directory." } testModLDSymbolValue() { - fixture "mod-ld-symbol-value" + fixture "mod-ld-symbol-value" - env "GO_LINKER_SYMBOL" "main.fixture" - env "GO_LINKER_VALUE" "fixture" + env "GO_LINKER_SYMBOL" "main.fixture" + env "GO_LINKER_VALUE" "fixture" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertCaptured "Running: go install -v -tags heroku -ldflags -X main.fixture=fixture" - assertCaptured "github.com/heroku/fixture" - assertCapturedExitSuccess - assertCompiledBinaryExists - assertCompiledBinaryOutputs "fixture" "fixture" + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertCaptured "Running: go install -v -tags heroku -ldflags -X main.fixture=fixture" + assertCaptured "github.com/heroku/fixture" + assertCapturedExitSuccess + assertCompiledBinaryExists + assertCompiledBinaryOutputs "fixture" "fixture" } testModBasicWithTools() { - fixture "mod-basic" + fixture "mod-basic" - env "GO_INSTALL_TOOLS_IN_IMAGE" "true" + env "GO_INSTALL_TOOLS_IN_IMAGE" "true" - assertDetected + assertDetected - compile - assertModulesBoilerplateCaptured - assertGoInstallCaptured - assertGoInstallOnlyFixturePackageCaptured - assertCaptured "Copying go tool chain to" - assertCapturedExitSuccess - assertCompiledBinaryExists - assertBuildDirFileExists ".heroku/go/bin/go" + compile + assertModulesBoilerplateCaptured + assertGoInstallCaptured + assertGoInstallOnlyFixturePackageCaptured + assertCaptured "Copying go tool chain to" + assertCapturedExitSuccess + assertCompiledBinaryExists + assertBuildDirFileExists ".heroku/go/bin/go" } testDeprecatedToolDetected() { - fixture "dep-deprecated" + fixture "dep-deprecated" - assertDetected + assertDetected - compile - assertCapturedError 1 "support for dep has been removed" + compile + assertCapturedError 1 "support for dep has been removed" } pushd $(dirname 0) >/dev/null diff --git a/test/utils.sh b/test/utils.sh index f218b422..93291f0e 100644 --- a/test/utils.sh +++ b/test/utils.sh @@ -3,35 +3,35 @@ ## oneTimeSetUp() { - TEST_SUITE_CACHE="$(mktemp -d ${SHUNIT_TMPDIR}/test_suite_cache.XXXX)" - BUILDPACK_HOME=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) - DEFAULT_GO_VERSION="$(<"${BUILDPACK_HOME}/data.json" jq -r '.Go.DefaultVersion')" + TEST_SUITE_CACHE="$(mktemp -d ${SHUNIT_TMPDIR}/test_suite_cache.XXXX)" + BUILDPACK_HOME=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + DEFAULT_GO_VERSION="$(<"${BUILDPACK_HOME}/data.json" jq -r '.Go.DefaultVersion')" } oneTimeTearDown() { - rm -rf ${TEST_SUITE_CACHE} + rm -rf ${TEST_SUITE_CACHE} } setUp() { - OUTPUT_DIR="$(mktemp -d ${SHUNIT_TMPDIR}/output.XXXX)" - STD_OUT="${OUTPUT_DIR}/stdout" - STD_ERR="${OUTPUT_DIR}/stderr" - BUILD_DIR="${OUTPUT_DIR}/build" - CACHE_DIR="${OUTPUT_DIR}/cache" - ENV_DIR="${OUTPUT_DIR}/env" - mkdir -p ${OUTPUT_DIR} - mkdir -p ${BUILD_DIR} - mkdir -p ${CACHE_DIR} - mkdir -p ${ENV_DIR} + OUTPUT_DIR="$(mktemp -d ${SHUNIT_TMPDIR}/output.XXXX)" + STD_OUT="${OUTPUT_DIR}/stdout" + STD_ERR="${OUTPUT_DIR}/stderr" + BUILD_DIR="${OUTPUT_DIR}/build" + CACHE_DIR="${OUTPUT_DIR}/cache" + ENV_DIR="${OUTPUT_DIR}/env" + mkdir -p ${OUTPUT_DIR} + mkdir -p ${BUILD_DIR} + mkdir -p ${CACHE_DIR} + mkdir -p ${ENV_DIR} } tearDown() { - if [ -d "${OUTPUT_DIR}" ]; then - # Go modules are read-only by default; give ourselves write permission to delete it first - chmod -R +w "${OUTPUT_DIR}" - rm -rf "${OUTPUT_DIR}" - fi + if [ -d "${OUTPUT_DIR}" ]; then + # Go modules are read-only by default; give ourselves write permission to delete it first + chmod -R +w "${OUTPUT_DIR}" + rm -rf "${OUTPUT_DIR}" + fi } ############## @@ -39,277 +39,278 @@ tearDown() { ## capture() { - resetCapture + resetCapture - LAST_COMMAND="$@" + LAST_COMMAND="$@" - $@ >${STD_OUT} 2>${STD_ERR} - RETURN=$? - rtrn=${RETURN} # deprecated + $@ >${STD_OUT} 2>${STD_ERR} + RETURN=$? + rtrn=${RETURN} # deprecated } continue_capture() { - LAST_COMMAND="$@" + LAST_COMMAND="$@" - $@ >>${STD_OUT} 2>>${STD_ERR} - local cr=$? - if [ "$RETURN" = "0" ]; then - RETURN=$cr - fi - rtrn=${RETURN} # deprecated + $@ >>${STD_OUT} 2>>${STD_ERR} + local cr=$? + if [ "$RETURN" = "0" ]; then + RETURN=$cr + fi + rtrn=${RETURN} # deprecated } resetCapture() { - if [ -f ${STD_OUT} ]; then - rm ${STD_OUT} - fi + if [ -f ${STD_OUT} ]; then + rm ${STD_OUT} + fi - if [ -f "${STD_OUT}_no_color" ]; then - rm "${STD_OUT}_no_color" - fi + if [ -f "${STD_OUT}_no_color" ]; then + rm "${STD_OUT}_no_color" + fi - if [ -f ${STD_ERR} ]; then - rm ${STD_ERR} - fi + if [ -f ${STD_ERR} ]; then + rm ${STD_ERR} + fi - unset LAST_COMMAND - unset RETURN - unset rtrn # deprecated + unset LAST_COMMAND + unset RETURN + unset rtrn # deprecated } fixture() { - local fixture="${1}" - echo "* fixture: ${fixture}" - local fp="${BUILDPACK_HOME}/test/fixtures/${fixture}" - tar -cf - -C $fp . | tar -x -C ${BUILD_DIR} + local fixture="${1}" + echo "* fixture: ${fixture}" + local fp="${BUILDPACK_HOME}/test/fixtures/${fixture}" + tar -cf - -C $fp . | tar -x -C ${BUILD_DIR} } env() { - local var="${1}" - local val="${2}" - if [ -z "${var}" ]; then - fail "set env var w/o specifying name" - exit 1 - fi - echo -n "${val}" > "${ENV_DIR}/${var}" + local var="${1}" + local val="${2}" + if [ -z "${var}" ]; then + fail "set env var w/o specifying name" + exit 1 + fi + echo -n "${val}" >"${ENV_DIR}/${var}" } detect() { - echo "* detect" - capture ${BUILDPACK_HOME}/bin/detect ${BUILD_DIR} + echo "* detect" + capture ${BUILDPACK_HOME}/bin/detect ${BUILD_DIR} } assertDetected() { - detect - assertCaptured "Go" - assertCapturedSuccess + detect + assertCaptured "Go" + assertCapturedSuccess } compile() { - echo "* compile" - capture ${BUILDPACK_HOME}/bin/compile ${BUILD_DIR} ${CACHE_DIR} ${ENV_DIR} + echo "* compile" + capture ${BUILDPACK_HOME}/bin/compile ${BUILD_DIR} ${CACHE_DIR} ${ENV_DIR} } dotest() { - # On Heroku CI, test-compile and test are run in such a way that the - # provided BUILD_DIR is the same as HOME. Simulate that here to get - # better fidelity. For example, this ensures that the default GOPATH of - # $HOME/go doesn't cause issues. - echo "* test-compile" - HOME="${BUILD_DIR}" capture "${BUILDPACK_HOME}/bin/test-compile" "${BUILD_DIR}" "${CACHE_DIR}" "${ENV_DIR}" - echo "* test" - HOME="${BUILD_DIR}" continue_capture "${BUILDPACK_HOME}/bin/test" "${BUILD_DIR}" "${ENV_DIR}" + # On Heroku CI, test-compile and test are run in such a way that the + # provided BUILD_DIR is the same as HOME. Simulate that here to get + # better fidelity. For example, this ensures that the default GOPATH of + # $HOME/go doesn't cause issues. + echo "* test-compile" + HOME="${BUILD_DIR}" capture "${BUILDPACK_HOME}/bin/test-compile" "${BUILD_DIR}" "${CACHE_DIR}" "${ENV_DIR}" + echo "* test" + HOME="${BUILD_DIR}" continue_capture "${BUILDPACK_HOME}/bin/test" "${BUILD_DIR}" "${ENV_DIR}" } release() { - capture ${BUILDPACK_HOME}/bin/release ${BUILD_DIR} + capture ${BUILDPACK_HOME}/bin/release ${BUILD_DIR} } assertFile() { - local content="${1}" - local name="${2}" - local tgt="${BUILD_DIR}/${name}" - assertEquals "${content}" "$(cat ${tgt})" + local content="${1}" + local name="${2}" + local tgt="${BUILD_DIR}/${name}" + assertEquals "${content}" "$(cat ${tgt})" } assertBuildDirFileDoesNotExist() { - local name="${1}" - local tgt="${BUILD_DIR}/${name}" - assertTrue "File ${name} exists" "[ ! -f ${tgt} ]" + local name="${1}" + local tgt="${BUILD_DIR}/${name}" + assertTrue "File ${name} exists" "[ ! -f ${tgt} ]" } assertBuildDirFileExists() { - local name="${1}" - local tgt="${BUILD_DIR}/${name}" - assertTrue "File ${name} does not exist" "[ -f ${tgt} ]" + local name="${1}" + local tgt="${BUILD_DIR}/${name}" + assertTrue "File ${name} does not exist" "[ -f ${tgt} ]" } assertFileExists() { - local name="${1}" - assertTrue "File ${name} does not exist" "[ -f ${name} ]" + local name="${1}" + assertTrue "File ${name} does not exist" "[ -f ${name} ]" } assertDirExists() { - local path="${1}" - assertTrue "Dir ${path} does not exist" "[ -d ${path} ]" + local path="${1}" + assertTrue "Dir ${path} does not exist" "[ -d ${path} ]" } assertDirDoesNotExist() { - local path="${1}" - assertTrue "Dir ${path} exists" "[ ! -d ${path} ]" + local path="${1}" + assertTrue "Dir ${path} exists" "[ ! -d ${path} ]" } assertCompiledBinaryExists() { - local name="${1:-fixture}" - local tgt="${BUILD_DIR}/bin/${name}" - assertTrue "Compiled binary (${tgt}) exists" "[ -x ${tgt} ]" + local name="${1:-fixture}" + local tgt="${BUILD_DIR}/bin/${name}" + assertTrue "Compiled binary (${tgt}) exists" "[ -x ${tgt} ]" } assertCompiledBinaryOutputs() { - local name="${1}" - local output="${2}" - capture "${BUILD_DIR}/bin/${name}" + local name="${1}" + local output="${2}" + capture "${BUILD_DIR}/bin/${name}" } assertCapturedEquals() { - assertEquals "$@" "$(cat ${STD_OUT})" + assertEquals "$@" "$(cat ${STD_OUT})" } assertCapturedNotEquals() { - assertNotEquals "$@" "$(cat ${STD_OUT})" + assertNotEquals "$@" "$(cat ${STD_OUT})" } assertCaptured() { - assertFileContains "$@" "${STD_OUT}" + assertFileContains "$@" "${STD_OUT}" } assertCapturedStderr() { - assertFileContains "$@" "${STD_ERR}" + assertFileContains "$@" "${STD_ERR}" } assertNotCaptured() { - assertFileNotContains "$@" "${STD_OUT}" + assertFileNotContains "$@" "${STD_OUT}" } assertCapturedSuccess() { - assertEquals "Expected captured exit code to be 0; was <${RETURN}>" "0" "${RETURN}" - assertEquals "Expected STD_ERR to be empty; was <$(cat ${STD_ERR})>" "" "$(cat ${STD_ERR})" + assertEquals "Expected captured exit code to be 0; was <${RETURN}>" "0" "${RETURN}" + assertEquals "Expected STD_ERR to be empty; was <$(cat ${STD_ERR})>" "" "$(cat ${STD_ERR})" } assertCapturedExitSuccess() { - assertEquals "Expected captured exit code to be 0; was <${RETURN}>" "0" "${RETURN}" + assertEquals "Expected captured exit code to be 0; was <${RETURN}>" "0" "${RETURN}" } # assertCapturedError [[expectedErrorCode] expectedErrorMsg] assertCapturedError() { - if [ $# -gt 1 ]; then - local expectedErrorCode="${1}" - shift - fi + if [ $# -gt 1 ]; then + local expectedErrorCode="${1}" + shift + fi - local expectedErrorMsg="${1:-""}" + local expectedErrorMsg="${1:-""}" - if [ -z ${expectedErrorCode} ]; then - assertTrue "Expected captured exit code to be greater than 0; was <${RETURN}>" "[ ${RETURN} -gt 0 ]" - else - assertTrue "Expected captured exit code to be <${expectedErrorCode}>; was <${RETURN}>" "[ ${RETURN} -eq ${expectedErrorCode} ]" - fi + if [ -z ${expectedErrorCode} ]; then + assertTrue "Expected captured exit code to be greater than 0; was <${RETURN}>" "[ ${RETURN} -gt 0 ]" + else + assertTrue "Expected captured exit code to be <${expectedErrorCode}>; was <${RETURN}>" "[ ${RETURN} -eq ${expectedErrorCode} ]" + fi - if [ "${expectedErrorMsg}" != "" ]; then - assertFileContains "Expected STD_ERR to contain error <${expectedErrorMsg}>" "${expectedErrorMsg}" "${STD_ERR}" - fi + if [ "${expectedErrorMsg}" != "" ]; then + assertFileContains "Expected STD_ERR to contain error <${expectedErrorMsg}>" "${expectedErrorMsg}" "${STD_ERR}" + fi } _assertContains() { - if [ 5 -eq $# ]; then - local msg="${1}" - shift - elif [ ! 4 -eq $# ]; then - fail "Expected 4 or 5 parameters; Receieved $# parameters" - fi - - local needle=$1 - local haystack=$2 - local expectation=$3 - local haystack_type=$4 - - case "${haystack_type}" in - "file") - local haystack_no_color="${haystack}_no_color" - if [ ! -e ${haystack_no_color} ]; then - sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" < ${haystack} > ${haystack_no_color} - fi - ## echo grep -q -F -e "${needle}" ${haystack_no_color} - grep -q -F -e "${needle}" ${haystack_no_color} ;; - "text") echo "${haystack}" | grep -q -F -e "${needle}" ;; - esac - - if [ "${expectation}" != "$?" ]; then - case "${expectation}" in - 0) default_msg="Expected <${haystack}> to contain <${needle}>" ;; - 1) default_msg="Did not expect <${haystack}> to contain <${needle}>" ;; - esac - - fail "${msg:-${default_msg}}" - fi + if [ 5 -eq $# ]; then + local msg="${1}" + shift + elif [ ! 4 -eq $# ]; then + fail "Expected 4 or 5 parameters; Receieved $# parameters" + fi + + local needle=$1 + local haystack=$2 + local expectation=$3 + local haystack_type=$4 + + case "${haystack_type}" in + "file") + local haystack_no_color="${haystack}_no_color" + if [ ! -e ${haystack_no_color} ]; then + sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <${haystack} >${haystack_no_color} + fi + ## echo grep -q -F -e "${needle}" ${haystack_no_color} + grep -q -F -e "${needle}" ${haystack_no_color} + ;; + "text") echo "${haystack}" | grep -q -F -e "${needle}" ;; + esac + + if [ "${expectation}" != "$?" ]; then + case "${expectation}" in + 0) default_msg="Expected <${haystack}> to contain <${needle}>" ;; + 1) default_msg="Did not expect <${haystack}> to contain <${needle}>" ;; + esac + + fail "${msg:-${default_msg}}" + fi } assertContains() { - _assertContains "$@" 0 "text" + _assertContains "$@" 0 "text" } assertNotContains() { - _assertContains "$@" 1 "text" + _assertContains "$@" 1 "text" } assertFileContains() { - _assertContains "$@" 0 "file" + _assertContains "$@" 0 "file" } assertFileNotContains() { - _assertContains "$@" 1 "file" + _assertContains "$@" 1 "file" } -command_exists () { - type "$1" > /dev/null 2>&1 ; +command_exists() { + type "$1" >/dev/null 2>&1 } assertFileMD5() { - expectedHash=$1 - filename=$2 + expectedHash=$1 + filename=$2 - if command_exists "md5sum"; then - md5_cmd="md5sum ${filename}" - expected_md5_cmd_output="${expectedHash} ${filename}" - elif command_exists "md5"; then - md5_cmd="md5 ${filename}" - expected_md5_cmd_output="MD5 (${filename}) = ${expectedHash}" - else - fail "no suitable MD5 hashing command found on this system" - fi + if command_exists "md5sum"; then + md5_cmd="md5sum ${filename}" + expected_md5_cmd_output="${expectedHash} ${filename}" + elif command_exists "md5"; then + md5_cmd="md5 ${filename}" + expected_md5_cmd_output="MD5 (${filename}) = ${expectedHash}" + else + fail "no suitable MD5 hashing command found on this system" + fi - assertEquals "${expected_md5_cmd_output}" "`${md5_cmd}`" + assertEquals "${expected_md5_cmd_output}" "$(${md5_cmd})" } assertModulesBoilerplateCaptured() { - assertCaptured "Detected go modules via go.mod" - assertCaptured "Detected Module Name: github.com/heroku/fixture" - assertCaptured "Determining packages to install" + assertCaptured "Detected go modules via go.mod" + assertCaptured "Detected Module Name: github.com/heroku/fixture" + assertCaptured "Determining packages to install" } -assertGoInstallOnlyFixturePackageCaptured(){ - assertCaptured "Running: go install -v -tags heroku github.com/heroku/fixture +assertGoInstallOnlyFixturePackageCaptured() { + assertCaptured "Running: go install -v -tags heroku github.com/heroku/fixture github.com/heroku/fixture" } assertInstalledFixtureBinary() { - assertCaptured "Installed the following binaries: + assertCaptured "Installed the following binaries: ./bin/fixture" - assertCompiledBinaryExists fixture + assertCompiledBinaryExists fixture } assertGoInstallCaptured() { - local go_ver=${1:-${DEFAULT_GO_VERSION}} - assertCaptured "Installing ${go_ver}" - assertCaptured "Fetching ${go_ver}" + local go_ver=${1:-${DEFAULT_GO_VERSION}} + assertCaptured "Installing ${go_ver}" + assertCaptured "Fetching ${go_ver}" } From 71b6e1da5cbc86454230726c904ff233e744a5b7 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 22:43:44 -0400 Subject: [PATCH 05/23] Fix ShellCheck warnings across all shell scripts --- bin/compile | 118 +++++++++++++++++++++-------------- bin/detect | 4 +- bin/test | 35 ++++++----- bin/test-compile | 7 ++- lib/common.sh | 120 +++++++++++++++++++++--------------- sbin/add-version | 16 ++--- sbin/publish.sh | 14 ++--- sbin/update-go-versions | 28 ++++----- test/run.sh | 14 +++-- test/utils.sh | 132 +++++++++++++++++++++++----------------- 10 files changed, 285 insertions(+), 203 deletions(-) diff --git a/bin/compile b/bin/compile index 40bbe621..e24ebd7a 100755 --- a/bin/compile +++ b/bin/compile @@ -3,7 +3,7 @@ set -eo pipefail -[ "$BUILDPACK_XTRACE" ] && set -o xtrace +[[ -n "${BUILDPACK_XTRACE}" ]] && set -o xtrace mkdir -p "$1" "$2" build=$(cd "$1/" && pwd) @@ -11,12 +11,15 @@ cache_root=$(cd "$2/" && pwd) cache="${cache_root}/.heroku/go" mkdir -p "${cache}" env_dir="${3}" -buildpack=$(cd "$(dirname $0)/.." && pwd) +buildpack=$(cd "$(dirname "$0")/.." && pwd) +# shellcheck source=lib/common.sh disable=SC1091 source "${buildpack}/lib/common.sh" # Set CACHE_DIR (used by build_data) +# shellcheck disable=SC2034 CACHE_DIR="${cache_root}" +# shellcheck source=lib/build_data.sh disable=SC1091 source "${buildpack}/lib/build_data.sh" compile_start_time=$(build_data::current_unix_realtime) @@ -30,7 +33,7 @@ snapshotBinBefore # Clean up old cache files if migrating from old cache structure. # We detect this legacy structure by looking for 'go-path' in the root. -if [ -d "${cache_root}/go-path" ] && [ ! -d "${cache}/go-path" ]; then +if [[ -d "${cache_root}/go-path" ]] && [[ ! -d "${cache}/go-path" ]]; then output::step "Clearing old build cache artifacts" # Remove artifacts that were previously stored in the cache root @@ -49,10 +52,11 @@ if [ -d "${cache_root}/go-path" ] && [ ! -d "${cache}/go-path" ]; then "${cache_root}/github.com/mattes" 2>/dev/null || true fi -DefaultGoVersion="$(<${DataJSON} jq -r '.Go.DefaultVersion')" +# shellcheck disable=SC2154 +DefaultGoVersion="$(<"${DataJSON}" jq -r '.Go.DefaultVersion')" handleDefaultPkgSpec() { - if [ "${pkgs}" = "default" ]; then + if [[ "${pkgs}" = "default" ]]; then output::warning <<-EOF Installing package '.' (default) @@ -74,14 +78,15 @@ expandVer() { if [[ "${v}" =~ ^[[:digit:]]+ ]]; then v="go${v}" fi - echo $(<${DataJSON} jq -r 'if .Go.VersionExpansion."'${v}'" then .Go.VersionExpansion."'${v}'" else "'${v}'" end') + # shellcheck disable=SC2310 + <"${DataJSON}" jq -r 'if .Go.VersionExpansion."'"${v}"'" then .Go.VersionExpansion."'"${v}"'" else "'"${v}"'" end' } # Report development versions to user # Use after expandVer reportVer() { local ver="${1}" - case $ver in + case ${ver} in devel*) output::warning <<-EOF You are using a development build of Go. @@ -91,6 +96,7 @@ reportVer() { Build tests are NOT RUN!! EOF ;; + *) ;; esac } @@ -99,18 +105,20 @@ ensureGo() { local goPath="${cache}/${goVersion}/go" local goFile="" local txt="Installing ${goVersion}" - if [ -d "${goPath}" ]; then + if [[ -d "${goPath}" ]]; then output::step "Using ${goVersion}" else #For a go version change, we delete everything in our namespace output::step "New Go Version, clearing old cache" - if [ -d "${cache}/go-path" ]; then + if [[ -d "${cache}/go-path" ]]; then find "${cache}/go-path" ! -perm -u=w -print0 | xargs -r -0 chmod u+w 2>&1 fi - rm -rf ${cache}/* + rm -rf "${cache:?}"/* case "${goVersion}" in devel*) - local bGoVersion="$(expandVer ${DefaultGoVersion})" + local bGoVersion + # shellcheck disable=SC2310 + bGoVersion="$(expandVer "${DefaultGoVersion}")" goFile="${bGoVersion}.linux-amd64.tar.gz" goPath="${cache}/${bGoVersion}/go" txt="Installing bootstrap ${bGoVersion}" @@ -132,18 +140,22 @@ ensureGo() { pushd "${cache}" &>/dev/null mkdir -p "${goVersion}" pushd "${goVersion}" &>/dev/null - local sha=$(echo ${goVersion} | cut -d - -f 2) #assumes devel- or devel- - local url="https://github.com/golang/go/archive/$sha.tar.gz" + local sha + sha=$(echo "${goVersion}" | cut -d - -f 2) #assumes devel- or devel- + local url="https://github.com/golang/go/archive/${sha}.tar.gz" output::step "Downloading development Go version ${goVersion}" - ${CURL} ${url} | tar zxf - - mv go-${sha}* go + # shellcheck disable=SC2154 + ${CURL} "${url}" | tar zxf - + mv go-"${sha}"* go output::step "Compiling development Go version ${goVersion}" pushd go/src &>/dev/null + # shellcheck disable=SC2312 echo "devel +${sha} $(date "+%a %b %H:%M:%S %G %z")" >../VERSION + # shellcheck disable=SC2312 GOROOT_BOOTSTRAP=$( - pushd ${cache}/${bGoVersion}/go >/dev/null + pushd "${cache}/${bGoVersion}/go" >/dev/null || exit pwd - popd >/dev/null + popd >/dev/null || exit ) ./make.bash 2>&1 popd &>/dev/null go/bin/go version @@ -166,7 +178,7 @@ ensureGo() { } warnGoVersionOverride() { - if [ ! -z "${GOVERSION}" ]; then + if [[ -n "${GOVERSION}" ]]; then output::warning <<-EOF Using \$GOVERSION override. \$GOVERSION = ${GOVERSION} @@ -178,7 +190,7 @@ warnGoVersionOverride() { } warnPackageSpecOverride() { - if [ ! -z "${GO_INSTALL_PACKAGE_SPEC}" ]; then + if [[ -n "${GO_INSTALL_PACKAGE_SPEC}" ]]; then output::warning <<-EOF Using \$GO_INSTALL_PACKAGE_SPEC override. \$GO_INSTALL_PACKAGE_SPEC = ${GO_INSTALL_PACKAGE_SPEC} @@ -191,6 +203,7 @@ warnPackageSpecOverride() { installPkgs() { output::step "Running: go install -v ${FLAGS[*]} ${pkgs}" + # shellcheck disable=SC2086 go install -v "${FLAGS[@]}" ${pkgs} 2>&1 | output::indent } @@ -204,25 +217,27 @@ FLAGS=(-tags heroku) determineTool # Track the requested version before expansion (for pinned check) +# shellcheck disable=SC2154 requested_go_version="${ver}" # Expand the version and track the resolved version -ver=$(expandVer $ver) +# shellcheck disable=SC2310 +ver=$(expandVer "${ver}") build_data::set_string "go_version" "${ver}" # Track whether the version was pinned (fully specified) -if [ "${requested_go_version}" = "${ver}" ]; then +if [[ "${requested_go_version}" = "${ver}" ]]; then build_data::set_raw "go_version_pinned" "true" else build_data::set_raw "go_version_pinned" "false" fi # If $GO_LINKER_SYMBOL and GO_LINKER_VALUE are set, tell the linker to DTRT -if [ -n "${GO_LINKER_SYMBOL}" -a -n "${GO_LINKER_VALUE}" ]; then +if [[ -n "${GO_LINKER_SYMBOL}" ]] && [[ -n "${GO_LINKER_VALUE}" ]]; then FLAGS+=(-ldflags "-X ${GO_LINKER_SYMBOL}=${GO_LINKER_VALUE}") fi -if [ -e "${build}/bin" -a ! -d "${build}/bin" ]; then +if [[ -e "${build}/bin" ]] && [[ ! -d "${build}/bin" ]]; then output::error <<-EOF Error: File bin exists and is not a directory. EOF @@ -243,33 +258,38 @@ mainPackagesInModule() { # For an explanation of what this is doing, see https://dave.cheney.net/2014/09/14/go-list-your-swiss-army-knife # Stderr (module download progress) is indented and redirected back to stderr so it displays # neatly under the current step without being captured into the command substitution's stdout. + # shellcheck disable=SC2312 go list -find "${FLAGS[@]}" -f '{{ if eq .Name "main" }} {{.ImportPath}} {{ end }}' ./... 2> >(output::indent >&2) } setupProcfile() { - if [ -f "${build}/Procfile" ]; then + if [[ -f "${build}/Procfile" ]]; then return fi - local pkgs=$(mainPackagesInModule) - if [ -z "${pkgs}" ]; then + local pkgs + # shellcheck disable=SC2310 + pkgs=$(mainPackagesInModule) + if [[ -z "${pkgs}" ]]; then return fi local pf=() for pkg in ${pkgs}; do - local bn=$(basename ${pkg}) + local bn + bn=$(basename "${pkg}") pf+=("${bn}: bin/${bn}") done - if [ ${#pf} -eq 0 ]; then + if [[ ${#pf} -eq 0 ]]; then return fi output::step "Created a Procfile with the following entries:" - if [ ${#pf[@]} -eq 1 ]; then - local line="web: bin/$(echo ${pf[0]} | cut -d / -f 2)" - echo "${line}" >${build}/Procfile + if [[ ${#pf[@]} -eq 1 ]]; then + local line + line="web: bin/$(echo "${pf[0]}" | cut -d / -f 2)" + echo "${line}" >"${build}/Procfile" echo "${line}" | output::indent - elif [ ${#pf[@]} -gt 1 ]; then + elif [[ ${#pf[@]} -gt 1 ]]; then for pfl in "${pf[@]}"; do - echo "${pfl}" >>${build}/Procfile + echo "${pfl}" >>"${build}/Procfile" echo "${pfl}" | output::indent done fi @@ -282,12 +302,13 @@ setupProcfile() { dependencies_install_start_time=$(build_data::current_unix_realtime) -cd ${build} +cd "${build}" || exit export GOBIN="${build}/bin" -if [ "${GO_SETUP_GOPATH_FOR_MODULE_CACHE}" = "true" ]; then +# shellcheck disable=SC2154 +if [[ "${GO_SETUP_GOPATH_FOR_MODULE_CACHE}" = "true" ]]; then export GOPATH="${build}/.heroku/go-path" - mkdir -p $GOPATH # ensure that it's created + mkdir -p "${GOPATH}" # ensure that it's created else export GOPATH="${cache}/go-path" fi @@ -296,15 +317,18 @@ fi # and only do this if it's <1.14. The 1.14 release and beyond handles this automatically # when the desired language version is 1.14+ and vendoring should be used. # https://golang.org/doc/go1.14#vendor -if [ -d "${build}/vendor" -a -s "${build}/go.sum" ]; then +if [[ -d "${build}/vendor" ]] && [[ -s "${build}/go.sum" ]]; then FLAGS+=(-mod=vendor) fi output::step "Determining packages to install" -pkgs=${GO_INSTALL_PACKAGE_SPEC:-$(awk '{ if ($1 == "//" && $2 == "+heroku" && $3 == "install" ) { print substr($0, index($0,$4)); exit }}' ${goMOD})} -if [ -z "${pkgs}" ]; then +# shellcheck disable=SC2154 +pkgs=${GO_INSTALL_PACKAGE_SPEC:-$(awk '{ if ($1 == "//" && $2 == "+heroku" && $3 == "install" ) { print substr($0, index($0,$4)); exit }}' "${goMOD}")} +if [[ -z "${pkgs}" ]]; then + # shellcheck disable=SC2310 pkgs=$(mainPackagesInModule) - if [ -z "${pkgs}" ]; then + if [[ -z "${pkgs}" ]]; then + # shellcheck disable=SC2154 output::warning <<-EOF No main packages found in module ${name} @@ -347,22 +371,28 @@ done setupProcfile -cd $build -mkdir -p $build/.profile.d -echo 'PATH=$PATH:$HOME/bin' >$build/.profile.d/go.sh +cd "${build}" || exit +mkdir -p "${build}/.profile.d" +# shellcheck disable=SC2016 +echo 'PATH=$PATH:$HOME/bin' >"${build}/.profile.d/go.sh" -if [ "${GO_INSTALL_TOOLS_IN_IMAGE}" = "true" ]; then +# shellcheck disable=SC2154 +if [[ "${GO_INSTALL_TOOLS_IN_IMAGE}" = "true" ]]; then output::step "Copying go tool chain to \$GOROOT=\$HOME/.heroku/go" mkdir -p "${build}/.heroku/go" cp -a "${GOROOT}/"* "${build}/.heroku/go" + # shellcheck disable=SC2016 echo 'export GOROOT=$HOME/.heroku/go' >"${build}/.profile.d/goroot.sh" + # shellcheck disable=SC2016 echo 'PATH=$PATH:$GOROOT/bin' >>"${build}/.profile.d/goroot.sh" fi t="${build}/.heroku/go" mkdir -p "${t}" t="${t}/.meta" +# shellcheck disable=SC2154 echo "TOOL=${TOOL}" >"${t}" +# shellcheck disable=SC2154 echo "NAME=${name}" >>"${t}" build_data::set_duration "total_duration" "${compile_start_time}" diff --git a/bin/detect b/bin/detect index 10234959..2eaa11bb 100755 --- a/bin/detect +++ b/bin/detect @@ -3,13 +3,15 @@ set -e build=$(cd "$1/" && pwd) -buildpack=$(cd "$(dirname $0)/.." && pwd) +buildpack=$(cd "$(dirname "$0")/.." && pwd) +# shellcheck disable=SC1091 source "${buildpack}/lib/output.sh" # Detect go.mod (supported) and legacy dependency manager files (unsupported, # but we still want to pass detection so that bin/compile can provide a helpful # migration error message rather than the generic "can't detect language" output). +# shellcheck disable=SC2235,SC2312 if test -f "${build}/go.mod" \ || #go modules diff --git a/bin/test b/bin/test index 144511c1..ba77885e 100755 --- a/bin/test +++ b/bin/test @@ -4,35 +4,40 @@ set -eo pipefail mkdir -p "${1}" "${2}" -build="$(cd ${1}/ && pwd)" -env_dir="$(cd ${2}/ && pwd)" -testpack=$(cd "$(dirname $0)/.." && pwd) +build="$(cd "${1}/" && pwd)" +env_dir="$(cd "${2}/" && pwd)" +testpack=$(cd "$(dirname "$0")/.." && pwd) +# shellcheck disable=SC2034 buildpack="${testpack}" +# shellcheck source=lib/common.sh disable=SC1091 source "${testpack}/lib/common.sh" # loadEnvDir "${env_dir}" # For now, load all of env_dir -if [ ! -z "${env_dir}" ]; then +if [[ -n "${env_dir}" ]]; then + # shellcheck disable=SC2231 for f in ${env_dir}/*; do - key=$(basename $f) - if [ -f "${f}" ]; then - export "${key}=$(cat "${f}" | sed -e "s:\${build_dir}:${build}:")" + key=$(basename "${f}") + if [[ -f "${f}" ]]; then + export "${key}=$(sed -e "s:\${build_dir}:${build}:" "${f}")" fi done fi -if [ -f "${build}/.heroku/go/.meta" ]; then +if [[ -f "${build}/.heroku/go/.meta" ]]; then + # shellcheck disable=SC1091 source "${build}/.heroku/go/.meta" fi export GOROOT="${build}/.heroku/go" # Use the GOPATH that was setup during compile, if it exists, so that # any modules downloaded during compile are re-used. -if [ -d "${build}/.heroku/go-path" ]; then +if [[ -d "${build}/.heroku/go-path" ]]; then export GOPATH="${build}/.heroku/go-path" else # Otherwise use a GOPATH that is outside of ${build} - export GOPATH="$(mktemp -d)" + GOPATH="$(mktemp -d)" + export GOPATH fi PATH="${build}/bin:${GOROOT}/bin:${PATH}" @@ -42,18 +47,18 @@ PATH="${build}/bin:${GOROOT}/bin:${PATH}" output=$(mktemp) cd "${build}" -if [ -f "${build}/.golangci.yml" -o -f "${build}/.golangci.toml" -o -f "${build}/.golangci.json" ]; then +if [[ -f "${build}/.golangci.yml" ]] || [[ -f "${build}/.golangci.toml" ]] || [[ -f "${build}/.golangci.json" ]]; then output::step "Running: golangci-lint -v --build-tags heroku run" - ${build}/.heroku/golangci/bin/golangci-lint -v --build-tags heroku run 2>&1 | output::indent + "${build}"/.heroku/golangci/bin/golangci-lint -v --build-tags heroku run 2>&1 | output::indent fi _tflags=("-race" "-v") -if [ -s "./go.sum" -a -d "./vendor" ]; then +if [[ -s "./go.sum" ]] && [[ -d "./vendor" ]]; then _tflags+=("-mod=vendor") else _tflags+=("-mod=readonly") fi output::step "Running Tests With: go test ${_tflags[*]} ./... | patter" -go test ${_tflags[@]} ./... 2>&1 | tee -a ${output} | patter +go test "${_tflags[@]}" ./... 2>&1 | tee -a "${output}" | patter echo if [[ -z "${GO_TEST_SKIP_BENCHMARK}" ]]; then _tflags=("${_tflags[@]:1}") # push -race off @@ -61,7 +66,7 @@ if [[ -z "${GO_TEST_SKIP_BENCHMARK}" ]]; then _tflags+=("-bench=.") _tflags+=("-benchmem") output::step "Running Benchmarks With: go test ${_tflags[*]} ./..." - go test ${_tflags[@]} ./... 2>&1 | output::indent + go test "${_tflags[@]}" ./... 2>&1 | output::indent fi output::step "Standard (Non TAP) test output" diff --git a/bin/test-compile b/bin/test-compile index 4414979f..5fb956d9 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -5,15 +5,16 @@ mkdir -p "$1" "$2" build=$(cd "$1/" && pwd) cache=$(cd "$2/" && pwd) env_dir="${3}" -testpack=$(cd "$(dirname $0)/.." && pwd) +testpack=$(cd "$(dirname "$0")/.." && pwd) echo "true" >"${env_dir}/GO_INSTALL_TOOLS_IN_IMAGE" echo "true" >"${env_dir}/GO_SETUP_GOPATH_FOR_MODULE_CACHE" buildpack="${testpack}" -source ${testpack}/bin/compile "${build}" "${cache}" "${env_dir}" +# shellcheck source=bin/compile +source "${testpack}/bin/compile" "${build}" "${cache}" "${env_dir}" -if [ -f "${build}/.golangci.yml" -o -f "${build}/.golangci.toml" -o -f "${build}/.golangci.json" ]; then +if [[ -f "${build}/.golangci.yml" ]] || [[ -f "${build}/.golangci.toml" ]] || [[ -f "${build}/.golangci.json" ]]; then output::step "/.golangci.{yml,toml,json} detected" tmp="$(mktemp -d)" mkdir -p "${build}/.heroku/golangci/bin" diff --git a/lib/common.sh b/lib/common.sh index db247f14..07c5817b 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -1,12 +1,17 @@ #!/bin/bash +# shellcheck disable=SC2034 # Variables like DataJSON, GO_LINKER_VALUE, TOOL are used by the caller (bin/compile) + # ----------------------------------------- # load environment variables # allow apps to specify cgo flags. The literal text '${build_dir}' is substituted for the build directory + +# shellcheck disable=SC2154 # buildpack, build, SOURCE_VERSION, DefaultGoVersion are set by the caller (bin/compile) DataJSON="${buildpack}/data.json" FilesJSON="${buildpack}/files.json" goMOD="${build}/go.mod" +# shellcheck disable=SC1091 source "$(dirname "${BASH_SOURCE[0]}")/output.sh" # We use --max-time/--retry-max-time for improved UX and metrics for hanging downloads compared to # seconds relying on the build system timeout. Go tarballs are up to ~70 MB and typically download in a few @@ -16,60 +21,64 @@ CURL="curl --no-progress-meter --location --fail --max-time 30 --retry-max-time TOOL="" # Default to $SOURCE_VERSION environment variable: https://devcenter.heroku.com/articles/buildpack-api#bin-compile -GO_LINKER_VALUE=${SOURCE_VERSION} +GO_LINKER_VALUE="${SOURCE_VERSION}" snapshotBinBefore() { - if [ ! -d "${build}/bin" ]; then + if [[ ! -d "${build}/bin" ]]; then return 0 fi - _oifs=$IFS + _oifs=${IFS} IFS=$'\n' _binBefore=() - for f in ${build}/bin/*; do - if [ -f $f ]; then - _binBefore+=($(shasum $f)) + for f in "${build}"/bin/*; do + if [[ -f "${f}" ]]; then + # shellcheck disable=SC2207 + _binBefore+=("$(shasum "${f}")") fi done - IFS=$_oifs + IFS=${_oifs} } binDiff() { - _oifs=$IFS + _oifs=${IFS} IFS=$'\n' local binAfter=() - for f in ${build}/bin/*; do - if [ -f $f ]; then - binAfter+=($(shasum $f)) + for f in "${build}"/bin/*; do + if [[ -f "${f}" ]]; then + # shellcheck disable=SC2207 + binAfter+=("$(shasum "${f}")") fi done local new=() for a in "${binAfter[@]}"; do - local let found=0 + local found=0 for b in "${_binBefore[@]}"; do - if [ "${a}" = "${b}" ]; then - let found+=1 + if [[ "${a}" = "${b}" ]]; then + ((found += 1)) fi done - if [ $found -eq 0 ]; then - new+=("./bin/$(basename $(echo $a | awk '{print $2}'))") + if [[ "${found}" -eq 0 ]]; then + # shellcheck disable=SC2312 + new+=("./bin/$(basename "$(echo "${a}" | awk '{print $2}')")") fi done - IFS=$_oifs - echo ${new[@]} + IFS=${_oifs} + echo "${new[@]}" } knownFile() { local fileName="${1}" - <${FilesJSON} jq -e 'to_entries | map(select(.key == "'${fileName}'")) | any' &>/dev/null + <"${FilesJSON}" jq -e 'to_entries | map(select(.key == "'"${fileName}"'")) | any' &>/dev/null } downloadFile() { local fileName="${1}" - if ! knownFile ${fileName}; then + # shellcheck disable=SC2310 + if ! knownFile "${fileName}"; then output::error <<-EOF Error: The requested file (${fileName}) is unknown to the buildpack! @@ -88,10 +97,13 @@ downloadFile() { local targetFile="${targetDir}/${fileName}" mkdir -p "${targetDir}" - pushd "${targetDir}" &>/dev/null + pushd "${targetDir}" &>/dev/null || return 1 output::step "Fetching ${fileName}" - local url="$(<"${FilesJSON}" jq -r '."'${fileName}'".URL')" + local url + url="$(<"${FilesJSON}" jq -r '."'"${fileName}"'".URL')" + # shellcheck disable=SC2312 ${CURL} -o "${fileName}" "${url}" 2>&1 | output::indent + # shellcheck disable=SC2310 if ! SHAValid "${fileName}" "${targetFile}"; then output::error <<-EOF Error: Downloaded file (${fileName}) SHA does not match recorded SHA. @@ -100,18 +112,22 @@ downloadFile() { EOF exit 1 fi - if [ -n "${xCmd}" ]; then - ${xCmd} ${targetFile} 2>&1 | output::indent + if [[ -n "${xCmd}" ]]; then + # shellcheck disable=SC2312 + ${xCmd} "${targetFile}" 2>&1 | output::indent fi - popd &>/dev/null + popd &>/dev/null || return 1 } SHAValid() { local fileName="${1}" local targetFile="${2}" - local expected="$(<"${FilesJSON}" jq -r '."'${fileName}'".SHA')" - local actual="$(shasum -a256 "${targetFile}" | cut -d \ -f 1)" - [ "${actual}" = "${expected}" ] + local expected + expected="$(<"${FilesJSON}" jq -r '."'"${fileName}"'".SHA')" + local actual + # shellcheck disable=SC2312 + actual="$(shasum -a256 "${targetFile}" | cut -d \ -f 1)" + [[ "${actual}" = "${expected}" ]] } ensureFile() { @@ -120,12 +136,13 @@ ensureFile() { local xCmd="${3}" local targetFile="${targetDir}/${fileName}" local download="false" - if [ ! -f "${targetFile}" ]; then + # shellcheck disable=SC2310 + if [[ ! -f "${targetFile}" ]]; then download="true" elif ! SHAValid "${fileName}" "${targetFile}"; then download="true" fi - if [ "${download}" = "true" ]; then + if [[ "${download}" = "true" ]]; then downloadFile "${fileName}" "${targetDir}" "${xCmd}" fi } @@ -163,12 +180,12 @@ loadEnvDir() { envFlags+=("GO_SETUP_GOPATH_FOR_MODULE_CACHE") envFlags+=("GO_TEST_SKIP_BENCHMARK") local env_dir="${1}" - if [ ! -z "${env_dir}" ]; then + if [[ -n "${env_dir}" ]]; then mkdir -p "${env_dir}" env_dir=$(cd "${env_dir}/" && pwd) - for key in ${envFlags[@]}; do - if [ -f "${env_dir}/${key}" ]; then - export "${key}=$(cat "${env_dir}/${key}" | sed -e "s:\${build_dir}:${build}:")" + for key in "${envFlags[@]}"; do + if [[ -f "${env_dir}/${key}" ]]; then + export "${key}=$(sed -e "s:\${build_dir}:${build}:" <"${env_dir}/${key}")" fi done fi @@ -178,9 +195,10 @@ clearGitCredHelper() { git config --global --unset credential.helper } +# shellcheck disable=SC2016,SC2086,SC2250,SC2292,SC2002,SC2248,SC2312 setGitCredHelper() { git config --global credential.helper '!#GoGitCredHelper - env_dir="'$(cd ${1}/ && pwd)'" + env_dir="'"$(cd "${1}"/ && pwd)"'" gitCredHelper() { #echo "${1}\n" >&2 #debug case "${1}" in @@ -256,27 +274,27 @@ supportsGoModules() { determineTool() { # Check GOVERSION first - it overrides all tool-specific configurations - if [ -n "${GOVERSION}" ]; then + if [[ -n "${GOVERSION}" ]]; then ver="${GOVERSION}" go_version_origin="GOVERSION" build_data::set_string "go_version_origin" "${go_version_origin}" build_data::set_string "go_version_requested" "${ver}" fi - if [ -f "${goMOD}" ]; then + if [[ -f "${goMOD}" ]]; then TOOL="gomodules" build_data::set_string "go_tool" "${TOOL}" output::step "Detected go modules via go.mod" # Determine Go version from go.mod if not already set by GOVERSION - if [ -z "${ver}" ]; then - ver=$(awk '{ if ($1 == "//" && $2 == "+heroku" && $3 == "goVersion" ) { print $4; exit } }' ${goMOD}) - if [ -n "${ver}" ]; then + if [[ -z "${ver}" ]]; then + ver=$(awk '{ if ($1 == "//" && $2 == "+heroku" && $3 == "goVersion" ) { print $4; exit } }' "${goMOD}") + if [[ -n "${ver}" ]]; then go_version_origin="go.mod (heroku comment)" else - ver=$(awk '{ if ($1 == "go" ) { print "go" $2; exit } }' ${goMOD}) - if [ -n "${ver}" ]; then + ver=$(awk '{ if ($1 == "go" ) { print "go" $2; exit } }' "${goMOD}") + if [[ -n "${ver}" ]]; then go_version_origin="go.mod" else ver=${DefaultGoVersion} @@ -287,11 +305,11 @@ determineTool() { build_data::set_string "go_version_requested" "${ver}" fi - name=$(awk '{ if ($1 == "module" ) { gsub(/"/, "", $2); print $2; exit } }' <${goMOD}) + name=$(awk '{ if ($1 == "module" ) { gsub(/"/, "", $2); print $2; exit } }' <"${goMOD}") output::step "Detected Module Name: ${name}" warnGoVersionOverride - if [ "${go_version_origin}" = "default" ]; then + if [[ "${go_version_origin}" = "default" ]]; then output::warning <<-EOF The go.mod file for this project does not specify a Go version. @@ -302,6 +320,7 @@ determineTool() { EOF fi + # shellcheck disable=SC2310 if supportsGoModules "${ver}"; then output::error <<-EOF Error: You are using ${ver}, which does not support Go modules. @@ -318,19 +337,20 @@ determineTool() { fi else local legacy_tool="" - if [ -f "${build}/Gopkg.lock" ]; then + # shellcheck disable=SC2312 + if [[ -f "${build}/Gopkg.lock" ]]; then legacy_tool="dep" - elif [ -f "${build}/Godeps/Godeps.json" ]; then + elif [[ -f "${build}/Godeps/Godeps.json" ]]; then legacy_tool="godep" - elif [ -f "${build}/vendor/vendor.json" ]; then + elif [[ -f "${build}/vendor/vendor.json" ]]; then legacy_tool="govendor" - elif [ -f "${build}/glide.yaml" ]; then + elif [[ -f "${build}/glide.yaml" ]]; then legacy_tool="glide" - elif [ -d "${build}/src" ] && [ -n "$(find "${build}/src" -mindepth 2 -type f -name '*.go' | sed 1q)" ]; then + elif [[ -d "${build}/src" ]] && [[ -n "$(find "${build}/src" -mindepth 2 -type f -name '*.go' | sed 1q)" ]]; then legacy_tool="gb" fi - if [ -n "${legacy_tool}" ]; then + if [[ -n "${legacy_tool}" ]]; then build_data::set_string "go_tool" "${legacy_tool}" output::error <<-EOF Error: Your app appears to use '${legacy_tool}' for dependency management, diff --git a/sbin/add-version b/sbin/add-version index 9cc11b9f..bb25f5a3 100755 --- a/sbin/add-version +++ b/sbin/add-version @@ -5,21 +5,21 @@ set -e BASEURL="https://dl.google.com/go" V="$1" -if [ -z "$V" ]; then +if [ -z "${V}" ]; then echo "usage: $0 " >&2 exit 1 fi -tgz_fn="go$V.linux-amd64.tar.gz" -tgz_url="$BASEURL/$tgz_fn" -sha256_url="$tgz_url.sha256" +tgz_fn="go${V}.linux-amd64.tar.gz" +tgz_url="${BASEURL}/${tgz_fn}" +sha256_url="${tgz_url}.sha256" -if ! sha256_content="$(curl -s -f "$sha256_url")"; then - echo "error: adding $V: couldn't fetch $sha256_url" >&2 +if ! sha256_content="$(curl -s -f "${sha256_url}")"; then + echo "error: adding ${V}: couldn't fetch ${sha256_url}" >&2 exit 1 fi -TGZ_FN="$tgz_fn" TGZ_URL="$tgz_url" SHA256="$sha256_content" jq -S '.[env.TGZ_FN] = {URL: env.TGZ_URL, SHA: env.SHA256 }' files.json >files.json.tmp +TGZ_FN="${tgz_fn}" TGZ_URL="${tgz_url}" SHA256="${sha256_content}" jq -S '.[env.TGZ_FN] = {URL: env.TGZ_URL, SHA: env.SHA256 }' files.json >files.json.tmp mv files.json.tmp files.json -echo "added $V to files.json" +echo "added ${V} to files.json" diff --git a/sbin/publish.sh b/sbin/publish.sh index f12228b6..e81c2d1d 100755 --- a/sbin/publish.sh +++ b/sbin/publish.sh @@ -4,11 +4,11 @@ set -euo pipefail BP_NAME=${1:-"heroku/go"} -curVersion=$(heroku buildpacks:versions "$BP_NAME" | awk 'FNR == 3 { print $1 }') +curVersion=$(heroku buildpacks:versions "${BP_NAME}" | awk 'FNR == 3 { print $1 }') newVersion="v$((curVersion + 1))" -read -p "Deploy as version: $newVersion [y/n]? " choice -case "$choice" in +read -rp "Deploy as version: ${newVersion} [y/n]? " choice +case "${choice}" in y | Y) echo "" ;; n | N) exit 0 ;; *) exit 1 ;; @@ -16,11 +16,11 @@ esac git fetch origin originMain=$(git rev-parse origin/main) -echo "Tagging commit $originMain with $newVersion... " -git tag "$newVersion" "${originMain:?}" -git push origin refs/tags/$newVersion +echo "Tagging commit ${originMain} with ${newVersion}... " +git tag "${newVersion}" "${originMain:?}" +git push origin "refs/tags/${newVersion}" echo -e "\nPublishing to the buildpack registry..." -heroku buildpacks:publish "$BP_NAME" "$newVersion" +heroku buildpacks:publish "${BP_NAME}" "${newVersion}" echo heroku buildpacks:versions "${BP_NAME}" | head -n 3 diff --git a/sbin/update-go-versions b/sbin/update-go-versions index d1132901..f1c5d58c 100755 --- a/sbin/update-go-versions +++ b/sbin/update-go-versions @@ -4,7 +4,7 @@ set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")/.." -if [ -z "${1:-}" ]; then +if [[ -z "${1:-}" ]]; then echo "Usage: $0 [comma-separated-versions]" echo "" echo "Examples:" @@ -25,34 +25,34 @@ if [ -z "${1:-}" ]; then 'https://go.dev/dl/?mode=json&include=stable' \ | jq -r '.[].version | sub("^go"; "")') - if [ -z "$latest_versions" ]; then + if [[ -z "${latest_versions}" ]]; then echo "Error: Failed to fetch latest versions from go.dev" exit 1 fi # Convert newline-separated versions to comma-separated - versions_arg=$(echo "$latest_versions" | tr '\n' ',' | sed 's/,$//') - echo "Found latest stable versions: $versions_arg" - IFS=',' read -ra VERSION_ARRAY <<<"$versions_arg" + versions_arg=$(echo "${latest_versions}" | tr '\n' ',' | sed 's/,$//') + echo "Found latest stable versions: ${versions_arg}" + IFS=',' read -ra VERSION_ARRAY <<<"${versions_arg}" else IFS=',' read -ra VERSION_ARRAY <<<"$1" fi TEMP_CHANGELOG=$(mktemp) -awk '/## \[Unreleased\]/{print; print ""; exit} {print}' CHANGELOG.md >"$TEMP_CHANGELOG" +awk '/## \[Unreleased\]/{print; print ""; exit} {print}' CHANGELOG.md >"${TEMP_CHANGELOG}" for version in "${VERSION_ARRAY[@]}"; do tarball="go${version}.linux-amd64.tar.gz" - if jq -e --arg key "$tarball" 'has($key)' files.json >/dev/null 2>&1; then - echo "Version $version already exists, skipping" + if jq -e --arg key "${tarball}" 'has($key)' files.json >/dev/null 2>&1; then + echo "Version ${version} already exists, skipping" continue fi - sbin/add-version "$version" - echo "* Add go${version}" >>"$TEMP_CHANGELOG" + sbin/add-version "${version}" + echo "* Add go${version}" >>"${TEMP_CHANGELOG}" - major_minor=$(echo "$version" | cut -d. -f1,2) + major_minor=$(echo "${version}" | cut -d. -f1,2) jq --arg major_minor "go${major_minor}" --arg version "go${version}" ' .Go.VersionExpansion[$major_minor] = $version | @@ -70,9 +70,9 @@ for version in "${VERSION_ARRAY[@]}"; do ) ' data.json >data.json.tmp mv data.json.tmp data.json - echo "* go${major_minor} defaults to ${version}" >>"$TEMP_CHANGELOG" + echo "* go${major_minor} defaults to ${version}" >>"${TEMP_CHANGELOG}" done -awk '/## \[Unreleased\]/{flag=1; next} flag{if(flag==1 && NF==0){flag=2; next}} flag==2' CHANGELOG.md >>"$TEMP_CHANGELOG" +awk '/## \[Unreleased\]/{flag=1; next} flag{if(flag==1 && NF==0){flag=2; next}} flag==2' CHANGELOG.md >>"${TEMP_CHANGELOG}" -mv "$TEMP_CHANGELOG" CHANGELOG.md +mv "${TEMP_CHANGELOG}" CHANGELOG.md diff --git a/test/run.sh b/test/run.sh index aeef76fe..ce91e3ec 100755 --- a/test/run.sh +++ b/test/run.sh @@ -313,7 +313,7 @@ testModPrivateProxy() { local repo="${BUILDPACK_HOME}/test/fixtures/mod-private-proxy/repo" fixture "mod-private-proxy/app" - env "GOPROXY" "file://$repo" + env "GOPROXY" "file://${repo}" env "GOPRIVATE" "git.fury.io/*" env "GONOPROXY" "none" @@ -485,8 +485,12 @@ testDeprecatedToolDetected() { assertCapturedError 1 "support for dep has been removed" } -pushd $(dirname 0) >/dev/null -popd >/dev/null +pushd "$(dirname 0)" >/dev/null || exit +popd >/dev/null || exit -source $(pwd)/test/utils.sh -source $(pwd)/test/shunit2.sh +# shellcheck source=test/utils.sh +# shellcheck disable=SC2312 +source "$(pwd)/test/utils.sh" +# shellcheck source=test/shunit2.sh +# shellcheck disable=SC1091,SC2312 +source "$(pwd)/test/shunit2.sh" diff --git a/test/utils.sh b/test/utils.sh index 93291f0e..4fb468b8 100644 --- a/test/utils.sh +++ b/test/utils.sh @@ -1,33 +1,36 @@ +# shellcheck shell=bash + ############## ## shunit2 setup/teardown functions ## +# shellcheck disable=SC2154 # SHUNIT_TMPDIR is set by the shunit2 test framework oneTimeSetUp() { - TEST_SUITE_CACHE="$(mktemp -d ${SHUNIT_TMPDIR}/test_suite_cache.XXXX)" + TEST_SUITE_CACHE="$(mktemp -d "${SHUNIT_TMPDIR}/test_suite_cache.XXXX")" BUILDPACK_HOME=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) DEFAULT_GO_VERSION="$(<"${BUILDPACK_HOME}/data.json" jq -r '.Go.DefaultVersion')" } oneTimeTearDown() { - rm -rf ${TEST_SUITE_CACHE} + rm -rf "${TEST_SUITE_CACHE}" } setUp() { - OUTPUT_DIR="$(mktemp -d ${SHUNIT_TMPDIR}/output.XXXX)" + OUTPUT_DIR="$(mktemp -d "${SHUNIT_TMPDIR}/output.XXXX")" STD_OUT="${OUTPUT_DIR}/stdout" STD_ERR="${OUTPUT_DIR}/stderr" BUILD_DIR="${OUTPUT_DIR}/build" CACHE_DIR="${OUTPUT_DIR}/cache" ENV_DIR="${OUTPUT_DIR}/env" - mkdir -p ${OUTPUT_DIR} - mkdir -p ${BUILD_DIR} - mkdir -p ${CACHE_DIR} - mkdir -p ${ENV_DIR} + mkdir -p "${OUTPUT_DIR}" + mkdir -p "${BUILD_DIR}" + mkdir -p "${CACHE_DIR}" + mkdir -p "${ENV_DIR}" } tearDown() { - if [ -d "${OUTPUT_DIR}" ]; then + if [[ -d "${OUTPUT_DIR}" ]]; then # Go modules are read-only by default; give ourselves write permission to delete it first chmod -R +w "${OUTPUT_DIR}" rm -rf "${OUTPUT_DIR}" @@ -41,35 +44,38 @@ tearDown() { capture() { resetCapture - LAST_COMMAND="$@" + LAST_COMMAND="$*" - $@ >${STD_OUT} 2>${STD_ERR} + "$@" >"${STD_OUT}" 2>"${STD_ERR}" RETURN=$? - rtrn=${RETURN} # deprecated + # shellcheck disable=SC2034 # deprecated, used externally + rtrn=${RETURN} } continue_capture() { - LAST_COMMAND="$@" + # shellcheck disable=SC2034 # LAST_COMMAND is used externally + LAST_COMMAND="$*" - $@ >>${STD_OUT} 2>>${STD_ERR} + "$@" >>"${STD_OUT}" 2>>"${STD_ERR}" local cr=$? - if [ "$RETURN" = "0" ]; then - RETURN=$cr + if [[ "${RETURN}" = "0" ]]; then + RETURN=${cr} fi - rtrn=${RETURN} # deprecated + # shellcheck disable=SC2034 # deprecated, used externally + rtrn=${RETURN} } resetCapture() { - if [ -f ${STD_OUT} ]; then - rm ${STD_OUT} + if [[ -f "${STD_OUT}" ]]; then + rm "${STD_OUT}" fi - if [ -f "${STD_OUT}_no_color" ]; then + if [[ -f "${STD_OUT}_no_color" ]]; then rm "${STD_OUT}_no_color" fi - if [ -f ${STD_ERR} ]; then - rm ${STD_ERR} + if [[ -f "${STD_ERR}" ]]; then + rm "${STD_ERR}" fi unset LAST_COMMAND @@ -81,13 +87,14 @@ fixture() { local fixture="${1}" echo "* fixture: ${fixture}" local fp="${BUILDPACK_HOME}/test/fixtures/${fixture}" - tar -cf - -C $fp . | tar -x -C ${BUILD_DIR} + # shellcheck disable=SC2312 + tar -cf - -C "${fp}" . | tar -x -C "${BUILD_DIR}" } env() { local var="${1}" local val="${2}" - if [ -z "${var}" ]; then + if [[ -z "${var}" ]]; then fail "set env var w/o specifying name" exit 1 fi @@ -96,7 +103,7 @@ env() { detect() { echo "* detect" - capture ${BUILDPACK_HOME}/bin/detect ${BUILD_DIR} + capture "${BUILDPACK_HOME}/bin/detect" "${BUILD_DIR}" } assertDetected() { @@ -107,7 +114,7 @@ assertDetected() { compile() { echo "* compile" - capture ${BUILDPACK_HOME}/bin/compile ${BUILD_DIR} ${CACHE_DIR} ${ENV_DIR} + capture "${BUILDPACK_HOME}/bin/compile" "${BUILD_DIR}" "${CACHE_DIR}" "${ENV_DIR}" } dotest() { @@ -122,61 +129,65 @@ dotest() { } release() { - capture ${BUILDPACK_HOME}/bin/release ${BUILD_DIR} + capture "${BUILDPACK_HOME}/bin/release" "${BUILD_DIR}" } assertFile() { local content="${1}" local name="${2}" local tgt="${BUILD_DIR}/${name}" - assertEquals "${content}" "$(cat ${tgt})" + # shellcheck disable=SC2312 + assertEquals "${content}" "$(cat "${tgt}")" } assertBuildDirFileDoesNotExist() { local name="${1}" local tgt="${BUILD_DIR}/${name}" - assertTrue "File ${name} exists" "[ ! -f ${tgt} ]" + assertTrue "File ${name} exists" "[[ ! -f ${tgt} ]]" } assertBuildDirFileExists() { local name="${1}" local tgt="${BUILD_DIR}/${name}" - assertTrue "File ${name} does not exist" "[ -f ${tgt} ]" + assertTrue "File ${name} does not exist" "[[ -f ${tgt} ]]" } assertFileExists() { local name="${1}" - assertTrue "File ${name} does not exist" "[ -f ${name} ]" + assertTrue "File ${name} does not exist" "[[ -f ${name} ]]" } assertDirExists() { local path="${1}" - assertTrue "Dir ${path} does not exist" "[ -d ${path} ]" + assertTrue "Dir ${path} does not exist" "[[ -d ${path} ]]" } assertDirDoesNotExist() { local path="${1}" - assertTrue "Dir ${path} exists" "[ ! -d ${path} ]" + assertTrue "Dir ${path} exists" "[[ ! -d ${path} ]]" } assertCompiledBinaryExists() { local name="${1:-fixture}" local tgt="${BUILD_DIR}/bin/${name}" - assertTrue "Compiled binary (${tgt}) exists" "[ -x ${tgt} ]" + assertTrue "Compiled binary (${tgt}) exists" "[[ -x ${tgt} ]]" } assertCompiledBinaryOutputs() { local name="${1}" + # shellcheck disable=SC2034 # output is reserved for future use local output="${2}" capture "${BUILD_DIR}/bin/${name}" } assertCapturedEquals() { - assertEquals "$@" "$(cat ${STD_OUT})" + # shellcheck disable=SC2312 + assertEquals "$@" "$(cat "${STD_OUT}")" } assertCapturedNotEquals() { - assertNotEquals "$@" "$(cat ${STD_OUT})" + # shellcheck disable=SC2312 + assertNotEquals "$@" "$(cat "${STD_OUT}")" } assertCaptured() { @@ -193,7 +204,8 @@ assertNotCaptured() { assertCapturedSuccess() { assertEquals "Expected captured exit code to be 0; was <${RETURN}>" "0" "${RETURN}" - assertEquals "Expected STD_ERR to be empty; was <$(cat ${STD_ERR})>" "" "$(cat ${STD_ERR})" + # shellcheck disable=SC2312 + assertEquals "Expected STD_ERR to be empty; was <$(cat "${STD_ERR}")>" "" "$(cat "${STD_ERR}")" } assertCapturedExitSuccess() { @@ -202,53 +214,57 @@ assertCapturedExitSuccess() { # assertCapturedError [[expectedErrorCode] expectedErrorMsg] assertCapturedError() { - if [ $# -gt 1 ]; then - local expectedErrorCode="${1}" + local expectedErrorCode="" + if [[ $# -gt 1 ]]; then + expectedErrorCode="${1}" shift fi local expectedErrorMsg="${1:-""}" - if [ -z ${expectedErrorCode} ]; then - assertTrue "Expected captured exit code to be greater than 0; was <${RETURN}>" "[ ${RETURN} -gt 0 ]" + if [[ -z "${expectedErrorCode}" ]]; then + assertTrue "Expected captured exit code to be greater than 0; was <${RETURN}>" "[[ ${RETURN} -gt 0 ]]" else - assertTrue "Expected captured exit code to be <${expectedErrorCode}>; was <${RETURN}>" "[ ${RETURN} -eq ${expectedErrorCode} ]" + assertTrue "Expected captured exit code to be <${expectedErrorCode}>; was <${RETURN}>" "[[ ${RETURN} -eq ${expectedErrorCode} ]]" fi - if [ "${expectedErrorMsg}" != "" ]; then + if [[ "${expectedErrorMsg}" != "" ]]; then assertFileContains "Expected STD_ERR to contain error <${expectedErrorMsg}>" "${expectedErrorMsg}" "${STD_ERR}" fi } _assertContains() { - if [ 5 -eq $# ]; then + if [[ 5 -eq $# ]]; then local msg="${1}" shift - elif [ ! 4 -eq $# ]; then + elif [[ 4 -ne $# ]]; then fail "Expected 4 or 5 parameters; Receieved $# parameters" fi - local needle=$1 - local haystack=$2 - local expectation=$3 - local haystack_type=$4 + local needle="${1}" + local haystack="${2}" + local expectation="${3}" + local haystack_type="${4}" case "${haystack_type}" in "file") local haystack_no_color="${haystack}_no_color" - if [ ! -e ${haystack_no_color} ]; then - sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <${haystack} >${haystack_no_color} + if [[ ! -e "${haystack_no_color}" ]]; then + sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" <"${haystack}" >"${haystack_no_color}" fi - ## echo grep -q -F -e "${needle}" ${haystack_no_color} - grep -q -F -e "${needle}" ${haystack_no_color} + grep -q -F -e "${needle}" "${haystack_no_color}" ;; "text") echo "${haystack}" | grep -q -F -e "${needle}" ;; + *) fail "Unknown haystack_type: ${haystack_type}" ;; esac - if [ "${expectation}" != "$?" ]; then + local result=$? + if [[ "${expectation}" != "${result}" ]]; then + local default_msg case "${expectation}" in 0) default_msg="Expected <${haystack}> to contain <${needle}>" ;; 1) default_msg="Did not expect <${haystack}> to contain <${needle}>" ;; + *) default_msg="Unexpected expectation value: ${expectation}" ;; esac fail "${msg:-${default_msg}}" @@ -276,9 +292,12 @@ command_exists() { } assertFileMD5() { - expectedHash=$1 - filename=$2 + local expectedHash="${1}" + local filename="${2}" + local md5_cmd + local expected_md5_cmd_output + # shellcheck disable=SC2310 # command_exists used in if condition if command_exists "md5sum"; then md5_cmd="md5sum ${filename}" expected_md5_cmd_output="${expectedHash} ${filename}" @@ -289,6 +308,7 @@ assertFileMD5() { fail "no suitable MD5 hashing command found on this system" fi + # shellcheck disable=SC2312 assertEquals "${expected_md5_cmd_output}" "$(${md5_cmd})" } @@ -310,7 +330,7 @@ assertInstalledFixtureBinary() { } assertGoInstallCaptured() { - local go_ver=${1:-${DEFAULT_GO_VERSION}} + local go_ver="${1:-${DEFAULT_GO_VERSION}}" assertCaptured "Installing ${go_ver}" assertCaptured "Fetching ${go_ver}" } From 4f9307abe5d6433ee6e9f92532be94ef7b0b8c9c Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 22:57:32 -0400 Subject: [PATCH 06/23] Standardize shebangs to `#!/usr/bin/env bash` --- bin/compile | 2 +- bin/test | 2 +- bin/test-compile | 2 +- lib/common.sh | 2 +- sbin/update-go-versions | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/compile b/bin/compile index e24ebd7a..de429a40 100755 --- a/bin/compile +++ b/bin/compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # usage: bin/compile set -eo pipefail diff --git a/bin/test b/bin/test index ba77885e..617fefaa 100755 --- a/bin/test +++ b/bin/test @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # usage: bin/test set -eo pipefail diff --git a/bin/test-compile b/bin/test-compile index 5fb956d9..789f8ec8 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # usage: bin/test-compile mkdir -p "$1" "$2" diff --git a/lib/common.sh b/lib/common.sh index 07c5817b..c6b22018 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # shellcheck disable=SC2034 # Variables like DataJSON, GO_LINKER_VALUE, TOOL are used by the caller (bin/compile) diff --git a/sbin/update-go-versions b/sbin/update-go-versions index f1c5d58c..746b13ef 100755 --- a/sbin/update-go-versions +++ b/sbin/update-go-versions @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -euo pipefail From 075c075207e1eb04a41ef1a4e0b609596db602c1 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:07:00 -0400 Subject: [PATCH 07/23] Use `BUILDPACK_DIR` with `BASH_SOURCE[0]` for buildpack path resolution --- bin/compile | 8 +++----- bin/detect | 5 ++--- bin/test | 10 ++++------ bin/test-compile | 6 ++---- lib/common.sh | 6 +++--- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/bin/compile b/bin/compile index de429a40..206f733a 100755 --- a/bin/compile +++ b/bin/compile @@ -11,16 +11,14 @@ cache_root=$(cd "$2/" && pwd) cache="${cache_root}/.heroku/go" mkdir -p "${cache}" env_dir="${3}" -buildpack=$(cd "$(dirname "$0")/.." && pwd) +BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) -# shellcheck source=lib/common.sh disable=SC1091 -source "${buildpack}/lib/common.sh" +source "${BUILDPACK_DIR}/lib/common.sh" # Set CACHE_DIR (used by build_data) # shellcheck disable=SC2034 CACHE_DIR="${cache_root}" -# shellcheck source=lib/build_data.sh disable=SC1091 -source "${buildpack}/lib/build_data.sh" +source "${BUILDPACK_DIR}/lib/build_data.sh" compile_start_time=$(build_data::current_unix_realtime) diff --git a/bin/detect b/bin/detect index 2eaa11bb..5aba0608 100755 --- a/bin/detect +++ b/bin/detect @@ -3,10 +3,9 @@ set -e build=$(cd "$1/" && pwd) -buildpack=$(cd "$(dirname "$0")/.." && pwd) +BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) -# shellcheck disable=SC1091 -source "${buildpack}/lib/output.sh" +source "${BUILDPACK_DIR}/lib/output.sh" # Detect go.mod (supported) and legacy dependency manager files (unsupported, # but we still want to pass detection so that bin/compile can provide a helpful diff --git a/bin/test b/bin/test index 617fefaa..82dde09f 100755 --- a/bin/test +++ b/bin/test @@ -6,11 +6,9 @@ set -eo pipefail mkdir -p "${1}" "${2}" build="$(cd "${1}/" && pwd)" env_dir="$(cd "${2}/" && pwd)" -testpack=$(cd "$(dirname "$0")/.." && pwd) -# shellcheck disable=SC2034 -buildpack="${testpack}" -# shellcheck source=lib/common.sh disable=SC1091 -source "${testpack}/lib/common.sh" +BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) + +source "${BUILDPACK_DIR}/lib/common.sh" # loadEnvDir "${env_dir}" # For now, load all of env_dir @@ -42,7 +40,7 @@ fi PATH="${build}/bin:${GOROOT}/bin:${PATH}" # Install our vendored copy of github.com/apg/patter -(cd "${testpack}/lib/src/github.com/apg/patter" && GOPATH="${testpack}/lib" GOBIN="${build}/bin" go install .) +(cd "${BUILDPACK_DIR}/lib/src/github.com/apg/patter" && GOPATH="${BUILDPACK_DIR}/lib" GOBIN="${build}/bin" go install .) output=$(mktemp) diff --git a/bin/test-compile b/bin/test-compile index 789f8ec8..184f5a53 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -5,14 +5,12 @@ mkdir -p "$1" "$2" build=$(cd "$1/" && pwd) cache=$(cd "$2/" && pwd) env_dir="${3}" -testpack=$(cd "$(dirname "$0")/.." && pwd) +BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) echo "true" >"${env_dir}/GO_INSTALL_TOOLS_IN_IMAGE" echo "true" >"${env_dir}/GO_SETUP_GOPATH_FOR_MODULE_CACHE" -buildpack="${testpack}" -# shellcheck source=bin/compile -source "${testpack}/bin/compile" "${build}" "${cache}" "${env_dir}" +source "${BUILDPACK_DIR}/bin/compile" "${build}" "${cache}" "${env_dir}" if [[ -f "${build}/.golangci.yml" ]] || [[ -f "${build}/.golangci.toml" ]] || [[ -f "${build}/.golangci.json" ]]; then output::step "/.golangci.{yml,toml,json} detected" diff --git a/lib/common.sh b/lib/common.sh index c6b22018..90ab44fd 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -6,9 +6,9 @@ # load environment variables # allow apps to specify cgo flags. The literal text '${build_dir}' is substituted for the build directory -# shellcheck disable=SC2154 # buildpack, build, SOURCE_VERSION, DefaultGoVersion are set by the caller (bin/compile) -DataJSON="${buildpack}/data.json" -FilesJSON="${buildpack}/files.json" +# shellcheck disable=SC2154 # BUILDPACK_DIR, build, SOURCE_VERSION, DefaultGoVersion are set by the caller (bin/compile) +DataJSON="${BUILDPACK_DIR}/data.json" +FilesJSON="${BUILDPACK_DIR}/files.json" goMOD="${build}/go.mod" # shellcheck disable=SC1091 From e7c03ca94449099543417e7ab85d30bd7405bf49 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:12:45 -0400 Subject: [PATCH 08/23] Add `set -euo pipefail` to all buildpack scripts --- bin/compile | 16 +++++++--------- bin/detect | 2 +- bin/release | 2 ++ bin/test | 4 ++-- bin/test-compile | 2 ++ lib/build_data.sh | 5 +++-- lib/common.sh | 15 +++++++-------- lib/output.sh | 2 +- 8 files changed, 25 insertions(+), 23 deletions(-) diff --git a/bin/compile b/bin/compile index 206f733a..ca5ca99a 100755 --- a/bin/compile +++ b/bin/compile @@ -1,9 +1,9 @@ #!/usr/bin/env bash # usage: bin/compile -set -eo pipefail +set -euo pipefail -[[ -n "${BUILDPACK_XTRACE}" ]] && set -o xtrace +[[ -n "${BUILDPACK_XTRACE:-}" ]] && set -o xtrace mkdir -p "$1" "$2" build=$(cd "$1/" && pwd) @@ -176,7 +176,7 @@ ensureGo() { } warnGoVersionOverride() { - if [[ -n "${GOVERSION}" ]]; then + if [[ -n "${GOVERSION:-}" ]]; then output::warning <<-EOF Using \$GOVERSION override. \$GOVERSION = ${GOVERSION} @@ -188,7 +188,7 @@ warnGoVersionOverride() { } warnPackageSpecOverride() { - if [[ -n "${GO_INSTALL_PACKAGE_SPEC}" ]]; then + if [[ -n "${GO_INSTALL_PACKAGE_SPEC:-}" ]]; then output::warning <<-EOF Using \$GO_INSTALL_PACKAGE_SPEC override. \$GO_INSTALL_PACKAGE_SPEC = ${GO_INSTALL_PACKAGE_SPEC} @@ -231,7 +231,7 @@ else fi # If $GO_LINKER_SYMBOL and GO_LINKER_VALUE are set, tell the linker to DTRT -if [[ -n "${GO_LINKER_SYMBOL}" ]] && [[ -n "${GO_LINKER_VALUE}" ]]; then +if [[ -n "${GO_LINKER_SYMBOL:-}" ]] && [[ -n "${GO_LINKER_VALUE:-}" ]]; then FLAGS+=(-ldflags "-X ${GO_LINKER_SYMBOL}=${GO_LINKER_VALUE}") fi @@ -303,8 +303,7 @@ dependencies_install_start_time=$(build_data::current_unix_realtime) cd "${build}" || exit export GOBIN="${build}/bin" -# shellcheck disable=SC2154 -if [[ "${GO_SETUP_GOPATH_FOR_MODULE_CACHE}" = "true" ]]; then +if [[ "${GO_SETUP_GOPATH_FOR_MODULE_CACHE:-}" = "true" ]]; then export GOPATH="${build}/.heroku/go-path" mkdir -p "${GOPATH}" # ensure that it's created else @@ -374,8 +373,7 @@ mkdir -p "${build}/.profile.d" # shellcheck disable=SC2016 echo 'PATH=$PATH:$HOME/bin' >"${build}/.profile.d/go.sh" -# shellcheck disable=SC2154 -if [[ "${GO_INSTALL_TOOLS_IN_IMAGE}" = "true" ]]; then +if [[ "${GO_INSTALL_TOOLS_IN_IMAGE:-}" = "true" ]]; then output::step "Copying go tool chain to \$GOROOT=\$HOME/.heroku/go" mkdir -p "${build}/.heroku/go" cp -a "${GOROOT}/"* "${build}/.heroku/go" diff --git a/bin/detect b/bin/detect index 5aba0608..7e12216f 100755 --- a/bin/detect +++ b/bin/detect @@ -1,6 +1,6 @@ #!/usr/bin/env bash # bin/detect -set -e +set -euo pipefail build=$(cd "$1/" && pwd) BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) diff --git a/bin/release b/bin/release index 4b7f74cc..750d026f 100755 --- a/bin/release +++ b/bin/release @@ -1,4 +1,6 @@ #!/usr/bin/env bash # bin/release +set -euo pipefail + echo "--- {}" diff --git a/bin/test b/bin/test index 82dde09f..ac863e84 100755 --- a/bin/test +++ b/bin/test @@ -1,7 +1,7 @@ #!/usr/bin/env bash # usage: bin/test -set -eo pipefail +set -euo pipefail mkdir -p "${1}" "${2}" build="$(cd "${1}/" && pwd)" @@ -58,7 +58,7 @@ fi output::step "Running Tests With: go test ${_tflags[*]} ./... | patter" go test "${_tflags[@]}" ./... 2>&1 | tee -a "${output}" | patter echo -if [[ -z "${GO_TEST_SKIP_BENCHMARK}" ]]; then +if [[ -z "${GO_TEST_SKIP_BENCHMARK:-}" ]]; then _tflags=("${_tflags[@]:1}") # push -race off _tflags+=("-run=_") _tflags+=("-bench=.") diff --git a/bin/test-compile b/bin/test-compile index 184f5a53..5d8923ca 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -1,6 +1,8 @@ #!/usr/bin/env bash # usage: bin/test-compile +set -euo pipefail + mkdir -p "$1" "$2" build=$(cd "$1/" && pwd) cache=$(cd "$2/" && pwd) diff --git a/lib/build_data.sh b/lib/build_data.sh index f026bbaa..8199af2e 100644 --- a/lib/build_data.sh +++ b/lib/build_data.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash -# Note: -u is omitted for now so we don't break non-strict callers. -set -eo pipefail +# This is technically redundant, since all consumers of this lib will have enabled these, +# however, it helps Shellcheck realise the options under which these functions will run. +set -euo pipefail BUILD_DATA_FILE="${CACHE_DIR:?}/build-data/go.json" PREVIOUS_BUILD_DATA_FILE="${CACHE_DIR:?}/build-data/go-prev.json" diff --git a/lib/common.sh b/lib/common.sh index 90ab44fd..44649b31 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -1,12 +1,11 @@ #!/usr/bin/env bash # shellcheck disable=SC2034 # Variables like DataJSON, GO_LINKER_VALUE, TOOL are used by the caller (bin/compile) - -# ----------------------------------------- -# load environment variables -# allow apps to specify cgo flags. The literal text '${build_dir}' is substituted for the build directory - # shellcheck disable=SC2154 # BUILDPACK_DIR, build, SOURCE_VERSION, DefaultGoVersion are set by the caller (bin/compile) + +# This is technically redundant, since all consumers of this lib will have enabled these, +# however, it helps Shellcheck realise the options under which these functions will run. +set -euo pipefail DataJSON="${BUILDPACK_DIR}/data.json" FilesJSON="${BUILDPACK_DIR}/files.json" goMOD="${build}/go.mod" @@ -21,7 +20,7 @@ CURL="curl --no-progress-meter --location --fail --max-time 30 --retry-max-time TOOL="" # Default to $SOURCE_VERSION environment variable: https://devcenter.heroku.com/articles/buildpack-api#bin-compile -GO_LINKER_VALUE="${SOURCE_VERSION}" +GO_LINKER_VALUE="${SOURCE_VERSION:-}" snapshotBinBefore() { if [[ ! -d "${build}/bin" ]]; then @@ -274,7 +273,7 @@ supportsGoModules() { determineTool() { # Check GOVERSION first - it overrides all tool-specific configurations - if [[ -n "${GOVERSION}" ]]; then + if [[ -n "${GOVERSION:-}" ]]; then ver="${GOVERSION}" go_version_origin="GOVERSION" build_data::set_string "go_version_origin" "${go_version_origin}" @@ -288,7 +287,7 @@ determineTool() { output::step "Detected go modules via go.mod" # Determine Go version from go.mod if not already set by GOVERSION - if [[ -z "${ver}" ]]; then + if [[ -z "${ver:-}" ]]; then ver=$(awk '{ if ($1 == "//" && $2 == "+heroku" && $3 == "goVersion" ) { print $4; exit } }' "${goMOD}") if [[ -n "${ver}" ]]; then go_version_origin="go.mod (heroku comment)" diff --git a/lib/output.sh b/lib/output.sh index d0a797b2..1f6dda76 100644 --- a/lib/output.sh +++ b/lib/output.sh @@ -2,7 +2,7 @@ # This is technically redundant, since all consumers of this lib will have enabled these, # however, it helps Shellcheck realise the options under which these functions will run. -set -eo pipefail +set -euo pipefail ANSI_BLUE=$'\e[1;34m' ANSI_RED=$'\e[1;31m' From 6a5dedcd16c6c26444de95eb3f6dd3ae62586d59 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:20:29 -0400 Subject: [PATCH 09/23] Convert `sbin/add-version` to bash --- .editorconfig | 1 + sbin/add-version | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index 52615237..bca43d8e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -27,6 +27,7 @@ switch_case_indent = true [**/sbin/**] binary_next_line = true indent_style = tab +shell_variant = bash switch_case_indent = true # Vendored third-party script that must not be modified. diff --git a/sbin/add-version b/sbin/add-version index bb25f5a3..e2435ecd 100755 --- a/sbin/add-version +++ b/sbin/add-version @@ -1,11 +1,11 @@ -#!/bin/sh +#!/usr/bin/env bash -set -e +set -euo pipefail BASEURL="https://dl.google.com/go" V="$1" -if [ -z "${V}" ]; then +if [[ -z "${V}" ]]; then echo "usage: $0 " >&2 exit 1 fi From 5c6c008f82b40c710b9cd907fefe6523e26d44d2 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:31:08 -0400 Subject: [PATCH 10/23] Source `output.sh` explicitly in callers instead of in `common.sh` --- bin/compile | 1 + bin/test | 1 + lib/common.sh | 2 -- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/compile b/bin/compile index ca5ca99a..c491a40f 100755 --- a/bin/compile +++ b/bin/compile @@ -13,6 +13,7 @@ mkdir -p "${cache}" env_dir="${3}" BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) +source "${BUILDPACK_DIR}/lib/output.sh" source "${BUILDPACK_DIR}/lib/common.sh" # Set CACHE_DIR (used by build_data) diff --git a/bin/test b/bin/test index ac863e84..e48cb1f6 100755 --- a/bin/test +++ b/bin/test @@ -8,6 +8,7 @@ build="$(cd "${1}/" && pwd)" env_dir="$(cd "${2}/" && pwd)" BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) +source "${BUILDPACK_DIR}/lib/output.sh" source "${BUILDPACK_DIR}/lib/common.sh" # loadEnvDir "${env_dir}" diff --git a/lib/common.sh b/lib/common.sh index 44649b31..86765c29 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -10,8 +10,6 @@ DataJSON="${BUILDPACK_DIR}/data.json" FilesJSON="${BUILDPACK_DIR}/files.json" goMOD="${build}/go.mod" -# shellcheck disable=SC1091 -source "$(dirname "${BASH_SOURCE[0]}")/output.sh" # We use --max-time/--retry-max-time for improved UX and metrics for hanging downloads compared to # seconds relying on the build system timeout. Go tarballs are up to ~70 MB and typically download in a few # seconds on Heroku, so we set relatively low timeouts to reduce delays before retries. From 80731bdd5f4386ec76072ac783b831dfe1272da5 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:41:59 -0400 Subject: [PATCH 11/23] Remove unnecessary `mkdir -p` for build and cache dirs --- bin/compile | 1 - bin/test | 1 - bin/test-compile | 1 - 3 files changed, 3 deletions(-) diff --git a/bin/compile b/bin/compile index c491a40f..31883349 100755 --- a/bin/compile +++ b/bin/compile @@ -5,7 +5,6 @@ set -euo pipefail [[ -n "${BUILDPACK_XTRACE:-}" ]] && set -o xtrace -mkdir -p "$1" "$2" build=$(cd "$1/" && pwd) cache_root=$(cd "$2/" && pwd) cache="${cache_root}/.heroku/go" diff --git a/bin/test b/bin/test index e48cb1f6..29a27a7b 100755 --- a/bin/test +++ b/bin/test @@ -3,7 +3,6 @@ set -euo pipefail -mkdir -p "${1}" "${2}" build="$(cd "${1}/" && pwd)" env_dir="$(cd "${2}/" && pwd)" BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) diff --git a/bin/test-compile b/bin/test-compile index 5d8923ca..b82567fa 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -3,7 +3,6 @@ set -euo pipefail -mkdir -p "$1" "$2" build=$(cd "$1/" && pwd) cache=$(cd "$2/" && pwd) env_dir="${3}" From e917c320d7a4052b7f7853e7bfb447d571ab16c9 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:46:12 -0400 Subject: [PATCH 12/23] Assign buildpack args directly No need to resolve via `cd && pwd` --- bin/compile | 4 ++-- bin/detect | 2 +- bin/test | 4 ++-- bin/test-compile | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bin/compile b/bin/compile index 31883349..f9364778 100755 --- a/bin/compile +++ b/bin/compile @@ -5,8 +5,8 @@ set -euo pipefail [[ -n "${BUILDPACK_XTRACE:-}" ]] && set -o xtrace -build=$(cd "$1/" && pwd) -cache_root=$(cd "$2/" && pwd) +build="${1}" +cache_root="${2}" cache="${cache_root}/.heroku/go" mkdir -p "${cache}" env_dir="${3}" diff --git a/bin/detect b/bin/detect index 7e12216f..5678718e 100755 --- a/bin/detect +++ b/bin/detect @@ -2,7 +2,7 @@ # bin/detect set -euo pipefail -build=$(cd "$1/" && pwd) +build="${1}" BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) source "${BUILDPACK_DIR}/lib/output.sh" diff --git a/bin/test b/bin/test index 29a27a7b..30759dd4 100755 --- a/bin/test +++ b/bin/test @@ -3,8 +3,8 @@ set -euo pipefail -build="$(cd "${1}/" && pwd)" -env_dir="$(cd "${2}/" && pwd)" +build="${1}" +env_dir="${2}" BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) source "${BUILDPACK_DIR}/lib/output.sh" diff --git a/bin/test-compile b/bin/test-compile index b82567fa..325afacb 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -3,8 +3,8 @@ set -euo pipefail -build=$(cd "$1/" && pwd) -cache=$(cd "$2/" && pwd) +build="${1}" +cache="${2}" env_dir="${3}" BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) From 8cb64b5957c6a704316b43aecb1ad3521e8cee4a Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:49:52 -0400 Subject: [PATCH 13/23] Add consistent comment for `BUILDPACK_DIR` across all scripts --- bin/compile | 2 ++ bin/detect | 2 ++ bin/test | 2 ++ bin/test-compile | 2 ++ 4 files changed, 8 insertions(+) diff --git a/bin/compile b/bin/compile index f9364778..cabeafc1 100755 --- a/bin/compile +++ b/bin/compile @@ -10,6 +10,8 @@ cache_root="${2}" cache="${cache_root}/.heroku/go" mkdir -p "${cache}" env_dir="${3}" + +# The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) source "${BUILDPACK_DIR}/lib/output.sh" diff --git a/bin/detect b/bin/detect index 5678718e..4bd1ce77 100755 --- a/bin/detect +++ b/bin/detect @@ -3,6 +3,8 @@ set -euo pipefail build="${1}" + +# The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) source "${BUILDPACK_DIR}/lib/output.sh" diff --git a/bin/test b/bin/test index 30759dd4..29cb22cf 100755 --- a/bin/test +++ b/bin/test @@ -5,6 +5,8 @@ set -euo pipefail build="${1}" env_dir="${2}" + +# The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) source "${BUILDPACK_DIR}/lib/output.sh" diff --git a/bin/test-compile b/bin/test-compile index 325afacb..f28ed9d3 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -6,6 +6,8 @@ set -euo pipefail build="${1}" cache="${2}" env_dir="${3}" + +# The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) echo "true" >"${env_dir}/GO_INSTALL_TOOLS_IN_IMAGE" From 63dbe7996eaa73e09f0414c6790792e2c55d76be Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:54:30 -0400 Subject: [PATCH 14/23] Rename `build` variable to `BUILD_DIR` --- bin/compile | 36 ++++++++++++++++++------------------ bin/detect | 16 ++++++++-------- bin/test | 26 +++++++++++++------------- bin/test-compile | 10 +++++----- lib/common.sh | 22 +++++++++++----------- 5 files changed, 55 insertions(+), 55 deletions(-) diff --git a/bin/compile b/bin/compile index cabeafc1..4f94d5d8 100755 --- a/bin/compile +++ b/bin/compile @@ -5,7 +5,7 @@ set -euo pipefail [[ -n "${BUILDPACK_XTRACE:-}" ]] && set -o xtrace -build="${1}" +BUILD_DIR="${1}" cache_root="${2}" cache="${cache_root}/.heroku/go" mkdir -p "${cache}" @@ -237,7 +237,7 @@ if [[ -n "${GO_LINKER_SYMBOL:-}" ]] && [[ -n "${GO_LINKER_VALUE:-}" ]]; then FLAGS+=(-ldflags "-X ${GO_LINKER_SYMBOL}=${GO_LINKER_VALUE}") fi -if [[ -e "${build}/bin" ]] && [[ ! -d "${build}/bin" ]]; then +if [[ -e "${BUILD_DIR}/bin" ]] && [[ ! -d "${BUILD_DIR}/bin" ]]; then output::error <<-EOF Error: File bin exists and is not a directory. EOF @@ -250,7 +250,7 @@ go_install_start_time=$(build_data::current_unix_realtime) ensureGo "${ver}" build_data::set_duration "go_install_duration" "${go_install_start_time}" -mkdir -p "${build}/bin" +mkdir -p "${BUILD_DIR}/bin" export GOPATH @@ -263,7 +263,7 @@ mainPackagesInModule() { } setupProcfile() { - if [[ -f "${build}/Procfile" ]]; then + if [[ -f "${BUILD_DIR}/Procfile" ]]; then return fi local pkgs @@ -285,11 +285,11 @@ setupProcfile() { if [[ ${#pf[@]} -eq 1 ]]; then local line line="web: bin/$(echo "${pf[0]}" | cut -d / -f 2)" - echo "${line}" >"${build}/Procfile" + echo "${line}" >"${BUILD_DIR}/Procfile" echo "${line}" | output::indent elif [[ ${#pf[@]} -gt 1 ]]; then for pfl in "${pf[@]}"; do - echo "${pfl}" >>"${build}/Procfile" + echo "${pfl}" >>"${BUILD_DIR}/Procfile" echo "${pfl}" | output::indent done fi @@ -302,11 +302,11 @@ setupProcfile() { dependencies_install_start_time=$(build_data::current_unix_realtime) -cd "${build}" || exit +cd "${BUILD_DIR}" || exit -export GOBIN="${build}/bin" +export GOBIN="${BUILD_DIR}/bin" if [[ "${GO_SETUP_GOPATH_FOR_MODULE_CACHE:-}" = "true" ]]; then - export GOPATH="${build}/.heroku/go-path" + export GOPATH="${BUILD_DIR}/.heroku/go-path" mkdir -p "${GOPATH}" # ensure that it's created else export GOPATH="${cache}/go-path" @@ -316,7 +316,7 @@ fi # and only do this if it's <1.14. The 1.14 release and beyond handles this automatically # when the desired language version is 1.14+ and vendoring should be used. # https://golang.org/doc/go1.14#vendor -if [[ -d "${build}/vendor" ]] && [[ -s "${build}/go.sum" ]]; then +if [[ -d "${BUILD_DIR}/vendor" ]] && [[ -s "${BUILD_DIR}/go.sum" ]]; then FLAGS+=(-mod=vendor) fi @@ -370,22 +370,22 @@ done setupProcfile -cd "${build}" || exit -mkdir -p "${build}/.profile.d" +cd "${BUILD_DIR}" || exit +mkdir -p "${BUILD_DIR}/.profile.d" # shellcheck disable=SC2016 -echo 'PATH=$PATH:$HOME/bin' >"${build}/.profile.d/go.sh" +echo 'PATH=$PATH:$HOME/bin' >"${BUILD_DIR}/.profile.d/go.sh" if [[ "${GO_INSTALL_TOOLS_IN_IMAGE:-}" = "true" ]]; then output::step "Copying go tool chain to \$GOROOT=\$HOME/.heroku/go" - mkdir -p "${build}/.heroku/go" - cp -a "${GOROOT}/"* "${build}/.heroku/go" + mkdir -p "${BUILD_DIR}/.heroku/go" + cp -a "${GOROOT}/"* "${BUILD_DIR}/.heroku/go" # shellcheck disable=SC2016 - echo 'export GOROOT=$HOME/.heroku/go' >"${build}/.profile.d/goroot.sh" + echo 'export GOROOT=$HOME/.heroku/go' >"${BUILD_DIR}/.profile.d/goroot.sh" # shellcheck disable=SC2016 - echo 'PATH=$PATH:$GOROOT/bin' >>"${build}/.profile.d/goroot.sh" + echo 'PATH=$PATH:$GOROOT/bin' >>"${BUILD_DIR}/.profile.d/goroot.sh" fi -t="${build}/.heroku/go" +t="${BUILD_DIR}/.heroku/go" mkdir -p "${t}" t="${t}/.meta" # shellcheck disable=SC2154 diff --git a/bin/detect b/bin/detect index 4bd1ce77..afee3110 100755 --- a/bin/detect +++ b/bin/detect @@ -2,7 +2,7 @@ # bin/detect set -euo pipefail -build="${1}" +BUILD_DIR="${1}" # The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) @@ -13,22 +13,22 @@ source "${BUILDPACK_DIR}/lib/output.sh" # but we still want to pass detection so that bin/compile can provide a helpful # migration error message rather than the generic "can't detect language" output). # shellcheck disable=SC2235,SC2312 -if test -f "${build}/go.mod" \ +if test -f "${BUILD_DIR}/go.mod" \ || #go modules - test -f "${build}/Gopkg.lock" \ + test -f "${BUILD_DIR}/Gopkg.lock" \ || #dep - test -f "${build}/Godeps/Godeps.json" \ + test -f "${BUILD_DIR}/Godeps/Godeps.json" \ || # godeps - test -f "${build}/vendor/vendor.json" \ + test -f "${BUILD_DIR}/vendor/vendor.json" \ || # govendor - test -f "${build}/glide.yaml" \ + test -f "${BUILD_DIR}/glide.yaml" \ || # glide - (test -d "${build}/src" && test -n "$(find "${build}/src" -mindepth 2 -type f -name '*.go' | sed 1q)"); then # gb + (test -d "${BUILD_DIR}/src" && test -n "$(find "${BUILD_DIR}/src" -mindepth 2 -type f -name '*.go' | sed 1q)"); then # gb echo Go else output::error <<-EOF @@ -40,7 +40,7 @@ else Currently the root directory of your app contains: - $(ls -1A --indicator-style=slash "${build}" || true) + $(ls -1A --indicator-style=slash "${BUILD_DIR}" || true) If your app already has a go.mod file, check that it: diff --git a/bin/test b/bin/test index 29cb22cf..14f511c3 100755 --- a/bin/test +++ b/bin/test @@ -3,7 +3,7 @@ set -euo pipefail -build="${1}" +BUILD_DIR="${1}" env_dir="${2}" # The absolute path to the root of the buildpack. @@ -19,37 +19,37 @@ if [[ -n "${env_dir}" ]]; then for f in ${env_dir}/*; do key=$(basename "${f}") if [[ -f "${f}" ]]; then - export "${key}=$(sed -e "s:\${build_dir}:${build}:" "${f}")" + export "${key}=$(sed -e "s:\${build_dir}:${BUILD_DIR}:" "${f}")" fi done fi -if [[ -f "${build}/.heroku/go/.meta" ]]; then +if [[ -f "${BUILD_DIR}/.heroku/go/.meta" ]]; then # shellcheck disable=SC1091 - source "${build}/.heroku/go/.meta" + source "${BUILD_DIR}/.heroku/go/.meta" fi -export GOROOT="${build}/.heroku/go" +export GOROOT="${BUILD_DIR}/.heroku/go" # Use the GOPATH that was setup during compile, if it exists, so that # any modules downloaded during compile are re-used. -if [[ -d "${build}/.heroku/go-path" ]]; then - export GOPATH="${build}/.heroku/go-path" +if [[ -d "${BUILD_DIR}/.heroku/go-path" ]]; then + export GOPATH="${BUILD_DIR}/.heroku/go-path" else - # Otherwise use a GOPATH that is outside of ${build} + # Otherwise use a GOPATH that is outside of ${BUILD_DIR} GOPATH="$(mktemp -d)" export GOPATH fi -PATH="${build}/bin:${GOROOT}/bin:${PATH}" +PATH="${BUILD_DIR}/bin:${GOROOT}/bin:${PATH}" # Install our vendored copy of github.com/apg/patter -(cd "${BUILDPACK_DIR}/lib/src/github.com/apg/patter" && GOPATH="${BUILDPACK_DIR}/lib" GOBIN="${build}/bin" go install .) +(cd "${BUILDPACK_DIR}/lib/src/github.com/apg/patter" && GOPATH="${BUILDPACK_DIR}/lib" GOBIN="${BUILD_DIR}/bin" go install .) output=$(mktemp) -cd "${build}" -if [[ -f "${build}/.golangci.yml" ]] || [[ -f "${build}/.golangci.toml" ]] || [[ -f "${build}/.golangci.json" ]]; then +cd "${BUILD_DIR}" +if [[ -f "${BUILD_DIR}/.golangci.yml" ]] || [[ -f "${BUILD_DIR}/.golangci.toml" ]] || [[ -f "${BUILD_DIR}/.golangci.json" ]]; then output::step "Running: golangci-lint -v --build-tags heroku run" - "${build}"/.heroku/golangci/bin/golangci-lint -v --build-tags heroku run 2>&1 | output::indent + "${BUILD_DIR}"/.heroku/golangci/bin/golangci-lint -v --build-tags heroku run 2>&1 | output::indent fi _tflags=("-race" "-v") if [[ -s "./go.sum" ]] && [[ -d "./vendor" ]]; then diff --git a/bin/test-compile b/bin/test-compile index f28ed9d3..71c540c3 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -3,7 +3,7 @@ set -euo pipefail -build="${1}" +BUILD_DIR="${1}" cache="${2}" env_dir="${3}" @@ -13,11 +13,11 @@ BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) echo "true" >"${env_dir}/GO_INSTALL_TOOLS_IN_IMAGE" echo "true" >"${env_dir}/GO_SETUP_GOPATH_FOR_MODULE_CACHE" -source "${BUILDPACK_DIR}/bin/compile" "${build}" "${cache}" "${env_dir}" +source "${BUILDPACK_DIR}/bin/compile" "${BUILD_DIR}" "${cache}" "${env_dir}" -if [[ -f "${build}/.golangci.yml" ]] || [[ -f "${build}/.golangci.toml" ]] || [[ -f "${build}/.golangci.json" ]]; then +if [[ -f "${BUILD_DIR}/.golangci.yml" ]] || [[ -f "${BUILD_DIR}/.golangci.toml" ]] || [[ -f "${BUILD_DIR}/.golangci.json" ]]; then output::step "/.golangci.{yml,toml,json} detected" tmp="$(mktemp -d)" - mkdir -p "${build}/.heroku/golangci/bin" - ensureFile "golangci-lint-1.20.0-linux-amd64.tar.gz" "${tmp}" "tar -C ${build}/.heroku/golangci/bin --strip-components=1 -zxf" + mkdir -p "${BUILD_DIR}/.heroku/golangci/bin" + ensureFile "golangci-lint-1.20.0-linux-amd64.tar.gz" "${tmp}" "tar -C ${BUILD_DIR}/.heroku/golangci/bin --strip-components=1 -zxf" fi diff --git a/lib/common.sh b/lib/common.sh index 86765c29..62b59b58 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash # shellcheck disable=SC2034 # Variables like DataJSON, GO_LINKER_VALUE, TOOL are used by the caller (bin/compile) -# shellcheck disable=SC2154 # BUILDPACK_DIR, build, SOURCE_VERSION, DefaultGoVersion are set by the caller (bin/compile) +# shellcheck disable=SC2154 # BUILDPACK_DIR, BUILD_DIR, SOURCE_VERSION, DefaultGoVersion are set by the caller (bin/compile) # This is technically redundant, since all consumers of this lib will have enabled these, # however, it helps Shellcheck realise the options under which these functions will run. set -euo pipefail DataJSON="${BUILDPACK_DIR}/data.json" FilesJSON="${BUILDPACK_DIR}/files.json" -goMOD="${build}/go.mod" +goMOD="${BUILD_DIR}/go.mod" # We use --max-time/--retry-max-time for improved UX and metrics for hanging downloads compared to # seconds relying on the build system timeout. Go tarballs are up to ~70 MB and typically download in a few @@ -21,13 +21,13 @@ TOOL="" GO_LINKER_VALUE="${SOURCE_VERSION:-}" snapshotBinBefore() { - if [[ ! -d "${build}/bin" ]]; then + if [[ ! -d "${BUILD_DIR}/bin" ]]; then return 0 fi _oifs=${IFS} IFS=$'\n' _binBefore=() - for f in "${build}"/bin/*; do + for f in "${BUILD_DIR}"/bin/*; do if [[ -f "${f}" ]]; then # shellcheck disable=SC2207 _binBefore+=("$(shasum "${f}")") @@ -40,7 +40,7 @@ binDiff() { _oifs=${IFS} IFS=$'\n' local binAfter=() - for f in "${build}"/bin/*; do + for f in "${BUILD_DIR}"/bin/*; do if [[ -f "${f}" ]]; then # shellcheck disable=SC2207 binAfter+=("$(shasum "${f}")") @@ -182,7 +182,7 @@ loadEnvDir() { env_dir=$(cd "${env_dir}/" && pwd) for key in "${envFlags[@]}"; do if [[ -f "${env_dir}/${key}" ]]; then - export "${key}=$(sed -e "s:\${build_dir}:${build}:" <"${env_dir}/${key}")" + export "${key}=$(sed -e "s:\${build_dir}:${BUILD_DIR}:" <"${env_dir}/${key}")" fi done fi @@ -335,15 +335,15 @@ determineTool() { else local legacy_tool="" # shellcheck disable=SC2312 - if [[ -f "${build}/Gopkg.lock" ]]; then + if [[ -f "${BUILD_DIR}/Gopkg.lock" ]]; then legacy_tool="dep" - elif [[ -f "${build}/Godeps/Godeps.json" ]]; then + elif [[ -f "${BUILD_DIR}/Godeps/Godeps.json" ]]; then legacy_tool="godep" - elif [[ -f "${build}/vendor/vendor.json" ]]; then + elif [[ -f "${BUILD_DIR}/vendor/vendor.json" ]]; then legacy_tool="govendor" - elif [[ -f "${build}/glide.yaml" ]]; then + elif [[ -f "${BUILD_DIR}/glide.yaml" ]]; then legacy_tool="glide" - elif [[ -d "${build}/src" ]] && [[ -n "$(find "${build}/src" -mindepth 2 -type f -name '*.go' | sed 1q)" ]]; then + elif [[ -d "${BUILD_DIR}/src" ]] && [[ -n "$(find "${BUILD_DIR}/src" -mindepth 2 -type f -name '*.go' | sed 1q)" ]]; then legacy_tool="gb" fi From 0fc388253e00d054e6603094a2332861d9ed095a Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sat, 14 Mar 2026 23:58:01 -0400 Subject: [PATCH 15/23] Rename `env_dir` variable to `ENV_DIR` --- bin/compile | 6 +++--- bin/test | 10 +++++----- bin/test-compile | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bin/compile b/bin/compile index 4f94d5d8..ae78648c 100755 --- a/bin/compile +++ b/bin/compile @@ -9,7 +9,7 @@ BUILD_DIR="${1}" cache_root="${2}" cache="${cache_root}/.heroku/go" mkdir -p "${cache}" -env_dir="${3}" +ENV_DIR="${3}" # The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) @@ -207,9 +207,9 @@ installPkgs() { go install -v "${FLAGS[@]}" ${pkgs} 2>&1 | output::indent } -loadEnvDir "${env_dir}" +loadEnvDir "${ENV_DIR}" -setGitCredHelper "${env_dir}" +setGitCredHelper "${ENV_DIR}" trap clearGitCredHelper INT TERM EXIT FLAGS=(-tags heroku) diff --git a/bin/test b/bin/test index 14f511c3..e69a1a75 100755 --- a/bin/test +++ b/bin/test @@ -4,7 +4,7 @@ set -euo pipefail BUILD_DIR="${1}" -env_dir="${2}" +ENV_DIR="${2}" # The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) @@ -12,11 +12,11 @@ BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) source "${BUILDPACK_DIR}/lib/output.sh" source "${BUILDPACK_DIR}/lib/common.sh" -# loadEnvDir "${env_dir}" -# For now, load all of env_dir -if [[ -n "${env_dir}" ]]; then +# loadEnvDir "${ENV_DIR}" +# For now, load all of ENV_DIR +if [[ -n "${ENV_DIR}" ]]; then # shellcheck disable=SC2231 - for f in ${env_dir}/*; do + for f in ${ENV_DIR}/*; do key=$(basename "${f}") if [[ -f "${f}" ]]; then export "${key}=$(sed -e "s:\${build_dir}:${BUILD_DIR}:" "${f}")" diff --git a/bin/test-compile b/bin/test-compile index 71c540c3..15492d38 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -5,15 +5,15 @@ set -euo pipefail BUILD_DIR="${1}" cache="${2}" -env_dir="${3}" +ENV_DIR="${3}" # The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) -echo "true" >"${env_dir}/GO_INSTALL_TOOLS_IN_IMAGE" -echo "true" >"${env_dir}/GO_SETUP_GOPATH_FOR_MODULE_CACHE" +echo "true" >"${ENV_DIR}/GO_INSTALL_TOOLS_IN_IMAGE" +echo "true" >"${ENV_DIR}/GO_SETUP_GOPATH_FOR_MODULE_CACHE" -source "${BUILDPACK_DIR}/bin/compile" "${BUILD_DIR}" "${cache}" "${env_dir}" +source "${BUILDPACK_DIR}/bin/compile" "${BUILD_DIR}" "${cache}" "${ENV_DIR}" if [[ -f "${BUILD_DIR}/.golangci.yml" ]] || [[ -f "${BUILD_DIR}/.golangci.toml" ]] || [[ -f "${BUILD_DIR}/.golangci.json" ]]; then output::step "/.golangci.{yml,toml,json} detected" From c4770c35656e44576dce511239d8260ca16cca55 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sun, 15 Mar 2026 00:12:27 -0400 Subject: [PATCH 16/23] Rename `cache`/`cache_root` variable to `CACHE_DIR` In `bin/compile`, also rename Go cache variable to `GO_CACHE_DIR` --- bin/compile | 58 ++++++++++++++++++++++-------------------------- bin/test-compile | 4 ++-- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/bin/compile b/bin/compile index ae78648c..0ea301d4 100755 --- a/bin/compile +++ b/bin/compile @@ -6,9 +6,9 @@ set -euo pipefail [[ -n "${BUILDPACK_XTRACE:-}" ]] && set -o xtrace BUILD_DIR="${1}" -cache_root="${2}" -cache="${cache_root}/.heroku/go" -mkdir -p "${cache}" +CACHE_DIR="${2}" +GO_CACHE_DIR="${CACHE_DIR}/.heroku/go" +mkdir -p "${GO_CACHE_DIR}" ENV_DIR="${3}" # The absolute path to the root of the buildpack. @@ -16,10 +16,6 @@ BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) source "${BUILDPACK_DIR}/lib/output.sh" source "${BUILDPACK_DIR}/lib/common.sh" - -# Set CACHE_DIR (used by build_data) -# shellcheck disable=SC2034 -CACHE_DIR="${cache_root}" source "${BUILDPACK_DIR}/lib/build_data.sh" compile_start_time=$(build_data::current_unix_realtime) @@ -33,23 +29,23 @@ snapshotBinBefore # Clean up old cache files if migrating from old cache structure. # We detect this legacy structure by looking for 'go-path' in the root. -if [[ -d "${cache_root}/go-path" ]] && [[ ! -d "${cache}/go-path" ]]; then +if [[ -d "${CACHE_DIR}/go-path" ]] && [[ ! -d "${GO_CACHE_DIR}/go-path" ]]; then output::step "Clearing old build cache artifacts" # Remove artifacts that were previously stored in the cache root - rm -rf "${cache_root}"/go[0-9]* \ - "${cache_root}"/devel-* \ - "${cache_root}/go-path" \ - "${cache_root}/go-build-cache" \ - "${cache_root}/glide" \ - "${cache_root}/gb" \ - "${cache_root}/dep" \ - "${cache_root}/godep" \ - "${cache_root}/govendor" \ - "${cache_root}/.tq" \ - "${cache_root}/.jq" \ - "${cache_root}/github.com/golang-migrate" \ - "${cache_root}/github.com/mattes" 2>/dev/null || true + rm -rf "${CACHE_DIR}"/go[0-9]* \ + "${CACHE_DIR}"/devel-* \ + "${CACHE_DIR}/go-path" \ + "${CACHE_DIR}/go-build-cache" \ + "${CACHE_DIR}/glide" \ + "${CACHE_DIR}/gb" \ + "${CACHE_DIR}/dep" \ + "${CACHE_DIR}/godep" \ + "${CACHE_DIR}/govendor" \ + "${CACHE_DIR}/.tq" \ + "${CACHE_DIR}/.jq" \ + "${CACHE_DIR}/github.com/golang-migrate" \ + "${CACHE_DIR}/github.com/mattes" 2>/dev/null || true fi # shellcheck disable=SC2154 @@ -102,7 +98,7 @@ reportVer() { ensureGo() { local goVersion="${1}" - local goPath="${cache}/${goVersion}/go" + local goPath="${GO_CACHE_DIR}/${goVersion}/go" local goFile="" local txt="Installing ${goVersion}" if [[ -d "${goPath}" ]]; then @@ -110,17 +106,17 @@ ensureGo() { else #For a go version change, we delete everything in our namespace output::step "New Go Version, clearing old cache" - if [[ -d "${cache}/go-path" ]]; then - find "${cache}/go-path" ! -perm -u=w -print0 | xargs -r -0 chmod u+w 2>&1 + if [[ -d "${GO_CACHE_DIR}/go-path" ]]; then + find "${GO_CACHE_DIR}/go-path" ! -perm -u=w -print0 | xargs -r -0 chmod u+w 2>&1 fi - rm -rf "${cache:?}"/* + rm -rf "${GO_CACHE_DIR:?}"/* case "${goVersion}" in devel*) local bGoVersion # shellcheck disable=SC2310 bGoVersion="$(expandVer "${DefaultGoVersion}")" goFile="${bGoVersion}.linux-amd64.tar.gz" - goPath="${cache}/${bGoVersion}/go" + goPath="${GO_CACHE_DIR}/${bGoVersion}/go" txt="Installing bootstrap ${bGoVersion}" ;; go1) @@ -137,7 +133,7 @@ ensureGo() { case "${goVersion}" in devel*) - pushd "${cache}" &>/dev/null + pushd "${GO_CACHE_DIR}" &>/dev/null mkdir -p "${goVersion}" pushd "${goVersion}" &>/dev/null local sha @@ -153,7 +149,7 @@ ensureGo() { echo "devel +${sha} $(date "+%a %b %H:%M:%S %G %z")" >../VERSION # shellcheck disable=SC2312 GOROOT_BOOTSTRAP=$( - pushd "${cache}/${bGoVersion}/go" >/dev/null || exit + pushd "${GO_CACHE_DIR}/${bGoVersion}/go" >/dev/null || exit pwd popd >/dev/null || exit ) ./make.bash 2>&1 @@ -162,7 +158,7 @@ ensureGo() { rm -rf "${goPath}" popd &>/dev/null popd &>/dev/null - goPath="${cache}/${goVersion}/go" + goPath="${GO_CACHE_DIR}/${goVersion}/go" ;; *) ;; esac @@ -173,7 +169,7 @@ ensureGo() { # Export GOCACHE if Go >= 1.10 if go env | grep -q '^GOCACHE='; then - export GOCACHE="${cache}/go-build-cache" + export GOCACHE="${GO_CACHE_DIR}/go-build-cache" fi } @@ -309,7 +305,7 @@ if [[ "${GO_SETUP_GOPATH_FOR_MODULE_CACHE:-}" = "true" ]]; then export GOPATH="${BUILD_DIR}/.heroku/go-path" mkdir -p "${GOPATH}" # ensure that it's created else - export GOPATH="${cache}/go-path" + export GOPATH="${GO_CACHE_DIR}/go-path" fi # TODO: Check the desired language version (eg `go mod edit -json | jq -r '.Go'`) diff --git a/bin/test-compile b/bin/test-compile index 15492d38..7d3e8423 100755 --- a/bin/test-compile +++ b/bin/test-compile @@ -4,7 +4,7 @@ set -euo pipefail BUILD_DIR="${1}" -cache="${2}" +CACHE_DIR="${2}" ENV_DIR="${3}" # The absolute path to the root of the buildpack. @@ -13,7 +13,7 @@ BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) echo "true" >"${ENV_DIR}/GO_INSTALL_TOOLS_IN_IMAGE" echo "true" >"${ENV_DIR}/GO_SETUP_GOPATH_FOR_MODULE_CACHE" -source "${BUILDPACK_DIR}/bin/compile" "${BUILD_DIR}" "${cache}" "${ENV_DIR}" +source "${BUILDPACK_DIR}/bin/compile" "${BUILD_DIR}" "${CACHE_DIR}" "${ENV_DIR}" if [[ -f "${BUILD_DIR}/.golangci.yml" ]] || [[ -f "${BUILD_DIR}/.golangci.toml" ]] || [[ -f "${BUILD_DIR}/.golangci.json" ]]; then output::step "/.golangci.{yml,toml,json} detected" From 88ca6ed6d4107327068009bc3128334d235491f5 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sun, 15 Mar 2026 00:14:05 -0400 Subject: [PATCH 17/23] Setup `GO_CACHE_DIR` after setting script arg dirs --- bin/compile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/compile b/bin/compile index 0ea301d4..2f9f0561 100755 --- a/bin/compile +++ b/bin/compile @@ -7,9 +7,10 @@ set -euo pipefail BUILD_DIR="${1}" CACHE_DIR="${2}" +ENV_DIR="${3}" + GO_CACHE_DIR="${CACHE_DIR}/.heroku/go" mkdir -p "${GO_CACHE_DIR}" -ENV_DIR="${3}" # The absolute path to the root of the buildpack. BUILDPACK_DIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) From 00d631f8112e910df7dfcf8641a8542e2e28a4be Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sun, 15 Mar 2026 00:59:59 -0400 Subject: [PATCH 18/23] Clean up `bin/detect` tool detection formatting --- bin/detect | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/bin/detect b/bin/detect index afee3110..474a4f7f 100755 --- a/bin/detect +++ b/bin/detect @@ -14,21 +14,11 @@ source "${BUILDPACK_DIR}/lib/output.sh" # migration error message rather than the generic "can't detect language" output). # shellcheck disable=SC2235,SC2312 if test -f "${BUILD_DIR}/go.mod" \ - || - #go modules - test -f "${BUILD_DIR}/Gopkg.lock" \ - || - #dep - test -f "${BUILD_DIR}/Godeps/Godeps.json" \ - || - # godeps - test -f "${BUILD_DIR}/vendor/vendor.json" \ - || - # govendor - test -f "${BUILD_DIR}/glide.yaml" \ - || - # glide - (test -d "${BUILD_DIR}/src" && test -n "$(find "${BUILD_DIR}/src" -mindepth 2 -type f -name '*.go' | sed 1q)"); then # gb + || test -f "${BUILD_DIR}/Gopkg.lock" \ + || test -f "${BUILD_DIR}/Godeps/Godeps.json" \ + || test -f "${BUILD_DIR}/vendor/vendor.json" \ + || test -f "${BUILD_DIR}/glide.yaml" \ + || (test -d "${BUILD_DIR}/src" && test -n "$(find "${BUILD_DIR}/src" -mindepth 2 -type f -name '*.go' | sed 1q)"); then echo Go else output::error <<-EOF From 781e58c89c928857afcb29d564c00153e20ba5c8 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sun, 15 Mar 2026 01:17:53 -0400 Subject: [PATCH 19/23] Quote glob in `bin/test` env dir loop --- bin/test | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/test b/bin/test index e69a1a75..1735cc24 100755 --- a/bin/test +++ b/bin/test @@ -15,8 +15,7 @@ source "${BUILDPACK_DIR}/lib/common.sh" # loadEnvDir "${ENV_DIR}" # For now, load all of ENV_DIR if [[ -n "${ENV_DIR}" ]]; then - # shellcheck disable=SC2231 - for f in ${ENV_DIR}/*; do + for f in "${ENV_DIR}"/*; do key=$(basename "${f}") if [[ -f "${f}" ]]; then export "${key}=$(sed -e "s:\${build_dir}:${BUILD_DIR}:" "${f}")" From f2ad1ea40ca1bd94c0e9e76f5311c8b152388c1c Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sun, 15 Mar 2026 01:37:56 -0400 Subject: [PATCH 20/23] Remove broken no-op `pushd`/`popd` --- test/run.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/run.sh b/test/run.sh index ce91e3ec..f08c92ee 100755 --- a/test/run.sh +++ b/test/run.sh @@ -485,9 +485,6 @@ testDeprecatedToolDetected() { assertCapturedError 1 "support for dep has been removed" } -pushd "$(dirname 0)" >/dev/null || exit -popd >/dev/null || exit - # shellcheck source=test/utils.sh # shellcheck disable=SC2312 source "$(pwd)/test/utils.sh" From 8bc36cc2b2a1a7595431462d79263517eb5aec60 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sun, 15 Mar 2026 01:39:35 -0400 Subject: [PATCH 21/23] Use `BASH_SOURCE[0]` for path resolution --- test/run.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/run.sh b/test/run.sh index f08c92ee..3a7f11c6 100755 --- a/test/run.sh +++ b/test/run.sh @@ -485,9 +485,7 @@ testDeprecatedToolDetected() { assertCapturedError 1 "support for dep has been removed" } -# shellcheck source=test/utils.sh -# shellcheck disable=SC2312 -source "$(pwd)/test/utils.sh" -# shellcheck source=test/shunit2.sh -# shellcheck disable=SC1091,SC2312 -source "$(pwd)/test/shunit2.sh" +BUILDPACK_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +source "${BUILDPACK_DIR}/test/utils.sh" +# shellcheck disable=SC1091 +source "${BUILDPACK_DIR}/test/shunit2.sh" From d142bb559f7eb2e8a676d7ebd22274d687f4c79e Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sun, 15 Mar 2026 01:43:52 -0400 Subject: [PATCH 22/23] Replace shellcheck shell directive with shebang --- test/utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils.sh b/test/utils.sh index 4fb468b8..69cf3fe2 100644 --- a/test/utils.sh +++ b/test/utils.sh @@ -1,4 +1,4 @@ -# shellcheck shell=bash +#!/usr/bin/env bash ############## ## shunit2 setup/teardown functions From 310df53eb420227e4be56e6d544fb35620e2abd9 Mon Sep 17 00:00:00 2001 From: Rune Soerensen Date: Sun, 15 Mar 2026 02:07:19 -0400 Subject: [PATCH 23/23] Narrow shellcheck disables in `lib/common.sh` to per-line scope --- lib/common.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/common.sh b/lib/common.sh index 62b59b58..f4656597 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash -# shellcheck disable=SC2034 # Variables like DataJSON, GO_LINKER_VALUE, TOOL are used by the caller (bin/compile) -# shellcheck disable=SC2154 # BUILDPACK_DIR, BUILD_DIR, SOURCE_VERSION, DefaultGoVersion are set by the caller (bin/compile) - # This is technically redundant, since all consumers of this lib will have enabled these, # however, it helps Shellcheck realise the options under which these functions will run. set -euo pipefail +# shellcheck disable=SC2034,SC2154 DataJSON="${BUILDPACK_DIR}/data.json" +# shellcheck disable=SC2034,SC2154 FilesJSON="${BUILDPACK_DIR}/files.json" +# shellcheck disable=SC2154 goMOD="${BUILD_DIR}/go.mod" # We use --max-time/--retry-max-time for improved UX and metrics for hanging downloads compared to @@ -18,6 +18,7 @@ CURL="curl --no-progress-meter --location --fail --max-time 30 --retry-max-time TOOL="" # Default to $SOURCE_VERSION environment variable: https://devcenter.heroku.com/articles/buildpack-api#bin-compile +# shellcheck disable=SC2034 GO_LINKER_VALUE="${SOURCE_VERSION:-}" snapshotBinBefore() { @@ -294,6 +295,7 @@ determineTool() { if [[ -n "${ver}" ]]; then go_version_origin="go.mod" else + # shellcheck disable=SC2154 ver=${DefaultGoVersion} go_version_origin="default" fi