diff --git a/README.md b/README.md index 7bc6f7e7..f07e2896 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![npm version](https://badge.fury.io/js/amclin-create-react-app.svg)](https://www.npmjs.com/@amclin/create-react-app) [![Build Status](https://travis-ci.org/amclin/react-project-boilerplate.svg?branch=master)](https://travis-ci.org/amclin/react-project-boilerplate) +[![npm version](https://badge.fury.io/js/amclin-create-react-app.svg)](https://www.npmjs.com/amclin-create-react-app) [![Build Status](https://travis-ci.org/amclin/react-project-boilerplate.svg?branch=master)](https://travis-ci.org/amclin/react-project-boilerplate) ![Branch Code Coverage](./coverage/badge-branches.svg) ![Functions Code Coverage](./coverage/badge-functions.svg) ![Lines Code Coverage](./coverage/badge-lines.svg) ![Statements Code Coverage](./coverage/badge-statements.svg) diff --git a/templates/default/Dockerfile b/templates/default/Dockerfile new file mode 100644 index 00000000..c690c36e --- /dev/null +++ b/templates/default/Dockerfile @@ -0,0 +1,32 @@ +###### Stage 1 - Copy the compiled React App +# This could be replaced with an npm build step +# to compile the app within a Docker container +# but it's not necessary if the app is already +# compiled and is just copied in +FROM alpine as build-stage +LABEL author="%%AUTHOR%%" + +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +COPY out /usr/src/app/build +COPY config /usr/src/app/config + +###### Stage 2 - Run production webserver on nginx +FROM nginx:alpine + +# Add app-specific configs and files +COPY config/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build-stage /usr/src/app/build/ /usr/share/nginx/html + +# nginix images don't run by default on OpenShift because of permissions +# https://torstenwalter.de/openshift/nginx/2017/08/04/nginx-on-openshift.html +# support running as arbitrary user which belongs to the root group +# RUN chmod g+rwx /var/cache/nginx /var/run /var/log/nginx +# comment user directive as master process is run as user in OpenShift anyhow +# RUN sed -i.bak 's/^user/#user/' /etc/nginx/nginx.conf +# If replacing with ${SERVICE_PORT} then /config/nginx.conf needs to be updated +# users are not allowed to listen on priviliged ports so replace default 80 with ${SERVICE_PORT} +# RUN sed -i.bak 's/listen\(.*\)80;/listen ${SERVICE_PORT};/' /etc/nginx/conf.d/default.conf +# EXPOSE ${SERVICE_PORT} +CMD ["nginx", "-g", "daemon off;"] diff --git a/templates/default/README.md b/templates/default/README.md index 6bf56b74..510163f9 100644 --- a/templates/default/README.md +++ b/templates/default/README.md @@ -33,3 +33,25 @@ To run a static site, deploy the contents of the `/build` folder to a webserver. #### Server-Side Rendering (SSR) To run a site with Server-Side Rendering, make sure the package is checked out and installed on a suitable NodeJS, NextJS, or serverless environment, and then run `npm start` (`npm run build` needs to have happened first ). See [NextJS documentation for more details](https://nextjs.org/docs#production-deployment). + +### Running In a Docker Container +#### Compile the App +To compile a docker image you first need to compile the NextJS app: +``` +npm run build +``` +If this is a statically-generated site (with or without client-side rendering), you then must export the NextJS app: +``` +npm run export +``` + +#### Creating a Docker Image +``` +./scripts/build-docker.sh +``` +#### Running Docker Image +Once the Docker image exists in your registry of choice (local or remote), you can then run the app and specify what port it should run on: +``` +docker run -p 0.0.0.0:3000:80 react-example/%%APPNAME%%:latest +``` +If you'd like to run on a different port, replace `3000` with the desired port. diff --git a/templates/default/config/nginx.conf b/templates/default/config/nginx.conf new file mode 100644 index 00000000..7194a2e3 --- /dev/null +++ b/templates/default/config/nginx.conf @@ -0,0 +1,82 @@ +# Compression + +# Enable Gzip compressed. +gzip on; + +# Compression level (1-9). +# 5 is a perfect compromise between size and cpu usage, offering about +# 75% reduction for most ascii files (almost identical to level 9). +gzip_comp_level 5; + +# Don't compress anything that's already small and unlikely to shrink much +# if at all (the default is 20 bytes, which is bad as that usually leads to +# larger files after gzipping). +gzip_min_length 256; + +# Compress data even for clients that are connecting to us via proxies, +# identified by the "Via" header (required for CloudFront). +gzip_proxied any; + +# Tell proxies to cache both the gzipped and regular version of a resource +# whenever the client's Accept-Encoding capabilities header varies; +# Avoids the issue where a non-gzip capable client (which is extremely rare +# today) would display gibberish if their proxy gave them the gzipped version. +gzip_vary on; + +# Compress all output labeled with one of the following MIME-types. +gzip_types + application/atom+xml + application/javascript + application/json + application/ld+json + application/manifest+json + application/rss+xml + application/vnd.geo+json + application/vnd.ms-fontobject + application/x-font-ttf + application/x-web-app-manifest+json + application/xhtml+xml + application/xml + font/opentype + image/bmp + image/svg+xml + image/x-icon + text/cache-manifest + text/css + text/plain + text/vcard + text/vnd.rim.location.xloc + text/vtt + text/x-component + text/x-cross-domain-policy; +# text/html is always compressed by HttpGzipModule + +# This should be turned on if you are going to have pre-compressed copies (.gz) of +# static files available. If not it should be left off as it will cause extra I/O +# for the check. It is best if you enable this in a location{} block for +# a specific directory, or on an individual server{} level. +# gzip_static on; + +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri /index.html; + + # Long cache policy on static assets + # https://developers.google.com/web/tools/lighthouse/audits/cache-policy + location ~ ^/(static|img)/ { + expires 365d; + } + } + + # redirect server error pages + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/templates/default/scripts/build-docker.sh b/templates/default/scripts/build-docker.sh new file mode 100755 index 00000000..2a840ad9 --- /dev/null +++ b/templates/default/scripts/build-docker.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +echo "Executing Docker build for %%APPNAME%%" +# //TODO: Fully support Docker naming/tag structure, +# Best to populate these variables from the CICD pipeline +# "docker build -t ${REPO_URL}/${BUILD_NAME}:${BUILD_IMAGE_VERSION} --build-arg APP_ENV=production --build-arg APP_NAME=${BUILD_NAME} --build-arg DEPLOY_ARTIFACT_DIR=. --build-arg SERVICE_PORT=${SOURCE_PORT} --rm=true --no-cache ." + +docker build -t react-example/%%APPNAME%% .