Skip to content

Commit

Permalink
1.0.3
Browse files Browse the repository at this point in the history
Improved Help output (#11)
Added alias "init" for "initialize" command (#21)
Updated initialize method to print out more accurate error messages (#20)
Fixed a supervisord bug (#18)
Added a check wether docker is installed before initializing. (#15)
  • Loading branch information
Daniel Jilg committed May 30, 2017
1 parent 8fa6af8 commit e28fb5f
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 28 deletions.
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Then run it using the ``jonah <command>`` syntax. To start a new project, run

::

> jonah initialize your_new_project
> jonah init your_new_project

Jonah will then create a new directory called ``your_new_project`` in the current working directory and create an empty
Django project inside.
Expand Down Expand Up @@ -110,7 +110,7 @@ Full List of Commands
===================== ==================================================================================================
Command Description
===================== ==================================================================================================
``initialize`` Initialize a new jonah project in the current directory
``init`` Initialize a new jonah project in the current directory
``build`` Build the image.
``cleanbuild`` Build the image from scratch instead of relying on cached layers.
``develop`` Run dev server
Expand All @@ -132,7 +132,7 @@ To get a full list of commands, run ``deploy.py`` without any arguments.
Configuration
-------------

The ``initialize`` command will create a number of configuration files. Here is what they are used for:
The ``init`` command will create a number of configuration files. Here is what they are used for:

============================= ==========================================================================================
File Description
Expand Down
65 changes: 41 additions & 24 deletions jonah/jonah.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import subprocess
import shlex
import shutil
import textwrap

# requests might now be available. Don't run the "deploy" command in this case
try:
Expand Down Expand Up @@ -43,9 +44,9 @@ def __init__(self, config_file_path=os.path.join(os.getcwd(), 'jonah.ini')):
self.working_dir = os.getcwd()

@staticmethod
def __dir__():
return ['initialize', 'build', 'cleanbuild', 'develop', 'compilemessages', 'stop', 'reload', 'shell', 'tag',
'test', 'stage', 'deploy', 'direct_deploy', 'clean']
def __dir__(**kwargs):
return ['initialize', 'init', 'build', 'clean_build', 'develop', 'compilemessages', 'stop', 'reload', 'shell', 'tag',
'test', 'stage', 'deploy', 'direct_deploy', 'cleanup']

def run(self, cmd, cwd=None, exceptions_should_bubble_up=False, spew=False):
"""Run a shell command"""
Expand Down Expand Up @@ -100,15 +101,19 @@ def backspace(self, string_to_escape):
# User Actions #####################################################################################################

def check_docker(self):
"""Check that the Docker executable is available on the user's system"""
"""Check that the Docker executable is available on the user's system."""
try:
docker_version_output = self.run('docker version', exceptions_should_bubble_up=True)
except (subprocess.CalledProcessError, OSError) as e:
self.printout('Error while calling "docker" executable. Is Docker installed on your system?')
exit(1)

def initialize(self):
"""Initialize a new jonah project in the current directory"""
"""(alias for init)"""
return self.init()

def init(self):
"""Initialize a new jonah project in the current directory."""
self.check_docker()

if len(sys.argv) > 2 and sys.argv[2] != 'debug':
Expand All @@ -133,7 +138,7 @@ def initialize(self):
try:
shutil.copytree(support_files_dir, os.path.join(self.working_dir, project_name))
except (OSError, FileExistsError):
print('A directory called "{}" already exists. Please choose another directory name.'.format(project_name))
print(sys.exc_info()[1])
return
self.printout('OK')

Expand All @@ -155,7 +160,7 @@ def initialize(self):
os.chdir(old_cwd)

def build(self, environment=develop, clean=False):
"""Build the image"""
"""Build the image."""
if self.already_built:
return

Expand Down Expand Up @@ -190,12 +195,12 @@ def build(self, environment=develop, clean=False):
print("\nBuild failed with '{}'".format(lastline.strip()))
self.already_built = True

def cleanbuild(self, environment=develop):
"""Build the image from scratch"""
def clean_build(self, environment=develop):
"""Build the image from scratch instead of relying on cached layers."""
self.build(environment=environment, clean=True)

def stop(self):
"""Stop a previously running development server"""
"""Stop a previously running development server."""
self.printout("Stopping previously started containers... ", False)
image_name = self.get_configuration(DOCKER_IMAGE_NAME, develop)
container_ids = self.run('docker ps -q --filter=ancestor=%s' % image_name).split('\n')
Expand All @@ -206,7 +211,7 @@ def stop(self):
self.printout("OK")

def develop(self):
"""Run dev server"""
"""Run development server that listens on port 80."""
self.build(develop)
self.printout("Starting dev server... ", False)
output = self.run('docker run -d -p 80:80 --env DJANGO_PRODUCTION=false --env ROOT_PASSWORD='
Expand All @@ -216,13 +221,13 @@ def develop(self):
self.printout("OK")

def reload(self):
"""Reload Django process on dev server"""
"""Reload the Django process on the development server."""
self.printout("Reloading Django... ", False)
self.run('docker exec -t -i %s killall gunicorn' % self.get_container_id())
self.printout("OK")

def shell(self):
"""Get a shell on the dev server"""
"""Get a shell on the development server."""
container_id = self.get_container_id()
if len(container_id) < 1:
self.develop()
Expand All @@ -231,7 +236,7 @@ def shell(self):
subprocess.call(cmd, shell=True)

def tag(self, environment, tag=None):
"""Tag git version and docker version"""
"""Add git and docker tags."""
self.build()
if tag:
new_tag = tag
Expand All @@ -255,7 +260,7 @@ def tag(self, environment, tag=None):
return new_tag

def compilemessages(self):
"""Compile I18N Strings"""
"""Compile internationalization Strings."""
container_id = self.get_container_id()

self.printout("Running compilemessages... ", False)
Expand All @@ -275,7 +280,7 @@ def compilemessages(self):
self.printout("No messages found")

def test(self):
"""Build and run Unit Tests"""
"""Build and run Unit Tests."""
self.build()
self.compilemessages()
print('Beginning Unit Tests...')
Expand Down Expand Up @@ -318,25 +323,25 @@ def notify_docker_cloud(self, environment):
self.printout("OK")

def stage(self):
"""Deploy on test servers"""
"""Deploy to staging."""
self.deploy(environment=staging)

def direct_deploy(self, environment=production):
"""Deploy as tag master on production server, without warning. Danger Zone."""
"""Deploy as tag "master" on production server, without warning and without asking for confirmation. Danger Zone. """
self.build()
self.tag(environment, tag=environment)
self.push(environment)
self.notify_newrelic(environment)

def deploy(self, environment=production):
"""Deploy on production servers"""
"""Deploy to production. This command will ask you for a tag before pushing anything to the server."""
self.test()
tag = 'latest' if environment == staging else None
self.tag(environment, tag=tag)
self.direct_deploy(environment=environment)

def clean(self):
"""Delete exited containers, dangling images, and volumes"""
def cleanup(self):
"""Delete exited containers, dangling images, and volumes, in order to clean up hard drive space."""
self.printout("Deleting exited containers... ", False)
exited_containers = self.run("docker ps -a -q -f status=exited").split("\n")
for exited_container in exited_containers:
Expand Down Expand Up @@ -372,8 +377,20 @@ def clean(self):
getattr(d, sys.argv[1])()
else:
print("USAGE:")
print("\t%s <COMMAND>, where command is one of the following:\n" % sys.argv[0])
print(" {} <COMMAND>, where <COMMMAND> is one of the following:".format(sys.argv[0]))

commands = {}
commands["General"] = ['init', 'build', 'clean_build', 'cleanup']
commands["Development"] = ['develop', 'reload', 'shell', 'stop', 'test', 'compilemessages']
commands["Deployment"] = ['stage', 'deploy', 'tag', 'direct_deploy']

for groupname in commands.keys():
print("\n {}:".format(groupname))
for command_name in commands[groupname]:
command_help = textwrap.wrap(getattr(d, command_name).__doc__, 56)
print(" - {}\t{}".format(command_name.ljust(12), command_help[0]))
if len(command_help) > 1:
for additional_line in command_help[1:]:
print((" " * 20) + "\t" + additional_line)

for arg in dir(d):
print("\t" + arg.ljust(10) + "\t" + getattr(d, arg).__doc__)
exit(0)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
version='1.0.2',
version='1.0.3',

description='A way to pack your Django Development, Deployment and Testing into Docker',
long_description=long_description,
Expand Down

0 comments on commit e28fb5f

Please sign in to comment.