Skip to content

Commit

Permalink
Merge pull request #186 from pacificclimate/process-env-replacement
Browse files Browse the repository at this point in the history
Deperecate use of process.env (improves startup speed), add healthcheck
  • Loading branch information
Nospamas authored Dec 16, 2024
2 parents b6acd2a + 651b941 commit 8cd71c4
Show file tree
Hide file tree
Showing 31 changed files with 441 additions and 410 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.git
.idea
build

**/*.env*
6 changes: 1 addition & 5 deletions .env
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
# PCDS config
REACT_APP_BC_BASE_MAP_TILES_URL=https://services.pacificclimate.org/tiles/bc-albers-lite/{z}/{x}/{y}.png

# YNWT config
REACT_APP_YNWT_BASE_MAP_TILES_URL=https://services.pacificclimate.org/tiles/yukon-albers-lite/{z}/{x}/{y}.png
PUBLIC_URL=%REPLACE_PUBLIC_URL%
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PUBLIC_URL=http://localhost:3000
2 changes: 1 addition & 1 deletion .env.production
Original file line number Diff line number Diff line change
@@ -1 +1 @@
PUBLIC_URL=http://localhost:3333/
PUBLIC_URL=%REPLACE_PUBLIC_URL%
4 changes: 4 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ jobs:
run: |
git fetch --prune --unshallow
echo "REACT_APP_APP_VERSION=$(git describe --tags --abbrev=0) ($(git rev-parse --abbrev-ref HEAD):$(git log -1 --format=%h))" >> $GITHUB_ENV
- name: Build npm package
run: |
npm ci
npm run build
- name: Publish to Registry
uses: docker/build-push-action@v1
with:
Expand Down
11 changes: 6 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# These variables are set to make it convenient to run the docker image locally.
tag = $(shell git rev-parse --abbrev-ref HEAD)
port = 30502
port = 30503
public_url = http://localhost:${port}

image:
@SDP_TAG=$(tag) SDP_PORT=$(port) SDP_PUBLIC_URL=$(public_url) docker compose -f docker/docker-compose.yaml build --build-arg REACT_APP_APP_VERSION='$(shell ./generate-commitish.sh)'
@npm run build
@SDP_TAG=$(tag) SDP_PORT=$(port) docker compose -f docker/docker-compose.yaml build --build-arg REACT_APP_APP_VERSION='$(shell ./generate-commitish.sh)'

up:
@SDP_TAG=$(tag) SDP_PORT=$(port) SDP_PUBLIC_URL=$(public_url) docker compose -f docker/docker-compose.yaml up -d --force-recreate
@echo "Station Data Portal running at $(public_url)"
@SDP_TAG=$(tag) SDP_PORT=$(port) docker compose -f docker/docker-compose.yaml up --force-recreate
@echo "Station Data Portal running on $(port)"
@docker logs -f station-data-portal-frontend

down:
@SDP_TAG=$(tag) SDP_PORT=$(port) SDP_PUBLIC_URL=$(public_url) docker compose -f docker/docker-compose.yaml down
@SDP_TAG=$(tag) SDP_PORT=$(port) docker compose -f docker/docker-compose.yaml down
19 changes: 19 additions & 0 deletions craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,29 @@ const { CracoAliasPlugin } = require("react-app-alias");
const options = {}; // default is empty for most cases

module.exports = {
eslint: null,
plugins: [
{
plugin: CracoAliasPlugin,
options: {},
},
],
// Works around a warning that plotly.js doesn't have a source map
// As warnings are treated as errors in the build, this is necessary
// node_modules are generally external code so it is hard to fix the warning
// and we can safely ignore sourcemap errors.
webpack: {
configure: {
ignoreWarnings: [
function ignoreSourcemapsloaderWarnings(warning) {
return (
warning.module &&
warning.module.resource.includes("node_modules") &&
warning.details &&
warning.details.includes("source-map-loader")
);
},
],
},
},
};
43 changes: 12 additions & 31 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,39 +1,20 @@
# This Dockerfile adapted from https://mherman.org/blog/dockerizing-a-react-app/
# and Client Explorer Dockerfile.

# This Dockerfile can (and should) be used to pass through automatically generated
# version information to the build which is triggered when the image is run.
# To do this, issue the following build command:
#
# docker build --build-arg REACT_APP_APP_VERSION="$(./generate-commitish.sh)" -t <tag> .

# At this moment, Node.js 10.16 LTS is recommended for most users.
#
# In future, as we scale up, we may want to use an Alpine base image, which would reduce
# the size of the image by about an order of magnitude and reduce the attack surface of
# the image as well.
FROM node:22-bookworm-slim

FROM node:16
RUN apt-get -y update && \
apt-get install --no-install-recommends \
-y curl rpl && \
rm -rf /var/lib/apt/lists/*

ADD . /app
WORKDIR /app
RUN chown node /app

ENV PATH /app/node_modules/.bin:$PATH
COPY --chown=node:node package.json /app/package.json

# Currently, we have to force install. For details, see ./docs/installation.md
RUN npm install --quiet --force
RUN npm install -g serve
COPY --chown=node:node . /app

EXPOSE 8080

# Move the build arg REACT_APP_APP_VERSION into an
# environment variable of the same name, for consumption
# by the npm build process in ./entrypoint.sh
ARG REACT_APP_APP_VERSION
ENV REACT_APP_APP_VERSION $REACT_APP_APP_VERSION
COPY --chown=node build /app
COPY --chown=node docker/entrypoint.sh /app/docker/entrypoint.sh
WORKDIR /app

USER node
ENTRYPOINT docker/entrypoint.sh
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "curl", "-f", "http://localhost:8080/healthcheck.js" ]

ENTRYPOINT ["docker/entrypoint.sh"]
25 changes: 0 additions & 25 deletions docker/bc-config.yaml

This file was deleted.

3 changes: 0 additions & 3 deletions docker/bc.env

This file was deleted.

25 changes: 25 additions & 0 deletions docker/config.bc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
window.env = {
PUBLIC_URL: "http://localhost:30503",
REACT_APP_BC_BASE_MAP_TILES_URL:
"https://services.pacificclimate.org/tiles/bc-albers-lite/{z}/{x}/{y}.png",
appTitle: "BC Station Data - PCDS",
baseMap: "BC",

// Uses swarm based dev url by default
sdsUrl:
"https://beehive.pacificclimate.org/station-data-portal/pcds/metadata/",

// Currently deployed metadata backends do not respond to provinces QP.
// When they do, we can invert the commenting out below.
//stationsQpProvinces: BC
stationFilters: 'histories[0].province = "BC"',

// Always necessary for CRMP database
networkFilters: 'name != "PCIC Climate Variables"',

// Uses monsoon database
pdpDataUrl:
"https://beehive.pacificclimate.org/station-data-portal/pcds/data/",
// Uses new database
//pdpDataUrl: http://docker-dev02.pcic.uvic.ca:???
};
26 changes: 26 additions & 0 deletions docker/config.ynwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Example environment config for a YNWT based deployment

window.env = {
PUBLIC_URL: "http://localhost:30503",
REACT_APP_YNWT_BASE_MAP_TILES_URL:
"https://services.pacificclimate.org/tiles/yukon-albers-lite/{z}/{x}/{y}.png",
appTitle: "YNWT Station Data",
baseMap: "YNWT",

// sdsUrl values will be replaced by prod URLs when they become ready.
// For now, we have a dev instance.
// Uses monsoon database
sdsUrl:
"https://beehive.pacificclimate.org/station-data-portal/ynwt/metadata/",

// We do not at present need to filter based on province (verify!)
//stationsQpProvinces: YK,NT
// We do not at present need to filter networks (verify!)
//networkFilters: ???

// pdpDataUrl values will be replaced by prod URLs when they become ready.
// For now, we have a dev instance.
// Uses monsoon database
pdpDataUrl:
"https://beehive.pacificclimate.org/station-data-portal/ynwt/data/",
};
10 changes: 1 addition & 9 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#
# `SDP_TAG`: Image tag
# `SDP_PORT`: External port to map to
# `SDP_PUBLIC_URL`: Public URL of app
# Note: These values are set as part of the makefile, initialize the container
# Using it.
#
Expand All @@ -19,14 +18,7 @@ services:
dockerfile: ./docker/Dockerfile
#image: pcic/station-data-portal-frontend:${SDP_TAG}
container_name: station-data-portal-frontend
environment:
- PUBLIC_URL=${SDP_PUBLIC_URL}
env_file:
- bc.env
volumes:
- type: bind
source: ./bc-config.yaml
target: /app/public/config.yaml
read_only: true
- ./config.bc.js:/app/config.js
ports:
- "${SDP_PORT}:8080"
27 changes: 19 additions & 8 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# We build the app as part of the container startup so that the build process
# can consume the runtime environment variables. (CRA apps can only access
# environment variables at build time, not at run time.) This makes starting a
# container a lot heavier, but we don't spin up many instances, or often,
# so it doesn't matter.

npm run build
serve -s build -l 8080
#!/bin/bash

# Note: this pulls the public url by a combination of grep and cut and relies on
# PUBLIC_URL to be on its own line with the value and in the format of PUBLIC_URL="http://localhost:8080"
# Fragile to additional quotes due to us looking for index 2
PUBLIC_URL=$(grep PUBLIC_URL config.js | cut -d'"' -f 2)

# update static files with the public url
rpl -iR \
-x **/*.js \
-x **/*.html \
-x **/*.css \
-x **/*.json \
"%REPLACE_PUBLIC_URL%" $PUBLIC_URL .

# It is possible that the above could be replaced by a node.js based
# script which may prove more resillient long term

serve -s . -l 8080
17 changes: 0 additions & 17 deletions docker/ynwt-config.yaml

This file was deleted.

2 changes: 0 additions & 2 deletions docker/ynwt.env

This file was deleted.

51 changes: 51 additions & 0 deletions docs/developer/build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Building the project

There are 3 modes of operation for the application which take slightly different build steps:

### Local

Everything in the project should be set up for easy development with defaults provided that allow
execution without modification to configuration. This execution is done via `npm run start`. The
project is then built via create react app and a local development server is started.

Local config is provided via [public/config.js](../public/config.js) and is loaded automatically
as a static javascript file via the local development server.

Public URL is overridden by the `.env.development` file to our expected `http://localhost:3000`

### Local Docker

Testing for deployment involves building in production mode and setting up a container as we will
in production. This allows us to ensure that dependencies are met and gives us a portable artifact
that we can set up on any docker capable machine and expect to work.

Creating the container can be done via the `make image` command. This command executes `npm run build`
creating a static version of the website. `process.env` variables are baked into the files at this
time, so it should be avoided for evironment specific configuration use. These static assests are in
the `build/` folder. Once built the [Dockerfile](../../docker/Dockerfile) pulls in these files along
with dependencies to generate a docker image.

Running the created docker image can be done via `make up`. This brings up the image based on the
specification in the [docker-compose.yaml](../../docker/docker-compose.yaml). This specification also
overrides our local development configuration values by mounting an alternative configuration. Two examples
are provided `config.bc.js` and `config.ynwt.js` representing our two common production versions. `bc`
is used by default.

`PUBLIC_URL` is handled in two steps. During the build process we define a replacement value in `.env.production`
which is injected into any locations where the public URL is required. When the container starts we replace
these instances with the public URL defined in whatever `/app/config.js` within the container has for the
`PUBLIC_URL` value. The specific implementation of this replacement can be found in the
[entrypoint.sh](../../docker/entrypoint.sh) file which is used as the default entrypoint for the container
when it starts.

### Production Docker

Production docker essentially follows the same steps as above (what good would a local test be otherwise!)
but is executed via a github workflow. The resulting image is uploaded to our
[docker hub](https://registry.hub.docker.com/r/pcic/station-data-portal-frontend) for use where desired.

Specific steps are defined in the [github workflow](../../.github/workflows/docker-publish.yml) file.

When running in production we need to provide environment specific config, this config will closely
resemble the templates defined in the `config.bc.js` and `config.ynwt.js` files noted above and should be
mounted to `/app/config.js` within the container.
Loading

0 comments on commit 8cd71c4

Please sign in to comment.