Skip to content
This repository was archived by the owner on Feb 13, 2023. It is now read-only.
11 changes: 7 additions & 4 deletions .profile.d/buildpack-boot.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
DNX_BIN=`find .dnx/runtimes -type d -name bin`
export PATH="$PATH:/app/mono/bin:$DNX_BIN"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/app/mono/lib:/app/libuv/lib"
export MONO_OPTIONS=--server
export PATH="$PATH:$HOME/dotnet:$HOME/.apt/usr/bin"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/.apt/usr/lib/x86_64-linux-gnu:$HOME/.apt/usr/lib/i386-linux-gnu:$HOME/.apt/usr/lib"
export LIBRARY_PATH="$LIBRARY_PATH:$HOME/.apt/usr/lib/x86_64-linux-gnu:$HOME/.apt/usr/lib/i386-linux-gnu:$HOME/.apt/usr/lib"
export INCLUDE_PATH="$INCLUDE_PATH:$HOME/.apt/usr/include"
export CPATH="$INCLUDE_PATH"
export CPPPATH="$INCLUDE_PATH"
export apt_CONFIG_PATH="$apt_CONFIG_PATH:$HOME/.apt/usr/lib/x86_64-linux-gnu/aptconfig:$HOME/.apt/usr/lib/i386-linux-gnu/aptconfig:$HOME/.apt/usr/lib/aptconfig"
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
**Please note:** This buildpack is an experimental project and is not officially supported.

# ASP.NET 5 Buildpack
# ASP.NET Core Buildpack

This is a [Heroku buildpack](http://devcenter.heroku.com/articles/buildpack) for building [ASP.NET 5](http://www.asp.net/vnext/overview/aspnet-vnext/aspnet-5-overview) apps using [`project.json` files](https://github.com/aspnet/Home/wiki/Project.json-file) and the [kpm package manager](https://github.com/aspnet/Home/wiki/Package-Manager).

[Mono](http://www.mono-project.com/) is bundled for runtime execution.
This is a [Heroku buildpack](http://devcenter.heroku.com/articles/buildpack) for building [ASP.NET Core](https://docs.asp.net/en/latest/conceptual-overview/aspnet.html) apps using [`project.json` files](https://github.com/aspnet/Home/wiki/Project.json-file) and the [.NET CLI](https://github.com/dotnet/cli).

## Usage

Expand All @@ -13,4 +11,8 @@ Example usage:
$ heroku create --buildpack http://github.com/heroku/dotnet-buildpack.git
$ git push heroku master

The buildpack will detect your app as ASP.NET 5 if it has `project.json`. If the source code you want to build contains multiple `project.json` files, you can use a [`.deployment`](https://github.com/projectkudu/kudu/wiki/Customizing-deployments) or set a `$PROJECT` config var to control which one is built.
The buildpack will detect your app as ASP.NET Core if it has `project.json`. If the source code you want to build contains multiple `project.json` files, you can use a [`.deployment`](https://github.com/projectkudu/kudu/wiki/Customizing-deployments) or set a `$PROJECT` config var to control which one is built.

## Attention
1. You should configure a unique compatible framework in `project.json`.
2. Your Project should use `Microsoft.Extensions.Configuration.CommandLine` package.
147 changes: 79 additions & 68 deletions bin/compile
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,18 @@ source $BUILDPACK_DIR/bin/util

export_env_dir ${ENV_DIR}

: ${MONO_VERSION:="4.0.1.44"}
: ${MONO_DOWNLOAD_LOCATION:="https://github.com/friism/mono-builder/releases/download/v${MONO_VERSION}/mono-${MONO_VERSION}.tar.gz"}
: ${LIBUV_VERSION:="1.6.0"}
: ${NODE_VERSION:="0.12.2"}
: ${DNVM_BRANCH:="dev"}
: ${DNX_VERSION:="latest"}

# REMARK: https://github.com/aspnet/dnx/pull/2620 and https://github.com/aspnet/aspnet-docker/issues/80
[ -z "$MONO_THREADS_PER_CPU" ] && export MONO_THREADS_PER_CPU=50
: ${CORE_VERSION:="latest"}
: ${CORE_SCRIPT_LOCATION:="https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0/scripts/obtain/dotnet-install.sh"}
: ${NODE_VERSION:="4.4.4"}
: ${CORE_VERSION:="latest"}
: ${CORE_CHANNEL:="preview"}

if [ -n "$BUILD_DEBUG" ]; then
DNU_FLAGS=""
echo "DNX_VERSION: ${DNX_VERSION}"
echo "DNVM_BRANCH: ${DNVM_BRANCH}"
else
DNU_FLAGS="--quiet"
fi

if [ -n "$UNSTABLE_TOOLCHAIN" ]; then
DNVM_FLAGS="-u"
CORE_CONFIGURATION="Debug"
CORE_ENVIRONMENT="--environment Development"
else
DNVM_FLAGS=""
CORE_CONFIGURATION="Release"
CORE_ENVIRONMENT=""
fi

SRC_DIR=`mktemp -d`
Expand All @@ -51,90 +41,111 @@ SRC_DIR=`mktemp -d`

mkdir -p ${CACHE_DIR}

echo "installing ${MONO_DOWNLOAD_LOCATION}"
# TODO: use s4cmd
curl ${MONO_DOWNLOAD_LOCATION} -Ls | tar xz -C ${BUILD_DIR}
# Mono expects to be running out of /app
ln -s ${BUILD_DIR}/mono /app

export PATH="/app/mono/bin:${PATH}"
export LD_LIBRARY_PATH="/app/mono/lib:${LD_LIBRARY_PATH}"

mozroots --import --sync --quiet
cp -r ~/.config ${BUILD_DIR}/.

# TODO: This test doesn't seen to actually work
# TODO: Maybe just run `npm install -g` and if anything is missing, install it after
if [[ -n $(find . -type f -iname package.json -o -iname bower.json -o -iname gruntfile.js) ]]
if [[ -n $(find ${SRC_DIR} -type f -iname package.json -o -iname bower.json) ]]
then
# Install Node and grunt-cli
echo "Installing Node"
NODE_PATH=`mktemp -d`
curl https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz -Ls \
| tar xz -C ${NODE_PATH}
export PATH="${NODE_PATH}/node-v${NODE_VERSION}-linux-x64/bin:${PATH}"
npm install -g grunt-cli bower gulp
echo "Installing bower"
if ! hash bower 2>/dev/null; then
npm install -g bower
fi
fi

# Install .NET Core
echo "Get .NET Core Version and Channel from the PROJECT"
DEPLOYMENT_FILE_LOCATION=${SRC_DIR}/.deployment
if [ -n "$(find ${SRC_DIR}/global.json)" ]; then
echo "Get version from global.json"
CORE_VERSION=$(ruby -rjson -e "j = JSON.parse(File.open('${SRC_DIR}/global.json','r:bom|utf-8').read); unless j['sdk'].nil? ; puts j['sdk']['version'];end")
if [ -z "${CORE_VERSION}" ]; then
CORE_VERSION="latest"
elif [[ "${CORE_VERSION}" != "latest" ]]; then
if [ ! "${CORE_VERSION}" > "1.0.0-preview1-002702" ]; then
CORE_CHANNEL="beta"
elif [ "${CORE_VERSION}" > "1.0.0-preview2-003121" ]; then
#TODO
CORE_DOWNLOAD_LOCATION="https://dotnetcli.blob.core.windows.net/dotnet/Sdk/rel-1.0.0/dotnet-dev-ubuntu-x64.latest.tar.gz"
fi
fi
fi

# Install DNX
# TODO: consider making this not dependent on GitHub being up
touch ~/.profile
curl -sSL https://raw.githubusercontent.com/aspnet/Home/${DNVM_BRANCH}/dnvminstall.sh \
| sh && source ~/.dnx/dnvm/dnvm.sh
if [[ "${CORE_VERSION}" == "latest" && -e "${DEPLOYMENT_FILE_LOCATION}" ]]; then
CORE_CHANNEL_TMP=$(awk -F "=" '/channel/ {print $2}' ${DEPLOYMENT_FILE_LOCATION} | tr -d ' ')
if [ ! -z "${CORE_CHANNEL_TMP}" ]; then
CORE_CHANNEL="${CORE_CHANNEL_TMP}"
fi
fi

CORE_VERSION_FIRST="$CORE_VERSION"

export DNVM_RUNTIME_LOCAL_CACHE="/app/.dnx"
export DNVM_RUNTIME_CACHE_LOCATION="${CACHE_DIR}/dnx/runtimes"
mkdir -p ${DNVM_RUNTIME_LOCAL_CACHE}
mkdir -p ${DNVM_RUNTIME_CACHE_LOCATION}
ln -s ${DNVM_RUNTIME_CACHE_LOCATION} ${DNVM_RUNTIME_LOCAL_CACHE}/runtimes
if [[ "${CORE_VERSION}" == "latest" ]]; then
CORE_VERSION_FIRST="Latest"
fi

# TODO: extract DNX version from global.json
dnvm install $DNX_VERSION $DNVM_FLAGS -a default
: ${CORE_DOWNLOAD_LOCATION:="https://dotnetcli.blob.core.windows.net/dotnet/${CORE_CHANNEL}/Binaries/${CORE_VERSION_FIRST}/dotnet-dev-ubuntu-x64.${CORE_VERSION}.tar.gz"}

# Add DNX to the build output
DNX_BUILD_LOCATION=${BUILD_DIR}/.dnx/runtimes/
mkdir -p ${DNX_BUILD_LOCATION}
cp -r ~/.dnx/runtimes/`dnvm alias default` ${DNX_BUILD_LOCATION}
echo "Installing the dependencies"
apt_install libunwind8 gettext

mkdir -p ${BUILD_DIR}/dotnet

echo "Installing .NET CORE ${CORE_VERSION} from ${CORE_DOWNLOAD_LOCATION}"
# dotnet-install.sh using `ldconfig` which search pkg in `/lib` or `/usr/lib`. So I change it to binaries.
#curl -sSL ${CORE_SCRIPT_LOCATION} | bash /dev/stdin --version ${CORE_VERSION} --install-dir ${BUILD_DIR}/dotnet
curl ${CORE_DOWNLOAD_LOCATION} -Ls | tar xz -C ${BUILD_DIR}/dotnet
ln -s ${BUILD_DIR}/dotnet /app

export PATH="/app/dotnet:${PATH}"
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"

DEPLOYMENT_FILE_LOCATION=${SRC_DIR}/.deployment
if [ -n "$PROJECT" ]; then
PROJECT_JSON_FILE=${SRC_DIR}/${PROJECT}
echo "Project file configured in PROJECT environment variable"
elif [ -e ${DEPLOYMENT_FILE_LOCATION} ]; then
PROJECT_JSON_FILE=${SRC_DIR}/$(awk -F "=" '/project/ {print $2}' ${DEPLOYMENT_FILE_LOCATION} | tr -d ' ')
echo "Project file configured in .deployment file"
else
PROJECT_JSON_FILES=$(find ${SRC_DIR}/. -maxdepth 3 -iname "project.json")
PROJECT_JSON_FILES=$(find ${SRC_DIR}/. -maxdepth 5 -iname "project.json")
printf 'Found %s project.json files\n' $(echo "$PROJECT_JSON_FILES" | wc -l | tr -d ' ')
PROJECT_JSON_FILE=$(echo "$PROJECT_JSON_FILES" | head -1)
PROJECT_JSON_FILE=$(readlink -m $PROJECT_JSON_FILE)
fi
echo "Building ${PROJECT_JSON_FILE}"

export DNU_LOCAL_CACHE="/app/.local/share/dnu"
export DNU_CACHE_LOCATION="${CACHE_DIR}/dnu/cache"
mkdir -p ${DNU_LOCAL_CACHE}
mkdir -p ${DNU_CACHE_LOCATION}
ln -s ${DNU_CACHE_LOCATION} ${DNU_LOCAL_CACHE}/cache
PROJECT_DIR="$PROJECT_JSON_FILE"
if [[ -f $PROJECT_JSON_FILE ]]; then
PROJECT_DIR=$(dirname $PROJECT_JSON_FILE)
fi

echo "Reading App Name from project.json"
PROJECT_DIR_NAME=$(basename $PROJECT_DIR)
PROJECT_NAME=$(ruby -rjson -e "j = JSON.parse(File.open('${PROJECT_DIR}/project.json','r:bom|utf-8').read); unless j['buildOptions'].nil? ; puts j['buildOptions']['outputName'];end")
echo "${PROJECT_NAME:=$PROJECT_DIR_NAME}"

echo "Restoring the packages"
dotnet restore ${SRC_DIR}

dnu restore $DNU_FLAGS ${PROJECT_JSON_FILE}
# configure some npm command during building
export PATH="${PROJECT_DIR}/node_modules/.bin:${PATH}"

dnu publish --out ${BUILD_DIR} $DNU_FLAGS --no-source --configuration Release ${PROJECT_JSON_FILE}
echo "Publishing the app"
# Publish to heroku_output
dotnet publish ${PROJECT_JSON_FILE} --output ${BUILD_DIR}/heroku_output --configuration ${CORE_CONFIGURATION}

# Clean out core-stuff that we don't need in the slug
find ${BUILD_DIR} -iname dnxcore50 -print0 | xargs -r -0 rm --
echo "Publish Successed"

mkdir -p ${BUILD_DIR}/.profile.d
cp -n ${BUILDPACK_DIR}/.profile.d/* ${BUILD_DIR}/.profile.d/

curl https://github.com/friism/libuv-builder/releases/download/v${LIBUV_VERSION}/libuv-${LIBUV_VERSION}.tar.gz -sL \
| tar xz -C ${BUILD_DIR}
if [ -e ${SRC_DIR}/Procfile ]; then
cp ${SRC_DIR}/Procfile ${BUILD_DIR}
else
ROOTS=`cd ${BUILD_DIR}; find approot/packages/ -name root`
if [ $( echo "${ROOTS}" | wc -l ) -gt 0 ]; then
APP_ROOT=$(echo "$ROOTS" | head -1)
cat << EOT >> ${BUILD_DIR}/Procfile
web: ./kestrel --server.urls http://+:\$PORT
web: cd \$HOME/heroku_output && dotnet ./${PROJECT_NAME}.dll --server.urls http://+:\$PORT ${CORE_ENVIRONMENT}
EOT
fi
fi
4 changes: 2 additions & 2 deletions bin/detect
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
# bin/detect <build-dir>

if [ -n "$( find $1 -maxdepth 3 -iname project.json )" ]; then
echo "ASP.NET 5" && exit 0
if [ -n "$( find $1 -maxdepth 5 -iname project.json )" ]; then
echo "ASP.NET Core" && exit 0
else
echo "no" && exit 1
fi
61 changes: 60 additions & 1 deletion bin/util
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function conditional_download() {

function export_env_dir() {
local env_dir=$1
local whitelist_regex=${2:-'(DEPENDENCY_S3_BUCKET_PATH|MONO_VERSION|LIBUV_VERSION|NODE_VERSION|PROJECT|BUILD_DEBUG|DNVM_BRANCH|DNX_VERSION)$'}
local whitelist_regex=${2:-'(DEPENDENCY_S3_BUCKET_PATH|MONO_VERSION|LIBUV_VERSION|NODE_VERSION|PROJECT|BUILD_DEBUG|CORE_VERSION|CORE_CHANNEL|CORE_DOWNLOAD_LOCATION|DEFAULT_CONNECTION|DefaultConnection|DATABASE_URL|JAWSDB_MARIA_URL|JAWSDB_URL|CLEARDB_DATABASE_URL)$'}
local blacklist_regex=${3:-'^(PATH|GIT_DIR|CPATH|CPPATH|LD_PRELOAD|LIBRARY_PATH)$'}
if [ -d "$env_dir" ]; then
for e in $(ls $env_dir); do
Expand All @@ -24,3 +24,62 @@ function export_env_dir() {
done
fi
}

# based on ddollar/heroku-buildpack-apt which is deprecated
function apt_install(){
echo "Install package"

local apt_cache_dir="$CACHE_DIR/apt/cache"
local apt_state_dir="$CACHE_DIR/apt/state"

mkdir -p "$apt_cache_dir/archives/partial"
mkdir -p "$apt_state_dir/lists/partial"

local apt_options="-o debug::nolocking=true -o dir::cache=$apt_cache_dir -o dir::state=$apt_state_dir"

topic "Cleaning apt caches"
apt-get $apt_options clean | indent

topic "Updating apt caches"
apt-get --allow-unauthenticated $apt_options update | indent

for package in "$@"; do
if [[ $package == *deb ]]; then
local package_name=$(basename $package .deb)
local package_file=$apt_cache_dir/archives/$package_name.deb
topic "Fetching $package"
curl -s -L -z $package_file -o $package_file $package 2>&1 | indent
else
topic "Fetching .debs for $package"
apt-get $apt_options -y --force-yes -d install --reinstall $package | indent
fi
done

mkdir -p "$BUILD_DIR/.apt"

for DEB in $(ls -1 $apt_cache_dir/archives/*.deb); do
topic "Installing $(basename $DEB)"
dpkg -x $DEB "$BUILD_DIR/.apt/"
done

export PATH="$PATH:$BUILD_DIR/.apt/usr/bin"
export 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:$LD_LIBRARY_PATH"
export LIBRARY_PATH="$BUILD_DIR/.apt/usr/lib/x86_64-linux-gnu:$BUILD_DIR/.apt/usr/lib/i386-linux-gnu:$BUILD_DIR/.apt/usr/lib:$LIBRARY_PATH"
export INCLUDE_PATH="$BUILD_DIR/.apt/usr/include:$INCLUDE_PATH"
export CPATH="$INCLUDE_PATH"
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"
echo "APT packages Installled"
}

function topic() {
echo "-----> $*"
}

function indent() {
c='s/^/ /'
case $(uname) in
Darwin) sed -l "$c";;
*) sed -u "$c";;
esac
}