From 0e9d456e32551116e5009c51a2380bcbb246953c Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 2 Apr 2025 22:25:52 +0200 Subject: [PATCH 01/18] feat: modernize cookiecutter uv, rspack, tooling * Update cookiecutter template for modern Python workflows * Switch tooling to uv workspaces and hatchling backend * Simplify package manager and configuration options * Refactor Dockerfiles for flexibility, caching, and clarity * Remove deprecated JS, database, and search options * Align project metadata, versions, and naming conventions * Update Docker images and clean up documentation --- cookiecutter.json | 10 +- run-tests.sh | 8 +- scripts/bootstrap | 20 +-- scripts/server | 5 +- scripts/setup | 21 +-- {{cookiecutter.project_shortname}}/Dockerfile | 132 +++++++++++++++--- {{cookiecutter.project_shortname}}/Pipfile | 22 --- {{cookiecutter.project_shortname}}/README.md | 9 +- .../docker-compose.full.yml | 9 +- .../docker-compose.yml | 11 +- .../docker-services.yml | 31 +--- .../invenio.cfg | 11 +- .../pyproject.toml | 56 ++++++++ .../site/pyproject.toml | 32 ++++- .../site/setup.cfg | 4 +- .../{{cookiecutter.package_name}}/__init__.py | 1 + 16 files changed, 252 insertions(+), 130 deletions(-) delete mode 100644 {{cookiecutter.project_shortname}}/Pipfile create mode 100644 {{cookiecutter.project_shortname}}/pyproject.toml diff --git a/cookiecutter.json b/cookiecutter.json index bbc74e70..8a909b7d 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -6,12 +6,6 @@ "author_name": "CERN", "author_email": "info@{{ cookiecutter.project_site }}", "year": "{% now 'local', '%Y' %}", - "database": [ - "postgresql" - ], - "search": [ - "opensearch2" - ], "file_storage": [ "local", "S3" @@ -25,7 +19,7 @@ "no" ], "use_reduced_vocabs": [ - "no", - "yes" + "yes", + "no" ] } diff --git a/run-tests.sh b/run-tests.sh index 69509891..940c6c1b 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2019 CERN. # SPDX-FileCopyrightText: 2019 Northwestern University. -# SPDX-FileCopyrightText: 2024 KTH Royal Institute of Technology. +# SPDX-FileCopyrightText: 2024-2025 KTH Royal Institute of Technology. # SPDX-License-Identifier: MIT # quit on errors: @@ -16,7 +16,7 @@ TESTDIR=$(pwd) finish() { echo "Cleaning up." docker compose -f ${PROJECTDIR}/docker-compose.full.yml down --volumes --remove-orphans & - pipenv --rm || true + rm -rf "${PROJECTDIR}/.venv" || true rm -rf "${WORKDIR}" echo "Test setup cleaned." } @@ -35,7 +35,7 @@ cookiecutter --no-input -o "$WORKDIR" . \ PROJECTDIR=${WORKDIR}/${PROJECT_NAME} export PROJECTDIR -# Check local installation (this also generates the Pipfile.lock) +# Check local installation (this also generates the uv.lock) ${TESTDIR}/scripts/bootstrap cd ${PROJECTDIR} @@ -45,7 +45,7 @@ git init git add -A # Update MANIFEST.in -pipenv run check-manifest -u || true +uv run check-manifest -u || true # Build application docker images ${TESTDIR}/scripts/build-images.sh diff --git a/scripts/bootstrap b/scripts/bootstrap index b60105d3..40bc4d18 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -1,24 +1,26 @@ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2019 CERN. # SPDX-FileCopyrightText: 2019 Northwestern University. +# Copyright (C) 2025 KTH Royal Institute of Technology. # SPDX-License-Identifier: MIT set -e cd ${PROJECTDIR} -pipfile_lock_path="./Pipfile.lock" +uv_lock_path="./uv.lock" -if [ ! -f $pipfile_lock_path ]; then - echo "'Pipfile.lock' not found. Generating via 'pipenv lock'..." - pipenv lock +if [ ! -f $uv_lock_path ]; then + echo "'uv.lock' not found. Generating via 'uv lock'..." + uv lock fi -# Installs all packages specified in Pipfile.lock -pipenv sync +# Install all dependency groups so check-manifest is available +uv sync --all-groups +export INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage # Build assets -INVENIO_LOCAL_INSTANCE_PATH=$(pipenv run invenio shell --no-term-title -c "print(app.instance_path)") +INVENIO_LOCAL_INSTANCE_PATH=$(uv run invenio shell --no-term-title -c "print(app.instance_path)") cp -r ./static/. ${INVENIO_LOCAL_INSTANCE_PATH}/static/ cp -r ./assets/. ${INVENIO_LOCAL_INSTANCE_PATH}/assets/ -pipenv run invenio collect -v -pipenv run invenio webpack buildall +uv run invenio collect -v +uv run invenio webpack buildall diff --git a/scripts/server b/scripts/server index 3b1db717..f54459cc 100755 --- a/scripts/server +++ b/scripts/server @@ -1,6 +1,7 @@ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2019 CERN. # SPDX-FileCopyrightText: 2019 Northwestern University. +# Copyright (C) 2025 KTH Royal Institute of Technology. # SPDX-License-Identifier: MIT set -e @@ -11,9 +12,9 @@ export FLASK_ENV=development export INVENIO_SQLALCHEMY_DATABASE_URI="postgresql+psycopg2://{{cookiecutter.project_shortname}}:{{cookiecutter.project_shortname}}@localhost/{{cookiecutter.project_shortname}}" # Start Worker and Server -pipenv run celery -A invenio_app.celery worker -l INFO & pid_celery=$! +uv run celery -A invenio_app.celery worker -l INFO & pid_celery=$! -pipenv run invenio run \ +uv run invenio run \ --cert "$script_path"/../docker/nginx/test.crt \ --key "$script_path"/../docker/nginx/test.key & pid_server=$! diff --git a/scripts/setup b/scripts/setup index de12b05f..3dc2fb55 100755 --- a/scripts/setup +++ b/scripts/setup @@ -2,6 +2,7 @@ # SPDX-FileCopyrightText: 2019 CERN. # SPDX-FileCopyrightText: 2019 Northwestern University. # SPDX-FileCopyrightText: 2021 Esteban J. G. Gabancho. +# Copyright (C) 2025 KTH Royal Institute of Technology. # SPDX-License-Identifier: MIT set -e @@ -9,19 +10,19 @@ set -e cd ${PROJECTDIR} # Clean redis -pipenv run invenio shell --no-term-title -c "import redis; redis.StrictRedis.from_url(app.config['CACHE_REDIS_URL']).flushall(); print('Cache cleared')" -pipenv run invenio db destroy --yes-i-know -pipenv run invenio db init create -pipenv run invenio index destroy --force --yes-i-know -pipenv run invenio index init --force -pipenv run invenio index queue init purge +uv run invenio shell --no-term-title -c "import redis; redis.StrictRedis.from_url(app.config['CACHE_REDIS_URL']).flushall(); print('Cache cleared')" +uv run invenio db destroy --yes-i-know +uv run invenio db init create +uv run invenio index destroy --force --yes-i-know +uv run invenio index init --force +uv run invenio index queue init purge if [ $COOKIECUTTER_FILE_STORAGE -eq "S3" ] then -pipenv run invenio files location s3-default s3://default --default +uv run invenio files location s3-default s3://default --default else -pipenv run invenio files location --default 'default-location' $(pipenv run invenio shell --no-term-title -c "print(app.instance_path)") +uv run invenio files location --default 'default-location' $(uv run invenio shell --no-term-title -c "print(app.instance_path)") fi # Create admin role to rectict access -pipenv run invenio roles create admin -pipenv run invenio access allow superuser-access role admin +uv run invenio roles create admin +uv run invenio access allow superuser-access role admin diff --git a/{{cookiecutter.project_shortname}}/Dockerfile b/{{cookiecutter.project_shortname}}/Dockerfile index e6685cff..75da8226 100644 --- a/{{cookiecutter.project_shortname}}/Dockerfile +++ b/{{cookiecutter.project_shortname}}/Dockerfile @@ -1,29 +1,125 @@ # Dockerfile that builds a fully functional image of your app. # # This image installs all Python dependencies for your application. It's based -# on Almalinux (https://github.com/inveniosoftware/docker-invenio) -# and includes Pip, Pipenv, Node.js, NPM and some few standard libraries +# on Alpine Linux. For other base images (AlmaLinux, Debian, etc.) see the +# multiple flavors available at https://github.com/inveniosoftware/docker-invenio. +# This image includes Python, uv, Node.js, PNPM +# and includes Pip, Pipenv, Node.js, PNPM and some few standard libraries # Invenio usually needs. -# -# Note: It is important to keep the commands in this file in sync with your -# bootstrap script located in ./scripts/bootstrap. -FROM registry.cern.ch/inveniosoftware/almalinux:1 +ARG ALPINE_VERSION=3.23.3 +ARG PYTHON_SERIES=3.12 +ARG JS_PACKAGE_MANAGER=pnpm@10.29.2 +ARG WORKING_DIR=/opt/invenio +ARG VIRTUAL_ENV=/opt/env +ARG INVENIO_INSTANCE_PATH=${WORKING_DIR}/var/instance -COPY site ./site -COPY Pipfile Pipfile.lock ./ -RUN pipenv install --deploy --system +FROM ghcr.io/astral-sh/uv:alpine AS builder +ARG JS_PACKAGE_MANAGER +ARG PYTHON_SERIES +ARG WORKING_DIR +ARG VIRTUAL_ENV +ARG INVENIO_INSTANCE_PATH -COPY ./docker/uwsgi/ ${INVENIO_INSTANCE_PATH} -COPY ./invenio.cfg ${INVENIO_INSTANCE_PATH} -COPY ./templates/ ${INVENIO_INSTANCE_PATH}/templates/ +ENV LANG=en_US.UTF-8 \ + LANGUAGE=en_US:en \ + LC_ALL=en_US.UTF-8 \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + UV_NO_MANAGED_PYTHON=1 \ + UV_PYTHON_DOWNLOADS=never \ + UV_SYSTEM_PYTHON=1 \ + UV_PROJECT_ENVIRONMENT=${VIRTUAL_ENV} \ + UV_CACHE_DIR=/opt/.cache/uv \ + WORKING_DIR=${WORKING_DIR} \ + VIRTUAL_ENV=${VIRTUAL_ENV} \ + INVENIO_INSTANCE_PATH=${INVENIO_INSTANCE_PATH} \ + PATH=${VIRTUAL_ENV}/bin:${PATH} + +WORKDIR ${WORKING_DIR} + +RUN apk add --no-cache \ + "python3~=${PYTHON_SERIES}" \ + "python3-dev~=${PYTHON_SERIES}" \ + autoconf automake bash binutils build-base cairo file gcc ghostscript git \ + libtool libxml2-dev libxslt-dev linux-headers musl-dev nodejs npm openssl xmlsec xmlsec-dev + +RUN npm install -g "${JS_PACKAGE_MANAGER}" + +RUN set -eux; \ + uv venv "${VIRTUAL_ENV}"; \ + "${VIRTUAL_ENV}/bin/python" -m ensurepip --upgrade; \ + "${VIRTUAL_ENV}/bin/python" -m pip install --no-cache-dir --upgrade pip; \ + mkdir -p "${INVENIO_INSTANCE_PATH}" /opt/.cache/uv + +COPY . . +COPY ./invenio.cfg ${INVENIO_INSTANCE_PATH}/ +COPY ./docker/uwsgi/ ${INVENIO_INSTANCE_PATH}/ COPY ./app_data/ ${INVENIO_INSTANCE_PATH}/app_data/ +COPY ./templates/ ${INVENIO_INSTANCE_PATH}/templates/ COPY ./translations/ ${INVENIO_INSTANCE_PATH}/translations/ -COPY ./ . -RUN cp -r ./static/. ${INVENIO_INSTANCE_PATH}/static/ && \ - cp -r ./assets/. ${INVENIO_INSTANCE_PATH}/assets/ && \ - invenio collect --verbose && \ - invenio webpack buildall +RUN --mount=type=cache,target=/opt/.cache/uv uv sync --frozen --no-dev + +ENV INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage \ + PNPM_HOME=/root/.local/share/pnpm + +RUN --mount=type=cache,target=/root/.local/share/pnpm/store \ + set -eux; \ + export PATH="${PNPM_HOME}:${PATH}"; \ + mkdir -p "${INVENIO_INSTANCE_PATH}/static" "${INVENIO_INSTANCE_PATH}/assets"; \ + cp -r ./static/. "${INVENIO_INSTANCE_PATH}/static/"; \ + cp -r ./assets/. "${INVENIO_INSTANCE_PATH}/assets/"; \ + uv run invenio collect --verbose; \ + uv run invenio webpack buildall; \ + uv cache clean; \ + rm -rf "${INVENIO_INSTANCE_PATH}/assets/node_modules" \ + "${INVENIO_INSTANCE_PATH}/assets/.pnpm-store" \ + "${INVENIO_INSTANCE_PATH}/assets/.npm" + +FROM alpine:${ALPINE_VERSION} AS runtime +ARG PYTHON_SERIES +ARG WORKING_DIR +ARG VIRTUAL_ENV +ARG INVENIO_INSTANCE_PATH +ARG IMAGE_BUILD_TIMESTAMP +ARG SENTRY_RELEASE + +ENV LANG=en_US.UTF-8 \ + LANGUAGE=en_US:en \ + LC_ALL=en_US.UTF-8 \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + UV_NO_MANAGED_PYTHON=1 \ + UV_PYTHON_DOWNLOADS=never \ + UV_SYSTEM_PYTHON=1 \ + UV_PROJECT_ENVIRONMENT=${VIRTUAL_ENV} \ + UV_NO_CACHE=1 \ + HOME=/opt/invenio \ + WORKING_DIR=${WORKING_DIR} \ + VIRTUAL_ENV=${VIRTUAL_ENV} \ + INVENIO_INSTANCE_PATH=${INVENIO_INSTANCE_PATH} \ + INVENIO_IMAGE_BUILD_TIMESTAMP="${IMAGE_BUILD_TIMESTAMP}" \ + SENTRY_RELEASE="${SENTRY_RELEASE}" \ + PATH=${VIRTUAL_ENV}/bin:${PATH} + +WORKDIR ${WORKING_DIR} + +RUN apk add --no-cache \ + "python3~=${PYTHON_SERIES}" \ + bash cairo fontconfig ghostscript libxslt ttf-dejavu uwsgi-python3 xmlsec + +COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV} +COPY --from=builder ${WORKING_DIR} ${WORKING_DIR} + +# we can remove these 2 lines if we do not want to include uv and uvx in the runtime image +COPY --from=builder /usr/local/bin/uv /usr/local/bin/uv +COPY --from=builder /usr/local/bin/uvx /usr/local/bin/uvx + +RUN set -eux; \ + mkdir -p ${INVENIO_INSTANCE_PATH}/data ${INVENIO_INSTANCE_PATH}/archive; \ + chown -R 100:101 ${INVENIO_INSTANCE_PATH}; \ + chmod -R u+w ${INVENIO_INSTANCE_PATH} -ENTRYPOINT [ "bash", "-c"] +USER 100:101 +ENTRYPOINT ["bash", "-c"] diff --git a/{{cookiecutter.project_shortname}}/Pipfile b/{{cookiecutter.project_shortname}}/Pipfile deleted file mode 100644 index 35827d0c..00000000 --- a/{{cookiecutter.project_shortname}}/Pipfile +++ /dev/null @@ -1,22 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] -check-manifest = ">=0.25" - -[packages] -invenio-app-rdm = {extras = ["{{ cookiecutter.search }}"{% if cookiecutter.file_storage == 'S3' %}, "s3"{% endif %}], version = "~=13.0.0"} -{%- if cookiecutter.site_code == 'yes'%} -{{cookiecutter.project_shortname}} = {editable = true, path="./site"} -{% endif %} -uwsgi = ">=2.0" -uwsgitop = ">=0.11" -uwsgi-tools = ">=1.1.1" - -[requires] -python_version = "3.12" - -[pipenv] -allow_prereleases = false diff --git a/{{cookiecutter.project_shortname}}/README.md b/{{cookiecutter.project_shortname}}/README.md index cde692c7..1ab8a88a 100644 --- a/{{cookiecutter.project_shortname}}/README.md +++ b/{{cookiecutter.project_shortname}}/README.md @@ -28,8 +28,8 @@ Following is an overview of the generated files and folders: | Name | Description | |---|---| | ``Dockerfile`` | Dockerfile used to build your application image. | -| ``Pipfile`` | Python requirements installed via [pipenv](https://pipenv.pypa.io) | -| ``Pipfile.lock`` | Locked requirements (generated on first install). | +| ``pyproject.toml`` | Python requirements installed via [uv](https://docs.astral.sh/uv/). | +| ``uv.lock`` | Locked requirements (generated on first install). | | ``app_data`` | Application data such as vocabularies. | | ``assets`` | Web assets (CSS, JavaScript, LESS, JSX templates) used in the Webpack build. | | ``docker`` | Example configuration for NGINX and uWSGI. | @@ -47,3 +47,8 @@ Following is an overview of the generated files and folders: To learn how to configure, customize, deploy and much more, visit the [InvenioRDM Documentation](https://inveniordm.docs.cern.ch/). + +## Development + +install local instance with: +`invenio-cli init RDM --template ~/src/cookiecutter-invenio-rdm` diff --git a/{{cookiecutter.project_shortname}}/docker-compose.full.yml b/{{cookiecutter.project_shortname}}/docker-compose.full.yml index 02721798..d4423476 100644 --- a/{{cookiecutter.project_shortname}}/docker-compose.full.yml +++ b/{{cookiecutter.project_shortname}}/docker-compose.full.yml @@ -13,7 +13,7 @@ # - UI application: UWSGI (not exposed) # - API application: UWSGI (not exposed) # - Cache: Redis (exposed port: 6379) -# - DB: (PostgresSQL/MySQL) (exposed port: 5432 or 3306) +# - DB: PostgreSQL (exposed port: 5432) # - Message queue: RabbitMQ (exposed ports: 5672, 15672) # - Search platform: (OpenSearch) (exposed ports: 9200, 9600) # - OpenSearch Dashboard/Kibana (view OS/ES indexes) (exposed ports: 5601) @@ -47,17 +47,10 @@ services: extends: file: docker-services.yml service: opensearch-dashboards - {%- if cookiecutter.database == 'postgresql'%} pgadmin: extends: file: docker-services.yml service: pgadmin - {%- elif cookiecutter.database == 'mysql'%} - phpmyadmin: - extends: - file: docker-services.yml - service: phpmyadmin - {%- endif %} {%- endif %} {%- if cookiecutter.file_storage == 'S3'%} s3: diff --git a/{{cookiecutter.project_shortname}}/docker-compose.yml b/{{cookiecutter.project_shortname}}/docker-compose.yml index 15b5aef2..f8e17e29 100644 --- a/{{cookiecutter.project_shortname}}/docker-compose.yml +++ b/{{cookiecutter.project_shortname}}/docker-compose.yml @@ -9,10 +9,10 @@ # # Following services are included: # - Cache: Redis (exposed port: 6379) -# - DB: (PostgresSQL/MySQL) (exposed port: 5432 or 3306) +# - DB: PostgreSQL (exposed port: 5432) # - Message queue: RabbitMQ (exposed ports: 5672, 15672) # - OpenSearch (exposed ports: 9200, 9600) -# - Kibana (view ES indexes) (exposed ports: 5601) +# - Kibana (view OS indexes) (exposed ports: 5601) # services: cache: @@ -43,17 +43,10 @@ services: extends: file: docker-services.yml service: opensearch-dashboards - {%- if cookiecutter.database == 'postgresql'%} pgadmin: extends: file: docker-services.yml service: pgadmin - {%- elif cookiecutter.database == 'mysql'%} - phpmyadmin: - extends: - file: docker-services.yml - service: phpmyadmin - {%- endif %} {%- endif %} {%- if cookiecutter.file_storage == 'S3'%} s3: diff --git a/{{cookiecutter.project_shortname}}/docker-services.yml b/{{cookiecutter.project_shortname}}/docker-services.yml index e0259e15..edf6873b 100644 --- a/{{cookiecutter.project_shortname}}/docker-services.yml +++ b/{{cookiecutter.project_shortname}}/docker-services.yml @@ -16,11 +16,7 @@ services: - "INVENIO_COMMUNITIES_IDENTITIES_CACHE_REDIS_URL=redis://cache:6379/4" - "INVENIO_SEARCH_HOSTS=['search:9200']" - "INVENIO_SECRET_KEY=CHANGE_ME" - {%- if cookiecutter.database == 'postgresql'%} - "INVENIO_SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://{{cookiecutter.project_shortname}}:{{cookiecutter.project_shortname}}@db/{{cookiecutter.project_shortname}}" - {%- elif cookiecutter.database == 'mysql'%} - - "INVENIO_SQLALCHEMY_DATABASE_URI=mysql+pymysql://{{cookiecutter.project_shortname}}:{{cookiecutter.project_shortname}}@db/{{cookiecutter.project_shortname}}" - {%- endif %} - "INVENIO_PROXYFIX_CONFIG={'x_for': 1, 'x_proto': 1}" - "INVENIO_RATELIMIT_STORAGE_URL=redis://cache:6379/3" frontend: @@ -36,9 +32,8 @@ services: read_only: true ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:6379:6379" - {%- if cookiecutter.database == 'postgresql'%} db: - image: postgres:14.13 + image: postgres:16.11 restart: "unless-stopped" environment: - "POSTGRES_USER={{cookiecutter.project_shortname}}" @@ -56,26 +51,6 @@ services: PGADMIN_DEFAULT_PASSWORD: "{{cookiecutter.project_shortname}}" volumes: - ./docker/pgadmin/servers.json:/pgadmin4/servers.json - {%- elif cookiecutter.database == 'mysql'%} - db: - image: mysql - restart: "unless-stopped" - environment: - - "MYSQL_ROOT_PASSWORD={{cookiecutter.project_shortname}}" - - "MYSQL_DATABAE={{cookiecutter.project_shortname}}" - - "MYSQL_USER={{cookiecutter.project_shortname}}" - - "MYSQL_PASSWORD={{cookiecutter.project_shortname}}" - ports: - - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:3306:3306" - phpmyadmin: - image: phpmyadmin/phpmyadmin - restart: "unless-stopped" - ports: - - '${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:8080:80' - environment: - PMA_HOST: db - MYSQL_ROOT_PASSWORD: {{cookiecutter.project_shortname}} - {%- endif %} # note: RabbitMQ uses the hostname to build the Mnesia data directory path (cf. https://www.rabbitmq.com/docs/configure) # using a randomly generated hostname "forgets" about old queued messages (e.g. celery tasks) with new builds, # and hard-coding the Mnesia path with random hostnames leads to connection issues on startup for old names @@ -100,7 +75,7 @@ services: mq: condition: service_started search: - image: opensearchproject/opensearch:2.17.1 + image: opensearchproject/opensearch:2.19.4 restart: "unless-stopped" environment: # Settings only for development. DO NOT use in production! @@ -124,7 +99,7 @@ services: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:9200:9200" - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:9600:9600" opensearch-dashboards: - image: opensearchproject/opensearch-dashboards:2.17.1 + image: opensearchproject/opensearch-dashboards:2.19.4 ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5601:5601" expose: diff --git a/{{cookiecutter.project_shortname}}/invenio.cfg b/{{cookiecutter.project_shortname}}/invenio.cfg index 6b686819..2eacf04a 100644 --- a/{{cookiecutter.project_shortname}}/invenio.cfg +++ b/{{cookiecutter.project_shortname}}/invenio.cfg @@ -9,7 +9,7 @@ https://inveniordm.docs.cern.ch/operate/customize/configuration/. from datetime import datetime from invenio_i18n import lazy_gettext as _ - +from invenio_app_rdm import __version__ def _(x): # needed to avoid start time failure with lazy strings return x @@ -41,11 +41,7 @@ TRUSTED_HOSTS = ['0.0.0.0', 'localhost', '127.0.0.1'] # See https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/ # TODO: Set -{%- if cookiecutter.database == 'postgresql'%} SQLALCHEMY_DATABASE_URI="postgresql+psycopg2://{{cookiecutter.project_shortname}}:{{cookiecutter.project_shortname}}@localhost/{{cookiecutter.project_shortname}}" -{%- elif cookiecutter.database == 'mysql'%} -SQLALCHEMY_DATABASE_URI="mysql+pymysql://{{cookiecutter.project_shortname}}:{{cookiecutter.project_shortname}}@localhost/{{cookiecutter.project_shortname}}" -{%- endif %} # Invenio-App @@ -228,12 +224,15 @@ SEARCH_INDEX_PREFIX = "{{ cookiecutter.project_shortname }}-" # Invenio-Administration # ---------------------- -from invenio_app_rdm import __version__ ADMINISTRATION_DISPLAY_VERSIONS = [ ("invenio-app-rdm", f"v{__version__}"), ("{{ cookiecutter.project_shortname }}", "v1.0.0"), ] +# Asset bundling configuration +# --------------------------- +WEBPACKEXT_PROJECT = "invenio_assets.webpack:rspack_project" +"""Project asset bundling configuration. See https://invenio-assets.readthedocs.io/en/latest/configuration.html.""" # Invenio-Stats # ---------------------- diff --git a/{{cookiecutter.project_shortname}}/pyproject.toml b/{{cookiecutter.project_shortname}}/pyproject.toml new file mode 100644 index 00000000..57a6f7b4 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/pyproject.toml @@ -0,0 +1,56 @@ +{%- set project_shortname = cookiecutter.project_shortname -%} +{%- set app_rdm_version = "~=14.0.0b3.dev3" -%} +{%- set app_rdm_base = "invenio-app-rdm" ~ app_rdm_version -%} +{%- set app_rdm_extras = ['opensearch2'] -%} +{%- if cookiecutter.file_storage == 'S3' -%} + {%- set app_rdm_extras = app_rdm_extras + ['s3'] -%} +{%- endif -%} +{%- if cookiecutter.site_code == 'yes' -%} + {%- set site_dist_name = cookiecutter.package_name + '_package' -%} +{%- endif -%} + +[project] +name = "{{ cookiecutter.project_name | slugify() }}" +description = "Add your description here" +readme = "README.md" +version = "14.0.0" +requires-python = ">= 3.12" + +dependencies = [ + "invenio-app-rdm{% if app_rdm_extras %}[{{ app_rdm_extras | join(',') }}]{% endif %}{{ app_rdm_version }}", + "python-dotenv>=0.21.0", + "uwsgi>=2.0", + "uwsgitop>=0.11", + "uwsgi-tools>=1.1.1", + "lxml-html-clean>=0.1.1", + {% if cookiecutter.development_tools == 'yes' -%} + "invenio-cli>=1.0.0", + {% endif -%} + {% if cookiecutter.site_code == 'yes' -%} + "{{ site_dist_name }}", + {%- endif %} +] + +{% if cookiecutter.site_code == 'yes' -%} +[tool.uv.sources] +"{{ site_dist_name }}" = { workspace = true } +[tool.uv.workspace] +members = ["site"] + +[tool.hatch.build.targets.wheel] +packages = ["site/{{ cookiecutter.package_name }}"] +{%- endif %} + +[project.optional-dependencies] +sentry = [ + "sentry-sdk>=1.45,<2.0.0", +] +tests = [ + "pytest-invenio>=3.0.0,<4.0.0", +] + +[dependency-groups] +dev = [ + "flask-debugtoolbar>=0.16.0", + "pytest-invenio>=3.0.0,<4.0.0", +] diff --git a/{{cookiecutter.project_shortname}}/site/pyproject.toml b/{{cookiecutter.project_shortname}}/site/pyproject.toml index ec4e1ea4..1fabaa89 100644 --- a/{{cookiecutter.project_shortname}}/site/pyproject.toml +++ b/{{cookiecutter.project_shortname}}/site/pyproject.toml @@ -1,3 +1,31 @@ +{%- set package_name = cookiecutter.package_name + '_package' -%} +[project] +name = "{{package_name}}" +version = "1.0.0" +description = "Add your description here" +requires-python = ">= 3.12" + +[project.entry-points."invenio_base.blueprints"] +{{cookiecutter.package_name}}_views = "{{cookiecutter.package_name}}.views:create_blueprint" + +[project.entry-points."invenio_assets.webpack"] +{{cookiecutter.package_name}}_theme = "{{cookiecutter.package_name}}.webpack:theme" + + [build-system] -requires = ["setuptools", "wheel", "babel>2.8"] -build-backend = "setuptools.build_meta" +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["{{cookiecutter.package_name}}"] + +[tool.isort] +profile = "black" + +[tool.pydocstyle] +add_ignore = ["D401", "D403"] + +[tool.pytest.ini_options] +addopts = ["--isort", "--pydocstyle", "--doctest-glob=\"*.rst\"", "--doctest-modules", "--cov={{cookiecutter.package_name}}", "--cov-report=term-missing", "--exclude-warning-annotations"] +testpaths = "tests {{cookiecutter.package_name}}" +live_server_scope = "module" diff --git a/{{cookiecutter.project_shortname}}/site/setup.cfg b/{{cookiecutter.project_shortname}}/site/setup.cfg index 226c143f..db863e81 100644 --- a/{{cookiecutter.project_shortname}}/site/setup.cfg +++ b/{{cookiecutter.project_shortname}}/site/setup.cfg @@ -1,6 +1,6 @@ - +{%- set package_name = cookiecutter.package_name + '_package' %} [metadata] -name = {{cookiecutter.project_shortname}} +name = {{package_name}} [options.extras_require] tests = diff --git a/{{cookiecutter.project_shortname}}/site/{{cookiecutter.package_name}}/__init__.py b/{{cookiecutter.project_shortname}}/site/{{cookiecutter.package_name}}/__init__.py index e69de29b..3dc1f76b 100644 --- a/{{cookiecutter.project_shortname}}/site/{{cookiecutter.package_name}}/__init__.py +++ b/{{cookiecutter.project_shortname}}/site/{{cookiecutter.package_name}}/__init__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" From 63d3b41f6f0095185ddbabb1db8e86c0c329cd68 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 1 Apr 2026 15:00:21 +0200 Subject: [PATCH 02/18] wip: update rdm version to latest --- {{cookiecutter.project_shortname}}/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/{{cookiecutter.project_shortname}}/pyproject.toml b/{{cookiecutter.project_shortname}}/pyproject.toml index 57a6f7b4..aec7b24c 100644 --- a/{{cookiecutter.project_shortname}}/pyproject.toml +++ b/{{cookiecutter.project_shortname}}/pyproject.toml @@ -1,5 +1,5 @@ {%- set project_shortname = cookiecutter.project_shortname -%} -{%- set app_rdm_version = "~=14.0.0b3.dev3" -%} +{%- set app_rdm_version = "~=14.0.0b9.dev0" -%} {%- set app_rdm_base = "invenio-app-rdm" ~ app_rdm_version -%} {%- set app_rdm_extras = ['opensearch2'] -%} {%- if cookiecutter.file_storage == 'S3' -%} From c15952f781d2ab70e7f9b72266ca2b2f41c2fdcc Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 1 Apr 2026 15:00:54 +0200 Subject: [PATCH 03/18] wip: add base_image options for cookiecutter template * Introduced base_image field with options for "debian" and "alpine" in cookiecutter.json. --- cookiecutter.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cookiecutter.json b/cookiecutter.json index 8a909b7d..e84eae9b 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -18,6 +18,10 @@ "yes", "no" ], + "base_image": [ + "debian", + "alpine" + ], "use_reduced_vocabs": [ "yes", "no" From b07091605e16da24bbec755a36d0dba1929c655d Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 1 Apr 2026 15:48:47 +0200 Subject: [PATCH 04/18] wip: add base_image option and frontend image build --- run-tests.sh | 1 + scripts/build-images.sh | 3 +++ scripts/wait-for-services.sh | 2 +- {{cookiecutter.project_shortname}}/README.md | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/run-tests.sh b/run-tests.sh index 940c6c1b..0ea7502b 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -28,6 +28,7 @@ export PROJECT_NAME cookiecutter --no-input -o "$WORKDIR" . \ project_name=${PROJECT_NAME} \ + base_image=${COOKIECUTTER_BASE_IMAGE:-debian} \ database=${COOKIECUTTER_DATABASE:-postgresql} \ opensearch=${COOKIECUTTER_OPENSEARCH:-opensearch2} \ file_storage=${COOKIECUTTER_FILE_STORAGE:-local} diff --git a/scripts/build-images.sh b/scripts/build-images.sh index 848145bd..45b929b3 100755 --- a/scripts/build-images.sh +++ b/scripts/build-images.sh @@ -7,3 +7,6 @@ cd ${PROJECTDIR} # Build application image docker build . --tag ${PROJECT_NAME} --build-arg include_assets=true + +# Build frontend image used by docker-compose.full.yml +docker build ./docker/nginx --tag ${PROJECT_NAME}-frontend diff --git a/scripts/wait-for-services.sh b/scripts/wait-for-services.sh index 7afb982c..31acf0e0 100755 --- a/scripts/wait-for-services.sh +++ b/scripts/wait-for-services.sh @@ -19,7 +19,7 @@ check_ready() { done } -if [ $COOKIECUTTER_FILE_STORAGE -eq "S3" ] +if [ "${COOKIECUTTER_FILE_STORAGE}" = "S3" ] then _s3_check(){ curl --output /dev/null --silent --head --fail http://localhost:9000/minio/health/live &>/dev/null;} check_ready "S3" _s3_check diff --git a/{{cookiecutter.project_shortname}}/README.md b/{{cookiecutter.project_shortname}}/README.md index 1ab8a88a..c46610d4 100644 --- a/{{cookiecutter.project_shortname}}/README.md +++ b/{{cookiecutter.project_shortname}}/README.md @@ -27,7 +27,7 @@ Following is an overview of the generated files and folders: | Name | Description | |---|---| -| ``Dockerfile`` | Dockerfile used to build your application image. | +| ``Dockerfile`` | Dockerfile used to build your application image on {{ cookiecutter.base_image }}. | | ``pyproject.toml`` | Python requirements installed via [uv](https://docs.astral.sh/uv/). | | ``uv.lock`` | Locked requirements (generated on first install). | | ``app_data`` | Application data such as vocabularies. | From 48bd415117971363baaac9bbff41e47231f213d1 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Thu, 2 Apr 2026 02:04:56 +0200 Subject: [PATCH 05/18] wip: enhance Dockerfile for pnpm and uv integration * Added support for pnpm installation and configuration. * Updated Dockerfile to include necessary dependencies for uv. * Improved directory structure and permissions for application. --- {{cookiecutter.project_shortname}}/Dockerfile | 128 +++++++++++++++++- 1 file changed, 121 insertions(+), 7 deletions(-) diff --git a/{{cookiecutter.project_shortname}}/Dockerfile b/{{cookiecutter.project_shortname}}/Dockerfile index 75da8226..5de85022 100644 --- a/{{cookiecutter.project_shortname}}/Dockerfile +++ b/{{cookiecutter.project_shortname}}/Dockerfile @@ -1,11 +1,124 @@ -# Dockerfile that builds a fully functional image of your app. +{%- if cookiecutter.base_image == "debian" %} +# syntax=docker/dockerfile:1 + +# +# Copyright (C) {{ cookiecutter.year }} Northwestern University. +# Copyright (C) {{ cookiecutter.year }} Frontmatter. +# Copyright (C) {{ cookiecutter.year }} KTH Royal Institute of Technology. +# +# Invenio is free software; you can redistribute it and/or modify it +# under the terms of the MIT License; see LICENSE file for more details. +# + +# Temporary inline Debian base image. +# Keep this block aligned with the external base image and replace the whole +# block with `FROM AS base` once that image exists. +ARG BUILD_PLATFORM=linux/amd64 +ARG PYTHON_VERSION=3.14 +ARG OS_VERSION=trixie +ARG NODE_VERSION=24 + +FROM --platform=${BUILD_PLATFORM} python:${PYTHON_VERSION}-${OS_VERSION} AS base + +LABEL org.opencontainers.image.source=https://github.com/inveniosoftware/docker-invenio + +ENV \ + WORKING_DIR=/opt/invenio \ + INVENIO_INSTANCE_PATH=/opt/invenio/var/instance \ + INVENIO_USER_ID=1000 + +# Create relevant application directories +RUN mkdir -p \ + ${WORKING_DIR}/src \ + ${INVENIO_INSTANCE_PATH}/data \ + ${INVENIO_INSTANCE_PATH}/archive \ + ${INVENIO_INSTANCE_PATH}/static + +# Invenio file will be in /src +WORKDIR ${WORKING_DIR}/src + +# Set folder permissions +RUN chgrp -R 0 ${WORKING_DIR} && \ + chmod -R g=u ${WORKING_DIR} && \ + useradd invenio --uid ${INVENIO_USER_ID} --gid 0 && \ + chown -R invenio:root ${WORKING_DIR} + +RUN \ + --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update && apt-get upgrade --yes \ + && \ + # Install Node.js deb source + curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - \ + && \ + # Install system dependencies + apt-get install --yes --no-install-recommends \ + build-essential \ + fonts-dejavu \ + locales \ + nodejs \ + npm \ + && \ + # Install pnpm + npm install -g pnpm@latest-10 + +# Setup uv +# From https://docs.astral.sh/uv/guides/integration/docker/ +# but using docker.io/astral/uv because failure to fetch oauth token +COPY --from=docker.io/astral/uv:0.11 /uv /uvx /bin/ + +# Set locale +RUN localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 +ENV \ + LANG=en_US.UTF-8 \ + LANGUAGE=en_US:en \ + LC_ALL=en_US.UTF-8 + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +# Once tests is successful, replace the above block with: +# FROM AS base +# Cookiecutter application layers start here. + +FROM base AS app-base + +COPY site ./site +COPY README.md pyproject.toml uv.lock ./ +RUN uv sync --frozen --no-dev + +COPY ./docker/uwsgi/ ${INVENIO_INSTANCE_PATH} +COPY ./invenio.cfg ${INVENIO_INSTANCE_PATH} +COPY ./templates/ ${INVENIO_INSTANCE_PATH}/templates/ +COPY ./app_data/ ${INVENIO_INSTANCE_PATH}/app_data/ +COPY ./translations/ ${INVENIO_INSTANCE_PATH}/translations/ +COPY ./ . + +RUN cp -r ./static/. ${INVENIO_INSTANCE_PATH}/static/ && \ + cp -r ./assets/. ${INVENIO_INSTANCE_PATH}/assets/ && \ + invenio collect --verbose && \ + invenio webpack buildall + +ENTRYPOINT [ "bash", "-c"] + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +# Alpine image +{%- else %} +# syntax=docker/dockerfile:1 + +# +# Copyright (C) {{ cookiecutter.year }} CERN. +# Copyright (C) {{ cookiecutter.year }} KTH Royal Institute of Technology. # -# This image installs all Python dependencies for your application. It's based -# on Alpine Linux. For other base images (AlmaLinux, Debian, etc.) see the -# multiple flavors available at https://github.com/inveniosoftware/docker-invenio. -# This image includes Python, uv, Node.js, PNPM -# and includes Pip, Pipenv, Node.js, PNPM and some few standard libraries -# Invenio usually needs. +# Invenio is free software; you can redistribute it and/or modify it +# under the terms of the MIT License; see LICENSE file for more details. +# + +# image size is around 1.16 GB. ARG ALPINE_VERSION=3.23.3 ARG PYTHON_SERIES=3.12 @@ -123,3 +236,4 @@ RUN set -eux; \ USER 100:101 ENTRYPOINT ["bash", "-c"] +{%- endif %} From c4961c311edbb55966bb27acadf2367671163933 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Thu, 2 Apr 2026 03:54:10 +0200 Subject: [PATCH 06/18] wip: remove alpine base image option from template * Simplified base image selection by removing alpine option. * Ensures consistency in Dockerfile usage with debian base image. --- cookiecutter.json | 3 +- {{cookiecutter.project_shortname}}/Dockerfile | 172 +++--------------- 2 files changed, 30 insertions(+), 145 deletions(-) diff --git a/cookiecutter.json b/cookiecutter.json index e84eae9b..6a0457e3 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -19,8 +19,7 @@ "no" ], "base_image": [ - "debian", - "alpine" + "debian" ], "use_reduced_vocabs": [ "yes", diff --git a/{{cookiecutter.project_shortname}}/Dockerfile b/{{cookiecutter.project_shortname}}/Dockerfile index 5de85022..5af6d4b2 100644 --- a/{{cookiecutter.project_shortname}}/Dockerfile +++ b/{{cookiecutter.project_shortname}}/Dockerfile @@ -10,9 +10,10 @@ # under the terms of the MIT License; see LICENSE file for more details. # -# Temporary inline Debian base image. -# Keep this block aligned with the external base image and replace the whole -# block with `FROM AS base` once that image exists. +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +# TODO delete once the test is successful ARG BUILD_PLATFORM=linux/amd64 ARG PYTHON_VERSION=3.14 ARG OS_VERSION=trixie @@ -25,7 +26,8 @@ LABEL org.opencontainers.image.source=https://github.com/inveniosoftware/docker- ENV \ WORKING_DIR=/opt/invenio \ INVENIO_INSTANCE_PATH=/opt/invenio/var/instance \ - INVENIO_USER_ID=1000 + INVENIO_USER_ID=1000 \ + PATH=/opt/invenio/src/.venv/bin:${PATH} # Create relevant application directories RUN mkdir -p \ @@ -74,166 +76,50 @@ ENV \ LANGUAGE=en_US:en \ LC_ALL=en_US.UTF-8 +# Delete the above code and replace it with: +# FROM AS base # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# Once tests is successful, replace the above block with: -# FROM AS base -# Cookiecutter application layers start here. +FROM base AS build-base -FROM base AS app-base +FROM build-base AS builder -COPY site ./site COPY README.md pyproject.toml uv.lock ./ -RUN uv sync --frozen --no-dev +COPY site ./site +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev --no-editable COPY ./docker/uwsgi/ ${INVENIO_INSTANCE_PATH} COPY ./invenio.cfg ${INVENIO_INSTANCE_PATH} COPY ./templates/ ${INVENIO_INSTANCE_PATH}/templates/ COPY ./app_data/ ${INVENIO_INSTANCE_PATH}/app_data/ COPY ./translations/ ${INVENIO_INSTANCE_PATH}/translations/ -COPY ./ . +COPY ./assets/ ./assets/ +COPY ./static/ ./static/ RUN cp -r ./static/. ${INVENIO_INSTANCE_PATH}/static/ && \ cp -r ./assets/. ${INVENIO_INSTANCE_PATH}/assets/ && \ - invenio collect --verbose && \ - invenio webpack buildall - -ENTRYPOINT [ "bash", "-c"] - -# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -# Alpine image -{%- else %} -# syntax=docker/dockerfile:1 - -# -# Copyright (C) {{ cookiecutter.year }} CERN. -# Copyright (C) {{ cookiecutter.year }} KTH Royal Institute of Technology. -# -# Invenio is free software; you can redistribute it and/or modify it -# under the terms of the MIT License; see LICENSE file for more details. -# - -# image size is around 1.16 GB. - -ARG ALPINE_VERSION=3.23.3 -ARG PYTHON_SERIES=3.12 -ARG JS_PACKAGE_MANAGER=pnpm@10.29.2 -ARG WORKING_DIR=/opt/invenio -ARG VIRTUAL_ENV=/opt/env -ARG INVENIO_INSTANCE_PATH=${WORKING_DIR}/var/instance + invenio collect --verbose && \ + invenio webpack buildall && \ + rm -rf ${INVENIO_INSTANCE_PATH}/assets && \ + rm -rf \ + /root/.cache \ + /tmp/* \ + ${INVENIO_INSTANCE_PATH}/assets/node_modules \ + ${INVENIO_INSTANCE_PATH}/assets/.cache -FROM ghcr.io/astral-sh/uv:alpine AS builder -ARG JS_PACKAGE_MANAGER -ARG PYTHON_SERIES -ARG WORKING_DIR -ARG VIRTUAL_ENV -ARG INVENIO_INSTANCE_PATH - -ENV LANG=en_US.UTF-8 \ - LANGUAGE=en_US:en \ - LC_ALL=en_US.UTF-8 \ - PYTHONUNBUFFERED=1 \ - PYTHONDONTWRITEBYTECODE=1 \ - UV_NO_MANAGED_PYTHON=1 \ - UV_PYTHON_DOWNLOADS=never \ - UV_SYSTEM_PYTHON=1 \ - UV_PROJECT_ENVIRONMENT=${VIRTUAL_ENV} \ - UV_CACHE_DIR=/opt/.cache/uv \ - WORKING_DIR=${WORKING_DIR} \ - VIRTUAL_ENV=${VIRTUAL_ENV} \ - INVENIO_INSTANCE_PATH=${INVENIO_INSTANCE_PATH} \ - PATH=${VIRTUAL_ENV}/bin:${PATH} - -WORKDIR ${WORKING_DIR} - -RUN apk add --no-cache \ - "python3~=${PYTHON_SERIES}" \ - "python3-dev~=${PYTHON_SERIES}" \ - autoconf automake bash binutils build-base cairo file gcc ghostscript git \ - libtool libxml2-dev libxslt-dev linux-headers musl-dev nodejs npm openssl xmlsec xmlsec-dev - -RUN npm install -g "${JS_PACKAGE_MANAGER}" - -RUN set -eux; \ - uv venv "${VIRTUAL_ENV}"; \ - "${VIRTUAL_ENV}/bin/python" -m ensurepip --upgrade; \ - "${VIRTUAL_ENV}/bin/python" -m pip install --no-cache-dir --upgrade pip; \ - mkdir -p "${INVENIO_INSTANCE_PATH}" /opt/.cache/uv +FROM base AS app-base COPY . . -COPY ./invenio.cfg ${INVENIO_INSTANCE_PATH}/ -COPY ./docker/uwsgi/ ${INVENIO_INSTANCE_PATH}/ -COPY ./app_data/ ${INVENIO_INSTANCE_PATH}/app_data/ +COPY --from=builder ${WORKING_DIR}/src/.venv ./.venv +COPY --from=builder ${INVENIO_INSTANCE_PATH}/static/ ${INVENIO_INSTANCE_PATH}/static/ +COPY ./docker/uwsgi/ ${INVENIO_INSTANCE_PATH} +COPY ./invenio.cfg ${INVENIO_INSTANCE_PATH} COPY ./templates/ ${INVENIO_INSTANCE_PATH}/templates/ +COPY ./app_data/ ${INVENIO_INSTANCE_PATH}/app_data/ COPY ./translations/ ${INVENIO_INSTANCE_PATH}/translations/ -RUN --mount=type=cache,target=/opt/.cache/uv uv sync --frozen --no-dev - -ENV INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage \ - PNPM_HOME=/root/.local/share/pnpm - -RUN --mount=type=cache,target=/root/.local/share/pnpm/store \ - set -eux; \ - export PATH="${PNPM_HOME}:${PATH}"; \ - mkdir -p "${INVENIO_INSTANCE_PATH}/static" "${INVENIO_INSTANCE_PATH}/assets"; \ - cp -r ./static/. "${INVENIO_INSTANCE_PATH}/static/"; \ - cp -r ./assets/. "${INVENIO_INSTANCE_PATH}/assets/"; \ - uv run invenio collect --verbose; \ - uv run invenio webpack buildall; \ - uv cache clean; \ - rm -rf "${INVENIO_INSTANCE_PATH}/assets/node_modules" \ - "${INVENIO_INSTANCE_PATH}/assets/.pnpm-store" \ - "${INVENIO_INSTANCE_PATH}/assets/.npm" - -FROM alpine:${ALPINE_VERSION} AS runtime -ARG PYTHON_SERIES -ARG WORKING_DIR -ARG VIRTUAL_ENV -ARG INVENIO_INSTANCE_PATH -ARG IMAGE_BUILD_TIMESTAMP -ARG SENTRY_RELEASE - -ENV LANG=en_US.UTF-8 \ - LANGUAGE=en_US:en \ - LC_ALL=en_US.UTF-8 \ - PYTHONUNBUFFERED=1 \ - PYTHONDONTWRITEBYTECODE=1 \ - UV_NO_MANAGED_PYTHON=1 \ - UV_PYTHON_DOWNLOADS=never \ - UV_SYSTEM_PYTHON=1 \ - UV_PROJECT_ENVIRONMENT=${VIRTUAL_ENV} \ - UV_NO_CACHE=1 \ - HOME=/opt/invenio \ - WORKING_DIR=${WORKING_DIR} \ - VIRTUAL_ENV=${VIRTUAL_ENV} \ - INVENIO_INSTANCE_PATH=${INVENIO_INSTANCE_PATH} \ - INVENIO_IMAGE_BUILD_TIMESTAMP="${IMAGE_BUILD_TIMESTAMP}" \ - SENTRY_RELEASE="${SENTRY_RELEASE}" \ - PATH=${VIRTUAL_ENV}/bin:${PATH} - -WORKDIR ${WORKING_DIR} - -RUN apk add --no-cache \ - "python3~=${PYTHON_SERIES}" \ - bash cairo fontconfig ghostscript libxslt ttf-dejavu uwsgi-python3 xmlsec - -COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV} -COPY --from=builder ${WORKING_DIR} ${WORKING_DIR} - -# we can remove these 2 lines if we do not want to include uv and uvx in the runtime image -COPY --from=builder /usr/local/bin/uv /usr/local/bin/uv -COPY --from=builder /usr/local/bin/uvx /usr/local/bin/uvx - -RUN set -eux; \ - mkdir -p ${INVENIO_INSTANCE_PATH}/data ${INVENIO_INSTANCE_PATH}/archive; \ - chown -R 100:101 ${INVENIO_INSTANCE_PATH}; \ - chmod -R u+w ${INVENIO_INSTANCE_PATH} - -USER 100:101 ENTRYPOINT ["bash", "-c"] {%- endif %} From 9b2c4c3cffcaeb0557420e2dad005c24302f8a8a Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Thu, 2 Apr 2026 04:35:54 +0200 Subject: [PATCH 07/18] feat: include wipe_recreate script * this is needed to run in worker pod when using: docker compose -f docker-compose.full.yml up -d --build --- .../wipe_recreate.sh | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100755 {{cookiecutter.project_shortname}}/wipe_recreate.sh diff --git a/{{cookiecutter.project_shortname}}/wipe_recreate.sh b/{{cookiecutter.project_shortname}}/wipe_recreate.sh new file mode 100755 index 00000000..b5bc5fa6 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/wipe_recreate.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env sh +# -*- coding: utf-8 -*- +# +# Copyright (C) 2020-2026 CERN. +# +# Demo-InvenioRDM is free software; you can redistribute it and/or modify it +# under the terms of the MIT License; see LICENSE file for more details. + +# Quit on errors +set -o errexit + +# Quit on unbound symbols +set -o nounset + +# Prompt to confirm action +printf "Are you sure you want to wipe everything and create a new empty instance? [y/N] " +read -r response +case "$response" in + [yY]|[yY][eE][sS]) + ;; + *) + exit 0 + ;; +esac + +# Wipe +# ---- +invenio shell --no-term-title -c "import redis; redis.StrictRedis.from_url(app.config['CACHE_REDIS_URL']).flushall(); print('Cache cleared')" +# NOTE: db destroy is not needed since DB keeps being created +# Just need to drop all tables from it. +invenio db drop --yes-i-know +invenio index destroy --force --yes-i-know +invenio index queue init purge + +# Recreate +# -------- +# NOTE: db init is not needed since DB keeps being created +# Just need to create all tables from it. +invenio db create +invenio files location create --default 'default-location' $(invenio shell --no-term-title -c "print(app.instance_path)")'/data' +# +# Create roles +# +# Superuser role +invenio roles create admin +invenio access allow superuser-access role admin +# Administration access role +invenio roles create administration +invenio access allow administration-access role administration +# Administration moderation role +invenio roles create administration-moderation +invenio access allow administration-moderation role administration-moderation + +invenio index init --force +invenio rdm-records custom-fields init +invenio communities custom-fields init + +# Add demo and fixtures data +# ------------- +invenio rdm-records fixtures +invenio rdm-records demo + +# Enable admin user +invenio users activate admin@inveniosoftware.org From 832f5664d6ca2fae18e517609fc3e44fd103a6fb Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Tue, 2 Jun 2026 16:28:40 +0200 Subject: [PATCH 08/18] feat: update project dependencies and scripts * modify run-tests.sh to use updated uv command * change bootstrap script to install project with test extras * update pyproject.toml for app version and dependencies * adjust site pyproject.toml for versioning and requirements --- run-tests.sh | 2 +- scripts/bootstrap | 4 +-- .../pyproject.toml | 31 +++++++------------ .../site/pyproject.toml | 4 +-- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/run-tests.sh b/run-tests.sh index 0ea7502b..3a1d0f98 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -46,7 +46,7 @@ git init git add -A # Update MANIFEST.in -uv run check-manifest -u || true +uv run --with check-manifest check-manifest -u || true # Build application docker images ${TESTDIR}/scripts/build-images.sh diff --git a/scripts/bootstrap b/scripts/bootstrap index 40bc4d18..4dd77710 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -14,8 +14,8 @@ if [ ! -f $uv_lock_path ]; then uv lock fi -# Install all dependency groups so check-manifest is available -uv sync --all-groups +# Install the project with its test extra declared in pyproject.toml. +uv sync --extra tests export INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage # Build assets INVENIO_LOCAL_INSTANCE_PATH=$(uv run invenio shell --no-term-title -c "print(app.instance_path)") diff --git a/{{cookiecutter.project_shortname}}/pyproject.toml b/{{cookiecutter.project_shortname}}/pyproject.toml index aec7b24c..4743faf1 100644 --- a/{{cookiecutter.project_shortname}}/pyproject.toml +++ b/{{cookiecutter.project_shortname}}/pyproject.toml @@ -1,5 +1,5 @@ {%- set project_shortname = cookiecutter.project_shortname -%} -{%- set app_rdm_version = "~=14.0.0b9.dev0" -%} +{%- set app_rdm_version = "~=14.0.0b11.dev1" -%} {%- set app_rdm_base = "invenio-app-rdm" ~ app_rdm_version -%} {%- set app_rdm_extras = ['opensearch2'] -%} {%- if cookiecutter.file_storage == 'S3' -%} @@ -13,19 +13,16 @@ name = "{{ cookiecutter.project_name | slugify() }}" description = "Add your description here" readme = "README.md" -version = "14.0.0" -requires-python = ">= 3.12" +version = "{{ app_rdm_version | replace('~=', '') }}" +requires-python = ">= 3.14" dependencies = [ "invenio-app-rdm{% if app_rdm_extras %}[{{ app_rdm_extras | join(',') }}]{% endif %}{{ app_rdm_version }}", - "python-dotenv>=0.21.0", "uwsgi>=2.0", "uwsgitop>=0.11", "uwsgi-tools>=1.1.1", "lxml-html-clean>=0.1.1", - {% if cookiecutter.development_tools == 'yes' -%} - "invenio-cli>=1.0.0", - {% endif -%} + "python-dotenv>=0.21.0", {% if cookiecutter.site_code == 'yes' -%} "{{ site_dist_name }}", {%- endif %} @@ -34,23 +31,19 @@ dependencies = [ {% if cookiecutter.site_code == 'yes' -%} [tool.uv.sources] "{{ site_dist_name }}" = { workspace = true } + [tool.uv.workspace] members = ["site"] - -[tool.hatch.build.targets.wheel] -packages = ["site/{{ cookiecutter.package_name }}"] {%- endif %} [project.optional-dependencies] -sentry = [ - "sentry-sdk>=1.45,<2.0.0", -] tests = [ - "pytest-invenio>=3.0.0,<4.0.0", + "pytest-invenio>=4.0.0,<5.0.0", ] -[dependency-groups] -dev = [ - "flask-debugtoolbar>=0.16.0", - "pytest-invenio>=3.0.0,<4.0.0", -] +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +bypass-selection = true diff --git a/{{cookiecutter.project_shortname}}/site/pyproject.toml b/{{cookiecutter.project_shortname}}/site/pyproject.toml index 1fabaa89..38b05992 100644 --- a/{{cookiecutter.project_shortname}}/site/pyproject.toml +++ b/{{cookiecutter.project_shortname}}/site/pyproject.toml @@ -1,9 +1,9 @@ {%- set package_name = cookiecutter.package_name + '_package' -%} [project] name = "{{package_name}}" -version = "1.0.0" +version = "0.1.0" description = "Add your description here" -requires-python = ">= 3.12" +requires-python = ">= 3.14" [project.entry-points."invenio_base.blueprints"] {{cookiecutter.package_name}}_views = "{{cookiecutter.package_name}}.views:create_blueprint" From 2500a3cb3b2d2814cf6f9bdb1f900e6be3526dbe Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 3 Jun 2026 11:20:28 +0200 Subject: [PATCH 09/18] feat: update project name handling in templates * Refactor project name variables for consistency * Use project_shortname for instance and distribution names --- {{cookiecutter.project_shortname}}/pyproject.toml | 6 +++--- {{cookiecutter.project_shortname}}/site/pyproject.toml | 4 ++-- {{cookiecutter.project_shortname}}/site/setup.cfg | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/{{cookiecutter.project_shortname}}/pyproject.toml b/{{cookiecutter.project_shortname}}/pyproject.toml index 4743faf1..ad5eb20d 100644 --- a/{{cookiecutter.project_shortname}}/pyproject.toml +++ b/{{cookiecutter.project_shortname}}/pyproject.toml @@ -5,12 +5,13 @@ {%- if cookiecutter.file_storage == 'S3' -%} {%- set app_rdm_extras = app_rdm_extras + ['s3'] -%} {%- endif -%} +{%- set instance_dist_name = (cookiecutter.project_name | slugify()) + '-instance' -%} {%- if cookiecutter.site_code == 'yes' -%} - {%- set site_dist_name = cookiecutter.package_name + '_package' -%} + {%- set site_dist_name = cookiecutter.project_shortname -%} {%- endif -%} [project] -name = "{{ cookiecutter.project_name | slugify() }}" +name = "{{ instance_dist_name }}" description = "Add your description here" readme = "README.md" version = "{{ app_rdm_version | replace('~=', '') }}" @@ -22,7 +23,6 @@ dependencies = [ "uwsgitop>=0.11", "uwsgi-tools>=1.1.1", "lxml-html-clean>=0.1.1", - "python-dotenv>=0.21.0", {% if cookiecutter.site_code == 'yes' -%} "{{ site_dist_name }}", {%- endif %} diff --git a/{{cookiecutter.project_shortname}}/site/pyproject.toml b/{{cookiecutter.project_shortname}}/site/pyproject.toml index 38b05992..e9192b27 100644 --- a/{{cookiecutter.project_shortname}}/site/pyproject.toml +++ b/{{cookiecutter.project_shortname}}/site/pyproject.toml @@ -1,6 +1,6 @@ -{%- set package_name = cookiecutter.package_name + '_package' -%} +{%- set dist_name = cookiecutter.project_shortname -%} [project] -name = "{{package_name}}" +name = "{{dist_name}}" version = "0.1.0" description = "Add your description here" requires-python = ">= 3.14" diff --git a/{{cookiecutter.project_shortname}}/site/setup.cfg b/{{cookiecutter.project_shortname}}/site/setup.cfg index db863e81..d298fcbe 100644 --- a/{{cookiecutter.project_shortname}}/site/setup.cfg +++ b/{{cookiecutter.project_shortname}}/site/setup.cfg @@ -1,6 +1,6 @@ -{%- set package_name = cookiecutter.package_name + '_package' %} +{%- set dist_name = cookiecutter.project_shortname %} [metadata] -name = {{package_name}} +name = {{dist_name}} [options.extras_require] tests = From 8a3997b1aa62b4b89d89ec78f31e5ee1cac6d5af Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 3 Jun 2026 13:37:15 +0200 Subject: [PATCH 10/18] feat: add volume configurations for database and message queue * Updated docker-compose and docker-services files to include volumes for db_data, mq_data, and search_data. * Adjusted S3 storage handling in the configuration. --- .../docker-compose.full.yml | 8 +++++++- .../docker-compose.yml | 7 ++++++- .../docker-services.yml | 16 +++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/{{cookiecutter.project_shortname}}/docker-compose.full.yml b/{{cookiecutter.project_shortname}}/docker-compose.full.yml index d4423476..83c46ad9 100644 --- a/{{cookiecutter.project_shortname}}/docker-compose.full.yml +++ b/{{cookiecutter.project_shortname}}/docker-compose.full.yml @@ -138,10 +138,16 @@ services: {%- if cookiecutter.file_storage == 'S3'%} volumes: + db_data: + mq_data: + search_data: static_data: - data: + s3_data: {%- else %} volumes: + db_data: + mq_data: + search_data: static_data: uploaded_data: archived_data: diff --git a/{{cookiecutter.project_shortname}}/docker-compose.yml b/{{cookiecutter.project_shortname}}/docker-compose.yml index f8e17e29..895eae3b 100644 --- a/{{cookiecutter.project_shortname}}/docker-compose.yml +++ b/{{cookiecutter.project_shortname}}/docker-compose.yml @@ -53,6 +53,11 @@ services: extends: file: docker-services.yml service: s3 +{%- endif %} volumes: - data: + db_data: + mq_data: + search_data: +{%- if cookiecutter.file_storage == 'S3'%} + s3_data: {%- endif %} diff --git a/{{cookiecutter.project_shortname}}/docker-services.yml b/{{cookiecutter.project_shortname}}/docker-services.yml index edf6873b..5b187f87 100644 --- a/{{cookiecutter.project_shortname}}/docker-services.yml +++ b/{{cookiecutter.project_shortname}}/docker-services.yml @@ -41,6 +41,8 @@ services: - "POSTGRES_DB={{cookiecutter.project_shortname}}" ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5432:5432" + volumes: + - db_data:/var/lib/postgresql/data pgadmin: image: dpage/pgadmin4:6 restart: "unless-stopped" @@ -65,6 +67,8 @@ services: ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:15672:15672" - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5672:5672" + volumes: + - mq_data:/var/lib/rabbitmq flower: image: mher/flower:2.0.1 restart: "unless-stopped" @@ -98,6 +102,8 @@ services: ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:9200:9200" - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:9600:9600" + volumes: + - search_data:/usr/share/opensearch/data opensearch-dashboards: image: opensearchproject/opensearch-dashboards:2.19.4 ports: @@ -119,7 +125,7 @@ services: MINIO_ROOT_USER: CHANGE_ME MINIO_ROOT_PASSWORD: CHANGE_ME volumes: - - ./data:/data + - s3_data:/data command: server /data --console-address :9001 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] @@ -127,3 +133,11 @@ services: timeout: 20s retries: 3 {%- endif %} + +volumes: + db_data: + mq_data: + search_data: +{%- if cookiecutter.file_storage == 'S3'%} + s3_data: +{%- endif %} From 742f78a8cbda25d694e66993a60db744dd6dc352 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 3 Jun 2026 14:33:39 +0200 Subject: [PATCH 11/18] feat: add npm package class for webpack extension * Introduced INVENIO_WEBPACKEXT_NPM_PKG_CLS * to support pynpm:PNPMPackage in Dockerfile --- {{cookiecutter.project_shortname}}/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/{{cookiecutter.project_shortname}}/Dockerfile b/{{cookiecutter.project_shortname}}/Dockerfile index 5af6d4b2..891be968 100644 --- a/{{cookiecutter.project_shortname}}/Dockerfile +++ b/{{cookiecutter.project_shortname}}/Dockerfile @@ -18,6 +18,7 @@ ARG BUILD_PLATFORM=linux/amd64 ARG PYTHON_VERSION=3.14 ARG OS_VERSION=trixie ARG NODE_VERSION=24 +ARG PNPM_VERSION=latest-10 FROM --platform=${BUILD_PLATFORM} python:${PYTHON_VERSION}-${OS_VERSION} AS base @@ -26,6 +27,7 @@ LABEL org.opencontainers.image.source=https://github.com/inveniosoftware/docker- ENV \ WORKING_DIR=/opt/invenio \ INVENIO_INSTANCE_PATH=/opt/invenio/var/instance \ + INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage \ INVENIO_USER_ID=1000 \ PATH=/opt/invenio/src/.venv/bin:${PATH} @@ -62,7 +64,7 @@ RUN \ npm \ && \ # Install pnpm - npm install -g pnpm@latest-10 + npm install -g pnpm@${PNPM_VERSION} # Setup uv # From https://docs.astral.sh/uv/guides/integration/docker/ From cd1ae07c0c36a4cd09f78a6978864b17813a1813 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Thu, 4 Jun 2026 13:25:19 +0200 Subject: [PATCH 12/18] fix: adress comments * Adjust uv.lock path handling in bootstrap script * Update Dockerfile to include new ARG for npm package class * Modify docker-services.yml to use updated postgres image * Add optional dependencies for testing in pyproject.toml * Remove deprecated setup.cfg and setup.py files * Clean up __init__.py by removing version definition --- scripts/bootstrap | 6 +++--- {{cookiecutter.project_shortname}}/Dockerfile | 9 +++++---- {{cookiecutter.project_shortname}}/README.md | 5 ----- .../docker-services.yml | 3 ++- .../site/pyproject.toml | 4 ++++ {{cookiecutter.project_shortname}}/site/setup.cfg | 13 ------------- {{cookiecutter.project_shortname}}/site/setup.py | 3 --- .../site/{{cookiecutter.package_name}}/__init__.py | 1 - 8 files changed, 14 insertions(+), 30 deletions(-) delete mode 100644 {{cookiecutter.project_shortname}}/site/setup.cfg delete mode 100644 {{cookiecutter.project_shortname}}/site/setup.py diff --git a/scripts/bootstrap b/scripts/bootstrap index 4dd77710..f0f448e3 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -9,7 +9,7 @@ set -e cd ${PROJECTDIR} uv_lock_path="./uv.lock" -if [ ! -f $uv_lock_path ]; then +if [ ! -f "${uv_lock_path}" ]; then echo "'uv.lock' not found. Generating via 'uv lock'..." uv lock fi @@ -19,8 +19,8 @@ uv sync --extra tests export INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage # Build assets INVENIO_LOCAL_INSTANCE_PATH=$(uv run invenio shell --no-term-title -c "print(app.instance_path)") -cp -r ./static/. ${INVENIO_LOCAL_INSTANCE_PATH}/static/ -cp -r ./assets/. ${INVENIO_LOCAL_INSTANCE_PATH}/assets/ +cp -r ./static/. "${INVENIO_LOCAL_INSTANCE_PATH}/static/" +cp -r ./assets/. "${INVENIO_LOCAL_INSTANCE_PATH}/assets/" uv run invenio collect -v uv run invenio webpack buildall diff --git a/{{cookiecutter.project_shortname}}/Dockerfile b/{{cookiecutter.project_shortname}}/Dockerfile index 891be968..d26477fa 100644 --- a/{{cookiecutter.project_shortname}}/Dockerfile +++ b/{{cookiecutter.project_shortname}}/Dockerfile @@ -19,15 +19,18 @@ ARG PYTHON_VERSION=3.14 ARG OS_VERSION=trixie ARG NODE_VERSION=24 ARG PNPM_VERSION=latest-10 +ARG INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage FROM --platform=${BUILD_PLATFORM} python:${PYTHON_VERSION}-${OS_VERSION} AS base LABEL org.opencontainers.image.source=https://github.com/inveniosoftware/docker-invenio +ARG PNPM_VERSION +ARG INVENIO_WEBPACKEXT_NPM_PKG_CLS + ENV \ WORKING_DIR=/opt/invenio \ INVENIO_INSTANCE_PATH=/opt/invenio/var/instance \ - INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage \ INVENIO_USER_ID=1000 \ PATH=/opt/invenio/src/.venv/bin:${PATH} @@ -84,9 +87,7 @@ ENV \ # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -FROM base AS build-base - -FROM build-base AS builder +FROM base AS builder COPY README.md pyproject.toml uv.lock ./ COPY site ./site diff --git a/{{cookiecutter.project_shortname}}/README.md b/{{cookiecutter.project_shortname}}/README.md index c46610d4..b346ae02 100644 --- a/{{cookiecutter.project_shortname}}/README.md +++ b/{{cookiecutter.project_shortname}}/README.md @@ -47,8 +47,3 @@ Following is an overview of the generated files and folders: To learn how to configure, customize, deploy and much more, visit the [InvenioRDM Documentation](https://inveniordm.docs.cern.ch/). - -## Development - -install local instance with: -`invenio-cli init RDM --template ~/src/cookiecutter-invenio-rdm` diff --git a/{{cookiecutter.project_shortname}}/docker-services.yml b/{{cookiecutter.project_shortname}}/docker-services.yml index 5b187f87..885349fc 100644 --- a/{{cookiecutter.project_shortname}}/docker-services.yml +++ b/{{cookiecutter.project_shortname}}/docker-services.yml @@ -4,6 +4,7 @@ services: context: ./ args: - ENVIRONMENT=DEV + - WEBPACKEXT_NPM_PKG_CLS=${WEBPACKEXT_NPM_PKG_CLS:-pynpm:PNPMPackage} image: {{cookiecutter.project_shortname}} restart: "unless-stopped" environment: @@ -33,7 +34,7 @@ services: ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:6379:6379" db: - image: postgres:16.11 + image: postgres:17.10 restart: "unless-stopped" environment: - "POSTGRES_USER={{cookiecutter.project_shortname}}" diff --git a/{{cookiecutter.project_shortname}}/site/pyproject.toml b/{{cookiecutter.project_shortname}}/site/pyproject.toml index e9192b27..cfec20af 100644 --- a/{{cookiecutter.project_shortname}}/site/pyproject.toml +++ b/{{cookiecutter.project_shortname}}/site/pyproject.toml @@ -11,6 +11,10 @@ requires-python = ">= 3.14" [project.entry-points."invenio_assets.webpack"] {{cookiecutter.package_name}}_theme = "{{cookiecutter.package_name}}.webpack:theme" +[project.optional-dependencies] +tests = [ + "pytest-invenio>=2.1.0,<3.0.0", +] [build-system] requires = ["hatchling"] diff --git a/{{cookiecutter.project_shortname}}/site/setup.cfg b/{{cookiecutter.project_shortname}}/site/setup.cfg deleted file mode 100644 index d298fcbe..00000000 --- a/{{cookiecutter.project_shortname}}/site/setup.cfg +++ /dev/null @@ -1,13 +0,0 @@ -{%- set dist_name = cookiecutter.project_shortname %} -[metadata] -name = {{dist_name}} - -[options.extras_require] -tests = - pytest-invenio>=2.1.0,<3.0.0 - -[options.entry_points] -invenio_base.blueprints = - {{cookiecutter.package_name}}_views = {{cookiecutter.package_name}}.views:create_blueprint -invenio_assets.webpack = - {{cookiecutter.package_name}}_theme = {{cookiecutter.package_name}}.webpack:theme diff --git a/{{cookiecutter.project_shortname}}/site/setup.py b/{{cookiecutter.project_shortname}}/site/setup.py deleted file mode 100644 index 60684932..00000000 --- a/{{cookiecutter.project_shortname}}/site/setup.py +++ /dev/null @@ -1,3 +0,0 @@ -from setuptools import setup - -setup() diff --git a/{{cookiecutter.project_shortname}}/site/{{cookiecutter.package_name}}/__init__.py b/{{cookiecutter.project_shortname}}/site/{{cookiecutter.package_name}}/__init__.py index 3dc1f76b..e69de29b 100644 --- a/{{cookiecutter.project_shortname}}/site/{{cookiecutter.package_name}}/__init__.py +++ b/{{cookiecutter.project_shortname}}/site/{{cookiecutter.package_name}}/__init__.py @@ -1 +0,0 @@ -__version__ = "0.1.0" From fa60c80d90bfdf7ecfe2eadbe9c50df8adccff2a Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Thu, 4 Jun 2026 13:34:08 +0200 Subject: [PATCH 13/18] fix: use quotes for variable expansions in scripts * assert proper handling of variables in bash scripts. --- hooks/post_gen_project.sh | 2 +- run-tests.sh | 29 +++++++++---------- scripts/bootstrap | 4 +-- scripts/build-images.sh | 6 ++-- scripts/server | 10 +++---- scripts/setup | 6 ++-- scripts/wait-for-services.sh | 18 ++++-------- .../wipe_recreate.sh | 4 +-- 8 files changed, 36 insertions(+), 43 deletions(-) diff --git a/hooks/post_gen_project.sh b/hooks/post_gen_project.sh index aaa2d43a..0ce3dae0 100644 --- a/hooks/post_gen_project.sh +++ b/hooks/post_gen_project.sh @@ -19,7 +19,7 @@ touch data/default/.gitkeep {%- if cookiecutter.site_code == 'no'%} DIR="site" -if [ -d "$DIR" ]; then +if [ -d "${DIR}" ]; then rm -r site fi {% endif %} diff --git a/run-tests.sh b/run-tests.sh index 3a1d0f98..93965a2f 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -10,12 +10,12 @@ set -o errexit # quit on unbound symbols: set -o nounset -WORKDIR=$(mktemp -d) -TESTDIR=$(pwd) +WORKDIR="$(mktemp -d)" +TESTDIR="$(pwd)" finish() { echo "Cleaning up." - docker compose -f ${PROJECTDIR}/docker-compose.full.yml down --volumes --remove-orphans & + docker compose -f "${PROJECTDIR}/docker-compose.full.yml" down --volumes --remove-orphans & rm -rf "${PROJECTDIR}/.venv" || true rm -rf "${WORKDIR}" echo "Test setup cleaned." @@ -26,20 +26,19 @@ trap finish EXIT PROJECT_NAME="my-site" export PROJECT_NAME -cookiecutter --no-input -o "$WORKDIR" . \ - project_name=${PROJECT_NAME} \ - base_image=${COOKIECUTTER_BASE_IMAGE:-debian} \ - database=${COOKIECUTTER_DATABASE:-postgresql} \ - opensearch=${COOKIECUTTER_OPENSEARCH:-opensearch2} \ - file_storage=${COOKIECUTTER_FILE_STORAGE:-local} +cookiecutter --no-input -o "${WORKDIR}" . \ + project_name="${PROJECT_NAME}" \ + base_image="${COOKIECUTTER_BASE_IMAGE:-debian}" \ + opensearch="${COOKIECUTTER_OPENSEARCH:-opensearch2}" \ + file_storage="${COOKIECUTTER_FILE_STORAGE:-local}" -PROJECTDIR=${WORKDIR}/${PROJECT_NAME} +PROJECTDIR="${WORKDIR}/${PROJECT_NAME}" export PROJECTDIR # Check local installation (this also generates the uv.lock) -${TESTDIR}/scripts/bootstrap +"${TESTDIR}/scripts/bootstrap" -cd ${PROJECTDIR} +cd "${PROJECTDIR}" # Initialize git in the repository for 'check-manifest' to work git init @@ -49,11 +48,11 @@ git add -A uv run --with check-manifest check-manifest -u || true # Build application docker images -${TESTDIR}/scripts/build-images.sh +"${TESTDIR}/scripts/build-images.sh" # Fire up a full instance via docker-compose.full.yml # We will use the services (DB, OS/ES, etc) for running the tests locally docker compose -f docker-compose.full.yml up -d -${TESTDIR}/scripts/wait-for-services.sh --full +"${TESTDIR}/scripts/wait-for-services.sh" --full echo "All services are up." docker compose -f docker-compose.full.yml down @@ -61,6 +60,6 @@ echo "Services successfully stopped" # Fire up the services we need for testing docker compose up -d -${TESTDIR}/scripts/wait-for-services.sh +"${TESTDIR}/scripts/wait-for-services.sh" # Run the instance tests # REQUIREMENTS=prod ./run-tests.sh diff --git a/scripts/bootstrap b/scripts/bootstrap index f0f448e3..417645ad 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -6,7 +6,7 @@ set -e -cd ${PROJECTDIR} +cd "${PROJECTDIR}" uv_lock_path="./uv.lock" if [ ! -f "${uv_lock_path}" ]; then @@ -18,7 +18,7 @@ fi uv sync --extra tests export INVENIO_WEBPACKEXT_NPM_PKG_CLS=pynpm:PNPMPackage # Build assets -INVENIO_LOCAL_INSTANCE_PATH=$(uv run invenio shell --no-term-title -c "print(app.instance_path)") +INVENIO_LOCAL_INSTANCE_PATH="$(uv run invenio shell --no-term-title -c "print(app.instance_path)")" cp -r ./static/. "${INVENIO_LOCAL_INSTANCE_PATH}/static/" cp -r ./assets/. "${INVENIO_LOCAL_INSTANCE_PATH}/assets/" diff --git a/scripts/build-images.sh b/scripts/build-images.sh index 45b929b3..a31067a5 100755 --- a/scripts/build-images.sh +++ b/scripts/build-images.sh @@ -3,10 +3,10 @@ # SPDX-FileCopyrightText: 2019-2020 Northwestern University. # SPDX-License-Identifier: MIT -cd ${PROJECTDIR} +cd "${PROJECTDIR}" # Build application image -docker build . --tag ${PROJECT_NAME} --build-arg include_assets=true +docker build . --tag "${PROJECT_NAME}" --build-arg include_assets=true # Build frontend image used by docker-compose.full.yml -docker build ./docker/nginx --tag ${PROJECT_NAME}-frontend +docker build ./docker/nginx --tag "${PROJECT_NAME}-frontend" diff --git a/scripts/server b/scripts/server index f54459cc..d5457345 100755 --- a/scripts/server +++ b/scripts/server @@ -6,7 +6,7 @@ set -e -cd ${PROJECTDIR} +cd "${PROJECTDIR}" export FLASK_ENV=development export INVENIO_SQLALCHEMY_DATABASE_URI="postgresql+psycopg2://{{cookiecutter.project_shortname}}:{{cookiecutter.project_shortname}}@localhost/{{cookiecutter.project_shortname}}" @@ -15,9 +15,9 @@ export INVENIO_SQLALCHEMY_DATABASE_URI="postgresql+psycopg2://{{cookiecutter.pro uv run celery -A invenio_app.celery worker -l INFO & pid_celery=$! uv run invenio run \ - --cert "$script_path"/../docker/nginx/test.crt \ - --key "$script_path"/../docker/nginx/test.key & pid_server=$! + --cert "${script_path}/../docker/nginx/test.crt" \ + --key "${script_path}/../docker/nginx/test.key" & pid_server=$! -trap 'kill $pid_celery $pid_server &>/dev/null' EXIT +trap 'kill "${pid_celery}" "${pid_server}" &>/dev/null' EXIT -wait $pid_celery $pid_server +wait "${pid_celery}" "${pid_server}" diff --git a/scripts/setup b/scripts/setup index 3dc2fb55..e6de47bb 100755 --- a/scripts/setup +++ b/scripts/setup @@ -7,7 +7,7 @@ set -e -cd ${PROJECTDIR} +cd "${PROJECTDIR}" # Clean redis uv run invenio shell --no-term-title -c "import redis; redis.StrictRedis.from_url(app.config['CACHE_REDIS_URL']).flushall(); print('Cache cleared')" @@ -16,11 +16,11 @@ uv run invenio db init create uv run invenio index destroy --force --yes-i-know uv run invenio index init --force uv run invenio index queue init purge -if [ $COOKIECUTTER_FILE_STORAGE -eq "S3" ] +if [ "${COOKIECUTTER_FILE_STORAGE}" = "S3" ] then uv run invenio files location s3-default s3://default --default else -uv run invenio files location --default 'default-location' $(uv run invenio shell --no-term-title -c "print(app.instance_path)") +uv run invenio files location --default 'default-location' "$(uv run invenio shell --no-term-title -c "print(app.instance_path)")" fi # Create admin role to rectict access diff --git a/scripts/wait-for-services.sh b/scripts/wait-for-services.sh index 31acf0e0..a1c3be05 100755 --- a/scripts/wait-for-services.sh +++ b/scripts/wait-for-services.sh @@ -7,13 +7,13 @@ # Verify that all services are running before continuing check_ready() { RETRIES=20 - while ! $2 + while ! "${2}" do - echo "Waiting for $1, $((RETRIES--)) remaining attempts..." + echo "Waiting for ${1}, $((RETRIES--)) remaining attempts..." sleep 15 - if [ $RETRIES -eq 0 ] + if [ "${RETRIES}" -eq 0 ] then - echo "Couldn't reach $1" + echo "Couldn't reach ${1}" exit 1 fi done @@ -25,14 +25,8 @@ then check_ready "S3" _s3_check fi -if [[ ${COOKIECUTTER_DATABASE} == "mysql" ]] -then - _db_check(){ docker compose exec db bash -c "mysql -p${PROJECT_NAME} -e \"select Version();\"" &>/dev/null; } - check_ready "MySQL" _db_check -else - _db_check(){ docker compose exec --user postgres db bash -c "pg_isready" &>/dev/null; } - check_ready "Postgres" _db_check -fi +_db_check(){ docker compose exec --user postgres db bash -c "pg_isready" &>/dev/null; } +check_ready "Postgres" _db_check _search_check(){ curl --output /dev/null --silent --head --fail http://localhost:9200 &>/dev/null; } check_ready "OpenSearch" _search_check diff --git a/{{cookiecutter.project_shortname}}/wipe_recreate.sh b/{{cookiecutter.project_shortname}}/wipe_recreate.sh index b5bc5fa6..25e481b4 100755 --- a/{{cookiecutter.project_shortname}}/wipe_recreate.sh +++ b/{{cookiecutter.project_shortname}}/wipe_recreate.sh @@ -15,7 +15,7 @@ set -o nounset # Prompt to confirm action printf "Are you sure you want to wipe everything and create a new empty instance? [y/N] " read -r response -case "$response" in +case "${response}" in [yY]|[yY][eE][sS]) ;; *) @@ -37,7 +37,7 @@ invenio index queue init purge # NOTE: db init is not needed since DB keeps being created # Just need to create all tables from it. invenio db create -invenio files location create --default 'default-location' $(invenio shell --no-term-title -c "print(app.instance_path)")'/data' +invenio files location create --default 'default-location' "$(invenio shell --no-term-title -c "print(app.instance_path)")/data" # # Create roles # From f97203a6dc1f93e0fc20bec644d9d053aea65a70 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Thu, 4 Jun 2026 13:56:01 +0200 Subject: [PATCH 14/18] fix: remove optional dependencies section from pyproject.toml * Cleaned up the pyproject.toml by removing the optional dependencies section for tests. * test installed with uv sync --extra tests --- {{cookiecutter.project_shortname}}/site/pyproject.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/{{cookiecutter.project_shortname}}/site/pyproject.toml b/{{cookiecutter.project_shortname}}/site/pyproject.toml index cfec20af..d57124a4 100644 --- a/{{cookiecutter.project_shortname}}/site/pyproject.toml +++ b/{{cookiecutter.project_shortname}}/site/pyproject.toml @@ -11,11 +11,6 @@ requires-python = ">= 3.14" [project.entry-points."invenio_assets.webpack"] {{cookiecutter.package_name}}_theme = "{{cookiecutter.package_name}}.webpack:theme" -[project.optional-dependencies] -tests = [ - "pytest-invenio>=2.1.0,<3.0.0", -] - [build-system] requires = ["hatchling"] build-backend = "hatchling.build" From b0f7c0d53c25b32f9e5aebf93f6e5a6cf383f9fa Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Thu, 4 Jun 2026 15:40:42 +0200 Subject: [PATCH 15/18] fix: update Dockerfile to improve ARG scoping * Adjusted the FROM instruction to remove the alias for base image. * Added comments to clarify ARG scoping in Dockerfile. --- {{cookiecutter.project_shortname}}/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/{{cookiecutter.project_shortname}}/Dockerfile b/{{cookiecutter.project_shortname}}/Dockerfile index d26477fa..27a01a38 100644 --- a/{{cookiecutter.project_shortname}}/Dockerfile +++ b/{{cookiecutter.project_shortname}}/Dockerfile @@ -25,6 +25,10 @@ FROM --platform=${BUILD_PLATFORM} python:${PYTHON_VERSION}-${OS_VERSION} AS base LABEL org.opencontainers.image.source=https://github.com/inveniosoftware/docker-invenio +# ARGs declared before FROM are only global defaults. Redeclare build args in +# the stage where they are used so passed values are in scope: +# https://docs.docker.com/build/building/variables/#scoping +ARG NODE_VERSION ARG PNPM_VERSION ARG INVENIO_WEBPACKEXT_NPM_PKG_CLS From 860252f4f44840910513b63b60463e8e4403a6d6 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Thu, 4 Jun 2026 17:18:14 +0200 Subject: [PATCH 16/18] fix: remove npm installation from Dockerfile * fix Conflicts: npm after moving the arg NODE_VERSION inder from --- {{cookiecutter.project_shortname}}/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/{{cookiecutter.project_shortname}}/Dockerfile b/{{cookiecutter.project_shortname}}/Dockerfile index 27a01a38..8a2f2afd 100644 --- a/{{cookiecutter.project_shortname}}/Dockerfile +++ b/{{cookiecutter.project_shortname}}/Dockerfile @@ -68,7 +68,6 @@ RUN \ fonts-dejavu \ locales \ nodejs \ - npm \ && \ # Install pnpm npm install -g pnpm@${PNPM_VERSION} From 55ab7435af8fced8c108f7979cec27e8b8e71ea4 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 17 Jun 2026 15:51:30 +0200 Subject: [PATCH 17/18] feat: enhance Docker configuration for service health checks * Add health checks for services in docker-compose * Update Dockerfile copyright format * Set environment variables for UI and API URLs --- {{cookiecutter.project_shortname}}/Dockerfile | 12 ++++-------- .../docker-compose.full.yml | 8 +++++++- .../docker-services.yml | 8 ++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/{{cookiecutter.project_shortname}}/Dockerfile b/{{cookiecutter.project_shortname}}/Dockerfile index 8a2f2afd..7a5978b8 100644 --- a/{{cookiecutter.project_shortname}}/Dockerfile +++ b/{{cookiecutter.project_shortname}}/Dockerfile @@ -1,14 +1,10 @@ {%- if cookiecutter.base_image == "debian" %} # syntax=docker/dockerfile:1 -# -# Copyright (C) {{ cookiecutter.year }} Northwestern University. -# Copyright (C) {{ cookiecutter.year }} Frontmatter. -# Copyright (C) {{ cookiecutter.year }} KTH Royal Institute of Technology. -# -# Invenio is free software; you can redistribute it and/or modify it -# under the terms of the MIT License; see LICENSE file for more details. -# +# SPDX-FileCopyrightText: {{ cookiecutter.year }} Northwestern University. +# SPDX-FileCopyrightText: {{ cookiecutter.year }} Frontmatter. +# SPDX-FileCopyrightText: {{ cookiecutter.year }} KTH Royal Institute of Technology. + # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/{{cookiecutter.project_shortname}}/docker-compose.full.yml b/{{cookiecutter.project_shortname}}/docker-compose.full.yml index 83c46ad9..8616ae2f 100644 --- a/{{cookiecutter.project_shortname}}/docker-compose.full.yml +++ b/{{cookiecutter.project_shortname}}/docker-compose.full.yml @@ -77,6 +77,9 @@ services: file: docker-services.yml service: app command: ["uwsgi /opt/invenio/var/instance/uwsgi_ui.ini"] + depends_on: + search: + condition: service_healthy image: {{cookiecutter.project_shortname}}:latest ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5000:5000" @@ -93,6 +96,9 @@ services: file: docker-services.yml service: app command: ["uwsgi /opt/invenio/var/instance/uwsgi_rest.ini"] + depends_on: + search: + condition: service_healthy image: {{cookiecutter.project_shortname}}:latest ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5001:5000" @@ -115,7 +121,7 @@ services: {%- endif %} depends_on: search: - condition: service_started + condition: service_healthy cache: condition: service_started db: diff --git a/{{cookiecutter.project_shortname}}/docker-services.yml b/{{cookiecutter.project_shortname}}/docker-services.yml index 885349fc..076fdac3 100644 --- a/{{cookiecutter.project_shortname}}/docker-services.yml +++ b/{{cookiecutter.project_shortname}}/docker-services.yml @@ -8,6 +8,8 @@ services: image: {{cookiecutter.project_shortname}} restart: "unless-stopped" environment: + - "INVENIO_SITE_UI_URL=https://127.0.0.1" + - "INVENIO_SITE_API_URL=https://127.0.0.1/api" - "INVENIO_ACCOUNTS_SESSION_REDIS_URL=redis://cache:6379/1" - "INVENIO_BROKER_URL=amqp://guest:guest@mq:5672/" - "INVENIO_CACHE_REDIS_URL=redis://cache:6379/0" @@ -103,6 +105,12 @@ services: ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:9200:9200" - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:9600:9600" + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:9200 >/dev/null || exit 1"] + interval: 5s + timeout: 5s + retries: 30 + start_period: 30s volumes: - search_data:/usr/share/opensearch/data opensearch-dashboards: From a3bf0647c928f0bad8bd0f814d41de4964a3b243 Mon Sep 17 00:00:00 2001 From: Sam Arbid Date: Wed, 17 Jun 2026 15:53:04 +0200 Subject: [PATCH 18/18] fix: update opensearch and opensearch-dashboards images * Bump opensearch to version 2.19.5 * Bump opensearch-dashboards to version 2.19.5 --- {{cookiecutter.project_shortname}}/docker-services.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.project_shortname}}/docker-services.yml b/{{cookiecutter.project_shortname}}/docker-services.yml index 076fdac3..0558dc82 100644 --- a/{{cookiecutter.project_shortname}}/docker-services.yml +++ b/{{cookiecutter.project_shortname}}/docker-services.yml @@ -82,7 +82,7 @@ services: mq: condition: service_started search: - image: opensearchproject/opensearch:2.19.4 + image: opensearchproject/opensearch:2.19.5 restart: "unless-stopped" environment: # Settings only for development. DO NOT use in production! @@ -114,7 +114,7 @@ services: volumes: - search_data:/usr/share/opensearch/data opensearch-dashboards: - image: opensearchproject/opensearch-dashboards:2.19.4 + image: opensearchproject/opensearch-dashboards:2.19.5 ports: - "${DOCKER_SERVICES_IP_BIND:-127.0.0.1}:5601:5601" expose: