Heroku Multipack: Node + Python
The problem of where performing all the deployment tasks has been tormented me for weeks. I did not know well enough all the witcheries that Heroku allows to make you. So, I was trying first to save the JS production files to Git. Yes, I know, this is a very bad practice and a mess.
In an attempt to redeem myself I took a second approach: upload my static files to a CDN. But recently, I have changed my mind about leaving the web applications static files in a different server. Then, I discovered that I could perform all tasks that I was executing in the Heroku platform instead of at localhost.
So, in this publication you can see the easy process you have to follow to set up your Python project along with a Node build-pack.
I. Set up NodeJS + Python buildpacks
Log into your Heroku application and check that you have just your Python buildpack, as you can see in the code below. Then add the NodeJS build-pack in the first place: on this way, the JS dependencies will be downloaded in first place and all JS task will be launched firstly. This is very important if you are running a framework as Django, because you need to traspile your files before collectstatic command is executed. Make sure that the NPM_CONFIG_PRODUCTION variable is set to true, if not change it, it could impact on your application performance.
$ heroku buildpacks
$ heroku buildpacks:add heroku/nodejs --index 1
$ heroku config:set NPM_CONFIG_PRODUCTION=true
II. Set up JS deployment tasks
Once your application is configured you have to set up the tasks that Heroku dependencies are downloaded and heroku-postbuild after the dependencies are downloaded. So, during the prebuild phase the environment is swiched to DEBUG to being able to download and install the devDependencies. After that, the application dependencies are downloaded and installed. Later, the postbuild task is executed, here is where the deployment tasks of code traspilation, minimization... have to be executed. Finally, the environment is switched again to production.
// package.json
"scripts": {
"heroku-prebuild": "NPM_CONFIG_PRODUCTION=false NODE_ENV=development npm install --only=dev --dev",
"heroku-postbuild": "npm run prod; export NPM_CONFIG_PRODUCTION=true; export NODE_ENV=production;",
"build": "webpack",
"start": "webpack-dev-server --progress",
"prod": "PROD_ENV=1 webpack"
}
III. Python tasks
Creating a bin/post_compile
bash file you can set up tasks to be executed after PIP dependencies are downloaded and installed. For instance, in the code bellow I have set up to download TextBlob library's corpora after all dependencies are installed.
#!/usr/bin/env bash
source $BIN_DIR/utils
python -m textblob.download_corpora
cd ${NLTK_DATA}
find . -name "*.zip" -type f -delete
IV. Conclusions
Heroku allows to set up quite easily pre/post build tasks. The key on this platform is to read carefully the docs and try to adjust your solution to the tools it offers. Happy coding!