diff --git a/.env.docker b/.env.docker new file mode 100644 index 0000000..3a2128e --- /dev/null +++ b/.env.docker @@ -0,0 +1,9 @@ +SECRET_KEY=devkey +ENV=dev + +DATABASE_URL=postgres://pythonph:password@db/pythonph + +SLACK_ORG=pythonph +SLACK_API_TOKEN=xxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxxx-xxxxxxxxxx +SLACK_BOARD_CHANNEL=pythonph +SLACK_JOBS_CHANNEL=jobs diff --git a/.env.virtualenv b/.env.virtualenv new file mode 100644 index 0000000..5650e4a --- /dev/null +++ b/.env.virtualenv @@ -0,0 +1,9 @@ +SECRET_KEY=devkey +ENV=dev + +DATABASE_URL=sqlite:///db.sqlite3 + +SLACK_ORG=pythonph +SLACK_API_TOKEN=xxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxxx-xxxxxxxxxx +SLACK_BOARD_CHANNEL=pythonph +SLACK_JOBS_CHANNEL=jobs diff --git a/.gitignore b/.gitignore index 92319cc..8c040e9 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ npm-debug.log # docker-compose *.env +db.sqlite3 diff --git a/Dockerfile b/Dockerfile index ff6e66a..e9ac3c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,16 @@ -FROM python:3.8.1 +FROM nikolaik/python-nodejs:python3.8-nodejs12 -RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - -RUN apt-get -y install nodejs -RUN apt-get -y install libcairo-dev - -RUN mkdir -p /usr/src/app - -COPY package.json /usr/src/app/ -RUN npm install --prefix /usr/src/app/ +WORKDIR /usr/src/app -COPY requirements.txt /usr/src/app -RUN pip install -r /usr/src/app/requirements.txt +COPY . /usr/src/app/ +RUN pip install -r requirements/docker.txt \ + && npm install \ + && npm run build-jobs \ + && python manage.py compress --force \ + && python manage.py collectstatic --noinput -COPY . /usr/src/app -WORKDIR /usr/src/app -RUN npm run-script build-jobs +COPY docker-entrypoint.sh /usr/local/bin/ +ENTRYPOINT ["docker-entrypoint.sh"] -CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] +EXPOSE 8000 +CMD ["runprod"] diff --git a/README.md b/README.md index 1de8b55..5e53fc5 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,68 @@ # PythonPH -## Development Setup +## Development Setup via virtualenv -1. Setup db +1. Make a copy of `.env.virtualenv` as `.env`. - ```bash - createuser -P pythonph - # You will be prompted to enter a password - # Enter what you set in POSTGRES_PASSWORD - createdb -O pythonph pythonph - ``` +```bash +$ cp .env.virtualenv .env +``` -2. Setup virtualenv +2. Create and activate your virtualenv. - ```bash - mkvirtualenv venv - venv/bin/pip install -r requirements.txt - ``` +```bash +$ virtualenv venv +$ source venv/bin/activate +``` -3. Setup npm +3. Install python packages. - ```bash - npm install - ``` +```bash +$ pip install -r requirements/virtualenv.txt +``` -4. Create `dev.env` +4. Run Django migrations. - ```bash - SECRET_KEY=secret - ENV=DEV - POSTGRES_USER=pythonph - POSTGRES_PASSWORD=password - SLACK_ORG=pythonph - SLACK_API_TOKEN=xxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxxx-xxxxxxxxxx - SLACK_BOARD_CHANNEL=pythonph - SLACK_JOBS_CHANNEL=jobs - ``` +```bash +$ python manage.py migrate +``` -5. Setup Django +5. Run Django server. - ```bash - bin/localmanage migrate - bin/localmanage createsuperuser - ``` +```bash +$ python manage.py runserver +``` -6. Run server +6. Install npm packages and run build watcher (Optional, only run if you need to work on JS). - ```bash - npm start - ``` +```bash +$ npm install +$ npm run dev +``` ## Development Setup via docker-compose -1. `./bin/build-dev` -2. `./bin/deploy-dev` -Note: For this setup, you have to run `./bin/build-dev && ./bin/deploy-dev` for changes to reflect. +1. Make a copy of `.env.docker` as `.env`. +```bash +$ cp .env.docker .env +``` -## Todo -1. Improve dockerized development setup +2. Build images and run containers. +```bash +$ docker-compose build +$ docker-compose up -d +``` + +3. Run migrations + +```bash +$ docker-compose run --rm web migrate +``` + +4. Running Django's manage.py to run other commands + +```bash +$ docker-compose run --rm web python manage.py +``` diff --git a/bin/build b/bin/build index 7cfd9a2..034e032 100755 --- a/bin/build +++ b/bin/build @@ -1,8 +1,7 @@ #!/bin/bash set -e -docker build --rm -t pythonph/pythonph . COMPOSE="docker-compose -f docker-compose-prod.yml" -$COMPOSE build nginx -$COMPOSE up -d source +$COMPOSE build +$COMPOSE up -d $COMPOSE logs source diff --git a/bin/build-dev b/bin/build-dev index 61dd6e5..e4881ef 100755 --- a/bin/build-dev +++ b/bin/build-dev @@ -1,8 +1,7 @@ #!/bin/bash set -e -docker build --rm -t pythonph/pythonph . -COMPOSE="docker-compose -f docker-compose-dev.yml" -$COMPOSE build nginx -$COMPOSE up -d source -$COMPOSE logs source +COMPOSE="docker-compose -f docker-compose.yml" +$COMPOSE build +$COMPOSE up -d +$COMPOSE logs web diff --git a/bin/deploy-dev b/bin/deploy-dev index d952cef..8c9c5fb 100755 --- a/bin/deploy-dev +++ b/bin/deploy-dev @@ -1,5 +1,5 @@ #!/bin/bash set -e -COMPOSE="docker-compose -f docker-compose-dev.yml" -$COMPOSE up -d --force-recreate web nginx db +COMPOSE="docker-compose -f docker-compose.yml" +$COMPOSE up -d --force-recreate web diff --git a/bin/install b/bin/install index 5829352..46d7cdf 100755 --- a/bin/install +++ b/bin/install @@ -1,7 +1,7 @@ #!/bin/bash set -e -pip install -r requirements.txt +pip install -r requirements/docker.txt mkdir -p static/jobs/js npm install diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml deleted file mode 100644 index bede0c3..0000000 --- a/docker-compose-dev.yml +++ /dev/null @@ -1,38 +0,0 @@ -source: - image: pythonph/pythonph - volumes: - - /usr/src/app/venv - - /usr/src/app/node_modules - - /usr/src/app/bower_components - - /usr/src/app/static - environment: - - ENV=DEV - env_file: dev.env - command: bin/install - -db: - image: postgres:latest - environment: - - ENV=DEV - env_file: dev.env - -web: - image: pythonph/pythonph - volumes_from: - - source - environment: - - ENV=DEV - env_file: dev.env - ports: - - 8000:8000 - links: - - db:db - command: gunicorn -b 0.0.0.0:8000 --access-logfile - --error-logfile - pythonph.wsgi - -nginx: - build: nginx - ports: - - 8080:80 - - 443:443 - links: - - web:web diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml index b6fa456..5c1ec48 100644 --- a/docker-compose-prod.yml +++ b/docker-compose-prod.yml @@ -1,28 +1,15 @@ -source: - image: pythonph/pythonph - volumes: - - /usr/src/app/venv - - /usr/src/app/node_modules - - /usr/src/app/bower_components - - /usr/src/app/static - environment: - - ENV=PROD - env_file: prod.env - command: bin/install +services: + web: + build: . + image: pythonph/pythonph + container_name: pythonph_web + command: runprod -web: - image: pythonph/pythonph - volumes_from: - - source - environment: - - ENV=PROD - env_file: prod.env - command: gunicorn -b 0.0.0.0:8000 --access-logfile - --error-logfile - pythonph.wsgi - -nginx: - build: nginx - ports: - - 80:80 - - 443:443 - links: - - web:web + nginx: + image: nginx:latest + container_name: pythonph_nginx + ports: + - 80:80 + - 443:443 + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf diff --git a/docker-compose.yml b/docker-compose.yml index ea89ece..02c7a46 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,21 +1,23 @@ -db: - image: postgres:latest - environment: - - POSTGRES_USER=pythonph - env_file: dev.env +services: + web: + build: . + image: pythonph/pythonph + container_name: pythonph_web + command: rundev + ports: + - 8000:8000 + volumes: + - ./:/usr/src/app/ -web: - build: . - volumes: - - .:/usr/src/app - links: - - db:db - environment: - - PYTHONUNBUFFERED=1 - - ENV=DEV - - POSTGRES_HOST=db - - POSTGRES_USER=pythonph - env_file: dev.env - ports: - - 8000:8000 - command: python manage.py runserver 0.0.0.0:8000 + db: + image: postgres:14.2-alpine + container_name: pythonph_db + environment: + - POSTGRES_DB=pythonph + - POSTGRES_USER=pythonph + - POSTGRES_PASSWORD=password + volumes: + - pg-volume:/var/lib/postgresql/data/ + +volumes: + pg-volume: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..61cff40 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env sh +set -e + +if [ "$1" = "migrate" ]; then + python manage.py migrate + exit 0 +fi + +if [ "$1" = "makemigrations" ]; then + python manage.py makemigrations + exit 0 +fi + +if [ "$1" = "createsuperuser" ]; then + python manage.py createsuperuser + exit 0 +fi + +if [ "$1" = "rundev" ]; then + npm run dev & python manage.py runserver 0.0.0.0:8000 + exit 0 +fi + +if [ "$1" = "runprod" ]; then + gunicorn -b 0.0.0.0:8000 --access-logfile - --error-logfile - pythonph.wsgi + exit 0 +fi + +exec "$@" diff --git a/nginx/Dockerfile b/nginx/Dockerfile deleted file mode 100644 index f8319c9..0000000 --- a/nginx/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM nginx:latest -COPY nginx.conf /etc/nginx/ diff --git a/package.json b/package.json index fc499ee..8f3b462 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "scripts": { "watch-jobs": "watchify jobs/static/jobs/js/src/main.js -o jobs/static/jobs/js/dist/main.js -v", "build-jobs": "browserify jobs/static/jobs/js/src/main.js -o jobs/static/jobs/js/dist/main.js", - "start": "npm run watch-jobs & bin/localmanage runserver $PORT" + "start": "npm run watch-jobs & bin/localmanage runserver $PORT", + "dev": "npm run watch-jobs" }, "repository": { "type": "git", diff --git a/pythonph/settings.py b/pythonph/settings.py index e009bb1..6f971fd 100644 --- a/pythonph/settings.py +++ b/pythonph/settings.py @@ -2,10 +2,19 @@ from django.utils._os import safe_join +import dj_database_url +import environ + +env = environ.Env( + SECRET_KEY=(str, 'devkey'), + ENV=(str, 'DEV'), + DATABASE_URL=(str, 'sqlite:///db.sqlite3'), +) + BASE_DIR = os.path.dirname(os.path.dirname(__file__)) -SECRET_KEY = os.environ['SECRET_KEY'] -DEBUG = os.environ['ENV'] == 'DEV' +SECRET_KEY = env('SECRET_KEY') +DEBUG = env('ENV') == 'DEV' TEMPLATE_DEBUG = DEBUG ALLOWED_HOSTS = ['python.ph', 'localhost', '*'] @@ -46,13 +55,7 @@ WSGI_APPLICATION = 'pythonph.wsgi.application' DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'HOST': os.environ.get('POSTGRES_HOST', 'localhost'), - 'NAME': os.environ['POSTGRES_USER'], - 'USER': os.environ['POSTGRES_USER'], - 'PASSWORD': os.environ['POSTGRES_PASSWORD'], - }, + 'default': dj_database_url.parse(env('DATABASE_URL')), } LANGUAGE_CODE = 'en-us' @@ -85,18 +88,21 @@ TASTYPIE_DEFAULT_FORMATS = ['json'] -if 'SENTRY_DSN' in os.environ: +SENTRY_DSN = env('SENTRY_DSN', default=None) + +if SENTRY_DSN: RAVEN_CONFIG = { - 'dsn': os.environ['SENTRY_DSN'], + 'dsn': SENTRY_DSN, } if DEBUG: INSTALLED_APPS += ('debug_toolbar',) + MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware',) -SLACK_ORG = os.environ['SLACK_ORG'] -SLACK_API_TOKEN = os.environ['SLACK_API_TOKEN'] -SLACK_BOARD_CHANNEL = os.environ['SLACK_BOARD_CHANNEL'] -SLACK_JOBS_CHANNEL = os.environ['SLACK_JOBS_CHANNEL'] +SLACK_ORG = env('SLACK_ORG', default=None) +SLACK_API_TOKEN = env('SLACK_API_TOKEN', default=None) +SLACK_BOARD_CHANNEL = env('SLACK_BOARD_CHANNEL', default=None) +SLACK_JOBS_CHANNEL = env('SLACK_JOBS_CHANNEL', default=None) TEMPLATES = [ { diff --git a/requirements.txt b/requirements/base.txt similarity index 87% rename from requirements.txt rename to requirements/base.txt index 7416b7c..d1cc974 100644 --- a/requirements.txt +++ b/requirements/base.txt @@ -1,12 +1,12 @@ Django==2.2.18 -gunicorn==20.0.4 -psycopg2-binary==2.8.5 Pillow==7.1.1 +dj-database-url==0.5.0 django-admin-sortable2 django-ckeditor==6.0.0 django-compressor==2.4 django-debug-toolbar==2.2 +django-environ==0.8.1 django-libsass==0.8 django-markdownx==3.0.1 django-storages==1.6.5 diff --git a/requirements/docker.txt b/requirements/docker.txt new file mode 100644 index 0000000..1cd0fe0 --- /dev/null +++ b/requirements/docker.txt @@ -0,0 +1,4 @@ +-r base.txt + +gunicorn==20.0.4 +psycopg2-binary==2.8.5 diff --git a/requirements/virtualenv.txt b/requirements/virtualenv.txt new file mode 100644 index 0000000..a3e81b8 --- /dev/null +++ b/requirements/virtualenv.txt @@ -0,0 +1 @@ +-r base.txt