Просмотр исходного кода

Merge pull request #1083 from rafalp/consolidate-scripts

Add ./dev bash script for dev actions
Rafał Pitoń 6 лет назад
Родитель
Сommit
48020609e5

+ 1 - 0
Dockerfile

@@ -4,6 +4,7 @@
 FROM python:3.5
 
 ENV PYTHONUNBUFFERED 1
+ENV IN_MISAGO_DOCKER 1
 ENV PATH "$PATH:/srv/misago"
 
 # Install dependencies in one single command/layer

+ 0 - 1
MANIFEST.in

@@ -4,7 +4,6 @@ include README.rst
 include requirements.txt
 prune devproject
 prune docs
-prune extras
 prune testproject
 graft misago
 global-exclude __pycache__

+ 6 - 21
README.rst

@@ -86,31 +86,16 @@ If you are looking into using Misago to run live forum, you are absolutely invit
 Development
 ===========
 
-Preferred way to setup Misago for local development is with `Docker <https://www.docker.com/community-edition#/download>`_, which makes it easy to spin up arbitrary number of instances running different code with separate databases and dependencies one besides the other with just three terminal commands.
+Preferred way to run Misago development instances on your machine is with `Docker <https://www.docker.com/community-edition#/download>`_, which makes it easy to spin up arbitrary number of instances running different code with separate databases and dependencies besides each other.
 
-To start, clone repository to your machine and then run following commands::
+To start, clone the repository and run ``./dev init`` command in your terminal. This will build necessary docker containers, install python dependencies, initialize the database and create development project for you. After command does its magic, you will be able to start development server using the ``docker-compose up`` command.
 
-   docker-compose build
-   docker-compose run --rm misago initdev
-   docker-compose up
+After development server starts, visit the ``http://127.0.0.1:8000/`` in your browser to see your Misago installation.
 
-Those commands will install necessary dependencies, create new Misago project ``devproject`` that you may use for development as well as start Django developer server, enabling you to visit ``127.0.0.1:8000``
-in your browser and see the forum index. You should now be able to sign in with the superuser account, using ``Admin`` username and ``password`` password.
-
-Admin Control Panel is available under the ``127.0.0.1:8000/admincp/`` url.
-
-`manage.py` is available through Docker's `run` command::
-    
-    docker-compose run --rm misago python manage.py
-
-Docker also allows you to run tests suite::
-    
-    docker-compose run --rm misago python runtests.py
-
-If you'll ever want to destroy Docker setup because you no longer need it, run this command::
-
-    docker-compose down
+Admin Control Panel is available under the ``http://127.0.0.1:8000/admincp/`` address. To log in to it use ``Admin`` username and ``password`` password.
 
+The ``./dev`` utility implements other features besides the ``init``. Run it without any arguments to get the list of available actions.
+ 
 
 Frontend
 --------

+ 0 - 6
cleansource

@@ -1,6 +0,0 @@
-#!/bin/bash
-
-yapf -ir ${1:-misago} -e '*/project_template/**/*.py' -e '*/conf/defaults.py'
-isort -rc ${1:-misago}
-pylint ${1:-misago}
-python pycodestyle.py ${1:-misago}

+ 375 - 0
dev

@@ -0,0 +1,375 @@
+#!/bin/bash
+# devctl is an utility script for automating some development tasks and actions.
+# To find out what options are available, run it without any arguments.
+
+# Text styles
+RED='\033[0;31m'
+BOLD=$(tput bold)
+NORMAL=$(tput sgr0)
+
+# Define dev paths
+# Those are paths to dirs and files created for dev project
+dev_paths=(
+    "./avatargallery"
+    "./devproject"
+    "./media"
+    "./static"
+    "./theme"
+    "./userdata"
+    "./cron.txt"
+    "./manage.py"
+)
+
+# Required ports
+# Some tasks test for those ports before continuing
+port_django=8000
+port_postgresql=5432
+
+required_ports=($port_postgresql $port_django)
+
+# Default superuser
+username="Admin"
+password="password"
+email="admin@example.com"
+
+# Utility functions used by action commands
+error() {
+    echo -e "${RED}Error:${NORMAL} $1"
+}
+
+require_in_docker() {
+    if [[ ! $IN_MISAGO_DOCKER = 1 ]]; then
+        error "This command can only be ran inside the running Misago docker container."
+        exit 1
+    fi
+}
+
+wait_for_db() {
+    require_in_docker
+
+    export PGPASSWORD=$POSTGRES_PASSWORD
+    RETRIES=10
+
+    until psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do
+        echo "Waiting for PostgreSQL to start, $((RETRIES--)) remaining attempts..."
+        sleep 2
+    done
+}
+
+# Check if user has docker-compose
+if [[ ! $IN_MISAGO_DOCKER = 1 ]]; then
+    if ! command -v docker-compose >/dev/null 2>&1; then
+        error "You need to have Docker installed to use this tool."
+        echo
+        echo "Docker release for your system can be downloaded for free from this page:"
+        echo "https://www.docker.com/get-started"
+        echo
+        exit 1
+    fi
+fi
+
+# Commands
+intro() {
+    echo "Usage: ./dev [arg] ..."
+    echo "Arguments grouped by type:"
+    echo
+    echo "Development project:"
+    echo
+    echo "    ${BOLD}init${NORMAL}              initialize new dev project for development, does nothing if project already exists."
+    echo "    ${BOLD}afterinit${NORMAL}         repeat help message displayed after init command is complete."
+    echo "    ${BOLD}remove${NORMAL}            if dev project exists, remove its files and docker containers."
+    echo "    ${BOLD}rebuild${NORMAL}           rebuild docker containers."
+    echo "    ${BOLD}reset${NORMAL}             run remove followed by init."
+    echo
+    echo "    Both init and rebuild args can be followed with any number of extra args and options that should be appended to docker-compose build."
+    echo
+    echo "Testing:"
+    echo
+    echo "    ${BOLD}test${NORMAL}              run tests suite."
+    echo "    ${BOLD}test module${NORMAL}       run tests suite in specified python module, eg. misago.users."
+    echo
+    echo "Translations:"
+    echo
+    echo "    ${BOLD}makemessages${NORMAL}      update translation files for \"en\" language."
+    echo "    ${BOLD}makemessages lang${NORMAL} update translation files for \"lang\" language."
+    echo "    ${BOLD}compilemessages${NORMAL}   compile translation files to \"mo\" format."
+    echo "    ${BOLD}txpull${NORMAL}            pull translations from Transifex."
+    echo "    ${BOLD}txpush${NORMAL}            push new source files to Transifex."
+    echo "    ${BOLD}txsync${NORMAL}            runs entire process of syncing translations with Transifex."
+    echo
+    echo "Shortcuts:"
+    echo
+    echo "    ${BOLD}manage.py${NORMAL}         runs \"python manage.py\" inside docker."
+    echo "    ${BOLD}bash${NORMAL}              starts bash session inside running Misago container."
+    echo "    ${BOLD}run${NORMAL}               runs \"docker-compose run --rm misago\"."
+    echo "    ${BOLD}psql${NORMAL}              runs psql connected to development database."
+    echo
+}
+
+# Handle invalid argument
+invalid_argument() {
+    echo -e "Invalid argument: ${RED}$1${NORMAL}"
+    echo "Please run this script without any arguments to see the list of available arguments."
+    exit 1
+}
+
+# Initialize new dev project
+init() {
+    for dev_path in "${dev_paths[@]}"; do
+        if [ -e $dev_path ]; then
+            error "Dev project already exists, or was not deleted completely."
+            echo
+            echo "Please use \"remove\" option to remove any possible remaining files and try again."
+            echo
+            exit 1
+        fi
+    done
+
+    for port in "${required_ports[@]}"; do
+        nc "127.0.0.1" "$port" < /dev/null
+        if [[ $? = "0" ]]; then
+            if [[ $port = $port_django ]]; then
+                error "Other application appears to already be running on http://127.0.0.1:8000"
+            elif [[ $port = $port_postgresql ]]; then
+                error "PostgreSQL appears to already be running on the port $port."
+                echo
+                echo "Misago runs its own PostgreSQL instance in the docker container and uses port $port to expose it to other programs."
+                echo "Please stop your PostgreSQL server and try again."
+                echo
+            fi
+            exit 1
+        fi
+    done
+    
+    docker-compose stop
+    docker-compose build --pull --force-rm "${@:2}"
+    docker-compose run --rm misago ./dev init_in_docker
+}
+
+# Initialization step that has to occur inside docker
+init_in_docker() {
+    require_in_docker
+    wait_for_db
+    # initialize django project
+    python misago/bin/misago-start-devproject.py
+    # move items of interest up one level
+    mv devproject/devproject devproject_tmp
+    mv devproject/avatargallery ./avatargallery
+    mv devproject/media ./media
+    mv devproject/userdata ./userdata
+    mv devproject/manage.py ./manage.py
+    rm -rf devproject
+    mv devproject_tmp devproject
+    # migrate the database
+    python manage.py migrate
+    # create superuser Admin with password "password"
+    python manage.py createsuperuser --username $username --email $email --password $password
+
+    # display after init message
+    echo
+    echo "================================================================================"
+    after_init_message
+}
+
+# After-init message
+after_init_message() {
+    echo
+    echo "You can now start the development server using:"
+    echo
+    echo "    docker-compose up"
+    echo
+    echo "Running server will be available in the browser under the http://127.0.0.1:8000 address."
+    echo
+    echo "Default superuser has been created with this username and password:"
+    echo
+    echo "Username:    $username"
+    echo "Password:    $password"
+    echo
+    echo "Development project directories:"
+    echo
+    echo "devproject        configuration files for development instance."
+    echo "media             user uploaded files."
+    echo "userdata          working directory for user data exports."
+    echo "avatargallery     example avatar gallery."
+    echo
+    echo "To connect to development database use following credentials:"
+    echo
+    echo "User:         misago"
+    echo "Password:     misago"
+    echo "Database:     misago"
+    echo "Host:         postgres"
+    echo "Port:         5432"
+    echo
+    echo "Note: development server must be running for connection to be possible."
+    echo
+}
+
+# Remove existing dev project
+remove() {
+    echo -e "${RED}Warning:${NORMAL} You are going remove current development project."
+    echo
+
+    will_delete_files=false
+    for dev_path in "${dev_paths[@]}"; do
+        if [ -e $dev_path ]; then
+            will_delete_files=true
+        fi
+    done
+    if [[ $will_delete_files = true ]]; then
+        echo "Following files and directories will be deleted:"
+        for dev_path in "${dev_paths[@]}"; do
+            if [ -e $dev_path ]; then
+                echo "  $dev_path"
+            fi
+        done
+        echo
+    fi
+
+    echo "Enter \"y\" to confirm:"
+
+    read confirmation
+    if [[ $confirmation = "y" ]]; then
+        for dev_path in "${dev_paths[@]}"; do
+            if [ -e $dev_path ]; then
+                echo "Removing $dev_path"
+                rm -rf $dev_path
+            fi
+        done
+
+        docker-compose stop
+        docker-compose down --remove-orphans
+    else
+        echo "Operation canceled."
+    fi
+}
+
+# Rebuild docker containers
+rebuild() {
+    docker-compose stop
+    docker-compose build --pull --force-rm "${@:2}"
+}
+
+# Run tests suite
+test() {
+    docker-compose run --rm misago runtests.py $1
+    docker-compose stop
+}
+
+# Make messages
+makemessages() {
+    docker-compose run --rm --no-deps misago ./dev makemessages_in_docker $1
+}
+
+# Docker part of makemessages
+makemessages_in_docker() {
+    require_in_docker
+    
+    echo "Extracting messages for $1 language:"
+    cd ./misago
+
+    echo "Processing .py and .html files..."
+    django-admin.py makemessages -l $1 -e html,txt,py > /dev/null
+
+    echo "Processing .js files..."
+    django-admin.py makemessages -l $1 -d djangojs > /dev/null
+}
+
+# Compile messages
+compilemessages() {
+    docker-compose run --rm --no-deps misago ./dev compilemessages_in_docker
+}
+
+# Docker part of compile messages
+compilemessages_in_docker() {
+    require_in_docker
+    cd ./misago
+    django-admin.py compilemessages
+}
+
+# Pull translation files from transifex
+txpull() {
+    tx pull
+    compilemessages
+}
+
+# Push translation sources to transifex
+txpush() {
+    tx push --source
+}
+
+# Shortcut for starting bash session in running container
+run_bash() {
+    docker exec -it misago_misago_1 bash
+}
+
+# Shortcut for docker-compose run --rm misago python manage.py
+run_managepy() {
+    docker-compose run --rm misago python manage.py "${@:2}"
+}
+
+# Shortcut for docker-compose run --rm misago...
+docker_run() {
+    docker-compose run --rm misago "${@:2}"
+}
+
+# Shortcut for psql
+run_psql() {
+    docker-compose run --rm misago ./dev psql_in_docker
+}
+
+# Docker part of psql shortcut
+psql_in_docker() {
+    wait_for_db
+    PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB
+}
+
+# Command dispatcher
+if [[ $1 ]]; then
+    if [[ $1 = "init" ]]; then
+        init $@
+    elif [[ $1 = "init_in_docker" ]]; then
+        init_in_docker
+    elif [[ $1 = "afterinit" ]]; then
+        after_init_message
+    elif [[ $1 = "remove" ]]; then
+        remove
+    elif [[ $1 = "reset" ]]; then
+        remove
+        init $@
+    elif [[ $1 = "rebuild" ]]; then
+        rebuild $@
+    elif [[ $1 = "test" ]]; then
+        test $2
+    elif [[ $1 = "makemessages" ]]; then
+        makemessages ${2:-en}
+    elif [[ $1 = "makemessages_in_docker" ]]; then
+        makemessages_in_docker $2
+    elif [[ $1 = "compilemessages" ]]; then
+        compilemessages
+    elif [[ $1 = "compilemessages_in_docker" ]]; then
+        compilemessages_in_docker
+    elif [[ $1 = "txpull" ]]; then
+        txpull
+    elif [[ $1 = "txpush" ]]; then
+        txpush
+    elif [[ $1 = "txsync" ]]; then
+        makemessages
+        txpush
+        txpull
+        compilemessages
+    elif [[ $1 = "bash" ]]; then
+        run_bash
+    elif [[ $1 = "manage.py" ]]; then
+        run_managepy $@
+    elif [[ $1 = "run" ]]; then
+        docker_run $@
+    elif [[ $1 = "psql" ]]; then
+        run_psql
+    elif [[ $1 = "psql_in_docker" ]]; then
+        psql_in_docker
+    else
+        invalid_argument $1
+    fi
+else
+    intro
+fi

+ 0 - 2
docker-compose.yaml

@@ -13,8 +13,6 @@ services:
     build: .
     command: python manage.py runserver 0.0.0.0:8000
     environment:
-      # Name of development project
-      - PROJECT_NAME=devproject
       # Postgres
       - POSTGRES_USER=misago
       - POSTGRES_PASSWORD=misago

+ 0 - 0
extras/__init__.py


+ 0 - 5
extras/config.py

@@ -1,5 +0,0 @@
-from django.utils.six.moves import configparser
-
-
-yapf = configparser.ConfigParser()
-yapf.read('.style.yapf')

+ 0 - 27
extras/createsuperuser.py

@@ -1,27 +0,0 @@
-"""
-Create superuser for the devproject
-"""
-
-import os
-import django
-
-os.environ['DJANGO_SETTINGS_MODULE'] = '{}.settings'.format(os.environ['PROJECT_NAME'])
-django.setup()
-
-from django.contrib.auth import get_user_model
-from django.utils.crypto import get_random_string
-
-User = get_user_model()
-
-
-if User.objects.count() == 0:
-    superuser = User.objects.create_superuser(
-        os.environ['SUPERUSER_USERNAME'],
-        os.environ['SUPERUSER_EMAIL'],
-        get_random_string(10), # set throwaway password
-        set_default_avatar=True,
-    )
-
-    # Override user's throwaway password with configured one
-    superuser.set_password(os.environ['SUPERUSER_PASSWORD'])
-    superuser.save()

+ 0 - 93
extras/fixabsoluteimports.py

@@ -1,93 +0,0 @@
-import os
-
-
-def walk_directory(root, dirs, files):
-    for file_path in files:
-        if 'project_template' not in root and file_path.lower().endswith('.py'):
-            clean_file(os.path.join(root, file_path))
-
-
-def clean_file(file_path):
-    py_source = file(file_path).read()
-    if 'misago.' in py_source:
-        package = file_path.rstrip('.py').split('/')
-
-        parse_file = True
-        save_file = False
-        cursor = 0
-
-        while parse_file:
-            try:
-                import_start = py_source.index('from ', cursor)
-                import_end = py_source.index(' import', import_start)
-                cursor = import_end
-
-                import_len = import_end - import_start
-                import_path = py_source[import_start:import_end].lstrip('from').strip()
-
-                if import_path.startswith('misago.'):
-                    cleaned_import = clean_import(package, import_path.split('.'))
-                    if cleaned_import:
-                        save_file = True
-
-                        cleaned_import_string = 'from {}'.format('.'.join(cleaned_import))
-                        py_source = ''.join((py_source[:import_start], cleaned_import_string, py_source[import_end:]))
-                        cursor -= import_end - import_start - len(cleaned_import_string)
-            except ValueError:
-                parse_file = False
-
-        if save_file:
-            with open(file_path, 'w') as package:
-                print file_path
-                package.write(py_source)
-
-
-def clean_import(package, import_path):
-    if len(package) < 2 or len(import_path) < 2:
-        # skip non-app import
-        return
-
-    if package[:2] != import_path[:2]:
-        # skip other app import
-        return
-
-    if package == import_path:
-        # import from direct child
-        return ['', '']
-
-    if package[:-1] == import_path[:-1]:
-        # import from sibling module
-        return ['', import_path[-1]]
-
-    if len(package) < len(import_path) and package == import_path[:len(package)]:
-        # import from child module
-        return [''] + import_path[len(package):]
-
-    if len(package) > len(import_path) and package[:len(import_path)] == import_path:
-        # import from parent module
-        return [''] * (len(package) - len(import_path) + 1)
-
-    # relative up and down path
-    relative_path = []
-
-    # find upwards path
-    overlap_len = 2
-    while True:
-        if package[:overlap_len + 1] != import_path[:overlap_len + 1]:
-            break
-        overlap_len += 1
-
-    relative_path += ([''] * (len(package) - overlap_len))[:-1]
-
-    # append eventual downwards path
-    if import_path[overlap_len:]:
-        relative_path += [''] + import_path[overlap_len:]
-
-    return relative_path
-
-
-if __name__ == '__main__':
-    for args in os.walk('../misago'):
-        walk_directory(*args)
-
-    print "\nDone! Don't forget to run isort to fix imports ordering!"

+ 0 - 160
extras/fixdictsformatting.py

@@ -1,160 +0,0 @@
-from __future__ import unicode_literals
-
-import sys
-
-from lib2to3.pytree import Node, Leaf
-from lib2to3.fixer_util import token, syms
-
-from yapf.yapflib import pytree_utils
-
-from django.utils import six
-
-from .config import yapf as yapf_config
-
-
-MAX_LINE_LENGTH = yapf_config.getint('style', 'column_limit') + 1
-
-
-def fix_formatting(filesource):
-    if not ('{'  in filesource and ('[' in filesource or '(' in filesource)):
-        return filesource
-
-    tree = pytree_utils.ParseCodeToTree(filesource)
-    for node in tree.children:
-        walk_tree(node, node.children)
-    return six.text_type(tree)
-
-
-def walk_tree(node, children):
-    for item in children:
-        if item.type == syms.dictsetmaker:
-            indent = item.parent.children[-1].column
-            walk_dict_tree(item, item.children, indent)
-        else:
-            walk_tree(item, item.children)
-
-
-def walk_dict_tree(node, children, indent):
-    for item in children:
-        prev = item.prev_sibling
-        if isinstance(prev, Leaf) and prev.value == ':':
-            if isinstance(item, Leaf):
-                if six.text_type(item).startswith("\n"):
-                    item.replace(Leaf(
-                        item.type,
-                        item.value,
-                        prefix=' ',
-                    ))
-            elif six.text_type(item).strip()[0] in ('[', '{'):
-                walk_tree(item, item.children)
-            else:
-                walk_dedent_tree(item, item.children, indent)
-
-
-def walk_dedent_tree(node, children, indent):
-    force_split_next = False
-    for item in children:
-        prev = item.prev_sibling
-        if not prev:
-            if isinstance(item, Leaf) and six.text_type(item).startswith("\n"):
-                prev = node.prev_sibling
-                next = node.next_sibling
-                final_length = 0
-
-                if prev and "\n" not in six.text_type(node).strip():
-                    final_length = prev.column + len(six.text_type(node).strip()) + 3
-
-                item.replace(Leaf(
-                    item.type,
-                    item.value,
-                    prefix=' ',
-                ))
-
-                if final_length and final_length > MAX_LINE_LENGTH:
-                    # tell next call to walk_dedent_tree_node that we need
-                    # different stringformat tactic
-                    force_split_next = True
-        elif isinstance(item, Node):
-            if node.type == syms.power:
-                for subitem in item.children[1:]:
-                    walk_dedent_power_node(subitem, subitem.children, indent)
-            else:
-                for subitem in item.children[1:]:
-                    walk_dedent_tree_node(subitem, subitem.children, indent, force_split_next)
-                    force_split_next = False
-
-
-def walk_dedent_tree_node(node, children, indent, force_split_next=False):
-    if six.text_type(node).startswith("\n"):
-        if isinstance(node, Leaf):
-            prev = node.prev_sibling
-            next = node.next_sibling
-
-            is_followup = prev and prev.type == token.STRING and node.type == token.STRING
-            if is_followup:
-                new_value = node.value
-
-                # insert linebreak after last string in braces, so its closing brace moves to new line
-                if not node.next_sibling:
-                    closing_bracket = node.parent.parent.children[-1]
-                    if not six.text_type(closing_bracket).startswith("\n"):
-                        new_value = "%s\n%s" % (node.value, (' ' * (indent + 4)))
-
-                node.replace(Leaf(
-                    node.type,
-                    new_value,
-                    prefix="\n%s" % (' ' * (indent + 8)),
-                ))
-            else:
-                if six.text_type(node).strip() in (')', '}'):
-                    new_prefix = "\n%s" % (' ' * (indent + 4))
-                else:
-                    new_prefix = "\n%s" % (' ' * (indent + 8))
-
-                node.replace(Leaf(
-                    node.type,
-                    node.value,
-                    prefix=new_prefix
-                ))
-        else:
-            for item in children:
-                walk_dedent_tree_node(item, item.children, indent)
-    elif isinstance(node, Leaf):
-        if node.type == token.STRING:
-            strings_tuple = node.parent.parent
-
-            prev = node.prev_sibling
-            next = node.next_sibling
-
-            is_opening = prev is None and six.text_type(strings_tuple).strip()[0] == '('
-            has_followup = next and next.type == token.STRING
-
-            if is_opening and has_followup:
-                node.replace(Leaf(
-                    node.type,
-                    node.value,
-                    prefix="\n%s" % (' ' * (indent + 8)),
-                ))
-            elif force_split_next:
-                node.replace(Leaf(
-                    node.type,
-                    "%s\n%s" % (node.value, (' ' * (indent + 4))),
-                    prefix="\n%s" % (' ' * (indent + 8)),
-                ))
-    else:
-        for item in children:
-            walk_dedent_tree_node(item, item.children, indent)
-
-
-def walk_dedent_power_node(node, children, indent):
-    if isinstance(node, Leaf):
-        if six.text_type(node).startswith("\n"):
-            node.replace(Leaf(
-                node.type,
-                node.value,
-                prefix=node.prefix[:-4],
-            ))
-    else:
-        for item in children:
-            walk_dedent_power_node(item, item.children, indent)
-

+ 0 - 53
extras/fixrelativeimports.py

@@ -1,53 +0,0 @@
-import os
-import re
-
-
-RELATIVE_IMPORT = re.compile(r'(from|import) \.\.+([a-z]+)?')
-
-
-def walk_directory(root, dirs, files):
-    for file_path in files:
-        if 'project_template' not in root and file_path.lower().endswith('.py'):
-            clean_file(os.path.join(root, file_path))
-
-
-def clean_file(file_path):
-    py_source = file(file_path).read()
-    if 'from ..' in py_source or 'import ..' in py_source:
-        print '====' * 8
-        print file_path
-        print '====' * 8
-
-        package = file_path.rstrip('.py').split('/')
-
-        def replace_import(matchobj):
-            prefix, suffix = matchobj.group(0).split()
-            return '{} {}'.format(prefix, clean_import(package, suffix))
-
-        py_source = RELATIVE_IMPORT.sub(replace_import, py_source)
-
-        #print py_source
-        with open(file_path, 'w') as package:
-            print file_path
-            package.write(py_source)
-
-
-def clean_import(package, match):
-    path = match[1:]
-
-    import_path = package[:]
-    while match and match[0] == '.':
-        import_path = import_path[:-1]
-        match = match[1:]
-
-    if match:
-        import_path.append(match)
-
-    return '.'.join(import_path)
-
-
-if __name__ == '__main__':
-    for args in os.walk('../misago'):
-        walk_directory(*args)
-
-    print "\nDone! Don't forget to run isort to fix imports ordering!"

+ 0 - 2
extras/psql.sh

@@ -1,2 +0,0 @@
-#!/usr/bin/env bash
-PGPASSWORD=$POSTGRES_PASSWORD psql --username $POSTGRES_USER --host $POSTGRES_HOST $POSTGRES_DB

+ 0 - 10
extras/wait_for_postgres.sh

@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-# Sometimes postgres is not ready before django attempts to connect.
-# This script waits until we can do a basic select before continuing.
-export PGPASSWORD=$POSTGRES_PASSWORD
-RETRIES=10
-
-until psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB -c "select 1" > /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do
-  echo "Waiting for postgres server, $((RETRIES--)) remaining attempts..."
-  sleep 5
-done

+ 0 - 58
initdev

@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-# This script is a shortcut for configuring newly-build docker container for Misago development
-python setup.py develop
-
-# Clear OS files
-rm -f /srv/misago/.DS_Store
-rm -f /srv/misago/Thumbs.db
-
-# If user specified "-f", clear after previous devinit
-if [ "$1" = "-f" ]
-then
-    echo "Cleaned files created by previous initdev"
-    rm -f /srv/misago/cron.txt
-    rm -f /srv/misago/manage.py
-    rm -rf /srv/misago/avatargallery
-    rm -rf /srv/misago/$PROJECT_NAME
-    rm -rf /srv/misago/media
-    rm -rf /srv/misago/static
-    rm -rf /srv/misago/theme
-    rm -rf /srv/misago/userdata
-fi
-
-# Create new project
-python extras/createdevproject.py $PROJECT_NAME /srv/misago
-
-# Clean up unnecessary project files
-rm -rf theme
-rm -f cron.txt
-
-# Database
-./extras/wait_for_postgres.sh
-python manage.py migrate
-python extras/createsuperuser.py
-
-# Print short bit of help at the end of cript
-RED='\033[0;31m'
-DEFAULT='\033[0m'
-
-echo ""
-echo "================================================================================"
-echo ""
-echo "Note: running 'initdev' after already having used it to setup Misago"
-echo "for development may result in any of following errors occuring:"
-echo ""
-echo "  - CommandError: /srv/misago/... already exists, overlaying a project or app into an existing directory won't replace conflicting files"
-echo "  - ModuleNotFoundError: No module named '$PROJECT_NAME'"
-echo "  - django.core.exceptions.ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the NAME value."
-echo "  - python: can't open file 'manage.py': [Errno 2] No such file or directory"
-echo ""
-echo "If you are experiencing either of those errors, this means that files are"
-echo "present in the repository's main directory preventing 'initdev' from succedding."
-echo "Please try running the 'initdev' with \"-f\" option to force old files deletion:"
-echo ""
-echo "  docker-compose run --rm misago initdev -f"
-echo ""
-echo -e "${RED}Warning:${DEFAULT} if you have uncommited changes to Misago's setup that should be included"
-echo "in next release, make sure that they are commited to 'misago/project_template'"
-echo "or 'initdev -f' will overwrite the files causing them to be lost."

+ 0 - 8
makemessages

@@ -1,8 +0,0 @@
-#!/bin/bash
-cd misago
-
-echo "Extracting messages from .py and .html files..."
-django-admin.py makemessages -l $1 -e html,txt,py
-
-echo "Extracting messages from js files..."
-django-admin.py makemessages -l $1 -d djangojs

+ 12 - 15
extras/createdevproject.py → misago/bin/misago-start-devproject.py

@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+# pylint: disable=E0401
 """
 Creates a dev project for local development
 """
@@ -5,27 +7,20 @@ Creates a dev project for local development
 import os
 import sys
 
-from misago.core import setup
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
-
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+PROJECT_NAME = 'devproject'
 
 
 def main():
-    project_name = os.environ['PROJECT_NAME']
-
-    # Allow for overriding project name
-    if len(sys.argv) > 1:
-        project_name = sys.argv[1]
-    else:
-        sys.argv.append(project_name)
-
-    settings_file = os.path.join(BASE_DIR, project_name, 'settings.py')
+    settings_file = os.path.join(BASE_DIR, PROJECT_NAME, PROJECT_NAME, 'settings.py')
 
     # Avoid recreating if already present
     if os.path.exists(settings_file):
         return
 
+    from misago.core import setup
+
     setup.start_misago_project()
     fill_in_settings(settings_file)
 
@@ -34,7 +29,7 @@ def fill_in_settings(f):
     with open(f, 'r') as fd:
         s = fd.read()
 
-        # Postgres
+        # Read PostgreSQL's config from env variables
         s = s.replace("'NAME': '',", "'NAME': os.environ.get('POSTGRES_DB'),")
         s = s.replace("'USER': '',", "'USER': os.environ.get('POSTGRES_USER'),")
         s = s.replace("'PASSWORD': '',", "'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),")
@@ -47,7 +42,7 @@ def fill_in_settings(f):
         pos = s.find('STATICFILES_DIRS')
         s = s[:s.find('[', pos) + 1] + s[s.find(']', pos):]
 
-        # Remote theme dir from template dirs
+        # Remove theme dir from template dirs
         pos = s.find("'DIRS': [")
         s = s[:s.find('[', pos) + 1] + s[s.find(']', pos):]
 
@@ -56,4 +51,6 @@ def fill_in_settings(f):
 
 
 if __name__ == '__main__':
-    main()
+    sys.argv.append(PROJECT_NAME)
+    sys.path.append(BASE_DIR)
+    main()

+ 0 - 3
pyclean

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-find ./misago -name '*.pyc' -delete -not -path './misago/frontend/*'

+ 0 - 38
pycodestyle.py

@@ -1,38 +0,0 @@
-"""
-Code style cleanups done after yapf
-"""
-import argparse
-import codecs
-import os
-
-from extras import fixdictsformatting
-
-
-CLEANUPS = [
-    fixdictsformatting,
-]
-
-
-def walk_directory(root, dirs, files):
-    for filename in files:
-        if filename.lower().endswith('.py'):
-            with codecs.open(os.path.join(root, filename), 'r', 'utf-8') as f:
-                filesource = f.read()
-
-            org_source = filesource
-
-            for cleanup in CLEANUPS:
-                filesource = cleanup.fix_formatting(filesource)
-
-            if org_source != filesource:
-                print 'afterclean: %s' % os.path.join(root, filename)
-                with codecs.open(os.path.join(root, filename), 'w', 'utf-8') as f:
-                    f.write(filesource)
-
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser()
-    parser.add_argument('path', nargs='?', default='./')
-
-    for args in os.walk(parser.parse_args().path):
-        walk_directory(*args)

+ 0 - 2
upload

@@ -1,2 +0,0 @@
-#!/bin/sh
-python setup.py sdist upload