diff --git a/bin/build b/bin/build index c29abb0..0da0db4 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" @@ -23,6 +26,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 +122,30 @@ 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/" + 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__original_HOME="${HOME}" +HOME=$(pwd) + +EOF + cat "${script}" >>"${dest}" + cat <<'EOF' >>"${dest}" + +HOME="${__cnb_shim__original_HOME}" +unset __cnb_shim__original_HOME +EOF + done + echo "launch = true" >"${profile_dir}.toml" fi @@ -135,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}" "$(pwd)" +"${bp_dir}/bin/release" "${target_dir}" "${layers_dir}" "${platform_dir}" "${app_dir}" diff --git a/build_test.go b/build_test.go new file mode 100644 index 0000000..a9815a2 --- /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", "ALLOW_EOL_SHIMMED_BUILDER=1") + 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"