Add a node image to run Gulp when selecting it with Docker (#1687)

## Description

Following up on @webyneter attempt in #1205, which is now getting outdated, I've tried to make Gulp task runner work with Docker. There is no documentation yet, but this seems to work locally with the custom bootstrap compilation...

- [x] Add a node image for local developement
- [x] Proxy the django image rather than localhost in Browser sync, pass header to keep hostname
- [x] Don't call the runserver command from Gulp, let docker-compose handle
- [x] Update package.json & gulpfile.js templates to reduce the number of unwanted empty lines
- [x] Use [multi-stage build](https://docs.docker.com/develop/develop-images/multistage-build/) in production to make sure the static assets are produced
- [x] Update documentation
- [x] Verify that the previous issue with static assets missing from production isn't there 

## Rationale

Currently, the static build isn't working nicely with Docker, extra manual setup is required.

Fixes #1762
This commit is contained in:
Bruno Alla 2019-03-25 12:10:55 +00:00 committed by GitHub
parent b9bb5ef43a
commit b91c70d755
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 83 additions and 26 deletions

View File

@ -7,8 +7,8 @@ Deployment with Docker
Prerequisites Prerequisites
------------- -------------
* Docker 1.10+. * Docker 17.05+.
* Docker Compose 1.6+ * Docker Compose 1.17+
Understanding the Docker Compose Setup Understanding the Docker Compose Setup
@ -144,3 +144,4 @@ Move it to ``/etc/supervisor/conf.d/{{cookiecutter.project_slug}}.conf`` and run
For status check, run:: For status check, run::
supervisorctl status supervisorctl status

View File

@ -267,6 +267,10 @@ def remove_celery_compose_dirs():
shutil.rmtree(os.path.join("compose", "production", "django", "celery")) shutil.rmtree(os.path.join("compose", "production", "django", "celery"))
def remove_node_dockerfile():
shutil.rmtree(os.path.join("compose", "local", "node"))
def main(): def main():
debug = "{{ cookiecutter.debug }}".lower() == "y" debug = "{{ cookiecutter.debug }}".lower() == "y"
@ -313,21 +317,8 @@ def main():
if "{{ cookiecutter.js_task_runner}}".lower() == "none": if "{{ cookiecutter.js_task_runner}}".lower() == "none":
remove_gulp_files() remove_gulp_files()
remove_packagejson_file() remove_packagejson_file()
if ( if "{{ cookiecutter.use_docker }}".lower() == "y":
"{{ cookiecutter.js_task_runner }}".lower() != "none" remove_node_dockerfile()
and "{{ cookiecutter.use_docker }}".lower() == "y"
):
print(
WARNING
+ "Docker and {} JS task runner ".format(
"{{ cookiecutter.js_task_runner }}".lower().capitalize()
)
+ "working together not supported yet. "
"You can continue using the generated project like you "
"normally would, however you would need to add a JS "
"task runner service to your Docker Compose configuration "
"manually." + TERMINATOR
)
if "{{ cookiecutter.use_celery }}".lower() == "n": if "{{ cookiecutter.use_celery }}".lower() == "n":
remove_celery_app() remove_celery_app()

View File

@ -0,0 +1,9 @@
FROM node:10-stretch-slim
WORKDIR /app
COPY ./package.json /app
RUN npm install && npm cache clean --force
ENV PATH ./node_modules/.bin/:$PATH

View File

@ -1,3 +1,14 @@
{% if cookiecutter.js_task_runner == 'Gulp' -%}
FROM node:10-stretch-slim as client-builder
WORKDIR /app
COPY ./package.json /app
RUN npm install && npm cache clean --force
COPY . /app
RUN npm run build
# Python build stage
{%- endif %}
FROM python:3.6-alpine FROM python:3.6-alpine
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
@ -28,7 +39,8 @@ COPY ./compose/production/django/start /start
RUN sed -i 's/\r//' /start RUN sed -i 's/\r//' /start
RUN chmod +x /start RUN chmod +x /start
RUN chown django /start RUN chown django /start
{% if cookiecutter.use_celery == "y" %}
{%- if cookiecutter.use_celery == "y" %}
COPY ./compose/production/django/celery/worker/start /start-celeryworker COPY ./compose/production/django/celery/worker/start /start-celeryworker
RUN sed -i 's/\r//' /start-celeryworker RUN sed -i 's/\r//' /start-celeryworker
RUN chmod +x /start-celeryworker RUN chmod +x /start-celeryworker
@ -42,8 +54,13 @@ RUN chown django /start-celerybeat
COPY ./compose/production/django/celery/flower/start /start-flower COPY ./compose/production/django/celery/flower/start /start-flower
RUN sed -i 's/\r//' /start-flower RUN sed -i 's/\r//' /start-flower
RUN chmod +x /start-flower RUN chmod +x /start-flower
{% endif %} {%- endif %}
{%- if cookiecutter.js_task_runner == 'Gulp' %}
COPY --from=client-builder /app /app
{% else %}
COPY . /app COPY . /app
{%- endif %}
RUN chown -R django /app RUN chown -R django /app

View File

@ -127,7 +127,23 @@ function initBrowserSync() {
`${paths.js}/*.js`, `${paths.js}/*.js`,
`${paths.templates}/*.html` `${paths.templates}/*.html`
], { ], {
proxy: "localhost:8000" // https://www.browsersync.io/docs/options/#option-proxy
{%- if cookiecutter.use_docker == 'n' %}
proxy: 'localhost:8000'
{% else %}
proxy: {
target: 'django:8000',
proxyReq: [
function(proxyReq, req) {
// Assign proxy "host" header same as current request at Browsersync server
proxyReq.setHeader('Host', req.headers.host)
}
]
},
// https://www.browsersync.io/docs/options/#option-open
// Disable as it doesn't work from inside a container
open: false
{%- endif %}
} }
) )
} }
@ -149,7 +165,9 @@ const generateAssets = parallel(
// Set up dev environment // Set up dev environment
const dev = parallel( const dev = parallel(
{%- if cookiecutter.use_docker == 'n' %}
runServer, runServer,
{%- endif %}
initBrowserSync, initBrowserSync,
watchPaths watchPaths
) )

View File

@ -79,3 +79,23 @@ services:
command: /start-flower command: /start-flower
{%- endif %} {%- endif %}
{%- if cookiecutter.js_task_runner == 'Gulp' %}
node:
build:
context: .
dockerfile: ./compose/local/node/Dockerfile
image: {{ cookiecutter.project_slug }}_local_node
depends_on:
- django
volumes:
- .:/app
# http://jdlm.info/articles/2016/03/06/lessons-building-node-app-docker.html
- /app/node_modules
command: npm run dev
ports:
- "3000:3000"
# Expose browsersync UI: https://www.browsersync.io/docs/options/#option-ui
- "3001:3001"
{%- endif %}

View File

@ -5,7 +5,7 @@
"devDependencies": { "devDependencies": {
{% if cookiecutter.js_task_runner == 'Gulp' -%} {% if cookiecutter.js_task_runner == 'Gulp' -%}
{% if cookiecutter.custom_bootstrap_compilation == 'y' -%} {% if cookiecutter.custom_bootstrap_compilation == 'y' -%}
"bootstrap": "4.1.1", "bootstrap": "4.3.1",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
"jquery": "3.3.1", "jquery": "3.3.1",
"popper.js": "1.14.3", "popper.js": "1.14.3",
@ -31,7 +31,8 @@
], ],
"scripts": { "scripts": {
{% if cookiecutter.js_task_runner == 'Gulp' -%} {% if cookiecutter.js_task_runner == 'Gulp' -%}
"dev": "gulp" "dev": "gulp",
"build": "gulp generate-assets"
{%- endif %} {%- endif %}
} }
} }

View File

@ -17,8 +17,8 @@
{% block css %} {% block css %}
{% endraw %}{% if cookiecutter.custom_bootstrap_compilation == "n" %}{% raw %} {% endraw %}{% if cookiecutter.custom_bootstrap_compilation == "n" %}{% raw %}
<!-- Latest compiled and minified Bootstrap 4.1.1 CSS --> <!-- Latest compiled and minified Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous"> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
{% endraw %}{% endif %}{% raw %} {% endraw %}{% endif %}{% raw %}
<!-- Your stuff: Third-party CSS libraries go here --> <!-- Your stuff: Third-party CSS libraries go here -->
@ -102,10 +102,10 @@
<script src="{% static 'js/vendors.js' %}"></script> <script src="{% static 'js/vendors.js' %}"></script>
{% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% endcompress %}{% endraw %}{% endif %}{% raw %} {% endraw %}{% if cookiecutter.use_compressor == "y" %}{% raw %}{% endcompress %}{% endraw %}{% endif %}{% raw %}
{% endraw %}{% else %}{% raw %} {% endraw %}{% else %}{% raw %}
<!-- Required by Bootstrap v4.1.1 --> <!-- Bootstrap JS and its dependencies-->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<!-- Your stuff: Third-party javascript libraries go here --> <!-- Your stuff: Third-party javascript libraries go here -->
{% endraw %}{% endif %}{% raw %} {% endraw %}{% endif %}{% raw %}