myherokuappname
. Anywhere you see this, please substitute in your Heroku app name. Later in the instructions I will cover how to make an app on Heroku.pipenv lock -r > requirements.txt
(note the >
after the -r
) in the directory where your Pipfile
and requirements.txt
files are located (they should be in the same directory)
pipenv lock -r
is a special text string containing all of your python dependencies designed to be written to file>
operator in bash/zsh takes the incoming buffer, in this case the text output, and runs an overwrite command on the following argument, in this case requirements.txt
, which will completely replace the contents of requirements.txt
with the output of pipenv lock -r
Pipfile
Dockerfile
, .dockerignore
, and .gitignore
, and your Dockerfile
needs to be correctly configured relative to the folders around it
If your file structure looks like the standard Python starter
then you're good to go
If it looks more like the Express starter
then you need to ensure that your Dockerfile
, .dockerignore
, and .gitignore
files all exist outisde your frontend and backend folders, and that the contents of your Dockerfile
are appropriately adjusted to build an app with that file structure:
where calls to react-app
and backend
are appropriately substituted with whatever you have opted to name your respective folders.
heroku
are directory and terminal-state agnostic; they may be run from any folder, inside or outside of your pipenv shell.Create an app on Heroku. This is as simple as signing in to the heroku dashboard and either
Once you've named your app, you should be taken to that app's dashboard page. Select the Resources
tab, and search for "Heroku Postgres" - you're looking for the result with what looks like an elephant with two differently colored ears, on a purple square. Select it, and then choose "Hobby dev - free".
Go to the Settings
tab, and click Reveal Config Vars
- you should see a DATABASE_URL
environment variable already created for you. In the empty box beneath, enter a key of SECRET_KEY
, and give it a value of any collection of legal US keyboard characters. This key is what Flask-WTF
will use to generate a CSRF token for your app.
.env
file - in fact, matching it won't do anything, since the CSRF token stored for http://localhost:xxxx
is inaccessible to any other site, so generating two keys with the same cipher is pointless.In your Dockerfile
, there is a line with the environment variable REACT_APP_BASE_URL
. Remove everything after the =
, including whatever <> or {} characters might be there, and put in plain text the link to your Heroku site. This link will be of the format https://myherokuappname.herokuapp.com
. If you're unsure, at the top of your heroku dashboard for the app you can click Open App
, which will open a new tab or window with the exact link to your site, which you can copy and paste. Make sure to eliminate any extra whitespace; REACT_APP_BASE_URL=https://myherokuappname.herokuapp.com
.
Open your terminal, and run heroku apps
. If you are successfully logged into the Heroku CLI, which you should be if you've ever done it before (it persists), you should see your email address, followed by all of the apps on your Heroku account. If you are not logged in, it will give you an invalid credentials warning, and then prompt you to press any key to trigger browser-authenticated login.
npm install -g heroku
- on Macs this may additionally require sudo
Once you've confirmed that you're logged in and that the new app you've created is accessible through the CLI, run
heroku authorizations:create
noting the lack of spaces on either side of the :
. What this will do is generate a long-lived CLI access token, which can be used in place of a username&password, as it uniquely identifies a user authorized to interact with your Heroku account on your behalf, using your email address. There will be a line that begins with token
- copy everything after that word, to the end of the line. It should be a series of letters, numbers, and hyphens.
Open the GitHub repo for your app, and click the Settings
tab. Scroll down and select Secrets
, then new repository secret
. You may name this secret whatever you like; I suggest HEROKU_API_KEY
. Then paste the token you copied from your terminal into the larger box below.
Click the Actions
tab.
Select set up a new workflow yourself
, which should open a new page with a text editor and a dummy file. Completely delete the contents of that dummy file, and replace it with this, exactly:
In this section:
if your primary, production-facing branch is called something other than main
(production
, master
, Deploy
, etc) be sure to change the word main
to match your primary branch name exactly, capitalization and all.
In this section:
if you've named your environment variable something other than HEROKU_API_KEY, be sure to postfix secrets.
with exactly that.
In this section:
between -a
and web
, you must insert your heroku app name, formatted exactly as it comes before .herokuapp.com
in the url.
Click start commit
and then feel free to add a personalized commit message if you want; if you don't, GitHub will add one for you automatically, so feel free to simply click the green button.
What you've just done is create a series of commands that instructs GitHub, every time there is an explicit push
to your main branch, OR a merged pull request (which is treated as a push
in GitHub's internal system), GitHub will, on your behalf, build your app on its very expensive, very fast servers, and then send your app over to Heroku using your authorization token.
You've also created changes on your GitHub that don't exist on your local machine. If we don't pull these changes down before the next time we try to merge or push, we'll likely introduce merge conflicts. Quickly run git pull
from your terminal to sync the .github
folder.
This github workflow will only build the static portions of your app; your database must now be configured directly. Back in your terminal, we have to run the commands to actually build your database. On your local machine, typically we would run flask db upgrade
and flask seed all
to do this. These same commands must now get run on your Heroku app; to do this, prefix them both with heroku run -a myherokuappname
. The result should look something like
Generally speaking, any commands which you can run to interface with your Python app on your local machine, you can also run to interface with your Heroku app; they just need to be prefixed with heroku run -a myherokuappname
to cast them through to Heroku.
One exception to this is flask db migrate -m "commit message"
- this is a command that creates migration files for Alembic to run. Because file creation is something that is resolved by simply pushing to GitHub and the consequent automatic build, you should only ever have to run this command locally, as the files will already exist by the time you're able to run any commands on Heroku.
At this point, any errors that occur while attempting to upgrade or seed your database are likely due to errors within your migrations or seed files, and not errors specifically to do with Heroku. If you ever run into a situation where your local migrations are totally out of whack, and your database models seem fine but you just can't seem to upgrade, the following general workflow should solve most problems:
Drop and re-create your database, either through Postbird or the psql
CLI.
DROP DATABASE database_name;
CREATE DATABSE database_name WITH OWNER flask_app_name;
In app/migrations/versions
, delete all of the contents of that versions
folder, but DO NOT delete the folder itself.
Before:
After:
Note that alembic.ini
and the files beneath it are not actually within the versions
folder, but are siblings of it within the migrations
folder, as they're at the same horizontal level.
Run flask db migrate
(the -m "commit message"
is optional) once, which tells Flask-Migrate and Alembic to diff the current state of your models against the current state of your database and previous migrations, and creates a migration that takes the minimum number of steps from the most recently run migration, to get your database to match your models. This is why, if you create migrations without running them, or if you try to downgrade before running flask db migrate
, you get an error that says target database is not up to date
- all of the migrations which already exist must be run before a new migration can be created.
Since at this point, we will have 0 migrations previously created, and an empty database, it will create one migration to match the total absolute state of our models, with all of the steps necessary to compeltely construct the database from scratch. Then, we can run flask db upgrade
once to run that migration, and flask seed all
to run our seeders.
To recap:
versions
folderflask db migrate
flask db upgrade
flask seed all
If any errors occur during steps 4-6, there are errors either in your models, or in your seeders. If you're seeing no such command seed
or similar, there is likely a syntax error somewhere in your models or in app/__init__.py
that is preventing the "seed" command from getting created (which happens in seeds/__init__.py
, if you were curious). Try simply starting your app with flask run
, which will usually throw the error properly for you to find it.
Lastly, if we've had to drop and recreate our database locally, we'll have to do the same thing on Heroku. Heroku doesn't let you explicitly drop your database, since that requires escalated privileges that you simply cannot be granted. However, you are allowed to destroy all of the tables and the extant database structure, which is pretty much the same thing. The command to do this is
heroku pg:reset -a myherokuappname --confirm myherokuappname
Notice you must enter your app name twice - this is beacuse for large production apps, destroying the entire database is unusual, and could be catastrophic, so Heroku wants you to be extra sure you know what you're doing. Optionally, you may simply run heroku pg:reset -a myherokuappname
, which will open a dialogue asking you to re-enter your app name to confirm.
After resetting your Heroku database, you must push the updates to your models/migrations/seeders to GitHub, wait for your app to successfully build and push, and then you can run the Heroku CLI commands for upgrading and seeding your database.
To recap:
heroku pg:reset -a myherokuappname --confirm myherokuappname
git add / commit / push
heroku run -a myappname flask db upgrade
heroku run -a myappname flask seed all