From e4293fe6722af578effe0c91eae38984ba2ed1fc Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sun, 16 Nov 2025 15:32:06 -0500 Subject: [PATCH] refactor(ci): clean up docker and kubernetes deployments --- .dockerignore | 3 + .github/workflows/containers-publish.yml | 40 ++++------- .gitignore | 6 +- Dockerfile.prod | 52 ++++++++++++++ config/env/.env.prod.db | 3 - config/env/{env.dev => dev.env.example} | 2 +- config/env/env.prod.db | 3 - config/env/{env.prod => prod.env.example} | 6 +- deploy/{kind-config.yml => kind-config.yaml} | 0 deploy/manifests/balancer/base/configmap.yml | 19 ----- .../base/{deployment.yml => deployment.yaml} | 9 ++- .../base/{ingress.yml => ingress.yaml} | 10 +-- .../{kustomization.yml => kustomization.yaml} | 8 +-- deploy/manifests/balancer/base/namespace.yaml | 4 ++ .../balancer/base/secret.template.yaml | 29 ++++++++ .../base/{service.yml => service.yaml} | 2 +- .../balancer/overlays/dev/kustomization.yaml | 26 +++++++ .../balancer/overlays/dev/kustomization.yml | 11 --- .../{clusterissuer.yml => clusterissuer.yaml} | 0 deployment.sh | 21 ------ docker-compose.prod.yml | 9 +-- docker-compose.yml | 2 +- frontend/.gitignore | 2 - frontend/Dockerfile.prod | 19 ----- frontend/vite.config.ts | 2 +- server/Dockerfile.prod | 30 -------- server/Dockerfile.prodBackup | 71 ------------------- server/entrypoint.prod.sh | 3 +- 28 files changed, 157 insertions(+), 235 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile.prod delete mode 100644 config/env/.env.prod.db rename config/env/{env.dev => dev.env.example} (93%) delete mode 100644 config/env/env.prod.db rename config/env/{env.prod => prod.env.example} (78%) rename deploy/{kind-config.yml => kind-config.yaml} (100%) delete mode 100644 deploy/manifests/balancer/base/configmap.yml rename deploy/manifests/balancer/base/{deployment.yml => deployment.yaml} (76%) rename deploy/manifests/balancer/base/{ingress.yml => ingress.yaml} (62%) rename deploy/manifests/balancer/base/{kustomization.yml => kustomization.yaml} (51%) create mode 100644 deploy/manifests/balancer/base/namespace.yaml create mode 100644 deploy/manifests/balancer/base/secret.template.yaml rename deploy/manifests/balancer/base/{service.yml => service.yaml} (90%) create mode 100644 deploy/manifests/balancer/overlays/dev/kustomization.yaml delete mode 100644 deploy/manifests/balancer/overlays/dev/kustomization.yml rename deploy/manifests/cert-manager/{clusterissuer.yml => clusterissuer.yaml} (100%) delete mode 100644 deployment.sh delete mode 100644 frontend/Dockerfile.prod delete mode 100644 server/Dockerfile.prod delete mode 100644 server/Dockerfile.prodBackup diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..338e2ec5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +server/.gitignore +server/Dockerfile +server/entrypoint.sh diff --git a/.github/workflows/containers-publish.yml b/.github/workflows/containers-publish.yml index b178b0b2..64758fe9 100644 --- a/.github/workflows/containers-publish.yml +++ b/.github/workflows/containers-publish.yml @@ -34,36 +34,20 @@ jobs: # - name: 'Pull previous Docker container image: :latest' # run: docker pull "${DOCKER_REPOSITORY}:latest" || true - - name: "Pull previous Docker container image: frontend-static:latest" - run: docker pull "${DOCKER_REPOSITORY}/frontend-static:latest" || true + - name: "Pull previous Docker container image: app:latest" + run: docker pull "${DOCKER_REPOSITORY}/app:latest" || true - - name: "Build Docker container image: frontend-static:latest" + - name: "Build Docker container image: app:latest" run: | docker build \ - --cache-from "${DOCKER_REPOSITORY}/frontend-static:latest" \ - --file frontend/Dockerfile.demo \ - --build-arg SERVER_NAME=localhost \ - --tag "${DOCKER_REPOSITORY}/frontend-static:latest" \ - --tag "${DOCKER_REPOSITORY}/frontend-static:${DOCKER_TAG}" \ - frontend - - name: "Push Docker container image frontend-static:latest" - run: docker push "${DOCKER_REPOSITORY}/frontend-static:latest" + --cache-from "${DOCKER_REPOSITORY}/app:latest" \ + --file Dockerfile.prod \ + --tag "${DOCKER_REPOSITORY}/app:latest" \ + --tag "${DOCKER_REPOSITORY}/app:${DOCKER_TAG}" \ + . - - name: "Push Docker container image frontend-static:v*" - run: docker push "${DOCKER_REPOSITORY}/frontend-static:${DOCKER_TAG}" -# -# -# - name: 'Build Docker container image: backend:latest' -# run: | -# cd backend && \ -# make && \ -# docker image tag "${DOCKER_REPOSITORY}/backend/local:latest" "${DOCKER_REPOSITORY}/backend:latest" -# -# - name: Push Docker container image backend:latest -# run: docker push "${DOCKER_REPOSITORY}/backend:latest" -# -# - name: Push Docker container image backend:v* -# run: docker push "${DOCKER_REPOSITORY}/backend:${DOCKER_TAG}" + - name: "Push Docker container image app:latest" + run: docker push "${DOCKER_REPOSITORY}/app:latest" -# - name: Push Docker container image :v*" -# run: docker push "${DOCKER_REPOSITORY}:${DOCKER_TAG}" + - name: "Push Docker container image app:v*" + run: docker push "${DOCKER_REPOSITORY}/app:${DOCKER_TAG}" diff --git a/.gitignore b/.gitignore index bf4f8dfe..d2cdbd62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -config/* -.idea/ -env* \ No newline at end of file +config/env/* +!config/env/*.example +.idea/ \ No newline at end of file diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 00000000..cd1f3604 --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,52 @@ +# Multi-stage Dockerfile for Balancer Application +# Produces a single image with Django backend serving the React frontend + +# Stage 1: Build Frontend +FROM node:18 AS frontend-builder + +WORKDIR /frontend + +# Copy frontend package files +COPY frontend/package*.json ./ + +# Install dependencies +RUN npm ci --legacy-peer-deps + +# Copy frontend source +COPY frontend/ ./ + +# Build frontend - outputs to dist/ as configured in vite.config.ts +RUN npm run build + +# Stage 2: Build Backend +FROM python:3.11.4-slim-bullseye + +# Set work directory +WORKDIR /usr/src/app + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +# Install system dependencies +RUN apt-get update && apt-get install -y netcat && rm -rf /var/lib/apt/lists/* + +# Install Python dependencies +RUN pip install --upgrade pip +COPY server/requirements.txt . +RUN pip install -r requirements.txt + +# Copy backend application code +COPY server/ . + +# Copy frontend build from frontend-builder stage to where Django expects it +COPY --from=frontend-builder /frontend/dist ./build + +# Expose port +EXPOSE 8000 + +# Run entrypoint +ENTRYPOINT ["./entrypoint.prod.sh"] + +# Start gunicorn +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000", "--noreload"] diff --git a/config/env/.env.prod.db b/config/env/.env.prod.db deleted file mode 100644 index 3c73c247..00000000 --- a/config/env/.env.prod.db +++ /dev/null @@ -1,3 +0,0 @@ -POSTGRES_USER=set_me -POSTGRES_PASSWORD=set_me -POSTGRES_DB=balancer_prod \ No newline at end of file diff --git a/config/env/env.dev b/config/env/dev.env.example similarity index 93% rename from config/env/env.dev rename to config/env/dev.env.example index 22e70e5d..38d8e382 100644 --- a/config/env/env.dev +++ b/config/env/dev.env.example @@ -13,4 +13,4 @@ OPENAI_API_KEY= ANTHROPIC_API_KEY= PINECONE_API_KEY= EMAIL_HOST_USER= -EMAIL_HOST_PASSWORD= \ No newline at end of file +EMAIL_HOST_PASSWORD= diff --git a/config/env/env.prod.db b/config/env/env.prod.db deleted file mode 100644 index 3c73c247..00000000 --- a/config/env/env.prod.db +++ /dev/null @@ -1,3 +0,0 @@ -POSTGRES_USER=set_me -POSTGRES_PASSWORD=set_me -POSTGRES_DB=balancer_prod \ No newline at end of file diff --git a/config/env/env.prod b/config/env/prod.env.example similarity index 78% rename from config/env/env.prod rename to config/env/prod.env.example index 12b3491a..e8e59112 100644 --- a/config/env/env.prod +++ b/config/env/prod.env.example @@ -1,7 +1,6 @@ DEBUG=0 SECRET_KEY=change_this DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1] -DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1] SQL_ENGINE=django.db.backends.postgresql SQL_DATABASE=balancer_prod SQL_USER=set_me @@ -10,5 +9,8 @@ SQL_HOST=db SQL_PORT=5432 DATABASE=postgres LOGIN_REDIRECT_URL= +OPENAI_API_KEY= +ANTHROPIC_API_KEY= +PINECONE_API_KEY= EMAIL_HOST_USER= -EMAIL_HOST_PASSWORD= \ No newline at end of file +EMAIL_HOST_PASSWORD= diff --git a/deploy/kind-config.yml b/deploy/kind-config.yaml similarity index 100% rename from deploy/kind-config.yml rename to deploy/kind-config.yaml diff --git a/deploy/manifests/balancer/base/configmap.yml b/deploy/manifests/balancer/base/configmap.yml deleted file mode 100644 index c513fd61..00000000 --- a/deploy/manifests/balancer/base/configmap.yml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -data: - DEBUG: "1" - SECRET_KEY: "foo" - DJANGO_ALLOWED_HOSTS: "localhost 127.0.0.1 [::1] balancertestsite.com" - SQL_ENGINE: "django.db.backends.postgresql" - SQL_DATABASE: "balancer_dev" - SQL_USER: "balancer" - SQL_PASSWORD: "" - SQL_HOST: "" - SQL_PORT: "5432" - DATABASE: "postgres" - LOGIN_REDIRECT_URL: "" - OPENAI_API_KEY: "" - PINECONE_API_KEY: "" - REACT_APP_API_BASE_URL: "https://balancertestsite.com/" -kind: ConfigMap -metadata: - name: balancer-config diff --git a/deploy/manifests/balancer/base/deployment.yml b/deploy/manifests/balancer/base/deployment.yaml similarity index 76% rename from deploy/manifests/balancer/base/deployment.yml rename to deploy/manifests/balancer/base/deployment.yaml index 919d10c9..c50012c2 100644 --- a/deploy/manifests/balancer/base/deployment.yml +++ b/deploy/manifests/balancer/base/deployment.yaml @@ -3,7 +3,6 @@ kind: Deployment metadata: labels: app: balancer - service: backend name: balancer spec: replicas: 1 @@ -17,16 +16,16 @@ spec: app: balancer spec: containers: - - image: ghcr.io/codeforphilly/balancer-main/backend - name: balancer + - image: ghcr.io/codeforphilly/balancer-main/app + name: app envFrom: - - configMapRef: + - secretRef: name: balancer-config ports: - containerPort: 8000 readinessProbe: httpGet: - path: / + path: /admin/ port: 8000 initialDelaySeconds: 30 periodSeconds: 10 diff --git a/deploy/manifests/balancer/base/ingress.yml b/deploy/manifests/balancer/base/ingress.yaml similarity index 62% rename from deploy/manifests/balancer/base/ingress.yml rename to deploy/manifests/balancer/base/ingress.yaml index d931fd5f..fc98305b 100644 --- a/deploy/manifests/balancer/base/ingress.yml +++ b/deploy/manifests/balancer/base/ingress.yaml @@ -2,22 +2,22 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: balancer - annotations: - cert-manager.io/cluster-issuer: "letsencrypt-staging" + annotations: {} spec: ingressClassName: nginx tls: - hosts: - - balancertestsite.com + - HOSTNAME_PLACEHOLDER secretName: balancer-tls rules: - - host: balancertestsite.com + - host: HOSTNAME_PLACEHOLDER http: paths: + # All traffic routes to balancer service (which serves both API and frontend) - path: / pathType: Prefix backend: service: name: balancer port: - number: 80 + number: 8000 diff --git a/deploy/manifests/balancer/base/kustomization.yml b/deploy/manifests/balancer/base/kustomization.yaml similarity index 51% rename from deploy/manifests/balancer/base/kustomization.yml rename to deploy/manifests/balancer/base/kustomization.yaml index d9e5dd29..c7d2dcd1 100644 --- a/deploy/manifests/balancer/base/kustomization.yml +++ b/deploy/manifests/balancer/base/kustomization.yaml @@ -2,7 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - deployment.yml - - service.yml - - configmap.yml - - ingress.yml + - namespace.yaml + - deployment.yaml + - service.yaml + - ingress.yaml diff --git a/deploy/manifests/balancer/base/namespace.yaml b/deploy/manifests/balancer/base/namespace.yaml new file mode 100644 index 00000000..11df30ee --- /dev/null +++ b/deploy/manifests/balancer/base/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: balancer diff --git a/deploy/manifests/balancer/base/secret.template.yaml b/deploy/manifests/balancer/base/secret.template.yaml new file mode 100644 index 00000000..bebc8a71 --- /dev/null +++ b/deploy/manifests/balancer/base/secret.template.yaml @@ -0,0 +1,29 @@ +# Secret Template for Balancer Application +# +# This file documents the required secret keys for the balancer application. +# +# IMPORTANT: This file is a TEMPLATE only. Do NOT create a Secret manifest in this +# repository. Secrets should be created in each target cluster using cluster-specific +# tools (e.g., SealedSecrets in the cfp-sandbox-cluster). +# +apiVersion: v1 +kind: Secret +metadata: + name: balancer-config + namespace: balancer +type: Opaque +stringData: + DATABASE: postgres + DEBUG: '1' + DJANGO_ALLOWED_HOSTS: localhost 127.0.0.1 [::1] balancer.sandbox.k8s.phl.io + LOGIN_REDIRECT_URL: '' + OPENAI_API_KEY: openapi_key_here + PINECONE_API_KEY: pinecone_key_here + REACT_APP_API_BASE_URL: https://balancer.sandbox.k8s.phl.io/ + SECRET_KEY: randomly_generated_key_ere + SQL_ENGINE: django.db.backends.postgresql + SQL_HOST: sql_host_here + SQL_PORT: '5432' + SQL_DATABASE: balancer_dev + SQL_USER: balancer + SQL_PASSWORD: sql_password_here diff --git a/deploy/manifests/balancer/base/service.yml b/deploy/manifests/balancer/base/service.yaml similarity index 90% rename from deploy/manifests/balancer/base/service.yml rename to deploy/manifests/balancer/base/service.yaml index 2c839248..8f294d53 100644 --- a/deploy/manifests/balancer/base/service.yml +++ b/deploy/manifests/balancer/base/service.yaml @@ -7,7 +7,7 @@ metadata: spec: ports: - name: http - port: 80 + port: 8000 targetPort: 8000 selector: app: balancer diff --git a/deploy/manifests/balancer/overlays/dev/kustomization.yaml b/deploy/manifests/balancer/overlays/dev/kustomization.yaml new file mode 100644 index 00000000..d3975eb2 --- /dev/null +++ b/deploy/manifests/balancer/overlays/dev/kustomization.yaml @@ -0,0 +1,26 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: balancer + +resources: + - ../../base + +images: + - name: ghcr.io/codeforphilly/balancer-main/app + newTag: latest + +patches: + - target: + kind: Ingress + name: balancer + patch: |- + - op: add + path: /metadata/annotations/cert-manager.io~1cluster-issuer + value: letsencrypt-staging + - op: replace + path: /spec/tls/0/hosts/0 + value: localhost + - op: replace + path: /spec/rules/0/host + value: localhost diff --git a/deploy/manifests/balancer/overlays/dev/kustomization.yml b/deploy/manifests/balancer/overlays/dev/kustomization.yml deleted file mode 100644 index 92a6001b..00000000 --- a/deploy/manifests/balancer/overlays/dev/kustomization.yml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - "../../base" - -images: - - name: ghcr.io/codeforphilly/balancer-main/backend - newTag: "1.0.2" - -namespace: balancer diff --git a/deploy/manifests/cert-manager/clusterissuer.yml b/deploy/manifests/cert-manager/clusterissuer.yaml similarity index 100% rename from deploy/manifests/cert-manager/clusterissuer.yml rename to deploy/manifests/cert-manager/clusterissuer.yaml diff --git a/deployment.sh b/deployment.sh deleted file mode 100644 index 491ed457..00000000 --- a/deployment.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cd frontend - -## Frontend - -npm run i && npm run build - -cd .. - -docker compose -f docker-compose.prod.yml up --build # This is generating balancer-backend:latest image - -aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com - -echo "$CR_PAT" | docker login ghcr.io -u TineoC --password-stdin - -docker tag balancer-backend:latest ghcr.io/codeforphilly/balancer-main/backend:latest . - -docker tag balancer-backend:latest chrissst/balancer:latest - -docker push chrissst/balancer:latest \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index f6772b9c..0bba34b1 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,12 +1,13 @@ name: balancer-prod version: "3.8" + services: - backend: - image: balancer-backend + app: + image: balancer-app build: - context: server + context: . dockerfile: Dockerfile.prod ports: - "8000:8000" env_file: - - ./config/env/env.dev + - ./config/env/prod.env diff --git a/docker-compose.yml b/docker-compose.yml index aea1993b..c6238dda 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,7 +36,7 @@ services: ports: - "8000:8000" env_file: - - ./config/env/env.dev + - ./config/env/dev.env depends_on: - db volumes: diff --git a/frontend/.gitignore b/frontend/.gitignore index 6e10711f..d67a573c 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -12,8 +12,6 @@ dist dist-ssr *.local -config/env/env.dev - # Editor directories and files .vscode/* !.vscode/extensions.json diff --git a/frontend/Dockerfile.prod b/frontend/Dockerfile.prod deleted file mode 100644 index c0648913..00000000 --- a/frontend/Dockerfile.prod +++ /dev/null @@ -1,19 +0,0 @@ -FROM node:18 as builder - -WORKDIR /usr/src/app - -COPY package*.json ./ - -RUN npm ci --legacy-peer-deps - -COPY . . - -RUN npm run build - -FROM nginx:latest - -COPY nginx.conf /etc/nginx/conf.d/default.conf - -COPY --from=builder /usr/src/app/build /usr/share/nginx/html - -EXPOSE 80 \ No newline at end of file diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index cdf82274..1d907506 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -4,7 +4,7 @@ import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig({ build: { - outDir: '../server/build', // Custom output directory + outDir: 'dist', // Output to local dist directory assetsDir: 'static', }, plugins: [react()], diff --git a/server/Dockerfile.prod b/server/Dockerfile.prod deleted file mode 100644 index 97b2c142..00000000 --- a/server/Dockerfile.prod +++ /dev/null @@ -1,30 +0,0 @@ -# pull official base image -FROM python:3.11.4-slim-bullseye - - -# set work directory -WORKDIR /usr/src/app - -# set environment variables -ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED 1 - -# install system dependencies -RUN apt-get update && apt-get install -y netcat - -# install dependencies -RUN pip install --upgrade pip -COPY ./requirements.txt . -RUN pip install -r requirements.txt - -# copy project -COPY . /usr/src/app - -# Correct line endings in entrypoint.sh and make it executable -RUN sed -i 's/\r$//' entrypoint.sh && chmod +x entrypoint.sh - -# run entrypoint.sh -ENTRYPOINT ["./entrypoint.prod.sh"] - -# Default command to run on container start -CMD ["python", "manage.py", "runserver", "0.0.0.0:8000", "--noreload"] diff --git a/server/Dockerfile.prodBackup b/server/Dockerfile.prodBackup deleted file mode 100644 index 9c5244c6..00000000 --- a/server/Dockerfile.prodBackup +++ /dev/null @@ -1,71 +0,0 @@ -########### -# BUILDER # -########### - -# pull official base image -FROM python:3.11.4-slim-buster as builder - -# set work directory -WORKDIR /usr/src/app - -# set environment variables -ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED 1 - -# install system dependencies -RUN apt-get update && \ - apt-get install -y --no-install-recommends gcc - -# lint -# RUN pip install --upgrade pip -# RUN pip install flake8==6.0.0 -# COPY . /usr/src/app/ -# RUN flake8 --ignore=E501,F401 . - -# install python dependencies -COPY ./requirements.txt . -RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt - - -######### -# FINAL # -######### - -# pull official base image -FROM python:3.11.4-slim-buster - -# create directory for the app user -RUN mkdir -p /home/app - -# create the app user -RUN addgroup --system app && adduser --system --group app - -# create the appropriate directories -ENV HOME=/home/app -ENV APP_HOME=/home/app/web -RUN mkdir $APP_HOME -WORKDIR $APP_HOME - -# install dependencies -RUN apt-get update && apt-get install -y --no-install-recommends netcat -COPY --from=builder /usr/src/app/wheels /wheels -COPY --from=builder /usr/src/app/requirements.txt . -RUN pip install --upgrade pip -RUN pip install --no-cache /wheels/* - -# copy entrypoint.prod.sh -COPY ./entrypoint.prod.sh . -RUN sed -i 's/\r$//g' $APP_HOME/entrypoint.prod.sh -RUN chmod +x $APP_HOME/entrypoint.prod.sh - -# copy project -COPY . $APP_HOME - -# chown all the files to the app user -RUN chown -R app:app $APP_HOME - -# change to the app user -USER app - -# run entrypoint.prod.sh -ENTRYPOINT ["/home/app/web/entrypoint.prod.sh"] \ No newline at end of file diff --git a/server/entrypoint.prod.sh b/server/entrypoint.prod.sh index 9506422f..68dfbc88 100755 --- a/server/entrypoint.prod.sh +++ b/server/entrypoint.prod.sh @@ -4,7 +4,7 @@ if [ "$DATABASE" = "postgres" ] then echo "Waiting for postgres..." - while ! nc -z $SQL_HOST $SQL_PORT; do + while ! nc -z "$SQL_HOST" "$SQL_PORT"; do sleep 0.1 done @@ -18,4 +18,5 @@ python manage.py migrate python manage.py createsu # populate the database on start up python manage.py populatedb + exec "$@"