heroku-modular-buildpack
A buildpack based on the idea of packages, delivering a modular system and basic dependency management.
Using
It is possible to use this buildpack as is, and contain all your extra data in your project itself.
Configuration for this buildpack should always be placed inside your project under /build/heroku/
.
A minimum configuration has at least these files living in /build/heroku/
:
install
: contains a list of packages to install separated by a space or a package per line.package
: the name of the buildpack as reported to heroku.release
: a release file as specified by heroku.
Optional components are:
installers
: a directory containing custom installers.compile
: a custom compile script for your project that will be run after all the packages have been installed.repos
: a file containing package repositories, the presence of this file enables the package manager.
Packages
Packages are represented by installer scripts that install one specific part of your environment, for example there is an installer included under installers
which install a basic nginx webserver.
The name of a package is the name of it’s installer script without the .sh
extension.
Package manager
This buildpack includes a package manager which allows you to centralize the management of your packages.
Enabling the package manager
By default the package manager is disabled, you can enable it simply by adding a repos
file to your /build/heroku/
directory.
The file-format of the repos
file is as simple as a link ( any link supported by curl ) per line of the file.
Sample repos
file:
http://jorgen.evens.eu/heroku-cedar14/index
http://example.com/deploy/heroku
Repositories
A repository is a plain text file using the following format <package-name> <package-link> <package-md5> <comments>
where each package is on its own line. It is important to use a space to separate the package from its link, a tab is currently not supported.
<package-link>
is a link ( any link supported by curl ) to the installer file for the package.
<package-md5>
is a md5 hash of the installer file which is used to check for an outdated cache.
A sample repository
nginx-deploy-page http://jorgen.evens.eu/heroku/nginx-deploy-page.sh 9bbb050c6bba8c7ad8f738ef056088ae # Exposes deployment information through a static page.
Note: Currently no versioning of packages is available. If you would like to add multiple versions you will have to include the version in the package name. nginx-1.4.2
for example.
Repository sample + tools
There is a github repository with the scripts to generate a repository for the packages contained within it.
Custom installers
You can build your own buildpack by forking this repository and adding your own installers, but if you would
like to keep your buildpack as clean as possible you can also install installer scripts in the /build/heroku/installers
folder of your project.
An alternative is to use the package manager to distribute the packages.
Variables
The buildpack passes 4 variables into the installer, three of which are the exact same variables as Heroku passes to the compile script. The fourth indicates the location at which the installer was stored.
- $BUILD_DIR
- $CACHE_DIR
- $ENV_DIR
- $INSTALLER_DIR
These directories are created for you when the buildpack runs.
Environment variables you may have set in Heroku will be loaded and available to your installer script as well.
Note: When using the package manager $INSTALLER_DIR
will point to the location in the repository file rather than to the local copy that gets downloaded.
Helper functions
An installer has access to some helper functions to print to the console and to manage dependencies it might require.
print_action <message>
: prints the message prefixed with the ‘——-> ‘ arrow.print <message>
: prints the message prefixed with spaces to align it withprint_action
.dependency_require <package>
: requires the installation of the dependency before continuing.dependency_mark <package>
: marks a package as installed. This is primarily for internal use.download <url> <target> [<md5>]
: Downloads a file to the specified location and checks if the MD5 hash is correct.cached_download <url> <target> [<md5> [<exit_on_fail>]]
: Performs the same function asdownload
but caches the files.unpack <url> <md5> [<target> [<clean_target>]]
: Downloads the specified .tar.gz file usingcached_download
and extracts the contents into/app/vendor
by default.env_extend <environment_variable> <value_to_be_added>
: Appendvalue_to_be_added
toenvironment_variable
separated by a semicolon. Use this to extendPATH
and/orLD_LIBRARY_PATH
.
Note: You should NOT mark your own package as installed using dependency_mark
, this is done for you.
dependency_mark
use case
A sample use-case for the dependency_mark
function is given here to illustrate its use.
If your package can be used as a drop-in replacement for another package you can mark the original package as installed so your package does not get overwritten when a different package depends on the original.
An example:
- There is a generic
nginx
package in your repository - There is a
nginx-module
package which is nginx with a specific module compiled in. - There is a package
nginx-status
which depends on the genericnginx
package.
In this scenario we would like to combine nginx-module
with nginx-status
, if we would setup our install
file like illustrated below we would overwrite nginx-module
with generic nginx
since nginx-status
depends on it.
nginx-module nginx-status
To solve this problem we can mark nginx
as installed at the end of our nginx-module
installer by calling dependency_mark nginx
.
Now when nginx-status
is installed nginx
will be reported as installed and nginx-module
will not get overwritten.
boot.sh
A boot.sh
file is always created with a shebang #!/bin/sh
followed by an empty line. If your installer wants to add something to the boot.sh
file you should simple append the lines to the ${BUILD_DIR}/boot.sh
file.
It is important that you send blocking applications to the background using &
. For example:
/app/vendor/nginx/sbin/nginx &
A wait
will be added to the end of the boot.sh script so that the script will wait for the background tasks to complete and heroku will not think that the script exited. This way all the commands in the boot.sh
file get executed and all services start as they are supposed to.
Project specific compile
The project specific compile
script has access to the same functionality as a custom installer and is for all intents and purposes a custom installer that always gets run at the end.
Cleaning cache
Setting the environment variable CLEAN_CACHE
to a non empty value will result in the buildpack removing the CACHE_DIR
before running any installers.
This will ensure that the cache is empty and all the required packages and files will be downloaded again.
Contributions
I really appreciate any contribution you would like to make, so don’t hesitate to report an issue or submit pull requests.
Special thanks to these people for contributing to this project
License
This project is available under the New BSD License.
About me
Hi, my name is Jorgen Evens. By day I built things (mainly in PHP and JavaScript) for employee advocacy platform Ambassify and by night I tinker around with these kinds of projects.