Thursday, December 26, 2013

Publishing a Django Project to Heroku using Git from Windows 8.1

Problem

I wanted to deploy my Django project to Heroku from Windows, but never done it before. My particular circumstances are:
  • This is the first time I try deploying to Heroku from this PC.
  • I don't know how to deploy a Django project to Heroku
  • I have installed Git for Windows
  • I was developing my Django project in Visual Studio 2012 using Python Tools for Visual Studio (I will explain this in the next section)
  • I have not setup any SSH private/public keys on this PC
I will not cover:
  • How to setup an account with Heroku
  • How to install Heroku, Git, Python, Visual Studio, PTVS on Windows
  • How to install, create a new project, and program in Django
  • How to use Git.
For Git, I will not go into details so you need to at least know the basics of using git (git add, git commit, git remote, git push). I will explain my experience, and the steps that worked for me getting my project up and running in Heroku. I'm writing this as a reminder for myself on how to do these things, but hopefully this will be helpful to someone out there in the same situation.

Side note: I will interject every now and then with side notes as I explain things, just because that's how my thought process works, or it's just difficult to put it in a logical step. Sorry in advance.

Django Development in VS2012 using PTVS

Python Tools for Visual Studio is a great plugin for Visual Studio 2012 to program Python [website].  I started my Django project in VS2012 using PTVS because it provides the ability to create a new Django project by default. Additionally, VS2012 provides a pretty good interface with Git if you want to manage your program/project with Git (requires a separate installation of Git plus a VS plugin for it).

Unfortunately, this feature does not work well if you want to publish your Django project on Heroku.  You see, VS only allows you to add the whole VS project (a.k.a. "solution" in VS terminology) to source control. When creating a new project in VS, there is a *.sln file that gets created as your 'VS project' designator. When you create a Django project in VS, this resides one level above your Django project's root folder ("VS project").  Meanwhile, if you are going to upload your project to Heroku using Git, Heroku requires that you are uploading the root directory of your project. In other words, your git repository starts at the Django project's root folder ("Django root").
Conclusion: can't upload our Django project to Heroku from VS2012 - until they allow 'Git init' at Django root.
Aside from that, developing Python in VS2012 using PTVS is actually pretty good. You should at least give it a try.

Versions etc.

The pieces of software I used are:
  • Git version 1.8.1.msysgit.1
  • Python 3.3, with modules:
    • Django 1.6.1
    • distribute 0.7.3
    • gunicorn 18.0
  • virtualenv 1.10.1
  • Windows 8.1
  • PuTTY, PuTTYgen, Pageant beta 0.63
  • heroku 3.2.1 (I think ...)

Heroku

Heroku provides a pretty good Getting-Started-With-Django tutorial on their website [link]. That tutorial will help you set up your Heroku account and creating a new Heroku project. Also, it covers 80% what you need to upload your Django project to Heroku. The missing 20% is how to do it in Windows.

Here, I will write down some missing things they don't mention in case of Windows.

Git

Well, now that I found out I can't upload my project through VS although it's already maintained using Git, I have to make a new git repo that starts at my Django project's root folder. 
  1. Copied my Django root project to a different location (e.g. C:\Projects\DjangoProject1).
  2. Start a git repo there.
Oops - side note: USE Git Bash. Git Bash is a terminal-like tool that you get by installing Git for Windows (imagine that?). If you don't know how to find it, just hit the Windows button and type in "Git bash".

In Git bash:

~/Projects$ cd DjangoProject1
~/Projects/DjangoProject1$ git init

That will create a new git repository for DjangoProject1. Before you add/commit add an entry in .gitignore to exclude the virtual environment folder. Create a new .gitignore file in the root of your git repo (I like to use vim)

~/Projects/DjangoProject1$ vim .gitignore

So if my virtual environment directory is in  /Projects/DjangoProject1/env, in .gitignore, type:

env/

Save the file and close the editor (:wq in vim).

Side note: ALWAYS use a virtual environment whenever you can. For Python (and hence, Django) it's virtualenv. If you're not familiar on how to do this, here's some links to some good tutorials [link].

Next, add your project to the git repo:

~/Projects/DjangoProject1$ git add -A
~/Projects/DjangoProject1$ git commit -m "First commit"

So this is your master branch. 

Next, add the Heroku git path as your remote. If you followed Heroku's guide and run "heroku create" (from this step) from your repo, that path is automatically added to your git remote.  Check it using 

~/Projects/DjangoProject1$ git config -l (lower case L)

You should see a remote.heroku.url= entry at the bottom of the config.

According to Heroku's guide, your next step would be doing:

~/Projects/DjangoProject1$ git push heroku master

Which should push your master branch to heroku. However, when I did it then, it gave me a "Permission denied" error.  Turns out, I need to create and add SSH keys for my system.

SSH Keys

Unfortunately Windows does not come with tools to generate SSH keys by default. I needed to download PUTTYgen to generate my SSH keys. I followed this tutorial. It's pretty straightforward, except at step 6.

Step 6 in that tutorial said to save the private key. By default, PUTTYgen will ask you to give a file name and it will have .ppk extension. To make this SSH keys work for my Windows machine, I had to do several things not mentioned in that tutorial:
  1. Create a .ssh directory under C:\Users\MyUserName. Like this:

    C:\Users\MyUserName\.ssh

    Note: if you can't create the folder from Windows Explorer, use Git Bash (or Window's Command Prompt) and use 'mkdir' command:

    Git bash:
    ~/Users/MyUserName$ mkdir .ssh
    Command Prompt: C:\Users\MyUserName > mkdir .ssh
  2. Once the keys are generated from that tutorial, save the private key using (from the toolbar) Conversions > Export OpenSSH keys
  3. Save the private key with file name "id_rsa" in C:\Users\MyUserName\.ssh
  4. Save the public key with file name "id_rsa.pub" in C:\Users\MyUserName\.ssh
  5. Add the keys to Heroku (from this guide)

    ~/Projects/DjangoProject1$ heroku keys:add
Now here's the confusing part for me. Before that, I tried different solutions including:
  • Installed Pageant and adding the keys to it
  • copied the private and public keys to C:\Program Files (x86)\Git\.ssh (private key not exported as OpenSSH keys at this point)
Which the upload kept failing. My upload to Heroku started working AFTER I save the private key using step 2 above (I think I might've copied the OpenSSH version of the private key to C:\Program Files (x86)\Git\.ssh as well ... I can't remember). So, I'm not sure whether or not saving the keys in Pageant and Git\.ssh have anything to do with it or not.  But now it's working. WAIT! There are two more things to do before Heroku actually accepts and recognize the project as a Django project: creating a requirements.txt file and Procfile file.

Side note: without the requirements.txt, the upload will fail with this error.

Pip freeze > requirements.txt

I have to create a requirements.txt file to let Heroku know what modules I need for my Django project. The requirements.txt file must reside in the Django project's root directory. To do this:

  1. Activate the virtual environment if it's not already. If not, all the globally-installed Python modules will be included, and we don't want that (this is the other important benefit of using virtual environments)

    ~/Projects/DjangoProject1$ source env/bin/activate
    (env)~/Projects/DjangoProject1$


    The (env) indicates that the virtual environment has been activated.
  2. Run the pip freeze command:

    (env)~/Projects/DjangoProject1$ pip freeze > requirements.txt

    This will create the requirements.txt file in the root directory.

For my simple project, all I'm using is Django, distribute, and gunicorn. Here's the content of my requirements.txt file:

requirements.txt

Gunicorn is required (as you can see from this section of the Heroku tutorial) - your app won't launch without it even if you successfully upload it to Heroku.

Procfile

Create a Procfile (capital P, lowercase f, no extensions) in the Django project root directory as explained in the Heroku tutorial section Declare process types with Procfile. Here's the content of my Procfile file:




Start Uploading!

Here's the checklist for uploading:
  • Git repo root folder is Django project root folder (all project files except the virtual environment files have been added to the repo)
  • Have the Heroku git repo as remote
  • gunicorn module is installed
  • requirements.txt and Procfile are in the Django project root folder
  • SSH keys saved as OpenSSH and saved in C:\Users\MyUserName\.ssh
Upload to Heroku using:

(env)~/Projects/DjangoProject1$ git push heroku master

That command basically push the master branch to heroku (remote repo).
When the upload is successful, I get something like this:


Additionally, if everything works and running, Heroku can launch my application automatically. I can check that it's running using "heroku ps" like so:

(I'm hiding the actual name of my app, sorry)

So when I go to my app url http://pacific-ravine-8494.herokuapp.com/ it's running! Yay!

References:

How to Install SSH private key generated by Puttygen (Ubuntu): http://askubuntu.com/questions/15378/how-do-i-install-a-ssh-private-key-generated-by-puttygen