heroku/base-images

Installation of gettext fails on heroku-20; heroku-18 is fine

Closed this issue · 5 comments

I manage a Django app. I frequently deploy new environments for testing purposes.

Although the Heroku stack seems to have included gettext for a while now, it has never worked for me; I always get CommandError: Can't find msgfmt. Make sure you have GNU gettext tools 0.15 or newer installed., which is mentioned in several GitHub Issues/Stack Overflow questions. So I use heroku-buildpack-gettext.

I haven't had any deploy problems until fairly recently. New versions of my app won't start, although all deploys appear to be successful. It seems this corresponds to when heroku-20 became the default stack.

In the logs, this error is repeated many times:

2021-01-16T15:22:48.065513+00:00 app[web.1]: processing file django.po in /app/.heroku/python/lib/python3.9/site-packages/django/contrib/gis/locale/en/LC_MESSAGES
2021-01-16T15:22:48.094738+00:00 app[web.1]: Execution of msgfmt failed: msgfmt: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory

The output from the deployment includes this:

remote: -----> Gettext library app detected
remote: -----> Installing gettext...
remote: /tmp/build_b06e1515/.heroku/gettext/bin/      gettext: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory

If I remove heroku-buildpack-gettext in heroku-20 or heroku-18, then I get this error, which is the one that led me to start using the buildpack in the first place:

2021-01-16T15:28:45.462654+00:00 app[web.1]: CommandError: Can't find msgfmt. Make sure you have GNU gettext tools 0.15 or newer installed.

If I set my stack back to heroku-18 (heroku create $app_name -s heroku-18) and include heroku-buildpack-gettext, then the problem goes away.

@RobertAKARobin Hi! The gettext packages are installed on all stacks, however only on the build image variants, not at runtime - see:
https://devcenter.heroku.com/articles/stack-packages

The error message above appears to come from the Django compilemessages command:
https://github.com/django/django/blob/3.1.5/django/core/management/commands/compilemessages.py

It looks like the translations in this case are being performed at runtime?

The compilemessages command should be run at build time instead (for example using the Python buildpack's bin/post_compile hook), otherwise every time a dyno boots it will have to repeat the compilation, compared to at build time where the result will be stored in the slug and then be able to be reused from then on.

Could you describe more about how the app is set up with regards to compilemessages?

gettext: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory

This error is because heroku-buildpack-gettext uses hardcoded binaries that are compiled for an older stack. The buildpack hasn't been updated since 2016 so I would recommend not using it. The reason your app worked using the buildpack on older stacks when it doesn't without the buildpack, is because the buildpack makes gettext (and this msgfmt) available at runtime too (and not just build time). However this is unnecessary for most use cases.

Ah, that makes sense. I wasn't aware Heroku supported build scripts. I only seem to find articles on defining buildpacks. Is that what you meant?

The only thing that is run during the build is buildpacks, but some buildpacks support arbitrary scripts being run at various stages of the build (for example Python supports bin/post_compile and bin/pre_compile scripts, and the Node buildpack supports certain scripts entries in package.json).

For more on using these to run compilemessages, see:
heroku/heroku-buildpack-python#198 (comment)

That's great to know about. Thank you! It also looks like app.json supports post-build scripts to some extent? https://devcenter.heroku.com/articles/app-json-schema#scripts It doesn't seem particularly well-documented.

Those app.json scripts are for tasks to be run after a build is created, when the app is first released. They shouldn't be used for creating assets (since the commands are run in a one-off dyno after the slug is already created, so any changes will be lost), more for things like interacting with a DB to seed initial data etc. There's more on this at:
https://devcenter.heroku.com/articles/github-integration-review-apps#the-postdeploy-script

For some more background on why separating build and runtime is important, see:
https://devcenter.heroku.com/articles/runtime-principles#build-release-run