Because of how shiv works, you can ship entire django apps with shiv, even including the database if you want!
First, we will need an entrypoint.
We'll call it main.py
, and store it at <project_name>/<project_name>/main.py
(alongside wsgi.py
)
import os
import sys
import django
# setup django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "<project_name>.settings")
django.setup()
try:
production = sys.argv[1] == "production"
except IndexError:
production = False
if production:
import gunicorn.app.wsgiapp as wsgi
# This is just a simple way to supply args to gunicorn
sys.argv = [".", "<project_name>.wsgi", "--bind=0.0.0.0:80"]
wsgi.run()
else:
from django.core.management import call_command
call_command("runserver")
This is meant as an example. While it's fully production-ready, you might want to tweak it according to your project's needs.
Next, we'll create a simple bash script that will build a zipapp for us.
Save it as build.sh
(next to manage.py)
#!/usr/bin/env bash
# clean old build
rm -r dist <project_name>.pyz
# include the dependencies from `pip freeze`
pip install -r <(pip freeze) --target dist/
# or, if you're using pipenv
# pip install -r <(pipenv lock -r) --target dist/
# specify which files to be included in the build
# You probably want to specify what goes here
cp -r \
-t dist \
<app1> <app2> manage.py db.sqlite3
# finally, build!
shiv --site-packages dist --compressed -p '/usr/bin/env python3' -o <project_name>.pyz -e <project_name>.main
And then, you can just do the following
$ ./build.sh
$ ./<project_name>.pyz
# In production -
$ ./<project_name>.pyz production