diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 25f6404..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @heroku/languages diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..16b3bcd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + labels: + - "dependencies" + - "github actions" + - "skip changelog" diff --git a/.github/workflows/check_changelog.yml b/.github/workflows/check_changelog.yml index c0cc43b..2ae887c 100644 --- a/.github/workflows/check_changelog.yml +++ b/.github/workflows/check_changelog.yml @@ -13,7 +13,7 @@ jobs: if: (!contains(github.event.pull_request.labels.*.name, 'skip changelog')) steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check that CHANGELOG is touched run: | git fetch origin ${{ github.base_ref }} --depth 1 && \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 958dad1..c124fc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,12 +16,12 @@ jobs: options: --user root strategy: matrix: - stack_number: ["20", "22", "24"] + stack_number: ["22", "24", "26"] env: STACK: heroku-${{ matrix.stack_number }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Functional tests on heroku:${{ matrix.stack_number }}-build run: test/run @@ -30,6 +30,6 @@ jobs: container: image: koalaman/shellcheck-alpine:v0.9.0 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: shellcheck run: shellcheck -x bin/compile bin/detect bin/release bin/report diff --git a/.gitignore b/.gitignore index 94c93ea..4befed3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -.anvil +.DS_Store .idea diff --git a/CHANGELOG.md b/CHANGELOG.md index c91a5ea..248c027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 2025-03-11 + +- Configures `GIT_EXEC_PATH` if `git` is installed. ([#143](https://github.com/heroku/heroku-buildpack-apt/pull/143)) + ## 2024-06-24 - The cache is now correctly invalidated if the stack version of an existing cache cannot be determined. ([#133](https://github.com/heroku/heroku-buildpack-apt/pull/133)) diff --git a/Makefile b/Makefile index 5dd5e97..bd6c9bb 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,13 @@ -test: heroku-24-build heroku-22-build heroku-20-build +test: heroku-26-build heroku-24-build heroku-22-build shellcheck: @shellcheck -x bin/compile bin/detect bin/release bin/report +heroku-26-build: + @echo "Running tests in docker (heroku-26-build)..." + @docker run --user root -v $(shell pwd):/buildpack:ro --rm -it -e "STACK=heroku-26" heroku/heroku:26-build bash -c 'cp -r /buildpack /buildpack_test; cd /buildpack_test/; test/run;' + @echo "" + heroku-24-build: @echo "Running tests in docker (heroku-24-build)..." @docker run --user root -v $(shell pwd):/buildpack:ro --rm -it -e "STACK=heroku-24" heroku/heroku:24-build bash -c 'cp -r /buildpack /buildpack_test; cd /buildpack_test/; test/run;' @@ -12,8 +17,3 @@ heroku-22-build: @echo "Running tests in docker (heroku-22-build)..." @docker run -v $(shell pwd):/buildpack:ro --rm -it -e "STACK=heroku-22" heroku/heroku:22-build bash -c 'cp -r /buildpack /buildpack_test; cd /buildpack_test/; test/run;' @echo "" - -heroku-20-build: - @echo "Running tests in docker (heroku-20-build)..." - @docker run -v $(shell pwd):/buildpack:ro --rm -it -e "STACK=heroku-20" heroku/heroku:20-build bash -c 'cp -r /buildpack /buildpack_test; cd /buildpack_test/; test/run;' - @echo "" diff --git a/README.md b/README.md index b1e201b..74a6b84 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ Add support for apt-based dependencies during both compile and runtime. Added ability to also specify custom repositories through **:repo:** in `Aptfile` (see example below). +> [!Important] +> This buildpack is compatible with Heroku's [Cedar generation platform](https://devcenter.heroku.com/articles/generations#cedar) only. +> +> For equivalent functionality on Heroku's [Fir generation platform](https://devcenter.heroku.com/articles/generations#fir), +> then check out the `.deb` Packages CNB [here](https://github.com/heroku/buildpacks-deb-packages). + ## Usage This buildpack is not meant to be used on its own, and instead should be in used in combination with Heroku's [multiple buildpack support](https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app). diff --git a/bin/compile b/bin/compile index f1e632a..863c04b 100755 --- a/bin/compile +++ b/bin/compile @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# bin/compile +# bin/compile # fail fast set -eo pipefail @@ -10,7 +10,7 @@ set -eo pipefail # parse and derive params BUILD_DIR=$1 CACHE_DIR=$2 -LP_DIR=$(cd "$(dirname "$0")"; cd ..; pwd) +BP_DIR=$(cd "$(dirname "$0")"; cd ..; pwd) function error() { echo " ! $*" >&2 @@ -138,7 +138,8 @@ done < <(grep -s -e '^blackhole ' "$BUILD_DIR"/Aptfile) topic "Writing profile script" mkdir -p "$BUILD_DIR/.profile.d" -cat <"$BUILD_DIR/.profile.d/000_apt.sh" +RUNTIME_PROFILE_SCRIPT="$BUILD_DIR/.profile.d/000_apt.sh" +cat <"$RUNTIME_PROFILE_SCRIPT" export PATH="\$HOME/.apt/usr/bin:\$PATH" export LD_LIBRARY_PATH="\$HOME/.apt/usr/lib/x86_64-linux-gnu:\$HOME/.apt/usr/lib/i386-linux-gnu:\$HOME/.apt/usr/lib:\$LD_LIBRARY_PATH" export LIBRARY_PATH="\$HOME/.apt/usr/lib/x86_64-linux-gnu:\$HOME/.apt/usr/lib/i386-linux-gnu:\$HOME/.apt/usr/lib:\$LIBRARY_PATH" @@ -157,7 +158,26 @@ export CPPPATH="$INCLUDE_PATH" export PKG_CONFIG_PATH="$BUILD_DIR/.apt/usr/lib/x86_64-linux-gnu/pkgconfig:$BUILD_DIR/.apt/usr/lib/i386-linux-gnu/pkgconfig:$BUILD_DIR/.apt/usr/lib/pkgconfig:$PKG_CONFIG_PATH" #give environment to later buildpacks -export | grep -E -e ' (PATH|LD_LIBRARY_PATH|LIBRARY_PATH|INCLUDE_PATH|CPATH|CPPPATH|PKG_CONFIG_PATH)=' > "$LP_DIR/export" +topic "Exporting environment" +BUILDPACK_EXPORT_FILE="$BP_DIR/export" +export | grep -E -e ' (PATH|LD_LIBRARY_PATH|LIBRARY_PATH|INCLUDE_PATH|CPATH|CPPPATH|PKG_CONFIG_PATH)=' > "$BUILDPACK_EXPORT_FILE" topic "Rewrite package-config files" find "$BUILD_DIR/.apt" -type f -ipath '*/pkgconfig/*.pc' -print0 | xargs -0 --no-run-if-empty -n 1 sed -i -e 's!^prefix=\(.*\)$!prefix='"$BUILD_DIR"'/.apt\1!g' + +# post-installation logic for git +# If git was installed by this buildpack set `GIT_EXEC_PATH` to work around: +# https://github.com/heroku/heroku-buildpack-apt/issues/137 +if [[ -f "${BUILD_DIR}/.apt/usr/bin/git" ]]; then + GIT_CORE_DIR="${BUILD_DIR}/.apt/usr/lib/git-core" + if [[ -d "$GIT_CORE_DIR" ]]; then + topic "Setting GIT_EXEC_PATH to $GIT_CORE_DIR" + # set env var in runtime profile script + # shellcheck disable=SC2016 + echo 'export GIT_EXEC_PATH="$HOME/.apt/usr/lib/git-core"' >> "$RUNTIME_PROFILE_SCRIPT" + # export the env var to later buildpacks + echo "declare -x GIT_EXEC_PATH=\"$GIT_CORE_DIR\"" >> "$BUILDPACK_EXPORT_FILE" + else + topic "Git core directory $GIT_CORE_DIR is either missing or empty. Skipping GIT_EXEC_PATH setup." + fi +fi diff --git a/test/fixtures/custom-package-url-heroku-20/Aptfile b/test/fixtures/custom-package-url-heroku-20/Aptfile deleted file mode 100644 index 0a2a322..0000000 --- a/test/fixtures/custom-package-url-heroku-20/Aptfile +++ /dev/null @@ -1 +0,0 @@ -https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb diff --git a/test/fixtures/custom-package-url-heroku-26/Aptfile b/test/fixtures/custom-package-url-heroku-26/Aptfile new file mode 100644 index 0000000..447d4e8 --- /dev/null +++ b/test/fixtures/custom-package-url-heroku-26/Aptfile @@ -0,0 +1,2 @@ +# no resolute package for wkhtmltopdf yet, so using jammy package +https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb diff --git a/test/fixtures/custom-repository-heroku-20/Aptfile b/test/fixtures/custom-repository-heroku-20/Aptfile deleted file mode 100644 index 8136ade..0000000 --- a/test/fixtures/custom-repository-heroku-20/Aptfile +++ /dev/null @@ -1,2 +0,0 @@ -:repo:deb http://us.archive.ubuntu.com/ubuntu/ focal multiverse -fasttracker2 diff --git a/test/fixtures/custom-repository-heroku-22/Aptfile b/test/fixtures/custom-repository-heroku-22/Aptfile index e595b46..7c2d829 100644 --- a/test/fixtures/custom-repository-heroku-22/Aptfile +++ b/test/fixtures/custom-repository-heroku-22/Aptfile @@ -1,2 +1,2 @@ :repo:deb http://us.archive.ubuntu.com/ubuntu/ jammy multiverse -fasttracker2 +translate-shell diff --git a/test/fixtures/custom-repository-heroku-24/Aptfile b/test/fixtures/custom-repository-heroku-24/Aptfile index 6f33d3e..ee68bc1 100644 --- a/test/fixtures/custom-repository-heroku-24/Aptfile +++ b/test/fixtures/custom-repository-heroku-24/Aptfile @@ -1,2 +1,2 @@ :repo:deb http://us.archive.ubuntu.com/ubuntu/ noble multiverse -fasttracker2 +translate-shell diff --git a/test/fixtures/custom-repository-heroku-26/Aptfile b/test/fixtures/custom-repository-heroku-26/Aptfile new file mode 100644 index 0000000..e85bd9c --- /dev/null +++ b/test/fixtures/custom-repository-heroku-26/Aptfile @@ -0,0 +1,2 @@ +:repo:deb http://us.archive.ubuntu.com/ubuntu/ resolute multiverse +translate-shell diff --git a/test/fixtures/git/Aptfile b/test/fixtures/git/Aptfile new file mode 100644 index 0000000..5664e30 --- /dev/null +++ b/test/fixtures/git/Aptfile @@ -0,0 +1 @@ +git diff --git a/test/run b/test/run index 83b62c2..62aac7a 100755 --- a/test/run +++ b/test/run @@ -1,7 +1,11 @@ #!/usr/bin/env bash testCompilePackageNames() { - compile "package-names" + bp_dir=$(mktmpdir) + build_dir=$(mktmpdir) + cache_dir=$(mktmpdir) + compile "package-names" "${bp_dir}" "${build_dir}" "${cache_dir}" + assertCaptured "Updating APT package index" assertCaptured "Fetching .debs for xmlsec1" assertCaptured "Fetching .debs for s3cmd wget" @@ -14,30 +18,50 @@ testCompilePackageNames() { assertCaptured "Writing profile script" assertCaptured "Rewrite package-config files" assertCapturedSuccess + + assertProfileContains 'export PATH="$HOME/.apt/usr/bin:$PATH"' "$build_dir" + assertProfileContains 'export LD_LIBRARY_PATH="$HOME/.apt/usr/lib/x86_64-linux-gnu:$HOME/.apt/usr/lib/i386-linux-gnu:$HOME/.apt/usr/lib:$LD_LIBRARY_PATH"' "$build_dir" + assertProfileContains 'export LIBRARY_PATH="$HOME/.apt/usr/lib/x86_64-linux-gnu:$HOME/.apt/usr/lib/i386-linux-gnu:$HOME/.apt/usr/lib:$LIBRARY_PATH"' "$build_dir" + assertProfileContains 'export INCLUDE_PATH="$HOME/.apt/usr/include:$HOME/.apt/usr/include/x86_64-linux-gnu:$INCLUDE_PATH"' "$build_dir" + assertProfileContains 'export CPATH="$INCLUDE_PATH"' "$build_dir" + assertProfileContains 'export CPPPATH="$INCLUDE_PATH"' "$build_dir" + assertProfileContains 'export PKG_CONFIG_PATH="$HOME/.apt/usr/lib/x86_64-linux-gnu/pkgconfig:$HOME/.apt/usr/lib/i386-linux-gnu/pkgconfig:$HOME/.apt/usr/lib/pkgconfig:$PKG_CONFIG_PATH"' "$build_dir" + assertProfileNotContains 'export GIT_EXEC_PATH="$HOME/.apt/usr/lib/git-core"' "$build_dir" + + assertExportContains "declare -x PATH=\"$build_dir/.apt/usr/bin:" "$bp_dir" + assertExportContains "declare -x LD_LIBRARY_PATH=\"$build_dir/.apt/usr/lib/x86_64-linux-gnu:$build_dir/.apt/usr/lib/i386-linux-gnu:$build_dir/.apt/usr/lib:\"" "$bp_dir" + assertExportContains "declare -x LIBRARY_PATH=\"$build_dir/.apt/usr/lib/x86_64-linux-gnu:$build_dir/.apt/usr/lib/i386-linux-gnu:$build_dir/.apt/usr/lib:\"" "$bp_dir" + assertExportContains "declare -x INCLUDE_PATH=\"$build_dir/.apt/usr/include:$build_dir/.apt/usr/include/x86_64-linux-gnu:\"" "$bp_dir" + assertExportContains "declare -x CPATH=\"$build_dir/.apt/usr/include:$build_dir/.apt/usr/include/x86_64-linux-gnu:\"" "$bp_dir" + assertExportContains "declare -x CPPPATH=\"$build_dir/.apt/usr/include:$build_dir/.apt/usr/include/x86_64-linux-gnu:\"" "$bp_dir" + assertExportContains "declare -x PKG_CONFIG_PATH=\"$build_dir/.apt/usr/lib/x86_64-linux-gnu/pkgconfig:$build_dir/.apt/usr/lib/i386-linux-gnu/pkgconfig:$build_dir/.apt/usr/lib/pkgconfig:\"" "$bp_dir" + assertExportNotContains "declare -x GIT_EXEC_PATH=\"$build_dir/.apt/usr/lib/git-core\"" "$bp_dir" } testCacheInvalidation() { + bp_dir=$(mktmpdir) + build_dir=$(mktmpdir) cache_dir=$(mktmpdir) # Cold cache - compile "package-names" "${cache_dir}" + compile "package-names" "${bp_dir}" "${build_dir}" "${cache_dir}" assertCaptured "Detected Aptfile or Stack changes, flushing cache" assertCapturedSuccess # Warm cache - compile "package-names" "${cache_dir}" + compile "package-names" "${bp_dir}" "${build_dir}" "${cache_dir}" assertCaptured "Reusing cache" assertCapturedSuccess # Cache invalidated on stack change echo 'some-old-stack' > "${cache_dir}/.apt/STACK" - compile "package-names" "${cache_dir}" + compile "package-names" "${bp_dir}" "${build_dir}" "${cache_dir}" assertCaptured "Detected Aptfile or Stack changes, flushing cache" assertCapturedSuccess # Cache invalidated if stack version file missing (eg cache from old buildpack version) rm "${cache_dir}/.apt/STACK" - compile "package-names" "${cache_dir}" + compile "package-names" "${bp_dir}" "${build_dir}" "${cache_dir}" assertCaptured "Detected Aptfile or Stack changes, flushing cache" assertCapturedSuccess } @@ -52,10 +76,11 @@ testReportPackageNames() { testCompileCustomPackageUrl() { declare -A download_urls=( - [heroku-20]="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb" [heroku-22]="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb" # no noble package for wkhtmltopdf yet, so using jammy package [heroku-24]="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb" + # no resolute package for wkhtmltopdf yet, so using jammy package + [heroku-26]="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb" ) compile "custom-package-url-$STACK" assertCaptured "Updating APT package index" @@ -68,10 +93,11 @@ testCompileCustomPackageUrl() { testReportCustomPackageUrl() { declare -A download_urls=( - [heroku-20]="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb" [heroku-22]="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb" # no noble package for wkhtmltopdf yet, so using jammy package [heroku-24]="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb" + # no resolute package for wkhtmltopdf yet, so using jammy package + [heroku-26]="https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb" ) report "custom-package-url-$STACK" assertNotCaptured "^packages" @@ -82,16 +108,17 @@ testReportCustomPackageUrl() { testCompileCustomRepository() { declare -A ubuntu_release_names=( - [heroku-20]="focal" [heroku-22]="jammy" [heroku-24]="noble" + [heroku-26]="resolute" ) + arch="$(dpkg --print-architecture)" compile "custom-repository-$STACK" assertCaptured "Adding custom repositories" assertCaptured "Updating APT package index" - assertCaptured "http://us.archive.ubuntu.com/ubuntu ${ubuntu_release_names[$STACK]}/multiverse amd64 Packages" - assertCaptured "Fetching .debs for fasttracker2" - assertCaptured "Installing fasttracker2" + assertCaptured "http://us.archive.ubuntu.com/ubuntu ${ubuntu_release_names[$STACK]}/multiverse ${arch} Packages" + assertCaptured "Fetching .debs for translate-shell" + assertCaptured "Installing translate-shell" assertCaptured "Writing profile script" assertCaptured "Rewrite package-config files" assertCapturedSuccess @@ -99,12 +126,12 @@ testCompileCustomRepository() { testReportCustomRepository() { declare -A ubuntu_release_names=( - [heroku-20]="focal" [heroku-22]="jammy" [heroku-24]="noble" + [heroku-26]="resolute" ) report "custom-repository-$STACK" - assertCaptured "packages: \"fasttracker2\"" + assertCaptured "packages: \"translate-shell\"" assertNotCaptured "custom_packages" assertCaptured "custom_repositories: \"deb http://us.archive.ubuntu.com/ubuntu/ ${ubuntu_release_names[$STACK]} multiverse\"" assertCapturedSuccess @@ -155,6 +182,19 @@ testReportCustomRepositoryNoPackages() { assertCapturedSuccess } +testGitEnvConfig() { + bp_dir=$(mktmpdir) + build_dir=$(mktmpdir) + compile "git" "${bp_dir}" "${build_dir}" + + assertCaptured "Setting GIT_EXEC_PATH" + assertCapturedSuccess + + assertProfileContains 'export GIT_EXEC_PATH="$HOME/.apt/usr/lib/git-core"' "$build_dir" + + assertExportContains "declare -x GIT_EXEC_PATH=\"$build_dir/.apt/usr/lib/git-core\"" "$bp_dir" +} + pushd "$(dirname 0)" >/dev/null || exit 1 popd >/dev/null || exit 1 @@ -162,22 +202,26 @@ source "$(pwd)"/test/utils compile() { default_process_types_cleanup - bp_dir=$(mktmpdir) - compile_dir=$(mktmpdir) + fixture="$1" + bp_dir=${2:-$(mktmpdir)} + build_dir=${3:-$(mktmpdir)} + cache_dir=${4:-$(mktmpdir)} + env_dir=$(mktmpdir) cp -a "$(pwd)"/* "${bp_dir}" - cp -a "${bp_dir}"/test/fixtures/"$1"/. "${compile_dir}" - capture "${bp_dir}"/bin/compile "${compile_dir}" "${2:-$(mktmpdir)}" "$3" + cp -a "${bp_dir}"/test/fixtures/"$fixture"/. "${build_dir}" + capture "${bp_dir}"/bin/compile "${build_dir}" "${cache_dir}" "${env_dir}" } report() { default_process_types_cleanup - compile_dir=${1:-$(mktmpdir)} - cache_dir=${2:-$(mktmpdir)} - env_dir=${3:-$(mktmpdir)} - bp_dir=$(mktmpdir) + fixture="$1" + bp_dir=${2:-$(mktmpdir)} + build_dir=${3:-$(mktmpdir)} + cache_dir=${4:-$(mktmpdir)} + env_dir=$(mktmpdir) cp -a "$(pwd)"/* "${bp_dir}" - cp -a "${bp_dir}"/test/fixtures/"$1"/. "${compile_dir}" - capture "${bp_dir}"/bin/report "${compile_dir}" "${cache_dir}" "${env_dir}" + cp -a "${bp_dir}"/test/fixtures/"$fixture"/. "${build_dir}" + capture "${bp_dir}"/bin/report "${build_dir}" "${cache_dir}" "${env_dir}" } mktmpdir() { @@ -194,4 +238,28 @@ default_process_types_cleanup() { fi } +assertProfileContains() { + text="$1" + build_dir="$2" + assertFileContains "$text" "$build_dir/.profile.d/000_apt.sh" +} + +assertProfileNotContains() { + text="$1" + build_dir="$2" + assertFileNotContains "$text" "$build_dir/.profile.d/000_apt.sh" +} + +assertExportContains() { + text="$1" + bp_dir="$2" + assertFileContains "$text" "$bp_dir/export" +} + +assertExportNotContains() { + text="$1" + bp_dir="$2" + assertFileNotContains "$text" "$bp_dir/export" +} + source "$(pwd)"/test/shunit2