diff --git a/.dockerignore b/.dockerignore index 8e3d0e1a7..bfc11840b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,28 @@ +# Python virtualenvs (note: "venv*" does NOT match ".venv" — list both) venv* env* +.venv/ +.env/ + +# local secrets / generated config oeplatform/securitysettings.py +# node deps (installed inside the image / named volume) node_modules/ + +# VCS + large runtime data that is bind-mounted, never needed at build time +.git/ +.gitignore +ontologies/ +media/ +static/ +data/ + +# editor / OS / build noise +.vscode/ +.idea/ +__pycache__/ +*.pyc +*.bak +*.bak-* +podman/ diff --git a/docker/docker-entrypoint.dev.sh b/docker/docker-entrypoint.dev.sh index 87c1e9d18..9020b8ddd 100755 --- a/docker/docker-entrypoint.dev.sh +++ b/docker/docker-entrypoint.dev.sh @@ -4,6 +4,16 @@ set -euo pipefail # give Postgres a moment sleep 5 +# ---------------------------------------------------------------- +# Ownership target for bind-mounted dirs. +# We reference the *numeric* uid:gid of the current process rather than +# the `appuser:appgroup` names: on macOS the host GID (e.g. 20) already +# exists in the base image, so no group literally named `appgroup` is +# created and `chown appuser:appgroup` fails with "invalid group". +# The container also runs as this (non-root) user via compose's `user:`, +# so any chown is at most a no-op — guard each call with `|| true`. +OWNER="$(id -u):$(id -g)" + # ---------------------------------------------------------------- # Bootstrap permissions on bind-mounted dirs so appuser can write # ---------------------------------------------------------------- @@ -13,8 +23,8 @@ for d in ontologies media/oeo_ext static; do # ensure the directory exists mkdir -p "$TARGET" - # make appuser own it - chown -R appuser:appgroup "$TARGET" + # make appuser own it (no-op when already owned; non-root can't chown) + chown -R "$OWNER" "$TARGET" || true # owner & group: read/write + conditional-exec (dirs executable, # files only if already marked) ; others: read + conditional-exec @@ -35,7 +45,7 @@ if [ ! -d "$ONT_DIR/oeo" ]; then unzip -q /tmp/ont.zip -d "$ONT_DIR" rm /tmp/ont.zip - chown -R appuser:appgroup "$ONT_DIR" + chown -R "$OWNER" "$ONT_DIR" || true chmod -R u+rwX,g+rwX,o+rX "$ONT_DIR" fi @@ -47,7 +57,7 @@ if [ ! -f "${MEDIA_DIR}/oeo_ext.owl" ]; then "$MEDIA_DIR/oeo_ext.owl" # fix perms on the new file - chown appuser:appgroup "$MEDIA_DIR/oeo_ext.owl" + chown "$OWNER" "$MEDIA_DIR/oeo_ext.owl" || true chmod u+rw,g+rw,o+rX "$MEDIA_DIR" fi @@ -59,7 +69,7 @@ SEC_DEF=/home/appuser/app/oeplatform/securitysettings.py.default if [ ! -f "$SEC" ]; then echo "Copying default securitysettings…" cp "$SEC_DEF" "$SEC" - chown appuser:appgroup "$SEC" + chown "$OWNER" "$SEC" || true chmod u+rw,g+rw,o+rX "$SEC" fi diff --git a/factsheet/oekg/connection.py b/factsheet/oekg/connection.py index 2f2d42dfa..bd28ee269 100644 --- a/factsheet/oekg/connection.py +++ b/factsheet/oekg/connection.py @@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later """ # noqa: 501 -import os from pathlib import Path from owlready2 import get_ontology @@ -23,13 +22,10 @@ RDF_DATABASES, USE_DOCKER, ) +from ontology.utils import get_ontology_version -versions = os.listdir( - Path(ONTOLOGY_ROOT, OPEN_ENERGY_ONTOLOGY_NAME) -) # TODO bad - windows dev will get path error - -version = max((d for d in versions), key=lambda d: [int(x) for x in d.split(".")]) onto_base_path = Path(ONTOLOGY_ROOT, OPEN_ENERGY_ONTOLOGY_NAME) +version = get_ontology_version(onto_base_path) path = onto_base_path / version # TODO bad - windows dev will get path error file = "oeo-full.owl" # TODO- set in settings diff --git a/ontology/utils.py b/ontology/utils.py index 0589ffe18..9a599fd77 100644 --- a/ontology/utils.py +++ b/ontology/utils.py @@ -24,11 +24,16 @@ def get_ontology_version(path, version=None): if not path.exists(): raise Http404 - versions = os.listdir(path) + # Only consider real version directories (e.g. "2.0.0"). This filters out + # entries like macOS' ".DS_Store" that would otherwise break the int() + # parse below (".DS_Store".split(".") -> ['', 'DS_Store'] -> int('')). + versions = [ + d + for d in os.listdir(path) + if (Path(path) / d).is_dir() and all(part.isdigit() for part in d.split(".")) + ] if not version: - version = max( - (d for d in versions), key=lambda d: [int(x) for x in d.split(".")] - ) + version = max(versions, key=lambda d: [int(x) for x in d.split(".")]) return version diff --git a/ontology/views.py b/ontology/views.py index c61c3440c..68cb41b60 100644 --- a/ontology/views.py +++ b/ontology/views.py @@ -60,15 +60,8 @@ def get_OEO_COMMON_DATA() -> dict: class OntologyAboutView(View): def get(self, request, ontology="oeo", version=None): onto_base_path = Path(ONTOLOGY_ROOT, ontology) - - if not onto_base_path.exists(): - raise Http404 - versions = os.listdir(onto_base_path) + version = get_ontology_version(onto_base_path, version) logger.info(f"Loaded oeo version {version}") - if not version: - version = max( - (d for d in versions), key=lambda d: [int(x) for x in d.split(".")] - ) return render( request, "ontology/about.html", @@ -185,10 +178,7 @@ def get( if not extension: extension = "owl" if not version: - version = max( - (d for d in os.listdir(onto_base_path)), - key=lambda d: [int(x) for x in d.split(".")], - ) + version = get_ontology_version(onto_base_path, version) if imports: file_path = onto_base_path / version / "imports" / f"{file}.{extension}" elif glossary: