197

I have two branches, Development and Production. Each has dependencies, some of which are different. Development points to dependencies that are themselves in development. Likewise for Production. I need to deploy to Heroku which expects each branch's dependencies in a single file called 'requirements.txt'.

What is the best way to organize?

What I've thought of:

  • Maintain separate requirements files, one in each branch (must survive frequent merges!)
  • Tell Heroku which requirements file I want to use (environment variable?)
  • Write deploy scripts (create temp branch, modify requirements file, commit, deploy, delete temp branch)
1
  • 2
    easier deploy script: maintain 2 files. use symlink between them.
    – Udy
    Commented Jul 23, 2013 at 7:28

4 Answers 4

373

You can cascade your requirements files and use the "-r" flag to tell pip to include the contents of one file inside another. You can break out your requirements into a modular folder hierarchy like this:

`-- django_project_root
|-- requirements
|   |-- common.txt
|   |-- dev.txt
|   `-- prod.txt
`-- requirements.txt

The files' contents would look like this:

common.txt:

# Contains requirements common to all environments
req1==1.0
req2==1.0
req3==1.0
...

dev.txt:

# Specifies only dev-specific requirements
# But imports the common ones too
-r common.txt
dev_req==1.0
...

prod.txt:

# Same for prod...
-r common.txt
prod_req==1.0
...

Outside of Heroku, you can now setup environments like this:

pip install -r requirements/dev.txt

or

pip install -r requirements/prod.txt

Since Heroku looks specifically for "requirements.txt" at the project root, it should just mirror prod, like this:

requirements.txt:

# Mirrors prod
-r requirements/prod.txt
15
  • 2
    You ignored the problem of how to use separate requirements files for different environments on heroku.
    – Ed J
    Commented Feb 26, 2016 at 3:23
  • 55
    I believe my answer addressed that. Commented Mar 27, 2016 at 9:00
  • 2
    I could be misunderstanding your question or perhaps your question is different than the original poster. But for clarification, the staging branch's requirements.txt can contain "-r requirements/staging.txt" (or similar), while the prod branch's one can contain "-r requirements/prod.txt" (see end of my answer). Sync the appropriate branch to its corresponding Heroku instance. Commented Dec 18, 2016 at 22:31
  • 5
    When I install a new package using pip install foobar, how do I tell pip to put foobar as a dependency only in the requirements/production.txt? Or do I have to manually put foobar in the requirements/production.txt file? Commented May 30, 2018 at 23:17
  • 6
    @SohamNavadiya That is not what I asked. Let's say I have a base.txt with 3 packages in it, and dev.txt with 1 package in it (and -r base.txt). ALL 4 packages are installed in my virtual environment. I want to now install the 5th package and list it in base, NOT in dev, how do I do it? Sure, I can install it and pip freeze > base.txt but that does NOT solve the problem. It then puts the 4th dev dependency in base which I do not want. Commented Aug 29, 2018 at 0:10
23

A viable option today which didn't exist when the original question and answer was posted is to use pipenv instead of pip to manage dependencies.

With pipenv, manually managing two separate requirement files like with pip is no longer necessary, and instead pipenv manages the development and production packages itself via interactions on the command line.

To install a package for use in both production and development:

pipenv install <package>

To install a package for the development environment only:

pipenv install <package> --dev

Via those commands, pipenv stores and manages the environment configuration in two files (Pipfile and Pipfile.lock). Heroku's current Python buildpack natively supports pipenv and will configure itself from Pipfile.lock if it exists instead of requirements.txt.

See the pipenv link for full documentation of the tool.

13
  • 30
    pipenv is a waste of time. Locking takes too long.
    – nurettin
    Commented Jan 7, 2019 at 8:59
  • 37
    pipenv is broken at almost all aspects. It promises a lot, but ships very few
    – ospider
    Commented Feb 23, 2019 at 1:24
  • 15
    @ospider Using pipenv on a daily base and am not experiencing such negative issues as you and nurettin are reporting. Working with pipenv version 2018.10.13. Broken in all aspects is thus a very empty statement.
    – Kwuite
    Commented Mar 16, 2019 at 9:46
  • 4
    @Kwuite I share the sentiment of your last sentence. There's little dialogue to engage in when a comment is critical yet vacuous. Commented Mar 17, 2019 at 10:16
  • 5
    Agree with nurettin and ospider. pipenv is awful. Commented Mar 31, 2019 at 5:21
8

If your requirement is to be able to switch between environments on the same machine, it may be necessary to create different virtualenv folders for each environment you need to switch to.

python3 -m venv venv_dev
source venv_dev/bin/activate
pip install -r pip/common.txt
pip install -r pip/dev.txt
exit
python3 -m venv venv_prod
source venv_prod/bin/activate
pip install -r pip/common.txt
exit
source venv_dev/bin/activate
# now we are in dev environment so your code editor and build systems will work.

# let's install a new dev package:
# pip install awesome
# pip freeze -r pip/temp.txt
# find that package, put it into pip/dev.txt
# rm pip/temp.txt

# pretty cumbersome, but it works. 
0

I ended up having requirements-all.txt, which I'll update with every install, and save it's diff to respective file.

E.g. I add pytest.

  • pip freeze > requirements-all.txt
  • git diff HEAD requirements-all.txt > temp-diff.txt saves what was installed for pytest to temp-diff.txt
  • (manually) copy all +<lines> from temp-diff.txt to requirements-dev.txt
  • commit both requirements-all.txt and requirements-dev.txt

Hope there is a better way

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.