diff --git a/.env b/.env index 40d69959..573874aa 100644 --- a/.env +++ b/.env @@ -60,3 +60,5 @@ USE_OPENWISP_CELERY_FIRMWARE=True OPENWISP_CELERY_FIRMWARE_COMMAND_FLAGS=--concurrency=1 # Metric collection METRIC_COLLECTION=True +# collectstatic +COLLECTSTATIC_WHEN_DEPS_CHANGE=true diff --git a/docs/user/settings.rst b/docs/user/settings.rst index da816f0e..13bdd7fb 100644 --- a/docs/user/settings.rst +++ b/docs/user/settings.rst @@ -1164,3 +1164,12 @@ NFS Server - **Valid Values:** STRING. - **Default:** ``10.0.0.0/8(rw,fsid=0,insecure,no_root_squash,no_subtree_check,sync)``. + +``COLLECTSTATIC_WHEN_DEPS_CHANGE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- **Description:** Runs ``collectstatic`` at container startup only if + Python dependencies have changed. Set to ``false`` if you're using the + development version of the OpenWISP python modules. +- **Accepted Values:** ``true``, ``false`` +- **Default:** ``true`` diff --git a/images/common/collectstatic.py b/images/common/collectstatic.py new file mode 100644 index 00000000..a0cbc036 --- /dev/null +++ b/images/common/collectstatic.py @@ -0,0 +1,51 @@ +"""Run ``collectstatic`` only when dependencies have changed. + +Speeds up startup time on cloud platforms. To disable this behavior, set +the ``COLLECTSTATIC_WHEN_DEPS_CHANGE`` environment variable to ``False``. +""" + +import hashlib +import os +import subprocess +import sys + +import django +import redis +from django.conf import settings + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openwisp.settings") +django.setup() + + +def get_pip_freeze_hash(): + try: + pip_freeze_output = subprocess.check_output(["pip", "freeze"]).decode() + return hashlib.sha256(pip_freeze_output.encode()).hexdigest() + except subprocess.CalledProcessError as e: + print(f"Error running 'pip freeze': {e}", file=sys.stderr) + sys.exit(1) + + +def run_collectstatic(): + try: + subprocess.run( + ["python", "manage.py", "collectstatic", "--noinput"], check=True + ) + except subprocess.CalledProcessError as e: + print(f"Error running 'collectstatic': {e}", file=sys.stderr) + sys.exit(1) + + +def main(): + if os.environ.get("COLLECTSTATIC_WHEN_DEPS_CHANGE", "true").lower() == "false": + run_collectstatic() + return + redis_connection = redis.Redis.from_url(settings.CACHES["default"]["LOCATION"]) + current_pip_hash = get_pip_freeze_hash() + cached_pip_hash = redis_connection.get("pip_freeze_hash") + if cached_pip_hash is None or cached_pip_hash.decode() != current_pip_hash: + print("Changes in Python dependencies detected, running collectstatic...") + run_collectstatic() + redis_connection.set("pip_freeze_hash", current_pip_hash) + else: + print("No changes in Python dependencies, skipping collectstatic...") diff --git a/images/common/init_command.sh b/images/common/init_command.sh index 02e6bb9c..b023589f 100644 --- a/images/common/init_command.sh +++ b/images/common/init_command.sh @@ -14,7 +14,7 @@ if [ "$MODULE_NAME" = 'dashboard' ]; then python manage.py migrate --noinput test -f "$SSH_PRIVATE_KEY_PATH" || ssh-keygen -t ed25519 -f "$SSH_PRIVATE_KEY_PATH" -N "" python load_init_data.py - python manage.py collectstatic --noinput + python collectstatic.py start_uwsgi elif [ "$MODULE_NAME" = 'postfix' ]; then postfix_config