Skip to content

Commit

Permalink
Deprecate use of process.env for runtime config
Browse files Browse the repository at this point in the history
- Replace process.env with window.env
- Move env variables into a config.js file
- Rework docker build to use file
- Rework docker build to use static built files
- Rework docker compose to load a config file
- Add makefile utility matching station-data-portal
- Add healthcheck to docker file
  • Loading branch information
Nospamas committed Dec 17, 2024
1 parent 0079586 commit 26a0540
Show file tree
Hide file tree
Showing 30 changed files with 177 additions and 147 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"ghcr.io/devcontainers/features/node:1": {
"version": "22"
},
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2.12.0": {},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.8.10"
}
Expand Down
5 changes: 3 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.git
build
node_modules
.idea

**/*.env*
8 changes: 1 addition & 7 deletions .env
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
REACT_APP_TILECACHE_URL=https://services.pacificclimate.org/mapproxy/service
REACT_APP_NCWMS_URL=https://beehive.pacificclimate.org/ncwms
REACT_APP_CE_ENSEMBLE_NAME=ce_files
REACT_APP_MAP_LAYER_ID_TYPE=dynamic
REACT_APP_MAP_LAYER_ID_PREFIX=x
REACT_APP_VARIABLE_OPTIONS=variable-options.yaml
REACT_APP_EXTERNAL_TEXT=external-text/default.yaml
PUBLIC_URL=%REPLACE_PUBLIC_URL%
3 changes: 1 addition & 2 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
REACT_APP_CE_BACKEND_URL=https://beehive.pacificclimate.org/pcex/api
REACT_APP_CE_BASE_PATH=
PUBLIC_URL=http://localhost:3001
3 changes: 1 addition & 2 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
REACT_APP_CE_BACKEND_URL=https://services.pacificclimate.org/pcex/api
REACT_APP_CE_BASE_PATH=/pcex/app
PUBLIC_URL=%REPLACE_PUBLIC_URL%
10 changes: 7 additions & 3 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ jobs:

steps:
- uses: actions/checkout@v2
- name: Export REACT_APP_CE_CURRENT_VERSION env var
- name: Export REACT_APP_APP_VERSION env var
run: |
git fetch --prune --unshallow
echo "REACT_APP_CE_CURRENT_VERSION=$(git describe --tags --abbrev=0) ($(git rev-parse --abbrev-ref HEAD):$(git log -1 --format=%h))" >> $GITHUB_ENV
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:
username: ${{ secrets.pcicdevops_at_dockerhub_username }}
password: ${{ secrets.pcicdevops_at_dockerhub_password }}
repository: pcic/climate-explorer-frontend
tag_with_ref: true
build_args: REACT_APP_CE_CURRENT_VERSION=${{ env.REACT_APP_CE_CURRENT_VERSION }}
build_args: REACT_APP_APP_VERSION=${{ env.REACT_APP_APP_VERSION }}
31 changes: 0 additions & 31 deletions Dockerfile

This file was deleted.

16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +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 = 30504
public_url = http://localhost:${port}

image:
@npm run build
@PCEX_TAG=$(tag) PCEX_PORT=$(port) docker compose -f docker/docker-compose.yaml build

up:
@PCEX_TAG=$(tag) PCEX_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:
@PCEX_TAG=$(tag) PCEX_PORT=$(port) docker compose -f docker/docker-compose.yaml down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Environment variables for configuring the app are:
- For production, set this to the path component of the URL for CE configured in our proxy server.
- **WARNING**: This **MUST** match the path component of `PUBLIC_URL` (see above).

`REACT_APP_CE_CURRENT_VERSION`
`REACT_APP_APP_VERSION`

- Current version of the app.
- This value should be set using `generate-commitish.sh` when the Docker image is built (see below).
Expand Down Expand Up @@ -270,10 +270,10 @@ Build a docker image:

```bash
docker build -t climate-explorer-frontend \
--build-arg REACT_APP_CE_CURRENT_VERSION="$(./generate-commitish.sh)" .
--build-arg REACT_APP_APP_VERSION="$(./generate-commitish.sh)" .
```

Setting build arg `REACT_APP_CE_CURRENT_VERSION` as above is the most reliable
Setting build arg `REACT_APP_APP_VERSION` as above is the most reliable
way to inject an accurate version into the final app. This value can be overridden
when the image is run, but it is not recommended, as it introduces the possibility
of error.
Expand Down
18 changes: 18 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM node:22-bookworm-slim

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

RUN npm install -g serve

COPY --chown=node build /app
COPY --chown=node docker/entrypoint.sh /app/docker/entrypoint.sh
WORKDIR /app

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

ENTRYPOINT ["docker/entrypoint.sh"]
13 changes: 13 additions & 0 deletions docker/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
window.env = {
PUBLIC_URL: "http://localhost:30504",
REACT_APP_TILECACHE_URL:
"https://beehive.pacificclimate.org/mapproxy/service",
REACT_APP_NCWMS_URL: "https://beehive.pacificclimate.org/ncwms",
REACT_APP_CE_BACKEND_URL: "https://beehive.pacificclimate.org/pcex/api",
REACT_APP_CE_ENSEMBLE_NAME: "ce_files",
REACT_APP_MAP_LAYER_ID_TYPE: "dynamic",
REACT_APP_MAP_LAYER_ID_PREFIX: "x",
REACT_APP_VARIABLE_OPTIONS: "variable-options.yaml",
REACT_APP_EXTERNAL_TEXT: "external-text/default.yaml",
REACT_APP_CE_BASE_PATH: "/",
};
24 changes: 24 additions & 0 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This docker-compose uses the following shell environment variables:
#
# `PCEX_TAG`: Image tag
# `PCEX_PORT`: External port to map to
# Note: These values are set as part of the makefile, initialize the container
# Using it.
#
# Their values are set appropriately in the Makefile for use on a workstation.
# For use in a production setting, either set the shell
# variables appropriately when invoking docker-compose, or modify (a copy of)
# this file with the desired values.

version: "3.2"
services:
frontend:
build:
context: ..
dockerfile: ./docker/Dockerfile
#image: pcic/station-data-portal-frontend:${PCEX_TAG}
container_name: station-data-portal-frontend
volumes:
- ./config.js:/app/config.js
ports:
- "${PCEX_PORT}:8080"
19 changes: 19 additions & 0 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/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
8 changes: 0 additions & 8 deletions entrypoint.sh

This file was deleted.

1 change: 0 additions & 1 deletion generate-commitish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ VERSIONTAG="$(git describe --tags --abbrev=0)"
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
COMMITSHA="$(git log -1 --format=%h)"

export REACT_APP_CE_CURRENT_VERSION="$VERSIONTAG ($BRANCH: $COMMITSHA)"
echo "$VERSIONTAG ($BRANCH: $COMMITSHA)"
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@
"scripts": {
"reinstall": "rm -rf ./node_modules; npm install",
"start": "react-scripts --openssl-legacy-provider start",
"start:rg": "CE_BACKEND_URL=https://services.pacificclimate.org/marmot/api NCWMS_URL=https://services.pacificclimate.org/marmot/ncwms TILECACHE_URL=https://tiles.pacificclimate.org/tilecache/tilecache.py CE_ENSEMBLE_NAME=all_downscale_files CE_CURRENT_VERSION=$(./generate-commitish.sh) react-scripts start",
"build": "react-scripts --openssl-legacy-provider build",
"start:rg": "CE_BACKEND_URL=https://services.pacificclimate.org/marmot/api NCWMS_URL=https://services.pacificclimate.org/marmot/ncwms TILECACHE_URL=https://tiles.pacificclimate.org/tilecache/tilecache.py CE_ENSEMBLE_NAME=all_downscale_files APP_VERSION=$(./generate-commitish.sh) react-scripts start",
"build": "REACT_APP_APP_VERSION=$(bash ./generate-commitish.sh) react-scripts --openssl-legacy-provider build",
"test": "react-scripts test --transformIgnorePatterns \"node_modules/(?!(leaflet|pcic-react-components)/)\"",
"jenkins-test": "CI=true react-scripts test --env=jsdom --transformIgnorePatterns \"node_modules/(?!(leaflet|pcic-react-components)/)\"",
"eject": "react-scripts eject",
Expand Down
12 changes: 12 additions & 0 deletions public/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
window.env = {
PUBLIC_URL: "http://localhost:3001",
REACT_APP_TILECACHE_URL:
"https://beehive.pacificclimate.org/mapproxy/service",
REACT_APP_NCWMS_URL: "https://beehive.pacificclimate.org/ncwms",
REACT_APP_CE_BACKEND_URL: "https://beehive.pacificclimate.org/pcex/api",
REACT_APP_CE_ENSEMBLE_NAME: "ce_files",
REACT_APP_MAP_LAYER_ID_TYPE: "dynamic",
REACT_APP_MAP_LAYER_ID_PREFIX: "x",
REACT_APP_VARIABLE_OPTIONS: "variable-options.yaml",
REACT_APP_EXTERNAL_TEXT: "external-text/default.yaml",
};
1 change: 1 addition & 0 deletions public/heathcheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// app is serving
32 changes: 20 additions & 12 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,41 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<link
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha256-MfvZlkHCEqatNoGiOXveE8FIwMzZg4W85qfrfIFBfYc= sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ=="
crossorigin="anonymous"
/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
See [Development Docs](docs/developer/development.md) for more information about configuring
public URLs for development and build time.
-->
<link
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha256-MfvZlkHCEqatNoGiOXveE8FIwMzZg4W85qfrfIFBfYc= sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ=="
crossorigin="anonymous"
/>
<title>Met Data Portal</title>
<script src="%PUBLIC_URL%/config.js"></script>
<title>PCIC Climate Explorer</title>
</head>

<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<noscript> You need to enable JavaScript to run this app. </noscript>
<!--[if lt IE 8]>
<p class="browserupgrade">
You are using an <strong>outdated</strong> browser. Please
Expand Down
69 changes: 16 additions & 53 deletions src/components/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ import marmot from "../../assets/marmot.png";
import styles from "./App.module.css";
import "../../../node_modules/react-input-range/lib/css/index.css";

/**
* When deploying the app to a URL that doesn't sit on the domain root we need to let the
* app router know the location that it at so it knows what portion of the URL it is
* responsible for.
*
* @returns string The base URL of the app
*/
const getBaseName = () => {
if (window.env.PUBLIC_URL?.indexOf(".") >= 0) {
return new URL(window.env.PUBLIC_URL).pathname;
}

return "";
};

export default class App extends React.Component {
constructor(props) {
super(props);
Expand Down Expand Up @@ -69,60 +84,8 @@ export default class App extends React.Component {
};

render() {
// Setting `Router.basename` correctly is a little tricky, for two reasons:
//
// 1. Dynamic deployment to different URLs.
// 2. Correctly handling requests containing client-side routes.
//
// Each of these concerns has different implications, as described below.
//
// 1. Dynamic deployment to different URLs.
//
// We don't want to set `basename` to a static value. If we did that,
// deploying the app to a different URL would require us to modify code
// and rebuild the app. This rules out both a static string here and
// `process.env.PUBLIC_URL`, which is set by the `homepage` property in
// `package.json`. (The latter approach is recommended in the
// create-react-app documentation
// (https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#building-for-relative-paths),
// but it does not meet our needs here.)
//
// Instead, we set `basename` from an environment variable, `CE_BASE_PATH`,
// which we set at deploy time, i.e., when the app is started.
//
// 2. Correctly handling requests containing client-side routes.
//
// Requests to the application be handled in two ways:
//
// A. Externally: That is, by the server proper. This occurs when
// a hyperlink to the app that originates outside the app (e.g., on
// a webpage somewhere) contains a client-side route component.
//
// B. Internally: By the app iteself, which is to say, by Router. This
// occurs when an internal `Link` is followed. `Router` intervenes and
// no request to the server is issued.
//
// Externally handled requests can cause errors: Most servers will, without
// special configuration, respond to the entire request URL, not just the
// base path. This results in errors, since what we'd like in this case
// is for the server to serve the app (at the base path), not a non-existent
// page identified by the base path plus the client-side routing part of
// URL.
//
// There are two possible solutions to this problem:
//
// 1. Configure the server to ignore the client-side routing
// part of the URL (i.e., to care only about the base path). See the
// create-react-app documentation
// (https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#serving-apps-with-client-side-routing).
//
// 2. Use a hash (#) to separate the base path from the client-side routes.
// This makes the client-side route component not a part of the URL path.
//
// We choose option 2, hence the hash (#) following the base path in the
// `Router.basename` value below.
return (
<Router basename={`${process.env.REACT_APP_CE_BASE_PATH}/#`}>
<Router basename={getBaseName()}>
<div>
<NavRoutes navSpec={this.navSpec} navClassName={styles.mainNav}>
<Navbar.Header>
Expand Down
2 changes: 1 addition & 1 deletion src/components/CanadaBaseMap/CanadaBaseMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class CanadaBaseMap extends React.Component {
>
<WMSTileLayer
attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url={process.env.REACT_APP_TILECACHE_URL}
url={window.env.REACT_APP_TILECACHE_URL}
layers={"osm"}
format={"image/png"}
transparent={true}
Expand Down
Loading

0 comments on commit 26a0540

Please sign in to comment.