From 3c7690a57aee35a6f4a1d9bec2f509e75095fe1d Mon Sep 17 00:00:00 2001 From: Kamal Nasser Date: Tue, 2 Feb 2021 00:42:02 +0200 Subject: [PATCH 1/5] set HOME env to app dir at runtime for profile.d scripts --- bin/build | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bin/build b/bin/build index c29abb0..79bcaf6 100755 --- a/bin/build +++ b/bin/build @@ -23,6 +23,7 @@ source_dir="${bp_dir}/target" layers_dir="${1:?}" platform_dir="${2:?}" +app_dir="$(pwd)" # translate new stack ID to old stack ID export STACK="$CNB_STACK_ID" @@ -118,13 +119,18 @@ cache_dir="${layers_dir}/shim" mkdir -p "${cache_dir}" echo "cache = true" >"${layers_dir}/shim.toml" -"${target_dir}/bin/compile" "$(pwd)" "${cache_dir}" "${platform_dir}/env" +"${target_dir}/bin/compile" "$app_dir" "${cache_dir}" "${platform_dir}/env" # copy profile.d scripts into a layer so they will be sourced if [[ -d .profile.d ]]; then profile_dir="${layers_dir}/profile" + mkdir -p "${profile_dir}/profile.d" cp .profile.d/* "${profile_dir}/profile.d/" + + mkdir -p "${profile_dir}/env.launch" + echo -n "$app_dir" >"${profile_dir}/env.launch/HOME.override" + echo "launch = true" >"${profile_dir}.toml" fi @@ -135,4 +141,4 @@ if [[ -f "${target_dir}/export" ]]; then fi # run bin/release, read Procfile, and generate launch.toml -"${bp_dir}/bin/release" "${target_dir}" "${layers_dir}" "${platform_dir}" "$(pwd)" +"${bp_dir}/bin/release" "${target_dir}" "${layers_dir}" "${platform_dir}" "$app_dir" From 4a57113075ae6b3811899eb655fa10279fe6c9ed Mon Sep 17 00:00:00 2001 From: Kamal Nasser Date: Wed, 3 Mar 2021 01:08:22 +0200 Subject: [PATCH 2/5] restrict the scope of HOME to the shimmed buildpack --- bin/build | 20 ++++-- build_test.go | 85 +++++++++++++++++++++++ exports_test.go | 3 + test/fixtures/build/app/package.json | 1 + test/fixtures/build/buildpack/bin/compile | 19 +++++ 5 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 build_test.go create mode 100644 test/fixtures/build/app/package.json create mode 100755 test/fixtures/build/buildpack/bin/compile diff --git a/bin/build b/bin/build index 79bcaf6..53c01b7 100755 --- a/bin/build +++ b/bin/build @@ -126,10 +126,22 @@ if [[ -d .profile.d ]]; then profile_dir="${layers_dir}/profile" mkdir -p "${profile_dir}/profile.d" - cp .profile.d/* "${profile_dir}/profile.d/" - - mkdir -p "${profile_dir}/env.launch" - echo -n "$app_dir" >"${profile_dir}/env.launch/HOME.override" + for script in .profile.d/*; do + dest="${profile_dir}/profile.d/$(basename "$script")" + + # wrap each script and set $HOME to + cat <<'EOF' >"$dest" +__cnb_shim__HOME=$HOME +HOME=$(pwd) + +EOF + cat "$script" >>"$dest" + cat <<'EOF' >>"$dest" + +HOME=$__cnb_shim__HOME +unset __cnb_shim__HOME +EOF + done echo "launch = true" >"${profile_dir}.toml" fi diff --git a/build_test.go b/build_test.go new file mode 100644 index 0000000..052afc8 --- /dev/null +++ b/build_test.go @@ -0,0 +1,85 @@ +package cnbshim_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBuild(t *testing.T) { + tmp, err := ioutil.TempDir("", "build") + require.NoError(t, err) + t.Cleanup(func() { + _ = os.RemoveAll(tmp) + }) + + // set up test env + require.NoError(t, os.MkdirAll(tmp+"/layers", 0755)) + require.NoError(t, os.MkdirAll(tmp+"/platform", 0755)) + require.NoError(t, os.MkdirAll(tmp+"/buildpack/target", 0755)) + require.NoError(t, os.MkdirAll(tmp+"/buildpack/bin", 0755)) + + require.NoError(t, exec.Command("cp", "-r", "test/fixtures/build/app", tmp+"/").Run()) + require.NoError(t, exec.Command("cp", "-r", "test/fixtures/build/buildpack/.", tmp+"/buildpack/target/").Run()) + require.NoError(t, exec.Command("cp", "bin/build", tmp+"/buildpack/bin/").Run()) + // add fake exports and release binaries + require.NoError(t, ioutil.WriteFile(tmp+"/buildpack/bin/exports", nil, 0755)) + require.NoError(t, ioutil.WriteFile(tmp+"/buildpack/bin/release", nil, 0755)) + + // run bin/build + var out bytes.Buffer + cmd := exec.Command(tmp+"/buildpack/bin/build", tmp+"/layers", tmp+"/platform") + cmd.Dir = tmp + "/app" + cmd.Env = append(os.Environ(), "CNB_STACK_ID=heroku-20") + cmd.Stdout = &out + cmd.Stderr = &out + err = cmd.Run() + if _, ok := err.(*exec.ExitError); err != nil && ok { + t.Logf("bin/build output:\n%s", out.String()) + } + require.NoError(t, err) + + contains := []string{ + "got STACK=heroku-20", + fmt.Sprintf("got arg 0=%s/app", tmp), + fmt.Sprintf("got arg 1=%s/layers/shim", tmp), + fmt.Sprintf("got arg 2=%s/platform/env", tmp), + } + for _, c := range contains { + assert.Contains(t, out.String(), c) + } + + files := []string{ + "/layers/profile.toml", + "/layers/profile/env.build", + "/layers/profile/profile.d/1.sh", + } + for _, f := range files { + _, err := os.Stat(tmp + f) + assert.NoError(t, err, f) + } + + out = bytes.Buffer{} + cmd = exec.Command("bash", "-c", fmt.Sprintf(` +echo +echo "before HOME=$HOME" +source "%s" +echo "after HOME=$HOME" +`, tmp+"/layers/profile/profile.d/1.sh")) + cmd.Dir = tmp + "/app" + cmd.Env = []string{"HOME=/home/app"} + cmd.Stdout = &out + cmd.Stderr = &out + require.NoError(t, cmd.Run()) + assert.Equal(t, fmt.Sprintf(` +before HOME=/home/app +buildpack HOME=%s +after HOME=/home/app +`, tmp+"/app"), out.String()) +} diff --git a/exports_test.go b/exports_test.go index 30fff89..456397b 100644 --- a/exports_test.go +++ b/exports_test.go @@ -50,6 +50,9 @@ func TestExports(t *testing.T) { t.Run(tc.name, func(t *testing.T) { tmp, err := ioutil.TempDir("", "exports") require.NoError(t, err) + t.Cleanup(func() { + _ = os.RemoveAll(tmp) + }) cmd := exec.Command("bin/exports", fmt.Sprintf("test/fixtures/%s/export", tc.name), ".", tmp) cmd.Stdout = os.Stdout diff --git a/test/fixtures/build/app/package.json b/test/fixtures/build/app/package.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test/fixtures/build/app/package.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/build/buildpack/bin/compile b/test/fixtures/build/buildpack/bin/compile new file mode 100755 index 0000000..7fc89a9 --- /dev/null +++ b/test/fixtures/build/buildpack/bin/compile @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +i=0 +for var in "$@"; do + echo "got arg ${i}=${var}" + i=$((i + 1)) +done + +echo "got STACK=$STACK" + +mkdir .profile.d +echo 'echo "buildpack HOME=$HOME"' >.profile.d/1.sh + +BIN_DIR=$( + cd "$(dirname "$0")" + pwd +) # absolute path +touch "${BIN_DIR}/../export" From b0d3636a0de785648e579a870ef33237eb0c6bf7 Mon Sep 17 00:00:00 2001 From: Kamal Nasser Date: Wed, 10 Mar 2021 21:41:49 +0200 Subject: [PATCH 3/5] set nullglob --- bin/build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/build b/bin/build index 53c01b7..69cda93 100755 --- a/bin/build +++ b/bin/build @@ -2,6 +2,9 @@ set -euo pipefail +# do not treat empty globs as literal +shopt -s nullglob + ANSI_RED="\033[1;31m" ANSI_RESET="\033[0m" From 74c904501e36b3bad66bea747a5bdc2c5f3e8aba Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:43:19 +0100 Subject: [PATCH 4/5] Small style and quoting tweaks --- bin/build | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bin/build b/bin/build index 69cda93..0da0db4 100755 --- a/bin/build +++ b/bin/build @@ -122,7 +122,7 @@ cache_dir="${layers_dir}/shim" mkdir -p "${cache_dir}" echo "cache = true" >"${layers_dir}/shim.toml" -"${target_dir}/bin/compile" "$app_dir" "${cache_dir}" "${platform_dir}/env" +"${target_dir}/bin/compile" "${app_dir}" "${cache_dir}" "${platform_dir}/env" # copy profile.d scripts into a layer so they will be sourced if [[ -d .profile.d ]]; then @@ -130,19 +130,19 @@ if [[ -d .profile.d ]]; then mkdir -p "${profile_dir}/profile.d" for script in .profile.d/*; do - dest="${profile_dir}/profile.d/$(basename "$script")" + dest="${profile_dir}/profile.d/$(basename "${script}")" # wrap each script and set $HOME to - cat <<'EOF' >"$dest" -__cnb_shim__HOME=$HOME + cat <<'EOF' >"${dest}" +__cnb_shim__original_HOME="${HOME}" HOME=$(pwd) EOF - cat "$script" >>"$dest" - cat <<'EOF' >>"$dest" + cat "${script}" >>"${dest}" + cat <<'EOF' >>"${dest}" -HOME=$__cnb_shim__HOME -unset __cnb_shim__HOME +HOME="${__cnb_shim__original_HOME}" +unset __cnb_shim__original_HOME EOF done @@ -156,4 +156,4 @@ if [[ -f "${target_dir}/export" ]]; then fi # run bin/release, read Procfile, and generate launch.toml -"${bp_dir}/bin/release" "${target_dir}" "${layers_dir}" "${platform_dir}" "$app_dir" +"${bp_dir}/bin/release" "${target_dir}" "${layers_dir}" "${platform_dir}" "${app_dir}" From 5339669a6761368ebc0f1900c38f9075f2fc1b8f Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:47:14 +0100 Subject: [PATCH 5/5] Add ALLOW_EOL_SHIMMED_BUILDER=1 Required now that we have the EOL warning/error. --- build_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_test.go b/build_test.go index 052afc8..a9815a2 100644 --- a/build_test.go +++ b/build_test.go @@ -36,7 +36,7 @@ func TestBuild(t *testing.T) { var out bytes.Buffer cmd := exec.Command(tmp+"/buildpack/bin/build", tmp+"/layers", tmp+"/platform") cmd.Dir = tmp + "/app" - cmd.Env = append(os.Environ(), "CNB_STACK_ID=heroku-20") + cmd.Env = append(os.Environ(), "CNB_STACK_ID=heroku-20", "ALLOW_EOL_SHIMMED_BUILDER=1") cmd.Stdout = &out cmd.Stderr = &out err = cmd.Run()