django-environments / bin / djenvlib

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
#
# django-environments initialization script
#
# Source this file, then use the "djp" command, or use "setproject"
# and "djenv" manually.
#

DJENV_HOME=$(cd -P "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)
DEFAULT_ROOT=/deploy
DEFAULT_REPOS=$DEFAULT_ROOT/repos

# Not to be called directly, see djenv below
function _djenv_init() {
    DJANGO_PROJECT=$1
    DJANGO_SETTINGS=$2

    # The root of the project should exist, of course
    [ -z "$PROJECT_ROOT" ] && \
        echo "Variable \$PROJECT_ROOT not set or empty" 1>&2 && return 1
    [ ! -d "$PROJECT_ROOT" ] && \
         echo "Variable \$PROJECT_ROOT '$PROJECT_ROOT'" \
            "does not point to a readable directory" 1>&2 && return 1

    # Check Django project as well
    [ -z "$DJANGO_PROJECT" ] && \
        echo "Argument \$DJANGO_PROJECT not set or empty" 1>&2 && return 1
    [ ! -d "$PROJECT_ROOT/$DJANGO_PROJECT" ] && \
        echo "Argument \$DJANGO_PROJECT '$DJANGO_PROJECT'" \
            "does not identify a readable directory within $PROJECT_ROOT" 1>&2 && \
        return 1

    # If no particular settings are defined, just use the "base" settings
    _OLD_DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
    if [ ! -z "$DJANGO_SETTINGS" ]; then
        DJANGO_SETTINGS_MODULE=$DJANGO_PROJECT.$DJANGO_SETTINGS
    else
        DJANGO_SETTINGS_MODULE=$DJANGO_PROJECT.settings
    fi

    # Set the PYTHONPATH to include PROJECT_ROOT
    _OLD_PYTHONPATH=$PYTHONPATH
    PYTHONPATH=$PROJECT_ROOT:$PROJECT_ROOT/lib:$PYTHONPATH

    export PROJECT_ROOT DJANGO_PROJECT PYTHONPATH DJANGO_SETTINGS_MODULE

    # Test settings import
    python -c "import $DJANGO_SETTINGS_MODULE" > /dev/null 2>&1
    if [ ! "$?" -eq 0 ]; then
        echo "Error importing settings $DJANGO_SETTINGS_MODULE" \
            "(PYTHONPATH: $PYTHONPATH)" 1>&2
        python -c "import $DJANGO_SETTINGS_MODULE"
        return 1
    fi

    # Test if the definitions in the settings match ours
    if [ ! -z "`get_django_setting :`" ]; then
        echo "Current Django settings generate output - debugging print statements maybe?" 1>&2
    else
        [ "$PROJECT_ROOT" != "`get_django_setting PROJECT_ROOT`" ] && \
            echo "\$PROJECT_ROOT in Django settings is different from shell" 1>&2 && \
            echo "\$PROJECT_ROOT: $PROJECT_ROOT" 1>&2 && \
            echo "settings.PROJECT_ROOT: `get_django_setting PROJECT_ROOT`" 1>&2 && \
            return 1
        [ "$DJANGO_PROJECT" != "`get_django_setting DJANGO_PROJECT`" ] && \
            echo "\$DJANGO_PROJECT in Django settings is different from shell" 1>&2 && \
            return 1
        [ "$PROJECT_ROOT/$DJANGO_PROJECT" != "`get_django_setting DJANGO_PROJECT_DIR`" ] && \
            echo "\$DJANGO_PROJECT_DIR in Django settings is different from" \
                "\$PROJECT_ROOT/\$DJANGO_PROJECT" 1>&2 && \
            return 1
    fi

    # We're good - let's set the prompt
    _OLD_DJENV_PS1=$PS1
    PS1=[$DJANGO_PROJECT]$PS1

    # Show environment info
    if [ "$SHOW_DJANGO_ENVIRONMENT" = "yes" ]; then
        echo "Welcome to `basename $PROJECT_ROOT`.$DJANGO_PROJECT. Environment info:"
        djenv
    fi

    # Check non-critical settings
    local dirs dir
    for dirs in TEMPLATE_DIRS FIXTURE_DIRS; do
        eval `import_django_settings $dirs`
        envname=DJANGO_$dirs
        for dir in ${!envname}; do
            [ ! -d $dir ] && \
                echo "Warning: \"$dir\" in settings.$dirs is not a valid directory" 1>&2
        done
    done

    return 0
}

# Register cleanup hook
function _djenv_register_cleanup() {
    local func
    for func in $*; do
        if [ -z "$DJENV_CLEANUP_FUNCTIONS" ]; then
            DJENV_CLEANUP_FUNCTIONS=$func
        else
            DJENV_CLEANUP_FUNCTIONS=$DJENV_CLEANUP_FUNCTIONS:$func
        fi
    done
}

# Verification functions

# Check if $PROJECT_ROOT is set
function _verify_project_root() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    return 0
}

# Check if $DJANGO_PROJECT is set
function _verify_django_project() {
    [ -z "$DJANGO_PROJECT" ] && echo "No \$DJANGO_PROJECT" 1>&2 && return 1
    return 0
}

# Check if both $PROJECT_ROOT and $DJANGO_PROJECT are set
function _verify_project_root_and_django_project() {
    _verify_project_root || return 1
    _verify_django_project || return 1
    return 0
}

# Check if $DJANGO_SETTINGS_MODULE is set
function _verify_django_settings_module() {
    [ -z "$DJANGO_SETTINGS_MODULE" ] && \
        echo "No \$DJANGO_SETTINGS_MODULE" 1>&2 && return 1
    return 0
}

# Some useful functions

# Exit current project
function djexit() {
    [ -z "$DJANGO_PROJECT" ] && return 1

    # Restore prompt
    if [ ! -z "$_OLD_DJENV_PS1" ]; then
        PS1=$_OLD_DJENV_PS1
        unset _OLD_DJENV_PS1
    fi

    # Restore django settings
    if [ ! -z "$_OLD_DJANGO_SETTINGS_MODULE" ]; then
        DJANGO_SETTINGS_MODULE=$_OLD_DJANGO_SETTINGS_MODULE
    else
        unset DJANGO_SETTINGS_MODULE
    fi

    # Restore python path
    if [ ! -z "$_OLD_PYTHONPATH" ]; then
        PYTHONPATH=$_OLD_PYTHONPATH
    else
        unset PYTHONPATH
    fi

    # Call the registerd cleanup functions
    _IFS=$IFS
    IFS=:
    local func
    for func in $DJENV_CLEANUP_FUNCTIONS; do
        $func
    done
    IFS=$_IFS
}

# Change django project
# Example:
# djenv # Print current environment settings
# or
# djenv www # Use default settings
# or
# djenv www settings.env.local # Use specific settins
function djenv() {
    # Environment info
    if [ -z "$1" ]; then
        echo "PROJECT_ROOT: '$PROJECT_ROOT'"
        echo "DJANGO_PROJECT: '$DJANGO_PROJECT'"
        echo "DJANGO_SETTINGS_MODULE: '$DJANGO_SETTINGS_MODULE'"
        echo "PYTHONPATH: '$PYTHONPATH'"
        return
    fi

    # Help
    if [ "$1" = "-h" -o "$1" = "--help" ]; then
        echo "Usage: djenv [Django project [Django settings]]"
        return
    fi

    # Check $PROJECT_ROOT
    if [ -z "$PROJECT_ROOT" ]; then
        echo "Variable \$PROJECT_ROOT not set or empty" 1>&2
        return 1
    fi

    # Exit current environment (if any)
    djexit

    # Initialize
    _djenv_init $*

    # On error, use djexit for cleanup
    [ ! $? -eq 0 ] && djexit && return 1

    # Change working directory
    cdroot

    return 0
}

# Get a django setting from the current or specified settings module
#
# Example:
# get_django_setting LANGUAGE_CODE
#
# Returns the second argument if setting cannot be found. The third
# argument is an alternative settings configuration, for example:
# get_django_setting DEBUG 'False' www.settings.env.prd
#
# Be aware that the settings file should not print anything to
# stdout for this to work!
function get_django_setting() {
    _verify_project_root || return 1
    [ -z "$1" ] && \
        echo "Usage: get_django_setting <setting-name>" \
            "[default-value] [settings-module]" 1>&2 && \
        return 1

    local settings_module
    if [ ! -z "$3" ]; then
        settings_module=$3
    else
        _verify_django_settings_module || return 1
        settings_module=$DJANGO_SETTINGS_MODULE
    fi

    DJANGO_SETTINGS_MODULE=$settings_module python << EOF
try:
    from django.conf import settings
except ImportError:
    print '$2'
    raise
else:
    try:
        print getattr(settings, '`echo $1 | tr 'a-z' 'A-Z'`', '')
    except KeyError:
        print '$2'
EOF
}

# Import django settings into the shell environment
#
# When using, set $IFS to empty (this is needed because we eval
# the output of the python-generated shell script code):
# IFS=''
#
# Then use the function as follows:
# eval `import_django_settings` # all settings
# or
# eval `import_django_settings ADMIN` # all settings starting with 'ADMIN'
#
# Tuples and lists items are separated with newlines, so set $IFS
# to newline to get to those:
# IFS='
# '
# Note that all variables are prefixed with value of $prefix, 'DJANGO_'.
function import_django_settings() {
    _verify_project_root || return 1
    _verify_django_settings_module || return 1

    prefix=DJANGO_
    python << EOF
import $DJANGO_SETTINGS_MODULE as settings
from types import TupleType, ListType, DictType

def escape(value):
    return str(value).replace('"', '\\\\"')

settings = settings.__dict__.items()
settings.sort()
for name, value in settings:
    if name.find('__') == -1 and name.find('$1') == 0:
        if type(value) in (TupleType, ListType):
            print '$prefix%s="' % name
            for item in value:
                print escape(item)
            print '"'
        elif type(value) == DictType:
            print '$prefix%s="' % name
            for name, value in value.items():
                print '%s:%s' % (name, escape(value))
            print '"'
        else:
            print '$prefix%s="%s"' % (name, escape(value))
EOF
}

# Set the project root to either current or specified directory
setproject() {
    if [ ! -z "$1" ]; then
        cd $1
        [ ! $? -eq 0 ] && return 1
    fi
    djexit
    export PROJECT_ROOT=`pwd`

    local projects=`show_django_projects`
    if [ ! -z "$projects" ]; then
        echo "Available Django projects (use the djenv command to select):" 1>&2
        echo $projects 1>&2
    else
        echo "Warning: no django projects found" 1>&2
    fi
}

# Change directory to project root
function cdroot() {
    _verify_project_root || return 1

    cd $PROJECT_ROOT/$1
}

# Change directory to project lib directory
function cdlib() {
    _verify_project_root || return 1

    cd $PROJECT_ROOT/lib/$1
}

# Install dependencies for a given environment (first argument);
# the file with the dependencies should be in the requirements
# directory and its name should have the format 'libs-<environment>.txt'
function pipup() {
    _verify_project_root || return 1
    [ -z "$1" ] && \
        echo "Usage: pipup <requirements-identifier> [pip options]" 1>&2 && \
        return 1

    local requirements=$1
    shift
    pip install --requirement=$PROJECT_ROOT/requirements/libs-$requirements.txt $*
}

function _externals() {
    while true; do
        read source destination

        if [ -z "$source" ]; then
            return
        fi
        revision=`echo $source | sed -E -n 's/([^#]*)#(.*)/\2/p'`
        source=`echo $source | cut -f1 -d#`
        if [ -z "$destination" ]; then
            destination=`basename $source`
        fi
        if [ -L "$destination" ]; then
            echo "Not updating symbolic link $destination"
            return
        fi
        if [ ! -d $destination ]; then
            hg clone --noupdate $source $destination
        else
            hg pull --repository $destination
        fi
        hg up --repository $destination $revision
    done
}

# Install or update dependencies in the externals directory for a given
# environment (first argument); the file with the dependencies should be
# in the requirements directory and its name should have the format
# 'externals-<environment>.txt', containing lines with the following format:
# repo-location[#revision] [destination]
# For instance:
# ../../lib-core
# ../../lib-tools#44 tools
# http://user@host/hg/lib-tools#44
#
# Relative paths are evaluated from the externals directory. Only
# Mercurial is supported.
function externalsup() {
    _verify_project_root || return 1
    [ -z "$1" ] && \
        echo "Usage: externalsup <requirements-identifier>" 1>&2 && \
        return 1

    local requirements=$1
    local requirements_file=$PROJECT_ROOT/requirements/externals-$requirements.txt
    [ ! -f "$requirements_file" ] && \
        echo "Requirements file $requirements_file not found" 1>&2 && \
        return 1

    (
        if [ ! -d $PROJECT_ROOT/externals ]; then
            mkdir $PROJECT_ROOT/externals
        fi
        cd $PROJECT_ROOT/externals
        grep -v '^#' $requirements_file | _externals
    )
}

# Use compileall to compile all .py files - handy for web server
# environments where the server user often has no write access to the
# .pyc files / directories
function pycompile() {
    _verify_project_root || return 1

    python -c "import compileall; compileall.compile_dir('$PROJECT_ROOT')"
    removeorphanpycs
}

# Remove .pyc files without a corresponding .py the fast way
function removeorphanpycs() {
    _verify_project_root || return 1

    # Use the GNU extension --no-run-if-empty on Linux, in case no files are found
    if [ `uname` = Linux ]; then
        local extra_xargs_args=--no-run-if-empty
    fi

    # Pipe through bash because this implementation uses process substitution,
    # and you don't want to export functions using that since /bin/sh will choke
    # on them with a syntax error because process substitution is not available in
    # POSIX mode
    echo "diff --old-line-format= --new-line-format=%L --unchanged-group-format= \
        <(find -H $PROJECT_ROOT -name \*.py | sort) \
        <(find -H $PROJECT_ROOT -name \*.pyc | sed 's/c$//' | sort) | \
        sed 's/$/c/' | xargs $extra_xargs_args rm -v" | bash
}

# Remove all directories in the project that contain only the specified file.
# Experimental.
function cleanupdirs() {
    [ -z "$1" ] && echo "Usage: cleanupdirs <filename>" 1>&2 && return 1

    local file=$1

    # Use the GNU extension --no-run-if-empty on Linux, in case no files are found
    if [ `uname` = Linux ]; then
        local extra_xargs_args=--no-run-if-empty
    fi

    for dir in `find $PROJECT_ROOT -type f -name $file | \
                xargs -n 1 $extra_xargs_args dirname`; do
        if [ `ls -a $dir | grep -v $file | wc -l` = 2 ]; then
            rm $dir/$file
            rmdir $dir
        fi
    done
}

# Remove all empty directories in the project
function removeemptydirs() {
    _verify_project_root || return 1

    # Make directories as empty as possible
    removeorphanpycs
    cleanupdirs .DS_Store
    # Then remove the totally empty ones
    find $PROJECT_ROOT -type d -empty -delete
}

# Change directory to Django project
function cdjango () {
    _verify_project_root_and_django_project || return 1

    cd $PROJECT_ROOT/$DJANGO_PROJECT/$1
}

# Forget manage.py, django-admin.py respects our settings!
function djadmin() {
    django-admin.py $*
}

# Get network IP address
function get_network_ip() {
    python << EOF
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('google.com', 80))
print s.getsockname()[0]
EOF
}

# Run development server on settings.LOCAL_SERVER_PORT, and restart
# automatically. With -p as first argument, the server listens to
# the network IP address instead of localhost.
function runserver() {
    _verify_project_root_and_django_project || return 1

    if [ "$1" = -p ]; then
        local ip=`get_network_ip`:
        shift
    fi
    local port=`get_django_setting LOCAL_SERVER_PORT 8000`

    while true; do
        django-admin.py runserver $ip$port $*
        echo "Server exited - restarting" 1>&2
        sleep 1
    done
}

# Run test server on settings.LOCAL_SERVER_PORT, and restart automatically
function testserver() {
    _verify_project_root_and_django_project || return 1

    local port=`get_django_setting LOCAL_SERVER_PORT 8000`
    django-admin.py testserver --addrport=$port $*
}

# Open an URL in the default browser
function open_url() {
    if which xdg-open > /dev/null; then
        # Linux
        xdg-open "$@"
    else
        # Mac OS
        open "$@"
    fi
}

# Points the browser to the server running the current settings:
# http://localhost:<settings.LOCAL_SERVER_PORT>/$1
function djbrowse() {
    _verify_project_root_and_django_project || return 1
    _verify_django_settings_module || return 1

    local port=`get_django_setting LOCAL_SERVER_PORT 8000`
    open_url http://localhost:$port/$1
}

# Points the browser to the named virtual host for the current
# settings. Assumes Apache is running as reverse proxy; see
# create_apache_vhoste_conf.sh for more information.
function djvirtualbrowse() {
    _verify_project_root_and_django_project || return 1
    _verify_django_settings_module || return 1

    local domain=local
    [ ! -z "$DOMAIN" ] && domain=`echo $DOMAIN | cut -f1 -d" "`
    local django_settings_id=`echo $DJANGO_SETTINGS_MODULE | sed 's#.*\\.##'`
    local PROJECT=`basename $PROJECT_ROOT`

    open_url http://$django_settings_id.$DJANGO_PROJECT.$PROJECT.$domain/$1
}

# Clear the cache via the Django cache API
function clearcache() {
    python -c "from django.core.cache import cache; cache.clear()"
}

# Gracefully restart Apache if configuration test is succesful
function graceful() {
    sudo apachectl configtest && \
    sudo apachectl -k graceful && \
    echo "Apache restarted" 2>&1
}

# Make any Sphinx documentation. Provided arguments are passed to
# the make command, default is "dirhtml".
function makedocs() {
    if [ -d "$PROJECT_ROOT/doc" ]; then
        for makefile in `ls $PROJECT_ROOT/doc/*/Makefile`; do
            dir=`dirname $makefile`
            echo "Making docs in $dir..."
            if [ "$1" ]; then
                args=$*
            else
                args=dirhtml
            fi
            make --directory $dir $args
            if [ $? -ne 0 ]; then
                return 1
            fi
        done
    fi
}

# Returns all Django projects that have a locale directory.
function localized_django_projects() {
    _verify_project_root || return 1

    cdroot
    for directory in `find * -type d -name locale`; do
        directory=`dirname $directory`
        if [ -f $directory/models.py -o -f $directory/models/__init__.py ];
        then
            echo $directory
        fi
    done
}

# Deploy the current Django application (and most of the project).
function deploy() {
    _verify_project_root_and_django_project || return 1
    _verify_django_settings_module || return 1

    echo ">>> Updating virtualenv..." 2>&1 && pipup prd > /dev/null && \
        if [ -f "$PROJECT_ROOT/requirements/externals-prd.txt" ]; then
            echo ">>> Updating externals..." 2>&1 && externalsup prd
        else
            echo ">>> No production externals defined" 2>&1
        fi && \
        echo 2>&1 && echo ">>> Compiling Python code..." 2>&1 && pycompile > /dev/null && \
        echo ">>> Removing orphan .pyc files..." 2>&1 && removeorphanpycs > /dev/null && \
        echo ">>> Validating models..." 2>&1 && djadmin validate && \
        echo 2>&1 && echo ">>> Compiling messages..." 2>&1 && \
            for dir in `localized_django_projects`; do
                (cdroot $dir && djadmin compilemessages)
            done && \
        echo 2>&1 && echo ">>> Making documentation..." 2>&1 && makedocs && \
        echo 2>&1 && echo ">>> Collecting static files..." 2>&1 && djadmin collectstatic --noinput && \
        echo 2>&1 && echo ">>> Setting permissions..." 2>&1 && set_permissions.sh && \
        echo 2>&1 && echo ">>> Clearing cache..." 2>&1 && clearcache && \
        echo ">>> Restarting server..." 2>&1 && graceful && \
        echo 2>&1 && echo "Ok." 2>&1 && \
        return 0

    echo 2>&1 && echo "Errors encountered - deploy aborted." 2>&1
}

# XXX
function bootstrapproject() {
    _verify_project_root || return 1

    # Standard directories
    for dir in bin db doc externals lib log media requirements static tmp; do
        if [ ! -e $PROJECT_ROOT/$dir ]; then
            mkdir $PROJECT_ROOT/$dir
        fi
    done

    # Symlink to django-environments/etc
    [ ! -L $PROJECT_ROOT/etc ] && \
        ln -s externals/django-environments/etc $PROJECT_ROOT

    # Django is a basic requirement
    local libs=$PROJECT_ROOT/requirements/libs-prd.txt
    [ ! -e $libs ] && echo "Django" > $libs
    pipup prd || return 1

    # Get django-environments as an external
    local externals=$PROJECT_ROOT/requirements/externals-prd.txt
    [ ! -e $externals ] && echo $DJENV_HOME > $externals
    externalsup prd || return 1
}

# XXX
function bootstrapdjangoproject() {
    [ -z "$1" ] && \
        echo "Usage: bootstrapdjangoproject <Django project>" 1>&2 && return 1
    _verify_project_root || return 1

    local path=$PROJECT_ROOT
    for dir in $1 settings env; do
        path=$path/$dir
        if [ ! -d $path ]; then
            mkdir $path
            touch $path/__init__.py
        fi
    done
    echo "from .generic import *" > \
        $PROJECT_ROOT/$1/settings/__init__.py
    echo "from etc.settings import *" > \
        $PROJECT_ROOT/$1/settings/generic.py
    echo "from .. import *" > \
        $PROJECT_ROOT/$1/settings/env/local.py
}

# Start working on a project (first argument, optional second
# argument is the Django project, default "www"), automatically
# activating the virtualenv with the name of the project and selecting
# "settings.env.local". Virtualenv and empty project are created if
# they do not yet exist.
function djp() {
    [ -z "$1" ] && \
        echo "Usage: djp <project> [Django project]" 1>&2 && return 1

    # Virtualenv
    [ -z "$PIP_VIRTUALENV_BASE" ] && PIP_VIRTUALENV_BASE=$DEFAULT_ROOT/virtualenvs
    if [ ! -d $PIP_VIRTUALENV_BASE/$1 ]; then
        echo "Creating virtualenv $1 in $PIP_VIRTUALENV_BASE" 1>&2
        virtualenv $PIP_VIRTUALENV_BASE/$1
        if [ $? -ne 0 ]; then
            echo "Error creating virtualenv $1" 1>&2
            return 1
        fi
    fi
    source $PIP_VIRTUALENV_BASE/$1/bin/activate
    if [ $? -ne 0 ]; then
        echo "Error activating virtualenv $1" 1>&2
        return 1
    fi

    # Project
    [ -z "$REPOS" ] && REPOS=$DEFAULT_REPOS
    if [ ! -d $REPOS/$1 ]; then
        mkdir $REPOS/$1
        local project_created=true
    fi
    setproject $REPOS/$1 > /dev/null 2>&1
    if [ $? -ne 0 ]; then
        echo "Error changing project to $1" 1>&2
        return 1
    fi
    if [ $project_created ]; then
        bootstrapproject
        if [ $? -ne 0 ]; then
            echo "Error bootstrapping project" 1>&2
            return 1
        fi
    fi

    # Django project
    local django_project=$2
    [ -z "$django_project" ] && django_project=www
    if [ ! -e $PROJECT_ROOT/$django_project/settings/env/local.py ]; then
        bootstrapdjangoproject $django_project
    fi
    djenv $django_project settings.env.local
    if [ $? -ne 0 ]; then
        echo "Error setting Django project to $django_project" 1>&2
        return 1
    fi

    # Shell search path (virtualenv manages PATH for us)
    for bindir in $PROJECT_ROOT/*/*/bin $PROJECT_ROOT/*/bin $PROJECT_ROOT/bin; do
        if [ -d $bindir ]; then
            PATH=$bindir:$PATH
        fi
    done
}

# Clean up the environment
function _djenv_cleanup () {
    unset DJANGO_PROJECT DJANGO_SETTINGS \
        _OLD_DJANGO_SETTINGS_MODULE _OLD_PYTHONPATH \
        DJANGO_TEMPLATE_DIRS DJANGO_FIXTURE_DIRS
}

# Use _djenv_register_cleanup to register your own cleanup functions
_djenv_register_cleanup _djenv_cleanup

function show_projects() {
    (
        [ -z "$REPOS" ] && REPOS=$DEFAULT_REPOS
        cd $REPOS
        shopt -s nullglob
        find * -type d -maxdepth 0
    )
}

function show_django_projects() {
    [ ! -z "$1" ] || _verify_project_root || return 1

    (
        [ ! -z "$1" ] && PROJECT_ROOT=$REPOS/$1
        cd $PROJECT_ROOT
        shopt -s nullglob
        for file in */settings/__init__.py; do
            echo $file
        done
    ) | sed 's#/settings/__init__.py##' | sort
}

function show_django_settings() {
    _verify_project_root || return 1

    (
        cd $PROJECT_ROOT/$1
        shopt -s nullglob
        for file in settings/env/*.py; do
            echo $file
        done
    ) | grep -v '/__init__.py$' | sed 's#settings/env/#settings.env.#' | \
        sed 's#\.py$##' | sort
}

# Export functions so they can be used in shell scripts
export -f _djenv_init _djenv_register_cleanup _verify_project_root _verify_django_project \
    _verify_project_root_and_django_project _verify_django_settings_module \
    djexit djenv get_django_setting import_django_settings cdroot cdlib \
    pipup _externals externalsup pycompile removeorphanpycs cleanupdirs removeemptydirs \
    cdjango djadmin get_network_ip runserver testserver open_url djbrowse djvirtualbrowse \
    clearcache graceful localized_django_projects makedocs deploy \
    _djenv_cleanup show_projects show_django_projects show_django_settings

#
# Tab completion
#

_projects_complete() {
    local current="${COMP_WORDS[COMP_CWORD]}"
    local previous="${COMP_WORDS[COMP_CWORD - 1]}"

    if [ $COMP_CWORD -eq 1 ]; then
        COMPREPLY=($(compgen -W "`show_projects`" -- ${current}))
    elif [ $COMP_CWORD -eq 2 ]; then
        COMPREPLY=( $(compgen -W "`show_django_projects $previous`" -- ${current}) )
    fi
}

_django_projects_complete() {
    local current="${COMP_WORDS[COMP_CWORD]}"
    local previous="${COMP_WORDS[COMP_CWORD - 1]}"

    if [ $COMP_CWORD -eq 1 ]; then
        COMPREPLY=($(compgen -W "`show_django_projects`" -- ${current}))
    elif [ $COMP_CWORD -eq 2 ]; then
        COMPREPLY=( $(compgen -W "`show_django_settings $previous`" -- ${current}) )
    fi
}

_cdroot_complete() {
    _verify_project_root || return 1

    COMPREPLY=($(cdroot && compgen -d -- "${2}"))
}

_cdlib_complete() {
    _verify_project_root || return 1

    COMPREPLY=($(cdlib && compgen -d -- "${2}"))
}

_cdjango_complete() {
    _verify_django_project || return 1

    COMPREPLY=($(cdjango && compgen -d -- "${2}"))
}

function show_requirements_environments() {
    _verify_project_root || return 1

    (
        cd $PROJECT_ROOT/requirements
        shopt -s nullglob
        for file in $1-*.txt; do
             echo $file
        done
    ) | sed "s#$1-##" | sed 's#.txt##' | sort
}

_pipup_complete() {
    _verify_project_root || return 1

    COMPREPLY=($(compgen -W "`show_requirements_environments libs`" -- "${2}" ))
}

_externalsup_complete() {
    _verify_project_root || return 1

    COMPREPLY=($(compgen -W "`show_requirements_environments externals`" -- "${2}" ))
}

complete -o nospace -F _cdroot_complete -S/ cdroot
complete -o nospace -F _cdlib_complete -S/ cdlib
complete -o nospace -F _cdjango_complete -S/ cdjango
complete -F _projects_complete djp
complete -F _django_projects_complete djenv
complete -F _pipup_complete pipup
complete -F _externalsup_complete externalsup
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.