LibraryHippo 2020 - A Bare-bones Flask App
Last time I laid out the uncertainties that have to be explore before I want to try hosting LibraryHippo on Heroku. Here they are again, roughly in descending order of importance and risk:
- web app hosting
- scheduled jobs
- scraping library websites on users' behalf
- a small (perhaps a few MB) persistent datastore
- authentication via social accounts
- sending e-mail
- free, or nearly so; as I said, this is a hobby project, and I'm not willing to dump several tens of dollars into it every month
- job queues
- custom domain name
Today I'll address the first of those: web app hosting. It's not particularly
risky, but it's very important. I'll start with an essentially empty repository:
just a license file, readme, and a .gitattributes
file.
Requirements
As I type, Heroku supports the Python 3.8.1 runtime, so I upgraded from 3.8.0 and then I created a virtual environment to work in, upgraded pip, and installed Flask.
Typically Flask will read some some values, such as the application file, secrets, or other configuration, from environment variables. I prefer to use python-dotenv and to save them in files (some committed, some not) for local use.
Finally, I install Invoke, since I can never
remember the syntax for the various tasks I have to do and tools I need to use
to them, and I think it's a nicer system than "a dozen batch files" that
accreted in the old LibraryHippo. Those should be all the dependencies I need
for now, so I freeze a requirements.txt
file.
py -3.8 -m venv venv venv\Scripts\activate py -m pip install --upgrade pip pip install flask pip install python-dotenv pip install invoke pip freeze | Out-File -encoding ascii requirements.txt
Create a Flask application
Now I'm ready to create an application! We need three files:
Typically, one would then set the FLASK_APP
environment variable to
libraryhippo.py
, but I find that inelegant, and I don't really enjoy making
sure it's set when I need it. Instead I'll set it in a .flaskenv.py
file:
I'll create a run
task in tasks.py
so I remember how to run the
application, and then invoke it:
And voilà:
It's not especially pretty, and it doesn't do a thing, but it's a running app.
Deploy to Heroku
Before deploying I needed
- a Heroku account and
- the Heroku CLI
I'd already signed up for a free account and installed the Heroku CLI while going through The Flask Mega-Tutorial, so I can move right ahead with the work necessary for this application.
Create a Heroku Application
Heroku needs an application to associate with your code. Use the CLI to add an application with a unique name:
Success! The last line of the output indicates the URL of the deployed application (it's boring right now, since it doesn't have the LibraryHippo code) and the URL of the git repository to push versions of LibraryHippo to.
Satisfy Heroku's Requirements
Heroku needs a Procfile
to understand how to run an application. So far
LibraryHippo's is simple:
This tells Heroku to use a web dyno to run the Gunicorn web server, which will host the LibraryHippo application. Gunicorn is required because the native Flask web server is not production-ready.
Of course, a Heroku web dyno doesn't come with Gunicorn installed, so it needs to be added to the requirements and frozen:
pip install gunicorn pip freeze | Out-File -encoding ascii requirements.txt
Finally, Heroku needs to know which version of Python to use. It has its own
defaults, but I prefer to know that my local environment is in sync with
Heroku's, so add a runtime.txt
file to tell Heroku what I expect:
Push the code to Heroku
I'd been committing my code to a local git repository as I went, so
heroku apps:create
automatcially added a new remote called "heroku" for me;
If I hadn't had git set up already, I could do so now and add the remote manually.
Pushing to Heroku was to have been anticlimactic, but I kept messing up the
syntax of the git command. Heroku serves apps from the master
branch, and
I'm working in lh2020
. The command that I thought meant "push lh2020 to
heroku as master" actually just pushed lh2020 and master, but the latter has
the code for the existing application, not the new Flask one. To save myself
from making this mistake again, I added a task:
And now the new LibraryHippo is running on Heroku.