Skip to content

Commit

Permalink
Merge pull request #17 from mrc-ide/mrc-4261
Browse files Browse the repository at this point in the history
mrc-4261 dockerise web app
  • Loading branch information
hillalex committed May 25, 2023
2 parents 521802c + 4eb357d commit 845ebf1
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 7 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/backend-test-and-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ jobs:
working-directory: api
run: ./scripts/build-and-push
- name: Smoke test
run: ./scripts/smoke-test
working-directory: api
run: ./scripts/run && ./scripts/smoke-test
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
pull_request:
branches:
- 'main'

env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}

Expand All @@ -27,6 +28,15 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Installing dependencies
run: npm ci --prefix=app
- name: Building app
Expand All @@ -36,10 +46,16 @@ jobs:
- name: Testing app
run: npm test --prefix=app -- --coverage
- name: Run dependencies
run: ./scripts/run-dependencies && ./api/scripts/run && ./scripts/smoke-test
run: ./scripts/run-dependencies && ./api/scripts/run && ./api/scripts/smoke-test
- name: Run integration tests
run : npm run integration-test --prefix=app
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
- name: Build image
working-directory: app
run: ./scripts/build-and-push
- name: Smoke test
working-directory: app
run: ./scripts/run && ./scripts/smoke-test
4 changes: 1 addition & 3 deletions scripts/smoke-test → api/scripts/smoke-test
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ else
TAG=$(git symbolic-ref --short HEAD)
fi

docker run --network=host -d mrcide/packit-api:$TAG

# The variable expansion below is 100s by default, or the argument provided
# The variable expansion below is 60s by default, or the argument provided
# to this script
TIMEOUT="${1:-60}"
wait_for
Expand Down
16 changes: 16 additions & 0 deletions app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM node:18

COPY package.json /app/package.json
COPY package-lock.json /app/package-lock.json
WORKDIR /app
RUN npm ci

COPY . /app

RUN npm run build

FROM nginx:stable

COPY nginx.conf /etc/nginx/nginx.conf

COPY --from=0 /app/build /usr/share/nginx/html
11 changes: 10 additions & 1 deletion app/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Packit Front End
#Packit Front End
Interface is built with [React library](https://reactjs.org)

## Requirements
Node 18.

## Available Scripts

**App can be started in the project directory when you run:**
Expand Down Expand Up @@ -44,3 +47,9 @@ Your app is ready to be deployed!

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.

## Building a docker image
The app is containerised into an image based on nginx. Note that in deployment this will be proxied, so no
security configuration is required here.
1. `./app/scripts/build` builds a docker image.
2. `./app/scripts/build-and-push` builds and pushes an image to dockerhub. This script is run on CI.
3. `./app/scripts/run` runs a built image with the current branch name.
51 changes: 51 additions & 0 deletions app/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
user nginx;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;


events {
worker_connections 1024;
}


http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;

keepalive_timeout 65;

server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;

# Don't cache these files
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
}

# CSS, JS, and source map files (do cache these)
location ~* \.(?:css|js|map)$ {
try_files $uri =404;
expires -1;
access_log off;
add_header Cache-Control "public";
}

# Any route that doesn't have a file extension, and which doesn't exist on the
# sever, we assume is a route within the React app, and just map it to index.html
location / {
try_files $uri $uri/ /index.html;
}
}
}
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"eject": "react-scripts eject",
"serve": "serve -s build"
},
"homepage": ".",
"eslintConfig": {
"extends": [
"react-app",
Expand Down
11 changes: 11 additions & 0 deletions app/scripts/build
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
HERE=$(dirname $0)
. $HERE/common

PACKAGE_ROOT=$(realpath $HERE/..)

docker build \
-t "$TAG_SHA" \
-t "$TAG_BRANCH" \
$PACKAGE_ROOT
11 changes: 11 additions & 0 deletions app/scripts/build-and-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e

HERE=$(dirname $0)
. "$HERE"/common

# build docker image
. "$HERE"/build

docker push $TAG_SHA
docker push $TAG_BRANCH
19 changes: 19 additions & 0 deletions app/scripts/common
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -ex

if [[ -v "GITHUB_SHA" ]]; then
GIT_ID=${GITHUB_SHA:0:7}
else
GIT_ID=$(git rev-parse --short=7 HEAD)
fi

if [[ -v "BRANCH_NAME" ]]; then
GIT_BRANCH=${BRANCH_NAME}
else
GIT_BRANCH=$(git symbolic-ref --short HEAD)
fi

ORG=mrcide
IMAGE_NAME=packit
TAG_SHA="${ORG}/${IMAGE_NAME}:${GIT_ID}"
TAG_BRANCH="${ORG}/${IMAGE_NAME}:${GIT_BRANCH}"
10 changes: 10 additions & 0 deletions app/scripts/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -e

HERE=$(dirname $0)
. $HERE/common

docker run -d \
--network=host \
--name packit \
"$TAG_BRANCH"
35 changes: 35 additions & 0 deletions app/scripts/smoke-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
wait_for()
{
echo "waiting up to $TIMEOUT seconds for web app"
start_ts=$(date +%s)
for i in $(seq $TIMEOUT); do

result=$(curl --write-out %{http_code} --silent --output /dev/null http://localhost)
if [[ $result -eq 200 ]]; then
end_ts=$(date +%s)
echo "Web app available after $((end_ts - start_ts)) seconds"
break
fi
sleep 1
echo "...still waiting"
done
return $result
}

if [[ -v "BRANCH_NAME" ]]; then
TAG=$BRANCH_NAME
else
TAG=$(git symbolic-ref --short HEAD)
fi

# The variable expansion below is 60s by default, or the argument provided
# to this script
TIMEOUT="${1:-60}"
wait_for
RESULT=$?
if [[ $RESULT -ne 200 ]]; then
echo "App did not become available in time"
exit 1
fi
exit 0
3 changes: 2 additions & 1 deletion app/src/apiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {
AsyncThunk, SerializedError,
} from "@reduxjs/toolkit";
import {RejectedErrorValue} from "./types";
import appConfig from "./config/appConfig";

const baseURL = "http://localhost:8080";
const baseURL = appConfig.apiUrl();

interface CustomAsyncThunkOptions extends AsyncThunkOptions<void, RejectedErrorValue> {
rejectValue: SerializedError
Expand Down
19 changes: 19 additions & 0 deletions app/src/config/appConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
interface AppConfig {
apiUrl: () => string
}

const devConfig: AppConfig = {
apiUrl: () => "http://localhost:8080"
};

const prodConfig: AppConfig = {
apiUrl: () => `https://${window.location.host}/packit/api`,
};

let appConfig = devConfig;

if (process.env.NODE_ENV == "production") {
appConfig = prodConfig;
}

export default appConfig;
29 changes: 29 additions & 0 deletions app/src/tests/appConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import appConfig from "../config/appConfig";

describe("api service", () => {

const OLD_ENV = process.env;

beforeEach(() => {
jest.resetModules(); // Important - it clears the cache
process.env = { ...OLD_ENV }; // Make a copy
});

afterAll(() => {
process.env = OLD_ENV; // Restore old environment
});

test("uses default config by default", () => {
expect(appConfig.apiUrl()).toBe("http://localhost:8080");
});

test("uses production config if node_env is production", () => {
/* eslint-disable */
// @ts-ignore
process.env.NODE_ENV = "production";
const appConfig = require("../config/appConfig").default;
/* eslint-enable */
expect(appConfig.apiUrl()).toBe("https://localhost/packit/api");
});
});

6 changes: 6 additions & 0 deletions scripts/clear-docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euxo pipefail

docker rm --force $(docker ps --all --quiet) || true
docker network prune --force
docker volume prune --force

0 comments on commit 845ebf1

Please sign in to comment.