Using bin/post_compile in Heroku Python CNB Buildpacks

Yesterday Heroku flagged their legacy buildpack builders as EOL. They are transitioning to CNB buildpacks which are the better and more standard solution. Unfortunately, the CNB buildpacks are not fully backwards compatible in all scenarios.

One semi-hidden feature in the old Python buildpacks was the ability to add a script at bin/post_compile in your repo and have it automatically execute after the Python "compile" process was complete. This was great for doing any sort of custom actions that weren't otherwise supported inside the buildpack. Projects that upgrade to the CNB stack (version 22) and depend on this functionality will get no warning that it has been removed, potentially resulting in broken images.

At the time of this writing, there is an open issue to support this feature, but the official recommendation is to use native CNB functionality to add it.

The Fix

CNBs use a file named project.toml in the top-level directory which can be used to describe how the project should be built. You can use it to define the buildpacks you want to be used in building your project, including an "inline" one which can run arbitrary commands. Here is an example:

[_]
schema-version = "0.2"
id = "io.buildpacks.my-app"

# set up Python
[[io.buildpacks.group]]
id = "heroku/python"

# parse Procfile for services
[[io.buildpacks.group]]
id = "heroku/procfile"

# run ./bin/post_compile
[[io.buildpacks.group]]
id = "my-app/post-compile"
script = { api = "0.9", inline = "bin/post_compile" }

If you use other buildpacks like Node.js, you can add additional [[io.buildpacks.group]] blocks for each one. They will be run in the same order they are listed in the file (top-to-bottom).


While I certainly found the "old way" more convenient, the workaround is usable. I hope that in the future, the Python buildpack regains this functionality similar to how build scripts are handled by the Node.js buildpacks. Part of the joy of working with buildpacks is that "things just work" without needing to understand all the internals. Adding a project.toml file breaks that illusion.