diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..5a40ce9f53 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,68 @@ +name: CI + +on: + workflow_dispatch: + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + node-version: ["24.x"] + env: + PLUGIN_API: true + DJANGO_VITE_DEV_MODE: true + + steps: + - uses: actions/checkout@v4 + + - name: Install uv and set the python version + uses: astral-sh/setup-uv@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install the project + run: uv sync --locked --dev + + - name: Install frontend packages + run: npm --prefix coldfront/static install + + - name: Check for lint violations + run: uv run ruff check + + - name: Check formatting + run: uv run ruff format --check + + - name: Check frontend with eslint and prettier + run: npm --prefix coldfront/static run check + + - name: Compile and bundle frontend static assets + run: npm --prefix coldfront/static run build + + - name: Check bundled frontend static assets have been commited + run: | + if [[ `git status --porcelain` ]]; then + echo "Error: pre-compiled bundled frontend static assets have not been committed" + git status + exit 1 + else + echo "Bundled frontend static assets check passed." + fi + + - name: Check licence with reuse + run: uv run reuse lint + + - name: Run tests + run: uv run coldfront test + + - name: Check for migrations + run: uv run coldfront makemigrations --check diff --git a/.gitignore b/.gitignore index 22fd9d0108..d8c9707614 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ coldfront.egg-info dist build +.zed/ *._* *.DS_Store *.swp @@ -24,3 +25,4 @@ db.json .devcontainer/* .bin/* coldfront-django.*.log +node_modules diff --git a/Containerfile.debugpy b/Containerfile.debugpy index 528eb50240..e8160819ae 100644 --- a/Containerfile.debugpy +++ b/Containerfile.debugpy @@ -1,20 +1,47 @@ +######################## +# 1) Frontend build stage +######################## +FROM node:20-alpine AS frontend +WORKDIR /app + +# Copy only npm manifests first for caching +COPY coldfront/static/package.json coldfront/static/package-lock.json ./coldfront/static/ +WORKDIR /app/coldfront/static +RUN npm ci + +# Copy the rest of the frontend sources +COPY coldfront/static ./ + +# Build (should output bundles + manifest) +RUN npm run build + + +######################## +# 2) Backend stage +######################## FROM python:3.12-slim-trixie -COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ +COPY --from=ghcr.io/astral-sh/uv:0.9.13 /uv /uvx /usr/local/bin/ ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - && rm -rf /var/lib/apt/lists/* +RUN apt-get update && rm -rf /var/lib/apt/lists/* WORKDIR /app -RUN --mount=type=cache,target=/root/.cache/uv \ - --mount=type=bind,source=uv.lock,target=uv.lock \ - --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ - uv sync --locked --no-install-project -COPY . /app -RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --extra prod --dev + +COPY pyproject.toml uv.lock ./ +RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \ + uv cache clean || rm -rf /root/.cache/uv/* +RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \ + uv sync --locked --no-install-project --extra prod + +# Copy backend code +COPY . . + +# Copy built bundles/manifest from frontend stage into the backend image +COPY --from=frontend /app/coldfront/static/bundles /app/coldfront/static/bundles + +RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \ + uv pip install debugpy ENV DEBUG=True ENV PYTHONUNBUFFERED=1 diff --git a/coldfront/config/base.py b/coldfront/config/base.py index cd47fa08cb..b1b2559051 100644 --- a/coldfront/config/base.py +++ b/coldfront/config/base.py @@ -1,12 +1,20 @@ +# SPDX-FileCopyrightText: (C) ColdFront Authors +# +# SPDX-License-Identifier: AGPL-3.0-or-later + """ Base Django settings for ColdFront project. """ +import importlib.util import os import sys import coldfront + from django.core.exceptions import ImproperlyConfigured from django.core.management.utils import get_random_secret_key + +import coldfront from coldfront.config.env import ENV, PROJECT_ROOT # ------------------------------------------------------------------------------ @@ -50,15 +58,13 @@ "django.contrib.humanize", ] -# Additional Apps -# Hack to fix fontawesome. Will be fixed in version 6 -sys.modules["fontawesome_free"] = __import__("fontawesome-free") INSTALLED_APPS += [ "crispy_forms", "crispy_bootstrap4", "django_q", "simple_history", - "fontawesome_free", + "django_vite", + "django_htmx", ] # ColdFront Apps @@ -91,6 +97,7 @@ "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "simple_history.middleware.HistoryRequestMiddleware", + "django_htmx.middleware.HtmxMiddleware", ] # ------------------------------------------------------------------------------ @@ -148,9 +155,20 @@ SETTINGS_EXPORT = [] STATIC_URL = "/static/" + +DJANGO_VITE = { + "default": { + "dev_mode": ENV.bool("DJANGO_VITE_DEV_MODE", default=False), + "dev_server_port": ENV.int("DJANGO_VITE_SERVER_PORT", default=5173), + "manifest_path": PROJECT_ROOT("coldfront/static/bundles/manifest.json"), + } +} + STATIC_ROOT = ENV.str("STATIC_ROOT", default=PROJECT_ROOT("static_root")) STATICFILES_DIRS = [ - PROJECT_ROOT("coldfront/static"), + PROJECT_ROOT("coldfront/static/bundles"), + PROJECT_ROOT("coldfront/static/assets"), + PROJECT_ROOT("coldfront/static/branding"), ] # Add local site static files if set diff --git a/coldfront/config/core.py b/coldfront/config/core.py index 48fd9a726b..5a89dffc1d 100644 --- a/coldfront/config/core.py +++ b/coldfront/config/core.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: (C) ColdFront Authors +# +# SPDX-License-Identifier: AGPL-3.0-or-later + from coldfront.config.base import SETTINGS_EXPORT from coldfront.config.env import ENV @@ -30,6 +34,11 @@ # ------------------------------------------------------------------------------ PROJECT_ENABLE_PROJECT_REVIEW = ENV.bool("PROJECT_ENABLE_PROJECT_REVIEW", default=True) +# ------------------------------------------------------------------------------ +# Enable EULA force agreement +# ------------------------------------------------------------------------------ +ALLOCATION_EULA_ENABLE = ENV.bool("ALLOCATION_EULA_ENABLE", default=False) + # ------------------------------------------------------------------------------ # Maximum number of projects per PI # ------------------------------------------------------------------------------ @@ -69,6 +78,8 @@ "RESEARCH_OUTPUT_ENABLE", "GRANT_ENABLE", "PUBLICATION_ENABLE", + "RESEARCH_OUTPUT_ENABLE", + "DJANGO_VITE", ] ADMIN_COMMENTS_SHOW_EMPTY = ENV.bool("ADMIN_COMMENTS_SHOW_EMPTY", default=True) diff --git a/coldfront/core/allocation/templates/allocation/allocation_add_users.html b/coldfront/core/allocation/templates/allocation/allocation_add_users.html index 81d56c857d..62b0950f0a 100644 --- a/coldfront/core/allocation/templates/allocation/allocation_add_users.html +++ b/coldfront/core/allocation/templates/allocation/allocation_add_users.html @@ -63,16 +63,4 @@