Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.git
.github
.gitlab-ci.yml

__pycache__/
*.py[cod]
*$py.class

.pytest_cache/
.coverage
htmlcov/

.venv/
venv/
env/

.env
*.env
config.ini

*.sqlite3
*.db

staticfiles/
media/

.DS_Store
.idea/
.vscode/
47 changes: 21 additions & 26 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]

services:
mysql:
image: mysql:8.0
Expand All @@ -24,20 +19,19 @@ jobs:
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h localhost" --health-interval=10s
--health-timeout=5s --health-retries=3
--health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot"
--health-interval=10s --health-timeout=5s --health-retries=10

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Change to the root directory
run: cd /home/runner/work

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
python-version: "3.12"
cache: pip
cache-dependency-path: requirements.txt

- name: Install dependencies
run: |
Expand All @@ -48,18 +42,12 @@ jobs:
- name: Create project config file
run: |
echo "# config.ini" > /home/runner/work/config.ini
echo "[database]" >> /home/runner/work/config.ini
echo "name=test_db" >> /home/runner/work/config.ini
echo "user=root" >> /home/runner/work/config.ini
echo "password=root" >> /home/runner/work/config.ini
echo "host=127.0.0.1" >> /home/runner/work/config.ini
echo "port=3306" >> /home/runner/work/config.ini
echo "[email]" >> /home/runner/work/config.ini
echo "from=test_g2p_help@ebi.ac.uk" >> /home/runner/work/config.ini
echo "host=smtp.ebi.ac.uk" >> /home/runner/work/config.ini
echo "port=465" >> /home/runner/work/config.ini
echo "mailing_list=test_g2p_admin@ebi.ac.uk" >> /home/runner/work/config.ini
echo "send_to_mailing_list=True" >> /home/runner/work/config.ini
echo "from=test_email" >> /home/runner/work/config.ini
echo "host=localhost" >> /home/runner/work/config.ini
echo "port=25" >> /home/runner/work/config.ini
echo "mailing_list=test_email" >> /home/runner/work/config.ini
echo "send_to_mailing_list=False" >> /home/runner/work/config.ini
echo "[settings]" >> /home/runner/work/config.ini
echo "DEBUG=True" >> /home/runner/work/config.ini
echo "ALLOWED_HOSTS=[]" >> /home/runner/work/config.ini
Expand All @@ -71,17 +59,24 @@ jobs:
echo "MAX_DISEASE_NAME_LENGTH=255" >> /home/runner/work/config.ini
echo "PUBLIC_APP_URL=http://localhost:5173" >> /home/runner/work/config.ini
echo "[g2p]" >> /home/runner/work/config.ini
echo "version=3.0.0" >> /home/runner/work/config.ini
echo "version=1.0.0" >> /home/runner/work/config.ini

- name: Run Tests
run: |
export SECRET_KEY=$(openssl rand -base64 12)
python manage.py makemigrations
python manage.py migrate
export DB_USERNAME=root
export DB_PASSWORD=root
export DB_HOST=127.0.0.1
export DB_NAME=test_db
export DB_PORT=3306
export DB_ENGINE=django.db.backends.mysql
python manage.py makemigrations --check --dry-run
python manage.py migrate --noinput
python -m coverage run --source=gene2phenotype_app --omit="*/tests/*" manage.py test gene2phenotype_app.tests
python -m coverage xml -o coverage.cobertura.xml
env:
PROJECT_CONFIG_PATH: /home/runner/work/config.ini
USE_SQLITE_TEST_DB: "false"
working-directory: gene2phenotype_project

- name: Code Coverage Summary
Expand All @@ -91,4 +86,4 @@ jobs:
format: markdown
output: both
fail_below_min: true
thresholds: '80 90'
thresholds: "80 90"
163 changes: 159 additions & 4 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,164 @@
# This is a placeholder GitLab CI configuration.

workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_TAG'
variables:
IMAGE_TAG: "$CI_COMMIT_TAG"
DOCKER_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
when: always
- if: '$CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_BRANCH'
variables:
IMAGE_TAG: "$CI_COMMIT_REF_SLUG-$CI_PIPELINE_IID-$CI_COMMIT_SHORT_SHA"
DOCKER_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-$CI_PIPELINE_IID-$CI_COMMIT_SHORT_SHA"
when: always
- when: never

hello-world:
stages:
- test
- build
- deploy

test:
stage: test
coverage: '/TOTAL.*\s+(\d+%)$/'
image: python:3.12-slim-bookworm
services:
- name: mysql:8.0
alias: mysql
variables:
MYSQL_ROOT_PASSWORD: ci-test-password
MYSQL_DATABASE: test_db
PROJECT_CONFIG_PATH: "$CI_PROJECT_DIR/config.ini"
VIRTUAL_ENV: "$CI_PROJECT_DIR/.venv"
DB_USERNAME: root
DB_PASSWORD: ci-test-password
DB_HOST: mysql
DB_NAME: test_db
DB_PORT: "3306"
DB_ENGINE: django.db.backends.mysql
USE_SQLITE_TEST_DB: "false"
before_script:
- |
apt-get update && apt-get install -y --no-install-recommends \
default-libmysqlclient-dev \
default-mysql-client \
build-essential \
pkg-config \
openssl && \
rm -rf /var/lib/apt/lists/*
- python -m venv "$VIRTUAL_ENV"
- . "$VIRTUAL_ENV/bin/activate"
- python -m pip install --upgrade pip
- python -m pip install -r requirements.txt
- python -m pip install coverage
- |
for i in $(seq 1 30); do
echo "Waiting for MySQL ($i/30)..."
if mysqladmin --host="$DB_HOST" --user="$DB_USERNAME" --password="$DB_PASSWORD" --ssl=0 ping; then
break
fi
if [ "$i" -eq 30 ]; then
echo "MySQL did not become ready"
exit 1
fi
sleep 2
done
- |
{
echo "# config.ini"
echo "[email]"
echo "from=test_email"
echo "host=localhost"
echo "port=25"
echo "mailing_list=test_email"
echo "send_to_mailing_list=False"
echo "[settings]"
echo "DEBUG=True"
echo "ALLOWED_HOSTS=[]"
echo "CSRF_TRUSTED_ORIGINS=[]"
echo "CORS_ALLOWED_ORIGINS=[]"
echo "AUTH_COOKIE_SECURE=False"
echo "STATIC_ROOT=/app/staticfiles"
echo "STATIC_URL=/static/"
echo "MAX_DISEASE_NAME_LENGTH=255"
echo "PUBLIC_APP_URL=localhost"
echo "[g2p]"
echo "version=1.0.0"
} > "$PROJECT_CONFIG_PATH"
script:
- echo "Hello, world!"
- cd gene2phenotype_project
- export SECRET_KEY="$(openssl rand -base64 12)"
- python manage.py makemigrations --check --dry-run
- python manage.py migrate --noinput
- python -m coverage run --source=gene2phenotype_app --omit="*/tests/*" manage.py test --noinput gene2phenotype_app.tests
- python -m coverage report --fail-under=80
- python -m coverage xml -o coverage.cobertura.xml
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: gene2phenotype_project/coverage.cobertura.xml
paths:
- gene2phenotype_project/coverage.cobertura.xml
expire_in: 1 week

build:
stage: build
needs: ["test"]
image: docker:27
services:
- docker:27-dind
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
script:
- docker build -t "$DOCKER_IMAGE" .
- docker tag "$DOCKER_IMAGE" "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
- docker push "$DOCKER_IMAGE"
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
- docker rmi "$DOCKER_IMAGE" "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
- docker logout "$CI_REGISTRY"

trigger_k8s_deploy_dev:
stage: deploy
rules:
- if: '$CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_BRANCH'
when: manual
- when: never
environment:
name: development
trigger:
project: "$K8S_MANIFESTS_PROJECT"
branch: "$K8S_MANIFESTS_BRANCH"
strategy: mirror
variables:
APP_NAME: g2p-api
IMAGE_TAG: "$CI_COMMIT_REF_SLUG-$CI_PIPELINE_IID-$CI_COMMIT_SHORT_SHA"
DOCKER_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-$CI_PIPELINE_IID-$CI_COMMIT_SHORT_SHA"
DEPLOY_COMMIT_SHA: "$CI_COMMIT_SHA"
DEPLOY_REF: "$CI_COMMIT_REF_NAME"
DEPLOY_PIPELINE_URL: "$CI_PIPELINE_URL"
DEPLOY_ENV: development
KUBE_CONTEXT: "$DEV_AGENT"
KUBE_NAMESPACE: "$DEV_NS"

trigger_k8s_deploy_prod:
stage: deploy
rules:
- if: '$CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_TAG'
when: manual
- when: never
environment:
name: production
trigger:
project: "$K8S_MANIFESTS_PROJECT"
branch: "$K8S_MANIFESTS_BRANCH"
strategy: mirror
variables:
APP_NAME: g2p-api
IMAGE_TAG: "$CI_COMMIT_TAG"
DOCKER_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
DEPLOY_COMMIT_SHA: "$CI_COMMIT_SHA"
DEPLOY_REF: "$CI_COMMIT_TAG"
DEPLOY_PIPELINE_URL: "$CI_PIPELINE_URL"
DEPLOY_ENV: production
KUBE_CONTEXT: "$PROD_AGENT"
KUBE_NAMESPACE: "$PROD_NS"
65 changes: 65 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
FROM python:3.12-slim-bookworm AS builder

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV VIRTUAL_ENV=/opt/venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"

# Build dependencies needed for mysqlclient
RUN apt-get update && apt-get install -y --no-install-recommends \
default-libmysqlclient-dev \
build-essential \
pkg-config \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /build

# Install dependencies
COPY requirements.txt .
RUN python -m venv "$VIRTUAL_ENV" && \
pip install --no-cache-dir -r requirements.txt

FROM python:3.12-slim-bookworm AS runtime

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV VIRTUAL_ENV=/opt/venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
ENV DJANGO_SETTINGS_MODULE=gene2phenotype_project.settings
ENV HOST=0.0.0.0
ENV PORT=8000

# Runtime dependency for mysqlclient
RUN apt-get update && apt-get install -y --no-install-recommends \
libmariadb3 \
&& rm -rf /var/lib/apt/lists/* && \
useradd -m -u 1000 django && \
mkdir -p /app && \
chown django:django /app

WORKDIR /app

COPY --from=builder /opt/venv /opt/venv

# Copy application code
COPY --chown=django:django . .

# Change to the gene2phenotype_project directory
WORKDIR /app/gene2phenotype_project

USER django

# Collect static files
RUN SECRET_KEY=dummy-key-for-collectstatic-only \
DB_ENGINE=django.db.backends.sqlite3 \
DB_NAME=:memory: \
DB_USERNAME=dummy \
DB_PASSWORD=dummy \
DB_HOST=localhost \
DB_PORT=5432 \
python manage.py collectstatic --noinput --clear

EXPOSE 8000

# Use gunicorn to serve the application
CMD ["sh", "-c", "exec gunicorn --bind ${HOST}:${PORT} --workers ${GUNICORN_WORKERS:-2} --timeout ${GUNICORN_TIMEOUT:-60} --access-logfile - --error-logfile - gene2phenotype_project.wsgi:application"]
Loading
Loading