# assuming you've already install heroku cli toolbelt installed
# wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
# assuming you've got your SSH key for bitbucket already setup
Create a new empty repo on bitbucket, REPONAME
THEN, on your development machine:
mkdir REPONAME
cd REPONAME
heroku login
# A LOT OF PROBLEMS CAN OCCUR AROUND MULTIPLE HEROKU ACCOUNTS (WORK AND PERSONAL) AND SSH KEYS
! Your account email@example.com does not have access to kittyandbear.
!
! SSH Key Fingerprint: e5:1c:12:4d:c7:6a:d1:d8:96:20:35:f0:bf:b4:47
1. install heroku toolbelt wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
2. create a new ssh key on a clean ubuntu vm: ssh-keygen -t rsa
3. ADD THE SSH KEY TO YOUR LOCAL KEYCHAIN: ssh-add ~/.ssh/id_rsa
4. remove all ssh keys from your personal account
5. add the NEW id_rsa.pub to the heroku Account -> SSH Keys (https://dashboard.heroku.com/account)
6. heroku git:clone -a APPNAME
- - - - - -
sudo pip install virtualenv # version 1.7.1.2 from Ubuntu is old
pip install virtualenv --upgrade # ensure the newest version, e.g. 1.11.4 http://www.virtualenv.org/en/latest/virtualenv.html#usage
virtulenv venv # creates a copy of Python in the ./venv directory,
source venv/bin/activate
pip install pip --upgrade # upgrade pip from 1.1 to 1.53, otherwise really bad pip
pip install Flask gunicorn --upgrade # if installing to the host OS it would be sudo pip install BUT then all of the packages would be in pip freeze # list the packages installed
# deactivate # stop using the virtaul environment
echo -e "web: gunicorn hello:app" > Procfile
# yes there's a space after the colon indicating run gunicorn passing in hello.py file, global variable app
pip freeze > requirements.txt
Flask==0.10.1
Jinja2==2.7.2
MarkupSafe==0.18
Werkzeug==0.9.4
argparse==1.2.1
gunicorn==18.0
itsdangerous==0.23
wsgiref==0.1.2
- - - - - -
vi hello_test.py
import unittest
import hello
class HelloTestCase(unittest.TestCase):
def setUp(self):
hello.app.config['TESTING'] = True
self.app = hello.app.test_client()
# def tearDown(self):
def test_index_route(self):
response = self.app.get('/', content_type='text/plaintext')
self.assertEqual(200, response.status_code)
self.assertEqual('Hello!', response.data)
def test_not_found_404(self):
response = self.app.get('/doesnotexist')
self.assertEqual(404, response.status_code)
if __name__ == '__main__':
unittest.main()
- - - - - -
vi hello.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello!'
if __name__ == '__main__':
app.run()
# "python hello_test.py" runs the unit tests (should all pass with . . ) and make a hello.pyc
# "python hello.py" runs Flask(Werkzeug) dev server on 127.0.0.1:5000 , for external access: app.run(host='0.0.0.0')
# "foreman start" uses the Heroku Toolbelt and the Procfile to run the app, localhost:5000 , control + C to quit
- - - - - -
# prevent binaries from being saved in the repository
echo -e "venv\n*.pyc" >> .gitignore
git init
git add .
git commit -m "part1: tdd hello flask app on heroku (with gitignore, requirements, Procfile)"
[master (root-commit) 192ec69] part1: tdd hello flask app on heroku (with gitignore, requirements, Procfile)
5 files changed, 47 insertions(+)
create mode 100644 .gitignore
create mode 100644 Procfile
create mode 100644 hello.py
create mode 100644 hello_test.py
create mode 100644 requirements.txt
- - - - - -
heroku apps:create REPONAME
git remote add all ssh://git@bitbucket.org/USERNAME/REPONAME.git
git remote set-url --add all git@heroku.com:REPONAME.git
git config --list
user.name=Your Name
user.email=yourname@example.com
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
remote.heroku.url=git@heroku.com:REPONAME.git
remote.heroku.fetch=+refs/heads/*:refs/remotes/heroku/*
remote.all.url=git@heroku.com:REPONAME.git
remote.all.url=ssh://git@bitbucket.org/USERNAME/REPONAME.git
remote.all.fetch=+refs/heads/*:refs/remotes/all/*
git push all master # Heroku first push takes a few minutes
Counting objects: 7, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 968 bytes, done.
Total 7 (delta 0), reused 0 (delta 0)
To ssh://git@bitbucket.org/USERNAME/REPONAME.git
* [new branch] master -> master
Initializing repository, done.
-----> Python app detected
-----> No runtime.txt provided; assuming python-2.7.6.
-----> Preparing Python runtime (python-2.7.6)
-----> Installing Setuptools (2.1)
-----> Installing Pip (1.5.4)
-----> Installing dependencies using Pip (1.5.4)
...
Successfully installed Flask Jinja2 MarkupSafe Werkzeug argparse gunicorn itsdangerous
Cleaning up...
-----> Discovering process types
Procfile declares types -> web
-----> Compressing... done, 30.4MB
-----> Launching... done, v3
http://tddflask.herokuapp.com deployed to Heroku
To git@heroku.com:tddflask.git
* [new branch] master -> master
curl http://REPONAME.herokuapp.com # Hello!
wget -q -O- http://REPONAME.herokuapp.com # Hello!
deactivate # turns off virtualenv and returns to normal OS python environment
# git pull all master
- - - - - -
- - - - - -
- - - - - -
# git remote add origin git@bitbucket.org:USERNAME/REPONAME.git
echo -e "venv\n__pycache__\n*.pyc\n*~\n*.swp\n*.egg\n*.egg-info" >> .gitignore
git config --list
- - -
#echo -e "# This is my README" >> README.md
#git add README.md .gitignore
#git commit -m "Adding a README"
#git push -u origin master
- - -
OR download an existing- repo...
git clone git@bitbucket.org:USERNAME/REPONAME.git
git clone git@heroku.com:tddflask.git -o heroku
- - -
CRAZY ERRORS? from old pip? argparse dependency can't be met?!?!
# answer: Heroku upgrade to pip 1.5.2 which introduced a bug, they upgraded again to 1.5.4 (and fixed a typo)
# https://github.com/heroku/heroku-buildpack-python/commits/master
-----> Installing Pip (1.5.2)
-----> Installing dependencies using Pip (1.5.2)
Downloading/unpacking Flask==0.10.1 (from -r requirements.txt (line 1))
...
Error with argparse and setup.py
- - -
TypeError: 'int' object is not callable
Flask types that are returned need some help, like:
from flask import jsonify
def build_response(status_code, message_dict):
response = jsonify(message_dict)
response.status_code = status_code
return response