Skip to content

Commit eec3a51

Browse files
authored
Merge pull request #94 from DEFRA/feature/NI-131-dockerise
NI-131: Dockerfile and .env files for fws-app
2 parents d7e48c2 + fd3b06e commit eec3a51

16 files changed

Lines changed: 534 additions & 54 deletions

.githooks/pre-commit

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/bin/bash
2+
3+
# Pre-commit hook to prevent committing .env files with secrets
4+
5+
ENV_FILES=(
6+
"docker/.env"
7+
"docker/.env.example"
8+
)
9+
10+
SECRETS_FOUND=false
11+
12+
for ENV_FILE in "${ENV_FILES[@]}"; do
13+
# Check if .env file is being committed
14+
if git diff --cached --name-only | grep -q "^${ENV_FILE}$"; then
15+
echo "⚠️ WARNING: Attempting to commit ${ENV_FILE}"
16+
echo ""
17+
18+
# Check for populated secrets
19+
SENSITIVE_VARS=(
20+
"AD_CLIENT_ID"
21+
"AD_CLIENT_SECRET"
22+
"AD_TENANT"
23+
"AD_COOKIE_PASSWORD"
24+
"FWS_API_KEY"
25+
)
26+
27+
for var in "${SENSITIVE_VARS[@]}"; do
28+
# Get the staged content
29+
staged_value=$(git diff --cached "${ENV_FILE}" | grep "^+${var}=" | cut -d'=' -f2- | tr -d ' ' || true)
30+
31+
# Also check current file content if it's being added
32+
if [[ -f "${ENV_FILE}" ]]; then
33+
current_value=$(grep "^${var}=" "${ENV_FILE}" 2>/dev/null | cut -d'=' -f2- | tr -d ' ' || true)
34+
35+
# Check if value is not empty and not a placeholder
36+
if [[ -n "$current_value" ]] && \
37+
[[ "$current_value" != "YOUR_VALUE_HERE" ]] && \
38+
[[ "$current_value" != "CHANGE_ME" ]] && \
39+
[[ "$current_value" != "DUMMY_VALUE" ]] && \
40+
[[ "$current_value" != "http://dummy.url" ]] && \
41+
[[ "$current_value" != "cookie_encryption_password_secure" ]]; then
42+
echo "❌ ERROR: Secret variable ${var} contains a value in ${ENV_FILE}"
43+
SECRETS_FOUND=true
44+
fi
45+
fi
46+
done
47+
48+
if [[ "$SECRETS_FOUND" = false ]]; then
49+
echo "✓ No secrets detected in ${ENV_FILE}"
50+
fi
51+
echo ""
52+
fi
53+
done
54+
55+
if [[ "$SECRETS_FOUND" = true ]]; then
56+
echo "Commit blocked! One or more .env files contain sensitive values."
57+
echo ""
58+
echo "To fix this:"
59+
echo " 1. Remove sensitive values from the .env files"
60+
echo " 2. Replace them with empty values or allowed placeholders:"
61+
echo " - DUMMY_VALUE"
62+
echo " - http://dummy.url"
63+
echo " - cookie_encryption_password_secure"
64+
echo " 3. Or remove the files from this commit with: git reset <file>"
65+
echo ""
66+
exit 1
67+
fi
68+
69+
exit 0

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ npm-debug.log
55
server/public/build
66
lcov.info
77
docker/.env
8+
coverage
9+
docker/.env

Dockerfile

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
ARG PARENT_VERSION=2.10.0-node22.21.1
2+
3+
FROM defradigital/node:${PARENT_VERSION} AS base
4+
ARG PORT=3000
5+
ENV PORT=${PORT}
6+
7+
USER root
8+
9+
# set -xe : -e abort on error : -x verbose output
10+
RUN set -xe \
11+
&& apk update && apk upgrade \
12+
&& rm -rf /var/cache/apk/* \
13+
&& mkdir -p /home/node/app
14+
15+
# Create app directory
16+
WORKDIR /home/node/app
17+
18+
# Copy the basic directories/files across
19+
RUN mkdir -p dist
20+
COPY --chown=root:root package*.json .
21+
COPY --chown=root:root ./index.js .
22+
COPY --chown=root:root ./bin ./bin
23+
COPY --chown=root:root ./client ./client
24+
COPY --chown=root:root ./server ./server
25+
26+
ARG BUILD_VERSION=v4.0.0-1-g6666666
27+
ARG GIT_COMMIT=0
28+
RUN echo -e "module.exports = { version: '$BUILD_VERSION', revision: '$GIT_COMMIT' }" > ./version.js
29+
30+
FROM base AS development
31+
32+
# Temporarily disable the postinstall NPM script
33+
RUN npm pkg set scripts.postinstall="echo no-postinstall" \
34+
&& npm ci --ignore-scripts --omit dev \
35+
&& npm run build
36+
37+
USER node
38+
EXPOSE ${PORT}/tcp
39+
EXPOSE 9229/tcp
40+
CMD [ "node", "--inspect=0.0.0.0:9229", "index.js" ]
41+
42+
FROM base AS production
43+
44+
# Temporarily disable the postinstall NPM script
45+
RUN npm pkg set scripts.postinstall="echo no-postinstall" \
46+
&& npm ci --ignore-scripts --omit dev \
47+
&& npm run build
48+
49+
USER node
50+
EXPOSE ${PORT}/tcp
51+
CMD [ "node", "index.js" ]

bin/build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
#!/bin/bash
1+
#!/bin/sh
22

33
npm run build:css

bin/build-css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/bin/bash
1+
#!/bin/sh
22

33
sass --style=expanded \
44
--load-path=node_modules \

bin/run-unit-tests

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
set -e
44

5-
# Ensure tests requring a Redis cache use a local containerised instance.
6-
REDIS_HOST=localhost
7-
REDIS_PORT=6379
8-
REDIS_TLS=false
5+
# Set environtment variables for test run
6+
set -a
7+
. docker/.env.example
8+
set +a
99

1010
lab -v -c -r console -o stdout -r lcov -o coverage/lcov.info

docker/.env.example

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# FWS app config
2+
# need to script the automation of picking up the localstack url
3+
# When using localstack the api url is in the format:
4+
# http://localstack-main:4566/_aws/execute-api/{api id}/local
5+
# use ./update-localstack-url.sh to update this value
6+
FWS_API_URL=http://dummy.url
7+
# if using localstack the authorizer function isn't supported so only needs a dummy key, if using dev will need a real key
8+
FWS_API_KEY=DUMMY_VALUE
9+
# These are secrets that need populating before starting the app
10+
# obtain from secret manager or another developer
11+
AD_CLIENT_ID=DUMMY_VALUE
12+
AD_CLIENT_SECRET=DUMMY_VALUE
13+
AD_TENANT=http://dummy.url
14+
AD_COOKIE_PASSWORD=cookie_encryption_password_secure
15+
LOG_LEVEL=info
16+
pm_cwd=
17+
HOME_PAGE=http://localhost:3000
18+
LOCAL_CACHE=false
19+
# REDIS_HOST needs to be fws-app-redis for when running in docker-compose, localhost for unit tests
20+
REDIS_HOST=localhost
21+
REDIS_PORT=6379

docker/app.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
services:
2+
fws-app:
3+
container_name: fws-app
4+
build:
5+
context: ..
6+
target: development
7+
ports:
8+
- "3000:3000"
9+
- "9229:9229"
10+
env_file:
11+
- .env
12+
depends_on:
13+
fws-app-redis:
14+
condition: service_healthy
15+
networks:
16+
- default
17+
- localstack
18+
deploy:
19+
restart_policy:
20+
condition: on-failure
21+
22+
networks:
23+
localstack:
24+
external: true
25+
name: docker_ls

docker/infrastructure.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@ services:
1111
test: ["CMD", "redis-cli", "ping"]
1212
interval: 5s
1313
timeout: 5s
14-
retries: 10
15-
14+
retries: 10

docker/scripts/start-local.sh

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
7+
ENV_FILE="$SCRIPT_DIR/../.env"
8+
readonly SEPARATOR="======================================"
9+
10+
echo "$SEPARATOR"
11+
echo "FWS App Local Startup Script"
12+
echo "$SEPARATOR"
13+
echo ""
14+
15+
# Step 0: Check for active Python virtual environment
16+
echo "Step 0: Checking Python virtual environment..."
17+
18+
if [[ -z "$VIRTUAL_ENV" ]]; then
19+
echo "Error: No Python virtual environment is active" >&2
20+
echo "" >&2
21+
echo "Please activate your Python virtual environment before running this script:" >&2
22+
echo " source ~/environments/my_env/bin/activate" >&2
23+
echo "" >&2
24+
echo "Or if using a different virtual environment:" >&2
25+
echo " source /path/to/your/venv/bin/activate" >&2
26+
echo "" >&2
27+
exit 1
28+
fi
29+
30+
echo "✓ Python virtual environment active: $VIRTUAL_ENV"
31+
echo ""
32+
33+
# Step 1: Check .env file for required variables
34+
echo "Step 1: Validating .env file..."
35+
36+
if [[ ! -f "$ENV_FILE" ]]; then
37+
echo "Error: .env file not found at $ENV_FILE" >&2
38+
exit 1
39+
fi
40+
41+
# Required variables (excluding FWS_API_URL which will be populated later)
42+
REQUIRED_VARS=(
43+
"FWS_API_KEY"
44+
"AD_CLIENT_ID"
45+
"AD_CLIENT_SECRET"
46+
"AD_TENANT"
47+
"AD_COOKIE_PASSWORD"
48+
"LOG_LEVEL"
49+
"HOME_PAGE"
50+
"REDIS_HOST"
51+
"REDIS_PORT"
52+
)
53+
54+
MISSING_VARS=()
55+
56+
for var in "${REQUIRED_VARS[@]}"; do
57+
# Extract value from .env file (handle both 'VAR=value' and 'export VAR=value')
58+
value=$(grep "^${var}=" "$ENV_FILE" | cut -d'=' -f2- | tr -d ' ' || true)
59+
60+
if [[ -z "$value" ]]; then
61+
MISSING_VARS+=("$var")
62+
fi
63+
done
64+
65+
if [[ ${#MISSING_VARS[@]} -gt 0 ]]; then
66+
echo "Error: The following required environment variables are missing or empty in .env:" >&2
67+
for var in "${MISSING_VARS[@]}"; do
68+
echo " - $var" >&2
69+
done
70+
echo "" >&2
71+
echo "Please populate these values in $ENV_FILE before running this script." >&2
72+
exit 1
73+
fi
74+
75+
echo "✓ All required environment variables are set"
76+
echo ""
77+
78+
# Step 2: Bootstrap LocalStack API
79+
echo "Step 2: Bootstrapping LocalStack API..."
80+
FWS_API_DIR="$PROJECT_ROOT/../fws-api"
81+
82+
if [[ ! -d "$FWS_API_DIR" ]]; then
83+
echo "Error: fws-api directory not found at $FWS_API_DIR" >&2
84+
echo "Please ensure fws-api is in the same parent directory as fws-app" >&2
85+
exit 1
86+
fi
87+
88+
cd "$FWS_API_DIR"
89+
echo "Running npm run bootstrap-debug in $FWS_API_DIR..."
90+
npm run bootstrap-debug
91+
92+
echo "✓ LocalStack API bootstrapped successfully"
93+
echo ""
94+
95+
# Step 3: Update LocalStack URL in .env
96+
echo "Step 3: Updating FWS_API_URL in .env..."
97+
cd "$SCRIPT_DIR"
98+
./update-localstack-url.sh
99+
100+
echo "✓ FWS_API_URL updated"
101+
echo ""
102+
103+
# Step 4: Start Docker Compose services
104+
echo "Step 4: Starting Docker Compose services..."
105+
cd "$PROJECT_ROOT"
106+
docker compose -f docker/infrastructure.yml -f docker/app.yml up -d --build
107+
108+
echo "✓ Services started successfully"
109+
echo ""
110+
111+
# Step 5: Display success message
112+
echo "$SEPARATOR"
113+
echo "✓ FWS App is now running!"
114+
echo "$SEPARATOR"
115+
echo ""
116+
echo "Access the application at:"
117+
echo " → http://localhost:3000"
118+
echo ""
119+
echo "To view logs:"
120+
echo " docker compose -f docker/infrastructure.yml -f docker/app.yml logs -f"
121+
echo ""
122+
echo "To stop services:"
123+
echo " docker compose -f docker/infrastructure.yml -f docker/app.yml down"
124+
echo ""

0 commit comments

Comments
 (0)