Skip to content

Commit

Permalink
Set $HOME to $CNB_APP_DIR when running .profile.d/ scripts at l…
Browse files Browse the repository at this point in the history
…aunch (#23)

`$HOME` is now temporarily set to `$CNB_APP_DIR` whilst `.profile.d/`
are run, since many classic buildpacks assume that `$HOME` refers to
the app source directory, and use `$HOME` when setting `PATH` and
other env vars in their `.profile.d/` scripts.

This removes cnb-shim's reliance on the base run image having overridden
`HOME` to eg `/app`.
  • Loading branch information
kamaln7 authored Apr 19, 2024
1 parent 334e67d commit 85148d7
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 3 deletions.
27 changes: 24 additions & 3 deletions bin/build
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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"
Expand Down Expand Up @@ -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 <app dir>
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

Expand All @@ -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}"
85 changes: 85 additions & 0 deletions build_test.go
Original file line number Diff line number Diff line change
@@ -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())
}
3 changes: 3 additions & 0 deletions exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/build/app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
19 changes: 19 additions & 0 deletions test/fixtures/build/buildpack/bin/compile
Original file line number Diff line number Diff line change
@@ -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"

0 comments on commit 85148d7

Please sign in to comment.