From 6e332fa68858e185efe36344688d8ef371c6c712 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 17 Dec 2025 14:34:25 +0100 Subject: [PATCH 01/19] update URL_STANDALONE for b3.23-pre5 branch --- install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.py b/install.py index 4e2ae1ef12c1..3fe93d651c8b 100644 --- a/install.py +++ b/install.py @@ -210,7 +210,7 @@ class devel(user): } DEMOS = "https://artifacts.openquake.org/travis/demos-master.zip" GITBRANCH = "https://github.com/gem/oq-engine/archive/%s.zip" -URL_STANDALONE = "https://wheelhouse.openquake.org/py/standalone/latest/" +URL_STANDALONE = "https://wheelhouse.openquake.org/py/standalone/b3.23-pre5/" def ensure(pip=None, pyvenv=None): From f97eeca41650326d89ecc2f55a2c35980ae2f465 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 24 Dec 2025 11:15:53 +0100 Subject: [PATCH 02/19] set STANDALONE_APP_NAME_MAP in settings.py directly --- openquake/server/settings.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openquake/server/settings.py b/openquake/server/settings.py index 0b98204df711..3f1ed63a022c 100644 --- a/openquake/server/settings.py +++ b/openquake/server/settings.py @@ -26,8 +26,6 @@ from openquake.baselib import config from openquake.commonlib import datastore -# optionally overrided in local_settings.py -STANDALONE_APP_NAME_MAP = {} try: from openquakeplatform.settings import STANDALONE, STANDALONE_APPS except ImportError: @@ -213,6 +211,14 @@ APPLICATION_MODE = 'PUBLIC' +# Definition of Django applications +STANDALONE_APP_NAME_MAP = { + 'openquakeplatform_ipt': 'ipt', + 'django_gem_taxonomy': 'taxonomy', + } +if APPLICATION_MODE != 'TOOLS_ONLY': + STANDALONE_APP_NAME_MAP['openquakeplatform_taxonomy'] = 'glossary' + ARISTOTLE_DEFAULT_USGS_ID = 'us7000n7n8' # loadable and convertible rupture # ARISTOTLE_DEFAULT_USGS_ID = 'us6000jllz' # loadable but with conversion err From 6dc82aa36cd7769747840eb9dc5eedc2b2aa56f5 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 24 Dec 2025 11:33:12 +0100 Subject: [PATCH 03/19] add 'django-gem-taxonomy' as new standalone app --- install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/install.py b/install.py index 3fe93d651c8b..82bd86acce05 100644 --- a/install.py +++ b/install.py @@ -280,6 +280,7 @@ def install_standalone(venv): "oq-platform-standalone", "oq-platform-ipt", "oq-platform-taxonomy", + "django-gem-taxonomy", ]: try: print("Applications " + app + " are not installed yet \n") From 764d14a6039271b3634725adc75546edfe096205 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Mon, 19 Jan 2026 17:51:54 +0100 Subject: [PATCH 04/19] manage properly 'openquake' namespace: remove 'openquake/__init__.py', fix 'setup.py' and 'pytest.ini' files --- openquake/__init__.py | 19 ------------------- pytest.ini | 1 + setup.py | 11 ++++++++--- 3 files changed, 9 insertions(+), 22 deletions(-) delete mode 100644 openquake/__init__.py diff --git a/openquake/__init__.py b/openquake/__init__.py deleted file mode 100644 index 9600bd23ce85..000000000000 --- a/openquake/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2010-2025 GEM Foundation -# -# OpenQuake is free software: you can redistribute it and/or modify it -# under the terms of the GNU Affero General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# OpenQuake is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with OpenQuake. If not, see . - -__import__('pkg_resources').declare_namespace(__name__) diff --git a/pytest.ini b/pytest.ini index 11455d4fd9b5..dfdd5512968a 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,4 +2,5 @@ markers = slow: a slow test addopts = --tb short +consider_namespace_packages = true DJANGO_SETTINGS_MODULE = openquake.server.settings diff --git a/setup.py b/setup.py index b22ca2061019..1233ff0b3fb8 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,8 @@ import os import re import sys -from setuptools import setup, find_packages +from setuptools import setup, find_namespace_packages + if sys.version_info < (3, 9): sys.exit('Sorry, Python < 3.9 is not supported') @@ -112,11 +113,15 @@ def get_readme(): 'Environment :: Console', 'Environment :: Web Environment', ], - packages=find_packages(exclude=["qa_tests", "qa_tests.*", + packages=find_namespace_packages(include=[ + "openquake.*"], + exclude=[ + "qa_tests", "qa_tests.*", "tools", "*.*.tests", "*.*.tests.*", "openquake.engine.bin", - "openquake.engine.bin.*"]), + "openquake.engine.bin.*"] + ), py_modules=PY_MODULES, include_package_data=True, package_data={"openquake.engine": [ From 98df5f57b43ced10d4582d6e7924d24b108931bd Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Mon, 19 Jan 2026 18:26:57 +0100 Subject: [PATCH 05/19] introduce post installation command management for standalone applications --- install.py | 67 ++++++++++++++----- .../commands/openquake_engine_postinstall.py | 59 ++++++++++++++++ 2 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 openquake/server/management/commands/openquake_engine_postinstall.py diff --git a/install.py b/install.py index 82bd86acce05..b93b795d8d70 100644 --- a/install.py +++ b/install.py @@ -263,12 +263,17 @@ def get_requirements_branch(version, inst, from_fork): return version -def install_standalone(venv): +def install_or_postinstall_standalone(venv, is_install=True): """ - Install the standalone Django applications if possible + Install the standalone Django applications if possible or + run '_postinstall' command if it exists """ errors = [] - print("The standalone applications are not installed yet") + if is_install: + print("The standalone applications are not installed yet") + else: + print("Run '_postinstall' command for each standalone\n" + " Django applications, if it exists") if sys.platform == "win32": if os.path.exists("python\\python._pth.old"): pycmd = inst.VENV + "\\python.exe" @@ -276,25 +281,49 @@ def install_standalone(venv): pycmd = inst.VENV + "\\Scripts\\python.exe" else: pycmd = inst.VENV + "/bin/python3" - for app in [ - "oq-platform-standalone", - "oq-platform-ipt", - "oq-platform-taxonomy", - "django-gem-taxonomy", - ]: - try: - print("Applications " + app + " are not installed yet \n") - subprocess.check_call( - [pycmd, "-m", "pip", "install", "--find-links", URL_STANDALONE, - app] - ) - except Exception as exc: - # for instance is somebody removed a wheel from the wheelhouse - errors.append("%s: could not install %s" % (exc, app)) + STANDALONE_APP_INFO = [ + {"pkg": "oq-platform-standalone", "name": None}, + {"pkg": "oq-platform-ipt", "name": "openquakeplatform_ipt"}, + {"pkg": "oq-platform-taxonomy", "name": "openquakeplatform_taxonomy"}, + {"pkg": "django-gem-taxonomy", "name": "django_gem_taxonomy"}, + ] + + if is_install: + for app in STANDALONE_APP_INFO: + try: + print("Applications " + app['pkg'] + " are not installed yet \n") + + subprocess.check_call( + [pycmd, "-m", "pip", "install", "--find-links", URL_STANDALONE, + app['pkg']] + ) + except Exception as exc: + # for instance is somebody removed a wheel from the wheelhouse + errors.append("%s: could not install %s" % (exc, app['pkg'])) + else: + for app in STANDALONE_APP_INFO: + if not app['name']: + continue + + try: + subprocess.check_call( + [os.path.join(inst.VENV, "bin", "django-admin"), + "openquake_engine_postinstall", app['name']] + ) + except Exception as exc: + # for instance is somebody removed a wheel from the wheelhouse + errors.append("%s: error during %s postinstall command execution" % (exc, app['name'])) + return errors +def install_standalone(venv): + return install_or_postinstall_standalone(venv, is_install=True) + +def postinstall_standalone(venv): + return install_or_postinstall_standalone(venv, is_install=False) + def before_checks(inst, venv, port, remove, usage): """ Checks to perform before the installation @@ -507,6 +536,8 @@ def install(inst, version, from_fork): if inst in (user, devel): # create/upgrade the db in the default location subprocess.run([oqreal, "engine", "--upgrade-db"]) + errors += postinstall_standalone(inst.VENV) + if ( inst is server and not os.path.exists(inst.OQ) diff --git a/openquake/server/management/commands/openquake_engine_postinstall.py b/openquake/server/management/commands/openquake_engine_postinstall.py new file mode 100644 index 000000000000..4908f982d5d8 --- /dev/null +++ b/openquake/server/management/commands/openquake_engine_postinstall.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# oq-geoviewer +# Copyright (C) 2018-2019 GEM Foundation +# +# oq-geoviewer is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# oq-geoviewer is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +# import subprocess +from django.apps import apps as django_apps +from django.core.management import call_command, get_commands +import sys +from django.core.management.base import BaseCommand + +class Command(BaseCommand): + help = ("Command that run a '_postinstall' command if it exists") + + def add_arguments(self, parser): + parser.add_argument('django_app', + help='django application name') + + def handle(self, *args, **options): + found = False + for app in django_apps.get_app_configs(): + label = app.label + if options['django_app'] == label: + found = True + break + + + if not found: + self.stdout.write( + self.style.ERROR( + "No django app '%s' found." % (options['django_app'],)) + ) + sys.exit(1) + + + postinstall_cmd = options['django_app'] + '_postinstall' + django_cmds = get_commands() + if not (postinstall_cmd in django_cmds): + self.stdout.write( + self.style.SUCCESS( + "No 'postinst' action needed for app %s, skipped." % (options['django_app'],)) + ) + sys.exit(0) + + call_command(postinstall_cmd) From 7d6f24a694e47fb18527b1fb9f9c16002917b95d Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Tue, 20 Jan 2026 10:03:44 +0100 Subject: [PATCH 06/19] some cleanup --- .../commands/openquake_engine_postinstall.py | 8 +++----- setup.py | 17 +++++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/openquake/server/management/commands/openquake_engine_postinstall.py b/openquake/server/management/commands/openquake_engine_postinstall.py index 4908f982d5d8..4789a999f873 100644 --- a/openquake/server/management/commands/openquake_engine_postinstall.py +++ b/openquake/server/management/commands/openquake_engine_postinstall.py @@ -29,7 +29,7 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument('django_app', help='django application name') - + def handle(self, *args, **options): found = False for app in django_apps.get_app_configs(): @@ -38,20 +38,18 @@ def handle(self, *args, **options): found = True break - if not found: self.stdout.write( self.style.ERROR( "No django app '%s' found." % (options['django_app'],)) ) sys.exit(1) - - + postinstall_cmd = options['django_app'] + '_postinstall' django_cmds = get_commands() if not (postinstall_cmd in django_cmds): self.stdout.write( - self.style.SUCCESS( + self.style.WARNING( "No 'postinst' action needed for app %s, skipped." % (options['django_app'],)) ) sys.exit(0) diff --git a/setup.py b/setup.py index 1233ff0b3fb8..13d3d23a6185 100644 --- a/setup.py +++ b/setup.py @@ -114,14 +114,15 @@ def get_readme(): 'Environment :: Web Environment', ], packages=find_namespace_packages(include=[ - "openquake.*"], - exclude=[ - "qa_tests", "qa_tests.*", - "tools", - "*.*.tests", "*.*.tests.*", - "openquake.engine.bin", - "openquake.engine.bin.*"] - ), + "openquake.*", + ], + exclude=[ + "qa_tests", "qa_tests.*", + "tools", + "*.*.tests", "*.*.tests.*", + "openquake.engine.bin", + "openquake.engine.bin.*", + ]), py_modules=PY_MODULES, include_package_data=True, package_data={"openquake.engine": [ From 5c2615909f3100da22525a3cc721ce8955795e23 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Tue, 20 Jan 2026 15:03:03 +0100 Subject: [PATCH 07/19] differentiate 'django-admin' command for different OS --- install.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/install.py b/install.py index b93b795d8d70..885a6dc3b167 100644 --- a/install.py +++ b/install.py @@ -307,8 +307,12 @@ def install_or_postinstall_standalone(venv, is_install=True): continue try: + if sys.platform == "win32": + django_admin = ['Scripts', 'django-admin.exe'] + else: + django_admin = ["bin", "django-admin"] subprocess.check_call( - [os.path.join(inst.VENV, "bin", "django-admin"), + [os.path.join(inst.VENV, *django_admin), "openquake_engine_postinstall", app['name']] ) except Exception as exc: From 1740d83a7b8ceb3f89e22d1584904fb29e818c98 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Tue, 20 Jan 2026 16:53:21 +0100 Subject: [PATCH 08/19] modify environment to be able to run django-admin with the proper settings --- install.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/install.py b/install.py index 885a6dc3b167..b2254f75baf5 100644 --- a/install.py +++ b/install.py @@ -311,10 +311,14 @@ def install_or_postinstall_standalone(venv, is_install=True): django_admin = ['Scripts', 'django-admin.exe'] else: django_admin = ["bin", "django-admin"] + + django_env = os.environ.copy() + django_env["DJANGO_SETTINGS_MODULE"] = "openquake.server.settings" + subprocess.check_call( [os.path.join(inst.VENV, *django_admin), - "openquake_engine_postinstall", app['name']] - ) + "openquake_engine_postinstall", app['name']], + env=django_env) except Exception as exc: # for instance is somebody removed a wheel from the wheelhouse errors.append("%s: error during %s postinstall command execution" % (exc, app['name'])) From 93a2a0f1c2c599e4ae014fca3164b214676d4de7 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 21 Jan 2026 10:49:14 +0100 Subject: [PATCH 09/19] wheelhouse link to engine-3.23 folder --- install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.py b/install.py index b2254f75baf5..5d4b27715a3d 100644 --- a/install.py +++ b/install.py @@ -210,7 +210,7 @@ class devel(user): } DEMOS = "https://artifacts.openquake.org/travis/demos-master.zip" GITBRANCH = "https://github.com/gem/oq-engine/archive/%s.zip" -URL_STANDALONE = "https://wheelhouse.openquake.org/py/standalone/b3.23-pre5/" +URL_STANDALONE = "https://wheelhouse.openquake.org/py/standalone/engine-3.23/" def ensure(pip=None, pyvenv=None): From 0c9140b370547489ca52e7a60637e66ccf048d1e Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 21 Jan 2026 11:29:57 +0100 Subject: [PATCH 10/19] more clear if condition --- .../server/management/commands/openquake_engine_postinstall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openquake/server/management/commands/openquake_engine_postinstall.py b/openquake/server/management/commands/openquake_engine_postinstall.py index 4789a999f873..f09f57c7c4da 100644 --- a/openquake/server/management/commands/openquake_engine_postinstall.py +++ b/openquake/server/management/commands/openquake_engine_postinstall.py @@ -47,7 +47,7 @@ def handle(self, *args, **options): postinstall_cmd = options['django_app'] + '_postinstall' django_cmds = get_commands() - if not (postinstall_cmd in django_cmds): + if postinstall_cmd not in django_cmds: self.stdout.write( self.style.WARNING( "No 'postinst' action needed for app %s, skipped." % (options['django_app'],)) From e681d0b81d50d5172a7d71d5bdb819d3acc18e6a Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 21 Jan 2026 15:53:41 +0100 Subject: [PATCH 11/19] actualize template before run oq-engine install script to avoid errors during engine installation (workflow) --- .github/workflows/engine_pr_test.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/engine_pr_test.yml b/.github/workflows/engine_pr_test.yml index 9a3c7bb59b38..cf0c7e5201ab 100644 --- a/.github/workflows/engine_pr_test.yml +++ b/.github/workflows/engine_pr_test.yml @@ -160,6 +160,11 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Actualize 'aelo' templates for email notifications + run: | + for file in openquake/server/templates/registration/*.aelo.tmpl; do + cp -- "$file" "${file%.aelo.tmpl}" + done - name: Install dependencies run: | if [[ $FROM_FORK == "true" ]]; then @@ -167,11 +172,6 @@ jobs: else python install.py devel --version=$GITHUB_HEAD_REF fi - - name: Actualize 'aelo' templates for email notifications - run: | - for file in openquake/server/templates/registration/*.aelo.tmpl; do - cp -- "$file" "${file%.aelo.tmpl}" - done - name: Server 'AELO' mode tests run: | source ~/openquake/bin/activate @@ -199,6 +199,11 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Actualize 'impact' templates for email notifications + run: | + for file in openquake/server/templates/registration/*.impact.tmpl; do + cp -- "$file" "${file%.impact.tmpl}" + done - name: Install dependencies run: | if [[ $FROM_FORK == "true" ]]; then @@ -206,11 +211,6 @@ jobs: else python install.py devel --version=$GITHUB_HEAD_REF fi - - name: Actualize 'impact' templates for email notifications - run: | - for file in openquake/server/templates/registration/*.impact.tmpl; do - cp -- "$file" "${file%.impact.tmpl}" - done - name: Server 'ARISTOTLE' mode tests run: | set -x From d94714d39f54a45e7b5612b337bd3eba9e444d13 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Thu, 22 Jan 2026 10:08:21 +0100 Subject: [PATCH 12/19] add '--keepvenv' argument to 'install.py' to preserve previous virtual environment --- install.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/install.py b/install.py index 5d4b27715a3d..5212691b562f 100644 --- a/install.py +++ b/install.py @@ -424,7 +424,7 @@ def fix_version(commit, venv): f.write("".join(lines)) -def install(inst, version, from_fork): +def install(inst, version, from_fork, keepvenv): """ Install the engine in one of the three possible modes """ @@ -444,9 +444,10 @@ def install(inst, version, from_fork): if inst is server or inst is devel_server: subprocess.check_call(["chown", "openquake", inst.OQDATA]) - # recreate the openquake venv - ensure(pyvenv=inst.VENV) - print("Created %s" % inst.VENV) + if not keepvenv: + # recreate the openquake venv + ensure(pyvenv=inst.VENV) + print("Created %s" % inst.VENV) if sys.platform == "win32": if os.path.exists("python\\python._pth.old"): @@ -650,6 +651,8 @@ def remove(inst): help="the kind of installation you want", ) parser.add_argument("--venv", help="venv directory") + parser.add_argument("--keepvenv", action="store_false", + help="keep the current virtual environment") parser.add_argument("--remove", action="store_true", help="disinstall the engine") parser.add_argument("--version", help="version to install (default stable)") @@ -668,7 +671,7 @@ def remove(inst): if args.remove: remove(inst) else: - errors = install(inst, args.version, args.from_fork) + errors = install(inst, args.version, args.from_fork, args.keepvenv) if errors: # NB: even if one of the tools is missing, the engine will work sys.exit('\n'.join(errors)) From a0795daa8e0117d42c6efdc6004a2b262959f9c6 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Thu, 22 Jan 2026 16:27:25 +0100 Subject: [PATCH 13/19] from keepvenv to no_env argument --- install.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/install.py b/install.py index 5212691b562f..ae1f37e7cb8e 100644 --- a/install.py +++ b/install.py @@ -424,7 +424,7 @@ def fix_version(commit, venv): f.write("".join(lines)) -def install(inst, version, from_fork, keepvenv): +def install(inst, version, from_fork, no_venv): """ Install the engine in one of the three possible modes """ @@ -444,19 +444,21 @@ def install(inst, version, from_fork, keepvenv): if inst is server or inst is devel_server: subprocess.check_call(["chown", "openquake", inst.OQDATA]) - if not keepvenv: + if not no_venv: # recreate the openquake venv ensure(pyvenv=inst.VENV) print("Created %s" % inst.VENV) - if sys.platform == "win32": - if os.path.exists("python\\python._pth.old"): - pycmd = inst.VENV + "\\python.exe" + if sys.platform == "win32": + if os.path.exists("python\\python._pth.old"): + pycmd = inst.VENV + "\\python.exe" + else: + pycmd = inst.VENV + "\\Scripts\\python.exe" else: - pycmd = inst.VENV + "\\Scripts\\python.exe" + pycmd = inst.VENV + "/bin/python3" else: - pycmd = inst.VENV + "/bin/python3" - + pycmd = os.path.join(os.getenv('LocalAppData'), 'Programs', + 'OpenQuake Engine', 'python3', 'python.exe') # upgrade pip and before check that it is installed in venv if sys.platform != "win32": ensure(pip=pycmd) @@ -651,7 +653,7 @@ def remove(inst): help="the kind of installation you want", ) parser.add_argument("--venv", help="venv directory") - parser.add_argument("--keepvenv", action="store_false", + parser.add_argument("--no-venv", action="store_false", help="keep the current virtual environment") parser.add_argument("--remove", action="store_true", help="disinstall the engine") @@ -671,7 +673,7 @@ def remove(inst): if args.remove: remove(inst) else: - errors = install(inst, args.version, args.from_fork, args.keepvenv) + errors = install(inst, args.version, args.from_fork, args.no_venv) if errors: # NB: even if one of the tools is missing, the engine will work sys.exit('\n'.join(errors)) From 925a0e8116b7b4c5ee5ac054de6afdf55196242f Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Thu, 22 Jan 2026 17:09:10 +0100 Subject: [PATCH 14/19] manage VENV path for no_venv case --- install.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/install.py b/install.py index ae1f37e7cb8e..b982c27b4773 100644 --- a/install.py +++ b/install.py @@ -332,7 +332,7 @@ def install_standalone(venv): def postinstall_standalone(venv): return install_or_postinstall_standalone(venv, is_install=False) -def before_checks(inst, venv, port, remove, usage): +def before_checks(inst, venv, port, remove, no_venv, usage): """ Checks to perform before the installation """ @@ -341,6 +341,10 @@ def before_checks(inst, venv, port, remove, usage): if port: inst.DBPORT = int(port) + if no_venv: + inst.VENV = os.path.join(os.getenv('LocalAppData'), 'Programs', + 'OpenQuake Engine', 'python3') + # check platform if (inst is server and sys.platform != "linux") or ( inst is devel_server and sys.platform != "linux" @@ -457,8 +461,7 @@ def install(inst, version, from_fork, no_venv): else: pycmd = inst.VENV + "/bin/python3" else: - pycmd = os.path.join(os.getenv('LocalAppData'), 'Programs', - 'OpenQuake Engine', 'python3', 'python.exe') + pycmd = os.path.join(inst.VENV, 'python.exe') # upgrade pip and before check that it is installed in venv if sys.platform != "win32": ensure(pip=pycmd) @@ -668,7 +671,7 @@ def remove(inst): args = parser.parse_args() if args.inst: inst = globals()[args.inst] - before_checks(inst, args.venv, args.dbport, args.remove, + before_checks(inst, args.venv, args.dbport, args.remove, args.no_venv, parser.format_usage()) if args.remove: remove(inst) From 313847f1c69629583e69264e7a9607559b64c832 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Thu, 22 Jan 2026 17:46:27 +0100 Subject: [PATCH 15/19] rename install argument --- install.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/install.py b/install.py index b982c27b4773..94e3987f2603 100644 --- a/install.py +++ b/install.py @@ -332,7 +332,7 @@ def install_standalone(venv): def postinstall_standalone(venv): return install_or_postinstall_standalone(venv, is_install=False) -def before_checks(inst, venv, port, remove, no_venv, usage): +def before_checks(inst, venv, port, remove, novenv, usage): """ Checks to perform before the installation """ @@ -341,7 +341,7 @@ def before_checks(inst, venv, port, remove, no_venv, usage): if port: inst.DBPORT = int(port) - if no_venv: + if novenv: inst.VENV = os.path.join(os.getenv('LocalAppData'), 'Programs', 'OpenQuake Engine', 'python3') @@ -428,7 +428,7 @@ def fix_version(commit, venv): f.write("".join(lines)) -def install(inst, version, from_fork, no_venv): +def install(inst, version, from_fork, novenv): """ Install the engine in one of the three possible modes """ @@ -448,7 +448,7 @@ def install(inst, version, from_fork, no_venv): if inst is server or inst is devel_server: subprocess.check_call(["chown", "openquake", inst.OQDATA]) - if not no_venv: + if not novenv: # recreate the openquake venv ensure(pyvenv=inst.VENV) print("Created %s" % inst.VENV) @@ -656,7 +656,7 @@ def remove(inst): help="the kind of installation you want", ) parser.add_argument("--venv", help="venv directory") - parser.add_argument("--no-venv", action="store_false", + parser.add_argument("--novenv", action="store_false", help="keep the current virtual environment") parser.add_argument("--remove", action="store_true", help="disinstall the engine") @@ -671,12 +671,12 @@ def remove(inst): args = parser.parse_args() if args.inst: inst = globals()[args.inst] - before_checks(inst, args.venv, args.dbport, args.remove, args.no_venv, + before_checks(inst, args.venv, args.dbport, args.remove, args.novenv, parser.format_usage()) if args.remove: remove(inst) else: - errors = install(inst, args.version, args.from_fork, args.no_venv) + errors = install(inst, args.version, args.from_fork, args.novenv) if errors: # NB: even if one of the tools is missing, the engine will work sys.exit('\n'.join(errors)) From 153e6e1b2c148561bfb5c14c8039b753a5f5536d Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Fri, 23 Jan 2026 11:49:49 +0100 Subject: [PATCH 16/19] add '--noupgrade' argument to install.py command --- install.py | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/install.py b/install.py index 94e3987f2603..a77f0d086db3 100644 --- a/install.py +++ b/install.py @@ -428,7 +428,7 @@ def fix_version(commit, venv): f.write("".join(lines)) -def install(inst, version, from_fork, novenv): +def install(inst, version, from_fork, novenv, noupgrade): """ Install the engine in one of the three possible modes """ @@ -465,19 +465,21 @@ def install(inst, version, from_fork, novenv): # upgrade pip and before check that it is installed in venv if sys.platform != "win32": ensure(pip=pycmd) - subprocess.check_call( - [pycmd, "-m", "pip", "install", "--upgrade", "pip", "wheel"] - ) + subprocess.check_call([pycmd, "-m", "pip", "install"] + [ + ] if noupgrade else ["--upgrade"] + [ + "pip", "wheel"]) else: if os.path.exists("python\\python._pth.old"): - subprocess.check_call( - [pycmd, "-m", "pip", "install", "--upgrade", "pip", "wheel", - "urllib3"]) + subprocess.check_call([pycmd, "-m", "pip", "install"] + [ + ] if noupgrade else ["--upgrade"] + [ + "pip", "wheel", "urllib3"]) else: - subprocess.check_call([pycmd, "-m", "ensurepip", "--upgrade"]) - subprocess.check_call( - [pycmd, "-m", "pip", "install", "--upgrade", "pip", "wheel", - "urllib3"]) + subprocess.check_call([pycmd, "-m", "ensurepip"] + [ + ] if noupgrade else ["--upgrade"]) + + subprocess.check_call([pycmd, "-m", "pip", "install"] + [ + ] if noupgrade else ["--upgrade"] + [ + "pip", "wheel", "urllib3"]) # install the requirements branch = get_requirements_branch(version, inst, from_fork) @@ -509,18 +511,20 @@ def install(inst, version, from_fork, novenv): subprocess.check_call([pycmd, "-m", "pip", "install", "-e", CDIR]) elif version is None: # install the stable version subprocess.check_call( - [pycmd, "-m", "pip", "install", "--upgrade", "openquake.engine"] + [pycmd, "-m", "pip", "install"] + [ + ] if noupgrade else ["--upgrade"] + ["openquake.engine"] ) elif re.match(r"\d+(\.\d+)+", version): # install an official version subprocess.check_call( - [pycmd, "-m", "pip", "install", "--upgrade", - "openquake.engine==" + version] + [pycmd, "-m", "pip", "install"] + [ + ] if noupgrade else ["--upgrade"] + ["openquake.engine==" + version] ) else: # install a branch from github (only for user or server) commit = latest_commit(version) print("Installing commit", commit) subprocess.check_call( - [pycmd, "-m", "pip", "install", "--upgrade", GITBRANCH % commit] + [pycmd, "-m", "pip", "install"] + [ + ] if noupgrade else ["--upgrade"] + [GITBRANCH % commit] ) fix_version(commit, inst.VENV) @@ -657,7 +661,9 @@ def remove(inst): ) parser.add_argument("--venv", help="venv directory") parser.add_argument("--novenv", action="store_false", - help="keep the current virtual environment") + help="keep the current python environment") + parser.add_argument("--noupgrade", action="store_false", + help="not use '--upgrade' in pip install calls") parser.add_argument("--remove", action="store_true", help="disinstall the engine") parser.add_argument("--version", help="version to install (default stable)") @@ -676,7 +682,7 @@ def remove(inst): if args.remove: remove(inst) else: - errors = install(inst, args.version, args.from_fork, args.novenv) + errors = install(inst, args.version, args.from_fork, args.novenv, args.noupgrade) if errors: # NB: even if one of the tools is missing, the engine will work sys.exit('\n'.join(errors)) From d7808fd7353e9d4745e3ed2beacedf7b60c52264 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Fri, 23 Jan 2026 15:23:27 +0100 Subject: [PATCH 17/19] wrong default argparse default values fixed --- install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.py b/install.py index a77f0d086db3..b83985e17f9b 100644 --- a/install.py +++ b/install.py @@ -660,9 +660,9 @@ def remove(inst): help="the kind of installation you want", ) parser.add_argument("--venv", help="venv directory") - parser.add_argument("--novenv", action="store_false", + parser.add_argument("--novenv", action="store_true", help="keep the current python environment") - parser.add_argument("--noupgrade", action="store_false", + parser.add_argument("--noupgrade", action="store_true", help="not use '--upgrade' in pip install calls") parser.add_argument("--remove", action="store_true", help="disinstall the engine") From 947e2d4bb7c6eba9dd3617050d8eb26db1a61596 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Fri, 23 Jan 2026 16:25:00 +0100 Subject: [PATCH 18/19] fix operators precedence order --- install.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/install.py b/install.py index b83985e17f9b..8801751a0fa4 100644 --- a/install.py +++ b/install.py @@ -465,20 +465,20 @@ def install(inst, version, from_fork, novenv, noupgrade): # upgrade pip and before check that it is installed in venv if sys.platform != "win32": ensure(pip=pycmd) - subprocess.check_call([pycmd, "-m", "pip", "install"] + [ - ] if noupgrade else ["--upgrade"] + [ + subprocess.check_call([pycmd, "-m", "pip", "install"] + ([ + ] if noupgrade else ["--upgrade"]) + [ "pip", "wheel"]) else: if os.path.exists("python\\python._pth.old"): - subprocess.check_call([pycmd, "-m", "pip", "install"] + [ - ] if noupgrade else ["--upgrade"] + [ + subprocess.check_call([pycmd, "-m", "pip", "install"] + ([ + ] if noupgrade else ["--upgrade"]) + [ "pip", "wheel", "urllib3"]) else: - subprocess.check_call([pycmd, "-m", "ensurepip"] + [ - ] if noupgrade else ["--upgrade"]) + subprocess.check_call([pycmd, "-m", "ensurepip"] + ([ + ] if noupgrade else ["--upgrade"])) - subprocess.check_call([pycmd, "-m", "pip", "install"] + [ - ] if noupgrade else ["--upgrade"] + [ + subprocess.check_call([pycmd, "-m", "pip", "install"] + ([ + ] if noupgrade else ["--upgrade"]) + [ "pip", "wheel", "urllib3"]) # install the requirements @@ -511,20 +511,20 @@ def install(inst, version, from_fork, novenv, noupgrade): subprocess.check_call([pycmd, "-m", "pip", "install", "-e", CDIR]) elif version is None: # install the stable version subprocess.check_call( - [pycmd, "-m", "pip", "install"] + [ - ] if noupgrade else ["--upgrade"] + ["openquake.engine"] + [pycmd, "-m", "pip", "install"] + ([ + ] if noupgrade else ["--upgrade"]) + ["openquake.engine"] ) elif re.match(r"\d+(\.\d+)+", version): # install an official version subprocess.check_call( - [pycmd, "-m", "pip", "install"] + [ - ] if noupgrade else ["--upgrade"] + ["openquake.engine==" + version] + [pycmd, "-m", "pip", "install"] + ([ + ] if noupgrade else ["--upgrade"]) + ["openquake.engine==" + version] ) else: # install a branch from github (only for user or server) commit = latest_commit(version) print("Installing commit", commit) subprocess.check_call( - [pycmd, "-m", "pip", "install"] + [ - ] if noupgrade else ["--upgrade"] + [GITBRANCH % commit] + [pycmd, "-m", "pip", "install"] + ([ + ] if noupgrade else ["--upgrade"]) + [GITBRANCH % commit] ) fix_version(commit, inst.VENV) From 9b0bd353f1cabe4c2724e78c98ec07968b4e3399 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Sun, 25 Jan 2026 20:29:43 +0100 Subject: [PATCH 19/19] add a docker_latest workflow to be able to run it in the current github infrastructure --- .github/workflows/docker_latest.yml | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/docker_latest.yml diff --git a/.github/workflows/docker_latest.yml b/.github/workflows/docker_latest.yml new file mode 100644 index 000000000000..fd564ad24589 --- /dev/null +++ b/.github/workflows/docker_latest.yml @@ -0,0 +1,54 @@ +--- +name: Build Image Latest for Docker +on: + workflow_dispatch: + inputs: + tags: + description: Comma-separated list of tags + default: latest + required: true + git-ref: + description: Git Ref + default: master + required: true + +jobs: + docker: + name: Build image and push after successfull calculation + runs-on: ubuntu-latest + + steps: + # This Checkout is necessary when using a context in docker/build-push-action + - name: Clone Repository (Latest) + uses: actions/checkout@v4 + if: github.event.inputs.git-ref == '' + - name: Clone Repository (Custom Ref) + uses: actions/checkout@v4 + if: github.event.inputs.git-ref != '' + with: + ref: ${{ github.event.inputs.git-ref }} + - name: Extract tag names + shell: bash + run: echo "##[set-output name=tags;]" | tr -d '\n'; for tag in $(echo ${{ github.event.inputs.tags }} | tr , '\n'); do echo "-t openquake/engine:$tag " | tr -d '\n'; done + id: extract_tags + - name: Build image engine with tag ${{ github.event.inputs.version }} + env: + DOCKER_USERNAME: ${{ secrets.docker_username }} + DOCKER_PASSWORD: ${{ secrets.docker_password }} + REPO_REF: ${{ github.event.inputs.git-ref }} + id: docker_engine + run: docker build --build-arg oq_branch=$REPO_REF ${{ steps.extract_tags.outputs.tags }} -f docker/Dockerfile.engine docker + - name: List Image + run: | + docker image ls + - name: Run calcs on single docker + run: | + time docker run openquake/engine:latest "oq engine --run "https://github.com/gem/oq-engine/blob/master/openquake/server/tests/data/classical.zip?raw=true"" + - name: push image engine with tags ${{ github.event.inputs.tags }} on dockerhub + env: + DOCKER_USERNAME: ${{ secrets.docker_username }} + DOCKER_PASSWORD: ${{ secrets.docker_password }} + DOCKER_TAG: ${{ github.event.inputs.version }} + run: | + docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" + docker push openquake/engine --all-tags